@statelyai/flow-react 0.8.0 → 0.9.2
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 +7 -3
- package/dist/esm/index.d.ts +24 -16
- package/dist/esm/index.js +161 -156
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ The React package owns React lifecycle, context, selector subscriptions, and a c
|
|
|
17
17
|
|
|
18
18
|
<!-- transient entity rendering split between src/StatelyFlow.tsx and flow-dom src/entity-geometry.ts -->
|
|
19
19
|
|
|
20
|
-
Optimization is part of the architecture, not a later cleanup pass. Components subscribe only to durable content and interaction state. During drag
|
|
20
|
+
Optimization is part of the architecture, not a later cleanup pass. Components subscribe only to durable content and interaction state. During drag and animated layout sessions, `@statelyai/flow-dom` batches node rectangles, edge paths, endpoint handles, label positions, and multi-selection bounds into imperative animation-frame writes, so React commits only at transient-session boundaries. Default edge paths expose `data-flow-edge-segment`; custom edge renderers can use the same indexed attribute to join the imperative path updates. Router-provided `PathData.svgPath` geometry is honored by both initial React rendering and those imperative updates.
|
|
21
21
|
|
|
22
22
|
The graph data structure is the source of truth for graph content, not for transient UI state. Core node and edge data should not grow flags like `selected: true` or `dragging: true`; selection, focus, dragging, hover, connection state, and similar interaction state live in the flow store and are derived into render props such as `selected` or `dragging`.
|
|
23
23
|
|
|
@@ -53,9 +53,9 @@ DOM measurement is one-directional. Nodes, ports, and edge labels report their r
|
|
|
53
53
|
| `PanePortal` / `ViewportPortal` | Render custom content into fixed screen-coordinate or transformed canvas-coordinate layers |
|
|
54
54
|
| `useFitViewExclusion()` | Register an overlay element whose measured edge band fit and center commands avoid |
|
|
55
55
|
| `EntityAnchor` | Position custom controls on entity sides or free-standing canvas bounds with screen-stable offsets and inverse scaling |
|
|
56
|
+
| `Port` | Render a measured connection port or a graph-owned readonly inspection port |
|
|
56
57
|
| `RenderEdgeLabelProps` | Edge, path, bounds, interaction state, and projected attrs passed to `renderEdgeLabel` |
|
|
57
58
|
| `Panel` | Positioned overlay panel for controls and custom UI |
|
|
58
|
-
| `NodeResizer` | Resize handles for custom node renderers; constraints live on `node.resize` |
|
|
59
59
|
| `ReactActiveGraphProjection` | React-typed active projection for `<StatelyFlow projections={...} />` or controller-owned projections |
|
|
60
60
|
| `ReactGraphNodeAttrs` / `ReactGraphEdgeAttrs` | React-typed visual attrs exposed to custom node/edge renderers |
|
|
61
61
|
| `FlowColorModeScope` | Applies the current flow color mode as DOM data attributes and `color-scheme` |
|
|
@@ -75,7 +75,11 @@ Color mode is store data, not engine behavior. `@statelyai/flow-dom` exports sma
|
|
|
75
75
|
|
|
76
76
|
Attach the ref from `useFitViewExclusion({ side, gap })` to floating controls that cover the canvas. The hook measures the element relative to the Flow root at command time, including content portaled outside the root, so responsive toolbars and panel-relative chrome do not need guessed fit padding.
|
|
77
77
|
|
|
78
|
-
Set `showSelectionBounds` to render a zoom-stable, square frame around established multi-selections. Its geometry follows drag
|
|
78
|
+
Set `showSelectionBounds` to render a zoom-stable, square frame around established multi-selections. Its geometry follows drag and layout frames through Flow DOM without React commits; theme `[data-flow-selection-bounds]`, `--flow-selection-color`, or the screen-pixel `--flow-selection-bounds-outset`. Set `preserveViewportCenterOnResize` when root container resizes should retain the world point at its center.
|
|
79
|
+
|
|
80
|
+
The default edge renderer honors `edge.style.markerStart` and `markerEnd` values `arrow`, `arrow-closed`, `circle`, and `none`, plus safe local custom marker IDs or `url(#id)` references. External and malformed marker URLs are rejected. Built-in marker definitions are namespaced per `StatelyFlow`, so multiple mounted flows cannot collide. It also renders canonical graph `edge.points`/`routing` and optional `edge.svgPath` without rerouting or splitting the route around a label.
|
|
81
|
+
|
|
82
|
+
Render layout-owned ports with `<Port measured={false} interaction="inspect" />`. Inspection ports are focusable, selectable addressables (`${node.id}:${port.name}`), emit the normal entity click events, and never start a connection gesture. Omit `interaction` (or use `"connect"`) to retain existing connection behavior.
|
|
79
83
|
|
|
80
84
|
Custom node and edge renderers should branch on the application-defined `entity.kind`. Keep `entity.type` as the structural `"node"` or `"edge"` discriminant so core selection, drag, connection, and CRUD behavior remains valid.
|
|
81
85
|
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Background, BackgroundProps, BackgroundVariant, CrossBackground, DotsBackground, LinesBackground } from "./backgrounds.js";
|
|
2
2
|
import { FlowView, ResolvedFlowColorMode } from "@statelyai/flow-dom";
|
|
3
|
-
import { ActiveGraphProjection, AnchorSpec, ConnectionEndedEvent, ConnectionState, CreateEdgeInput, CreateFlowOptions, EdgePathData, FitViewExclusionSide, FlowColorMode, FlowCommitEvent, FlowConnection, FlowController, FlowEdge, FlowGraph, FlowInputMode, FlowKeymap, FlowNode, FlowProfile, FlowStoreContext, GraphEdgeAttrs, GraphNodeAttrs, LayoutRequestedEvent, Point, Rect,
|
|
3
|
+
import { ActiveGraphProjection, AnchorSpec, ConnectionEndedEvent, ConnectionState, CreateEdgeInput, CreateFlowOptions, EdgePathData, FitViewExclusionSide, FlowColorMode, FlowCommitEvent, FlowConnection, FlowController, FlowEdge, FlowGraph, FlowInputMode, FlowKeymap, FlowNode, FlowProfile, FlowStoreContext, GraphEdgeAttrs, GraphNodeAttrs, LayoutRequestedEvent, Point, Rect, Viewport, VisualPort } from "@statelyai/flow";
|
|
4
4
|
import { CSSProperties, ComponentPropsWithoutRef, HTMLAttributes, ReactNode, Ref, RefCallback, RefObject } from "react";
|
|
5
5
|
export * from "@statelyai/flow-dom";
|
|
6
6
|
//#region src/createFlowInstance.d.ts
|
|
@@ -54,6 +54,13 @@ type FlowInstance<TNodeData = any, TEdgeData = any, TLabelData = any> = FlowCont
|
|
|
54
54
|
fireImmediately?: boolean;
|
|
55
55
|
}): () => void;
|
|
56
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* Create the React adapter's imperative Flow instance.
|
|
59
|
+
*
|
|
60
|
+
* The instance owns its controller and all listeners registered through
|
|
61
|
+
* `listen` or creation options. `destroy()` removes those listeners and
|
|
62
|
+
* destroys the underlying controller.
|
|
63
|
+
*/
|
|
57
64
|
declare function createFlowInstance<TNodeData = any, TEdgeData = any, TLabelData = any>(options: CreateFlowInstanceOptions<TNodeData, TEdgeData, TLabelData>): FlowInstance<TNodeData, TEdgeData, TLabelData>;
|
|
58
65
|
//#endregion
|
|
59
66
|
//#region src/FlowInstanceContext.d.ts
|
|
@@ -67,9 +74,16 @@ declare function useOptionalFlowInstance(): FlowInstance | null;
|
|
|
67
74
|
//#region src/hooks.d.ts
|
|
68
75
|
type UseCreateFlowInstanceOptions<TNodeData = any, TEdgeData = any, TLabelData = any> = CreateFlowInstanceOptions<TNodeData, TEdgeData, TLabelData>;
|
|
69
76
|
type UseFlowOptions<TNodeData = any, TEdgeData = any, TLabelData = any> = UseCreateFlowInstanceOptions<TNodeData, TEdgeData, TLabelData>;
|
|
77
|
+
/**
|
|
78
|
+
* Create one Flow instance for a component lifetime.
|
|
79
|
+
*
|
|
80
|
+
* Creation settings are initial-only; use `flow.configure()` for runtime
|
|
81
|
+
* changes. Event/listener props are rebound when their identities change.
|
|
82
|
+
*/
|
|
70
83
|
declare function useFlow<TNodeData = any, TEdgeData = any, TLabelData = any>(options: UseFlowOptions<TNodeData, TEdgeData, TLabelData>): FlowInstance<TNodeData, TEdgeData, TLabelData>;
|
|
71
84
|
/** @deprecated Use `useFlow(options)`. */
|
|
72
85
|
declare function useCreateFlowInstance<TNodeData = any, TEdgeData = any, TLabelData = any>(options: UseCreateFlowInstanceOptions<TNodeData, TEdgeData, TLabelData>): FlowInstance<TNodeData, TEdgeData, TLabelData>;
|
|
86
|
+
/** Subscribe to a derived slice of Flow context with optional equality. */
|
|
73
87
|
declare function useFlowSelector<T>(selector: FlowSelector<T>, options?: {
|
|
74
88
|
flow?: FlowInstance;
|
|
75
89
|
isEqual?: FlowSelectorEquality<T>;
|
|
@@ -329,7 +343,7 @@ type MiniMapProps = {
|
|
|
329
343
|
/**
|
|
330
344
|
* Overview map. Deliberately thin: all math lives in core
|
|
331
345
|
* (`getMinimapProjection`, `viewportCenteredOn`) and every interaction is a
|
|
332
|
-
*
|
|
346
|
+
* controller viewport command — this component is exactly what you would write in
|
|
333
347
|
* userland, kept here as a convenience.
|
|
334
348
|
*/
|
|
335
349
|
declare function MiniMap(props: MiniMapProps): import("react").JSX.Element;
|
|
@@ -351,8 +365,7 @@ type ControlsProps = {
|
|
|
351
365
|
children?: ReactNode;
|
|
352
366
|
};
|
|
353
367
|
/**
|
|
354
|
-
* Zoom/fit toolbar
|
|
355
|
-
* (`zoomBy`, `fitView`) — replicate or extend it freely in userland.
|
|
368
|
+
* Zoom/fit toolbar backed by the controller's constrained viewport API.
|
|
356
369
|
*/
|
|
357
370
|
declare function Controls(props: ControlsProps): import("react").JSX.Element;
|
|
358
371
|
//#endregion
|
|
@@ -388,6 +401,11 @@ declare function EntityAnchor(props: EntityAnchorProps): import("react").JSX.Ele
|
|
|
388
401
|
//#region src/Port.d.ts
|
|
389
402
|
type PortProps = {
|
|
390
403
|
name: string;
|
|
404
|
+
/**
|
|
405
|
+
* `inspect` makes a graph-owned port focusable/selectable without starting
|
|
406
|
+
* a connection. Defaults to `connect`.
|
|
407
|
+
*/
|
|
408
|
+
interaction?: "connect" | "inspect";
|
|
391
409
|
/**
|
|
392
410
|
* Whether the DOM element reports its bounds back to the graph (default
|
|
393
411
|
* true). Ports are unidirectional, one way or the other:
|
|
@@ -444,16 +462,6 @@ type DragHandleProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
444
462
|
*/
|
|
445
463
|
declare function DragHandle({ children, style, ...rest }: DragHandleProps): import("react").JSX.Element;
|
|
446
464
|
//#endregion
|
|
447
|
-
//#region src/NodeResizer.d.ts
|
|
448
|
-
type NodeResizerProps = {
|
|
449
|
-
handles?: ResizeHandle[];
|
|
450
|
-
/** Defaults to node selection state. Constraints belong on `node.resize`. */
|
|
451
|
-
isVisible?: boolean;
|
|
452
|
-
className?: string;
|
|
453
|
-
style?: CSSProperties;
|
|
454
|
-
};
|
|
455
|
-
declare function NodeResizer(props: NodeResizerProps): import("react").JSX.Element | null;
|
|
456
|
-
//#endregion
|
|
457
465
|
//#region src/baseStyles.d.ts
|
|
458
466
|
/**
|
|
459
467
|
* Minimal base stylesheet for the flow canvas. Intentionally tiny and
|
|
@@ -474,7 +482,7 @@ declare function NodeResizer(props: NodeResizerProps): import("react").JSX.Eleme
|
|
|
474
482
|
* - `data-flow-selection-bounds` — the established multi-selection frame.
|
|
475
483
|
*/
|
|
476
484
|
declare const FLOW_BASE_STYLES_ID = "flow-base-styles";
|
|
477
|
-
declare const FLOW_BASE_CSS = "\n[data-flow-root] {\n --flow-selection-color: #2563eb;\n --flow-selection-bg: rgba(37, 99, 235, 0.12);\n --flow-selection-bounds-outset: 1px;\n --flow-layer-edges: 0;\n --flow-layer-entities: 10;\n --flow-layer-labels: 20;\n --flow-layer-interaction: 30;\n --flow-layer-guides: 40;\n --flow-layer-chrome: 50;\n touch-action: none;\n}\n\n[data-flow-root]:focus {\n outline: var(--flow-focus-outline, none);\n}\n\n[data-flow-interactive-when-selected] {\n pointer-events: none;\n}\n[data-flow-selected=\"true\"] [data-flow-interactive-when-selected] {\n pointer-events: auto;\n}\n\n[data-flow-selection-box] {\n position: absolute;\n box-sizing: border-box;\n border: calc(1px / var(--flow-zoom, 1)) solid var(--flow-selection-color);\n border-radius: 0;\n background: var(--flow-selection-bg);\n pointer-events: none;\n}\n\n[data-flow-selection-bounds] {\n position: absolute;\n box-sizing: border-box;\n outline: calc(1px / var(--flow-zoom, 1)) solid var(--flow-selection-color);\n outline-offset: calc(var(--flow-selection-bounds-outset) / var(--flow-zoom, 1));\n border-radius: 0;\n pointer-events: none;\n}\n";
|
|
485
|
+
declare const FLOW_BASE_CSS = "\n[data-flow-root] {\n --flow-selection-color: #2563eb;\n --flow-selection-bg: rgba(37, 99, 235, 0.12);\n --flow-selection-bounds-outset: 1px;\n --flow-layer-edges: 0;\n --flow-layer-entities: 10;\n --flow-layer-labels: 20;\n --flow-layer-interaction: 30;\n --flow-layer-guides: 40;\n --flow-layer-chrome: 50;\n touch-action: none;\n}\n\n[data-flow-root]:focus {\n outline: var(--flow-focus-outline, none);\n}\n\n[data-flow-interactive-when-selected] {\n pointer-events: none;\n}\n[data-flow-selected=\"true\"] [data-flow-interactive-when-selected] {\n pointer-events: auto;\n}\n\n[data-flow-custom-node] :where(\n button,\n input,\n select,\n textarea,\n a[href],\n [role=\"button\"],\n [tabindex],\n [data-flow-drag-handle],\n [data-flow-port-name]\n) {\n pointer-events: auto;\n}\n\n[data-flow-selection-box] {\n position: absolute;\n box-sizing: border-box;\n border: calc(1px / var(--flow-zoom, 1)) solid var(--flow-selection-color);\n border-radius: 0;\n background: var(--flow-selection-bg);\n pointer-events: none;\n}\n\n[data-flow-selection-bounds] {\n position: absolute;\n box-sizing: border-box;\n outline: calc(1px / var(--flow-zoom, 1)) solid var(--flow-selection-color);\n outline-offset: calc(var(--flow-selection-bounds-outset) / var(--flow-zoom, 1));\n border-radius: 0;\n pointer-events: none;\n}\n";
|
|
478
486
|
/** Inject the base stylesheet once per document (deduped by element id). */
|
|
479
487
|
declare function useFlowBaseStyles(): void;
|
|
480
488
|
//#endregion
|
|
@@ -491,4 +499,4 @@ declare function useFlowColorMode(): {
|
|
|
491
499
|
};
|
|
492
500
|
declare function useApplyFlowColorMode(element: HTMLElement | null): void;
|
|
493
501
|
//#endregion
|
|
494
|
-
export { Background, type BackgroundProps, type BackgroundVariant, Controls, type ControlsProps, type CreateFlowInstanceOptions, CrossBackground, DotsBackground, DragHandle, type DragHandleProps, EntityAnchor, type EntityAnchorAlign, type EntityAnchorPoint, type EntityAnchorProps, type EntityAnchorSide, FLOW_BASE_CSS, FLOW_BASE_STYLES_ID, FlowColorModeScope, type FlowColorModeScopeProps, type FlowEdgeBinding, type FlowEventListeners, type FlowInstance, FlowInstanceProvider, type FlowInstanceSettings, type FlowNodeBinding, type FlowPortBinding, type FlowSelector, type FlowSelectorEquality, type FlowStoreListener, FlowToolbar, type FlowToolbarProps, type FlowToolbarState, LinesBackground, MiniMap, type MiniMapProps,
|
|
502
|
+
export { Background, type BackgroundProps, type BackgroundVariant, Controls, type ControlsProps, type CreateFlowInstanceOptions, CrossBackground, DotsBackground, DragHandle, type DragHandleProps, EntityAnchor, type EntityAnchorAlign, type EntityAnchorPoint, type EntityAnchorProps, type EntityAnchorSide, FLOW_BASE_CSS, FLOW_BASE_STYLES_ID, FlowColorModeScope, type FlowColorModeScopeProps, type FlowEdgeBinding, type FlowEventListeners, type FlowInstance, FlowInstanceProvider, type FlowInstanceSettings, type FlowNodeBinding, type FlowPortBinding, type FlowSelector, type FlowSelectorEquality, type FlowStoreListener, FlowToolbar, type FlowToolbarProps, type FlowToolbarState, LinesBackground, MiniMap, type MiniMapProps, PanePortal, Panel, type PanelPosition, type PanelProps, Port, type PortProps, type ReactActiveGraphProjection, type ReactGraphEdgeAttrs, type ReactGraphNodeAttrs, type RenderConnectionLineProps, type RenderEdgeLabelProps, type RenderEdgeProps, type RenderNodeProps, StatelyFlow, type StatelyFlowProps, type StatelyFlowRootProps, type StatelyFlowStyle, type UseCreateFlowInstanceOptions, type UseFitViewExclusionOptions, type UseFlowInputOptions, type UseFlowOptions, type UseFlowToolbarOptions, ViewportPortal, createFlowInstance, useApplyFlowColorMode, useConnection, useCreateFlowInstance, useFitViewExclusion, useFlow, useFlowBaseStyles, useFlowColorMode, useFlowConnections, useFlowContext, useFlowEdge, useFlowEntity, useFlowInput, useFlowInstance, useFlowMeasurement, useFlowNode, useFlowPort, useFlowSelector, useFlowToolbar, useFlowValue, useFlowView, useFlowViewport, useKeyPress, useMeasuredSize, useNodesInitialized, useOptionalFlowInstance };
|
package/dist/esm/index.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { a as FlowInstanceProvider, i as LinesBackground, n as CrossBackground, o as useFlowInstance, r as DotsBackground, s as useOptionalFlowInstance, t as Background } from "./backgrounds2.js";
|
|
2
2
|
import { applyFlowColorMode, attachContainer, attachInput, attachViewportTransform, clientToCanvas, computeRenderableIds, createEntityGeometryObserver, createFlowView, createMeasurementObserver, nodePositionStyle, renderableIdsEqual, resolveFlowColorMode, viewportStyle } from "@statelyai/flow-dom";
|
|
3
|
-
import { CONNECTION_PREVIEW_EDGE_ID, areNodesMeasured, createConnectionPreviewGraph, createEntityChangeNotifier, createFlow, createFlowInputRuntime, getAnchorTransform, getAutoPanDelta, getMinimapProjection, getProjectedEdgeAttrs, getProjectedNodeAttrs, getToolbarPlacement,
|
|
4
|
-
import { createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
3
|
+
import { CONNECTION_PREVIEW_EDGE_ID, areNodesMeasured, createConnectionPreviewGraph, createEntityChangeNotifier, createFlow, createFlowInputRuntime, getAnchorTransform, getAutoPanDelta, getMinimapProjection, getProjectedEdgeAttrs, getProjectedNodeAttrs, getToolbarPlacement, markerToSVG, pathToSVG, profileModesForContext, profilePoliciesForContext, viewportCenteredOn } from "@statelyai/flow";
|
|
4
|
+
import { createContext, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector";
|
|
7
7
|
import { createPortal } from "react-dom";
|
|
8
8
|
export * from "@statelyai/flow-dom";
|
|
9
9
|
//#region src/createFlowInstance.ts
|
|
10
|
+
/**
|
|
11
|
+
* Create the React adapter's imperative Flow instance.
|
|
12
|
+
*
|
|
13
|
+
* The instance owns its controller and all listeners registered through
|
|
14
|
+
* `listen` or creation options. `destroy()` removes those listeners and
|
|
15
|
+
* destroys the underlying controller.
|
|
16
|
+
*/
|
|
10
17
|
function createFlowInstance(options) {
|
|
11
18
|
const { keymap, ...createFlowSettings } = options.settings ?? {};
|
|
12
19
|
const flow = createFlow({
|
|
@@ -68,6 +75,12 @@ function createFlowInstance(options) {
|
|
|
68
75
|
}
|
|
69
76
|
//#endregion
|
|
70
77
|
//#region src/hooks.ts
|
|
78
|
+
/**
|
|
79
|
+
* Create one Flow instance for a component lifetime.
|
|
80
|
+
*
|
|
81
|
+
* Creation settings are initial-only; use `flow.configure()` for runtime
|
|
82
|
+
* changes. Event/listener props are rebound when their identities change.
|
|
83
|
+
*/
|
|
71
84
|
function useFlow(options) {
|
|
72
85
|
return useCreateFlowInstance(options);
|
|
73
86
|
}
|
|
@@ -125,6 +138,7 @@ function useCreateFlowInstance(options) {
|
|
|
125
138
|
}, [flow]);
|
|
126
139
|
return flow;
|
|
127
140
|
}
|
|
141
|
+
/** Subscribe to a derived slice of Flow context with optional equality. */
|
|
128
142
|
function useFlowSelector(selector, options = {}) {
|
|
129
143
|
const contextFlow = useOptionalFlowInstance();
|
|
130
144
|
const flow = options.flow ?? contextFlow;
|
|
@@ -403,6 +417,20 @@ const FLOW_BASE_CSS = `
|
|
|
403
417
|
pointer-events: auto;
|
|
404
418
|
}
|
|
405
419
|
|
|
420
|
+
[data-flow-custom-node] :where(
|
|
421
|
+
button,
|
|
422
|
+
input,
|
|
423
|
+
select,
|
|
424
|
+
textarea,
|
|
425
|
+
a[href],
|
|
426
|
+
[role="button"],
|
|
427
|
+
[tabindex],
|
|
428
|
+
[data-flow-drag-handle],
|
|
429
|
+
[data-flow-port-name]
|
|
430
|
+
) {
|
|
431
|
+
pointer-events: auto;
|
|
432
|
+
}
|
|
433
|
+
|
|
406
434
|
[data-flow-selection-box] {
|
|
407
435
|
position: absolute;
|
|
408
436
|
box-sizing: border-box;
|
|
@@ -442,6 +470,18 @@ function useNodeRenderContext() {
|
|
|
442
470
|
return context;
|
|
443
471
|
}
|
|
444
472
|
//#endregion
|
|
473
|
+
//#region src/focus.ts
|
|
474
|
+
/** Whether a focused descendant owns its own graph focus identity. */
|
|
475
|
+
function descendantOwnsFlowFocus(event) {
|
|
476
|
+
const target = event.target;
|
|
477
|
+
return target instanceof Element && target.closest("[data-flow-port-interaction=\"inspect\"]") !== null;
|
|
478
|
+
}
|
|
479
|
+
function clearEntityFocusOnBlur(flow, entityId, event) {
|
|
480
|
+
const nextTarget = event.relatedTarget;
|
|
481
|
+
if (nextTarget instanceof Node && event.currentTarget.contains(nextTarget)) return;
|
|
482
|
+
if (flow.getSnapshot().focusedEntityId === entityId) flow.internals.store.trigger.clearFocus();
|
|
483
|
+
}
|
|
484
|
+
//#endregion
|
|
445
485
|
//#region src/LayerPortals.tsx
|
|
446
486
|
const FlowLayerTargetsContext = createContext(null);
|
|
447
487
|
function FlowLayerTargetsProvider(props) {
|
|
@@ -517,11 +557,6 @@ function nodePaintEntriesEqual(a, b) {
|
|
|
517
557
|
function classNames$1(...values) {
|
|
518
558
|
return values.filter(Boolean).join(" ");
|
|
519
559
|
}
|
|
520
|
-
function clearEntityFocusOnBlur(flow, entityId, event) {
|
|
521
|
-
const nextTarget = event.relatedTarget;
|
|
522
|
-
if (nextTarget instanceof Node && event.currentTarget.contains(nextTarget)) return;
|
|
523
|
-
if (flow.getSnapshot().focusedEntityId === entityId) flow.internals.store.trigger.clearFocus();
|
|
524
|
-
}
|
|
525
560
|
function assignRef(ref, value) {
|
|
526
561
|
if (typeof ref === "function") ref(value);
|
|
527
562
|
else if (ref) ref.current = value;
|
|
@@ -897,10 +932,11 @@ function SelectionBoundsLayer(props) {
|
|
|
897
932
|
}
|
|
898
933
|
function EdgeLayer(props) {
|
|
899
934
|
const instance = useFlowInstance();
|
|
935
|
+
const markerPrefix = `stately-flow-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
900
936
|
const renderableIds = useRenderableIds(instance, props.onlyRenderVisible);
|
|
901
|
-
const edgeIds = useFlowSelector((
|
|
937
|
+
const edgeIds = useFlowSelector((_context) => getOrderedEntities(instance).edges.map((edge) => edge.id), { isEqual: stringArraysEqual });
|
|
902
938
|
const renderEdge = props.renderEdge;
|
|
903
|
-
return /* @__PURE__ */
|
|
939
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
904
940
|
"aria-label": "edges",
|
|
905
941
|
className: "stately-flow__edges",
|
|
906
942
|
"data-flow-canvas-layer": true,
|
|
@@ -913,20 +949,22 @@ function EdgeLayer(props) {
|
|
|
913
949
|
overflow: "visible",
|
|
914
950
|
pointerEvents: "none"
|
|
915
951
|
},
|
|
916
|
-
children: /* @__PURE__ */ jsxs("g", { children: [edgeIds.map((id) => {
|
|
952
|
+
children: [/* @__PURE__ */ jsx(DefaultMarkerDefinitions, { prefix: markerPrefix }), /* @__PURE__ */ jsxs("g", { children: [edgeIds.map((id) => {
|
|
917
953
|
if (renderableIds && !renderableIds.edgeIds.has(id)) return null;
|
|
918
954
|
return /* @__PURE__ */ jsx(EdgeView, {
|
|
919
955
|
flow: instance,
|
|
920
956
|
geometryObserver: props.geometryObserver,
|
|
921
957
|
id,
|
|
922
958
|
projections: props.projections,
|
|
923
|
-
renderEdge
|
|
959
|
+
renderEdge,
|
|
960
|
+
markerPrefix
|
|
924
961
|
}, id);
|
|
925
962
|
}), /* @__PURE__ */ jsx(ConnectionPreviewEdge, {
|
|
926
963
|
flow: instance,
|
|
927
964
|
renderEdge,
|
|
928
|
-
renderConnectionLine: props.renderConnectionLine
|
|
929
|
-
|
|
965
|
+
renderConnectionLine: props.renderConnectionLine,
|
|
966
|
+
markerPrefix
|
|
967
|
+
})] })]
|
|
930
968
|
});
|
|
931
969
|
}
|
|
932
970
|
const EdgeView = memo(function EdgeView(props) {
|
|
@@ -970,7 +1008,10 @@ const EdgeView = memo(function EdgeView(props) {
|
|
|
970
1008
|
"aria-label": props.flow.internals.engine.getAriaLabel(props.id),
|
|
971
1009
|
"aria-selected": selected,
|
|
972
1010
|
tabIndex: focusable ? 0 : void 0,
|
|
973
|
-
onFocus: () =>
|
|
1011
|
+
onFocus: (event) => {
|
|
1012
|
+
if (descendantOwnsFlowFocus(event)) return;
|
|
1013
|
+
props.flow.internals.store.trigger.focus({ entityId: props.id });
|
|
1014
|
+
},
|
|
974
1015
|
onBlur: (event) => clearEntityFocusOnBlur(props.flow, props.id, event)
|
|
975
1016
|
};
|
|
976
1017
|
if (props.renderEdge) return /* @__PURE__ */ jsxs("g", {
|
|
@@ -1010,7 +1051,8 @@ const EdgeView = memo(function EdgeView(props) {
|
|
|
1010
1051
|
edge,
|
|
1011
1052
|
pathData,
|
|
1012
1053
|
attrs,
|
|
1013
|
-
selected
|
|
1054
|
+
selected,
|
|
1055
|
+
markerPrefix: props.markerPrefix
|
|
1014
1056
|
})
|
|
1015
1057
|
});
|
|
1016
1058
|
});
|
|
@@ -1042,6 +1084,7 @@ function ConnectionPreviewEdge(props) {
|
|
|
1042
1084
|
pathData,
|
|
1043
1085
|
attrs: {},
|
|
1044
1086
|
selected: false,
|
|
1087
|
+
markerPrefix: props.markerPrefix,
|
|
1045
1088
|
preview: true
|
|
1046
1089
|
})
|
|
1047
1090
|
});
|
|
@@ -1053,9 +1096,11 @@ function DefaultEdge(props) {
|
|
|
1053
1096
|
d,
|
|
1054
1097
|
"data-flow-edge-segment": index,
|
|
1055
1098
|
fill: "none",
|
|
1056
|
-
stroke: props.attrs.color ?? (props.preview ? props.edge.data?.isValid === false ? "#dc2626" : props.edge.data?.isValid === true ? "#16a34a" : "#0f766e" : props.selected ? "#2563eb" : "#94a3b8"),
|
|
1057
|
-
strokeDasharray: props.preview ? "6 6" :
|
|
1058
|
-
strokeWidth: props.attrs.strokeWidth ?? (props.selected ? 3 : 2),
|
|
1099
|
+
stroke: props.attrs.color ?? (props.preview ? props.edge.data?.isValid === false ? "#dc2626" : props.edge.data?.isValid === true ? "#16a34a" : "#0f766e" : props.selected ? "#2563eb" : props.edge.style?.stroke ?? "#94a3b8"),
|
|
1100
|
+
strokeDasharray: props.preview ? "6 6" : props.edge.style?.strokeDasharray,
|
|
1101
|
+
strokeWidth: props.attrs.strokeWidth ?? (props.selected ? 3 : props.edge.style?.strokeWidth ?? 2),
|
|
1102
|
+
markerStart: index === 0 ? markerToSVG(props.edge.style?.markerStart, props.markerPrefix) : void 0,
|
|
1103
|
+
markerEnd: index === props.pathData.segments.length - 1 ? markerToSVG(props.edge.style?.markerEnd, props.markerPrefix) : void 0,
|
|
1059
1104
|
style: props.attrs.style
|
|
1060
1105
|
}), props.preview ? null : /* @__PURE__ */ jsx("path", {
|
|
1061
1106
|
d,
|
|
@@ -1074,6 +1119,52 @@ function DefaultEdge(props) {
|
|
|
1074
1119
|
selected: props.selected
|
|
1075
1120
|
})] });
|
|
1076
1121
|
}
|
|
1122
|
+
function DefaultMarkerDefinitions(props) {
|
|
1123
|
+
return /* @__PURE__ */ jsxs("defs", { children: [
|
|
1124
|
+
/* @__PURE__ */ jsx("marker", {
|
|
1125
|
+
id: `${props.prefix}-arrow`,
|
|
1126
|
+
viewBox: "0 0 10 10",
|
|
1127
|
+
refX: "9",
|
|
1128
|
+
refY: "5",
|
|
1129
|
+
markerWidth: "6",
|
|
1130
|
+
markerHeight: "6",
|
|
1131
|
+
orient: "auto-start-reverse",
|
|
1132
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
1133
|
+
d: "M 1 1 L 9 5 L 1 9",
|
|
1134
|
+
fill: "none",
|
|
1135
|
+
stroke: "context-stroke"
|
|
1136
|
+
})
|
|
1137
|
+
}),
|
|
1138
|
+
/* @__PURE__ */ jsx("marker", {
|
|
1139
|
+
id: `${props.prefix}-arrow-closed`,
|
|
1140
|
+
viewBox: "0 0 10 10",
|
|
1141
|
+
refX: "9",
|
|
1142
|
+
refY: "5",
|
|
1143
|
+
markerWidth: "6",
|
|
1144
|
+
markerHeight: "6",
|
|
1145
|
+
orient: "auto-start-reverse",
|
|
1146
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
1147
|
+
d: "M 1 1 L 9 5 L 1 9 Z",
|
|
1148
|
+
fill: "context-stroke"
|
|
1149
|
+
})
|
|
1150
|
+
}),
|
|
1151
|
+
/* @__PURE__ */ jsx("marker", {
|
|
1152
|
+
id: `${props.prefix}-circle`,
|
|
1153
|
+
viewBox: "0 0 10 10",
|
|
1154
|
+
refX: "5",
|
|
1155
|
+
refY: "5",
|
|
1156
|
+
markerWidth: "5",
|
|
1157
|
+
markerHeight: "5",
|
|
1158
|
+
orient: "auto",
|
|
1159
|
+
children: /* @__PURE__ */ jsx("circle", {
|
|
1160
|
+
cx: "5",
|
|
1161
|
+
cy: "5",
|
|
1162
|
+
r: "4",
|
|
1163
|
+
fill: "context-stroke"
|
|
1164
|
+
})
|
|
1165
|
+
})
|
|
1166
|
+
] });
|
|
1167
|
+
}
|
|
1077
1168
|
function EdgeHitTarget(props) {
|
|
1078
1169
|
return /* @__PURE__ */ jsx(Fragment, { children: props.pathData.segments.map((segment, index) => {
|
|
1079
1170
|
return /* @__PURE__ */ jsx("path", {
|
|
@@ -1293,7 +1384,7 @@ function NodeLayer(props) {
|
|
|
1293
1384
|
const instance = useFlowInstance();
|
|
1294
1385
|
const renderableIds = useRenderableIds(instance, props.onlyRenderVisible);
|
|
1295
1386
|
const nodePaintEntries = useFlowSelector((context) => {
|
|
1296
|
-
const paintOrder = new Map(getOrderedEntities(instance
|
|
1387
|
+
const paintOrder = new Map(getOrderedEntities(instance).nodes.map((node, orderIndex) => [node.id, orderIndex]));
|
|
1297
1388
|
return context.graph.nodes.flatMap((node) => {
|
|
1298
1389
|
const orderIndex = paintOrder.get(node.id);
|
|
1299
1390
|
return orderIndex === void 0 ? [] : [{
|
|
@@ -1328,33 +1419,8 @@ function NodeLayer(props) {
|
|
|
1328
1419
|
})
|
|
1329
1420
|
});
|
|
1330
1421
|
}
|
|
1331
|
-
function getOrderedEntities(flow
|
|
1332
|
-
|
|
1333
|
-
const visibleOrdered = maybeEngine.getVisibleEntities?.();
|
|
1334
|
-
if (visibleOrdered) return visibleOrdered;
|
|
1335
|
-
const ordered = maybeEngine.getEntityOrder?.();
|
|
1336
|
-
if (ordered) return ordered;
|
|
1337
|
-
const depths = nodeDepths(context.graph.nodes);
|
|
1338
|
-
const nodeOrder = new Map(context.graph.nodes.map((node, index) => [node.id, index]));
|
|
1339
|
-
const edgeOrder = new Map(context.graph.edges.map((edge, index) => [edge.id, index]));
|
|
1340
|
-
return {
|
|
1341
|
-
nodes: [...context.graph.nodes].sort((a, b) => {
|
|
1342
|
-
const depthDelta = (depths.get(a.id) ?? 0) - (depths.get(b.id) ?? 0);
|
|
1343
|
-
if (depthDelta !== 0) return depthDelta;
|
|
1344
|
-
const zDelta = (a.zIndex ?? 0) - (b.zIndex ?? 0);
|
|
1345
|
-
if (zDelta !== 0) return zDelta;
|
|
1346
|
-
const selectedDelta = Number(context.selection.has(a.id)) - Number(context.selection.has(b.id));
|
|
1347
|
-
if (selectedDelta !== 0) return selectedDelta;
|
|
1348
|
-
return (nodeOrder.get(a.id) ?? 0) - (nodeOrder.get(b.id) ?? 0);
|
|
1349
|
-
}),
|
|
1350
|
-
edges: [...context.graph.edges].sort((a, b) => {
|
|
1351
|
-
const depthDelta = Math.max(depths.get(a.sourceId) ?? 0, depths.get(a.targetId) ?? 0) - Math.max(depths.get(b.sourceId) ?? 0, depths.get(b.targetId) ?? 0);
|
|
1352
|
-
if (depthDelta !== 0) return depthDelta;
|
|
1353
|
-
const zDelta = (a.zIndex ?? 0) - (b.zIndex ?? 0);
|
|
1354
|
-
if (zDelta !== 0) return zDelta;
|
|
1355
|
-
return (edgeOrder.get(a.id) ?? 0) - (edgeOrder.get(b.id) ?? 0);
|
|
1356
|
-
})
|
|
1357
|
-
};
|
|
1422
|
+
function getOrderedEntities(flow) {
|
|
1423
|
+
return flow.internals.engine.getVisibleEntities();
|
|
1358
1424
|
}
|
|
1359
1425
|
function rectsShallowEqual(a, b) {
|
|
1360
1426
|
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
@@ -1445,6 +1511,8 @@ const NodeView = memo(function NodeView(props) {
|
|
|
1445
1511
|
projections: props.projections
|
|
1446
1512
|
});
|
|
1447
1513
|
const focusable = props.flow.internals.engine.resolveEntityConfig(node).focusable;
|
|
1514
|
+
const groupGeometry = node.group ? props.flow.internals.engine.getGroupGeometry(node.id) : void 0;
|
|
1515
|
+
const groupHasInteractiveContent = Boolean(groupGeometry?.contentBounds.width && groupGeometry.contentBounds.height);
|
|
1448
1516
|
return /* @__PURE__ */ jsx(NodeRenderContextProvider, {
|
|
1449
1517
|
value: {
|
|
1450
1518
|
node,
|
|
@@ -1461,7 +1529,10 @@ const NodeView = memo(function NodeView(props) {
|
|
|
1461
1529
|
"aria-selected": selected,
|
|
1462
1530
|
"aria-label": props.flow.internals.engine.getAriaLabel(props.id) + (selected ? ", selected" : ""),
|
|
1463
1531
|
tabIndex: focusable ? 0 : void 0,
|
|
1464
|
-
onFocus: () =>
|
|
1532
|
+
onFocus: (event) => {
|
|
1533
|
+
if (descendantOwnsFlowFocus(event)) return;
|
|
1534
|
+
props.flow.internals.store.trigger.focus({ entityId: props.id });
|
|
1535
|
+
},
|
|
1465
1536
|
onBlur: (event) => clearEntityFocusOnBlur(props.flow, props.id, event),
|
|
1466
1537
|
style: {
|
|
1467
1538
|
...nodePositionStyle(bounds, {
|
|
@@ -1469,20 +1540,27 @@ const NodeView = memo(function NodeView(props) {
|
|
|
1469
1540
|
dragging
|
|
1470
1541
|
}),
|
|
1471
1542
|
cursor: dragging ? "grabbing" : "grab",
|
|
1472
|
-
pointerEvents: "auto",
|
|
1543
|
+
pointerEvents: node.group && groupHasInteractiveContent ? "none" : "auto",
|
|
1473
1544
|
userSelect: "none",
|
|
1474
1545
|
zIndex: props.orderIndex,
|
|
1475
1546
|
opacity: attrs.opacity
|
|
1476
1547
|
},
|
|
1477
|
-
children: props.renderNode ?
|
|
1478
|
-
flow:
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1548
|
+
children: props.renderNode ? /* @__PURE__ */ jsx("div", {
|
|
1549
|
+
"data-flow-custom-node": true,
|
|
1550
|
+
style: {
|
|
1551
|
+
display: "contents",
|
|
1552
|
+
pointerEvents: "none"
|
|
1553
|
+
},
|
|
1554
|
+
children: props.renderNode({
|
|
1555
|
+
flow: props.flow,
|
|
1556
|
+
node,
|
|
1557
|
+
bounds,
|
|
1558
|
+
attrs,
|
|
1559
|
+
selected,
|
|
1560
|
+
dragging,
|
|
1561
|
+
hovered,
|
|
1562
|
+
highlights
|
|
1563
|
+
})
|
|
1486
1564
|
}) : node.group ? /* @__PURE__ */ jsx(DefaultGroupNode, {
|
|
1487
1565
|
flow: props.flow,
|
|
1488
1566
|
node,
|
|
@@ -1548,6 +1626,7 @@ function DefaultGroupNode(props) {
|
|
|
1548
1626
|
color: "#0f172a",
|
|
1549
1627
|
font: "600 13px/1.2 system-ui, sans-serif",
|
|
1550
1628
|
cursor: props.dragging ? "grabbing" : "grab",
|
|
1629
|
+
pointerEvents: "auto",
|
|
1551
1630
|
userSelect: "none",
|
|
1552
1631
|
...props.attrs.style
|
|
1553
1632
|
},
|
|
@@ -1710,7 +1789,7 @@ function FlowToolbar(props) {
|
|
|
1710
1789
|
/**
|
|
1711
1790
|
* Overview map. Deliberately thin: all math lives in core
|
|
1712
1791
|
* (`getMinimapProjection`, `viewportCenteredOn`) and every interaction is a
|
|
1713
|
-
*
|
|
1792
|
+
* controller viewport command — this component is exactly what you would write in
|
|
1714
1793
|
* userland, kept here as a convenience.
|
|
1715
1794
|
*/
|
|
1716
1795
|
function MiniMap(props) {
|
|
@@ -1735,7 +1814,7 @@ function MiniMap(props) {
|
|
|
1735
1814
|
x: event.clientX - rect.left,
|
|
1736
1815
|
y: event.clientY - rect.top
|
|
1737
1816
|
});
|
|
1738
|
-
flow.
|
|
1817
|
+
flow.viewport.set(viewportCenteredOn(canvasPoint, viewportSize, viewport.zoom));
|
|
1739
1818
|
};
|
|
1740
1819
|
return /* @__PURE__ */ jsxs("svg", {
|
|
1741
1820
|
"aria-label": "minimap",
|
|
@@ -1858,8 +1937,7 @@ const buttonStyle = {
|
|
|
1858
1937
|
cursor: "pointer"
|
|
1859
1938
|
};
|
|
1860
1939
|
/**
|
|
1861
|
-
* Zoom/fit toolbar
|
|
1862
|
-
* (`zoomBy`, `fitView`) — replicate or extend it freely in userland.
|
|
1940
|
+
* Zoom/fit toolbar backed by the controller's constrained viewport API.
|
|
1863
1941
|
*/
|
|
1864
1942
|
function Controls(props) {
|
|
1865
1943
|
const flow = useFlowInstance();
|
|
@@ -1879,21 +1957,21 @@ function Controls(props) {
|
|
|
1879
1957
|
type: "button",
|
|
1880
1958
|
"aria-label": "zoom in",
|
|
1881
1959
|
style: buttonStyle,
|
|
1882
|
-
onClick: () => flow.
|
|
1960
|
+
onClick: () => flow.viewport.zoomBy(1.2),
|
|
1883
1961
|
children: "+"
|
|
1884
1962
|
}),
|
|
1885
1963
|
/* @__PURE__ */ jsx("button", {
|
|
1886
1964
|
type: "button",
|
|
1887
1965
|
"aria-label": "zoom out",
|
|
1888
1966
|
style: buttonStyle,
|
|
1889
|
-
onClick: () => flow.
|
|
1967
|
+
onClick: () => flow.viewport.zoomBy(1 / 1.2),
|
|
1890
1968
|
children: "−"
|
|
1891
1969
|
}),
|
|
1892
1970
|
/* @__PURE__ */ jsx("button", {
|
|
1893
1971
|
type: "button",
|
|
1894
1972
|
"aria-label": "fit view",
|
|
1895
1973
|
style: buttonStyle,
|
|
1896
|
-
onClick: () => flow.
|
|
1974
|
+
onClick: () => flow.viewport.fit({ padding: .1 }),
|
|
1897
1975
|
children: "⛶"
|
|
1898
1976
|
}),
|
|
1899
1977
|
props.children
|
|
@@ -2000,6 +2078,11 @@ function Port(props) {
|
|
|
2000
2078
|
const observerRef = useRef(null);
|
|
2001
2079
|
const observer = useMeasurementObserver();
|
|
2002
2080
|
const measured = props.measured ?? true;
|
|
2081
|
+
const interaction = props.interaction ?? "connect";
|
|
2082
|
+
const port = node.ports?.find((candidate) => candidate.name === props.name);
|
|
2083
|
+
const portId = `${node.id}:${props.name}`;
|
|
2084
|
+
const focusable = flow.internals.engine.resolveEntityConfig(node).focusable;
|
|
2085
|
+
const selected = useFlowSelector((context) => context.selection.has(portId), { flow });
|
|
2003
2086
|
useLayoutEffect(() => {
|
|
2004
2087
|
const element = ref.current;
|
|
2005
2088
|
if (!element || !measured) return;
|
|
@@ -2035,8 +2118,20 @@ function Port(props) {
|
|
|
2035
2118
|
className: props.className,
|
|
2036
2119
|
"data-flow-port-name": props.name,
|
|
2037
2120
|
"data-flow-node-id": node.id,
|
|
2121
|
+
"data-flow-port-id": portId,
|
|
2122
|
+
"data-flow-port-interaction": interaction,
|
|
2123
|
+
"data-flow-selected": selected ? "true" : void 0,
|
|
2038
2124
|
"data-connection-candidate": candidate ?? void 0,
|
|
2039
|
-
|
|
2125
|
+
role: interaction === "inspect" ? "button" : void 0,
|
|
2126
|
+
"aria-label": interaction === "inspect" ? `${port?.label ?? props.name} port` : void 0,
|
|
2127
|
+
"aria-pressed": interaction === "inspect" ? selected : void 0,
|
|
2128
|
+
tabIndex: interaction === "inspect" && focusable ? 0 : void 0,
|
|
2129
|
+
onFocus: interaction === "inspect" && focusable ? () => flow.internals.store.trigger.focus({ entityId: portId }) : void 0,
|
|
2130
|
+
onBlur: interaction === "inspect" ? (event) => clearEntityFocusOnBlur(flow, portId, event) : void 0,
|
|
2131
|
+
style: {
|
|
2132
|
+
pointerEvents: "auto",
|
|
2133
|
+
...props.style
|
|
2134
|
+
},
|
|
2040
2135
|
children: props.children
|
|
2041
2136
|
});
|
|
2042
2137
|
}
|
|
@@ -2073,96 +2168,6 @@ function DragHandle({ children, style, ...rest }) {
|
|
|
2073
2168
|
});
|
|
2074
2169
|
}
|
|
2075
2170
|
//#endregion
|
|
2076
|
-
//#region src/NodeResizer.tsx
|
|
2077
|
-
const DEFAULT_HANDLES = [
|
|
2078
|
-
"top",
|
|
2079
|
-
"right",
|
|
2080
|
-
"bottom",
|
|
2081
|
-
"left",
|
|
2082
|
-
"top-left",
|
|
2083
|
-
"top-right",
|
|
2084
|
-
"bottom-left",
|
|
2085
|
-
"bottom-right"
|
|
2086
|
-
];
|
|
2087
|
-
const HANDLE_STYLE = {
|
|
2088
|
-
top: {
|
|
2089
|
-
left: "50%",
|
|
2090
|
-
top: 0,
|
|
2091
|
-
cursor: "ns-resize"
|
|
2092
|
-
},
|
|
2093
|
-
right: {
|
|
2094
|
-
left: "100%",
|
|
2095
|
-
top: "50%",
|
|
2096
|
-
cursor: "ew-resize"
|
|
2097
|
-
},
|
|
2098
|
-
bottom: {
|
|
2099
|
-
left: "50%",
|
|
2100
|
-
top: "100%",
|
|
2101
|
-
cursor: "ns-resize"
|
|
2102
|
-
},
|
|
2103
|
-
left: {
|
|
2104
|
-
left: 0,
|
|
2105
|
-
top: "50%",
|
|
2106
|
-
cursor: "ew-resize"
|
|
2107
|
-
},
|
|
2108
|
-
"top-left": {
|
|
2109
|
-
left: 0,
|
|
2110
|
-
top: 0,
|
|
2111
|
-
cursor: "nwse-resize"
|
|
2112
|
-
},
|
|
2113
|
-
"top-right": {
|
|
2114
|
-
left: "100%",
|
|
2115
|
-
top: 0,
|
|
2116
|
-
cursor: "nesw-resize"
|
|
2117
|
-
},
|
|
2118
|
-
"bottom-left": {
|
|
2119
|
-
left: 0,
|
|
2120
|
-
top: "100%",
|
|
2121
|
-
cursor: "nesw-resize"
|
|
2122
|
-
},
|
|
2123
|
-
"bottom-right": {
|
|
2124
|
-
left: "100%",
|
|
2125
|
-
top: "100%",
|
|
2126
|
-
cursor: "nwse-resize"
|
|
2127
|
-
}
|
|
2128
|
-
};
|
|
2129
|
-
function NodeResizer(props) {
|
|
2130
|
-
const { node } = useNodeRenderContext();
|
|
2131
|
-
const selected = useFlowSelector((context) => context.selection.has(node.id));
|
|
2132
|
-
if (!(props.isVisible ?? selected)) return null;
|
|
2133
|
-
return /* @__PURE__ */ jsx("div", {
|
|
2134
|
-
"aria-hidden": "true",
|
|
2135
|
-
className: props.className,
|
|
2136
|
-
"data-flow-node-resizer": "",
|
|
2137
|
-
style: {
|
|
2138
|
-
position: "absolute",
|
|
2139
|
-
inset: 0,
|
|
2140
|
-
pointerEvents: "none",
|
|
2141
|
-
...props.style
|
|
2142
|
-
},
|
|
2143
|
-
children: (props.handles ?? DEFAULT_HANDLES).map((handle) => /* @__PURE__ */ jsx("div", {
|
|
2144
|
-
"data-entity-id": node.id,
|
|
2145
|
-
"data-flow-part": `resize-handle:${handle}`,
|
|
2146
|
-
"data-flow-resize-handle": handle,
|
|
2147
|
-
style: {
|
|
2148
|
-
position: "absolute",
|
|
2149
|
-
width: "calc(10px / var(--flow-zoom, 1))",
|
|
2150
|
-
height: "calc(10px / var(--flow-zoom, 1))",
|
|
2151
|
-
minWidth: 6,
|
|
2152
|
-
minHeight: 6,
|
|
2153
|
-
boxSizing: "border-box",
|
|
2154
|
-
border: "calc(1px / var(--flow-zoom, 1)) solid #ffffff",
|
|
2155
|
-
borderRadius: "calc(2px / var(--flow-zoom, 1))",
|
|
2156
|
-
background: "var(--flow-selection-color, #2563eb)",
|
|
2157
|
-
boxShadow: "0 0 0 calc(1px / var(--flow-zoom, 1)) rgba(15, 23, 42, 0.8)",
|
|
2158
|
-
pointerEvents: "auto",
|
|
2159
|
-
transform: "translate(-50%, -50%)",
|
|
2160
|
-
...HANDLE_STYLE[handle]
|
|
2161
|
-
}
|
|
2162
|
-
}, handle))
|
|
2163
|
-
});
|
|
2164
|
-
}
|
|
2165
|
-
//#endregion
|
|
2166
2171
|
//#region src/ColorMode.tsx
|
|
2167
2172
|
function FlowColorModeScope(props) {
|
|
2168
2173
|
const { colorMode, resolvedColorMode } = useFlowColorMode();
|
|
@@ -2207,4 +2212,4 @@ function useApplyFlowColorMode(element) {
|
|
|
2207
2212
|
]);
|
|
2208
2213
|
}
|
|
2209
2214
|
//#endregion
|
|
2210
|
-
export { Background, Controls, CrossBackground, DotsBackground, DragHandle, EntityAnchor, FLOW_BASE_CSS, FLOW_BASE_STYLES_ID, FlowColorModeScope, FlowInstanceProvider, FlowToolbar, LinesBackground, MiniMap,
|
|
2215
|
+
export { Background, Controls, CrossBackground, DotsBackground, DragHandle, EntityAnchor, FLOW_BASE_CSS, FLOW_BASE_STYLES_ID, FlowColorModeScope, FlowInstanceProvider, FlowToolbar, LinesBackground, MiniMap, PanePortal, Panel, Port, StatelyFlow, ViewportPortal, createFlowInstance, useApplyFlowColorMode, useConnection, useCreateFlowInstance, useFitViewExclusion, useFlow, useFlowBaseStyles, useFlowColorMode, useFlowConnections, useFlowContext, useFlowEdge, useFlowEntity, useFlowInput, useFlowInstance, useFlowMeasurement, useFlowNode, useFlowPort, useFlowSelector, useFlowToolbar, useFlowValue, useFlowView, useFlowViewport, useKeyPress, useMeasuredSize, useNodesInitialized, useOptionalFlowInstance };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/flow-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Thin React adapter for @statelyai/flow with core and DOM re-exports.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"use-sync-external-store": "^1.6.0",
|
|
37
|
-
"@statelyai/flow": "0.
|
|
38
|
-
"@statelyai/flow-dom": "3.
|
|
37
|
+
"@statelyai/flow": "0.8.2",
|
|
38
|
+
"@statelyai/flow-dom": "3.4.2"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "^18.2.0 || ^19.0.0",
|