Browse Source

feat: add group block 2

Ahmad Kholid 3 years ago
parent
commit
714db8b679

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

@@ -16,6 +16,10 @@
 		}
 	}
 
+	&.vue-flow__node-BlockGroup2 {
+		z-index: 0 !important;
+	}
+
 	.vue-flow__handle {
 		@apply h-4 w-4 rounded-full border-0;
 		&.target {

+ 112 - 0
src/components/block/BlockGroup2.vue

@@ -0,0 +1,112 @@
+<template>
+  <div
+    :style="{
+      width: `${data.width || 400}px`,
+      height: `${data.height || 300}px`,
+    }"
+    class="group-block-2 group relative border-2 rounded-lg relative"
+    style="
+      min-width: 400px;
+      min-height: 300px;
+      border-color: '#2563eb';
+      background-color: rgb(37, 99, 235, 0.3);
+    "
+  >
+    <div class="p-4 flex items-center">
+      <input
+        :value="data.name"
+        placeholder="name"
+        type="text"
+        class="px-4 py-2 bg-white rounded-lg"
+        @input="emit('update', { name: $event.target.value })"
+      />
+    </div>
+    <span
+      ref="dragHandle"
+      style="cursor: nw-resize"
+      class="drag-handle h-4 w-4 invisible group-hover:visible bg-accent absolute bottom-0 right-0"
+    />
+  </div>
+</template>
+<script setup>
+import { ref, onMounted, onBeforeUnmount } from 'vue';
+import { debounce } from '@/utils/helper';
+
+defineProps({
+  id: {
+    type: String,
+    default: '',
+  },
+  label: {
+    type: String,
+    default: '',
+  },
+  data: {
+    type: Object,
+    default: () => ({}),
+  },
+  position: {
+    type: Object,
+    default: () => ({}),
+  },
+  events: {
+    type: Object,
+    default: () => ({}),
+  },
+  dimensions: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+const emit = defineEmits(['delete', 'edit', 'update']);
+
+let parent = null;
+const initialRect = {
+  x: 0,
+  y: 0,
+  width: 0,
+  height: 0,
+};
+
+const dragHandle = ref(null);
+
+const onMousemove = debounce((event) => {
+  event.preventDefault();
+  event.stopPropagation();
+
+  const width = initialRect.width + event.clientX - initialRect.x;
+  const height = initialRect.height + event.clientY - initialRect.y;
+
+  parent.style.width = `${width}px`;
+  parent.style.height = `${height}px`;
+
+  emit('update', { height, width });
+}, 100);
+
+function onMouseup() {
+  document.documentElement.removeEventListener('mouseup', onMouseup);
+  document.documentElement.removeEventListener('mousemove', onMousemove);
+}
+function initDragging(event) {
+  event.preventDefault();
+  event.stopPropagation();
+
+  const { height, width } = getComputedStyle(parent);
+
+  initialRect.x = event.clientX;
+  initialRect.y = event.clientY;
+  initialRect.width = parseInt(width, 10);
+  initialRect.height = parseInt(height, 10);
+
+  document.documentElement.addEventListener('mouseup', onMouseup);
+  document.documentElement.addEventListener('mousemove', onMousemove);
+}
+
+onMounted(() => {
+  parent = dragHandle.value.closest('.group-block-2');
+  dragHandle.value.addEventListener('mousedown', initDragging);
+});
+onBeforeUnmount(() => {
+  dragHandle.value.removeEventListener('mousedown', initDragging);
+});
+</script>

+ 4 - 0
src/locales/en/blocks.json

@@ -230,6 +230,10 @@
         "activeTabLoaded": "Active tab",
         "setAsActiveTab": "Set as active tab"
       },
+      "blocks-group-2": {
+        "name": "@:workflow.blocks.blocks-group.name 2",
+        "description": "@:workflow.blocks.blocks-group.description"
+      },
       "blocks-group": {
         "name": "Blocks group",
         "groupName": "Group name",

+ 40 - 0
src/newtab/pages/workflows/[id].vue

@@ -174,6 +174,7 @@ import {
   shallowRef,
   onBeforeUnmount,
 } from 'vue';
+import { getNodesInside } from '@braks/vue-flow';
 import { useI18n } from 'vue-i18n';
 import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router';
 import { customAlphabet } from 'nanoid';
@@ -580,6 +581,42 @@ function onActionUpdated({ data, changedIndicator }) {
   workflowPayload.data = { ...workflowPayload.data, ...data };
   updateHostedWorkflow();
 }
+function isNodesInGroup(nodes) {
+  const groupNodes = editor.value.getNodes.value.filter(
+    (node) => node.label === 'blocks-group-2'
+  );
+
+  const nodeInGroup = new Set();
+  const filteredNodes = nodes.filter((node) => node.label !== 'blocks-group-2');
+
+  groupNodes.forEach(({ computedPosition, dimensions, id }) => {
+    const rect = { ...computedPosition, ...dimensions };
+    const nodesInGroup = getNodesInside(filteredNodes, rect);
+
+    nodesInGroup.forEach((node) => {
+      state.dataChanged = true;
+
+      if (node.parentNode === id) {
+        nodeInGroup.add(node.id);
+        return;
+      }
+
+      nodeInGroup.add(node.id);
+
+      const currentNode = editor.value.getNode.value(node.id);
+      currentNode.parentNode = id;
+      currentNode.position.x -= 450;
+    });
+  });
+  filteredNodes.forEach((node) => {
+    if (nodeInGroup.has(node.id)) return;
+
+    const currentNode = editor.value.getNode.value(node.id);
+    if (!currentNode.parentNode) return;
+
+    currentNode.parentNode = undefined;
+  });
+}
 function onEditorInit(instance) {
   editor.value = instance;
 
@@ -591,7 +628,10 @@ function onEditorInit(instance) {
   // instance.onEdgeUpdateEnd(({ edge }) => {
   //   editorCommands.state.edges[edge.id] = edge;
   // });
+
   instance.onNodeDragStop(({ nodes }) => {
+    isNodesInGroup(nodes);
+
     nodes.forEach((node) => {
       editorCommands.state.nodes[node.id] = node;
     });

+ 20 - 0
src/utils/shared.js

@@ -739,6 +739,26 @@ export const tasks = {
       blocks: [],
     },
   },
+  'blocks-group-2': {
+    name: 'Blocks group',
+    description: 'Grouping blocks',
+    icon: 'riFolderZipLine',
+    component: 'BlockGroup2',
+    category: 'general',
+    disableEdit: true,
+    inputs: 1,
+    outputs: 1,
+    allowedInputs: true,
+    maxConnection: 1,
+    data: {
+      disableBlock: false,
+      name: '',
+      width: 400,
+      height: 300,
+      borderColor: '#2563eb',
+      backgroundColor: 'rgb(37, 99, 235, 0.3)',
+    },
+  },
   clipboard: {
     name: 'Clipboard',
     description: 'Get the copied text from the clipboard',