fix(editor): Align undo/redo functionality on new canvas (no-changelog) (#11154)

This commit is contained in:
Alex Grozav 2024-10-08 16:41:54 +03:00 committed by GitHub
parent 18c0c8612c
commit ad4cb0ea0c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 64 additions and 60 deletions

View file

@ -87,6 +87,8 @@ describe('Canvas', () => {
});
it('should handle `update:nodes:position` event', async () => {
vi.useFakeTimers();
const nodes = [createCanvasNodeElement()];
const { container, emitted } = renderComponent({
props: {
@ -110,22 +112,9 @@ describe('Canvas', () => {
});
await fireEvent.mouseUp(node, { view: window });
vi.advanceTimersByTime(250); // Event debounce time
expect(emitted()['update:nodes:position']).toEqual([
[
[
{
id: '1',
type: 'position',
dragging: true,
from: {
x: 100,
y: 100,
z: 0,
},
position: { x: 80, y: 80 },
},
],
],
[
[
{

View file

@ -29,7 +29,7 @@ import type { PinDataSource } from '@/composables/usePinnedData';
import { isPresent } from '@/utils/typesUtils';
import { GRID_SIZE } from '@/utils/nodeViewUtils';
import { CanvasKey } from '@/constants';
import { onKeyDown, onKeyUp } from '@vueuse/core';
import { onKeyDown, onKeyUp, useDebounceFn } from '@vueuse/core';
import CanvasArrowHeadMarker from './elements/edges/CanvasArrowHeadMarker.vue';
const $style = useCssModule();
@ -180,21 +180,22 @@ function onClickNodeAdd(id: string, handle: string) {
emit('click:node:add', id, handle);
}
function onUpdateNodesPosition(events: NodePositionChange[]) {
// Debounced to prevent emitting too many events, necessary for undo/redo
const onUpdateNodesPosition = useDebounceFn((events: NodePositionChange[]) => {
emit('update:nodes:position', events);
}
}, 200);
function onUpdateNodePosition(id: string, position: XYPosition) {
emit('update:node:position', id, position);
}
function onNodesChange(events: NodeChange[]) {
const isPositionChangeEvent = (event: NodeChange): event is NodePositionChange =>
event.type === 'position' && 'position' in event;
const isPositionChangeEvent = (event: NodeChange): event is NodePositionChange =>
event.type === 'position' && 'position' in event;
function onNodesChange(events: NodeChange[]) {
const positionChangeEndEvents = events.filter(isPositionChangeEvent);
if (positionChangeEndEvents.length > 0) {
onUpdateNodesPosition(positionChangeEndEvents);
void onUpdateNodesPosition(positionChangeEndEvents);
}
}

View file

@ -164,7 +164,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
updateNodePosition(id, position, { trackHistory });
});
if (trackBulk) {
if (trackHistory && trackBulk) {
historyStore.stopRecordingUndo();
}
}
@ -198,11 +198,16 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
updateNodePosition(node.id, position);
}
async function renameNode(currentName: string, newName: string, { trackHistory = false } = {}) {
async function renameNode(
currentName: string,
newName: string,
{ trackHistory = false, trackBulk = true } = {},
) {
if (currentName === newName) {
return;
}
if (trackHistory) {
if (trackHistory && trackBulk) {
historyStore.startRecordingUndo();
}
@ -227,7 +232,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
ndvStore.activeNodeName = newName;
}
if (trackHistory) {
if (trackHistory && trackBulk) {
historyStore.stopRecordingUndo();
}
}
@ -327,10 +332,16 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
trackDeleteNode(id);
}
function deleteNodes(ids: string[]) {
historyStore.startRecordingUndo();
ids.forEach((id) => deleteNode(id, { trackHistory: true, trackBulk: false }));
historyStore.stopRecordingUndo();
function deleteNodes(ids: string[], { trackHistory = true, trackBulk = true } = {}) {
if (trackHistory && trackBulk) {
historyStore.startRecordingUndo();
}
ids.forEach((id) => deleteNode(id, { trackHistory, trackBulk: false }));
if (trackHistory && trackBulk) {
historyStore.stopRecordingUndo();
}
}
function revertDeleteNode(node: INodeUi) {
@ -401,18 +412,15 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
uiStore.lastSelectedNode = node.name;
}
function toggleNodesDisabled(
ids: string[],
{ trackHistory = true, trackBulk = true }: { trackHistory?: boolean; trackBulk?: boolean } = {},
) {
if (trackBulk) {
function toggleNodesDisabled(ids: string[], { trackHistory = true, trackBulk = true } = {}) {
if (trackHistory && trackBulk) {
historyStore.startRecordingUndo();
}
const nodes = workflowsStore.getNodesByIds(ids);
nodeHelpers.disableNodes(nodes, trackHistory);
nodeHelpers.disableNodes(nodes, { trackHistory, trackBulk: false });
if (trackBulk) {
if (trackHistory && trackBulk) {
historyStore.stopRecordingUndo();
}
}
@ -424,8 +432,14 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
}
}
function toggleNodesPinned(ids: string[], source: PinDataSource) {
historyStore.startRecordingUndo();
function toggleNodesPinned(
ids: string[],
source: PinDataSource,
{ trackHistory = true, trackBulk = true } = {},
) {
if (trackHistory && trackBulk) {
historyStore.startRecordingUndo();
}
const nodes = workflowsStore.getNodesByIds(ids);
const nextStatePinned = nodes.some((node) => !workflowsStore.pinDataByNodeName(node.name));
@ -442,7 +456,9 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
}
}
historyStore.stopRecordingUndo();
if (trackHistory && trackBulk) {
historyStore.stopRecordingUndo();
}
}
function requireNodeTypeDescription(
@ -478,10 +494,6 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
let insertPosition = options.position;
let lastAddedNode: INodeUi | undefined;
if (options.trackBulk) {
historyStore.startRecordingUndo();
}
const nodesWithTypeVersion = nodes.map((node) => {
const typeVersion =
node.typeVersion ?? resolveNodeVersion(requireNodeTypeDescription(node.type));
@ -493,7 +505,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
await loadNodeTypesProperties(nodesWithTypeVersion);
if (options.trackBulk) {
if (options.trackHistory && options.trackBulk) {
historyStore.startRecordingUndo();
}
@ -532,7 +544,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
updatePositionForNodeWithMultipleInputs(lastAddedNode);
}
if (options.trackBulk) {
if (options.trackHistory && options.trackBulk) {
historyStore.stopRecordingUndo();
}
@ -588,11 +600,11 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
workflowsStore.addNode(nodeData);
void nextTick(() => {
if (options.trackHistory) {
historyStore.pushCommandToUndo(new AddNodeCommand(nodeData));
}
if (options.trackHistory) {
historyStore.pushCommandToUndo(new AddNodeCommand(nodeData));
}
void nextTick(() => {
workflowsStore.setNodePristine(nodeData.name, true);
nodeHelpers.matchCredentials(nodeData);
@ -1376,7 +1388,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
connections: CanvasConnectionCreateData[] | CanvasConnection[],
{ trackBulk = true, trackHistory = false } = {},
) {
if (trackBulk) {
if (trackBulk && trackHistory) {
historyStore.startRecordingUndo();
}
@ -1404,7 +1416,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
);
}
if (trackBulk) {
if (trackBulk && trackHistory) {
historyStore.stopRecordingUndo();
}
}
@ -1614,11 +1626,11 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
}
// Add the nodes with the changed node names, expressions and connections
if (trackBulk) {
if (trackBulk && trackHistory) {
historyStore.startRecordingUndo();
}
await addNodes(Object.values(tempWorkflow.nodes), { trackBulk: false, trackHistory: true });
await addNodes(Object.values(tempWorkflow.nodes), { trackBulk: false, trackHistory });
addConnections(
mapLegacyConnectionsToCanvasConnections(
tempWorkflow.connectionsBySourceNode,
@ -1627,7 +1639,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
{ trackBulk: false, trackHistory },
);
if (trackBulk) {
if (trackBulk && trackHistory) {
historyStore.stopRecordingUndo();
}
@ -1643,6 +1655,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
workflowData: IWorkflowDataUpdate,
source: string,
importTags = true,
{ trackBulk = true, trackHistory = true } = {},
): Promise<IWorkflowDataUpdate> {
uiStore.resetLastInteractedWith();
@ -1731,7 +1744,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
NodeViewUtils.getNewNodePosition(editableWorkflow.value.nodes, lastClickPosition.value),
);
await addImportedNodesToWorkflow(workflowData);
await addImportedNodesToWorkflow(workflowData, { trackBulk, trackHistory });
if (importTags && settingsStore.areTagsEnabled && Array.isArray(workflowData.tags)) {
await importWorkflowTags(workflowData);

View file

@ -652,10 +652,10 @@ export function useNodeHelpers() {
return returnData;
}
function disableNodes(nodes: INodeUi[], trackHistory = false) {
function disableNodes(nodes: INodeUi[], { trackHistory = false, trackBulk = true } = {}) {
const telemetry = useTelemetry();
if (trackHistory) {
if (trackHistory && trackBulk) {
historyStore.startRecordingUndo();
}
@ -690,7 +690,8 @@ export function useNodeHelpers() {
);
}
}
if (trackHistory) {
if (trackHistory && trackBulk) {
historyStore.stopRecordingUndo();
}
}

View file

@ -1534,7 +1534,7 @@ export default defineComponent({
return;
}
this.nodeHelpers.disableNodes(nodes, true);
this.nodeHelpers.disableNodes(nodes, { trackHistory: true, trackBulk: true });
},
togglePinNodes(nodes: INode[], source: 'keyboard-shortcut' | 'context-menu') {