Browse Source

feat: add block menu

Ahmad Kholid 2 years ago
parent
commit
3eae89c49d

+ 4 - 5
src/assets/css/flow.css

@@ -6,13 +6,12 @@
 	& > div {
 		@apply rounded-lg transition;
 	}
-	&.selected > div {
+	&.selected .block-base__content {
 		@apply ring-2 ring-accent;
 	}
-	&:hover,
-	&.selected {
-		.menu {
-			@apply translate-y-11;
+	&:hover {
+		.block-menu-container {
+			display: block;
 		}
 	}
 

+ 82 - 36
src/components/block/BlockBase.vue

@@ -1,60 +1,106 @@
 <template>
   <div class="block-base relative w-48" @dblclick.stop="$emit('edit')">
-    <slot name="prepend" />
-    <ui-card :class="contentClass" class="z-10 relative block-base__content">
-      <slot></slot>
-    </ui-card>
-    <slot name="append" />
     <div
-      v-if="!minimap"
-      class="absolute bottom-1 transition-transform duration-300 pt-4 ml-1 menu"
+      class="top-0 w-full absolute block-menu-container hidden"
+      style="transform: translateY(-100%)"
     >
-      <div
-        class="bg-accent dark:bg-gray-100 dark:text-black text-white rounded-lg flex items-center"
-      >
+      <div class="inline-flex items-center dark:text-gray-300 block-menu">
         <button
-          v-if="!hideEdit"
-          class="px-3 focus:ring-0 py-2"
-          title="Edit block"
-          @click="$emit('edit')"
-        >
-          <v-remixicon size="20" name="riPencilLine" />
-        </button>
-        <hr
-          v-if="!hideDelete && !hideEdit"
-          class="border-r border-gray-600 h-5"
-        />
-        <button
-          v-if="!hideDelete"
-          class="px-3 focus:ring-0 py-2"
+          v-if="!blockData.details?.disableDelete"
           title="Delete block"
           @click.stop="$emit('delete')"
         >
           <v-remixicon size="20" name="riDeleteBin7Line" />
         </button>
+        <button
+          v-if="!excludeGroupBlocks.includes(blockData.details?.id)"
+          :title="$t('workflow.blocks.base.moveToGroup')"
+          draggable="true"
+          class="cursor-move"
+          @dragstart="handleStartDrag"
+          @mousedown.stop
+        >
+          <v-remixicon name="riDragDropLine" size="20" />
+        </button>
+        <button
+          v-if="blockData.details?.id !== 'trigger'"
+          title="Enable/Disable block"
+          @click.stop="$emit('update', { disableBlock: !data.disableBlock })"
+        >
+          <v-remixicon
+            size="20"
+            :name="data.disableBlock ? 'riToggleLine' : 'riToggleFill'"
+          />
+        </button>
+        <button title="Run workflow from here" @click.stop="runWorkflow">
+          <v-remixicon size="20" name="riPlayLine" />
+        </button>
+        <button
+          v-if="!blockData.details?.disableEdit"
+          title="Edit block"
+          @click="$emit('edit')"
+        >
+          <v-remixicon size="20" name="riPencilLine" />
+        </button>
         <slot name="action" />
       </div>
     </div>
+    <slot name="prepend" />
+    <ui-card :class="contentClass" class="z-10 relative block-base__content">
+      <slot></slot>
+    </ui-card>
+    <slot name="append" />
   </div>
 </template>
 <script setup>
-defineProps({
-  hideDelete: {
-    type: Boolean,
-    default: false,
+import { inject } from 'vue';
+import { excludeGroupBlocks } from '@/utils/shared';
+
+const props = defineProps({
+  contentClass: {
+    type: String,
+    default: '',
   },
-  minimap: {
-    type: Boolean,
-    default: false,
+  blockData: {
+    type: Object,
+    default: () => ({}),
   },
-  hideEdit: {
-    type: Boolean,
-    default: false,
+  data: {
+    type: Object,
+    default: () => ({}),
   },
-  contentClass: {
+  blockId: {
     type: String,
     default: '',
   },
 });
-defineEmits(['delete', 'edit']);
+defineEmits(['delete', 'edit', 'update']);
+
+const workflowUtils = inject('workflow-utils', null);
+
+function handleStartDrag(event) {
+  const payload = {
+    data: props.data,
+    fromBlockBasic: true,
+    blockId: props.blockId,
+    id: props.blockData.details.id,
+  };
+
+  event.dataTransfer.setData('block', JSON.stringify(payload));
+}
+function runWorkflow() {
+  if (!workflowUtils) return;
+
+  workflowUtils.executeFromBlock(props.blockId);
+}
 </script>
+<style>
+.block-menu {
+  @apply mb-1 bg-box-transparent-2 rounded-md;
+  button {
+    padding-left: 6px;
+    padding-right: 6px;
+    @apply focus:ring-0 py-1 hover:text-primary;
+  }
+}
+</style>

+ 4 - 24
src/components/block/BlockBasic.vue

@@ -1,12 +1,14 @@
 <template>
   <block-base
     :id="componentId"
-    :hide-edit="block.details.disableEdit"
-    :hide-delete="block.details.disableDelete"
+    :data="data"
+    :block-id="id"
+    :block-data="block"
     :data-position="JSON.stringify(position)"
     class="block-basic group"
     @edit="$emit('edit')"
     @delete="$emit('delete', id)"
+    @update="$emit('update', $event)"
   >
     <Handle
       v-if="label !== 'trigger'"
@@ -49,18 +51,6 @@
       </div>
     </div>
     <slot :block="block"></slot>
-    <template #prepend>
-      <div
-        v-if="block.details.id !== 'trigger'"
-        :title="t('workflow.blocks.base.moveToGroup')"
-        draggable="true"
-        class="bg-white dark:bg-gray-700 invisible group-hover:visible z-50 absolute -top-2 -right-2 rounded-md p-1 shadow-md"
-        @dragstart="handleStartDrag"
-        @mousedown.stop
-      >
-        <v-remixicon name="riDragDropLine" size="20" />
-      </div>
-    </template>
     <div
       v-if="data.onError?.enable && data.onError?.toDo === 'fallback'"
       class="fallback flex items-center justify-end"
@@ -124,16 +114,6 @@ const { t, te } = useI18n();
 const block = useEditorBlock(props.label);
 const componentId = useComponentId('block-base');
 
-function handleStartDrag(event) {
-  const payload = {
-    data: props.data,
-    id: block.details.id,
-    blockId: props.id,
-    fromBlockBasic: true,
-  };
-
-  event.dataTransfer.setData('block', JSON.stringify(payload));
-}
 function copyLoopId() {
   navigator.clipboard.writeText(props.data.loopId);
 }

+ 4 - 25
src/components/block/BlockBasicWithFallback.vue

@@ -1,11 +1,13 @@
 <template>
   <block-base
     :id="componentId"
-    :hide-edit="block.details.disableEdit"
-    :hide-delete="block.details.disableDelete"
+    :data="data"
+    :block-id="id"
+    :block-data="block"
     class="block-basic group"
     @edit="$emit('edit')"
     @delete="$emit('delete', id)"
+    @update="$emit('update', $event)"
   >
     <Handle :id="`${id}-input-1`" type="target" :position="Position.Left" />
     <div class="flex items-center">
@@ -46,18 +48,6 @@
       :position="Position.Right"
       style="top: auto; bottom: 10px"
     />
-    <template #prepend>
-      <div
-        v-if="block.details.id !== 'trigger'"
-        :title="t('workflow.blocks.base.moveToGroup')"
-        draggable="true"
-        class="bg-white dark:bg-gray-700 invisible group-hover:visible z-50 absolute -top-2 -right-2 rounded-md p-1 shadow-md"
-        @dragstart="handleStartDrag"
-        @mousedown.stop
-      >
-        <v-remixicon name="riDragDropLine" size="20" />
-      </div>
-    </template>
   </block-base>
 </template>
 <script setup>
@@ -86,15 +76,4 @@ defineEmits(['delete', 'edit', 'update']);
 const { t } = useI18n();
 const block = useEditorBlock(props.label);
 const componentId = useComponentId('block-base');
-
-function handleStartDrag(event) {
-  const payload = {
-    data: block.data,
-    id: block.details.id,
-    blockId: block.id,
-    fromBlockBasic: true,
-  };
-
-  event.dataTransfer.setData('block', JSON.stringify(payload));
-}
 </script>

+ 5 - 1
src/components/block/BlockConditions.vue

@@ -1,9 +1,13 @@
 <template>
   <block-base
     :id="componentId"
+    :data="data"
+    :block-id="id"
+    :block-data="block"
     class="w-64"
     @edit="$emit('edit')"
     @delete="$emit('delete', id)"
+    @update="$emit('update', $event)"
   >
     <Handle :id="`${id}-input-1`" type="target" :position="Position.Left" />
     <div class="flex items-center">
@@ -94,7 +98,7 @@ const props = defineProps({
     default: () => ({}),
   },
 });
-defineEmits(['delete', 'edit']);
+defineEmits(['delete', 'edit', 'update']);
 
 const { t } = useI18n();
 const componentId = useComponentId('block-conditions');

+ 12 - 3
src/components/block/BlockDelay.vue

@@ -1,9 +1,17 @@
 <template>
-  <ui-card :id="componentId" class="p-4 w-48 block-basic">
+  <block-base
+    :id="componentId"
+    :data="data"
+    :block-id="id"
+    :block-data="block"
+    class="w-48"
+    @delete="$emit('delete', id)"
+    @update="$emit('update', $event)"
+  >
     <Handle :id="`${id}-input-1`" type="target" :position="Position.Left" />
     <div class="flex items-center mb-2">
       <div
-        :class="block.category.color"
+        :class="data.disableBlock ? 'bg-box-transparent' : block.category.color"
         class="inline-block text-sm mr-4 p-2 rounded-lg dark:text-black"
       >
         <v-remixicon name="riTimerLine" size="20" class="inline-block mr-1" />
@@ -37,13 +45,14 @@
       <v-remixicon name="riDragDropLine" size="20" />
     </div>
     <Handle :id="`${id}-output-1`" type="source" :position="Position.Right" />
-  </ui-card>
+  </block-base>
 </template>
 <script setup>
 import { useI18n } from 'vue-i18n';
 import { Handle, Position } from '@vue-flow/core';
 import { useComponentId } from '@/composable/componentId';
 import { useEditorBlock } from '@/composable/editorBlock';
+import BlockBase from './BlockBase.vue';
 
 const props = defineProps({
   id: {

+ 5 - 2
src/components/block/BlockElementExists.vue

@@ -1,10 +1,13 @@
 <template>
   <block-base
     :id="componentId"
-    class="element-exists"
+    :data="data"
+    :block-id="id"
+    :block-data="block"
     style="width: 195px"
     @edit="$emit('edit')"
     @delete="$emit('delete', id)"
+    @update="$emit('update', $event)"
   >
     <Handle :id="`${id}-input-1`" type="target" :position="Position.Left" />
     <div
@@ -62,7 +65,7 @@ const props = defineProps({
     default: () => ({}),
   },
 });
-defineEmits(['delete', 'edit']);
+defineEmits(['delete', 'edit', 'update']);
 
 const { t } = useI18n();
 const block = useEditorBlock(props.label);

+ 16 - 9
src/components/block/BlockGroup.vue

@@ -1,10 +1,22 @@
 <template>
-  <ui-card :id="componentId" class="w-64" padding="p-0">
+  <block-base
+    :id="componentId"
+    :data="data"
+    :block-id="id"
+    :block-data="block"
+    class="w-64"
+    content-class="p-0"
+    @edit="$emit('edit')"
+    @delete="$emit('delete', id)"
+    @update="$emit('update', $event)"
+  >
     <Handle :id="`${id}-input-1`" type="target" :position="Position.Left" />
     <div class="p-4">
       <div class="flex items-center mb-2">
         <div
-          :class="block.category.color"
+          :class="
+            data.disableBlock ? 'bg-box-transparent' : block.category.color
+          "
           class="inline-flex items-center text-sm mr-4 p-2 rounded-lg dark:text-black"
         >
           <v-remixicon
@@ -14,12 +26,6 @@
           />
           <span>{{ t('workflow.blocks.blocks-group.name') }}</span>
         </div>
-        <div class="flex-grow"></div>
-        <v-remixicon
-          name="riDeleteBin7Line"
-          class="cursor-pointer"
-          @click.stop="emit('delete', id)"
-        />
       </div>
       <input
         :value="data.name"
@@ -91,7 +97,7 @@
       </template>
     </draggable>
     <Handle :id="`${id}-output-1`" type="source" :position="Position.Right" />
-  </ui-card>
+  </block-base>
 </template>
 <script setup>
 import { inject, computed, shallowReactive } from 'vue';
@@ -103,6 +109,7 @@ import draggable from 'vuedraggable';
 import { tasks, excludeGroupBlocks } from '@/utils/shared';
 import { useComponentId } from '@/composable/componentId';
 import { useEditorBlock } from '@/composable/editorBlock';
+import BlockBase from './BlockBase.vue';
 
 const props = defineProps({
   id: {

+ 12 - 3
src/components/block/BlockLoopBreakpoint.vue

@@ -1,9 +1,17 @@
 <template>
-  <ui-card :id="componentId" class="w-48">
+  <block-base
+    :id="componentId"
+    :data="data"
+    :block-id="id"
+    :block-data="block"
+    class="w-48"
+    @delete="$emit('delete', id)"
+    @update="$emit('update', $event)"
+  >
     <Handle :id="`${id}-input-1`" type="target" :position="Position.Left" />
     <div class="flex items-center mb-2">
       <div
-        :class="block.category.color"
+        :class="data.disableBlock ? 'bg-box-transparent' : block.category.color"
         class="inline-block text-sm mr-4 p-2 rounded-lg dark:text-black text-overflow"
       >
         <v-remixicon name="riStopLine" size="20" class="inline-block mr-1" />
@@ -32,13 +40,14 @@
       Stop loop
     </ui-checkbox>
     <Handle :id="`${id}-output-1`" type="source" :position="Position.Right" />
-  </ui-card>
+  </block-base>
 </template>
 <script setup>
 import { useI18n } from 'vue-i18n';
 import { Handle, Position } from '@vue-flow/core';
 import { useComponentId } from '@/composable/componentId';
 import { useEditorBlock } from '@/composable/editorBlock';
+import BlockBase from './BlockBase.vue';
 
 const props = defineProps({
   id: {

+ 11 - 2
src/components/block/BlockPackage.vue

@@ -1,5 +1,13 @@
 <template>
-  <ui-card :id="componentId" class="p-4 w-64 block-package">
+  <block-base
+    :id="componentId"
+    :data="data"
+    :block-id="id"
+    :block-data="block"
+    class="w-64 block-package"
+    @delete="$emit('delete', id)"
+    @update="$emit('update', $event)"
+  >
     <div class="flex items-center">
       <img
         v-if="data.icon.startsWith('http')"
@@ -82,7 +90,7 @@
         <v-remixicon size="18" name="riExternalLinkLine" />
       </a>
     </div>
-  </ui-card>
+  </block-base>
 </template>
 <script setup>
 import { onMounted, shallowReactive } from 'vue';
@@ -91,6 +99,7 @@ import { Handle, Position } from '@vue-flow/core';
 import { usePackageStore } from '@/stores/package';
 import { useComponentId } from '@/composable/componentId';
 import { useEditorBlock } from '@/composable/editorBlock';
+import BlockBase from './BlockBase.vue';
 
 const props = defineProps({
   id: {

+ 12 - 9
src/components/block/BlockRepeatTask.vue

@@ -1,20 +1,22 @@
 <template>
-  <ui-card :id="componentId" class="p-4 repeat-task w-64">
+  <block-base
+    :id="componentId"
+    :data="data"
+    :block-id="id"
+    :block-data="block"
+    class="repeat-task w-64"
+    @delete="$emit('delete', id)"
+    @update="$emit('update', $event)"
+  >
     <Handle :id="`${id}-input-1`" type="target" :position="Position.Left" />
     <div class="flex items-center mb-2">
       <div
-        :class="block.category.color"
+        :class="data.disableBlock ? 'bg-box-transparent' : block.category.color"
         class="inline-block text-sm mr-4 p-2 rounded-lg dark:text-black"
       >
         <v-remixicon name="riRepeat2Line" size="20" class="inline-block mr-1" />
         <span>{{ t('workflow.blocks.repeat-task.name') }}</span>
       </div>
-      <div class="flex-grow"></div>
-      <v-remixicon
-        name="riDeleteBin7Line"
-        class="cursor-pointer"
-        @click="$emit('delete', id)"
-      />
     </div>
     <div class="flex bg-input rounded-lg items-center relative">
       <input
@@ -39,13 +41,14 @@
       :position="Position.Right"
       style="top: auto; bottom: 12px"
     />
-  </ui-card>
+  </block-base>
 </template>
 <script setup>
 import { useI18n } from 'vue-i18n';
 import { Handle, Position } from '@vue-flow/core';
 import { useComponentId } from '@/composable/componentId';
 import { useEditorBlock } from '@/composable/editorBlock';
+import BlockBase from './BlockBase.vue';
 
 const { t } = useI18n();
 const props = defineProps({

+ 2 - 2
src/components/newtab/workflow/edit/EditWorkflowParameters.vue

@@ -55,8 +55,8 @@
               </div>
               <div class="col-span-4 flex items-center">
                 <component
-                  :is="paramTypes[param.type].valueComp"
-                  v-if="paramTypes[param.type].valueComp"
+                  :is="paramTypes[param.type]?.valueComp"
+                  v-if="paramTypes[param.type]?.valueComp"
                   v-model="param.defaultValue"
                   :param-data="param"
                   :editor="true"

+ 1 - 13
src/components/newtab/workflow/editor/EditorLocalCtxMenu.vue

@@ -50,7 +50,6 @@ const emit = defineEmits([
   'saveBlock',
   'duplicate',
   'packageIo',
-  'execute:block',
 ]);
 
 const { t } = useI18n();
@@ -127,11 +126,6 @@ const menuItems = {
     name: 'Set as block output',
     event: () => emit('packageIo', { type: 'outputs', ...ctxData }),
   },
-  executeBlock: {
-    id: 'executeBlock',
-    name: 'Execute from here',
-    event: () => emit('execute:block', ctxData),
-  },
 };
 
 /* eslint-disable-next-line */
@@ -166,13 +160,7 @@ function clearContextMenu() {
 
 onMounted(() => {
   props.editor.onNodeContextMenu(({ event, node }) => {
-    const items = [
-      'copy',
-      'duplicate',
-      'executeBlock',
-      'saveToFolder',
-      'delete',
-    ];
+    const items = ['copy', 'duplicate', 'saveToFolder', 'delete'];
     if (node.label === 'blocks-group') {
       items.splice(items.indexOf('saveToFolder'), 0, 'ungroup');
     } else if (!excludeGroupBlocks.includes(node.label)) {

+ 2 - 0
src/lib/vRemixicon.js

@@ -46,6 +46,7 @@ import {
   riTimerLine,
   riMagicLine,
   riHtml5Line,
+  riToggleFill,
   riToggleLine,
   riFolderLine,
   riGithubFill,
@@ -180,6 +181,7 @@ export const icons = {
   riTimerLine,
   riMagicLine,
   riHtml5Line,
+  riToggleFill,
   riToggleLine,
   riFolderLine,
   riGithubFill,

+ 14 - 15
src/newtab/pages/workflows/[id].vue

@@ -231,7 +231,6 @@
             @paste="pasteCopiedElements"
             @saveBlock="initBlockFolder"
             @duplicate="duplicateElements"
-            @execute:block="executeFromBlock"
           />
         </ui-tab-panel>
         <ui-tab-panel value="logs" class="mt-24 container">
@@ -548,14 +547,6 @@ const editorData = computed(() => {
   return workflow.value.drawflow;
 });
 
-provide('workflow', {
-  editState,
-  data: workflow,
-  columns: workflowColumns,
-});
-provide('workflow-editor', editor);
-provide('autocompleteData', autocompleteList);
-
 const updateBlockData = debounce((data) => {
   if (!haveEditAccess.value) return;
   const node = editor.value.getNode.value(editState.blockData.blockId);
@@ -684,14 +675,11 @@ function closeEditingCard() {
 
   state.showSidebar = state.sidebarState;
 }
-async function executeFromBlock({ nodes }) {
+async function executeFromBlock(blockId) {
   try {
-    if (nodes.length === 0) return;
+    if (!blockId) return;
 
-    const [node] = nodes;
-    const workflowOptions = {
-      blockId: node.id,
-    };
+    const workflowOptions = { blockId };
 
     const [tab] = await browser.tabs.query({ active: true, url: '*://*/*' });
     if (tab) {
@@ -1573,6 +1561,17 @@ const shortcut = useShortcut([
   getShortcut('editor:duplicate-block', duplicateElements),
 ]);
 
+provide('workflow-editor', editor);
+provide('autocompleteData', autocompleteList);
+provide('workflow', {
+  editState,
+  data: workflow,
+  columns: workflowColumns,
+});
+provide('workflow-utils', {
+  executeFromBlock,
+});
+
 watch(
   () => state.activeTab,
   (value) => {

+ 0 - 1
src/newtab/workflowEngine/blocksHandler/handlerWebhook.js

@@ -62,7 +62,6 @@ export async function webhook({ data, id }, { refData }) {
       const blob = await response.blob();
       const base64 = await fileReader(blob);
 
-      console.log(base64);
       returnData = base64;
     } else {
       returnData = await response.text();