refactor(editor): Decouple Draggable from NDV store (#14531)

This commit is contained in:
Milorad FIlipović
2025-04-11 11:17:44 +02:00
committed by GitHub
parent e54f450a9d
commit be627f08a4
6 changed files with 63 additions and 30 deletions

View File

@@ -157,6 +157,8 @@ export interface INodeUpdatePropertiesInformation {
export type XYPosition = [number, number]; export type XYPosition = [number, number];
export type DraggableMode = 'mapping' | 'panel-resize';
export interface INodeUi extends INode { export interface INodeUi extends INode {
position: XYPosition; position: XYPosition;
color?: string; color?: string;

View File

@@ -1,22 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
import type { XYPosition } from '@/Interface'; import type { DraggableMode, XYPosition } from '@/Interface';
import { useNDVStore } from '@/stores/ndv.store';
import { isPresent } from '@/utils/typesUtils'; import { isPresent } from '@/utils/typesUtils';
import { type StyleValue, computed, ref } from 'vue'; import { type StyleValue, computed, ref } from 'vue';
type Props = { type Props = {
type: string; type: DraggableMode;
data?: string; data?: string | null;
tag?: string; tag?: keyof HTMLElementTagNameMap;
targetDataKey?: string; targetDataKey?: string | null;
disabled?: boolean; disabled?: boolean;
canDrop?: boolean;
stickyPosition?: XYPosition | null;
}; };
const props = withDefaults(defineProps<Props>(), { tag: 'div', disabled: false }); const props = withDefaults(defineProps<Props>(), {
data: null,
tag: 'div',
targetDataKey: null,
disabled: false,
canDrop: false,
stickyPosition: null,
});
const emit = defineEmits<{ const emit = defineEmits<{
drag: [value: XYPosition]; drag: [value: XYPosition];
dragstart: [value: HTMLElement]; dragstart: [value: HTMLElement, data: string | undefined];
dragend: [value: HTMLElement]; dragend: [value: HTMLElement];
}>(); }>();
@@ -24,22 +32,18 @@ const isDragging = ref(false);
const draggingElement = ref<HTMLElement>(); const draggingElement = ref<HTMLElement>();
const draggablePosition = ref<XYPosition>([0, 0]); const draggablePosition = ref<XYPosition>([0, 0]);
const animationFrameId = ref<number>(); const animationFrameId = ref<number>();
const ndvStore = useNDVStore();
const draggableStyle = computed<StyleValue>(() => ({ const draggableStyle = computed<StyleValue>(() => ({
transform: `translate(${draggablePosition.value[0]}px, ${draggablePosition.value[1]}px)`, transform: `translate(${draggablePosition.value[0]}px, ${draggablePosition.value[1]}px)`,
})); }));
const canDrop = computed(() => ndvStore.canDraggableDrop);
const stickyPosition = computed(() => ndvStore.draggableStickyPos);
const onDragStart = (event: MouseEvent) => { const onDragStart = (event: MouseEvent) => {
if (props.disabled) { if (props.disabled) {
return; return;
} }
draggingElement.value = event.target as HTMLElement; draggingElement.value = event.target as HTMLElement;
if (props.targetDataKey && draggingElement.value.dataset?.target !== props.targetDataKey) { if (props.targetDataKey && draggingElement.value.dataset?.target !== props.targetDataKey) {
draggingElement.value = draggingElement.value.closest( draggingElement.value = draggingElement.value.closest(
`[data-target="${props.targetDataKey}"]`, `[data-target="${props.targetDataKey}"]`,
@@ -79,19 +83,13 @@ const onDrag = (event: MouseEvent) => {
const data = props.targetDataKey ? draggingElement.value.dataset.value : (props.data ?? ''); const data = props.targetDataKey ? draggingElement.value.dataset.value : (props.data ?? '');
ndvStore.draggableStartDragging({ emit('dragstart', draggingElement.value, data);
type: props.type,
data: data ?? '',
dimensions: draggingElement.value?.getBoundingClientRect() ?? null,
});
emit('dragstart', draggingElement.value);
document.body.style.cursor = 'grabbing'; document.body.style.cursor = 'grabbing';
} }
animationFrameId.value = window.requestAnimationFrame(() => { animationFrameId.value = window.requestAnimationFrame(() => {
if (canDrop.value && stickyPosition.value) { if (props.canDrop && props.stickyPosition) {
draggablePosition.value = stickyPosition.value; draggablePosition.value = props.stickyPosition;
} else { } else {
draggablePosition.value = [event.pageX, event.pageY]; draggablePosition.value = [event.pageX, event.pageY];
} }
@@ -115,7 +113,6 @@ const onDragEnd = () => {
if (draggingElement.value) emit('dragend', draggingElement.value); if (draggingElement.value) emit('dragend', draggingElement.value);
isDragging.value = false; isDragging.value = false;
draggingElement.value = undefined; draggingElement.value = undefined;
ndvStore.draggableStopDragging();
}, 0); }, 0);
}; };
</script> </script>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import Draggable from './Draggable.vue'; import Draggable from '@/components/Draggable.vue';
import type { XYPosition } from '@/Interface'; import type { XYPosition } from '@/Interface';
defineProps<{ defineProps<{

View File

@@ -72,15 +72,24 @@ const getJsonParameterPath = (path: string) => {
}); });
}; };
const onDragStart = (el: HTMLElement) => { const canDraggableDrop = computed(() => ndvStore.canDraggableDrop);
const draggableStickyPosition = computed(() => ndvStore.draggableStickyPos);
const onDragStart = (el: HTMLElement, data?: string) => {
if (el?.dataset.path) { if (el?.dataset.path) {
draggingPath.value = el.dataset.path; draggingPath.value = el.dataset.path;
} }
ndvStore.draggableStartDragging({
type: 'mapping',
data: data ?? '',
dimensions: el?.getBoundingClientRect() ?? null,
});
ndvStore.resetMappingTelemetry(); ndvStore.resetMappingTelemetry();
}; };
const onDragEnd = (el: HTMLElement) => { const onDragEnd = (el: HTMLElement) => {
ndvStore.draggableStopDragging();
draggingPath.value = null; draggingPath.value = null;
const mappingTelemetry = ndvStore.mappingTelemetry; const mappingTelemetry = ndvStore.mappingTelemetry;
const telemetryPayload = { const telemetryPayload = {
@@ -132,6 +141,8 @@ const getListItemName = (path: string) => {
type="mapping" type="mapping"
target-data-key="mappable" target-data-key="mappable"
:disabled="!mappingEnabled" :disabled="!mappingEnabled"
:can-drop="canDraggableDrop"
:sticky-position="draggableStickyPosition"
@dragstart="onDragStart" @dragstart="onDragStart"
@dragend="onDragEnd" @dragend="onDragEnd"
> >

View File

@@ -8,7 +8,7 @@ import { getPairedItemId } from '@/utils/pairedItemUtils';
import { shorten } from '@/utils/typesUtils'; import { shorten } from '@/utils/typesUtils';
import type { GenericValue, IDataObject, INodeExecutionData } from 'n8n-workflow'; import type { GenericValue, IDataObject, INodeExecutionData } from 'n8n-workflow';
import { computed, onMounted, ref, watch } from 'vue'; import { computed, onMounted, ref, watch } from 'vue';
import Draggable from './Draggable.vue'; import Draggable from '@/components/Draggable.vue';
import MappingPill from './MappingPill.vue'; import MappingPill from './MappingPill.vue';
import TextWithHighlights from './TextWithHighlights.vue'; import TextWithHighlights from './TextWithHighlights.vue';
import { useI18n } from '@/composables/useI18n'; import { useI18n } from '@/composables/useI18n';
@@ -71,6 +71,9 @@ const {
focusedMappableInput, focusedMappableInput,
highlightDraggables: highlight, highlightDraggables: highlight,
} = storeToRefs(ndvStore); } = storeToRefs(ndvStore);
const canDraggableDrop = computed(() => ndvStore.canDraggableDrop);
const draggableStickyPosition = computed(() => ndvStore.draggableStickyPos);
const pairedItemMappings = computed(() => workflowsStore.workflowExecutionPairedItemMappings); const pairedItemMappings = computed(() => workflowsStore.workflowExecutionPairedItemMappings);
const tableData = computed(() => convertToTable(props.inputData)); const tableData = computed(() => convertToTable(props.inputData));
@@ -244,17 +247,22 @@ function getValueToRender(value: unknown): string {
return JSON.stringify(value); return JSON.stringify(value);
} }
function onDragStart() { function onDragStart(el: HTMLElement, data?: string) {
draggedColumn.value = true; draggedColumn.value = true;
ndvStore.draggableStartDragging({
type: 'mapping',
data: data ?? '',
dimensions: el?.getBoundingClientRect() ?? null,
});
ndvStore.resetMappingTelemetry(); ndvStore.resetMappingTelemetry();
} }
function onCellDragStart(el: HTMLElement) { function onCellDragStart(el: HTMLElement, data?: string) {
if (el?.dataset.value) { if (el?.dataset.value) {
draggingPath.value = el.dataset.value; draggingPath.value = el.dataset.value;
} }
onDragStart(); onDragStart(el, data);
} }
function onCellDragEnd(el: HTMLElement) { function onCellDragEnd(el: HTMLElement) {
@@ -272,6 +280,7 @@ function isDraggingKey(path: Array<string | number>, colIndex: number) {
} }
function onDragEnd(column: string, src: string, depth = '0') { function onDragEnd(column: string, src: string, depth = '0') {
ndvStore.draggableStopDragging();
setTimeout(() => { setTimeout(() => {
const mappingTelemetry = ndvStore.mappingTelemetry; const mappingTelemetry = ndvStore.mappingTelemetry;
const telemetryPayload = { const telemetryPayload = {
@@ -492,6 +501,8 @@ watch(focusedMappableInput, (curr) => {
type="mapping" type="mapping"
:data="getExpression(column)" :data="getExpression(column)"
:disabled="!mappingEnabled" :disabled="!mappingEnabled"
:can-drop="canDraggableDrop"
:sticky-position="draggableStickyPosition"
@dragstart="onDragStart" @dragstart="onDragStart"
@dragend="(column) => onDragEnd(column?.textContent ?? '', 'column')" @dragend="(column) => onDragEnd(column?.textContent ?? '', 'column')"
> >

View File

@@ -86,6 +86,7 @@ const schemaPreviewStore = useSchemaPreviewStore();
const environmentsStore = useEnvironmentsStore(); const environmentsStore = useEnvironmentsStore();
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
const posthogStore = usePostHog(); const posthogStore = usePostHog();
const { getSchemaForExecutionData, getSchemaForJsonSchema, getSchema, filterSchema } = const { getSchemaForExecutionData, getSchemaForJsonSchema, getSchema, filterSchema } =
useDataSchema(); useDataSchema();
const { closedNodes, flattenSchema, flattenMultipleSchemas, toggleLeaf, toggleNode } = const { closedNodes, flattenSchema, flattenMultipleSchemas, toggleLeaf, toggleNode } =
@@ -98,6 +99,9 @@ const emit = defineEmits<{
const scroller = ref<RecycleScrollerInstance>(); const scroller = ref<RecycleScrollerInstance>();
const canDraggableDrop = computed(() => ndvStore.canDraggableDrop);
const draggableStickyPosition = computed(() => ndvStore.draggableStickyPos);
const toggleNodeAndScrollTop = (id: string) => { const toggleNodeAndScrollTop = (id: string) => {
toggleNode(id); toggleNode(id);
scroller.value?.scrollToItem(0); scroller.value?.scrollToItem(0);
@@ -333,11 +337,17 @@ watch(
{ once: true, immediate: true }, { once: true, immediate: true },
); );
const onDragStart = () => { const onDragStart = (el: HTMLElement, data?: string) => {
ndvStore.draggableStartDragging({
type: 'mapping',
data: data ?? '',
dimensions: el?.getBoundingClientRect() ?? null,
});
ndvStore.resetMappingTelemetry(); ndvStore.resetMappingTelemetry();
}; };
const onDragEnd = (el: HTMLElement) => { const onDragEnd = (el: HTMLElement) => {
ndvStore.draggableStopDragging();
setTimeout(() => { setTimeout(() => {
const mappingTelemetry = ndvStore.mappingTelemetry; const mappingTelemetry = ndvStore.mappingTelemetry;
const parentNode = nodesSchemas.value.find(({ node }) => node.name === el.dataset.nodeName); const parentNode = nodesSchemas.value.find(({ node }) => node.name === el.dataset.nodeName);
@@ -387,6 +397,8 @@ const onDragEnd = (el: HTMLElement) => {
type="mapping" type="mapping"
target-data-key="mappable" target-data-key="mappable"
:disabled="!mappingEnabled" :disabled="!mappingEnabled"
:can-drop="canDraggableDrop"
:sticky-position="draggableStickyPosition"
@dragstart="onDragStart" @dragstart="onDragStart"
@dragend="onDragEnd" @dragend="onDragEnd"
> >