109 lines
3.5 KiB
TypeScript
109 lines
3.5 KiB
TypeScript
// Canvas slice: selection, edit forms, context menu, left-panel filter inputs,
|
|
// position-draft reactive mirror.
|
|
|
|
import type { StateCreator } from "zustand";
|
|
|
|
import type { NodePositionDraft, ScheduleContextMenu, ScheduleForm, NodeForm } from "./types";
|
|
import { positionDrafts, clearPositionDrafts } from "./helpers";
|
|
import type { SchedulesStore } from "./useSchedulesStore";
|
|
|
|
// ---- State ----
|
|
|
|
export type CanvasSliceState = {
|
|
selectedNodeId: string | null;
|
|
selectedEdgeId: string | null;
|
|
linkSourceId: string | null;
|
|
scheduleKeyword: string;
|
|
artifactKeyword: string;
|
|
scheduleForm: ScheduleForm;
|
|
nodeForm: NodeForm;
|
|
contextMenu: ScheduleContextMenu | null;
|
|
positionDraftCount: number;
|
|
};
|
|
|
|
// ---- Actions ----
|
|
|
|
export type CanvasSliceActions = {
|
|
// pure setters
|
|
setSelectedNodeId: (id: string | null) => void;
|
|
setSelectedEdgeId: (id: string | null) => void;
|
|
setLinkSourceId: (id: string | null) => void;
|
|
setScheduleKeyword: (k: string) => void;
|
|
setArtifactKeyword: (k: string) => void;
|
|
setScheduleForm: (form: ScheduleForm) => void;
|
|
setNodeForm: (form: NodeForm) => void;
|
|
setContextMenu: (menu: ScheduleContextMenu | null) => void;
|
|
closeContextMenu: () => void;
|
|
|
|
// drag helpers
|
|
setPositionDraft: (nodeId: string, draft: NodePositionDraft) => void;
|
|
setPositionDraftCount: (count: number) => void;
|
|
getPositionDrafts: () => Map<string, NodePositionDraft>;
|
|
clearAllPositionDrafts: () => void;
|
|
prunePositionDrafts: (validNodeIds: Set<string>) => void;
|
|
};
|
|
|
|
// `SchedulesStore` is the combined state+actions of all slices, so `get()`
|
|
// returns a fully typed snapshot — no `any` propagation.
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
export const createCanvasSlice: StateCreator<SchedulesStore, [], [], CanvasSliceState & CanvasSliceActions> = (set, get) => ({
|
|
// ---- initial state ----
|
|
selectedNodeId: null,
|
|
selectedEdgeId: null,
|
|
linkSourceId: null,
|
|
scheduleKeyword: "",
|
|
artifactKeyword: "",
|
|
scheduleForm: {
|
|
scheduleName: "",
|
|
description: "",
|
|
triggerType: "manual",
|
|
cronExpression: "",
|
|
timezone: "Asia/Shanghai",
|
|
enabled: false,
|
|
maxConcurrency: "1",
|
|
failurePolicy: "stop",
|
|
},
|
|
nodeForm: {
|
|
nodeName: "",
|
|
timeoutSeconds: "600",
|
|
retryCount: "0",
|
|
retryIntervalSec: "5",
|
|
argumentsJson: "{}",
|
|
envRefsJson: "{}",
|
|
pythonVersion: "3.12",
|
|
},
|
|
contextMenu: null,
|
|
positionDraftCount: 0,
|
|
|
|
// ---- pure setters ----
|
|
|
|
setSelectedNodeId: (id) => set({ selectedNodeId: id }),
|
|
setSelectedEdgeId: (id) => set({ selectedEdgeId: id }),
|
|
setLinkSourceId: (id) => set({ linkSourceId: id }),
|
|
setScheduleKeyword: (k) => set({ scheduleKeyword: k }),
|
|
setArtifactKeyword: (k) => set({ artifactKeyword: k }),
|
|
setScheduleForm: (form) => set({ scheduleForm: form }),
|
|
setNodeForm: (form) => set({ nodeForm: form }),
|
|
setContextMenu: (menu) => set({ contextMenu: menu }),
|
|
closeContextMenu: () => set({ contextMenu: null }),
|
|
|
|
// ---- drag helpers ----
|
|
|
|
setPositionDraft: (nodeId, draft) => {
|
|
positionDrafts.set(nodeId, draft);
|
|
set({ positionDraftCount: positionDrafts.size });
|
|
},
|
|
setPositionDraftCount: (count) => set({ positionDraftCount: count }),
|
|
getPositionDrafts: () => positionDrafts,
|
|
clearAllPositionDrafts: () => {
|
|
clearPositionDrafts();
|
|
set({ positionDraftCount: 0 });
|
|
},
|
|
prunePositionDrafts: (validNodeIds) => {
|
|
for (const nodeId of [...positionDrafts.keys()]) {
|
|
if (!validNodeIds.has(nodeId)) positionDrafts.delete(nodeId);
|
|
}
|
|
set({ positionDraftCount: positionDrafts.size });
|
|
},
|
|
});
|