@statelyai/flow-react 0.9.2 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.js +39 -12
- package/package.json +7 -4
package/dist/esm/index.js
CHANGED
|
@@ -561,8 +561,31 @@ function assignRef(ref, value) {
|
|
|
561
561
|
if (typeof ref === "function") ref(value);
|
|
562
562
|
else if (ref) ref.current = value;
|
|
563
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* `dragRevision` changes only for transient drag geometry. Topology, paint
|
|
566
|
+
* order, and viewport membership stay stable during that transition, so
|
|
567
|
+
* list-level selectors can retain their prior value while per-entity DOM
|
|
568
|
+
* observers update only the moved cohort.
|
|
569
|
+
*/
|
|
570
|
+
function useDragStableFlowSelector(selector, isEqual) {
|
|
571
|
+
const cached = useRef(null);
|
|
572
|
+
return useFlowSelector((context) => {
|
|
573
|
+
if (cached.current && context.canvasState === "dragging" && cached.current.canvasState === "dragging" && context.dragRevision !== cached.current.dragRevision) {
|
|
574
|
+
cached.current.canvasState = context.canvasState;
|
|
575
|
+
cached.current.dragRevision = context.dragRevision;
|
|
576
|
+
return cached.current.value;
|
|
577
|
+
}
|
|
578
|
+
const value = selector(context);
|
|
579
|
+
cached.current = {
|
|
580
|
+
canvasState: context.canvasState,
|
|
581
|
+
dragRevision: context.dragRevision,
|
|
582
|
+
value
|
|
583
|
+
};
|
|
584
|
+
return value;
|
|
585
|
+
}, { isEqual });
|
|
586
|
+
}
|
|
564
587
|
function useRenderableIds(instance, enabled) {
|
|
565
|
-
return
|
|
588
|
+
return useDragStableFlowSelector((context) => computeRenderableIds(instance.internals.engine, context, enabled), renderableIdsEqual);
|
|
566
589
|
}
|
|
567
590
|
function useViewportTransform(ref, instance) {
|
|
568
591
|
useLayoutEffect(() => {
|
|
@@ -934,7 +957,7 @@ function EdgeLayer(props) {
|
|
|
934
957
|
const instance = useFlowInstance();
|
|
935
958
|
const markerPrefix = `stately-flow-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
936
959
|
const renderableIds = useRenderableIds(instance, props.onlyRenderVisible);
|
|
937
|
-
const edgeIds =
|
|
960
|
+
const edgeIds = useDragStableFlowSelector((_context) => getOrderedEntities(instance).edges.map((edge) => edge.id), stringArraysEqual);
|
|
938
961
|
const renderEdge = props.renderEdge;
|
|
939
962
|
return /* @__PURE__ */ jsxs("svg", {
|
|
940
963
|
"aria-label": "edges",
|
|
@@ -979,7 +1002,8 @@ const EdgeView = memo(function EdgeView(props) {
|
|
|
979
1002
|
hovered: context.hoveredEntityIds.has(props.id),
|
|
980
1003
|
highlights: props.flow.internals.engine.getHighlights(props.id)
|
|
981
1004
|
}), { isEqual: (a, b) => {
|
|
982
|
-
if (!(a.selected === b.selected && a.focused === b.focused && a.hovered === b.hovered && a.highlights === b.highlights)
|
|
1005
|
+
if (!(a.selected === b.selected && a.focused === b.focused && a.hovered === b.hovered && a.highlights === b.highlights)) return false;
|
|
1006
|
+
if (a.geometryActive !== b.geometryActive) return b.geometryActive ? edgeRenderContentEqual(a.edge, b.edge) : false;
|
|
983
1007
|
if (a.geometryActive) return edgeRenderContentEqual(a.edge, b.edge);
|
|
984
1008
|
return a.graph === b.graph && a.edge === b.edge && a.pathData === b.pathData;
|
|
985
1009
|
} });
|
|
@@ -1258,7 +1282,8 @@ const EdgeLabelItem = memo(function EdgeLabelItem(props) {
|
|
|
1258
1282
|
hovered: context.hoveredEntityIds.has(props.id),
|
|
1259
1283
|
highlights: props.flow.internals.engine.getHighlights(props.id)
|
|
1260
1284
|
}), { isEqual: (a, b) => {
|
|
1261
|
-
if (!(a.selected === b.selected && a.dragging === b.dragging && a.hovered === b.hovered && a.highlights === b.highlights)
|
|
1285
|
+
if (!(a.selected === b.selected && a.dragging === b.dragging && a.hovered === b.hovered && a.highlights === b.highlights)) return false;
|
|
1286
|
+
if (a.geometryActive !== b.geometryActive) return b.geometryActive ? edgeRenderContentEqual(a.edge, b.edge) : false;
|
|
1262
1287
|
if (a.geometryActive) return edgeRenderContentEqual(a.edge, b.edge);
|
|
1263
1288
|
return a.graph === b.graph && a.edge === b.edge && a.pathData === b.pathData;
|
|
1264
1289
|
} });
|
|
@@ -1383,16 +1408,17 @@ function projectedEdgeLabelBounds(pathData, label) {
|
|
|
1383
1408
|
function NodeLayer(props) {
|
|
1384
1409
|
const instance = useFlowInstance();
|
|
1385
1410
|
const renderableIds = useRenderableIds(instance, props.onlyRenderVisible);
|
|
1386
|
-
const nodePaintEntries =
|
|
1387
|
-
const paintOrder = new Map(getOrderedEntities(instance).nodes.map((node, orderIndex) => [node.id, orderIndex]));
|
|
1411
|
+
const nodePaintEntries = useDragStableFlowSelector((context) => {
|
|
1412
|
+
const paintOrder = new Map(getOrderedEntities(instance, { elevateSelected: false }).nodes.map((node, orderIndex) => [node.id, orderIndex]));
|
|
1413
|
+
const selectedBoost = paintOrder.size;
|
|
1388
1414
|
return context.graph.nodes.flatMap((node) => {
|
|
1389
1415
|
const orderIndex = paintOrder.get(node.id);
|
|
1390
1416
|
return orderIndex === void 0 ? [] : [{
|
|
1391
1417
|
id: node.id,
|
|
1392
|
-
orderIndex
|
|
1418
|
+
orderIndex: orderIndex + (context.selection.has(node.id) ? selectedBoost : 0)
|
|
1393
1419
|
}];
|
|
1394
1420
|
});
|
|
1395
|
-
},
|
|
1421
|
+
}, nodePaintEntriesEqual);
|
|
1396
1422
|
const observer = useMeasurementObserver();
|
|
1397
1423
|
return /* @__PURE__ */ jsx("div", {
|
|
1398
1424
|
"aria-label": "nodes",
|
|
@@ -1419,8 +1445,8 @@ function NodeLayer(props) {
|
|
|
1419
1445
|
})
|
|
1420
1446
|
});
|
|
1421
1447
|
}
|
|
1422
|
-
function getOrderedEntities(flow) {
|
|
1423
|
-
return flow.internals.engine.getVisibleEntities();
|
|
1448
|
+
function getOrderedEntities(flow, options = {}) {
|
|
1449
|
+
return flow.internals.engine.getVisibleEntities(options);
|
|
1424
1450
|
}
|
|
1425
1451
|
function rectsShallowEqual(a, b) {
|
|
1426
1452
|
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
@@ -1469,7 +1495,8 @@ const NodeView = memo(function NodeView(props) {
|
|
|
1469
1495
|
bounds: context.resizingEntityId === props.id && context.resizeCorners ? props.flow.internals.engine.getResizeBounds() ?? props.flow.internals.engine.visualBounds(props.id) : props.flow.internals.engine.visualBounds(props.id)
|
|
1470
1496
|
};
|
|
1471
1497
|
}, { isEqual: (a, b) => {
|
|
1472
|
-
if (!(a.hidden === b.hidden && a.selected === b.selected && a.dragging === b.dragging && a.focused === b.focused && a.hovered === b.hovered && a.highlights === b.highlights)
|
|
1498
|
+
if (!(a.hidden === b.hidden && a.selected === b.selected && a.dragging === b.dragging && a.focused === b.focused && a.hovered === b.hovered && a.highlights === b.highlights)) return false;
|
|
1499
|
+
if (a.geometryActive !== b.geometryActive) return b.geometryActive ? nodeRenderContentEqual(a.node, b.node) : false;
|
|
1473
1500
|
if (a.geometryActive) return nodeRenderContentEqual(a.node, b.node);
|
|
1474
1501
|
return a.graph === b.graph && a.node === b.node && rectsShallowEqual(a.bounds, b.bounds);
|
|
1475
1502
|
} });
|
|
@@ -1540,7 +1567,7 @@ const NodeView = memo(function NodeView(props) {
|
|
|
1540
1567
|
dragging
|
|
1541
1568
|
}),
|
|
1542
1569
|
cursor: dragging ? "grabbing" : "grab",
|
|
1543
|
-
pointerEvents: node.group && groupHasInteractiveContent ? "none" : "auto",
|
|
1570
|
+
pointerEvents: node.group?.type === "derived" || node.group && groupHasInteractiveContent ? "none" : "auto",
|
|
1544
1571
|
userSelect: "none",
|
|
1545
1572
|
zIndex: props.orderIndex,
|
|
1546
1573
|
opacity: attrs.opacity
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/flow-react",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
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.4.
|
|
37
|
+
"@statelyai/flow": "0.9.0",
|
|
38
|
+
"@statelyai/flow-dom": "3.4.3"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "^18.2.0 || ^19.0.0",
|
|
@@ -83,7 +83,10 @@
|
|
|
83
83
|
"typecheck": "tsc --noEmit",
|
|
84
84
|
"test:browser": "vitest run --config vitest.browser.config.mjs",
|
|
85
85
|
"test:e2e": "playwright test",
|
|
86
|
-
"bench:browser": "playwright test --config playwright.performance.config.ts",
|
|
86
|
+
"bench:browser": "playwright test --config playwright.performance.config.ts && node ../../scripts/report-browser-benchmarks.mjs",
|
|
87
|
+
"bench:react": "playwright test --config playwright.performance.config.ts --grep StatelyFlow && node ../../scripts/report-browser-benchmarks.mjs",
|
|
88
|
+
"bench:interaction": "playwright test --config playwright.performance.config.ts --grep 'viewport pan' && node ../../scripts/report-browser-benchmarks.mjs",
|
|
89
|
+
"bench:comparison": "playwright test --config playwright.performance.config.ts --grep 'StatelyFlow|React Flow' && node ../../scripts/report-browser-benchmarks.mjs",
|
|
87
90
|
"storybook": "storybook dev -p 6008",
|
|
88
91
|
"storybook:build": "storybook build"
|
|
89
92
|
}
|