panelui-native 0.62.0 → 0.65.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/README.md +5 -0
- package/lib/module/components/area-chart/index.js +14 -4
- package/lib/module/components/area-chart/index.js.map +1 -1
- package/lib/module/components/badge/index.js +1 -1
- package/lib/module/components/badge/index.js.map +1 -1
- package/lib/module/components/bar-chart/index.js +53 -6
- package/lib/module/components/bar-chart/index.js.map +1 -1
- package/lib/module/components/bottom-sheet/index.js +2 -2
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/button/index.js +19 -9
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/candlestick-chart/index.js +14 -4
- package/lib/module/components/candlestick-chart/index.js.map +1 -1
- package/lib/module/components/card/index.js +26 -1
- package/lib/module/components/card/index.js.map +1 -1
- package/lib/module/components/carousel/index.js +28 -14
- package/lib/module/components/carousel/index.js.map +1 -1
- package/lib/module/components/checkbox/index.js +2 -3
- package/lib/module/components/checkbox/index.js.map +1 -1
- package/lib/module/components/dialog/index.js +51 -5
- package/lib/module/components/dialog/index.js.map +1 -1
- package/lib/module/components/drawer/index.js +2 -2
- package/lib/module/components/drawer/index.js.map +1 -1
- package/lib/module/components/fab/index.js +4 -3
- package/lib/module/components/fab/index.js.map +1 -1
- package/lib/module/components/field/index.js +41 -6
- package/lib/module/components/field/index.js.map +1 -1
- package/lib/module/components/flow/flow-accessibility.js +84 -0
- package/lib/module/components/flow/flow-accessibility.js.map +1 -0
- package/lib/module/components/flow/index.js +100 -39
- package/lib/module/components/flow/index.js.map +1 -1
- package/lib/module/components/form/use-form.js +93 -66
- package/lib/module/components/form/use-form.js.map +1 -1
- package/lib/module/components/heatmap-chart/index.js +2 -2
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/hex-chart/index.js +4 -4
- package/lib/module/components/hex-chart/index.js.map +1 -1
- package/lib/module/components/kpi/index.js +3 -1
- package/lib/module/components/kpi/index.js.map +1 -1
- package/lib/module/components/line-chart/index.js +17 -7
- package/lib/module/components/line-chart/index.js.map +1 -1
- package/lib/module/components/map/index.js +71 -8
- package/lib/module/components/map/index.js.map +1 -1
- package/lib/module/components/map/map-accessibility.js +20 -0
- package/lib/module/components/map/map-accessibility.js.map +1 -0
- package/lib/module/components/map/maplibre.js.map +1 -1
- package/lib/module/components/menu/index.js +4 -1
- package/lib/module/components/menu/index.js.map +1 -1
- package/lib/module/components/number-input/index.js +1 -23
- package/lib/module/components/number-input/index.js.map +1 -1
- package/lib/module/components/number-input/number-input-math.js +44 -0
- package/lib/module/components/number-input/number-input-math.js.map +1 -0
- package/lib/module/components/pie-chart/index.js +2 -2
- package/lib/module/components/pie-chart/index.js.map +1 -1
- package/lib/module/components/plot/index.js +1329 -0
- package/lib/module/components/plot/index.js.map +1 -0
- package/lib/module/components/polar-area-chart/index.js +2 -2
- package/lib/module/components/polar-area-chart/index.js.map +1 -1
- package/lib/module/components/radar-chart/index.js +14 -4
- package/lib/module/components/radar-chart/index.js.map +1 -1
- package/lib/module/components/radio-group/index.js +1 -2
- package/lib/module/components/radio-group/index.js.map +1 -1
- package/lib/module/components/ring-chart/index.js +2 -2
- package/lib/module/components/ring-chart/index.js.map +1 -1
- package/lib/module/components/section-rail/index.js +120 -15
- package/lib/module/components/section-rail/index.js.map +1 -1
- package/lib/module/components/selection-mode/index.js +129 -18
- package/lib/module/components/selection-mode/index.js.map +1 -1
- package/lib/module/components/sortable/index.js +20 -60
- package/lib/module/components/sortable/index.js.map +1 -1
- package/lib/module/components/sortable/reorder.js +63 -0
- package/lib/module/components/sortable/reorder.js.map +1 -0
- package/lib/module/components/swipe/index.js +4 -4
- package/lib/module/components/swipe/index.js.map +1 -1
- package/lib/module/components/switch/index.js +36 -3
- package/lib/module/components/switch/index.js.map +1 -1
- package/lib/module/components/time-picker/accessibility.js +21 -0
- package/lib/module/components/time-picker/accessibility.js.map +1 -0
- package/lib/module/components/time-picker/index.js +34 -8
- package/lib/module/components/time-picker/index.js.map +1 -1
- package/lib/module/components/waterfall-chart/index.js +1325 -0
- package/lib/module/components/waterfall-chart/index.js.map +1 -0
- package/lib/module/index.js +8 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/modal-isolation-store.js +29 -0
- package/lib/module/primitives/modal-isolation-store.js.map +1 -0
- package/lib/module/primitives/portal.js +36 -4
- package/lib/module/primitives/portal.js.map +1 -1
- package/lib/module/providers/panel-ui-provider.js +27 -6
- package/lib/module/providers/panel-ui-provider.js.map +1 -1
- package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/badge/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/candlestick-chart/index.d.ts +6 -3
- package/lib/typescript/src/components/candlestick-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/card/index.d.ts +48 -1
- package/lib/typescript/src/components/card/index.d.ts.map +1 -1
- package/lib/typescript/src/components/carousel/index.d.ts +1 -1
- package/lib/typescript/src/components/carousel/index.d.ts.map +1 -1
- package/lib/typescript/src/components/checkbox/index.d.ts.map +1 -1
- package/lib/typescript/src/components/dialog/index.d.ts +47 -2
- package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
- package/lib/typescript/src/components/fab/index.d.ts.map +1 -1
- package/lib/typescript/src/components/field/index.d.ts +8 -1
- package/lib/typescript/src/components/field/index.d.ts.map +1 -1
- package/lib/typescript/src/components/flow/flow-accessibility.d.ts +49 -0
- package/lib/typescript/src/components/flow/flow-accessibility.d.ts.map +1 -0
- package/lib/typescript/src/components/flow/index.d.ts +14 -2
- package/lib/typescript/src/components/flow/index.d.ts.map +1 -1
- package/lib/typescript/src/components/form/use-form.d.ts.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/hex-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/kpi/index.d.ts.map +1 -1
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/map/index.d.ts +13 -5
- package/lib/typescript/src/components/map/index.d.ts.map +1 -1
- package/lib/typescript/src/components/map/map-accessibility.d.ts +19 -0
- package/lib/typescript/src/components/map/map-accessibility.d.ts.map +1 -0
- package/lib/typescript/src/components/map/maplibre.d.ts +2 -0
- package/lib/typescript/src/components/map/maplibre.d.ts.map +1 -1
- package/lib/typescript/src/components/menu/index.d.ts.map +1 -1
- package/lib/typescript/src/components/number-input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/number-input/number-input-math.d.ts +9 -0
- package/lib/typescript/src/components/number-input/number-input-math.d.ts.map +1 -0
- package/lib/typescript/src/components/plot/index.d.ts +471 -0
- package/lib/typescript/src/components/plot/index.d.ts.map +1 -0
- package/lib/typescript/src/components/radar-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/section-rail/index.d.ts +8 -1
- package/lib/typescript/src/components/section-rail/index.d.ts.map +1 -1
- package/lib/typescript/src/components/selection-mode/index.d.ts +82 -6
- package/lib/typescript/src/components/selection-mode/index.d.ts.map +1 -1
- package/lib/typescript/src/components/sortable/index.d.ts.map +1 -1
- package/lib/typescript/src/components/sortable/reorder.d.ts +14 -0
- package/lib/typescript/src/components/sortable/reorder.d.ts.map +1 -0
- package/lib/typescript/src/components/swipe/index.d.ts.map +1 -1
- package/lib/typescript/src/components/switch/index.d.ts +3 -3
- package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
- package/lib/typescript/src/components/time-picker/accessibility.d.ts +10 -0
- package/lib/typescript/src/components/time-picker/accessibility.d.ts.map +1 -0
- package/lib/typescript/src/components/time-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/waterfall-chart/index.d.ts +379 -0
- package/lib/typescript/src/components/waterfall-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/modal-isolation-store.d.ts +10 -0
- package/lib/typescript/src/primitives/modal-isolation-store.d.ts.map +1 -0
- package/lib/typescript/src/primitives/portal.d.ts +6 -0
- package/lib/typescript/src/primitives/portal.d.ts.map +1 -1
- package/lib/typescript/src/providers/panel-ui-provider.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/components/area-chart/index.tsx +14 -4
- package/src/components/badge/index.tsx +4 -1
- package/src/components/bar-chart/index.tsx +54 -7
- package/src/components/bottom-sheet/index.tsx +3 -3
- package/src/components/button/index.tsx +15 -21
- package/src/components/candlestick-chart/index.tsx +20 -7
- package/src/components/card/index.tsx +32 -7
- package/src/components/carousel/index.tsx +42 -20
- package/src/components/checkbox/index.tsx +1 -2
- package/src/components/dialog/index.tsx +58 -5
- package/src/components/drawer/index.tsx +3 -3
- package/src/components/fab/index.tsx +8 -12
- package/src/components/field/index.tsx +52 -10
- package/src/components/flow/flow-accessibility.ts +109 -0
- package/src/components/flow/index.tsx +162 -50
- package/src/components/form/use-form.ts +101 -65
- package/src/components/heatmap-chart/index.tsx +2 -3
- package/src/components/hex-chart/index.tsx +4 -5
- package/src/components/kpi/index.tsx +3 -1
- package/src/components/line-chart/index.tsx +17 -6
- package/src/components/map/index.tsx +110 -7
- package/src/components/map/map-accessibility.ts +41 -0
- package/src/components/map/maplibre.ts +2 -0
- package/src/components/menu/index.tsx +4 -1
- package/src/components/number-input/index.tsx +1 -26
- package/src/components/number-input/number-input-math.ts +50 -0
- package/src/components/pie-chart/index.tsx +2 -2
- package/src/components/plot/index.tsx +1565 -0
- package/src/components/polar-area-chart/index.tsx +2 -2
- package/src/components/radar-chart/index.tsx +14 -4
- package/src/components/radio-group/index.tsx +1 -2
- package/src/components/ring-chart/index.tsx +2 -2
- package/src/components/section-rail/index.tsx +133 -14
- package/src/components/selection-mode/index.tsx +163 -26
- package/src/components/sortable/index.tsx +27 -69
- package/src/components/sortable/reorder.ts +88 -0
- package/src/components/swipe/index.tsx +7 -4
- package/src/components/switch/index.tsx +42 -6
- package/src/components/time-picker/accessibility.ts +24 -0
- package/src/components/time-picker/index.tsx +48 -2
- package/src/components/waterfall-chart/index.tsx +1589 -0
- package/src/index.ts +69 -0
- package/src/primitives/modal-isolation-store.ts +31 -0
- package/src/primitives/portal.tsx +34 -4
- package/src/providers/panel-ui-provider.tsx +41 -9
- package/theme.css +54 -3
|
@@ -18,7 +18,12 @@
|
|
|
18
18
|
* <Field.Title>Marketing emails</Field.Title>
|
|
19
19
|
* <Field.Description>Product updates, at most weekly.</Field.Description>
|
|
20
20
|
* </Field.Content>
|
|
21
|
-
* <Switch
|
|
21
|
+
* <Switch
|
|
22
|
+
* value={subscribed}
|
|
23
|
+
* onValueChange={setSubscribed}
|
|
24
|
+
* accessibilityLabel="Marketing emails"
|
|
25
|
+
* accessibilityHint="Product updates, at most weekly."
|
|
26
|
+
* />
|
|
22
27
|
* </Field>
|
|
23
28
|
* ```
|
|
24
29
|
*/
|
|
@@ -26,6 +31,7 @@ import {
|
|
|
26
31
|
createContext,
|
|
27
32
|
forwardRef,
|
|
28
33
|
useContext,
|
|
34
|
+
useId,
|
|
29
35
|
useMemo,
|
|
30
36
|
type ReactNode,
|
|
31
37
|
} from 'react';
|
|
@@ -73,14 +79,22 @@ interface FieldState {
|
|
|
73
79
|
invalid: boolean;
|
|
74
80
|
disabled: boolean;
|
|
75
81
|
required: boolean;
|
|
82
|
+
relationshipIds: string[];
|
|
76
83
|
}
|
|
77
84
|
|
|
78
85
|
const FieldContext = createContext<FieldState>({
|
|
79
86
|
invalid: false,
|
|
80
87
|
disabled: false,
|
|
81
88
|
required: false,
|
|
89
|
+
relationshipIds: [],
|
|
82
90
|
});
|
|
83
91
|
|
|
92
|
+
/** Android can announce the label, help and error text rendered beside a control. */
|
|
93
|
+
export function useFieldLabelledBy(): string[] | undefined {
|
|
94
|
+
const { relationshipIds } = useContext(FieldContext);
|
|
95
|
+
return relationshipIds.length > 0 ? relationshipIds : undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
84
98
|
export interface FieldProps extends ViewProps {
|
|
85
99
|
className?: string;
|
|
86
100
|
/** `horizontal` puts a label beside the control instead of above it. */
|
|
@@ -107,9 +121,19 @@ const FieldRoot = forwardRef<View, FieldProps>(
|
|
|
107
121
|
ref
|
|
108
122
|
) => {
|
|
109
123
|
const { root } = fieldVariants({ orientation, disabled });
|
|
124
|
+
const id = useId().replace(/:/g, '');
|
|
125
|
+
const labelId = `panelui-field-${id}-label`;
|
|
126
|
+
const titleId = `panelui-field-${id}-title`;
|
|
127
|
+
const descriptionId = `panelui-field-${id}-description`;
|
|
128
|
+
const errorId = `panelui-field-${id}-error`;
|
|
110
129
|
const state = useMemo(
|
|
111
|
-
() => ({
|
|
112
|
-
|
|
130
|
+
() => ({
|
|
131
|
+
invalid,
|
|
132
|
+
disabled,
|
|
133
|
+
required,
|
|
134
|
+
relationshipIds: [labelId, titleId, descriptionId, errorId],
|
|
135
|
+
}),
|
|
136
|
+
[invalid, disabled, required, labelId, titleId, descriptionId, errorId]
|
|
113
137
|
);
|
|
114
138
|
|
|
115
139
|
return (
|
|
@@ -145,7 +169,7 @@ FieldContent.displayName = 'Field.Content';
|
|
|
145
169
|
export interface FieldLabelProps extends LabelProps {}
|
|
146
170
|
|
|
147
171
|
const FieldLabel = forwardRef<View, FieldLabelProps>(
|
|
148
|
-
({ isRequired, isInvalid, isDisabled, ...props }, ref) => {
|
|
172
|
+
({ isRequired, isInvalid, isDisabled, nativeID, ...props }, ref) => {
|
|
149
173
|
const ctx = useContext(FieldContext);
|
|
150
174
|
return (
|
|
151
175
|
<Label
|
|
@@ -153,6 +177,7 @@ const FieldLabel = forwardRef<View, FieldLabelProps>(
|
|
|
153
177
|
isRequired={isRequired ?? ctx.required}
|
|
154
178
|
isInvalid={isInvalid ?? ctx.invalid}
|
|
155
179
|
isDisabled={isDisabled ?? ctx.disabled}
|
|
180
|
+
nativeID={nativeID ?? ctx.relationshipIds[0]}
|
|
156
181
|
{...props}
|
|
157
182
|
/>
|
|
158
183
|
);
|
|
@@ -165,10 +190,17 @@ export interface FieldDescriptionProps extends TextProps {
|
|
|
165
190
|
}
|
|
166
191
|
|
|
167
192
|
const FieldDescription = forwardRef<RNText, FieldDescriptionProps>(
|
|
168
|
-
({ className, ...props }, ref) => {
|
|
169
|
-
const { disabled } = useContext(FieldContext);
|
|
193
|
+
({ className, nativeID, ...props }, ref) => {
|
|
194
|
+
const { disabled, relationshipIds } = useContext(FieldContext);
|
|
170
195
|
const { description } = fieldVariants({ disabled });
|
|
171
|
-
return
|
|
196
|
+
return (
|
|
197
|
+
<Text
|
|
198
|
+
ref={ref}
|
|
199
|
+
nativeID={nativeID ?? relationshipIds[2]}
|
|
200
|
+
className={description({ className })}
|
|
201
|
+
{...props}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
172
204
|
}
|
|
173
205
|
);
|
|
174
206
|
FieldDescription.displayName = 'Field.Description';
|
|
@@ -186,7 +218,8 @@ export interface FieldErrorProps extends Omit<ViewProps, 'children'> {
|
|
|
186
218
|
}
|
|
187
219
|
|
|
188
220
|
const FieldError = forwardRef<View, FieldErrorProps>(
|
|
189
|
-
({ className, errors, children, ...props }, ref) => {
|
|
221
|
+
({ className, errors, children, nativeID, ...props }, ref) => {
|
|
222
|
+
const { relationshipIds } = useContext(FieldContext);
|
|
190
223
|
const messages = useMemo(() => {
|
|
191
224
|
if (!errors) return null;
|
|
192
225
|
const seen = new Set<string>();
|
|
@@ -213,6 +246,7 @@ const FieldError = forwardRef<View, FieldErrorProps>(
|
|
|
213
246
|
// accessibilityRole list has no alert, the ARIA-aligned one does.
|
|
214
247
|
role="alert"
|
|
215
248
|
accessibilityLiveRegion="polite"
|
|
249
|
+
nativeID={nativeID ?? relationshipIds[3]}
|
|
216
250
|
className={error({ className })}
|
|
217
251
|
{...props}
|
|
218
252
|
>
|
|
@@ -307,9 +341,17 @@ export interface FieldTitleProps extends TextProps {
|
|
|
307
341
|
}
|
|
308
342
|
|
|
309
343
|
const FieldTitle = forwardRef<RNText, FieldTitleProps>(
|
|
310
|
-
({ className, ...props }, ref) => {
|
|
344
|
+
({ className, nativeID, ...props }, ref) => {
|
|
311
345
|
const { title } = fieldVariants();
|
|
312
|
-
|
|
346
|
+
const { relationshipIds } = useContext(FieldContext);
|
|
347
|
+
return (
|
|
348
|
+
<Text
|
|
349
|
+
ref={ref}
|
|
350
|
+
nativeID={nativeID ?? relationshipIds[1]}
|
|
351
|
+
className={title({ className })}
|
|
352
|
+
{...props}
|
|
353
|
+
/>
|
|
354
|
+
);
|
|
313
355
|
}
|
|
314
356
|
);
|
|
315
357
|
FieldTitle.displayName = 'Field.Title';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { FlowConnection, FlowNodePosition } from '.';
|
|
2
|
+
import type { FlowRect } from './flow-paths';
|
|
3
|
+
|
|
4
|
+
export const FLOW_MOVE_ACTIONS = [
|
|
5
|
+
{ name: 'flow-move-up', label: 'Move up', dx: 0, dy: -1 },
|
|
6
|
+
{ name: 'flow-move-right', label: 'Move right', dx: 1, dy: 0 },
|
|
7
|
+
{ name: 'flow-move-down', label: 'Move down', dx: 0, dy: 1 },
|
|
8
|
+
{ name: 'flow-move-left', label: 'Move left', dx: -1, dy: 0 },
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
export const FLOW_DELETE_ACTION = { name: 'flow-delete', label: 'Delete node' } as const;
|
|
12
|
+
|
|
13
|
+
export interface FlowAccessibilityNode {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface FlowAccessibilityHandle {
|
|
19
|
+
key: string;
|
|
20
|
+
node: string;
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
type: 'source' | 'target' | 'both';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface FlowConnectionAction {
|
|
27
|
+
name: string;
|
|
28
|
+
label: string;
|
|
29
|
+
connection: FlowConnection;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Keep pointer and assistive-technology movement inside the same group bounds. */
|
|
33
|
+
export function clampNodePosition(
|
|
34
|
+
rect: FlowRect,
|
|
35
|
+
bounds: FlowRect | undefined,
|
|
36
|
+
position: FlowNodePosition
|
|
37
|
+
): FlowNodePosition {
|
|
38
|
+
'worklet';
|
|
39
|
+
if (!bounds) return position;
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
x: Math.min(Math.max(position.x, bounds.x), bounds.x + bounds.width - rect.width),
|
|
43
|
+
y: Math.min(Math.max(position.y, bounds.y), bounds.y + bounds.height - rect.height),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function moveNodePosition(
|
|
48
|
+
rect: FlowRect,
|
|
49
|
+
bounds: FlowRect | undefined,
|
|
50
|
+
actionName: string,
|
|
51
|
+
step: number
|
|
52
|
+
): FlowNodePosition | undefined {
|
|
53
|
+
const action = FLOW_MOVE_ACTIONS.find((entry) => entry.name === actionName);
|
|
54
|
+
if (!action) return undefined;
|
|
55
|
+
|
|
56
|
+
return clampNodePosition(rect, bounds, {
|
|
57
|
+
x: rect.x + action.dx * Math.max(1, step),
|
|
58
|
+
y: rect.y + action.dy * Math.max(1, step),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Build connection choices from the same registered nodes and handles used by drag hit-testing. */
|
|
63
|
+
export function getFlowConnectionActions(
|
|
64
|
+
sourceNode: string,
|
|
65
|
+
nodes: FlowAccessibilityNode[],
|
|
66
|
+
handles: FlowAccessibilityHandle[]
|
|
67
|
+
): FlowConnectionAction[] {
|
|
68
|
+
const sources = handles.filter(
|
|
69
|
+
(handle) => handle.node === sourceNode && handle.type !== 'target'
|
|
70
|
+
);
|
|
71
|
+
if (sources.length === 0) return [];
|
|
72
|
+
|
|
73
|
+
const actions: FlowConnectionAction[] = [];
|
|
74
|
+
for (const source of sources) {
|
|
75
|
+
for (const targetNode of nodes) {
|
|
76
|
+
if (targetNode.id === sourceNode) continue;
|
|
77
|
+
const targets = handles.filter(
|
|
78
|
+
(handle) => handle.node === targetNode.id && handle.type !== 'source'
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
if (targets.length === 0) {
|
|
82
|
+
actions.push({
|
|
83
|
+
name: `flow-connect:${source.key}->${targetNode.id}`,
|
|
84
|
+
label: `Connect ${source.label} to ${targetNode.label}`,
|
|
85
|
+
connection: {
|
|
86
|
+
source: sourceNode,
|
|
87
|
+
sourceHandle: source.id,
|
|
88
|
+
target: targetNode.id,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const target of targets) {
|
|
95
|
+
actions.push({
|
|
96
|
+
name: `flow-connect:${source.key}->${target.key}`,
|
|
97
|
+
label: `Connect ${source.label} to ${targetNode.label}, ${target.label}`,
|
|
98
|
+
connection: {
|
|
99
|
+
source: sourceNode,
|
|
100
|
+
sourceHandle: source.id,
|
|
101
|
+
target: target.node,
|
|
102
|
+
targetHandle: target.id,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return actions;
|
|
109
|
+
}
|
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
useCallback,
|
|
49
49
|
useContext,
|
|
50
50
|
useEffect,
|
|
51
|
+
useId,
|
|
51
52
|
useMemo,
|
|
52
53
|
useRef,
|
|
53
54
|
useState,
|
|
@@ -97,6 +98,15 @@ import {
|
|
|
97
98
|
type FlowRect,
|
|
98
99
|
type FlowSide,
|
|
99
100
|
} from './flow-paths';
|
|
101
|
+
import {
|
|
102
|
+
clampNodePosition,
|
|
103
|
+
FLOW_DELETE_ACTION,
|
|
104
|
+
FLOW_MOVE_ACTIONS,
|
|
105
|
+
getFlowConnectionActions,
|
|
106
|
+
moveNodePosition,
|
|
107
|
+
type FlowAccessibilityHandle,
|
|
108
|
+
type FlowAccessibilityNode,
|
|
109
|
+
} from './flow-accessibility';
|
|
100
110
|
|
|
101
111
|
const AnimatedPath = Animated.createAnimatedComponent(Path);
|
|
102
112
|
const AnimatedRect = Animated.createAnimatedComponent(Rect);
|
|
@@ -167,13 +177,9 @@ export interface FlowConnection {
|
|
|
167
177
|
targetHandle?: string;
|
|
168
178
|
}
|
|
169
179
|
|
|
170
|
-
interface HandleEntry {
|
|
171
|
-
key: string;
|
|
172
|
-
node: string;
|
|
173
|
-
id: string;
|
|
180
|
+
interface HandleEntry extends FlowAccessibilityHandle {
|
|
174
181
|
side: FlowSide;
|
|
175
182
|
offset: number;
|
|
176
|
-
type: 'source' | 'target' | 'both';
|
|
177
183
|
}
|
|
178
184
|
|
|
179
185
|
interface FlowContextValue {
|
|
@@ -215,7 +221,7 @@ interface FlowContextValue {
|
|
|
215
221
|
maxZoom: number;
|
|
216
222
|
locked: boolean;
|
|
217
223
|
setLocked: (locked: boolean) => void;
|
|
218
|
-
|
|
224
|
+
nodes: FlowAccessibilityNode[];
|
|
219
225
|
/**
|
|
220
226
|
* Which of `nodeIds` are containers rather than nodes. A group writes its box
|
|
221
227
|
* into the same registry — that is what lets an edge name one and the minimap
|
|
@@ -224,7 +230,7 @@ interface FlowContextValue {
|
|
|
224
230
|
groupIds: string[];
|
|
225
231
|
handles: HandleEntry[];
|
|
226
232
|
edges: { key: string; props: FlowEdgeProps }[];
|
|
227
|
-
registerNode: (id: string) => void;
|
|
233
|
+
registerNode: (id: string, label?: string) => void;
|
|
228
234
|
unregisterNode: (id: string) => void;
|
|
229
235
|
registerGroup: (id: string) => void;
|
|
230
236
|
unregisterGroup: (id: string) => void;
|
|
@@ -232,8 +238,8 @@ interface FlowContextValue {
|
|
|
232
238
|
unregisterHandle: (key: string) => void;
|
|
233
239
|
registerEdge: (key: string, props: FlowEdgeProps) => void;
|
|
234
240
|
unregisterEdge: (key: string) => void;
|
|
235
|
-
|
|
236
|
-
|
|
241
|
+
connectNodes: (connection: FlowConnection) => void;
|
|
242
|
+
canConnect: boolean;
|
|
237
243
|
onNodeDragEnd?: (id: string, position: FlowNodePosition) => void;
|
|
238
244
|
fitView: () => void;
|
|
239
245
|
zoomBy: (factor: number) => void;
|
|
@@ -332,7 +338,7 @@ function FlowRoot({
|
|
|
332
338
|
const size = useSharedValue({ width: 0, height: 0 });
|
|
333
339
|
|
|
334
340
|
const [locked, setLocked] = useState(false);
|
|
335
|
-
const [
|
|
341
|
+
const [nodes, setNodes] = useState<FlowAccessibilityNode[]>([]);
|
|
336
342
|
const [groupIds, setGroupIds] = useState<string[]>([]);
|
|
337
343
|
const [handles, setHandles] = useState<HandleEntry[]>([]);
|
|
338
344
|
const [edges, setEdges] = useState<{ key: string; props: FlowEdgeProps }[]>([]);
|
|
@@ -441,12 +447,12 @@ function FlowRoot({
|
|
|
441
447
|
viewportChangeRef.current?.({ x, y, zoom: z });
|
|
442
448
|
}, []);
|
|
443
449
|
|
|
444
|
-
const registerNode = useCallback((id: string) => {
|
|
445
|
-
|
|
450
|
+
const registerNode = useCallback((id: string, label = id) => {
|
|
451
|
+
setNodes((current) => [...current.filter((entry) => entry.id !== id), { id, label }]);
|
|
446
452
|
}, []);
|
|
447
453
|
|
|
448
454
|
const unregisterNode = useCallback((id: string) => {
|
|
449
|
-
|
|
455
|
+
setNodes((current) => current.filter((entry) => entry.id !== id));
|
|
450
456
|
}, []);
|
|
451
457
|
|
|
452
458
|
const registerGroup = useCallback((id: string) => {
|
|
@@ -476,6 +482,15 @@ function FlowRoot({
|
|
|
476
482
|
setEdges((current) => current.filter((edge) => edge.key !== key));
|
|
477
483
|
}, []);
|
|
478
484
|
|
|
485
|
+
const connectRef = useRef(onConnect);
|
|
486
|
+
connectRef.current = onConnect;
|
|
487
|
+
const validConnectionRef = useRef(isValidConnection);
|
|
488
|
+
validConnectionRef.current = isValidConnection;
|
|
489
|
+
const connectNodes = useCallback((next: FlowConnection) => {
|
|
490
|
+
if (validConnectionRef.current && !validConnectionRef.current(next)) return;
|
|
491
|
+
connectRef.current?.(next);
|
|
492
|
+
}, []);
|
|
493
|
+
|
|
479
494
|
const fitView = useCallback(() => {
|
|
480
495
|
const { width, height } = size.value;
|
|
481
496
|
const all = Object.values(rects.value);
|
|
@@ -549,10 +564,10 @@ function FlowRoot({
|
|
|
549
564
|
// mount has nothing to fit to yet. One frame later they do.
|
|
550
565
|
const fitOnMount = useRef(fitViewOnMount);
|
|
551
566
|
useEffect(() => {
|
|
552
|
-
if (!fitOnMount.current ||
|
|
567
|
+
if (!fitOnMount.current || nodes.length === 0) return;
|
|
553
568
|
const timer = setTimeout(fitView, 32);
|
|
554
569
|
return () => clearTimeout(timer);
|
|
555
|
-
}, [fitView,
|
|
570
|
+
}, [fitView, nodes.length]);
|
|
556
571
|
|
|
557
572
|
const panStart = useSharedValue({ x: 0, y: 0 });
|
|
558
573
|
const pinchStart = useSharedValue({ zoom: 1, x: 0, y: 0 });
|
|
@@ -647,7 +662,7 @@ function FlowRoot({
|
|
|
647
662
|
maxZoom,
|
|
648
663
|
locked,
|
|
649
664
|
setLocked,
|
|
650
|
-
|
|
665
|
+
nodes,
|
|
651
666
|
groupIds,
|
|
652
667
|
handles,
|
|
653
668
|
edges,
|
|
@@ -659,8 +674,8 @@ function FlowRoot({
|
|
|
659
674
|
unregisterHandle,
|
|
660
675
|
registerEdge,
|
|
661
676
|
unregisterEdge,
|
|
662
|
-
|
|
663
|
-
|
|
677
|
+
connectNodes,
|
|
678
|
+
canConnect: Boolean(onConnect),
|
|
664
679
|
onNodeDragEnd,
|
|
665
680
|
fitView,
|
|
666
681
|
zoomBy,
|
|
@@ -678,11 +693,11 @@ function FlowRoot({
|
|
|
678
693
|
handles,
|
|
679
694
|
registerEdge,
|
|
680
695
|
unregisterEdge,
|
|
681
|
-
|
|
696
|
+
connectNodes,
|
|
682
697
|
locked,
|
|
683
698
|
maxZoom,
|
|
684
699
|
minZoom,
|
|
685
|
-
|
|
700
|
+
nodes,
|
|
686
701
|
groupIds,
|
|
687
702
|
onConnect,
|
|
688
703
|
onNodeDragEnd,
|
|
@@ -856,7 +871,7 @@ function FlowBackground({
|
|
|
856
871
|
const token = useCSSVariable('--color-muted-foreground');
|
|
857
872
|
const tint = color ?? (typeof token === 'string' ? token : '#737373');
|
|
858
873
|
// A pattern is referenced by id, and two canvases on one screen would collide.
|
|
859
|
-
const id =
|
|
874
|
+
const id = `panelui-flow-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
|
|
860
875
|
|
|
861
876
|
/*
|
|
862
877
|
* The grid is a tile that follows the viewport in whole-cell steps.
|
|
@@ -972,6 +987,13 @@ export interface FlowNodeProps extends Omit<ViewProps, 'children'> {
|
|
|
972
987
|
selected?: boolean;
|
|
973
988
|
/** Tapping the node — separate from dragging it. */
|
|
974
989
|
onPress?: () => void;
|
|
990
|
+
/**
|
|
991
|
+
* Delete the node when assistive technology requests the advertised Delete
|
|
992
|
+
* node action. No delete action is exposed when this is omitted.
|
|
993
|
+
*/
|
|
994
|
+
onDelete?: () => void;
|
|
995
|
+
/** Graph points covered by each Move up, right, down or left accessibility action. */
|
|
996
|
+
accessibilityMoveStep?: number;
|
|
975
997
|
/** Spoken name. Defaults to the node's id. */
|
|
976
998
|
accessibilityLabel?: string;
|
|
977
999
|
children?: ReactNode;
|
|
@@ -991,7 +1013,11 @@ function FlowNode({
|
|
|
991
1013
|
pinned = false,
|
|
992
1014
|
selected = false,
|
|
993
1015
|
onPress,
|
|
1016
|
+
onDelete,
|
|
1017
|
+
accessibilityMoveStep = 24,
|
|
994
1018
|
accessibilityLabel,
|
|
1019
|
+
accessibilityActions,
|
|
1020
|
+
onAccessibilityAction,
|
|
995
1021
|
children,
|
|
996
1022
|
...props
|
|
997
1023
|
}: FlowNodeProps) {
|
|
@@ -1009,6 +1035,10 @@ function FlowNode({
|
|
|
1009
1035
|
registerGroup,
|
|
1010
1036
|
unregisterGroup,
|
|
1011
1037
|
onNodeDragEnd,
|
|
1038
|
+
nodes,
|
|
1039
|
+
handles,
|
|
1040
|
+
connectNodes,
|
|
1041
|
+
canConnect,
|
|
1012
1042
|
} = flow;
|
|
1013
1043
|
|
|
1014
1044
|
/*
|
|
@@ -1018,13 +1048,13 @@ function FlowNode({
|
|
|
1018
1048
|
* layer both need to know it is a container and not a card.
|
|
1019
1049
|
*/
|
|
1020
1050
|
useEffect(() => {
|
|
1021
|
-
registerNode(id);
|
|
1051
|
+
registerNode(id, accessibilityLabel ?? id);
|
|
1022
1052
|
registerGroup(id);
|
|
1023
1053
|
return () => {
|
|
1024
1054
|
unregisterNode(id);
|
|
1025
1055
|
unregisterGroup(id);
|
|
1026
1056
|
};
|
|
1027
|
-
}, [id, registerGroup, registerNode, unregisterGroup, unregisterNode]);
|
|
1057
|
+
}, [accessibilityLabel, id, registerGroup, registerNode, unregisterGroup, unregisterNode]);
|
|
1028
1058
|
|
|
1029
1059
|
// Writing on every render would fight the drag, snapping a node back to the
|
|
1030
1060
|
// position it was first given. Only an actual change to the prop moves it.
|
|
@@ -1065,11 +1095,70 @@ function FlowNode({
|
|
|
1065
1095
|
);
|
|
1066
1096
|
|
|
1067
1097
|
const start = useSharedValue({ x: 0, y: 0 });
|
|
1068
|
-
|
|
1069
1098
|
const pressRef = useRef(onPress);
|
|
1070
1099
|
pressRef.current = onPress;
|
|
1071
1100
|
const press = useCallback(() => pressRef.current?.(), []);
|
|
1072
1101
|
|
|
1102
|
+
const moveNode = useCallback(
|
|
1103
|
+
(actionName: string) => {
|
|
1104
|
+
const rect = rects.value[id];
|
|
1105
|
+
if (!rect) return;
|
|
1106
|
+
const bounds = confine && group ? rects.value[group] : undefined;
|
|
1107
|
+
const next = moveNodePosition(rect, bounds, actionName, accessibilityMoveStep);
|
|
1108
|
+
if (!next) return;
|
|
1109
|
+
setNodeRect(id, next);
|
|
1110
|
+
dragEnd(next.x, next.y);
|
|
1111
|
+
},
|
|
1112
|
+
[accessibilityMoveStep, confine, dragEnd, group, id, rects, setNodeRect]
|
|
1113
|
+
);
|
|
1114
|
+
|
|
1115
|
+
const connectionActions = useMemo(
|
|
1116
|
+
() => (canConnect && !locked ? getFlowConnectionActions(id, nodes, handles) : []),
|
|
1117
|
+
[canConnect, handles, id, locked, nodes]
|
|
1118
|
+
);
|
|
1119
|
+
const connectionByAction = useMemo(
|
|
1120
|
+
() => new Map(connectionActions.map((entry) => [entry.name, entry.connection])),
|
|
1121
|
+
[connectionActions]
|
|
1122
|
+
);
|
|
1123
|
+
const movable = draggable && !pinned && !locked;
|
|
1124
|
+
const nodeActions = useMemo(
|
|
1125
|
+
() => [
|
|
1126
|
+
...(onPress ? [{ name: 'activate' }] : []),
|
|
1127
|
+
...(movable ? FLOW_MOVE_ACTIONS : []),
|
|
1128
|
+
...connectionActions.map(({ name, label }) => ({ name, label })),
|
|
1129
|
+
...(onDelete ? [FLOW_DELETE_ACTION] : []),
|
|
1130
|
+
...(accessibilityActions ?? []),
|
|
1131
|
+
],
|
|
1132
|
+
[accessibilityActions, connectionActions, movable, onDelete, onPress]
|
|
1133
|
+
);
|
|
1134
|
+
|
|
1135
|
+
const handleAccessibilityAction = useCallback<
|
|
1136
|
+
NonNullable<ViewProps['onAccessibilityAction']>
|
|
1137
|
+
>(
|
|
1138
|
+
(event) => {
|
|
1139
|
+
const action = event.nativeEvent.actionName;
|
|
1140
|
+
if (action === 'activate' && onPress) {
|
|
1141
|
+
press();
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
if (movable && FLOW_MOVE_ACTIONS.some((entry) => entry.name === action)) {
|
|
1145
|
+
moveNode(action);
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
const nextConnection = connectionByAction.get(action);
|
|
1149
|
+
if (nextConnection) {
|
|
1150
|
+
connectNodes(nextConnection);
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
if (action === FLOW_DELETE_ACTION.name && onDelete) {
|
|
1154
|
+
onDelete();
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
onAccessibilityAction?.(event);
|
|
1158
|
+
},
|
|
1159
|
+
[connectNodes, connectionByAction, movable, moveNode, onAccessibilityAction, onDelete, onPress, press]
|
|
1160
|
+
);
|
|
1161
|
+
|
|
1073
1162
|
const drag = useMemo(
|
|
1074
1163
|
() =>
|
|
1075
1164
|
Gesture.Pan()
|
|
@@ -1096,13 +1185,9 @@ function FlowNode({
|
|
|
1096
1185
|
* would leave the edges drawn to a position the node never reached.
|
|
1097
1186
|
*/
|
|
1098
1187
|
if (confine && group) {
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
const maxY = box.y + box.height - rect.height;
|
|
1103
|
-
x = x < box.x ? box.x : x > maxX ? maxX : x;
|
|
1104
|
-
y = y < box.y ? box.y : y > maxY ? maxY : y;
|
|
1105
|
-
}
|
|
1188
|
+
const next = clampNodePosition(rect, rects.value[group], { x, y });
|
|
1189
|
+
x = next.x;
|
|
1190
|
+
y = next.y;
|
|
1106
1191
|
}
|
|
1107
1192
|
|
|
1108
1193
|
rects.value = { ...rects.value, [id]: { ...rect, x, y } };
|
|
@@ -1158,6 +1243,8 @@ function FlowNode({
|
|
|
1158
1243
|
accessibilityRole={onPress ? 'button' : 'none'}
|
|
1159
1244
|
accessibilityLabel={accessibilityLabel ?? id}
|
|
1160
1245
|
accessibilityState={{ selected }}
|
|
1246
|
+
accessibilityActions={nodeActions}
|
|
1247
|
+
onAccessibilityAction={handleAccessibilityAction}
|
|
1161
1248
|
{...props}
|
|
1162
1249
|
>
|
|
1163
1250
|
{textChildren(children)}
|
|
@@ -1185,6 +1272,11 @@ export interface FlowHandleProps {
|
|
|
1185
1272
|
/** Where along the face, 0–1. For more than one handle on a side. */
|
|
1186
1273
|
offset?: number;
|
|
1187
1274
|
className?: string;
|
|
1275
|
+
/**
|
|
1276
|
+
* Spoken handle name used in the parent node's connection actions. Defaults
|
|
1277
|
+
* to the handle's id.
|
|
1278
|
+
*/
|
|
1279
|
+
accessibilityLabel?: string;
|
|
1188
1280
|
/** Draw nothing. The handle still anchors edges and still accepts a drop. */
|
|
1189
1281
|
hidden?: boolean;
|
|
1190
1282
|
}
|
|
@@ -1203,6 +1295,7 @@ function FlowHandle({
|
|
|
1203
1295
|
type = 'both',
|
|
1204
1296
|
offset = 0.5,
|
|
1205
1297
|
className,
|
|
1298
|
+
accessibilityLabel,
|
|
1206
1299
|
hidden = false,
|
|
1207
1300
|
}: FlowHandleProps) {
|
|
1208
1301
|
const flow = useFlow('Flow.Handle');
|
|
@@ -1211,31 +1304,51 @@ function FlowHandle({
|
|
|
1211
1304
|
throw new Error('Flow.Handle must be used within a <Flow.Node>');
|
|
1212
1305
|
}
|
|
1213
1306
|
|
|
1214
|
-
const {
|
|
1307
|
+
const {
|
|
1308
|
+
rects,
|
|
1309
|
+
zoom,
|
|
1310
|
+
connection,
|
|
1311
|
+
handles,
|
|
1312
|
+
locked,
|
|
1313
|
+
registerHandle,
|
|
1314
|
+
unregisterHandle,
|
|
1315
|
+
connectNodes,
|
|
1316
|
+
} = flow;
|
|
1215
1317
|
const key = `${node}.${id}`;
|
|
1216
1318
|
|
|
1217
1319
|
useEffect(() => {
|
|
1218
|
-
registerHandle({
|
|
1320
|
+
registerHandle({
|
|
1321
|
+
key,
|
|
1322
|
+
node,
|
|
1323
|
+
id,
|
|
1324
|
+
label: accessibilityLabel ?? id,
|
|
1325
|
+
side: position,
|
|
1326
|
+
offset,
|
|
1327
|
+
type,
|
|
1328
|
+
});
|
|
1219
1329
|
return () => unregisterHandle(key);
|
|
1220
|
-
}, [
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1330
|
+
}, [
|
|
1331
|
+
accessibilityLabel,
|
|
1332
|
+
id,
|
|
1333
|
+
key,
|
|
1334
|
+
node,
|
|
1335
|
+
offset,
|
|
1336
|
+
position,
|
|
1337
|
+
registerHandle,
|
|
1338
|
+
type,
|
|
1339
|
+
unregisterHandle,
|
|
1340
|
+
]);
|
|
1226
1341
|
|
|
1227
1342
|
const land = useCallback(
|
|
1228
1343
|
(target: string, targetHandle: string | undefined) => {
|
|
1229
|
-
|
|
1344
|
+
connectNodes({
|
|
1230
1345
|
source: node,
|
|
1231
1346
|
sourceHandle: id,
|
|
1232
1347
|
target,
|
|
1233
1348
|
targetHandle,
|
|
1234
|
-
};
|
|
1235
|
-
if (validRef.current && !validRef.current(payload)) return;
|
|
1236
|
-
connectRef.current?.(payload);
|
|
1349
|
+
});
|
|
1237
1350
|
},
|
|
1238
|
-
[id, node]
|
|
1351
|
+
[connectNodes, id, node]
|
|
1239
1352
|
);
|
|
1240
1353
|
|
|
1241
1354
|
// A snapshot of what a drag can land on, rebuilt only when handles come and
|
|
@@ -1354,8 +1467,7 @@ function FlowHandle({
|
|
|
1354
1467
|
<View
|
|
1355
1468
|
collapsable={false}
|
|
1356
1469
|
hitSlop={12}
|
|
1357
|
-
|
|
1358
|
-
accessibilityLabel={`${position} connection point`}
|
|
1470
|
+
accessible={false}
|
|
1359
1471
|
style={placement}
|
|
1360
1472
|
className={flowVariants().handle({ className })}
|
|
1361
1473
|
/>
|
|
@@ -1645,9 +1757,9 @@ function FlowGroup({
|
|
|
1645
1757
|
} = flow;
|
|
1646
1758
|
|
|
1647
1759
|
useEffect(() => {
|
|
1648
|
-
registerNode(id);
|
|
1760
|
+
registerNode(id, label ?? id);
|
|
1649
1761
|
return () => unregisterNode(id);
|
|
1650
|
-
}, [id, registerNode, unregisterNode]);
|
|
1762
|
+
}, [id, label, registerNode, unregisterNode]);
|
|
1651
1763
|
|
|
1652
1764
|
useEffect(() => {
|
|
1653
1765
|
setNodeRect(id, {
|
|
@@ -1919,7 +2031,7 @@ function FlowMiniMap({
|
|
|
1919
2031
|
height = 84,
|
|
1920
2032
|
nodeColor,
|
|
1921
2033
|
}: FlowMiniMapProps) {
|
|
1922
|
-
const { rects,
|
|
2034
|
+
const { rects, nodes, groupIds, translateX, translateY, zoom, size } =
|
|
1923
2035
|
useFlow('Flow.MiniMap');
|
|
1924
2036
|
const token = useCSSVariable('--color-muted-foreground');
|
|
1925
2037
|
const tint = nodeColor ?? (typeof token === 'string' ? token : '#737373');
|
|
@@ -1999,7 +2111,7 @@ function FlowMiniMap({
|
|
|
1999
2111
|
{/* Containers first and drawn as outlines, so the map reads the way
|
|
2000
2112
|
the canvas does — boxes with things in them, rather than a wash of
|
|
2001
2113
|
identical rectangles where the largest happens to be a group. */}
|
|
2002
|
-
{
|
|
2114
|
+
{nodes.map(({ id }) => {
|
|
2003
2115
|
const group = groups.has(id);
|
|
2004
2116
|
return (
|
|
2005
2117
|
<MiniMapNode
|