@statelyai/flow-react 0.9.4 → 0.9.5
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.d.ts +2 -4
- package/dist/esm/index.js +192 -41
- package/package.json +3 -3
package/dist/esm/index.d.ts
CHANGED
|
@@ -341,10 +341,8 @@ type MiniMapProps = {
|
|
|
341
341
|
renderNode?: (node: FlowNode, rect: Rect) => ReactNode;
|
|
342
342
|
};
|
|
343
343
|
/**
|
|
344
|
-
* Overview map.
|
|
345
|
-
*
|
|
346
|
-
* controller viewport command — this component is exactly what you would write in
|
|
347
|
-
* userland, kept here as a convenience.
|
|
344
|
+
* Overview map. Projection and navigation math live in core; this adapter owns
|
|
345
|
+
* only React rendering and the browser pointer session.
|
|
348
346
|
*/
|
|
349
347
|
declare function MiniMap(props: MiniMapProps): import("react").JSX.Element;
|
|
350
348
|
//#endregion
|
package/dist/esm/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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, markerToSVG, pathToSVG, profileModesForContext, profilePoliciesForContext, viewportCenteredOn } from "@statelyai/flow";
|
|
4
|
-
import { createContext, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
5
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { CONNECTION_PREVIEW_EDGE_ID, areNodesMeasured, createConnectionPreviewGraph, createEntityChangeNotifier, createFlow, createFlowInputRuntime, getAnchorTransform, getAutoPanDelta, getMinimapProjection, getMinimapViewportCenter, getProjectedEdgeAttrs, getProjectedNodeAttrs, getToolbarPlacement, getVisibleBounds, markerToSVG, pathToSVG, profileModesForContext, profilePoliciesForContext, viewportCenteredOn } from "@statelyai/flow";
|
|
4
|
+
import { Fragment, createContext, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
5
|
+
import { Fragment as Fragment$1, 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";
|
|
@@ -323,7 +323,7 @@ function useFlowInput(flow, containerRef, options = {}) {
|
|
|
323
323
|
store: flow.internals.store,
|
|
324
324
|
runtime,
|
|
325
325
|
keyboardTarget,
|
|
326
|
-
autoPan:
|
|
326
|
+
autoPan: getAutoPanDelta
|
|
327
327
|
});
|
|
328
328
|
}, [
|
|
329
329
|
flow,
|
|
@@ -1114,7 +1114,7 @@ function ConnectionPreviewEdge(props) {
|
|
|
1114
1114
|
});
|
|
1115
1115
|
}
|
|
1116
1116
|
function DefaultEdge(props) {
|
|
1117
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [props.pathData.segments.map((segment, index) => {
|
|
1117
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [props.pathData.segments.map((segment, index) => {
|
|
1118
1118
|
const d = pathToSVG(segment);
|
|
1119
1119
|
return /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("path", {
|
|
1120
1120
|
d,
|
|
@@ -1190,7 +1190,7 @@ function DefaultMarkerDefinitions(props) {
|
|
|
1190
1190
|
] });
|
|
1191
1191
|
}
|
|
1192
1192
|
function EdgeHitTarget(props) {
|
|
1193
|
-
return /* @__PURE__ */ jsx(Fragment, { children: props.pathData.segments.map((segment, index) => {
|
|
1193
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: props.pathData.segments.map((segment, index) => {
|
|
1194
1194
|
return /* @__PURE__ */ jsx("path", {
|
|
1195
1195
|
d: pathToSVG(segment),
|
|
1196
1196
|
"data-flow-edge-segment": index,
|
|
@@ -1211,7 +1211,7 @@ function EdgeEndpointHandles(props) {
|
|
|
1211
1211
|
if (!first || !last) return null;
|
|
1212
1212
|
const radius = 6 / props.flow.getContext().viewport.zoom;
|
|
1213
1213
|
const strokeWidth = 2 / props.flow.getContext().viewport.zoom;
|
|
1214
|
-
return /* @__PURE__ */ jsx(Fragment, { children: [{
|
|
1214
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: [{
|
|
1215
1215
|
end: "source",
|
|
1216
1216
|
point: first,
|
|
1217
1217
|
cursor: "crosshair"
|
|
@@ -1627,7 +1627,7 @@ function DefaultGroupNode(props) {
|
|
|
1627
1627
|
height: rect.height
|
|
1628
1628
|
});
|
|
1629
1629
|
const dropTarget = props.highlights.some((entry) => entry.kind === "drop-target");
|
|
1630
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", { style: {
|
|
1630
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", { style: {
|
|
1631
1631
|
position: "absolute",
|
|
1632
1632
|
...relative(geometry.childArea),
|
|
1633
1633
|
boxSizing: "border-box",
|
|
@@ -1812,35 +1812,115 @@ function FlowToolbar(props) {
|
|
|
1812
1812
|
}
|
|
1813
1813
|
//#endregion
|
|
1814
1814
|
//#region src/MiniMap.tsx
|
|
1815
|
+
const DRAG_JITTER_THRESHOLD_SQ = 4;
|
|
1816
|
+
function containsPoint(rect, point) {
|
|
1817
|
+
return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
|
|
1818
|
+
}
|
|
1819
|
+
function setRect(element, rect) {
|
|
1820
|
+
if (!element) return;
|
|
1821
|
+
element.setAttribute("x", String(rect.x));
|
|
1822
|
+
element.setAttribute("y", String(rect.y));
|
|
1823
|
+
element.setAttribute("width", String(rect.width));
|
|
1824
|
+
element.setAttribute("height", String(rect.height));
|
|
1825
|
+
}
|
|
1826
|
+
function projectionTransform(from, to) {
|
|
1827
|
+
const scale = to.scale / from.scale;
|
|
1828
|
+
const fromOrigin = from.toMinimap({
|
|
1829
|
+
x: 0,
|
|
1830
|
+
y: 0
|
|
1831
|
+
});
|
|
1832
|
+
const toOrigin = to.toMinimap({
|
|
1833
|
+
x: 0,
|
|
1834
|
+
y: 0
|
|
1835
|
+
});
|
|
1836
|
+
return `translate(${toOrigin.x - fromOrigin.x * scale} ${toOrigin.y - fromOrigin.y * scale}) scale(${scale})`;
|
|
1837
|
+
}
|
|
1815
1838
|
/**
|
|
1816
|
-
* Overview map.
|
|
1817
|
-
*
|
|
1818
|
-
* controller viewport command — this component is exactly what you would write in
|
|
1819
|
-
* userland, kept here as a convenience.
|
|
1839
|
+
* Overview map. Projection and navigation math live in core; this adapter owns
|
|
1840
|
+
* only React rendering and the browser pointer session.
|
|
1820
1841
|
*/
|
|
1821
1842
|
function MiniMap(props) {
|
|
1822
1843
|
const flow = useFlowInstance();
|
|
1823
1844
|
const width = props.width ?? 200;
|
|
1824
1845
|
const height = props.height ?? 140;
|
|
1825
1846
|
const nodes = useFlowSelector((context) => context.graph.nodes);
|
|
1826
|
-
const
|
|
1847
|
+
const measurements = useFlowSelector((context) => context.measurements);
|
|
1827
1848
|
const viewportSize = useFlowSelector((context) => context.viewportSize);
|
|
1828
|
-
const
|
|
1829
|
-
const
|
|
1830
|
-
|
|
1831
|
-
|
|
1849
|
+
const markerGroupRef = useRef(null);
|
|
1850
|
+
const viewportRectRef = useRef(null);
|
|
1851
|
+
const sessionRef = useRef(null);
|
|
1852
|
+
const endSessionRef = useRef(null);
|
|
1853
|
+
const paintViewportRef = useRef(() => {});
|
|
1854
|
+
const contentBounds = useMemo(() => {
|
|
1855
|
+
return flow.getGraphBounds();
|
|
1856
|
+
}, [
|
|
1857
|
+
flow,
|
|
1858
|
+
nodes,
|
|
1859
|
+
measurements
|
|
1860
|
+
]);
|
|
1861
|
+
const nodeBounds = useMemo(() => {
|
|
1862
|
+
return nodes.map((node) => flow.internals.engine.visualBounds(node.id));
|
|
1863
|
+
}, [
|
|
1864
|
+
flow,
|
|
1865
|
+
nodes,
|
|
1866
|
+
measurements
|
|
1867
|
+
]);
|
|
1868
|
+
const baseProjection = useMemo(() => getMinimapProjection({
|
|
1869
|
+
contentBounds,
|
|
1870
|
+
viewport: flow.getSnapshot().viewport,
|
|
1832
1871
|
viewportSize,
|
|
1833
1872
|
width,
|
|
1834
1873
|
height,
|
|
1835
1874
|
padding: props.padding
|
|
1836
|
-
})
|
|
1837
|
-
|
|
1875
|
+
}), [
|
|
1876
|
+
contentBounds,
|
|
1877
|
+
flow,
|
|
1878
|
+
height,
|
|
1879
|
+
props.padding,
|
|
1880
|
+
viewportSize,
|
|
1881
|
+
width
|
|
1882
|
+
]);
|
|
1883
|
+
const projectedNodes = useMemo(() => nodeBounds.map((bounds) => baseProjection.rectToMinimap(bounds)), [baseProjection, nodeBounds]);
|
|
1884
|
+
const initialViewport = flow.getSnapshot().viewport;
|
|
1885
|
+
const viewportRect = baseProjection.rectToMinimap(getVisibleBounds(initialViewport, viewportSize));
|
|
1886
|
+
paintViewportRef.current = (nextViewport) => {
|
|
1887
|
+
const projection = sessionRef.current?.projection ?? getMinimapProjection({
|
|
1888
|
+
contentBounds,
|
|
1889
|
+
viewport: nextViewport,
|
|
1890
|
+
viewportSize,
|
|
1891
|
+
width,
|
|
1892
|
+
height,
|
|
1893
|
+
padding: props.padding
|
|
1894
|
+
});
|
|
1895
|
+
markerGroupRef.current?.setAttribute("transform", projectionTransform(baseProjection, projection));
|
|
1896
|
+
setRect(viewportRectRef.current, projection.rectToMinimap(getVisibleBounds(nextViewport, viewportSize)));
|
|
1897
|
+
};
|
|
1898
|
+
useLayoutEffect(() => {
|
|
1899
|
+
paintViewportRef.current(flow.getSnapshot().viewport);
|
|
1900
|
+
}, [
|
|
1901
|
+
baseProjection,
|
|
1902
|
+
flow,
|
|
1903
|
+
nodeBounds
|
|
1904
|
+
]);
|
|
1905
|
+
useEffect(() => flow.listen((context) => context.viewport, (nextViewport) => paintViewportRef.current(nextViewport)), [flow]);
|
|
1906
|
+
useEffect(() => () => endSessionRef.current?.(), []);
|
|
1907
|
+
const pointFromEvent = (event, activeProjection) => {
|
|
1838
1908
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1839
|
-
|
|
1909
|
+
return activeProjection.toCanvas({
|
|
1840
1910
|
x: event.clientX - rect.left,
|
|
1841
1911
|
y: event.clientY - rect.top
|
|
1842
1912
|
});
|
|
1843
|
-
|
|
1913
|
+
};
|
|
1914
|
+
const navigate = (event, session) => {
|
|
1915
|
+
const viewport = flow.getSnapshot().viewport;
|
|
1916
|
+
const visibleBounds = getVisibleBounds(viewport, viewportSize);
|
|
1917
|
+
const center = getMinimapViewportCenter({
|
|
1918
|
+
point: pointFromEvent(event, session.projection),
|
|
1919
|
+
grabOffset: session.grabOffset,
|
|
1920
|
+
contentBounds,
|
|
1921
|
+
viewportBounds: visibleBounds
|
|
1922
|
+
});
|
|
1923
|
+
flow.viewport.set(viewportCenteredOn(center, viewportSize, viewport.zoom));
|
|
1844
1924
|
};
|
|
1845
1925
|
return /* @__PURE__ */ jsxs("svg", {
|
|
1846
1926
|
"aria-label": "minimap",
|
|
@@ -1848,19 +1928,84 @@ function MiniMap(props) {
|
|
|
1848
1928
|
height,
|
|
1849
1929
|
className: props.className,
|
|
1850
1930
|
onPointerDown: (event) => {
|
|
1851
|
-
|
|
1852
|
-
|
|
1931
|
+
if (event.button !== 0 || nodes.length === 0) return;
|
|
1932
|
+
endSessionRef.current?.();
|
|
1933
|
+
const viewport = flow.getSnapshot().viewport;
|
|
1934
|
+
const currentProjection = getMinimapProjection({
|
|
1935
|
+
contentBounds,
|
|
1936
|
+
viewport,
|
|
1937
|
+
viewportSize,
|
|
1938
|
+
width,
|
|
1939
|
+
height,
|
|
1940
|
+
padding: props.padding
|
|
1941
|
+
});
|
|
1942
|
+
const pointerPoint = pointFromEvent(event, currentProjection);
|
|
1943
|
+
const visibleBounds = getVisibleBounds(viewport, viewportSize);
|
|
1944
|
+
const viewportCenter = {
|
|
1945
|
+
x: visibleBounds.x + visibleBounds.width / 2,
|
|
1946
|
+
y: visibleBounds.y + visibleBounds.height / 2
|
|
1947
|
+
};
|
|
1948
|
+
const grabbedLens = containsPoint(visibleBounds, pointerPoint);
|
|
1949
|
+
const session = {
|
|
1950
|
+
pointerId: event.pointerId,
|
|
1951
|
+
originClient: {
|
|
1952
|
+
x: event.clientX,
|
|
1953
|
+
y: event.clientY
|
|
1954
|
+
},
|
|
1955
|
+
grabOffset: grabbedLens ? {
|
|
1956
|
+
x: pointerPoint.x - viewportCenter.x,
|
|
1957
|
+
y: pointerPoint.y - viewportCenter.y
|
|
1958
|
+
} : {
|
|
1959
|
+
x: 0,
|
|
1960
|
+
y: 0
|
|
1961
|
+
},
|
|
1962
|
+
projection: currentProjection
|
|
1963
|
+
};
|
|
1964
|
+
sessionRef.current = session;
|
|
1965
|
+
const element = event.currentTarget;
|
|
1966
|
+
const body = element.ownerDocument.body;
|
|
1967
|
+
const finish = (pointerId) => {
|
|
1968
|
+
if (pointerId !== void 0 && pointerId !== session.pointerId) return;
|
|
1969
|
+
if (sessionRef.current?.pointerId !== session.pointerId) return;
|
|
1970
|
+
sessionRef.current = null;
|
|
1971
|
+
endSessionRef.current = null;
|
|
1972
|
+
body.removeEventListener("pointerup", onPointerEnd);
|
|
1973
|
+
body.removeEventListener("pointercancel", onPointerEnd);
|
|
1974
|
+
body.removeEventListener("contextmenu", onContextMenu, true);
|
|
1975
|
+
try {
|
|
1976
|
+
if (element.hasPointerCapture?.(session.pointerId)) element.releasePointerCapture(session.pointerId);
|
|
1977
|
+
} catch {}
|
|
1978
|
+
paintViewportRef.current(flow.getSnapshot().viewport);
|
|
1979
|
+
};
|
|
1980
|
+
const onPointerEnd = (pointerEvent) => finish(pointerEvent.pointerId);
|
|
1981
|
+
const onContextMenu = () => finish();
|
|
1982
|
+
endSessionRef.current = () => finish();
|
|
1983
|
+
body.addEventListener("pointerup", onPointerEnd);
|
|
1984
|
+
body.addEventListener("pointercancel", onPointerEnd);
|
|
1985
|
+
body.addEventListener("contextmenu", onContextMenu, true);
|
|
1853
1986
|
try {
|
|
1854
|
-
|
|
1987
|
+
element.setPointerCapture(event.pointerId);
|
|
1855
1988
|
} catch {}
|
|
1989
|
+
if (!grabbedLens) navigate(event, session);
|
|
1856
1990
|
},
|
|
1857
1991
|
onPointerMove: (event) => {
|
|
1858
|
-
|
|
1992
|
+
const session = sessionRef.current;
|
|
1993
|
+
if (!session || event.pointerId !== session.pointerId) return;
|
|
1994
|
+
const dx = event.clientX - session.originClient.x;
|
|
1995
|
+
const dy = event.clientY - session.originClient.y;
|
|
1996
|
+
if (dx * dx + dy * dy <= DRAG_JITTER_THRESHOLD_SQ) return;
|
|
1997
|
+
navigate(event, session);
|
|
1859
1998
|
},
|
|
1860
1999
|
onPointerUp: (event) => {
|
|
1861
|
-
|
|
1862
|
-
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2000
|
+
if (sessionRef.current?.pointerId === event.pointerId) endSessionRef.current?.();
|
|
1863
2001
|
},
|
|
2002
|
+
onPointerCancel: (event) => {
|
|
2003
|
+
if (sessionRef.current?.pointerId === event.pointerId) endSessionRef.current?.();
|
|
2004
|
+
},
|
|
2005
|
+
onLostPointerCapture: (event) => {
|
|
2006
|
+
if (sessionRef.current?.pointerId === event.pointerId) endSessionRef.current?.();
|
|
2007
|
+
},
|
|
2008
|
+
onContextMenu: () => endSessionRef.current?.(),
|
|
1864
2009
|
style: {
|
|
1865
2010
|
position: "absolute",
|
|
1866
2011
|
right: 12,
|
|
@@ -1873,21 +2018,27 @@ function MiniMap(props) {
|
|
|
1873
2018
|
pointerEvents: "auto",
|
|
1874
2019
|
...props.style
|
|
1875
2020
|
},
|
|
1876
|
-
children: [
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
2021
|
+
children: [/* @__PURE__ */ jsx("g", {
|
|
2022
|
+
ref: markerGroupRef,
|
|
2023
|
+
children: nodes.map((node, index) => {
|
|
2024
|
+
const rect = projectedNodes[index];
|
|
2025
|
+
return /* @__PURE__ */ jsx(Fragment, { children: props.renderNode?.(node, rect) ?? /* @__PURE__ */ jsx("rect", {
|
|
2026
|
+
"data-flow-minimap-node": node.id,
|
|
2027
|
+
x: rect.x,
|
|
2028
|
+
y: rect.y,
|
|
2029
|
+
width: Math.max(rect.width, 1),
|
|
2030
|
+
height: Math.max(rect.height, 1),
|
|
2031
|
+
rx: 1,
|
|
2032
|
+
fill: "#94a3b8"
|
|
2033
|
+
}) }, node.id);
|
|
2034
|
+
})
|
|
1886
2035
|
}), /* @__PURE__ */ jsx("rect", {
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
2036
|
+
ref: viewportRectRef,
|
|
2037
|
+
"data-flow-minimap-viewport": "",
|
|
2038
|
+
x: viewportRect.x,
|
|
2039
|
+
y: viewportRect.y,
|
|
2040
|
+
width: viewportRect.width,
|
|
2041
|
+
height: viewportRect.height,
|
|
1891
2042
|
fill: "rgba(37, 99, 235, 0.08)",
|
|
1892
2043
|
stroke: "#2563eb",
|
|
1893
2044
|
strokeWidth: 1
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/flow-react",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
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.10.0",
|
|
38
|
+
"@statelyai/flow-dom": "3.4.5"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "^18.2.0 || ^19.0.0",
|