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 DraggableMode = 'mapping' | 'panel-resize';
export interface INodeUi extends INode {
position: XYPosition;
color?: string;

View File

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

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import Draggable from './Draggable.vue';
import Draggable from '@/components/Draggable.vue';
import type { XYPosition } from '@/Interface';
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) {
draggingPath.value = el.dataset.path;
}
ndvStore.draggableStartDragging({
type: 'mapping',
data: data ?? '',
dimensions: el?.getBoundingClientRect() ?? null,
});
ndvStore.resetMappingTelemetry();
};
const onDragEnd = (el: HTMLElement) => {
ndvStore.draggableStopDragging();
draggingPath.value = null;
const mappingTelemetry = ndvStore.mappingTelemetry;
const telemetryPayload = {
@@ -132,6 +141,8 @@ const getListItemName = (path: string) => {
type="mapping"
target-data-key="mappable"
:disabled="!mappingEnabled"
:can-drop="canDraggableDrop"
:sticky-position="draggableStickyPosition"
@dragstart="onDragStart"
@dragend="onDragEnd"
>

View File

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

View File

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