seat-editor 3.4.8 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/constant.d.ts +1 -1
- package/dist/app/graph-view/page.d.ts +1 -0
- package/dist/app/graph-view/page.js +343 -0
- package/dist/app/graph-view/page.jsx +445 -0
- package/dist/app/graph-view-new/constant.d.ts +581 -0
- package/dist/app/graph-view-new/constant.js +6973 -0
- package/dist/app/graph-view-new/page.d.ts +1 -0
- package/dist/app/graph-view-new/page.js +71 -0
- package/dist/app/graph-view-new/page.jsx +98 -0
- package/dist/app/layout.d.ts +1 -1
- package/dist/app/new-board/page.d.ts +1 -1
- package/dist/app/new-board/page.js +43 -7
- package/dist/app/new-board/page.jsx +45 -12
- package/dist/app/old-board/page.d.ts +1 -2
- package/dist/app/only-view/chair.d.ts +1 -1
- package/dist/app/only-view/chair.js +2 -10
- package/dist/app/only-view/page.d.ts +1 -1
- package/dist/app/only-view/user.d.ts +1 -1
- package/dist/app/only-view/user.js +2 -10
- package/dist/app/page.d.ts +1 -1
- package/dist/app/test/page.d.ts +1 -2
- package/dist/app/v2/page.d.ts +1 -1
- package/dist/components/button-tools/index.d.ts +1 -1
- package/dist/components/button-tools/index.js +7 -5
- package/dist/components/button-tools/index.jsx +21 -9
- package/dist/components/form-tools/label.d.ts +1 -1
- package/dist/components/form-tools/label.js +9 -20
- package/dist/components/form-tools/label.jsx +38 -28
- package/dist/components/form-tools/shape.d.ts +1 -1
- package/dist/components/form-tools/shape.js +5 -5
- package/dist/components/form-tools/shape.jsx +8 -8
- package/dist/components/input/number-indicator.d.ts +1 -1
- package/dist/components/joystick/index.d.ts +1 -2
- package/dist/components/layer/index.d.ts +1 -1
- package/dist/components/layer-v2/index.d.ts +1 -1
- package/dist/components/layer-v3/index.d.ts +1 -1
- package/dist/components/layer-v3/index.js +44 -3
- package/dist/components/layer-v3/index.jsx +120 -3
- package/dist/components/layer-v4/index.d.ts +1 -1
- package/dist/components/layer-v5/constant.d.ts +60 -0
- package/dist/components/layer-v5/constant.js +93 -0
- package/dist/components/layer-v5/index.d.ts +24 -0
- package/dist/components/layer-v5/index.js +927 -0
- package/dist/components/layer-v5/index.jsx +1049 -0
- package/dist/components/lib/index.d.ts +1 -1
- package/dist/components/modal-preview/index.d.ts +1 -1
- package/dist/features/board/index.d.ts +1 -1
- package/dist/features/board-v2/index.d.ts +1 -2
- package/dist/features/board-v3/index.d.ts +1 -1
- package/dist/features/board-v3/index.js +350 -72
- package/dist/features/board-v3/index.jsx +369 -75
- package/dist/features/board-v3/resize-element.js +5 -0
- package/dist/features/board-v3/utils.d.ts +8 -0
- package/dist/features/board-v3/utils.js +23 -7
- package/dist/features/navbar/index.d.ts +1 -1
- package/dist/features/package/index.d.ts +3 -1
- package/dist/features/package/index.js +1 -1
- package/dist/features/package/index.jsx +6 -1
- package/dist/features/panel/index.d.ts +9 -1
- package/dist/features/panel/index.js +160 -38
- package/dist/features/panel/index.jsx +173 -46
- package/dist/features/panel/polygon.d.ts +2 -0
- package/dist/features/panel/polygon.js +44 -0
- package/dist/features/panel/polygon.jsx +70 -0
- package/dist/features/panel/select-tool.d.ts +1 -1
- package/dist/features/panel/select-tool.js +3 -0
- package/dist/features/panel/select-tool.jsx +3 -0
- package/dist/features/panel/selected-group.d.ts +1 -1
- package/dist/features/panel/selected-group.js +24 -26
- package/dist/features/panel/selected-group.jsx +56 -51
- package/dist/features/panel/square-circle-tool.d.ts +1 -1
- package/dist/features/panel/table-seat-circle.d.ts +1 -1
- package/dist/features/panel/table-seat-square.d.ts +1 -1
- package/dist/features/panel/text-tool.d.ts +1 -1
- package/dist/features/panel/text-tool.js +17 -2
- package/dist/features/panel/text-tool.jsx +19 -2
- package/dist/features/panel/upload-tool.d.ts +1 -1
- package/dist/features/panel/upload-tool.js +17 -3
- package/dist/features/panel/upload-tool.jsx +23 -4
- package/dist/features/side-tool/index.d.ts +1 -1
- package/dist/features/side-tool/index.js +43 -6
- package/dist/features/side-tool/index.jsx +47 -10
- package/dist/features/view-only/index.d.ts +1 -1
- package/dist/features/view-only-2/index.d.ts +1 -1
- package/dist/features/view-only-3/index.d.ts +1 -1
- package/dist/features/view-only-4/connect-handle.d.ts +13 -0
- package/dist/features/view-only-4/connect-handle.js +23 -0
- package/dist/features/view-only-4/connect-handle.jsx +30 -0
- package/dist/features/view-only-4/connection-layer.d.ts +21 -0
- package/dist/features/view-only-4/connection-layer.js +219 -0
- package/dist/features/view-only-4/connection-layer.jsx +291 -0
- package/dist/features/view-only-4/index.d.ts +99 -0
- package/dist/features/view-only-4/index.js +684 -0
- package/dist/features/view-only-4/index.jsx +722 -0
- package/dist/features/view-only-4/integration-guide.d.ts +0 -0
- package/dist/features/view-only-4/integration-guide.js +0 -0
- package/dist/features/view-only-4/use-connection-graph.d.ts +41 -0
- package/dist/features/view-only-4/use-connection-graph.js +182 -0
- package/dist/features/view-only-4/utils.d.ts +74 -0
- package/dist/features/view-only-4/utils.js +106 -0
- package/dist/features/view-only-5/connect-handle.d.ts +30 -0
- package/dist/features/view-only-5/connect-handle.js +88 -0
- package/dist/features/view-only-5/connect-handle.jsx +96 -0
- package/dist/features/view-only-5/connection-layer.d.ts +34 -0
- package/dist/features/view-only-5/connection-layer.js +182 -0
- package/dist/features/view-only-5/connection-layer.jsx +265 -0
- package/dist/features/view-only-5/index.d.ts +102 -0
- package/dist/features/view-only-5/index.js +585 -0
- package/dist/features/view-only-5/index.jsx +614 -0
- package/dist/features/view-only-5/use-connection-graph.d.ts +57 -0
- package/dist/features/view-only-5/use-connection-graph.js +196 -0
- package/dist/features/view-only-5/utils.d.ts +52 -0
- package/dist/features/view-only-5/utils.js +80 -0
- package/dist/features/view-only-6/connect-handle.d.ts +13 -0
- package/dist/features/view-only-6/connect-handle.js +20 -0
- package/dist/features/view-only-6/connect-handle.jsx +21 -0
- package/dist/features/view-only-6/connection-layer.d.ts +22 -0
- package/dist/features/view-only-6/connection-layer.js +191 -0
- package/dist/features/view-only-6/connection-layer.jsx +244 -0
- package/dist/features/view-only-6/index.d.ts +99 -0
- package/dist/features/view-only-6/index.js +687 -0
- package/dist/features/view-only-6/index.jsx +724 -0
- package/dist/features/view-only-6/use-connection-graph.d.ts +26 -0
- package/dist/features/view-only-6/use-connection-graph.js +103 -0
- package/dist/features/view-only-6/utils.d.ts +66 -0
- package/dist/features/view-only-6/utils.js +96 -0
- package/dist/features/view-only-7/connect-handle.d.ts +13 -0
- package/dist/features/view-only-7/connect-handle.js +23 -0
- package/dist/features/view-only-7/connect-handle.jsx +30 -0
- package/dist/features/view-only-7/connection-layer.d.ts +22 -0
- package/dist/features/view-only-7/connection-layer.js +165 -0
- package/dist/features/view-only-7/connection-layer.jsx +217 -0
- package/dist/features/view-only-7/index.d.ts +99 -0
- package/dist/features/view-only-7/index.js +687 -0
- package/dist/features/view-only-7/index.jsx +724 -0
- package/dist/features/view-only-7/use-connection-graph.d.ts +26 -0
- package/dist/features/view-only-7/use-connection-graph.js +104 -0
- package/dist/features/view-only-7/utils.d.ts +69 -0
- package/dist/features/view-only-7/utils.js +144 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/provider/redux-provider.d.ts +1 -1
- package/dist/provider/store-provider.d.ts +1 -1
- package/dist/seat-editor.css +1 -1
- package/package.json +1 -1
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { EdgeType, NodeType } from "./utils";
|
|
2
|
+
import { PropertiesProps } from "@/dto/table";
|
|
3
|
+
import { TableMatchKey } from ".";
|
|
4
|
+
export type UseConnectionGraphOptions = {
|
|
5
|
+
svgRef: React.RefObject<SVGSVGElement>;
|
|
6
|
+
/**
|
|
7
|
+
* Fungsi untuk convert komponen dari store ke NodeType.
|
|
8
|
+
* Karena node kamu bukan node biasa tapi komponen dari board,
|
|
9
|
+
* kamu perlu provide ini untuk mapping id -> posisi
|
|
10
|
+
*/
|
|
11
|
+
getNodeById: (id: string) => NodeType | null;
|
|
12
|
+
onEdgesChange?: (edges: EdgeType[], table: PropertiesProps[]) => void;
|
|
13
|
+
keyNode?: string;
|
|
14
|
+
mappingKey?: string;
|
|
15
|
+
tableMatchKey?: TableMatchKey[];
|
|
16
|
+
statusKey?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const useConnectionGraph: ({ svgRef, getNodeById, onEdgesChange, keyNode, mappingKey, tableMatchKey, statusKey, }: UseConnectionGraphOptions) => {
|
|
19
|
+
edges: EdgeType[];
|
|
20
|
+
setEdges: import("react").Dispatch<import("react").SetStateAction<EdgeType[]>>;
|
|
21
|
+
selectedEdge: string;
|
|
22
|
+
connecting: {
|
|
23
|
+
fromId: string;
|
|
24
|
+
};
|
|
25
|
+
mousePos: {
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
};
|
|
29
|
+
startConnect: (fromId: string) => void;
|
|
30
|
+
endConnect: (toId: string) => void;
|
|
31
|
+
cancelConnect: () => void;
|
|
32
|
+
startDragWaypoint: (e: React.MouseEvent, edgeId: string, index: number) => void;
|
|
33
|
+
insertWaypoint: (e: React.MouseEvent, edgeId: string, insertIndex: number, x: number, y: number) => void;
|
|
34
|
+
removeWaypoint: (e: React.MouseEvent, edgeId: string, index: number) => void;
|
|
35
|
+
selectEdge: (edgeId: string) => void;
|
|
36
|
+
deleteEdge: (edgeId: string) => void;
|
|
37
|
+
deleteSelectedEdge: () => void;
|
|
38
|
+
clearSelection: () => void;
|
|
39
|
+
onMouseMove: (e: React.MouseEvent<SVGSVGElement> | MouseEvent) => void;
|
|
40
|
+
onMouseUp: () => void;
|
|
41
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// hooks/use-connection-graph.ts
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { defaultWaypoints, getRectEdge, snap, } from "./utils"; // sesuaikan path
|
|
4
|
+
import { useAppSelector } from "@/hooks/use-redux";
|
|
5
|
+
export const useConnectionGraph = ({ svgRef, getNodeById, onEdgesChange, keyNode, mappingKey, tableMatchKey, statusKey, }) => {
|
|
6
|
+
const components = useAppSelector((state) => state.board.components);
|
|
7
|
+
const [selectedWaypoint, setSelectedWaypoint] = useState(null);
|
|
8
|
+
const [edges, setEdges] = useState([]);
|
|
9
|
+
const [selectedEdge, setSelectedEdge] = useState(null);
|
|
10
|
+
const [connecting, setConnecting] = useState(null);
|
|
11
|
+
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
|
|
12
|
+
// Dragging waypoint state
|
|
13
|
+
const draggingWP = useRef(null);
|
|
14
|
+
// Convert client coords -> SVG coords (dengan transform matrix)
|
|
15
|
+
const getSVGPoint = useCallback((clientX, clientY) => {
|
|
16
|
+
var _a;
|
|
17
|
+
const svg = svgRef.current;
|
|
18
|
+
if (!svg)
|
|
19
|
+
return { x: 0, y: 0 };
|
|
20
|
+
const pt = svg.createSVGPoint();
|
|
21
|
+
pt.x = clientX;
|
|
22
|
+
pt.y = clientY;
|
|
23
|
+
const svgP = pt.matrixTransform((_a = svg.getScreenCTM()) === null || _a === void 0 ? void 0 : _a.inverse());
|
|
24
|
+
return { x: svgP.x, y: svgP.y };
|
|
25
|
+
}, [svgRef]);
|
|
26
|
+
// ─── Connect ─────────────────────────────────────────────────────────────
|
|
27
|
+
const startConnect = useCallback((fromId) => {
|
|
28
|
+
setConnecting({ fromId });
|
|
29
|
+
setSelectedEdge(null);
|
|
30
|
+
}, []);
|
|
31
|
+
const endConnect = useCallback((toId) => {
|
|
32
|
+
if (!connecting || connecting.fromId === toId) {
|
|
33
|
+
setConnecting(null);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const fromNode = getNodeById(connecting.fromId);
|
|
37
|
+
const toNode = getNodeById(toId);
|
|
38
|
+
if (!fromNode || !toNode) {
|
|
39
|
+
setConnecting(null);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Cegah duplikat
|
|
43
|
+
setEdges((prev) => {
|
|
44
|
+
const exists = prev.find((e) => (e.from === connecting.fromId && e.to === toId) ||
|
|
45
|
+
(e.from === toId && e.to === connecting.fromId));
|
|
46
|
+
if (exists)
|
|
47
|
+
return prev;
|
|
48
|
+
const start = getRectEdge(fromNode, toNode, fromNode.width, fromNode.height);
|
|
49
|
+
const end = getRectEdge(toNode, fromNode, toNode.width, toNode.height);
|
|
50
|
+
return [
|
|
51
|
+
...prev,
|
|
52
|
+
{
|
|
53
|
+
id: `edge-${Date.now()}`,
|
|
54
|
+
from: connecting.fromId,
|
|
55
|
+
to: toId,
|
|
56
|
+
waypoints: defaultWaypoints(start, end),
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
});
|
|
60
|
+
setConnecting(null);
|
|
61
|
+
}, [connecting, getNodeById]);
|
|
62
|
+
const cancelConnect = useCallback(() => setConnecting(null), []);
|
|
63
|
+
// ─── Mouse move (track mouse + drag waypoint) ─────────────────────────────
|
|
64
|
+
const onMouseMove = useCallback((e) => {
|
|
65
|
+
const clientX = "clientX" in e ? e.clientX : 0;
|
|
66
|
+
const clientY = "clientY" in e ? e.clientY : 0;
|
|
67
|
+
const svgPt = getSVGPoint(clientX, clientY);
|
|
68
|
+
setMousePos(svgPt);
|
|
69
|
+
if (draggingWP.current) {
|
|
70
|
+
const { edgeId, index } = draggingWP.current;
|
|
71
|
+
setEdges((prev) => prev.map((ed) => {
|
|
72
|
+
if (ed.id !== edgeId)
|
|
73
|
+
return ed;
|
|
74
|
+
return Object.assign(Object.assign({}, ed), { waypoints: ed.waypoints.map((wp, i) => i === index ? { x: snap(svgPt.x), y: snap(svgPt.y) } : wp) });
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
}, [getSVGPoint]);
|
|
78
|
+
const onMouseUp = useCallback(() => {
|
|
79
|
+
draggingWP.current = null;
|
|
80
|
+
}, []);
|
|
81
|
+
// ─── Waypoint drag ────────────────────────────────────────────────────────
|
|
82
|
+
const startDragWaypoint = useCallback((e, edgeId, index) => {
|
|
83
|
+
e.stopPropagation();
|
|
84
|
+
draggingWP.current = { edgeId, index };
|
|
85
|
+
setSelectedEdge(edgeId);
|
|
86
|
+
}, []);
|
|
87
|
+
// Insert waypoint di midpoint
|
|
88
|
+
const insertWaypoint = useCallback((e, edgeId, insertIndex, x, y) => {
|
|
89
|
+
e.stopPropagation();
|
|
90
|
+
setEdges((prev) => prev.map((ed) => {
|
|
91
|
+
if (ed.id !== edgeId)
|
|
92
|
+
return ed;
|
|
93
|
+
const newWP = [...ed.waypoints];
|
|
94
|
+
newWP.splice(insertIndex, 0, { x: snap(x), y: snap(y) });
|
|
95
|
+
return Object.assign(Object.assign({}, ed), { waypoints: newWP });
|
|
96
|
+
}));
|
|
97
|
+
// Langsung set drag ke waypoint baru
|
|
98
|
+
draggingWP.current = { edgeId, index: insertIndex };
|
|
99
|
+
}, []);
|
|
100
|
+
// Remove waypoint kanan klik
|
|
101
|
+
const removeWaypoint = useCallback((e, edgeId, index) => {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
e.stopPropagation();
|
|
104
|
+
setEdges((prev) => prev.map((ed) => {
|
|
105
|
+
if (ed.id !== edgeId)
|
|
106
|
+
return ed;
|
|
107
|
+
if (ed.waypoints.length <= 1)
|
|
108
|
+
return ed;
|
|
109
|
+
return Object.assign(Object.assign({}, ed), { waypoints: ed.waypoints.filter((_, i) => i !== index) });
|
|
110
|
+
}));
|
|
111
|
+
}, []);
|
|
112
|
+
// ─── Edge select / delete ─────────────────────────────────────────────────
|
|
113
|
+
const selectEdge = useCallback((edgeId) => {
|
|
114
|
+
setSelectedEdge(edgeId);
|
|
115
|
+
}, []);
|
|
116
|
+
const deleteEdge = useCallback((edgeId) => {
|
|
117
|
+
setEdges((prev) => prev.filter((e) => e.id !== edgeId));
|
|
118
|
+
setSelectedEdge(null);
|
|
119
|
+
}, []);
|
|
120
|
+
const deleteSelectedEdge = useCallback(() => {
|
|
121
|
+
if (selectedEdge)
|
|
122
|
+
deleteEdge(selectedEdge);
|
|
123
|
+
}, [selectedEdge, deleteEdge]);
|
|
124
|
+
const clearSelection = useCallback(() => {
|
|
125
|
+
setSelectedEdge(null);
|
|
126
|
+
}, []);
|
|
127
|
+
// Tambahkan di dalam useConnectionGraph hook
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
const handleKeyDown = (e) => {
|
|
130
|
+
if (e.key === "Delete" || e.key === "Backspace") {
|
|
131
|
+
// Pastikan user tidak sedang ketik di input/textarea
|
|
132
|
+
const tag = e.target.tagName.toLowerCase();
|
|
133
|
+
if (tag === "input" || tag === "textarea")
|
|
134
|
+
return;
|
|
135
|
+
if (selectedEdge) {
|
|
136
|
+
deleteSelectedEdge();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (e.key === "Escape") {
|
|
140
|
+
cancelConnect();
|
|
141
|
+
clearSelection();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
145
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
146
|
+
}, [selectedEdge, deleteSelectedEdge, cancelConnect, clearSelection]);
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
const sourceToTargets = new Map();
|
|
149
|
+
edges.forEach((e) => {
|
|
150
|
+
if (!sourceToTargets.has(e.from))
|
|
151
|
+
sourceToTargets.set(e.from, []);
|
|
152
|
+
sourceToTargets.get(e.from).push(e.to);
|
|
153
|
+
});
|
|
154
|
+
const result = components === null || components === void 0 ? void 0 : components.map((table) => {
|
|
155
|
+
var _a, _b;
|
|
156
|
+
return (Object.assign(Object.assign({}, table), { [keyNode]: (_b = sourceToTargets.get(String((_a = table === null || table === void 0 ? void 0 : table[mappingKey]) === null || _a === void 0 ? void 0 : _a.id))) !== null && _b !== void 0 ? _b : [] }));
|
|
157
|
+
});
|
|
158
|
+
onEdgesChange === null || onEdgesChange === void 0 ? void 0 : onEdgesChange(edges, result);
|
|
159
|
+
}, [edges]);
|
|
160
|
+
return {
|
|
161
|
+
// state
|
|
162
|
+
edges,
|
|
163
|
+
setEdges,
|
|
164
|
+
selectedEdge,
|
|
165
|
+
connecting,
|
|
166
|
+
mousePos,
|
|
167
|
+
// actions
|
|
168
|
+
startConnect,
|
|
169
|
+
endConnect,
|
|
170
|
+
cancelConnect,
|
|
171
|
+
startDragWaypoint,
|
|
172
|
+
insertWaypoint,
|
|
173
|
+
removeWaypoint,
|
|
174
|
+
selectEdge,
|
|
175
|
+
deleteEdge,
|
|
176
|
+
deleteSelectedEdge,
|
|
177
|
+
clearSelection,
|
|
178
|
+
// event handlers untuk SVG
|
|
179
|
+
onMouseMove,
|
|
180
|
+
onMouseUp,
|
|
181
|
+
};
|
|
182
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ComponentProps, TableMatchKey } from ".";
|
|
2
|
+
export declare const GRID_SIZE = 20;
|
|
3
|
+
export declare const NODE_WIDTH = 120;
|
|
4
|
+
export declare const NODE_HEIGHT = 50;
|
|
5
|
+
export declare const snap: (v: number) => number;
|
|
6
|
+
export type EdgeType = {
|
|
7
|
+
id: string;
|
|
8
|
+
from: string;
|
|
9
|
+
to: string;
|
|
10
|
+
waypoints: {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
}[];
|
|
14
|
+
};
|
|
15
|
+
export type NodeType = {
|
|
16
|
+
id: string;
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
rotation?: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Rotate point (px, py) sekitar origin (0,0) sebesar deg derajat.
|
|
25
|
+
*/
|
|
26
|
+
export declare const rotatePoint: (px: number, py: number, deg: number) => {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Hitung titik edge pada sisi rect node yang menghadap ke arah `to`,
|
|
32
|
+
* dengan mempertimbangkan rotation node.
|
|
33
|
+
*/
|
|
34
|
+
export declare const getRectEdge: (from: {
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
width?: number;
|
|
38
|
+
height?: number;
|
|
39
|
+
rotation?: number;
|
|
40
|
+
}, to: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
}, width: number, height: number) => {
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Build SVG path melalui array of points.
|
|
49
|
+
* Selalu berakhir tepat di titik tujuan.
|
|
50
|
+
*
|
|
51
|
+
* Routing per segmen:
|
|
52
|
+
* - Sejajar vertikal (dx <= threshold) → V
|
|
53
|
+
* - Sejajar horizontal (dy <= threshold) → H
|
|
54
|
+
* - Dominan vertikal (dy > dx) → H midX V curr.y H curr.x
|
|
55
|
+
* - Dominan horizontal (dx >= dy) → H midX V curr.y H curr.x
|
|
56
|
+
*
|
|
57
|
+
* Semua routing diakhiri dengan koordinat EKSAK curr.x, curr.y
|
|
58
|
+
* sehingga path selalu sampai ke titik tujuan.
|
|
59
|
+
*/
|
|
60
|
+
export declare const buildPath: (points: {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
}[]) => string;
|
|
64
|
+
export declare const defaultWaypoints: (start: {
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
}, end: {
|
|
68
|
+
x: number;
|
|
69
|
+
y: number;
|
|
70
|
+
}) => {
|
|
71
|
+
x: number;
|
|
72
|
+
y: number;
|
|
73
|
+
}[];
|
|
74
|
+
export declare const renderElements: (elementEditor: ComponentProps[], mappingKey?: string, tableMatchKey?: TableMatchKey[], statusKey?: string) => ComponentProps[];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// utils.ts
|
|
2
|
+
export const GRID_SIZE = 20;
|
|
3
|
+
export const NODE_WIDTH = 120;
|
|
4
|
+
export const NODE_HEIGHT = 50;
|
|
5
|
+
export const snap = (v) => Math.round(v / GRID_SIZE) * GRID_SIZE;
|
|
6
|
+
/**
|
|
7
|
+
* Rotate point (px, py) sekitar origin (0,0) sebesar deg derajat.
|
|
8
|
+
*/
|
|
9
|
+
export const rotatePoint = (px, py, deg) => {
|
|
10
|
+
const rad = (deg * Math.PI) / 180;
|
|
11
|
+
return {
|
|
12
|
+
x: px * Math.cos(rad) - py * Math.sin(rad),
|
|
13
|
+
y: px * Math.sin(rad) + py * Math.cos(rad),
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Hitung titik edge pada sisi rect node yang menghadap ke arah `to`,
|
|
18
|
+
* dengan mempertimbangkan rotation node.
|
|
19
|
+
*/
|
|
20
|
+
export const getRectEdge = (from, to, width, height) => {
|
|
21
|
+
var _a, _b, _c;
|
|
22
|
+
const w = (_a = from.width) !== null && _a !== void 0 ? _a : NODE_WIDTH;
|
|
23
|
+
const h = (_b = from.height) !== null && _b !== void 0 ? _b : NODE_HEIGHT;
|
|
24
|
+
const rotation = (_c = from.rotation) !== null && _c !== void 0 ? _c : 0;
|
|
25
|
+
const dx = to.x - from.x;
|
|
26
|
+
const dy = to.y - from.y;
|
|
27
|
+
if (dx === 0 && dy === 0)
|
|
28
|
+
return { x: from.x, y: from.y };
|
|
29
|
+
const angle = Math.atan2(dy, dx);
|
|
30
|
+
// Un-rotate arah ke local space node
|
|
31
|
+
const rad = -(rotation * Math.PI) / 180;
|
|
32
|
+
const localAngle = angle + rad;
|
|
33
|
+
const absCos = Math.abs(Math.cos(localAngle));
|
|
34
|
+
const absSin = Math.abs(Math.sin(localAngle));
|
|
35
|
+
const offset = (h / 2) * absCos <= (w / 2) * absSin
|
|
36
|
+
? (h / 2) / absSin
|
|
37
|
+
: (w / 2) / absCos;
|
|
38
|
+
const localEdgeX = Math.cos(localAngle) * offset;
|
|
39
|
+
const localEdgeY = Math.sin(localAngle) * offset;
|
|
40
|
+
// Re-rotate balik ke world space
|
|
41
|
+
const worldEdge = rotatePoint(localEdgeX, localEdgeY, rotation);
|
|
42
|
+
return {
|
|
43
|
+
x: snap(from.x + worldEdge.x),
|
|
44
|
+
y: snap(from.y + worldEdge.y),
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Build SVG path melalui array of points.
|
|
49
|
+
* Selalu berakhir tepat di titik tujuan.
|
|
50
|
+
*
|
|
51
|
+
* Routing per segmen:
|
|
52
|
+
* - Sejajar vertikal (dx <= threshold) → V
|
|
53
|
+
* - Sejajar horizontal (dy <= threshold) → H
|
|
54
|
+
* - Dominan vertikal (dy > dx) → H midX V curr.y H curr.x
|
|
55
|
+
* - Dominan horizontal (dx >= dy) → H midX V curr.y H curr.x
|
|
56
|
+
*
|
|
57
|
+
* Semua routing diakhiri dengan koordinat EKSAK curr.x, curr.y
|
|
58
|
+
* sehingga path selalu sampai ke titik tujuan.
|
|
59
|
+
*/
|
|
60
|
+
export const buildPath = (points) => {
|
|
61
|
+
if (points.length < 2)
|
|
62
|
+
return "";
|
|
63
|
+
let d = `M ${points[0].x} ${points[0].y}`;
|
|
64
|
+
const threshold = 2; // presisi tinggi agar tidak salah deteksi lurus
|
|
65
|
+
for (let i = 1; i < points.length; i++) {
|
|
66
|
+
const prev = points[i - 1];
|
|
67
|
+
const curr = points[i];
|
|
68
|
+
const dx = Math.abs(curr.x - prev.x);
|
|
69
|
+
const dy = Math.abs(curr.y - prev.y);
|
|
70
|
+
if (dx <= threshold) {
|
|
71
|
+
// Lurus vertikal — snap ke curr.x agar tidak offset kecil
|
|
72
|
+
d += ` V ${curr.y}`;
|
|
73
|
+
}
|
|
74
|
+
else if (dy <= threshold) {
|
|
75
|
+
// Lurus horizontal
|
|
76
|
+
d += ` H ${curr.x}`;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Orthogonal — selalu akhiri dengan posisi EKSAK curr.x curr.y
|
|
80
|
+
const midX = snap((prev.x + curr.x) / 2);
|
|
81
|
+
d += ` H ${midX} V ${curr.y} H ${curr.x}`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return d;
|
|
85
|
+
};
|
|
86
|
+
export const defaultWaypoints = (start, end) => {
|
|
87
|
+
return [
|
|
88
|
+
{
|
|
89
|
+
x: snap((start.x + end.x) / 2),
|
|
90
|
+
y: snap((start.y + end.y) / 2),
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
};
|
|
94
|
+
export const renderElements = (elementEditor, mappingKey, tableMatchKey, statusKey) => {
|
|
95
|
+
return elementEditor.map((editorItem) => {
|
|
96
|
+
const isUsingMapping = mappingKey &&
|
|
97
|
+
typeof editorItem[mappingKey] === "object" &&
|
|
98
|
+
editorItem[mappingKey] !== null;
|
|
99
|
+
let finalProps = isUsingMapping ? editorItem[mappingKey] : editorItem;
|
|
100
|
+
if (tableMatchKey) {
|
|
101
|
+
const tableMatch = tableMatchKey.find((item) => item.key == (editorItem === null || editorItem === void 0 ? void 0 : editorItem[statusKey]));
|
|
102
|
+
finalProps = Object.assign(Object.assign(Object.assign({}, finalProps), tableMatch === null || tableMatch === void 0 ? void 0 : tableMatch.properties), { className: tableMatch === null || tableMatch === void 0 ? void 0 : tableMatch.className });
|
|
103
|
+
}
|
|
104
|
+
return finalProps;
|
|
105
|
+
});
|
|
106
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
type Props = {
|
|
3
|
+
cx: number;
|
|
4
|
+
cy: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
nodeId: string;
|
|
8
|
+
rotation?: number;
|
|
9
|
+
isConnecting: boolean;
|
|
10
|
+
isDraggingAnchor: boolean;
|
|
11
|
+
onStartConnect: (nodeId: string, clickPos: {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}) => void;
|
|
15
|
+
onEndConnect: (nodeId: string, clickPos: {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
}) => void;
|
|
19
|
+
isConnectEdge?: boolean;
|
|
20
|
+
onSelectNode: (nodeId: string) => void;
|
|
21
|
+
isSelectNode?: boolean;
|
|
22
|
+
onSelectEdge?: (edgeId: string) => void;
|
|
23
|
+
edges: {
|
|
24
|
+
id: string;
|
|
25
|
+
from: string;
|
|
26
|
+
to: string;
|
|
27
|
+
}[];
|
|
28
|
+
};
|
|
29
|
+
export declare const ConnectHandle: React.FC<Props>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// connect-handle.tsx
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
// 4 titik per sisi dalam local space
|
|
5
|
+
const getLocalAnchors = (w, h) => {
|
|
6
|
+
const hw = w / 2;
|
|
7
|
+
const hh = h / 2;
|
|
8
|
+
return [
|
|
9
|
+
{ x: 0, y: -hh }, // top center
|
|
10
|
+
{ x: hw, y: 0 }, // right center
|
|
11
|
+
{ x: 0, y: hh }, // bottom center
|
|
12
|
+
{ x: -hw, y: 0 }, // left center
|
|
13
|
+
];
|
|
14
|
+
};
|
|
15
|
+
const nearestAnchor = (lx, ly, anchors) => {
|
|
16
|
+
let best = anchors[0];
|
|
17
|
+
let bestD = Infinity;
|
|
18
|
+
for (const a of anchors) {
|
|
19
|
+
const d = Math.hypot(a.x - lx, a.y - ly);
|
|
20
|
+
if (d < bestD) {
|
|
21
|
+
bestD = d;
|
|
22
|
+
best = a;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return best;
|
|
26
|
+
};
|
|
27
|
+
export const ConnectHandle = ({ cx, cy, width, height, nodeId, rotation = 0, isConnecting, isDraggingAnchor, onStartConnect, onEndConnect, isConnectEdge, onSelectNode, isSelectNode, onSelectEdge, edges }) => {
|
|
28
|
+
const hw = width / 2;
|
|
29
|
+
const hh = height / 2;
|
|
30
|
+
const [hovered, setHovered] = useState(false);
|
|
31
|
+
const isActive = isConnecting || isDraggingAnchor;
|
|
32
|
+
const isActiveSelectEdge = isSelectNode;
|
|
33
|
+
const anchors = getLocalAnchors(width, height);
|
|
34
|
+
const toLocalPos = (e) => {
|
|
35
|
+
var _a;
|
|
36
|
+
const svg = e.currentTarget.closest("svg");
|
|
37
|
+
const pt = svg.createSVGPoint();
|
|
38
|
+
pt.x = e.clientX;
|
|
39
|
+
pt.y = e.clientY;
|
|
40
|
+
const world = pt.matrixTransform((_a = svg.getScreenCTM()) === null || _a === void 0 ? void 0 : _a.inverse());
|
|
41
|
+
const dx = world.x - cx;
|
|
42
|
+
const dy = world.y - cy;
|
|
43
|
+
const rad = -(rotation * Math.PI) / 180;
|
|
44
|
+
return {
|
|
45
|
+
x: dx * Math.cos(rad) - dy * Math.sin(rad),
|
|
46
|
+
y: dx * Math.sin(rad) + dy * Math.cos(rad),
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
const toWorld = (lx, ly) => {
|
|
50
|
+
const rad = (rotation * Math.PI) / 180;
|
|
51
|
+
return {
|
|
52
|
+
x: cx + lx * Math.cos(rad) - ly * Math.sin(rad),
|
|
53
|
+
y: cy + lx * Math.sin(rad) + ly * Math.cos(rad),
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
const handlePointerDown = (e) => {
|
|
57
|
+
e.stopPropagation();
|
|
58
|
+
const local = toLocalPos(e);
|
|
59
|
+
const snapped = nearestAnchor(local.x, local.y, anchors);
|
|
60
|
+
const worldPos = toWorld(snapped.x, snapped.y);
|
|
61
|
+
console.log({ isActiveSelectEdge });
|
|
62
|
+
if (isActiveSelectEdge) {
|
|
63
|
+
const findEdge = edges.find(e => e.from === nodeId || e.to === nodeId);
|
|
64
|
+
console.log({ findEdge });
|
|
65
|
+
onSelectEdge(findEdge === null || findEdge === void 0 ? void 0 : findEdge.id);
|
|
66
|
+
onSelectNode(nodeId);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
else if (isActive) {
|
|
70
|
+
onEndConnect(nodeId, worldPos);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
onStartConnect(nodeId, worldPos);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
return (_jsxs("g", { transform: `translate(${cx}, ${cy}) rotate(${rotation})`, children: [_jsx("rect", { x: -hw - 4, y: -hh - 4, width: width + 8, height: height + 8, fill: "transparent", stroke: isActive ? "#a78bfa" : hovered ? "#38bdf8" : "transparent", strokeWidth: isActive ? 2 : 1.5, strokeDasharray: isActive ? "4 2" : "none", rx: 4, style: { pointerEvents: "none" } }), _jsx("rect", { x: -hw - 4, y: -hh - 4, width: width + 8, height: height + 8, fill: "transparent", style: {
|
|
78
|
+
cursor: isConnectEdge ? "crosshair" : "pointer",
|
|
79
|
+
pointerEvents: isConnectEdge ? "all" : "none",
|
|
80
|
+
}, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), onPointerDown: (e) => {
|
|
81
|
+
console.log({ isConnectEdge, isSelectNode });
|
|
82
|
+
// console.log({ isConnectEdge, isSelectNode })
|
|
83
|
+
// if (!isConnectEdge ) return;
|
|
84
|
+
// if (!isSelectNode) return;
|
|
85
|
+
handlePointerDown(e);
|
|
86
|
+
} }), ((hovered && isConnectEdge) || isActive) &&
|
|
87
|
+
anchors.map((a, i) => (_jsx("circle", { cx: a.x, cy: a.y, r: 3, fill: isActive ? "#a78bfa" : "#38bdf8", opacity: 0.5, style: { pointerEvents: "none" } }, i)))] }));
|
|
88
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// connect-handle.tsx
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
// 4 titik per sisi dalam local space
|
|
4
|
+
const getLocalAnchors = (w, h) => {
|
|
5
|
+
const hw = w / 2;
|
|
6
|
+
const hh = h / 2;
|
|
7
|
+
return [
|
|
8
|
+
{ x: 0, y: -hh }, // top center
|
|
9
|
+
{ x: hw, y: 0 }, // right center
|
|
10
|
+
{ x: 0, y: hh }, // bottom center
|
|
11
|
+
{ x: -hw, y: 0 }, // left center
|
|
12
|
+
];
|
|
13
|
+
};
|
|
14
|
+
const nearestAnchor = (lx, ly, anchors) => {
|
|
15
|
+
let best = anchors[0];
|
|
16
|
+
let bestD = Infinity;
|
|
17
|
+
for (const a of anchors) {
|
|
18
|
+
const d = Math.hypot(a.x - lx, a.y - ly);
|
|
19
|
+
if (d < bestD) {
|
|
20
|
+
bestD = d;
|
|
21
|
+
best = a;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return best;
|
|
25
|
+
};
|
|
26
|
+
export const ConnectHandle = ({ cx, cy, width, height, nodeId, rotation = 0, isConnecting, isDraggingAnchor, onStartConnect, onEndConnect, isConnectEdge, onSelectNode, isSelectNode, onSelectEdge, edges }) => {
|
|
27
|
+
const hw = width / 2;
|
|
28
|
+
const hh = height / 2;
|
|
29
|
+
const [hovered, setHovered] = useState(false);
|
|
30
|
+
const isActive = isConnecting || isDraggingAnchor;
|
|
31
|
+
const isActiveSelectEdge = isSelectNode;
|
|
32
|
+
const anchors = getLocalAnchors(width, height);
|
|
33
|
+
const toLocalPos = (e) => {
|
|
34
|
+
var _a;
|
|
35
|
+
const svg = e.currentTarget.closest("svg");
|
|
36
|
+
const pt = svg.createSVGPoint();
|
|
37
|
+
pt.x = e.clientX;
|
|
38
|
+
pt.y = e.clientY;
|
|
39
|
+
const world = pt.matrixTransform((_a = svg.getScreenCTM()) === null || _a === void 0 ? void 0 : _a.inverse());
|
|
40
|
+
const dx = world.x - cx;
|
|
41
|
+
const dy = world.y - cy;
|
|
42
|
+
const rad = -(rotation * Math.PI) / 180;
|
|
43
|
+
return {
|
|
44
|
+
x: dx * Math.cos(rad) - dy * Math.sin(rad),
|
|
45
|
+
y: dx * Math.sin(rad) + dy * Math.cos(rad),
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
const toWorld = (lx, ly) => {
|
|
49
|
+
const rad = (rotation * Math.PI) / 180;
|
|
50
|
+
return {
|
|
51
|
+
x: cx + lx * Math.cos(rad) - ly * Math.sin(rad),
|
|
52
|
+
y: cy + lx * Math.sin(rad) + ly * Math.cos(rad),
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
const handlePointerDown = (e) => {
|
|
56
|
+
e.stopPropagation();
|
|
57
|
+
const local = toLocalPos(e);
|
|
58
|
+
const snapped = nearestAnchor(local.x, local.y, anchors);
|
|
59
|
+
const worldPos = toWorld(snapped.x, snapped.y);
|
|
60
|
+
console.log({ isActiveSelectEdge });
|
|
61
|
+
if (isActiveSelectEdge) {
|
|
62
|
+
const findEdge = edges.find(e => e.from === nodeId || e.to === nodeId);
|
|
63
|
+
console.log({ findEdge });
|
|
64
|
+
onSelectEdge(findEdge === null || findEdge === void 0 ? void 0 : findEdge.id);
|
|
65
|
+
onSelectNode(nodeId);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
else if (isActive) {
|
|
69
|
+
onEndConnect(nodeId, worldPos);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
onStartConnect(nodeId, worldPos);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return (<g transform={`translate(${cx}, ${cy}) rotate(${rotation})`}>
|
|
77
|
+
{/* Border visual */}
|
|
78
|
+
<rect x={-hw - 4} y={-hh - 4} width={width + 8} height={height + 8} fill="transparent" stroke={isActive ? "#a78bfa" : hovered ? "#38bdf8" : "transparent"} strokeWidth={isActive ? 2 : 1.5} strokeDasharray={isActive ? "4 2" : "none"} rx={4} style={{ pointerEvents: "none" }}/>
|
|
79
|
+
|
|
80
|
+
{/* Hit area */}
|
|
81
|
+
<rect x={-hw - 4} y={-hh - 4} width={width + 8} height={height + 8} fill="transparent" style={{
|
|
82
|
+
cursor: isConnectEdge ? "crosshair" : "pointer",
|
|
83
|
+
pointerEvents: isConnectEdge ? "all" : "none",
|
|
84
|
+
}} onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)} onPointerDown={(e) => {
|
|
85
|
+
console.log({ isConnectEdge, isSelectNode });
|
|
86
|
+
// console.log({ isConnectEdge, isSelectNode })
|
|
87
|
+
// if (!isConnectEdge ) return;
|
|
88
|
+
// if (!isSelectNode) return;
|
|
89
|
+
handlePointerDown(e);
|
|
90
|
+
}}/>
|
|
91
|
+
|
|
92
|
+
{/* Anchor dots — muncul saat hover atau active */}
|
|
93
|
+
{((hovered && isConnectEdge) || isActive) &&
|
|
94
|
+
anchors.map((a, i) => (<circle key={i} cx={a.x} cy={a.y} r={3} fill={isActive ? "#a78bfa" : "#38bdf8"} opacity={0.5} style={{ pointerEvents: "none" }}/>))}
|
|
95
|
+
</g>);
|
|
96
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { EdgeType, NodeType } from "./utils";
|
|
3
|
+
export type DraggingAnchor = {
|
|
4
|
+
edgeId: string;
|
|
5
|
+
side: "from" | "to";
|
|
6
|
+
};
|
|
7
|
+
type Props = {
|
|
8
|
+
edges: EdgeType[];
|
|
9
|
+
selectedEdge: string | null;
|
|
10
|
+
connecting: {
|
|
11
|
+
fromId: string;
|
|
12
|
+
fromPos: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
};
|
|
16
|
+
} | null;
|
|
17
|
+
draggingAnchor: DraggingAnchor | null;
|
|
18
|
+
mousePos: {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
};
|
|
22
|
+
getNodeById: (id: string) => NodeType | null;
|
|
23
|
+
onSelectEdge: (edgeId: string) => void;
|
|
24
|
+
onDeleteEdge: (edgeId: string) => void;
|
|
25
|
+
onStartDragWaypoint: (e: React.MouseEvent, edgeId: string, index: number) => void;
|
|
26
|
+
onInsertWaypoint: (e: React.MouseEvent, edgeId: string, insertIndex: number, x: number, y: number) => void;
|
|
27
|
+
onRemoveWaypoint: (e: React.MouseEvent, edgeId: string, index: number) => void;
|
|
28
|
+
onStartDragAnchor: (e: React.MouseEvent, edgeId: string, side: "from" | "to") => void;
|
|
29
|
+
isConnectEdge: boolean;
|
|
30
|
+
isSelectNode: boolean;
|
|
31
|
+
selectedNodeId: string | null;
|
|
32
|
+
};
|
|
33
|
+
export declare const ConnectionLayer: React.FC<Props>;
|
|
34
|
+
export {};
|