refactor(editor): Migrate ndv.store to composition API (#11574)
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions

This commit is contained in:
Ricardo Espinoza 2024-11-08 08:11:36 -05:00 committed by GitHub
parent aec372793b
commit 38fefff348
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 458 additions and 315 deletions

View file

@ -1585,3 +1585,44 @@ export type ApiKey = {
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
}; };
export type InputPanel = {
displayMode: IRunDataDisplayMode;
nodeName?: string;
run?: number;
branch?: number;
data: {
isEmpty: boolean;
};
};
export type OutputPanel = {
branch?: number;
displayMode: IRunDataDisplayMode;
data: {
isEmpty: boolean;
};
editMode: {
enabled: boolean;
value: string;
};
};
export type Draggable = {
isDragging: boolean;
type: string;
data: string;
dimensions: DOMRect | null;
activeTarget: { id: string; stickyPosition: null | XYPosition } | null;
};
export type MainPanelType = 'regular' | 'dragless' | 'inputless' | 'unknown' | 'wide';
export type MainPanelDimensions = Record<
MainPanelType,
{
relativeLeft: number;
relativeRight: number;
relativeWidth: number;
}
>;

View file

@ -26,7 +26,8 @@ describe('InlineExpressionTip.vue', () => {
beforeEach(() => { beforeEach(() => {
mockNdvState = { mockNdvState = {
hasInputData: true, hasInputData: true,
isNDVDataEmpty: vi.fn(() => true), isInputPanelEmpty: true,
isOutputPanelEmpty: true,
setHighlightDraggables: vi.fn(), setHighlightDraggables: vi.fn(),
}; };
}); });
@ -42,7 +43,8 @@ describe('InlineExpressionTip.vue', () => {
test('should show the drag-n-drop tip', async () => { test('should show the drag-n-drop tip', async () => {
mockNdvState = { mockNdvState = {
hasInputData: true, hasInputData: true,
isNDVDataEmpty: vi.fn(() => false), isInputPanelEmpty: false,
isOutputPanelEmpty: false,
focusedMappableInput: 'Some Input', focusedMappableInput: 'Some Input',
setHighlightDraggables: vi.fn(), setHighlightDraggables: vi.fn(),
}; };
@ -62,7 +64,8 @@ describe('InlineExpressionTip.vue', () => {
mockNdvState = { mockNdvState = {
hasInputData: false, hasInputData: false,
isInputParentOfActiveNode: true, isInputParentOfActiveNode: true,
isNDVDataEmpty: vi.fn(() => false), isInputPanelEmpty: false,
isOutputPanelEmpty: false,
focusedMappableInput: 'Some Input', focusedMappableInput: 'Some Input',
setHighlightDraggables: vi.fn(), setHighlightDraggables: vi.fn(),
}; };
@ -77,7 +80,8 @@ describe('InlineExpressionTip.vue', () => {
test('should show the correct tip for objects', async () => { test('should show the correct tip for objects', async () => {
mockNdvState = { mockNdvState = {
hasInputData: true, hasInputData: true,
isNDVDataEmpty: vi.fn(() => false), isInputPanelEmpty: false,
isOutputPanelEmpty: false,
focusedMappableInput: 'Some Input', focusedMappableInput: 'Some Input',
setHighlightDraggables: vi.fn(), setHighlightDraggables: vi.fn(),
}; };
@ -106,7 +110,8 @@ describe('InlineExpressionTip.vue', () => {
test('should show the correct tip for primitives', async () => { test('should show the correct tip for primitives', async () => {
mockNdvState = { mockNdvState = {
hasInputData: true, hasInputData: true,
isNDVDataEmpty: vi.fn(() => false), isInputPanelEmpty: false,
isOutputPanelEmpty: false,
focusedMappableInput: 'Some Input', focusedMappableInput: 'Some Input',
setHighlightDraggables: vi.fn(), setHighlightDraggables: vi.fn(),
}; };

View file

@ -30,7 +30,7 @@ const canAddDotToExpression = ref(false);
const resolvedExpressionHasFields = ref(false); const resolvedExpressionHasFields = ref(false);
const canDragToFocusedInput = computed( const canDragToFocusedInput = computed(
() => !ndvStore.isNDVDataEmpty('input') && ndvStore.focusedMappableInput, () => !ndvStore.isInputPanelEmpty && ndvStore.focusedMappableInput,
); );
const emptyExpression = computed(() => props.unresolvedExpression.trim().length === 0); const emptyExpression = computed(() => props.unresolvedExpression.trim().length === 0);

View file

@ -9,7 +9,7 @@ import { useNDVStore } from '@/stores/ndv.store';
import { ndvEventBus } from '@/event-bus'; import { ndvEventBus } from '@/event-bus';
import NDVFloatingNodes from '@/components/NDVFloatingNodes.vue'; import NDVFloatingNodes from '@/components/NDVFloatingNodes.vue';
import { useDebounce } from '@/composables/useDebounce'; import { useDebounce } from '@/composables/useDebounce';
import type { XYPosition } from '@/Interface'; import type { MainPanelType, XYPosition } from '@/Interface';
import { ref, onMounted, onBeforeUnmount, computed, watch } from 'vue'; import { ref, onMounted, onBeforeUnmount, computed, watch } from 'vue';
import { useUIStore } from '@/stores/ui.store'; import { useUIStore } from '@/stores/ui.store';
@ -20,7 +20,7 @@ const PANEL_WIDTH = 350;
const PANEL_WIDTH_LARGE = 420; const PANEL_WIDTH_LARGE = 420;
const MIN_WINDOW_WIDTH = 2 * (SIDE_MARGIN + SIDE_PANELS_MARGIN) + MIN_PANEL_WIDTH; const MIN_WINDOW_WIDTH = 2 * (SIDE_MARGIN + SIDE_PANELS_MARGIN) + MIN_PANEL_WIDTH;
const initialMainPanelWidth: { [key: string]: number } = { const initialMainPanelWidth: Record<MainPanelType, number> = {
regular: MAIN_NODE_PANEL_WIDTH, regular: MAIN_NODE_PANEL_WIDTH,
dragless: MAIN_NODE_PANEL_WIDTH, dragless: MAIN_NODE_PANEL_WIDTH,
unknown: MAIN_NODE_PANEL_WIDTH, unknown: MAIN_NODE_PANEL_WIDTH,
@ -106,22 +106,16 @@ watch(containerWidth, (width) => {
setPositions(mainPanelDimensions.value.relativeLeft); setPositions(mainPanelDimensions.value.relativeLeft);
}); });
const currentNodePaneType = computed((): string => { const currentNodePaneType = computed((): MainPanelType => {
if (!hasInputSlot.value) return 'inputless'; if (!hasInputSlot.value) return 'inputless';
if (!props.isDraggable) return 'dragless'; if (!props.isDraggable) return 'dragless';
if (props.nodeType === null) return 'unknown'; if (props.nodeType === null) return 'unknown';
return props.nodeType.parameterPane ?? 'regular'; return props.nodeType.parameterPane ?? 'regular';
}); });
const mainPanelDimensions = computed( const mainPanelDimensions = computed(() => {
(): { return ndvStore.mainPanelDimensions[currentNodePaneType.value];
relativeWidth: number; });
relativeLeft: number;
relativeRight: number;
} => {
return ndvStore.getMainPanelDimensions(currentNodePaneType.value);
},
);
const calculatedPositions = computed( const calculatedPositions = computed(
(): { inputPanelRelativeRight: number; outputPanelRelativeLeft: number } => { (): { inputPanelRelativeRight: number; outputPanelRelativeLeft: number } => {

View file

@ -316,7 +316,7 @@ async function onClick() {
codeGenerationInProgress.value = false; codeGenerationInProgress.value = false;
} }
if (isChatNode.value || (isChatChild.value && ndvStore.isNDVDataEmpty('input'))) { if (isChatNode.value || (isChatChild.value && ndvStore.isInputPanelEmpty)) {
ndvStore.setActiveNodeName(null); ndvStore.setActiveNodeName(null);
nodeViewEventBus.emit('openChat'); nodeViewEventBus.emit('openChat');
} else if (isListeningForEvents.value) { } else if (isListeningForEvents.value) {

View file

@ -78,7 +78,7 @@ const { isSubNodeType } = useNodeType({
}); });
const pinnedData = usePinnedData(activeNode, { const pinnedData = usePinnedData(activeNode, {
runIndex: props.runIndex, runIndex: props.runIndex,
displayMode: ndvStore.getPanelDisplayMode('output'), displayMode: ndvStore.outputPanelDisplayMode,
}); });
// Data // Data

View file

@ -54,7 +54,8 @@ describe('ParameterInput.vue', () => {
type: 'test', type: 'test',
typeVersion: 1, typeVersion: 1,
}, },
isNDVDataEmpty: vi.fn(() => false), isInputPanelEmpty: false,
isOutputPanelEmpty: false,
}; };
mockNodeTypesState = { mockNodeTypesState = {
allNodeTypes: [], allNodeTypes: [],

View file

@ -523,7 +523,7 @@ const isHtmlNode = computed(() => !!node.value && node.value.type === HTML_NODE_
const isInputTypeString = computed(() => props.parameter.type === 'string'); const isInputTypeString = computed(() => props.parameter.type === 'string');
const isInputTypeNumber = computed(() => props.parameter.type === 'number'); const isInputTypeNumber = computed(() => props.parameter.type === 'number');
const isInputDataEmpty = computed(() => ndvStore.isNDVDataEmpty('input')); const isInputDataEmpty = computed(() => ndvStore.isInputPanelEmpty);
const isDropDisabled = computed( const isDropDisabled = computed(
() => () =>
props.parameter.noDataExpression || props.parameter.noDataExpression ||

View file

@ -185,12 +185,17 @@ const node = toRef(props, 'node');
const pinnedData = usePinnedData(node, { const pinnedData = usePinnedData(node, {
runIndex: props.runIndex, runIndex: props.runIndex,
displayMode: ndvStore.getPanelDisplayMode(props.paneType), displayMode:
props.paneType === 'input' ? ndvStore.inputPanelDisplayMode : ndvStore.outputPanelDisplayMode,
}); });
const { isSubNodeType } = useNodeType({ const { isSubNodeType } = useNodeType({
node, node,
}); });
const displayMode = computed(() =>
props.paneType === 'input' ? ndvStore.inputPanelDisplayMode : ndvStore.outputPanelDisplayMode,
);
const isReadOnlyRoute = computed(() => route.meta.readOnlyCanvas === true); const isReadOnlyRoute = computed(() => route.meta.readOnlyCanvas === true);
const isWaitNodeWaiting = computed( const isWaitNodeWaiting = computed(
() => () =>
@ -200,7 +205,6 @@ const isWaitNodeWaiting = computed(
); );
const { activeNode } = storeToRefs(ndvStore); const { activeNode } = storeToRefs(ndvStore);
const displayMode = computed(() => ndvStore.getPanelDisplayMode(props.paneType));
const nodeType = computed(() => { const nodeType = computed(() => {
if (!node.value) return null; if (!node.value) return null;

View file

@ -1,10 +1,13 @@
import type { import type {
INodeUi, Draggable,
InputPanel,
IRunDataDisplayMode, IRunDataDisplayMode,
MainPanelDimensions,
MainPanelType,
NDVState, NDVState,
NodePanelType, NodePanelType,
OutputPanel,
TargetItem, TargetItem,
XYPosition,
} from '@/Interface'; } from '@/Interface';
import { useStorage } from '@/composables/useStorage'; import { useStorage } from '@/composables/useStorage';
import { import {
@ -13,316 +16,411 @@ import {
LOCAL_STORAGE_TABLE_HOVER_IS_ONBOARDED, LOCAL_STORAGE_TABLE_HOVER_IS_ONBOARDED,
STORES, STORES,
} from '@/constants'; } from '@/constants';
import type { INodeExecutionData, INodeIssues } from 'n8n-workflow'; import type { INodeIssues } from 'n8n-workflow';
import { NodeConnectionType } from 'n8n-workflow'; import { NodeConnectionType } from 'n8n-workflow';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { useWorkflowsStore } from './workflows.store'; import { useWorkflowsStore } from './workflows.store';
import { computed, ref } from 'vue';
export const useNDVStore = defineStore(STORES.NDV, { const DEFAULT_MAIN_PANEL_DIMENSIONS = {
state: (): NDVState => ({ relativeLeft: 1,
activeNodeName: null, relativeRight: 1,
mainPanelDimensions: {}, relativeWidth: 1,
pushRef: '', };
input: {
displayMode: 'schema', export const useNDVStore = defineStore(STORES.NDV, () => {
nodeName: undefined, const localStorageMappingIsOnboarded = useStorage(LOCAL_STORAGE_MAPPING_IS_ONBOARDED);
run: undefined, const localStorageTableHoverIsOnboarded = useStorage(LOCAL_STORAGE_TABLE_HOVER_IS_ONBOARDED);
branch: undefined, const localStorageAutoCompleteIsOnboarded = useStorage(LOCAL_STORAGE_AUTOCOMPLETE_IS_ONBOARDED);
data: {
isEmpty: true, const activeNodeName = ref<string | null>(null);
}, const mainPanelDimensions = ref<MainPanelDimensions>({
unknown: { ...DEFAULT_MAIN_PANEL_DIMENSIONS },
regular: { ...DEFAULT_MAIN_PANEL_DIMENSIONS },
dragless: { ...DEFAULT_MAIN_PANEL_DIMENSIONS },
inputless: { ...DEFAULT_MAIN_PANEL_DIMENSIONS },
wide: { ...DEFAULT_MAIN_PANEL_DIMENSIONS },
});
const pushRef = ref('');
const input = ref<InputPanel>({
displayMode: 'schema',
nodeName: undefined,
run: undefined,
branch: undefined,
data: {
isEmpty: true,
}, },
output: { });
displayMode: 'table', const output = ref<OutputPanel>({
branch: undefined, displayMode: 'table',
data: { branch: undefined,
isEmpty: true, data: {
}, isEmpty: true,
editMode: {
enabled: false,
value: '',
},
}, },
focusedMappableInput: '', editMode: {
focusedInputPath: '', enabled: false,
mappingTelemetry: {}, value: '',
hoveringItem: null, },
expressionOutputItemIndex: 0, });
draggable: { const focusedMappableInput = ref('');
const focusedInputPath = ref('');
const mappingTelemetry = ref<Record<string, string | number | boolean>>({});
const hoveringItem = ref<null | TargetItem>(null);
const expressionOutputItemIndex = ref(0);
const draggable = ref<Draggable>({
isDragging: false,
type: '',
data: '',
dimensions: null,
activeTarget: null,
});
const isMappingOnboarded = ref(localStorageMappingIsOnboarded.value === 'true');
const isTableHoverOnboarded = ref(localStorageTableHoverIsOnboarded.value === 'true');
const isAutocompleteOnboarded = ref(localStorageAutoCompleteIsOnboarded.value === 'true');
const highlightDraggables = ref(false);
const workflowsStore = useWorkflowsStore();
const activeNode = computed(() => {
return workflowsStore.getNodeByName(activeNodeName.value || '');
});
const ndvInputData = computed(() => {
const executionData = workflowsStore.getWorkflowExecution;
const inputNodeName: string | undefined = input.value.nodeName;
const inputRunIndex: number = input.value.run ?? 0;
const inputBranchIndex: number = input.value.branch ?? 0;
if (
!executionData ||
!inputNodeName ||
inputRunIndex === undefined ||
inputBranchIndex === undefined
) {
return [];
}
return (
executionData.data?.resultData?.runData?.[inputNodeName]?.[inputRunIndex]?.data?.main?.[
inputBranchIndex
] ?? []
);
});
const ndvInputNodeName = computed(() => {
return input.value.nodeName;
});
const ndvInputDataWithPinnedData = computed(() => {
const data = ndvInputData.value;
return ndvInputNodeName.value
? (workflowsStore.pinDataByNodeName(ndvInputNodeName.value) ?? data)
: data;
});
const hasInputData = computed(() => {
return ndvInputDataWithPinnedData.value.length > 0;
});
const inputPanelDisplayMode = computed(() => input.value.displayMode);
const outputPanelDisplayMode = computed(() => output.value.displayMode);
const isDraggableDragging = computed(() => draggable.value.isDragging);
const draggableType = computed(() => draggable.value.type);
const draggableData = computed(() => draggable.value.data);
const canDraggableDrop = computed(() => draggable.value.activeTarget !== null);
const outputPanelEditMode = computed(() => output.value.editMode);
const draggableStickyPos = computed(() => draggable.value.activeTarget?.stickyPosition ?? null);
const ndvNodeInputNumber = computed(() => {
const returnData: { [nodeName: string]: number[] } = {};
const workflow = workflowsStore.getCurrentWorkflow();
const activeNodeConections = (
workflow.connectionsByDestinationNode[activeNode.value?.name || ''] ?? {}
).main;
if (!activeNodeConections || activeNodeConections.length < 2) return returnData;
for (const [index, connection] of activeNodeConections.entries()) {
for (const node of connection) {
if (!returnData[node.node]) {
returnData[node.node] = [];
}
returnData[node.node].push(index + 1);
}
}
return returnData;
});
const ndvInputRunIndex = computed(() => input.value.run);
const ndvInputBranchIndex = computed(() => input.value.branch);
const isInputPanelEmpty = computed(() => input.value.data.isEmpty);
const isOutputPanelEmpty = computed(() => output.value.data.isEmpty);
const isInputParentOfActiveNode = computed(() => {
const inputNodeName = ndvInputNodeName.value;
if (!activeNode.value || !inputNodeName) {
return false;
}
const workflow = workflowsStore.getCurrentWorkflow();
const parentNodes = workflow.getParentNodes(activeNode.value.name, NodeConnectionType.Main, 1);
return parentNodes.includes(inputNodeName);
});
const getHoveringItem = computed(() => {
if (isInputParentOfActiveNode.value) {
return hoveringItem.value;
}
return null;
});
const expressionTargetItem = computed(() => {
if (getHoveringItem.value) {
return getHoveringItem.value;
}
if (expressionOutputItemIndex.value && ndvInputNodeName.value) {
return {
nodeName: ndvInputNodeName.value,
runIndex: ndvInputRunIndex.value ?? 0,
outputIndex: ndvInputBranchIndex.value ?? 0,
itemIndex: expressionOutputItemIndex.value,
};
}
return null;
});
const isNDVOpen = computed(() => activeNodeName.value !== null);
const setActiveNodeName = (nodeName: string | null): void => {
activeNodeName.value = nodeName;
};
const setInputNodeName = (nodeName: string | undefined): void => {
input.value.nodeName = nodeName;
};
const setInputRunIndex = (run?: number): void => {
input.value.run = run;
};
const setMainPanelDimensions = (params: {
panelType: MainPanelType;
dimensions: { relativeLeft?: number; relativeRight?: number; relativeWidth?: number };
}): void => {
mainPanelDimensions.value[params.panelType] = {
...mainPanelDimensions.value[params.panelType],
...params.dimensions,
};
};
const setNDVPushRef = (): void => {
pushRef.value = `ndv-${uuid()}`;
};
const resetNDVPushRef = (): void => {
pushRef.value = '';
};
const setPanelDisplayMode = (params: {
pane: NodePanelType;
mode: IRunDataDisplayMode;
}): void => {
if (params.pane === 'input') {
input.value.displayMode = params.mode;
} else {
output.value.displayMode = params.mode;
}
};
const setOutputPanelEditModeEnabled = (isEnabled: boolean): void => {
output.value.editMode.enabled = isEnabled;
};
const setOutputPanelEditModeValue = (payload: string): void => {
output.value.editMode.value = payload;
};
const setMappableNDVInputFocus = (paramName: string): void => {
focusedMappableInput.value = paramName;
};
const draggableStartDragging = ({
type,
data,
dimensions,
}: { type: string; data: string; dimensions: DOMRect | null }): void => {
draggable.value = {
isDragging: true,
type,
data,
dimensions,
activeTarget: null,
};
};
const draggableStopDragging = (): void => {
draggable.value = {
isDragging: false, isDragging: false,
type: '', type: '',
data: '', data: '',
dimensions: null, dimensions: null,
activeTarget: null, activeTarget: null,
}, };
isMappingOnboarded: useStorage(LOCAL_STORAGE_MAPPING_IS_ONBOARDED).value === 'true', };
isTableHoverOnboarded: useStorage(LOCAL_STORAGE_TABLE_HOVER_IS_ONBOARDED).value === 'true',
isAutocompleteOnboarded: useStorage(LOCAL_STORAGE_AUTOCOMPLETE_IS_ONBOARDED).value === 'true',
highlightDraggables: false,
}),
getters: {
activeNode(): INodeUi | null {
const workflowsStore = useWorkflowsStore();
return workflowsStore.getNodeByName(this.activeNodeName || '');
},
ndvInputData(): INodeExecutionData[] {
const workflowsStore = useWorkflowsStore();
const executionData = workflowsStore.getWorkflowExecution;
const inputNodeName: string | undefined = this.input.nodeName;
const inputRunIndex: number = this.input.run ?? 0;
const inputBranchIndex: number = this.input.branch ?? 0;
if ( const setDraggableTarget = (target: NDVState['draggable']['activeTarget']): void => {
!executionData || draggable.value.activeTarget = target;
!inputNodeName || };
inputRunIndex === undefined ||
inputBranchIndex === undefined
) {
return [];
}
return ( const setMappingTelemetry = (telemetry: { [key: string]: string | number | boolean }): void => {
executionData.data?.resultData?.runData?.[inputNodeName]?.[inputRunIndex]?.data?.main?.[ mappingTelemetry.value = { ...mappingTelemetry.value, ...telemetry };
inputBranchIndex };
] ?? []
);
},
ndvInputDataWithPinnedData(): INodeExecutionData[] {
const data = this.ndvInputData;
return this.ndvInputNodeName
? (useWorkflowsStore().pinDataByNodeName(this.ndvInputNodeName) ?? data)
: data;
},
hasInputData(): boolean {
return this.ndvInputDataWithPinnedData.length > 0;
},
getPanelDisplayMode() {
return (panel: NodePanelType) => this[panel].displayMode;
},
inputPanelDisplayMode(): IRunDataDisplayMode {
return this.input.displayMode;
},
outputPanelDisplayMode(): IRunDataDisplayMode {
return this.output.displayMode;
},
isDraggableDragging(): boolean {
return this.draggable.isDragging;
},
draggableType(): string {
return this.draggable.type;
},
draggableData(): string {
return this.draggable.data;
},
canDraggableDrop(): boolean {
return this.draggable.activeTarget !== null;
},
outputPanelEditMode(): NDVState['output']['editMode'] {
return this.output.editMode;
},
getMainPanelDimensions() {
return (panelType: string) => {
const defaults = { relativeRight: 1, relativeLeft: 1, relativeWidth: 1 };
return { ...defaults, ...this.mainPanelDimensions[panelType] };
};
},
draggableStickyPos(): XYPosition | null {
return this.draggable.activeTarget?.stickyPosition ?? null;
},
ndvInputNodeName(): string | undefined {
return this.input.nodeName;
},
ndvInputRunIndex(): number | undefined {
return this.input.run;
},
ndvInputBranchIndex(): number | undefined {
return this.input.branch;
},
isNDVDataEmpty() {
return (panel: 'input' | 'output'): boolean => this[panel].data.isEmpty;
},
isInputParentOfActiveNode(): boolean {
const inputNodeName = this.ndvInputNodeName;
if (!this.activeNode || !inputNodeName) {
return false;
}
const workflow = useWorkflowsStore().getCurrentWorkflow();
const parentNodes = workflow.getParentNodes(this.activeNode.name, NodeConnectionType.Main, 1);
return parentNodes.includes(inputNodeName);
},
getHoveringItem(): TargetItem | null {
if (this.isInputParentOfActiveNode) {
return this.hoveringItem;
}
return null; const resetMappingTelemetry = (): void => {
}, mappingTelemetry.value = {};
expressionTargetItem(): TargetItem | null { };
if (this.getHoveringItem) {
return this.getHoveringItem;
}
if (this.expressionOutputItemIndex && this.ndvInputNodeName) { const setHoveringItem = (item: TargetItem | null): void => {
return { if (item) setTableHoverOnboarded();
nodeName: this.ndvInputNodeName, hoveringItem.value = item;
runIndex: this.ndvInputRunIndex ?? 0, };
outputIndex: this.ndvInputBranchIndex ?? 0,
itemIndex: this.expressionOutputItemIndex,
};
}
return null; const setNDVBranchIndex = (e: { pane: NodePanelType; branchIndex: number }): void => {
}, if (e.pane === 'input') {
isNDVOpen(): boolean { input.value.branch = e.branchIndex;
return this.activeNodeName !== null; } else {
}, output.value.branch = e.branchIndex;
ndvNodeInputNumber() { }
const returnData: { [nodeName: string]: number[] } = {}; };
const workflow = useWorkflowsStore().getCurrentWorkflow();
const activeNodeConections = (
workflow.connectionsByDestinationNode[this.activeNode?.name || ''] ?? {}
).main;
if (!activeNodeConections || activeNodeConections.length < 2) return returnData; const setNDVPanelDataIsEmpty = (params: {
panel: NodePanelType;
isEmpty: boolean;
}): void => {
if (params.panel === 'input') {
input.value.data.isEmpty = params.isEmpty;
} else {
output.value.data.isEmpty = params.isEmpty;
}
};
for (const [index, connection] of activeNodeConections.entries()) { const setMappingOnboarded = () => {
for (const node of connection) { isMappingOnboarded.value = true;
if (!returnData[node.node]) { localStorageMappingIsOnboarded.value = 'true';
returnData[node.node] = []; };
}
returnData[node.node].push(index + 1);
}
}
return returnData; const setTableHoverOnboarded = () => {
}, isTableHoverOnboarded.value = true;
}, localStorageTableHoverIsOnboarded.value = 'true';
actions: { };
setActiveNodeName(nodeName: string | null): void {
this.activeNodeName = nodeName; const setAutocompleteOnboarded = () => {
}, isAutocompleteOnboarded.value = true;
setInputNodeName(nodeName: string | undefined): void { localStorageAutoCompleteIsOnboarded.value = 'true';
this.input = { };
...this.input,
nodeName, const setHighlightDraggables = (highlight: boolean) => {
}; highlightDraggables.value = highlight;
}, };
setInputRunIndex(run?: number): void {
this.input = { const updateNodeParameterIssues = (issues: INodeIssues): void => {
...this.input, const activeNode = workflowsStore.getNodeByName(activeNodeName.value || '');
run,
}; if (activeNode) {
}, const nodeIndex = workflowsStore.workflow.nodes.findIndex((node) => {
setMainPanelDimensions(params: { return node.name === activeNode.name;
panelType: string; });
dimensions: { relativeLeft?: number; relativeRight?: number; relativeWidth?: number };
}): void { workflowsStore.updateNodeAtIndex(nodeIndex, {
this.mainPanelDimensions = { issues: {
...this.mainPanelDimensions, ...activeNode.issues,
[params.panelType]: { ...issues,
...this.mainPanelDimensions[params.panelType],
...params.dimensions,
}, },
}; });
}, }
setNDVPushRef(): void { };
this.pushRef = `ndv-${uuid()}`;
},
resetNDVPushRef(): void {
this.pushRef = '';
},
setPanelDisplayMode(params: { pane: NodePanelType; mode: IRunDataDisplayMode }): void {
this[params.pane].displayMode = params.mode;
},
setOutputPanelEditModeEnabled(isEnabled: boolean): void {
this.output.editMode.enabled = isEnabled;
},
setOutputPanelEditModeValue(payload: string): void {
this.output.editMode.value = payload;
},
setMappableNDVInputFocus(paramName: string): void {
this.focusedMappableInput = paramName;
},
draggableStartDragging({
type,
data,
dimensions,
}: {
type: string;
data: string;
dimensions: DOMRect | null;
}): void {
this.draggable = {
isDragging: true,
type,
data,
dimensions,
activeTarget: null,
};
},
draggableStopDragging(): void {
this.draggable = {
isDragging: false,
type: '',
data: '',
dimensions: null,
activeTarget: null,
};
},
setDraggableTarget(target: NDVState['draggable']['activeTarget']): void {
this.draggable.activeTarget = target;
},
setMappingTelemetry(telemetry: { [key: string]: string | number | boolean }): void {
this.mappingTelemetry = { ...this.mappingTelemetry, ...telemetry };
},
resetMappingTelemetry(): void {
this.mappingTelemetry = {};
},
setHoveringItem(item: null | NDVState['hoveringItem']): void {
if (item) this.setTableHoverOnboarded();
this.hoveringItem = item;
},
setNDVBranchIndex(e: { pane: 'input' | 'output'; branchIndex: number }): void {
this[e.pane].branch = e.branchIndex;
},
setNDVPanelDataIsEmpty(payload: { panel: 'input' | 'output'; isEmpty: boolean }): void {
this[payload.panel].data.isEmpty = payload.isEmpty;
},
setMappingOnboarded() {
this.isMappingOnboarded = true;
useStorage(LOCAL_STORAGE_MAPPING_IS_ONBOARDED).value = 'true';
},
setTableHoverOnboarded() {
this.isTableHoverOnboarded = true;
useStorage(LOCAL_STORAGE_TABLE_HOVER_IS_ONBOARDED).value = 'true';
},
setAutocompleteOnboarded() {
this.isAutocompleteOnboarded = true;
useStorage(LOCAL_STORAGE_AUTOCOMPLETE_IS_ONBOARDED).value = 'true';
},
setHighlightDraggables(highlight: boolean) {
this.highlightDraggables = highlight;
},
updateNodeParameterIssues(issues: INodeIssues): void {
const workflowsStore = useWorkflowsStore();
const activeNode = workflowsStore.getNodeByName(this.activeNodeName || '');
if (activeNode) { const setFocusedInputPath = (path: string) => {
const nodeIndex = workflowsStore.workflow.nodes.findIndex((node) => { focusedInputPath.value = path;
return node.name === activeNode.name; };
});
workflowsStore.updateNodeAtIndex(nodeIndex, { return {
issues: { activeNode,
...activeNode.issues, ndvInputData,
...issues, ndvInputNodeName,
}, ndvInputDataWithPinnedData,
}); hasInputData,
} inputPanelDisplayMode,
}, outputPanelDisplayMode,
setFocusedInputPath(path: string) { isDraggableDragging,
this.focusedInputPath = path; draggableType,
}, draggableData,
}, canDraggableDrop,
outputPanelEditMode,
draggableStickyPos,
ndvNodeInputNumber,
ndvInputRunIndex,
ndvInputBranchIndex,
isInputParentOfActiveNode,
getHoveringItem,
expressionTargetItem,
isNDVOpen,
isInputPanelEmpty,
isOutputPanelEmpty,
focusedMappableInput,
isMappingOnboarded,
pushRef,
activeNodeName,
focusedInputPath,
input,
output,
hoveringItem,
highlightDraggables,
mappingTelemetry,
draggable,
isAutocompleteOnboarded,
expressionOutputItemIndex,
isTableHoverOnboarded,
mainPanelDimensions,
setActiveNodeName,
setInputNodeName,
setInputRunIndex,
setMainPanelDimensions,
setNDVPushRef,
resetNDVPushRef,
setPanelDisplayMode,
setOutputPanelEditModeEnabled,
setOutputPanelEditModeValue,
setMappableNDVInputFocus,
draggableStartDragging,
draggableStopDragging,
setDraggableTarget,
setMappingTelemetry,
resetMappingTelemetry,
setHoveringItem,
setNDVBranchIndex,
setNDVPanelDataIsEmpty,
setMappingOnboarded,
setTableHoverOnboarded,
setAutocompleteOnboarded,
setHighlightDraggables,
updateNodeParameterIssues,
setFocusedInputPath,
};
}); });