refactor: SchedulePage.tsx
This commit is contained in:
@@ -2,7 +2,6 @@ import {
|
||||
type DragEvent,
|
||||
type FormEvent,
|
||||
type MouseEvent as ReactMouseEvent,
|
||||
type PointerEvent as ReactPointerEvent,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
@@ -24,6 +23,8 @@ import { ScheduleInspector } from "./ScheduleInspector";
|
||||
import { ScheduleList } from "./ScheduleList";
|
||||
import { ArtifactList } from "./ArtifactList";
|
||||
import { ScheduleCanvasHeader } from "./ScheduleCanvasHeader";
|
||||
import { useCanvasNodeDrag } from "./hooks/useCanvasNodeDrag";
|
||||
import { useContextMenuDismiss } from "./hooks/useContextMenuDismiss";
|
||||
import {
|
||||
EMPTY_NODE_FORM,
|
||||
type ScheduleContextMenu,
|
||||
@@ -37,16 +38,6 @@ type Notice = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
type DragState = {
|
||||
nodeId: string;
|
||||
pointerId: number;
|
||||
startClientX: number;
|
||||
startClientY: number;
|
||||
originX: number;
|
||||
originY: number;
|
||||
moved: boolean;
|
||||
};
|
||||
|
||||
type ScheduleContextMenuTarget =
|
||||
| { kind: "schedule-list" }
|
||||
| { kind: "schedule"; schedule: Schedule }
|
||||
@@ -110,7 +101,6 @@ export default function SchedulePage({
|
||||
const runsLoading = useSchedulesStore((s) => s.runsLoading);
|
||||
const setRunsLoading = useSchedulesStore((s) => s.setRunsLoading);
|
||||
const canvasRef = useRef<HTMLDivElement | null>(null);
|
||||
const dragRef = useRef<DragState | null>(null);
|
||||
const positionDraftCount = useSchedulesStore((state) => state.positionDraftCount);
|
||||
|
||||
const selectedNode = schedule?.nodes.find(
|
||||
@@ -128,26 +118,7 @@ export default function SchedulePage({
|
||||
setNodeForm(selectedNode ? nodeToForm(selectedNode) : EMPTY_NODE_FORM);
|
||||
}, [selectedNode?.node_id, selectedNode?.updated_at]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!contextMenu) return undefined;
|
||||
const close = (): void => setContextMenu(null);
|
||||
const onKeyDown = (event: KeyboardEvent): void => {
|
||||
if (event.key === "Escape") close();
|
||||
};
|
||||
window.addEventListener("pointerdown", close);
|
||||
window.addEventListener("blur", close);
|
||||
window.addEventListener("resize", close);
|
||||
window.addEventListener("scroll", close, true);
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
return () => {
|
||||
window.removeEventListener("pointerdown", close);
|
||||
window.removeEventListener("blur", close);
|
||||
window.removeEventListener("resize", close);
|
||||
window.removeEventListener("scroll", close, true);
|
||||
window.removeEventListener("keydown", onKeyDown);
|
||||
};
|
||||
}, [contextMenu]);
|
||||
|
||||
useContextMenuDismiss();
|
||||
|
||||
const openContextMenu = (
|
||||
event: ReactMouseEvent<HTMLElement | SVGElement>,
|
||||
@@ -204,77 +175,7 @@ export default function SchedulePage({
|
||||
void useSchedulesStore.getState().addArtifactAt(artifact, positionX, positionY);
|
||||
};
|
||||
|
||||
const startNodeDrag = (
|
||||
event: ReactPointerEvent<HTMLDivElement>,
|
||||
node: ScheduleNode,
|
||||
): void => {
|
||||
if (event.button !== 0 || busy || linkSourceId) return;
|
||||
const target = event.target as HTMLElement;
|
||||
if (target.closest("button")) return;
|
||||
event.currentTarget.setPointerCapture(event.pointerId);
|
||||
dragRef.current = {
|
||||
nodeId: node.node_id,
|
||||
pointerId: event.pointerId,
|
||||
startClientX: event.clientX,
|
||||
startClientY: event.clientY,
|
||||
originX: node.position_x,
|
||||
originY: node.position_y,
|
||||
moved: false,
|
||||
};
|
||||
setSelectedNodeId(node.node_id);
|
||||
setSelectedEdgeId(null);
|
||||
};
|
||||
|
||||
const moveNode = (
|
||||
event: ReactPointerEvent<HTMLDivElement>,
|
||||
): void => {
|
||||
const drag = dragRef.current;
|
||||
if (!drag || drag.pointerId !== event.pointerId) return;
|
||||
const deltaX = event.clientX - drag.startClientX;
|
||||
const deltaY = event.clientY - drag.startClientY;
|
||||
if (Math.abs(deltaX) + Math.abs(deltaY) > 3) drag.moved = true;
|
||||
setSchedule((current) => {
|
||||
if (!current) return current;
|
||||
return {
|
||||
...current,
|
||||
nodes: current.nodes.map((item) => (
|
||||
item.node_id === drag.nodeId
|
||||
? {
|
||||
...item,
|
||||
position_x: Math.max(10, drag.originX + deltaX),
|
||||
position_y: Math.max(10, drag.originY + deltaY),
|
||||
}
|
||||
: item
|
||||
)),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const finishNodeDrag = (
|
||||
event: ReactPointerEvent<HTMLDivElement>,
|
||||
): void => {
|
||||
const drag = dragRef.current;
|
||||
if (!drag || drag.pointerId !== event.pointerId) return;
|
||||
dragRef.current = null;
|
||||
if (!drag.moved) return;
|
||||
const current = useSchedulesStore.getState().schedule;
|
||||
const node = current?.nodes.find((item) => item.node_id === drag.nodeId);
|
||||
if (!current || !node) return;
|
||||
const position = {
|
||||
position_x: Math.round(node.position_x),
|
||||
position_y: Math.round(node.position_y),
|
||||
};
|
||||
useSchedulesStore.getState().setPositionDraft(node.node_id, position);
|
||||
setSchedule((value) => {
|
||||
if (!value) return value;
|
||||
return {
|
||||
...value,
|
||||
nodes: value.nodes.map((item) => (
|
||||
item.node_id === node.node_id ? { ...item, ...position } : item
|
||||
)),
|
||||
};
|
||||
});
|
||||
};
|
||||
const { startDrag, moveDrag, finishDrag } = useCanvasNodeDrag({ busy, linkSourceId });
|
||||
|
||||
const filteredSchedules = useMemo(() => {
|
||||
const keyword = scheduleKeyword.trim().toLowerCase();
|
||||
@@ -466,10 +367,10 @@ export default function SchedulePage({
|
||||
width: NODE_WIDTH,
|
||||
height: NODE_HEIGHT,
|
||||
}}
|
||||
onPointerDown={(event) => startNodeDrag(event, node)}
|
||||
onPointerMove={moveNode}
|
||||
onPointerUp={finishNodeDrag}
|
||||
onPointerCancel={finishNodeDrag}
|
||||
onPointerDown={(event) => startDrag(event, node)}
|
||||
onPointerMove={moveDrag}
|
||||
onPointerUp={finishDrag}
|
||||
onPointerCancel={finishDrag}
|
||||
onContextMenu={(event) => openContextMenu(event, {
|
||||
kind: "node",
|
||||
node,
|
||||
|
||||
Reference in New Issue
Block a user