Browse Source

feat(editor): add edit trigger block

Ahmad Kholid 3 years ago
parent
commit
3baeaaf7f4

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

@@ -42,13 +42,13 @@
       "
     >
       <div class="bg-accent px-4 py-2 text-white rounded-lg flex items-center">
-        <button class="-ml-1">
+        <button class="-ml-1" @click="editBlock">
           <v-remixicon size="20" :path="icons.riPencilLine" />
         </button>
         <hr class="border-r border-gray-600 h-5 mx-3" />
         <button
           class="-mr-1"
-          @click="emitter.emit('block:delete', state.blockId)"
+          @click="editor.removeNodeId(`node-${state.blockId}`)"
         >
           <v-remixicon size="20" :path="icons.riDeleteBin7Line" />
         </button>
@@ -76,6 +76,15 @@ const state = shallowReactive({
   blockData: {},
 });
 
+function editBlock() {
+  const { data } = props.editor.getNodeFromId(state.blockId);
+  emitter.emit('editor:edit-block', {
+    ...state.blockData,
+    data,
+    blockId: state.blockId,
+  });
+}
+
 nextTick(() => {
   state.blockId = rootRef.value?.parentElement.parentElement.id.replace(
     'node-',
@@ -85,7 +94,7 @@ nextTick(() => {
   if (state.blockId) {
     const { name } = props.editor.getNodeFromId(state.blockId);
 
-    state.blockData = tasks[name];
+    state.blockData = { id: name, ...tasks[name] };
   }
 });
 </script>

+ 17 - 23
src/components/newtab/workflow/WorkflowBuilder.vue

@@ -5,7 +5,7 @@
     @drop="dropHandler"
     @dragover.prevent
   >
-    <div class="absolute p-4 bottom-0 left-0">
+    <div class="absolute z-10 p-4 bottom-0 left-0">
       <button class="p-2 rounded-lg bg-white mr-2" @click="editor.zoom_reset()">
         <v-remixicon name="riFullscreenLine" />
       </button>
@@ -22,18 +22,17 @@
   </div>
 </template>
 <script>
-import { onMounted, onUnmounted, shallowRef } from 'vue';
-import emitter from 'tiny-emitter/instance';
+import { onMounted, shallowRef, getCurrentInstance } from 'vue';
 import drawflow from '@/lib/drawflow';
 
 export default {
   props: {
     data: {
-      type: Object,
+      type: [Object, String],
       default: null,
     },
   },
-  emits: ['addBlock', 'deleteBlock', 'saveWorkflow'],
+  emits: ['addBlock', 'export', 'load', 'deleteBlock'],
   setup(props, { emit }) {
     const editor = shallowRef(null);
 
@@ -64,26 +63,26 @@ export default {
         xPosition,
         yPosition,
         block.id,
-        { id: block.id, name: block.name },
+        { id: block.id, name: block.name, data: block.data },
         block.component,
         'vue'
       );
     }
-    function saveWorkflow() {
-      emit('saveWorkflow', editor.value.export());
-    }
-    function deleteBlock(id) {
-      editor.value.removeNodeId(`node-${id}`);
-    }
 
     onMounted(() => {
-      const element = document.querySelector('#drawflow');
+      const element = document.querySelector('#drawflow', getCurrentInstance());
 
       editor.value = drawflow(element);
       editor.value.start();
 
+      emit('load', editor.value);
+
       if (props.data) {
-        editor.value.import(props.data);
+        console.log(props.data, 'asas');
+        const data =
+          typeof props.data === 'string' ? JSON.parse(props.data) : props.data;
+
+        editor.value.import(data);
       } else {
         editor.value.addNode(
           'trigger',
@@ -92,20 +91,15 @@ export default {
           50,
           300,
           'trigger',
-          {},
+          { type: 'manual' },
           'BlockBase',
           'vue'
         );
       }
 
-      emitter.on('block:delete', deleteBlock);
-
-      window.addEventListener('beforeunload', saveWorkflow);
-    });
-    onUnmounted(() => {
-      saveWorkflow();
-      emitter.off('block:delete', deleteBlock);
-      window.removeEventListener('beforeunload', saveWorkflow);
+      editor.value.on('nodeRemoved', (id) => {
+        emit('deleteBlock', id);
+      });
     });
 
     return {

+ 73 - 127
src/components/newtab/workflow/WorkflowDetailsCard.vue

@@ -1,119 +1,89 @@
 <template>
-  <div
-    class="w-80 bg-white py-4 relative border-l border-gray-100 flex flex-col"
-    padding="p-0"
-  >
-    <div class="px-4 mb-2">
-      <span
-        class="
-          p-2
-          inline-block
-          rounded-lg
-          bg-accent
-          text-white
-          mr-2
-          align-middle
-        "
-      >
-        <v-remixicon :name="workflow.icon" />
-      </span>
-      <p class="font-semibold inline-block text-lg flex-1 mr-4 align-middle">
-        {{ workflow.name }}
-      </p>
-    </div>
-    <div class="flex px-4 mt-2">
-      <ui-button variant="accent" class="flex-1 mr-4"> Execute </ui-button>
-      <ui-button icon class="text-red-500">
-        <v-remixicon name="riDeleteBin7Line" />
-      </ui-button>
-    </div>
-    <hr class="m-4 border-gray-100" />
-    <!-- <ui-tabs v-model="state.activeTab" fill class="mx-4 mb-4">
-      <ui-tab value="blocks">Blocks</ui-tab>
-      <ui-tab value="data-schema">Data Columns</ui-tab>
-    </ui-tabs> -->
-    <!-- <div class="px-4 mb-2">
-      <ui-input prepend-icon="riSearch2Line" class="w-full" placeholder="Search..." />
-    </div> -->
-    <ui-tab-panels
-      v-model="state.activeTab"
-      class="scroll bg-scroll overflow-auto px-4 flex-1"
-      style="overflow: overlay"
+  <div class="px-4 mb-2">
+    <span
+      class="p-2 inline-block rounded-lg bg-accent text-white mr-2 align-middle"
     >
-      <ui-tab-panel value="blocks">
-        <template v-for="(items, catId) in taskList" :key="catId">
-          <div class="flex items-center top-0 space-x-2 mb-2">
-            <span
-              :class="categories[catId].color"
-              class="h-3 w-3 rounded-full"
-            ></span>
-            <p class="capitalize text-gray-600">{{ categories[catId].name }}</p>
-          </div>
-          <div class="grid grid-cols-2 gap-2 mb-4">
-            <div
-              v-for="task in items"
-              :key="task.id"
-              :title="task.name"
-              draggable="true"
-              class="
-                select-none
-                cursor-move
-                relative
-                p-4
-                rounded-lg
-                bg-input
-                transition
-              "
-              @dragstart="
-                $event.dataTransfer.setData('block', JSON.stringify(task))
-              "
-            >
-              <v-remixicon :name="task.icon" size="24" class="mb-2" />
-              <p class="leading-tight text-overflow">
-                {{ task.name }}
-              </p>
-            </div>
-          </div>
-        </template>
-      </ui-tab-panel>
-      <ui-tab-panel value="data-schema" class="pt-1">
-        <div class="mb-4 space-y-2">
-          <transition-group name="list">
-            <div
-              v-for="(item, index) in state.dataSchema"
-              :key="index"
-              class="flex items-center list-item-transition"
-            >
-              <ui-input
-                v-model="state.dataSchema[index]"
-                class="mr-2"
-                placeholder="Column name"
-              />
-              <button @click="state.dataSchema.splice(index, 1)">
-                <v-remixicon name="riDeleteBin7Line" />
-              </button>
-            </div>
-          </transition-group>
-        </div>
-        <ui-button variant="accent" @click="state.dataSchema.push('')">
-          Add column
+      <v-remixicon :name="workflow.icon" />
+    </span>
+    <p class="font-semibold inline-block text-lg flex-1 mr-4 align-middle">
+      {{ workflow.name }}
+    </p>
+  </div>
+  <div class="flex px-4 mt-2 space-x-2">
+    <ui-button variant="accent" class="flex-1" @click="$emit('save')">
+      <v-remixicon name="riSaveLine" class="mr-2 -ml-1" />
+      Save
+    </ui-button>
+    <ui-popover>
+      <template #trigger>
+        <ui-button icon>
+          <v-remixicon name="riMore2Line" />
         </ui-button>
-      </ui-tab-panel>
-    </ui-tab-panels>
+      </template>
+      <ui-list class="w-36">
+        <ui-list-item>
+          <v-remixicon name="riPlayLine" class="mr-2 -ml-1" />
+          <span>Execute</span>
+        </ui-list-item>
+        <ui-list-item>
+          <v-remixicon name="riPencilLine" class="mr-2 -ml-1" />
+          <span>Rename</span>
+        </ui-list-item>
+        <ui-list-item>
+          <v-remixicon name="riDeleteBin7Line" class="mr-2 -ml-1" />
+          <span>Delete</span>
+        </ui-list-item>
+      </ui-list>
+    </ui-popover>
+  </div>
+  <hr class="m-4 border-gray-100" />
+  <div class="scroll bg-scroll overflow-auto px-4 flex-1 overflow-auto">
+    <template v-for="(items, catId) in taskList" :key="catId">
+      <div class="flex items-center top-0 space-x-2 mb-2">
+        <span
+          :class="categories[catId].color"
+          class="h-3 w-3 rounded-full"
+        ></span>
+        <p class="capitalize text-gray-600">{{ categories[catId].name }}</p>
+      </div>
+      <div class="grid grid-cols-2 gap-2 mb-4">
+        <div
+          v-for="task in items"
+          :key="task.id"
+          :title="task.name"
+          draggable="true"
+          class="
+            select-none
+            cursor-move
+            relative
+            p-4
+            rounded-lg
+            bg-input
+            transition
+          "
+          @dragstart="
+            $event.dataTransfer.setData('block', JSON.stringify(task))
+          "
+        >
+          <v-remixicon :name="task.icon" size="24" class="mb-2" />
+          <p class="leading-tight text-overflow">
+            {{ task.name }}
+          </p>
+        </div>
+      </div>
+    </template>
   </div>
 </template>
 <script setup>
-import { reactive, watch } from 'vue';
 import { tasks, categories } from '@/utils/shared';
-import { debounce } from '@/utils/helper';
 
-const props = defineProps({
+defineProps({
   workflow: {
     type: Object,
     default: () => ({}),
   },
 });
-const emit = defineEmits(['update-workflow']);
+defineEmits(['update', 'execute', 'save']);
 
 const taskList = Object.keys(tasks).reduce((arr, key) => {
   const task = tasks[key];
@@ -122,28 +92,4 @@ const taskList = Object.keys(tasks).reduce((arr, key) => {
 
   return arr;
 }, {});
-
-const state = reactive({
-  show: false,
-  dataSchema: [],
-  activeTab: 'blocks',
-});
-
-watch(
-  () => props.workflow.id,
-  () => {
-    const data = props.workflow.dataSchema;
-
-    state.dataSchema = Array.isArray(data) ? data : Object.values(data);
-  },
-  { immediate: true }
-);
-watch(
-  () => state.dataSchema,
-  debounce((value) => {
-    const uniqueData = [...new Set(value)];
-    emit('update-workflow', uniqueData);
-  }, 500),
-  { deep: true }
-);
 </script>

+ 48 - 0
src/components/newtab/workflow/WorkflowEditBlock.vue

@@ -0,0 +1,48 @@
+<template>
+  <div class="px-4">
+    <div>
+      <button class="mr-2 align-middle" @click="$emit('close')">
+        <v-remixicon name="riArrowLeftLine" />
+      </button>
+      <p class="font-semibold inline-block align-middle">
+        {{ data.name }}
+      </p>
+    </div>
+    <hr class="mb-4 mt-5 w-full border-gray-100" />
+    <ui-textarea
+      :model-value="blockData.description"
+      autoresize
+      placeholder="Description"
+      class="w-full mb-2"
+      @change="$emit('update', { ...blockData, description: $event })"
+    />
+    <component :is="data.editComponent" v-model:data="blockData" />
+  </div>
+</template>
+<script>
+import { computed } from 'vue';
+import EditTrigger from './edit/EditTrigger.vue';
+</script>
+<script setup>
+export default {
+  components: { EditTrigger },
+};
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+const emit = defineEmits(['close', 'update']);
+
+const blockData = computed({
+  get() {
+    console.log(props.data);
+    return props.data.data || {};
+  },
+  set(value) {
+    emit('update', value);
+  },
+});
+</script>

+ 0 - 9
src/components/newtab/workflow/WorkflowTask.vue

@@ -60,22 +60,13 @@
           <ui-input placeholder="Element selector" class="mr-4 flex-1" />
           <ui-checkbox>Multiple</ui-checkbox>
         </div>
-        <!-- <component is="TaskClickElement" /> -->
       </div>
     </transition-expand>
   </div>
 </template>
-<script>
-import { ref, computed } from 'vue';
-import TaskClickElement from './task/TaskClickElement.vue';
-</script>
 <script setup>
 import { tasks } from '@/utils/shared';
 
-export default {
-  components: { TaskClickElement },
-};
-
 const props = defineProps({
   task: {
     type: Object,

+ 50 - 0
src/components/newtab/workflow/edit/EditTrigger.vue

@@ -0,0 +1,50 @@
+<template>
+  <ui-select
+    :model-value="data.type || 'manual'"
+    placeholder="Trigger workflow"
+    class="w-full"
+    @change="updateData({ type: $event })"
+  >
+    <option value="manual">Manual</option>
+    <option value="interval">Interval</option>
+  </ui-select>
+  <div v-if="data.type === 'interval'" class="flex items-center">
+    <ui-input
+      :model-value="data.interval || '60'"
+      type="number"
+      class="mt-1 w-full mr-2"
+      label="Interval (minutes)"
+      placeholder="5-120"
+      @change="updateInputValue($event, { key: 'interval', min: 5, max: 120 })"
+    />
+    <ui-input
+      :model-value="data.delay || '5'"
+      type="number"
+      class="mt-1 w-full"
+      label="Delay (minutes)"
+      placeholder="0-20"
+      @change="updateInputValue($event, { key: 'delay', min: 0, max: 20 })"
+    />
+  </div>
+</template>
+<script setup>
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+const emit = defineEmits(['update:data']);
+
+function updateData(value) {
+  emit('update:data', { ...props.data, ...value });
+}
+function updateInputValue(value, { key, min, max }) {
+  let num = +value;
+
+  if (num < min) num = min;
+  else if (num > max) num = max;
+
+  updateData({ [key]: num });
+}
+</script>

+ 0 - 9
src/components/newtab/workflow/task/TaskClickElement.vue

@@ -1,9 +0,0 @@
-<template>
-  <div class="flex items-center">
-    <ui-button icon class="mr-2">
-      <v-remixicon name="riFocus3Line" />
-    </ui-button>
-    <ui-input placeholder="Element selector" class="mr-4 flex-1" />
-    <ui-checkbox>Multiple</ui-checkbox>
-  </div>
-</template>

+ 1 - 1
src/components/ui/UiInput.vue

@@ -55,7 +55,7 @@ export default {
       default: false,
     },
     modelValue: {
-      type: String,
+      type: [String, Number],
       default: '',
     },
     prependIcon: {

+ 84 - 0
src/components/ui/UiTextarea.vue

@@ -0,0 +1,84 @@
+<template>
+  <label :class="[block ? 'block' : 'inline-block']">
+    <span v-if="label" class="text-gray-500 text-sm ml-2 block">{{
+      label
+    }}</span>
+    <textarea
+      ref="textarea"
+      v-bind="{ value: modelValue, placeholder, maxlength: max }"
+      class="
+        ui-textarea
+        w-full
+        ui-input
+        rounded-lg
+        px-4
+        py-2
+        transition
+        bg-input
+      "
+      :class="{ 'overflow-hidden resize-none': autoresize }"
+      :style="{ height }"
+      @input="emitValue"
+    ></textarea>
+  </label>
+</template>
+<script>
+import { ref, onMounted } from 'vue';
+
+export default {
+  props: {
+    modelValue: {
+      type: String,
+      default: '',
+    },
+    label: {
+      type: String,
+      default: '',
+    },
+    placeholder: {
+      type: String,
+      default: '',
+    },
+    autoresize: {
+      type: Boolean,
+      default: false,
+    },
+    height: {
+      type: [Number, String],
+      default: '',
+    },
+    max: [Number, String],
+    block: Boolean,
+  },
+  emits: ['update:modelValue', 'change'],
+  setup(props, { emit }) {
+    const textarea = ref(null);
+
+    function calcHeight() {
+      if (!props.autoresize) return;
+
+      textarea.value.style.height = 'auto';
+      textarea.value.style.height = `${textarea.value.scrollHeight}px`;
+    }
+    function emitValue(event) {
+      let { value } = event.target;
+      const maxLength = Math.abs(props.max);
+
+      if (value.length > maxLength) {
+        value = value.slice(0, maxLength);
+      }
+
+      emit('update:modelValue', value);
+      emit('change', value);
+      calcHeight();
+    }
+
+    onMounted(calcHeight);
+
+    return {
+      textarea,
+      emitValue,
+    };
+  },
+};
+</script>

+ 1 - 1
src/lib/comps-ui.js

@@ -20,7 +20,7 @@ function componentsExtractor(app, components) {
 export default function (app) {
   app.directive('autofocus', VAutofocus);
   app.directive('close-popover', VClosePopover);
-
+  console.log(app);
   componentsExtractor(app, uiComponents);
   componentsExtractor(app, transitionComponents);
 }

+ 6 - 2
src/lib/drawflow.js

@@ -1,11 +1,15 @@
-import { createApp, h } from 'vue';
+import { createApp, h, defineComponent } from 'vue';
 import Drawflow from 'drawflow';
 import '@/assets/css/drawflow.css';
 
 const blockComponents = require.context('../components/block', false, /\.vue$/);
 
 export default function (element, ctx) {
-  const editor = new Drawflow(element, { createApp, version: 3, h }, ctx);
+  const editor = new Drawflow(
+    element,
+    { defineComponent, createApp, version: 3, h },
+    ctx
+  );
 
   editor.useuuid = true;
   editor.reroute = true;

+ 2 - 0
src/lib/v-remixicon.js

@@ -1,6 +1,7 @@
 import vRemixicon from 'v-remixicon';
 import {
   riHome5Line,
+  riSaveLine,
   riSubtractLine,
   riPlayLine,
   riPauseLine,
@@ -43,6 +44,7 @@ import {
 
 export const icons = {
   riHome5Line,
+  riSaveLine,
   riSubtractLine,
   riPlayLine,
   riPauseLine,

+ 1 - 1
src/models/workflow.js

@@ -13,7 +13,7 @@ class Workflow extends Model {
       name: this.string(''),
       icon: this.string('riGlobalLine'),
       data: this.attr(null),
-      drawflow: this.attr(null),
+      drawflow: this.string(''),
       dataSchema: this.attr([]),
       lastRunAt: this.number(),
       createdAt: this.number(),

+ 2 - 1
src/newtab/App.vue

@@ -6,7 +6,7 @@
   <ui-dialog />
 </template>
 <script setup>
-import { onMounted, ref } from 'vue';
+import { onMounted, ref, getCurrentInstance } from 'vue';
 import { useStore } from 'vuex';
 import browser from 'webextension-polyfill';
 import AppSidebar from '@/components/newtab/app/AppSidebar.vue';
@@ -14,6 +14,7 @@ import AppSidebar from '@/components/newtab/app/AppSidebar.vue';
 const store = useStore();
 
 const retrieved = ref(false);
+console.log(getCurrentInstance(), AppSidebar);
 
 onMounted(async () => {
   try {

+ 58 - 7
src/newtab/pages/workflows/[id].vue

@@ -1,44 +1,90 @@
 <template>
   <div class="flex h-screen">
-    <workflow-details-card :workflow="workflow" />
+    <div
+      class="w-80 bg-white py-4 relative border-l border-gray-100 flex flex-col"
+    >
+      <workflow-edit-block
+        v-if="state.isEditBlock"
+        :data="state.blockData"
+        @update="updateBlockData"
+        @close="(state.isEditBlock = false), (state.blockData = {})"
+      />
+      <workflow-details-card
+        v-else
+        :workflow="workflow"
+        @save="saveWorkflow"
+        @execute="executeWorkflow"
+        @update="updateWorkflow"
+      />
+    </div>
     <workflow-builder
       class="flex-1"
       :data="workflow.drawflow"
+      @load="editor = $event"
       @addBlock="addBlock"
       @deleteBlock="deleteBlock"
-      @saveWorkflow="saveWorkflow"
+      @export="updateWorkflow({ drawflow: $event })"
     />
   </div>
 </template>
 <script setup>
-import { computed, onMounted } from 'vue';
+import { computed, reactive, shallowRef, onMounted, onUnmounted } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
+import emitter from 'tiny-emitter/instance';
 import Task from '@/models/task';
 import Workflow from '@/models/workflow';
+import { debounce } from '@/utils/helper';
 import WorkflowBuilder from '@/components/newtab/workflow/WorkflowBuilder.vue';
+import WorkflowEditBlock from '@/components/newtab/workflow/WorkflowEditBlock.vue';
 import WorkflowDetailsCard from '@/components/newtab/workflow/WorkflowDetailsCard.vue';
 
 const route = useRoute();
 const router = useRouter();
 
 const workflowId = route.params.id;
+
+const editor = shallowRef(null);
+const state = reactive({
+  isEditBlock: true,
+  blockData: {},
+});
 const workflow = computed(() => Workflow.find(workflowId) || {});
 
+const updateBlockData = debounce((data) => {
+  state.blockData.data = data;
+  editor.value.updateNodeDataFromId(state.blockData.blockId, data);
+  console.log(editor.value.export());
+}, 250);
 function addBlock(data) {
   Task.insert({
     data: { ...data, workflowId },
   });
 }
 function deleteBlock(id) {
-  Task.delete(id);
+  if (state.isEditBlock && state.blockData.blockId === id) {
+    state.isEditBlock = false;
+    state.blockData = {};
+  }
 }
-function saveWorkflow(data) {
-  console.log('saved', workflowId, data);
+function updateWorkflow(data) {
   Workflow.update({
     where: workflowId,
-    data: { drawflow: data },
+    data,
   });
 }
+function saveWorkflow() {
+  const data = editor.value.export();
+
+  updateWorkflow({ drawflow: JSON.stringify(data) });
+}
+function editBlock(data) {
+  console.log(data);
+  state.isEditBlock = true;
+  state.blockData = data;
+}
+function executeWorkflow() {
+  console.log(editor.value);
+}
 
 onMounted(() => {
   const isWorkflowExists = Workflow.query().where('id', workflowId).exists();
@@ -46,6 +92,11 @@ onMounted(() => {
   if (!isWorkflowExists) {
     router.push('/workflows');
   }
+
+  emitter.on('editor:edit-block', editBlock);
+});
+onUnmounted(() => {
+  emitter.off('editor:edit-block', editBlock);
 });
 </script>
 <style>

+ 67 - 0
src/utils/shared.js

@@ -3,111 +3,178 @@ export const tasks = {
     name: 'Trigger',
     icon: 'riFlashlightLine',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'general',
     inputs: 0,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      type: 'manual',
+    },
   },
   'event-click': {
     name: 'Click element',
     icon: 'riCursorLine',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'interaction',
     inputs: 1,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      selector: '',
+      multiple: false,
+    },
   },
   delay: {
     name: 'Delay',
     icon: 'riTimerLine',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'general',
     inputs: 1,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      time: 500,
+    },
   },
   'get-text': {
     name: 'Get text',
     icon: 'riParagraph',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'interaction',
     inputs: 1,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      selector: '',
+      multiple: false,
+      regex: '',
+    },
   },
   'export-data': {
     name: 'Export data',
     icon: 'riDownloadLine',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'general',
     inputs: 1,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      type: 'JSON',
+    },
   },
   'element-scroll': {
     name: 'Scroll element',
     icon: 'riMouseLine',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'interaction',
     inputs: 1,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      selector: '',
+      multiple: false,
+      scrollY: 0,
+      scrollX: 0,
+    },
   },
   'get-attribute': {
     name: 'Get attribute',
     icon: 'riBracketsLine',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'interaction',
     inputs: 1,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      selector: '',
+      multiple: false,
+    },
   },
   'open-website': {
     name: 'Open website',
     icon: 'riGlobalLine',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'general',
     inputs: 1,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      url: '',
+    },
   },
   'text-input': {
     name: 'Text input',
     icon: 'riInputCursorMove',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'interaction',
     inputs: 1,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      selector: '',
+      multiple: false,
+      text: '',
+    },
   },
   'repeat-task': {
     name: 'Repeat tasks',
     icon: 'riRepeat2Line',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'general',
     inputs: 1,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      from: '',
+      for: 1,
+    },
   },
   'trigger-element-events': {
     name: 'Trigger element events',
     icon: 'riLightbulbFlashLine',
     component: 'BlockBase',
+    editComponent: 'EditTrigger',
     category: 'interaction',
     inputs: 1,
     outputs: 1,
     allowedInputs: [],
     maxConnection: 1,
+    data: {
+      description: '',
+      selector: '',
+      multiple: false,
+      events: [],
+    },
   },
 };