@canmingir/link 1.2.10 → 1.2.12
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/package.json +3 -2
- package/src/config/schemas.js +6 -0
- package/src/http/index.js +21 -10
- package/src/layouts/auth/modern.jsx +26 -10
- package/src/layouts/common/ProjectBar/index.jsx +17 -4
- package/src/lib/Flow/core/Flow.jsx +79 -0
- package/src/lib/Flow/core/FlowNode.jsx +68 -0
- package/src/lib/Flow/core/FlowViewport.jsx +259 -0
- package/src/lib/Flow/graph/FloatingGraph.jsx +44 -0
- package/src/lib/Flow/hooks/useGraphOperations.js +362 -0
- package/src/lib/Flow/hooks/useNodeStyle.js +56 -0
- package/src/lib/Flow/index.js +1 -1
- package/src/lib/Flow/nodes/DefaultCard.jsx +107 -0
- package/src/lib/Flow/nodes/DraggableNode.jsx +162 -0
- package/src/lib/Flow/nodes/FlowNodeView.jsx +214 -0
- package/src/lib/Flow/selection/SelectionContext.jsx +259 -0
- package/src/lib/Flow/selection/SelectionOverlay.jsx +31 -0
- package/src/lib/Flow/utils/flowUtils.js +268 -0
- package/src/lib/index.js +1 -1
- package/src/pages/LoginPage.jsx +15 -1
- package/src/widgets/Login/CognitoLogin.jsx +45 -0
- package/src/widgets/Login/DemoLogin.jsx +178 -0
- package/src/widgets/Login/cognitoAuth.jsx +44 -0
- package/src/lib/Flow/DraggableNode.jsx +0 -128
- package/src/lib/Flow/Flow.jsx +0 -40
- package/src/lib/Flow/FlowNode.jsx +0 -519
- package/src/lib/Flow/SelectionContext.jsx +0 -123
- package/src/lib/Flow/flowUtils.js +0 -111
- /package/src/lib/Flow/{DynamicConnector.jsx → connectors/DynamicConnector.jsx} +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { Box } from "@mui/material";
|
|
2
|
+
import DefaultNodeCard from "./DefaultCard";
|
|
3
|
+
import DraggableNode from "./DraggableNode";
|
|
4
|
+
import DynamicConnector from "../connectors/DynamicConnector";
|
|
5
|
+
import FlowNode from "../core/FlowNode";
|
|
6
|
+
import { getContentParts } from "../utils/flowUtils";
|
|
7
|
+
import { toPxNumber } from "../styles";
|
|
8
|
+
import { useNodeStyle } from "../hooks/useNodeStyle";
|
|
9
|
+
|
|
10
|
+
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
11
|
+
|
|
12
|
+
const FlowNodeView = ({
|
|
13
|
+
node,
|
|
14
|
+
type,
|
|
15
|
+
variant,
|
|
16
|
+
style,
|
|
17
|
+
plugin,
|
|
18
|
+
registerRef,
|
|
19
|
+
onDrag,
|
|
20
|
+
onConnect,
|
|
21
|
+
}) => {
|
|
22
|
+
const hasChildren = Array.isArray(node.children) && node.children.length > 0;
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
baseStyle,
|
|
26
|
+
nodeStyle,
|
|
27
|
+
plugin: _plugin,
|
|
28
|
+
} = useNodeStyle({
|
|
29
|
+
node,
|
|
30
|
+
type,
|
|
31
|
+
variant,
|
|
32
|
+
style,
|
|
33
|
+
plugin,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
direction = "vertical",
|
|
38
|
+
lineColor = baseStyle.lineColor,
|
|
39
|
+
lineWidth = baseStyle.lineWidth,
|
|
40
|
+
lineStyle = baseStyle.lineStyle,
|
|
41
|
+
gap = baseStyle.gap,
|
|
42
|
+
levelGap = baseStyle.levelGap ?? 2.5,
|
|
43
|
+
nodeSx = {},
|
|
44
|
+
borderWidth,
|
|
45
|
+
borderColor = baseStyle.borderColor,
|
|
46
|
+
cardWidth,
|
|
47
|
+
shape,
|
|
48
|
+
shadowLevel,
|
|
49
|
+
minHeight,
|
|
50
|
+
showDots = baseStyle.showDots ?? false,
|
|
51
|
+
dotRadius = baseStyle.dotRadius ?? 4,
|
|
52
|
+
dotColor = baseStyle.dotColor,
|
|
53
|
+
showArrow = baseStyle.showArrow ?? true,
|
|
54
|
+
arrowSize = baseStyle.arrowSize ?? 6,
|
|
55
|
+
animated = baseStyle.animated ?? false,
|
|
56
|
+
animationSpeed = baseStyle.animationSpeed ?? 1,
|
|
57
|
+
gradient = baseStyle.gradient ?? null,
|
|
58
|
+
curvature = baseStyle.curvature ?? 0.5,
|
|
59
|
+
selectionColor = baseStyle.selectionColor ?? "#64748b",
|
|
60
|
+
} = nodeStyle;
|
|
61
|
+
|
|
62
|
+
const isHorizontal = direction === "horizontal";
|
|
63
|
+
|
|
64
|
+
const strokeWidth = toPxNumber(lineWidth, 1.5);
|
|
65
|
+
const dashStyle =
|
|
66
|
+
lineStyle === "dashed" || lineStyle === "dotted" ? lineStyle : "solid";
|
|
67
|
+
|
|
68
|
+
const containerRef = useRef(null);
|
|
69
|
+
const parentRef = useRef(null);
|
|
70
|
+
const childRefs = useRef({});
|
|
71
|
+
const [childElList, setChildElList] = useState([]);
|
|
72
|
+
|
|
73
|
+
const [connectorTick, setConnectorTick] = useState(0);
|
|
74
|
+
|
|
75
|
+
const handleDrag = (newOffset) => {
|
|
76
|
+
setConnectorTick((t) => t + 1);
|
|
77
|
+
if (onDrag) onDrag(newOffset);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
useLayoutEffect(() => {
|
|
81
|
+
const els = (node.children || [])
|
|
82
|
+
.map((c) => childRefs.current[c.id])
|
|
83
|
+
.filter(Boolean);
|
|
84
|
+
setChildElList(els);
|
|
85
|
+
}, [node.children]);
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
const t = setTimeout(() => {
|
|
89
|
+
const els = (node.children || [])
|
|
90
|
+
.map((c) => childRefs.current[c.id])
|
|
91
|
+
.filter(Boolean);
|
|
92
|
+
setChildElList(els);
|
|
93
|
+
}, 0);
|
|
94
|
+
return () => clearTimeout(t);
|
|
95
|
+
}, [node.children]);
|
|
96
|
+
|
|
97
|
+
const { title, subtitle, metaEntries } = getContentParts(node);
|
|
98
|
+
|
|
99
|
+
const renderContent = () => {
|
|
100
|
+
if (_plugin && typeof _plugin.node === "function") {
|
|
101
|
+
return _plugin.node({
|
|
102
|
+
node,
|
|
103
|
+
title,
|
|
104
|
+
subtitle,
|
|
105
|
+
metaEntries,
|
|
106
|
+
nodeStyle,
|
|
107
|
+
baseStyle,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return (
|
|
111
|
+
<DefaultNodeCard
|
|
112
|
+
title={title}
|
|
113
|
+
subtitle={subtitle}
|
|
114
|
+
metaEntries={metaEntries}
|
|
115
|
+
nodeStyle={nodeStyle}
|
|
116
|
+
baseStyle={baseStyle}
|
|
117
|
+
variant={variant}
|
|
118
|
+
borderWidth={borderWidth}
|
|
119
|
+
borderColor={borderColor}
|
|
120
|
+
cardWidth={cardWidth}
|
|
121
|
+
shape={shape}
|
|
122
|
+
shadowLevel={shadowLevel}
|
|
123
|
+
minHeight={minHeight}
|
|
124
|
+
nodeSx={nodeSx}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<Box
|
|
131
|
+
ref={containerRef}
|
|
132
|
+
sx={{
|
|
133
|
+
display: "inline-flex",
|
|
134
|
+
flexDirection: isHorizontal ? "row" : "column",
|
|
135
|
+
alignItems: "center",
|
|
136
|
+
position: "relative",
|
|
137
|
+
}}
|
|
138
|
+
>
|
|
139
|
+
<DraggableNode
|
|
140
|
+
registerRef={(el) => {
|
|
141
|
+
parentRef.current = el;
|
|
142
|
+
if (registerRef) registerRef(el);
|
|
143
|
+
}}
|
|
144
|
+
onDrag={handleDrag}
|
|
145
|
+
nodeId={node.id}
|
|
146
|
+
selectionColor={selectionColor}
|
|
147
|
+
initialPosition={node._pastePosition}
|
|
148
|
+
onConnect={onConnect}
|
|
149
|
+
>
|
|
150
|
+
{renderContent()}
|
|
151
|
+
</DraggableNode>
|
|
152
|
+
|
|
153
|
+
{hasChildren && (
|
|
154
|
+
<>
|
|
155
|
+
<DynamicConnector
|
|
156
|
+
containerEl={containerRef.current}
|
|
157
|
+
parentEl={parentRef.current}
|
|
158
|
+
childEls={childElList}
|
|
159
|
+
stroke={lineColor}
|
|
160
|
+
strokeWidth={strokeWidth}
|
|
161
|
+
lineStyle={dashStyle}
|
|
162
|
+
tick={connectorTick}
|
|
163
|
+
orientation={direction}
|
|
164
|
+
showDots={showDots}
|
|
165
|
+
dotRadius={dotRadius}
|
|
166
|
+
dotColor={dotColor}
|
|
167
|
+
showArrow={showArrow}
|
|
168
|
+
arrowSize={arrowSize}
|
|
169
|
+
animated={animated}
|
|
170
|
+
animationSpeed={animationSpeed}
|
|
171
|
+
gradient={gradient}
|
|
172
|
+
curvature={curvature}
|
|
173
|
+
/>
|
|
174
|
+
|
|
175
|
+
<Box
|
|
176
|
+
sx={{
|
|
177
|
+
display: "flex",
|
|
178
|
+
flexDirection: isHorizontal ? "column" : "row",
|
|
179
|
+
...(isHorizontal
|
|
180
|
+
? {
|
|
181
|
+
marginLeft: levelGap,
|
|
182
|
+
rowGap: gap,
|
|
183
|
+
}
|
|
184
|
+
: {
|
|
185
|
+
marginTop: levelGap,
|
|
186
|
+
columnGap: gap,
|
|
187
|
+
}),
|
|
188
|
+
position: "relative",
|
|
189
|
+
alignItems: "flex-start",
|
|
190
|
+
justifyContent: "center",
|
|
191
|
+
}}
|
|
192
|
+
>
|
|
193
|
+
{node.children.map((child) => (
|
|
194
|
+
<FlowNode
|
|
195
|
+
key={child.id}
|
|
196
|
+
node={child}
|
|
197
|
+
type={type}
|
|
198
|
+
variant={variant}
|
|
199
|
+
style={style}
|
|
200
|
+
plugin={plugin}
|
|
201
|
+
registerRef={(el) => (childRefs.current[child.id] = el)}
|
|
202
|
+
onDrag={() => setConnectorTick((t) => t + 1)}
|
|
203
|
+
isRoot={false}
|
|
204
|
+
onConnect={onConnect}
|
|
205
|
+
/>
|
|
206
|
+
))}
|
|
207
|
+
</Box>
|
|
208
|
+
</>
|
|
209
|
+
)}
|
|
210
|
+
</Box>
|
|
211
|
+
);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export default FlowNodeView;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useCallback,
|
|
4
|
+
useContext,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from "react";
|
|
9
|
+
|
|
10
|
+
const SelectionContext = createContext(null);
|
|
11
|
+
|
|
12
|
+
const DEFAULT_CONTEXT = {
|
|
13
|
+
selectedIds: new Set(),
|
|
14
|
+
selectNode: () => {},
|
|
15
|
+
deselectNode: () => {},
|
|
16
|
+
toggleSelection: () => {},
|
|
17
|
+
clearSelection: () => {},
|
|
18
|
+
selectMultiple: () => {},
|
|
19
|
+
addToSelection: () => {},
|
|
20
|
+
isSelected: () => false,
|
|
21
|
+
registerNodeHandlers: () => () => {},
|
|
22
|
+
moveSelectedNodes: () => {},
|
|
23
|
+
cutSelectedNodes: () => {},
|
|
24
|
+
pasteNodes: () => {},
|
|
25
|
+
hasClipboard: false,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const filterNextToSelection = (next, selectedSet) => {
|
|
29
|
+
if (!next) return undefined;
|
|
30
|
+
|
|
31
|
+
if (Array.isArray(next)) {
|
|
32
|
+
const filtered = next.filter((id) => selectedSet.has(id));
|
|
33
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return selectedSet.has(next) ? next : undefined;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const buildClipboardNode = (id, node, selectedSet) => {
|
|
40
|
+
const filteredNext = filterNextToSelection(node.next, selectedSet);
|
|
41
|
+
const hasPreviousInSelection =
|
|
42
|
+
node.previous && selectedSet.has(node.previous);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
...node,
|
|
46
|
+
id,
|
|
47
|
+
_originalId: id,
|
|
48
|
+
next: filteredNext,
|
|
49
|
+
previous: hasPreviousInSelection ? node.previous : undefined,
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const buildPasteStructure = (clipboardNodes, position) => {
|
|
54
|
+
if (!clipboardNodes?.length) return null;
|
|
55
|
+
|
|
56
|
+
const seenIds = new Set();
|
|
57
|
+
const nodes = {};
|
|
58
|
+
|
|
59
|
+
clipboardNodes.forEach((clipNode) => {
|
|
60
|
+
const nodeId = clipNode._originalId || clipNode.id;
|
|
61
|
+
if (!nodeId || seenIds.has(nodeId)) return;
|
|
62
|
+
|
|
63
|
+
seenIds.add(nodeId);
|
|
64
|
+
|
|
65
|
+
nodes[nodeId] = {
|
|
66
|
+
id: nodeId,
|
|
67
|
+
label: clipNode.label,
|
|
68
|
+
next: clipNode.next,
|
|
69
|
+
previous: clipNode.previous,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
Object.keys(clipNode).forEach((key) => {
|
|
73
|
+
if (!["_originalId", "id", "next", "previous", "label"].includes(key)) {
|
|
74
|
+
nodes[nodeId][key] = clipNode[key];
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const rootIds = Object.keys(nodes).filter((id) => !nodes[id].previous);
|
|
80
|
+
const uniqueRoots = [...new Set(rootIds)];
|
|
81
|
+
const finalRoots =
|
|
82
|
+
uniqueRoots.length > 0 ? uniqueRoots : [Object.keys(nodes)[0]];
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
nodes,
|
|
86
|
+
roots: finalRoots,
|
|
87
|
+
_pastePosition: position,
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const SelectionProvider = ({ children }) => {
|
|
92
|
+
const [selectedIds, setSelectedIds] = useState(new Set());
|
|
93
|
+
const [clipboard, setClipboard] = useState([]);
|
|
94
|
+
const nodeHandlersRef = useRef(new Map());
|
|
95
|
+
const isPastingRef = useRef(false);
|
|
96
|
+
|
|
97
|
+
const selectNode = useCallback((id, addToSelection = false) => {
|
|
98
|
+
setSelectedIds((prev) => {
|
|
99
|
+
const next = new Set(addToSelection ? prev : []);
|
|
100
|
+
next.add(id);
|
|
101
|
+
return next;
|
|
102
|
+
});
|
|
103
|
+
}, []);
|
|
104
|
+
|
|
105
|
+
const deselectNode = useCallback((id) => {
|
|
106
|
+
setSelectedIds((prev) => {
|
|
107
|
+
const next = new Set(prev);
|
|
108
|
+
next.delete(id);
|
|
109
|
+
return next;
|
|
110
|
+
});
|
|
111
|
+
}, []);
|
|
112
|
+
|
|
113
|
+
const toggleSelection = useCallback((id) => {
|
|
114
|
+
setSelectedIds((prev) => {
|
|
115
|
+
const next = new Set(prev);
|
|
116
|
+
next.has(id) ? next.delete(id) : next.add(id);
|
|
117
|
+
return next;
|
|
118
|
+
});
|
|
119
|
+
}, []);
|
|
120
|
+
|
|
121
|
+
const clearSelection = useCallback(() => setSelectedIds(new Set()), []);
|
|
122
|
+
|
|
123
|
+
const selectMultiple = useCallback((ids) => setSelectedIds(new Set(ids)), []);
|
|
124
|
+
|
|
125
|
+
const addToSelection = useCallback(
|
|
126
|
+
(ids) => setSelectedIds((prev) => new Set([...prev, ...ids])),
|
|
127
|
+
[]
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const isSelected = useCallback((id) => selectedIds.has(id), [selectedIds]);
|
|
131
|
+
|
|
132
|
+
const registerNodeHandlers = useCallback((id, handlers) => {
|
|
133
|
+
nodeHandlersRef.current.set(id, handlers);
|
|
134
|
+
return () => nodeHandlersRef.current.delete(id);
|
|
135
|
+
}, []);
|
|
136
|
+
|
|
137
|
+
const moveSelectedNodes = useCallback(
|
|
138
|
+
(deltaX, deltaY, excludeId = null) => {
|
|
139
|
+
selectedIds.forEach((id) => {
|
|
140
|
+
if (id === excludeId) return;
|
|
141
|
+
|
|
142
|
+
const handlers = nodeHandlersRef.current.get(id);
|
|
143
|
+
if (!handlers) return;
|
|
144
|
+
|
|
145
|
+
handlers.setOffset?.((prev) => ({
|
|
146
|
+
x: prev.x + deltaX,
|
|
147
|
+
y: prev.y + deltaY,
|
|
148
|
+
}));
|
|
149
|
+
handlers.onDrag?.();
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
[selectedIds]
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const cutSelectedNodes = useCallback(
|
|
156
|
+
(nodesById, onCut) => {
|
|
157
|
+
if (!nodesById || selectedIds.size === 0) return;
|
|
158
|
+
|
|
159
|
+
const selectedSet = new Set(selectedIds);
|
|
160
|
+
const seenIds = new Set();
|
|
161
|
+
const cutNodes = [];
|
|
162
|
+
|
|
163
|
+
[...selectedIds].forEach((id) => {
|
|
164
|
+
if (!nodesById[id] || seenIds.has(id)) return;
|
|
165
|
+
seenIds.add(id);
|
|
166
|
+
cutNodes.push(buildClipboardNode(id, nodesById[id], selectedSet));
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
setClipboard(cutNodes);
|
|
170
|
+
onCut?.([...selectedIds]);
|
|
171
|
+
setSelectedIds(new Set());
|
|
172
|
+
},
|
|
173
|
+
[selectedIds]
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const pasteNodes = useCallback(
|
|
177
|
+
(callback, x = 0, y = 0) => {
|
|
178
|
+
if (!clipboard.length || !callback || isPastingRef.current) return;
|
|
179
|
+
|
|
180
|
+
isPastingRef.current = true;
|
|
181
|
+
const nodesToPaste = [...clipboard];
|
|
182
|
+
|
|
183
|
+
const pasteData = buildPasteStructure(nodesToPaste, { x, y });
|
|
184
|
+
if (!pasteData) {
|
|
185
|
+
isPastingRef.current = false;
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let result;
|
|
190
|
+
try {
|
|
191
|
+
result = callback(pasteData, { x, y });
|
|
192
|
+
} catch (error) {
|
|
193
|
+
isPastingRef.current = false;
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (result && typeof result.then === "function") {
|
|
198
|
+
return result
|
|
199
|
+
.then(() => {
|
|
200
|
+
setClipboard([]);
|
|
201
|
+
})
|
|
202
|
+
.finally(() => {
|
|
203
|
+
isPastingRef.current = false;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
setClipboard([]);
|
|
208
|
+
isPastingRef.current = false;
|
|
209
|
+
return result;
|
|
210
|
+
},
|
|
211
|
+
[clipboard]
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const hasClipboard = clipboard.length > 0;
|
|
215
|
+
|
|
216
|
+
const contextValue = useMemo(
|
|
217
|
+
() => ({
|
|
218
|
+
selectedIds,
|
|
219
|
+
selectNode,
|
|
220
|
+
deselectNode,
|
|
221
|
+
toggleSelection,
|
|
222
|
+
clearSelection,
|
|
223
|
+
selectMultiple,
|
|
224
|
+
addToSelection,
|
|
225
|
+
isSelected,
|
|
226
|
+
registerNodeHandlers,
|
|
227
|
+
moveSelectedNodes,
|
|
228
|
+
cutSelectedNodes,
|
|
229
|
+
pasteNodes,
|
|
230
|
+
hasClipboard,
|
|
231
|
+
}),
|
|
232
|
+
[
|
|
233
|
+
selectedIds,
|
|
234
|
+
selectNode,
|
|
235
|
+
deselectNode,
|
|
236
|
+
toggleSelection,
|
|
237
|
+
clearSelection,
|
|
238
|
+
selectMultiple,
|
|
239
|
+
addToSelection,
|
|
240
|
+
isSelected,
|
|
241
|
+
registerNodeHandlers,
|
|
242
|
+
moveSelectedNodes,
|
|
243
|
+
cutSelectedNodes,
|
|
244
|
+
pasteNodes,
|
|
245
|
+
hasClipboard,
|
|
246
|
+
]
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<SelectionContext.Provider value={contextValue}>
|
|
251
|
+
{children}
|
|
252
|
+
</SelectionContext.Provider>
|
|
253
|
+
);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
export const useSelection = () =>
|
|
257
|
+
useContext(SelectionContext) || DEFAULT_CONTEXT;
|
|
258
|
+
|
|
259
|
+
export default SelectionContext;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Box } from "@mui/material";
|
|
2
|
+
import { hexToRgba } from "../utils/flowUtils";
|
|
3
|
+
|
|
4
|
+
const SelectionOverlay = ({ box, selectionColor = "#64748b" }) => {
|
|
5
|
+
if (!box) return null;
|
|
6
|
+
|
|
7
|
+
const { startX, startY, currentX, currentY } = box;
|
|
8
|
+
const left = Math.min(startX, currentX);
|
|
9
|
+
const top = Math.min(startY, currentY);
|
|
10
|
+
const width = Math.abs(currentX - startX);
|
|
11
|
+
const height = Math.abs(currentY - startY);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<Box
|
|
15
|
+
sx={{
|
|
16
|
+
position: "fixed",
|
|
17
|
+
left,
|
|
18
|
+
top,
|
|
19
|
+
width,
|
|
20
|
+
height,
|
|
21
|
+
border: `2px solid ${selectionColor}`,
|
|
22
|
+
backgroundColor: hexToRgba(selectionColor, 0.1),
|
|
23
|
+
pointerEvents: "none",
|
|
24
|
+
zIndex: 9999,
|
|
25
|
+
borderRadius: "4px",
|
|
26
|
+
}}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default SelectionOverlay;
|