@ankhorage/zora 25.10.9 → 25.11.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -1
  3. package/dist/features/graph-view/adapters/inbound/web-artifact/GraphView.d.ts +10 -0
  4. package/dist/features/graph-view/adapters/inbound/web-artifact/GraphView.d.ts.map +1 -1
  5. package/dist/features/graph-view/adapters/inbound/web-artifact/GraphView.js +18 -8
  6. package/dist/features/graph-view/adapters/inbound/web-artifact/GraphView.js.map +1 -1
  7. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphViewDomCallbacks.d.ts +9 -0
  8. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphViewDomCallbacks.d.ts.map +1 -0
  9. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphViewDomCallbacks.js +36 -0
  10. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphViewDomCallbacks.js.map +1 -0
  11. package/dist/features/graph-view/constants/events.d.ts +11 -0
  12. package/dist/features/graph-view/constants/events.d.ts.map +1 -0
  13. package/dist/features/graph-view/constants/events.js +10 -0
  14. package/dist/features/graph-view/constants/events.js.map +1 -0
  15. package/dist/features/graph-view/public.d.ts +2 -1
  16. package/dist/features/graph-view/public.d.ts.map +1 -1
  17. package/dist/features/graph-view/public.js +1 -0
  18. package/dist/features/graph-view/public.js.map +1 -1
  19. package/dist/types/graph-view.d.ts +4 -0
  20. package/dist/types/graph-view.d.ts.map +1 -1
  21. package/dist/types/graph-view.js.map +1 -1
  22. package/package.json +1 -1
  23. package/src/features/graph-view/adapters/inbound/web-artifact/GraphView.tsx +36 -13
  24. package/src/features/graph-view/adapters/inbound/web-artifact/createGraphViewDomCallbacks.test.ts +77 -0
  25. package/src/features/graph-view/adapters/inbound/web-artifact/createGraphViewDomCallbacks.ts +55 -0
  26. package/src/features/graph-view/constants/events.ts +12 -0
  27. package/src/features/graph-view/public.ts +2 -0
  28. package/src/types/graph-view.ts +5 -0
  29. package/web-dist/components/graph-view/GraphView.d.ts +12 -1
  30. package/web-dist/components/graph-view/GraphView.js +45 -8
  31. package/web-dist/components/graph-view/GraphView.types-0.d.ts +4 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 25.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 9f6a15f: Expose public bubbling GraphView browser lifecycle events carrying the existing controller, so integrations can measure and target actual rendered graph nodes without private renderer access.
8
+
3
9
  ## 25.10.9
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @ankhorage/zora
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v25.10.9](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![paradox: warnings](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v25.11.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![paradox: warnings](././paradox/badges/docs.svg)
7
7
 
8
8
  Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.
9
9
 
@@ -8,10 +8,20 @@ import type { GraphViewProps } from '../../../../../types/graph-view';
8
8
  * text. Keep padding in styleRules. Compound and rich-node dimensions retain their existing owners.
9
9
  * minReadableLabelSize Expand the manual zoom ceiling until plain labels reach this CSS-pixel size.
10
10
  * maxFitLabelSize Cap automatic fitting at this plain-label CSS-pixel size, leaving whitespace for tiny graphs.
11
+ * Browser lifecycle events named by public GRAPH_VIEW_EVENTS bubble from the root
12
+ * with `detail.controller`: the same public GraphViewController delivered to React.
13
+ * Browser integrations can call `measureNodes` without accessing the Cytoscape runtime.
14
+ * The standalone browser artifact exposes the same names as `GraphView.events`.
11
15
  * onSpacingFactorChange Receive the spacing accepted by explicit fit({ optimizeSpacing: true }).
12
16
  * Ordinary zoom never adjusts spacing or relayouts. Explicit optimization preserves layout ordering,
13
17
  * stops once effective fit reaches maxFitLabelSize, keeps labels and unrelated compound boundaries
14
18
  * collision-safe, and permits only a small rendered background-overlap fallback for dense graphs.
15
19
  */
16
20
  export declare function GraphView(props: GraphViewProps): React.JSX.Element;
21
+ export declare namespace GraphView {
22
+ var events: {
23
+ readonly ready: "zora-graph-view-ready";
24
+ readonly layoutComplete: "zora-graph-view-layout-complete";
25
+ };
26
+ }
17
27
  //# sourceMappingURL=GraphView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GraphView.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/GraphView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAGV,cAAc,EAEf,MAAM,iCAAiC,CAAC;AAMzC;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,qBA+B9C"}
1
+ {"version":3,"file":"GraphView.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/GraphView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAGV,cAAc,EAEf,MAAM,iCAAiC,CAAC;AAQzC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,qBAiC9C;yBAjCe,SAAS"}
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
+ import { GRAPH_VIEW_EVENTS } from '../../../constants/events';
2
3
  import { createGraphRuntime } from './createGraphRuntime';
4
+ import { createGraphViewDomCallbacks } from './createGraphViewDomCallbacks';
3
5
  import { GraphNodeOverlay } from './GraphNodeOverlay';
4
6
  /***
5
7
  * Render a browser graph while one runtime owns Cytoscape layout, viewport, and disposal.
@@ -9,14 +11,19 @@ import { GraphNodeOverlay } from './GraphNodeOverlay';
9
11
  * text. Keep padding in styleRules. Compound and rich-node dimensions retain their existing owners.
10
12
  * minReadableLabelSize Expand the manual zoom ceiling until plain labels reach this CSS-pixel size.
11
13
  * maxFitLabelSize Cap automatic fitting at this plain-label CSS-pixel size, leaving whitespace for tiny graphs.
14
+ * Browser lifecycle events named by public GRAPH_VIEW_EVENTS bubble from the root
15
+ * with `detail.controller`: the same public GraphViewController delivered to React.
16
+ * Browser integrations can call `measureNodes` without accessing the Cytoscape runtime.
17
+ * The standalone browser artifact exposes the same names as `GraphView.events`.
12
18
  * onSpacingFactorChange Receive the spacing accepted by explicit fit({ optimizeSpacing: true }).
13
19
  * Ordinary zoom never adjusts spacing or relayouts. Explicit optimization preserves layout ordering,
14
20
  * stops once effective fit reaches maxFitLabelSize, keeps labels and unrelated compound boundaries
15
21
  * collision-safe, and permits only a small rendered background-overlap fallback for dense graphs.
16
22
  */
17
23
  export function GraphView(props) {
18
- const { containerRef, renderedNodes, runtimeRef } = useGraphViewRuntime(props);
19
- return (<div aria-label={props.ariaLabel ?? 'Graph'} className={props.className} role="application" style={{
24
+ const hostRef = React.useRef(null);
25
+ const { containerRef, renderedNodes, runtimeRef } = useGraphViewRuntime(props, hostRef);
26
+ return (<div ref={hostRef} aria-label={props.ariaLabel ?? 'Graph'} className={props.className} role="application" style={{
20
27
  height: '100%',
21
28
  minHeight: 0,
22
29
  minWidth: 0,
@@ -28,11 +35,13 @@ export function GraphView(props) {
28
35
  {props.renderNode ? (<GraphNodeOverlay nodes={props.nodes} renderedNodes={renderedNodes} renderNode={props.renderNode} onNodeEvent={(id, type, selectionIntent) => runtimeRef.current?.handleOverlayNodeEvent(id, type, selectionIntent)} onNodeSize={(id, size) => runtimeRef.current?.setNodeSize(id, size)}/>) : null}
29
36
  </div>);
30
37
  }
38
+ /*** Publish the same canonical event registry on the standalone GraphView artifact. */
39
+ GraphView.events = GRAPH_VIEW_EVENTS;
31
40
  /*** Compose React synchronization around the single imperative graph runtime. */
32
- function useGraphViewRuntime(props) {
41
+ function useGraphViewRuntime(props, hostRef) {
33
42
  const containerRef = React.useRef(null);
34
43
  const runtimeRef = React.useRef(null);
35
- const callbacksRef = useGraphCallbacksRef(props);
44
+ const callbacksRef = useGraphCallbacksRef(props, hostRef);
36
45
  const [renderedNodes, setRenderedNodes] = React.useState([]);
37
46
  useRuntimeLifecycle(containerRef, runtimeRef, callbacksRef);
38
47
  useRuntimeGraphUpdate(runtimeRef, props);
@@ -42,18 +51,18 @@ function useGraphViewRuntime(props) {
42
51
  return { containerRef, renderedNodes, runtimeRef };
43
52
  }
44
53
  /*** Keep the callback ref current without mutating refs during render. */
45
- function useGraphCallbacksRef(props) {
46
- const callbacksRef = React.useRef(readCallbacks(props));
54
+ function useGraphCallbacksRef(props, hostRef) {
55
+ const callbacksRef = React.useRef(createGraphViewDomCallbacks(readCallbacks(props), hostRef));
47
56
  const { onEdgeEvent, onLayoutComplete, onNodeEvent, onReady, onViewportChange, onSpacingFactorChange, } = props;
48
57
  React.useEffect(() => {
49
- callbacksRef.current = {
58
+ callbacksRef.current = createGraphViewDomCallbacks({
50
59
  onEdgeEvent,
51
60
  onLayoutComplete,
52
61
  onNodeEvent,
53
62
  onReady,
54
63
  onViewportChange,
55
64
  onSpacingFactorChange,
56
- };
65
+ }, hostRef);
57
66
  }, [
58
67
  onEdgeEvent,
59
68
  onLayoutComplete,
@@ -61,6 +70,7 @@ function useGraphCallbacksRef(props) {
61
70
  onReady,
62
71
  onViewportChange,
63
72
  onSpacingFactorChange,
73
+ hostRef,
64
74
  ]);
65
75
  return callbacksRef;
66
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GraphView.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/GraphView.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { CytoscapeOptions } from 'cytoscape';\nimport React from 'react';\n\nimport type {\n GraphViewCallbacks,\n GraphViewNode,\n GraphViewProps,\n GraphViewRenderedNode,\n} from '../../../../../types/graph-view';\nimport { createGraphRuntime, type GraphRuntime } from './createGraphRuntime';\nimport { GraphNodeOverlay } from './GraphNodeOverlay';\n\ntype GraphContainer = NonNullable<CytoscapeOptions['container']>;\n\n/***\n * Render a browser graph while one runtime owns Cytoscape layout, viewport, and disposal.\n * zoomMode Use `fit-relative` to make controller zoom, events and min/max limits relative\n * to the visible-node fit (1 = fit). The default `absolute` keeps engine-space zoom units.\n * sizeNodesToLabels Opt in to renderer-measured plain labels, including bold and Unicode\n * text. Keep padding in styleRules. Compound and rich-node dimensions retain their existing owners.\n * minReadableLabelSize Expand the manual zoom ceiling until plain labels reach this CSS-pixel size.\n * maxFitLabelSize Cap automatic fitting at this plain-label CSS-pixel size, leaving whitespace for tiny graphs.\n * onSpacingFactorChange Receive the spacing accepted by explicit fit({ optimizeSpacing: true }).\n * Ordinary zoom never adjusts spacing or relayouts. Explicit optimization preserves layout ordering,\n * stops once effective fit reaches maxFitLabelSize, keeps labels and unrelated compound boundaries\n * collision-safe, and permits only a small rendered background-overlap fallback for dense graphs.\n */\nexport function GraphView(props: GraphViewProps) {\n const { containerRef, renderedNodes, runtimeRef } = useGraphViewRuntime(props);\n\n return (\n <div\n aria-label={props.ariaLabel ?? 'Graph'}\n className={props.className}\n role=\"application\"\n style={{\n height: '100%',\n minHeight: 0,\n minWidth: 0,\n position: 'relative',\n width: '100%',\n ...props.style,\n }}\n >\n <div ref={containerRef} style={{ height: '100%', width: '100%' }} />\n {props.renderNode ? (\n <GraphNodeOverlay\n nodes={props.nodes}\n renderedNodes={renderedNodes}\n renderNode={props.renderNode}\n onNodeEvent={(id, type, selectionIntent) =>\n runtimeRef.current?.handleOverlayNodeEvent(id, type, selectionIntent)\n }\n onNodeSize={(id, size) => runtimeRef.current?.setNodeSize(id, size)}\n />\n ) : null}\n </div>\n );\n}\n\n/*** Compose React synchronization around the single imperative graph runtime. */\nfunction useGraphViewRuntime(props: GraphViewProps) {\n const containerRef = React.useRef<GraphContainer | null>(null);\n const runtimeRef = React.useRef<GraphRuntime | null>(null);\n const callbacksRef = useGraphCallbacksRef(props);\n const [renderedNodes, setRenderedNodes] = React.useState<readonly GraphViewRenderedNode[]>([]);\n\n useRuntimeLifecycle(containerRef, runtimeRef, callbacksRef);\n useRuntimeGraphUpdate(runtimeRef, props);\n useRuntimeSelection(runtimeRef, props.selectedNodeIds);\n useRenderedNodeSubscription(runtimeRef, props.renderNode, setRenderedNodes);\n useKnownNodeSizes(runtimeRef, props.nodes, props.getNodeSize);\n\n return { containerRef, renderedNodes, runtimeRef };\n}\n\n/*** Keep the callback ref current without mutating refs during render. */\nfunction useGraphCallbacksRef(props: GraphViewProps) {\n const callbacksRef = React.useRef<GraphViewCallbacks>(readCallbacks(props));\n const {\n onEdgeEvent,\n onLayoutComplete,\n onNodeEvent,\n onReady,\n onViewportChange,\n onSpacingFactorChange,\n } = props;\n\n React.useEffect(() => {\n callbacksRef.current = {\n onEdgeEvent,\n onLayoutComplete,\n onNodeEvent,\n onReady,\n onViewportChange,\n onSpacingFactorChange,\n };\n }, [\n onEdgeEvent,\n onLayoutComplete,\n onNodeEvent,\n onReady,\n onViewportChange,\n onSpacingFactorChange,\n ]);\n\n return callbacksRef;\n}\n\n/*** Create and dispose the runtime only with the underlying browser container lifetime. */\nfunction useRuntimeLifecycle(\n containerRef: { current: GraphContainer | null },\n runtimeRef: { current: GraphRuntime | null },\n callbacksRef: { current: GraphViewCallbacks },\n) {\n React.useEffect(() => {\n const container = containerRef.current;\n if (container === null) return;\n const runtime = createGraphRuntime(container, callbacksRef);\n runtimeRef.current = runtime;\n return () => {\n runtime.destroy();\n runtimeRef.current = null;\n };\n }, [callbacksRef, containerRef, runtimeRef]);\n}\n\n/*** Synchronize graph data and layout policy without exposing Cytoscape to React. */\nfunction useRuntimeGraphUpdate(\n runtimeRef: { current: GraphRuntime | null },\n props: GraphViewProps,\n) {\n const {\n edges,\n fitPadding,\n layout,\n layoutOptions,\n maxZoom,\n minReadableLabelSize,\n maxFitLabelSize,\n minZoom,\n zoomMode,\n sizeNodesToLabels,\n nodes,\n renderNode,\n spacingFactor,\n styleRules,\n } = props;\n\n React.useEffect(() => {\n runtimeRef.current?.update({\n edges,\n fitPadding,\n layout,\n layoutOptions,\n maxZoom,\n minReadableLabelSize,\n maxFitLabelSize,\n minZoom,\n zoomMode,\n sizeNodesToLabels,\n nodes,\n richNodeRendering: renderNode !== undefined,\n spacingFactor,\n styleRules,\n });\n }, [\n edges,\n fitPadding,\n layout,\n layoutOptions,\n maxZoom,\n minReadableLabelSize,\n maxFitLabelSize,\n minZoom,\n zoomMode,\n sizeNodesToLabels,\n nodes,\n renderNode,\n runtimeRef,\n spacingFactor,\n styleRules,\n ]);\n}\n\n/*** Synchronize optional controlled selection independently from graph layout. */\nfunction useRuntimeSelection(\n runtimeRef: { current: GraphRuntime | null },\n selectedNodeIds: readonly string[] | undefined,\n) {\n React.useEffect(() => {\n runtimeRef.current?.setSelectedNodeIds(selectedNodeIds);\n }, [runtimeRef, selectedNodeIds]);\n}\n\n/*** Subscribe the optional rich-node overlay to rendered runtime positions. */\nfunction useRenderedNodeSubscription(\n runtimeRef: { current: GraphRuntime | null },\n renderNode: GraphViewProps['renderNode'],\n setRenderedNodes: (nodes: readonly GraphViewRenderedNode[]) => void,\n) {\n React.useEffect(() => {\n const runtime = runtimeRef.current;\n if (runtime === null || renderNode === undefined) {\n setRenderedNodes([]);\n return;\n }\n return runtime.subscribeRenderedNodes(setRenderedNodes);\n }, [renderNode, runtimeRef, setRenderedNodes]);\n}\n\n/*** Seed known rich-node dimensions without requiring DOM measurement. */\nfunction useKnownNodeSizes(\n runtimeRef: { current: GraphRuntime | null },\n nodes: readonly GraphViewNode[],\n getNodeSize: GraphViewProps['getNodeSize'],\n) {\n React.useEffect(() => {\n const runtime = runtimeRef.current;\n if (runtime === null || getNodeSize === undefined) return;\n for (const node of nodes) {\n const size = getNodeSize(node);\n if (size) runtime.setNodeSize(node.id, size);\n }\n }, [getNodeSize, nodes, runtimeRef]);\n}\n\n/*** Read callback props for the initial runtime callback-ref value. */\nfunction readCallbacks(props: GraphViewProps): GraphViewCallbacks {\n return {\n onEdgeEvent: props.onEdgeEvent,\n onLayoutComplete: props.onLayoutComplete,\n onNodeEvent: props.onNodeEvent,\n onReady: props.onReady,\n onViewportChange: props.onViewportChange,\n onSpacingFactorChange: props.onSpacingFactorChange,\n };\n}\n"]}
1
+ {"version":3,"file":"GraphView.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/GraphView.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { CytoscapeOptions } from 'cytoscape';\nimport React from 'react';\n\nimport type {\n GraphViewCallbacks,\n GraphViewNode,\n GraphViewProps,\n GraphViewRenderedNode,\n} from '../../../../../types/graph-view';\nimport { GRAPH_VIEW_EVENTS } from '../../../constants/events';\nimport { createGraphRuntime, type GraphRuntime } from './createGraphRuntime';\nimport { createGraphViewDomCallbacks } from './createGraphViewDomCallbacks';\nimport { GraphNodeOverlay } from './GraphNodeOverlay';\n\ntype GraphContainer = NonNullable<CytoscapeOptions['container']>;\n\n/***\n * Render a browser graph while one runtime owns Cytoscape layout, viewport, and disposal.\n * zoomMode Use `fit-relative` to make controller zoom, events and min/max limits relative\n * to the visible-node fit (1 = fit). The default `absolute` keeps engine-space zoom units.\n * sizeNodesToLabels Opt in to renderer-measured plain labels, including bold and Unicode\n * text. Keep padding in styleRules. Compound and rich-node dimensions retain their existing owners.\n * minReadableLabelSize Expand the manual zoom ceiling until plain labels reach this CSS-pixel size.\n * maxFitLabelSize Cap automatic fitting at this plain-label CSS-pixel size, leaving whitespace for tiny graphs.\n * Browser lifecycle events named by public GRAPH_VIEW_EVENTS bubble from the root\n * with `detail.controller`: the same public GraphViewController delivered to React.\n * Browser integrations can call `measureNodes` without accessing the Cytoscape runtime.\n * The standalone browser artifact exposes the same names as `GraphView.events`.\n * onSpacingFactorChange Receive the spacing accepted by explicit fit({ optimizeSpacing: true }).\n * Ordinary zoom never adjusts spacing or relayouts. Explicit optimization preserves layout ordering,\n * stops once effective fit reaches maxFitLabelSize, keeps labels and unrelated compound boundaries\n * collision-safe, and permits only a small rendered background-overlap fallback for dense graphs.\n */\nexport function GraphView(props: GraphViewProps) {\n const hostRef = React.useRef<HTMLDivElement | null>(null);\n const { containerRef, renderedNodes, runtimeRef } = useGraphViewRuntime(props, hostRef);\n\n return (\n <div\n ref={hostRef}\n aria-label={props.ariaLabel ?? 'Graph'}\n className={props.className}\n role=\"application\"\n style={{\n height: '100%',\n minHeight: 0,\n minWidth: 0,\n position: 'relative',\n width: '100%',\n ...props.style,\n }}\n >\n <div ref={containerRef} style={{ height: '100%', width: '100%' }} />\n {props.renderNode ? (\n <GraphNodeOverlay\n nodes={props.nodes}\n renderedNodes={renderedNodes}\n renderNode={props.renderNode}\n onNodeEvent={(id, type, selectionIntent) =>\n runtimeRef.current?.handleOverlayNodeEvent(id, type, selectionIntent)\n }\n onNodeSize={(id, size) => runtimeRef.current?.setNodeSize(id, size)}\n />\n ) : null}\n </div>\n );\n}\n\n/*** Publish the same canonical event registry on the standalone GraphView artifact. */\nGraphView.events = GRAPH_VIEW_EVENTS;\n\n/*** Compose React synchronization around the single imperative graph runtime. */\nfunction useGraphViewRuntime(\n props: GraphViewProps,\n hostRef: React.RefObject<HTMLDivElement | null>,\n) {\n const containerRef = React.useRef<GraphContainer | null>(null);\n const runtimeRef = React.useRef<GraphRuntime | null>(null);\n const callbacksRef = useGraphCallbacksRef(props, hostRef);\n const [renderedNodes, setRenderedNodes] = React.useState<readonly GraphViewRenderedNode[]>([]);\n\n useRuntimeLifecycle(containerRef, runtimeRef, callbacksRef);\n useRuntimeGraphUpdate(runtimeRef, props);\n useRuntimeSelection(runtimeRef, props.selectedNodeIds);\n useRenderedNodeSubscription(runtimeRef, props.renderNode, setRenderedNodes);\n useKnownNodeSizes(runtimeRef, props.nodes, props.getNodeSize);\n\n return { containerRef, renderedNodes, runtimeRef };\n}\n\n/*** Keep the callback ref current without mutating refs during render. */\nfunction useGraphCallbacksRef(\n props: GraphViewProps,\n hostRef: React.RefObject<HTMLDivElement | null>,\n) {\n const callbacksRef = React.useRef<GraphViewCallbacks>(\n createGraphViewDomCallbacks(readCallbacks(props), hostRef),\n );\n const {\n onEdgeEvent,\n onLayoutComplete,\n onNodeEvent,\n onReady,\n onViewportChange,\n onSpacingFactorChange,\n } = props;\n\n React.useEffect(() => {\n callbacksRef.current = createGraphViewDomCallbacks(\n {\n onEdgeEvent,\n onLayoutComplete,\n onNodeEvent,\n onReady,\n onViewportChange,\n onSpacingFactorChange,\n },\n hostRef,\n );\n }, [\n onEdgeEvent,\n onLayoutComplete,\n onNodeEvent,\n onReady,\n onViewportChange,\n onSpacingFactorChange,\n hostRef,\n ]);\n\n return callbacksRef;\n}\n\n/*** Create and dispose the runtime only with the underlying browser container lifetime. */\nfunction useRuntimeLifecycle(\n containerRef: { current: GraphContainer | null },\n runtimeRef: { current: GraphRuntime | null },\n callbacksRef: { current: GraphViewCallbacks },\n) {\n React.useEffect(() => {\n const container = containerRef.current;\n if (container === null) return;\n const runtime = createGraphRuntime(container, callbacksRef);\n runtimeRef.current = runtime;\n return () => {\n runtime.destroy();\n runtimeRef.current = null;\n };\n }, [callbacksRef, containerRef, runtimeRef]);\n}\n\n/*** Synchronize graph data and layout policy without exposing Cytoscape to React. */\nfunction useRuntimeGraphUpdate(\n runtimeRef: { current: GraphRuntime | null },\n props: GraphViewProps,\n) {\n const {\n edges,\n fitPadding,\n layout,\n layoutOptions,\n maxZoom,\n minReadableLabelSize,\n maxFitLabelSize,\n minZoom,\n zoomMode,\n sizeNodesToLabels,\n nodes,\n renderNode,\n spacingFactor,\n styleRules,\n } = props;\n\n React.useEffect(() => {\n runtimeRef.current?.update({\n edges,\n fitPadding,\n layout,\n layoutOptions,\n maxZoom,\n minReadableLabelSize,\n maxFitLabelSize,\n minZoom,\n zoomMode,\n sizeNodesToLabels,\n nodes,\n richNodeRendering: renderNode !== undefined,\n spacingFactor,\n styleRules,\n });\n }, [\n edges,\n fitPadding,\n layout,\n layoutOptions,\n maxZoom,\n minReadableLabelSize,\n maxFitLabelSize,\n minZoom,\n zoomMode,\n sizeNodesToLabels,\n nodes,\n renderNode,\n runtimeRef,\n spacingFactor,\n styleRules,\n ]);\n}\n\n/*** Synchronize optional controlled selection independently from graph layout. */\nfunction useRuntimeSelection(\n runtimeRef: { current: GraphRuntime | null },\n selectedNodeIds: readonly string[] | undefined,\n) {\n React.useEffect(() => {\n runtimeRef.current?.setSelectedNodeIds(selectedNodeIds);\n }, [runtimeRef, selectedNodeIds]);\n}\n\n/*** Subscribe the optional rich-node overlay to rendered runtime positions. */\nfunction useRenderedNodeSubscription(\n runtimeRef: { current: GraphRuntime | null },\n renderNode: GraphViewProps['renderNode'],\n setRenderedNodes: (nodes: readonly GraphViewRenderedNode[]) => void,\n) {\n React.useEffect(() => {\n const runtime = runtimeRef.current;\n if (runtime === null || renderNode === undefined) {\n setRenderedNodes([]);\n return;\n }\n return runtime.subscribeRenderedNodes(setRenderedNodes);\n }, [renderNode, runtimeRef, setRenderedNodes]);\n}\n\n/*** Seed known rich-node dimensions without requiring DOM measurement. */\nfunction useKnownNodeSizes(\n runtimeRef: { current: GraphRuntime | null },\n nodes: readonly GraphViewNode[],\n getNodeSize: GraphViewProps['getNodeSize'],\n) {\n React.useEffect(() => {\n const runtime = runtimeRef.current;\n if (runtime === null || getNodeSize === undefined) return;\n for (const node of nodes) {\n const size = getNodeSize(node);\n if (size) runtime.setNodeSize(node.id, size);\n }\n }, [getNodeSize, nodes, runtimeRef]);\n}\n\n/*** Read callback props for the initial runtime callback-ref value. */\nfunction readCallbacks(props: GraphViewProps): GraphViewCallbacks {\n return {\n onEdgeEvent: props.onEdgeEvent,\n onLayoutComplete: props.onLayoutComplete,\n onNodeEvent: props.onNodeEvent,\n onReady: props.onReady,\n onViewportChange: props.onViewportChange,\n onSpacingFactorChange: props.onSpacingFactorChange,\n };\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { GraphViewCallbacks } from '../../../../../types/graph-view';
2
+ /***
3
+ * Mirror public GraphView controller callbacks as bubbling browser lifecycle events.
4
+ * Browser hosts can measure actual rendered nodes without reaching into the graph engine.
5
+ */
6
+ export declare function createGraphViewDomCallbacks(callbacks: GraphViewCallbacks, hostRef: {
7
+ readonly current: unknown;
8
+ }): GraphViewCallbacks;
9
+ //# sourceMappingURL=createGraphViewDomCallbacks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createGraphViewDomCallbacks.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/createGraphViewDomCallbacks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAGnB,MAAM,iCAAiC,CAAC;AAGzC;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,kBAAkB,EAC7B,OAAO,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACrC,kBAAkB,CAYpB"}
@@ -0,0 +1,36 @@
1
+ import { GRAPH_VIEW_EVENTS } from '../../../constants/events';
2
+ /***
3
+ * Mirror public GraphView controller callbacks as bubbling browser lifecycle events.
4
+ * Browser hosts can measure actual rendered nodes without reaching into the graph engine.
5
+ */
6
+ export function createGraphViewDomCallbacks(callbacks, hostRef) {
7
+ return {
8
+ ...callbacks,
9
+ onReady(controller) {
10
+ callbacks.onReady?.(controller);
11
+ dispatchGraphViewControllerEvent(hostRef, GRAPH_VIEW_EVENTS.ready, controller);
12
+ },
13
+ onLayoutComplete(controller) {
14
+ callbacks.onLayoutComplete?.(controller);
15
+ dispatchGraphViewControllerEvent(hostRef, GRAPH_VIEW_EVENTS.layoutComplete, controller);
16
+ },
17
+ };
18
+ }
19
+ /*** Deliver the existing public controller reference on the GraphView root event channel. */
20
+ function dispatchGraphViewControllerEvent(hostRef, name, controller) {
21
+ const host = hostRef.current;
22
+ if (!canDispatchBrowserEvent(host))
23
+ return;
24
+ host.dispatchEvent(new CustomEvent(name, {
25
+ bubbles: true,
26
+ detail: { controller },
27
+ }));
28
+ }
29
+ /*** Narrow an untrusted browser host to the standard event-dispatch method. */
30
+ function canDispatchBrowserEvent(value) {
31
+ return (typeof value === 'object' &&
32
+ value !== null &&
33
+ 'dispatchEvent' in value &&
34
+ typeof value.dispatchEvent === 'function');
35
+ }
36
+ //# sourceMappingURL=createGraphViewDomCallbacks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createGraphViewDomCallbacks.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/createGraphViewDomCallbacks.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAwC,MAAM,2BAA2B,CAAC;AAEpG;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CACzC,SAA6B,EAC7B,OAAsC;IAEtC,OAAO;QACL,GAAG,SAAS;QACZ,OAAO,CAAC,UAAU;YAChB,SAAS,CAAC,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC;YAChC,gCAAgC,CAAC,OAAO,EAAE,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QACjF,CAAC;QACD,gBAAgB,CAAC,UAAU;YACzB,SAAS,CAAC,gBAAgB,EAAE,CAAC,UAAU,CAAC,CAAC;YACzC,gCAAgC,CAAC,OAAO,EAAE,iBAAiB,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAC1F,CAAC;KACF,CAAC;AACJ,CAAC;AAED,6FAA6F;AAC7F,SAAS,gCAAgC,CACvC,OAAsC,EACtC,IAAqC,EACrC,UAA+B;IAE/B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAC7B,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC;QAAE,OAAO;IAC3C,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAoC,IAAI,EAAE;QACvD,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,EAAE,UAAU,EAAE;KACvB,CAAC,CACH,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,SAAS,uBAAuB,CAC9B,KAAc;IAEd,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,eAAe,IAAI,KAAK;QACxB,OAAO,KAAK,CAAC,aAAa,KAAK,UAAU,CAC1C,CAAC;AACJ,CAAC","sourcesContent":["import type {\n GraphViewCallbacks,\n GraphViewController,\n GraphViewDomControllerEventDetail,\n} from '../../../../../types/graph-view';\nimport { GRAPH_VIEW_EVENTS, type GraphViewDomControllerEventName } from '../../../constants/events';\n\n/***\n * Mirror public GraphView controller callbacks as bubbling browser lifecycle events.\n * Browser hosts can measure actual rendered nodes without reaching into the graph engine.\n */\nexport function createGraphViewDomCallbacks(\n callbacks: GraphViewCallbacks,\n hostRef: { readonly current: unknown },\n): GraphViewCallbacks {\n return {\n ...callbacks,\n onReady(controller) {\n callbacks.onReady?.(controller);\n dispatchGraphViewControllerEvent(hostRef, GRAPH_VIEW_EVENTS.ready, controller);\n },\n onLayoutComplete(controller) {\n callbacks.onLayoutComplete?.(controller);\n dispatchGraphViewControllerEvent(hostRef, GRAPH_VIEW_EVENTS.layoutComplete, controller);\n },\n };\n}\n\n/*** Deliver the existing public controller reference on the GraphView root event channel. */\nfunction dispatchGraphViewControllerEvent(\n hostRef: { readonly current: unknown },\n name: GraphViewDomControllerEventName,\n controller: GraphViewController,\n): void {\n const host = hostRef.current;\n if (!canDispatchBrowserEvent(host)) return;\n host.dispatchEvent(\n new CustomEvent<GraphViewDomControllerEventDetail>(name, {\n bubbles: true,\n detail: { controller },\n }),\n );\n}\n\n/*** Narrow an untrusted browser host to the standard event-dispatch method. */\nfunction canDispatchBrowserEvent(\n value: unknown,\n): value is { dispatchEvent(event: Event): boolean } {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'dispatchEvent' in value &&\n typeof value.dispatchEvent === 'function'\n );\n}\n"]}
@@ -0,0 +1,11 @@
1
+ /***
2
+ * Canonical public GraphView browser lifecycle event names.
3
+ * Candidate for a shared Ankhorage event strategy: define common naming, typed
4
+ * payloads and dispatch conventions before extracting a cross-component contract.
5
+ */
6
+ export declare const GRAPH_VIEW_EVENTS: {
7
+ readonly ready: "zora-graph-view-ready";
8
+ readonly layoutComplete: "zora-graph-view-layout-complete";
9
+ };
10
+ export type GraphViewDomControllerEventName = (typeof GRAPH_VIEW_EVENTS)[keyof typeof GRAPH_VIEW_EVENTS];
11
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../../src/features/graph-view/constants/events.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;CAGpB,CAAC;AAEX,MAAM,MAAM,+BAA+B,GACzC,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /***
2
+ * Canonical public GraphView browser lifecycle event names.
3
+ * Candidate for a shared Ankhorage event strategy: define common naming, typed
4
+ * payloads and dispatch conventions before extracting a cross-component contract.
5
+ */
6
+ export const GRAPH_VIEW_EVENTS = {
7
+ ready: 'zora-graph-view-ready',
8
+ layoutComplete: 'zora-graph-view-layout-complete',
9
+ };
10
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../../src/features/graph-view/constants/events.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,KAAK,EAAE,uBAAuB;IAC9B,cAAc,EAAE,iCAAiC;CACzC,CAAC","sourcesContent":["/***\n * Canonical public GraphView browser lifecycle event names.\n * Candidate for a shared Ankhorage event strategy: define common naming, typed\n * payloads and dispatch conventions before extracting a cross-component contract.\n */\nexport const GRAPH_VIEW_EVENTS = {\n ready: 'zora-graph-view-ready',\n layoutComplete: 'zora-graph-view-layout-complete',\n} as const;\n\nexport type GraphViewDomControllerEventName =\n (typeof GRAPH_VIEW_EVENTS)[keyof typeof GRAPH_VIEW_EVENTS];\n"]}
@@ -1,3 +1,4 @@
1
- export type { GraphViewCallbacks, GraphViewController, GraphViewEdge, GraphViewElementEvent, GraphViewElementEventType, GraphViewFitOptions, GraphViewLayoutName, GraphViewMeasurement, GraphViewNode, GraphViewNodeMeasurement, GraphViewNodeRenderContext, GraphViewPoint, GraphViewProps, GraphViewRenderedNode, GraphViewSize, GraphViewStyleRule, GraphViewViewport, } from '../../types/graph-view';
1
+ export type { GraphViewCallbacks, GraphViewController, GraphViewDomControllerEventDetail, GraphViewEdge, GraphViewElementEvent, GraphViewElementEventType, GraphViewFitOptions, GraphViewLayoutName, GraphViewMeasurement, GraphViewNode, GraphViewNodeMeasurement, GraphViewNodeRenderContext, GraphViewPoint, GraphViewProps, GraphViewRenderedNode, GraphViewSize, GraphViewStyleRule, GraphViewViewport, } from '../../types/graph-view';
2
2
  export { GraphView } from './adapters/inbound/web-artifact/GraphView';
3
+ export { GRAPH_VIEW_EVENTS } from './constants/events';
3
4
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/features/graph-view/public.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,wBAAwB,EACxB,0BAA0B,EAC1B,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,2CAA2C,CAAC"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/features/graph-view/public.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,kBAAkB,EAClB,mBAAmB,EACnB,iCAAiC,EACjC,aAAa,EACb,qBAAqB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,wBAAwB,EACxB,0BAA0B,EAC1B,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,2CAA2C,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export { GraphView } from './adapters/inbound/web-artifact/GraphView';
2
+ export { GRAPH_VIEW_EVENTS } from './constants/events';
2
3
  //# sourceMappingURL=public.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.js","sourceRoot":"","sources":["../../../src/features/graph-view/public.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,SAAS,EAAE,MAAM,2CAA2C,CAAC","sourcesContent":["export type {\n GraphViewCallbacks,\n GraphViewController,\n GraphViewEdge,\n GraphViewElementEvent,\n GraphViewElementEventType,\n GraphViewFitOptions,\n GraphViewLayoutName,\n GraphViewMeasurement,\n GraphViewNode,\n GraphViewNodeMeasurement,\n GraphViewNodeRenderContext,\n GraphViewPoint,\n GraphViewProps,\n GraphViewRenderedNode,\n GraphViewSize,\n GraphViewStyleRule,\n GraphViewViewport,\n} from '../../types/graph-view';\nexport { GraphView } from './adapters/inbound/web-artifact/GraphView';\n"]}
1
+ {"version":3,"file":"public.js","sourceRoot":"","sources":["../../../src/features/graph-view/public.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,SAAS,EAAE,MAAM,2CAA2C,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export type {\n GraphViewCallbacks,\n GraphViewController,\n GraphViewDomControllerEventDetail,\n GraphViewEdge,\n GraphViewElementEvent,\n GraphViewElementEventType,\n GraphViewFitOptions,\n GraphViewLayoutName,\n GraphViewMeasurement,\n GraphViewNode,\n GraphViewNodeMeasurement,\n GraphViewNodeRenderContext,\n GraphViewPoint,\n GraphViewProps,\n GraphViewRenderedNode,\n GraphViewSize,\n GraphViewStyleRule,\n GraphViewViewport,\n} from '../../types/graph-view';\nexport { GraphView } from './adapters/inbound/web-artifact/GraphView';\nexport { GRAPH_VIEW_EVENTS } from './constants/events';\n"]}
@@ -81,6 +81,10 @@ export interface GraphViewController {
81
81
  setZoom(zoom: number): void;
82
82
  zoomBy(factor: number): void;
83
83
  }
84
+ /*** The browser lifecycle events carry only the existing public graph controller. */
85
+ export interface GraphViewDomControllerEventDetail {
86
+ readonly controller: GraphViewController;
87
+ }
84
88
  export interface GraphViewProps {
85
89
  readonly nodes: readonly GraphViewNode[];
86
90
  readonly edges: readonly GraphViewEdge[];
@@ -1 +1 @@
1
- {"version":3,"file":"graph-view.d.ts","sourceRoot":"","sources":["../../src/types/graph-view.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graph-view.d.ts","sourceRoot":"","sources":["../../src/types/graph-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,QAAQ,GAAG,YAAY,GAAG,KAAK,GAAG,MAAM,CAAC;AAC5F,MAAM,MAAM,yBAAyB,GACnC,OAAO,GAAG,cAAc,GAAG,QAAQ,GAAG,UAAU,GAAG,eAAe,GAAG,eAAe,CAAC;AAEvF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,wBAAwB,EAAE,CAAC;IACpD,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;IACzC,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;CAC5C;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACzC,gFAAgF;IAChF,YAAY,CAAC,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,oBAAoB,GAAG,IAAI,CAAC;IACvE,WAAW,IAAI,iBAAiB,CAAC;IACjC,YAAY,IAAI;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,MAAM,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAAC;IAClC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,qFAAqF;AACrF,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAC;CAC1C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACpD,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,KAAK,CAAC,SAAS,CAAC;IAC/E,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,aAAa,GAAG,SAAS,CAAC;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;IAChD,2FAA2F;IAC3F,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC7D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACtE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC9D,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC9D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;CAClE;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,cAAc,EACZ,aAAa,GACb,kBAAkB,GAClB,aAAa,GACb,SAAS,GACT,kBAAkB,GAClB,uBAAuB,CAC1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"graph-view.js","sourceRoot":"","sources":["../../src/types/graph-view.ts"],"names":[],"mappings":"","sourcesContent":["import type { SelectionIntent } from '@ankhorage/utility/selection';\nimport type React from 'react';\n\nexport type GraphViewLayoutName = 'breadthfirst' | 'circle' | 'concentric' | 'elk' | 'grid';\nexport type GraphViewElementEventType =\n 'press' | 'double-press' | 'select' | 'unselect' | 'pointer-enter' | 'pointer-leave';\n\nexport interface GraphViewNode {\n readonly id: string;\n readonly label?: string;\n readonly parentId?: string;\n readonly classes?: string;\n readonly data?: Readonly<Record<string, unknown>>;\n}\n\nexport interface GraphViewEdge {\n readonly id?: string;\n readonly source: string;\n readonly target: string;\n readonly classes?: string;\n readonly data?: Readonly<Record<string, unknown>>;\n}\n\nexport interface GraphViewStyleRule {\n readonly selector: string;\n readonly style: Readonly<Record<string, unknown>>;\n}\n\nexport interface GraphViewPoint {\n readonly x: number;\n readonly y: number;\n}\n\nexport interface GraphViewSize {\n readonly width: number;\n readonly height: number;\n}\n\nexport interface GraphViewNodeRenderContext {\n readonly hovered: boolean;\n readonly node: GraphViewNode;\n readonly selected: boolean;\n readonly zoom: number;\n}\n\nexport interface GraphViewRenderedNode {\n readonly hovered: boolean;\n readonly id: string;\n readonly position: GraphViewPoint;\n readonly selected: boolean;\n readonly zoom: number;\n}\n\nexport interface GraphViewNodeMeasurement {\n readonly id: string;\n readonly position: GraphViewPoint;\n readonly size: GraphViewSize;\n}\n\nexport interface GraphViewMeasurement {\n readonly nodes: readonly GraphViewNodeMeasurement[];\n readonly viewport: GraphViewSize;\n}\n\nexport interface GraphViewViewport {\n readonly zoom: number;\n readonly pan: GraphViewPoint;\n}\n\nexport interface GraphViewFitOptions {\n /**\n * Optimize effective rendered fit without rerunning the layout algorithm; whole-graph fits only.\n * Stops at the configured fit-size target and keeps semantic labels collision-safe.\n */\n readonly optimizeSpacing?: boolean;\n readonly nodeIds?: readonly string[];\n readonly padding?: number;\n}\n\nexport interface GraphViewElementEvent {\n readonly id: string;\n readonly type: GraphViewElementEventType;\n readonly selectionIntent?: SelectionIntent;\n}\n\nexport interface GraphViewController {\n fit(options?: GraphViewFitOptions): void;\n /** Measure rendered node geometry without exposing the backing graph engine. */\n measureNodes?(nodeIds: readonly string[]): GraphViewMeasurement | null;\n getViewport(): GraphViewViewport;\n getZoomRange(): { readonly min: number; readonly max: number };\n setPan(pan: GraphViewPoint): void;\n setZoom(zoom: number): void;\n zoomBy(factor: number): void;\n}\n\nexport interface GraphViewProps {\n readonly nodes: readonly GraphViewNode[];\n readonly edges: readonly GraphViewEdge[];\n readonly layout?: GraphViewLayoutName;\n readonly layoutOptions?: Readonly<Record<string, unknown>>;\n readonly spacingFactor?: number;\n readonly styleRules?: readonly GraphViewStyleRule[];\n readonly selectedNodeIds?: readonly string[];\n readonly renderNode?: (context: GraphViewNodeRenderContext) => React.ReactNode;\n readonly getNodeSize?: (node: GraphViewNode) => GraphViewSize | undefined;\n readonly fitPadding?: number;\n readonly minZoom?: number;\n readonly maxZoom?: number;\n readonly minReadableLabelSize?: number;\n readonly maxFitLabelSize?: number;\n /** Zoom values and bounds use the full-node fit as 1 when fit-relative is selected. */\n readonly zoomMode?: 'absolute' | 'fit-relative';\n /** Measure plain labels before layout instead of estimating width from character count. */\n readonly sizeNodesToLabels?: boolean;\n readonly className?: string;\n readonly style?: React.CSSProperties;\n readonly ariaLabel?: string;\n readonly onReady?: (controller: GraphViewController) => void;\n readonly onLayoutComplete?: (controller: GraphViewController) => void;\n readonly onNodeEvent?: (event: GraphViewElementEvent) => void;\n readonly onEdgeEvent?: (event: GraphViewElementEvent) => void;\n readonly onViewportChange?: (viewport: GraphViewViewport) => void;\n readonly onSpacingFactorChange?: (spacingFactor: number) => void;\n}\n\nexport type GraphViewCallbacks = Pick<\n GraphViewProps,\n | 'onEdgeEvent'\n | 'onLayoutComplete'\n | 'onNodeEvent'\n | 'onReady'\n | 'onViewportChange'\n | 'onSpacingFactorChange'\n>;\n"]}
1
+ {"version":3,"file":"graph-view.js","sourceRoot":"","sources":["../../src/types/graph-view.ts"],"names":[],"mappings":"","sourcesContent":["import type { SelectionIntent } from '@ankhorage/utility/selection';\nimport type React from 'react';\n\nexport type GraphViewLayoutName = 'breadthfirst' | 'circle' | 'concentric' | 'elk' | 'grid';\nexport type GraphViewElementEventType =\n 'press' | 'double-press' | 'select' | 'unselect' | 'pointer-enter' | 'pointer-leave';\n\nexport interface GraphViewNode {\n readonly id: string;\n readonly label?: string;\n readonly parentId?: string;\n readonly classes?: string;\n readonly data?: Readonly<Record<string, unknown>>;\n}\n\nexport interface GraphViewEdge {\n readonly id?: string;\n readonly source: string;\n readonly target: string;\n readonly classes?: string;\n readonly data?: Readonly<Record<string, unknown>>;\n}\n\nexport interface GraphViewStyleRule {\n readonly selector: string;\n readonly style: Readonly<Record<string, unknown>>;\n}\n\nexport interface GraphViewPoint {\n readonly x: number;\n readonly y: number;\n}\n\nexport interface GraphViewSize {\n readonly width: number;\n readonly height: number;\n}\n\nexport interface GraphViewNodeRenderContext {\n readonly hovered: boolean;\n readonly node: GraphViewNode;\n readonly selected: boolean;\n readonly zoom: number;\n}\n\nexport interface GraphViewRenderedNode {\n readonly hovered: boolean;\n readonly id: string;\n readonly position: GraphViewPoint;\n readonly selected: boolean;\n readonly zoom: number;\n}\n\nexport interface GraphViewNodeMeasurement {\n readonly id: string;\n readonly position: GraphViewPoint;\n readonly size: GraphViewSize;\n}\n\nexport interface GraphViewMeasurement {\n readonly nodes: readonly GraphViewNodeMeasurement[];\n readonly viewport: GraphViewSize;\n}\n\nexport interface GraphViewViewport {\n readonly zoom: number;\n readonly pan: GraphViewPoint;\n}\n\nexport interface GraphViewFitOptions {\n /**\n * Optimize effective rendered fit without rerunning the layout algorithm; whole-graph fits only.\n * Stops at the configured fit-size target and keeps semantic labels collision-safe.\n */\n readonly optimizeSpacing?: boolean;\n readonly nodeIds?: readonly string[];\n readonly padding?: number;\n}\n\nexport interface GraphViewElementEvent {\n readonly id: string;\n readonly type: GraphViewElementEventType;\n readonly selectionIntent?: SelectionIntent;\n}\n\nexport interface GraphViewController {\n fit(options?: GraphViewFitOptions): void;\n /** Measure rendered node geometry without exposing the backing graph engine. */\n measureNodes?(nodeIds: readonly string[]): GraphViewMeasurement | null;\n getViewport(): GraphViewViewport;\n getZoomRange(): { readonly min: number; readonly max: number };\n setPan(pan: GraphViewPoint): void;\n setZoom(zoom: number): void;\n zoomBy(factor: number): void;\n}\n\n/*** The browser lifecycle events carry only the existing public graph controller. */\nexport interface GraphViewDomControllerEventDetail {\n readonly controller: GraphViewController;\n}\n\nexport interface GraphViewProps {\n readonly nodes: readonly GraphViewNode[];\n readonly edges: readonly GraphViewEdge[];\n readonly layout?: GraphViewLayoutName;\n readonly layoutOptions?: Readonly<Record<string, unknown>>;\n readonly spacingFactor?: number;\n readonly styleRules?: readonly GraphViewStyleRule[];\n readonly selectedNodeIds?: readonly string[];\n readonly renderNode?: (context: GraphViewNodeRenderContext) => React.ReactNode;\n readonly getNodeSize?: (node: GraphViewNode) => GraphViewSize | undefined;\n readonly fitPadding?: number;\n readonly minZoom?: number;\n readonly maxZoom?: number;\n readonly minReadableLabelSize?: number;\n readonly maxFitLabelSize?: number;\n /** Zoom values and bounds use the full-node fit as 1 when fit-relative is selected. */\n readonly zoomMode?: 'absolute' | 'fit-relative';\n /** Measure plain labels before layout instead of estimating width from character count. */\n readonly sizeNodesToLabels?: boolean;\n readonly className?: string;\n readonly style?: React.CSSProperties;\n readonly ariaLabel?: string;\n readonly onReady?: (controller: GraphViewController) => void;\n readonly onLayoutComplete?: (controller: GraphViewController) => void;\n readonly onNodeEvent?: (event: GraphViewElementEvent) => void;\n readonly onEdgeEvent?: (event: GraphViewElementEvent) => void;\n readonly onViewportChange?: (viewport: GraphViewViewport) => void;\n readonly onSpacingFactorChange?: (spacingFactor: number) => void;\n}\n\nexport type GraphViewCallbacks = Pick<\n GraphViewProps,\n | 'onEdgeEvent'\n | 'onLayoutComplete'\n | 'onNodeEvent'\n | 'onReady'\n | 'onViewportChange'\n | 'onSpacingFactorChange'\n>;\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/zora",
3
3
  "type": "module",
4
- "version": "25.10.9",
4
+ "version": "25.11.0",
5
5
  "description": "Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.",
6
6
  "homepage": "https://github.com/ankhorage/zora#readme",
7
7
  "bugs": {
@@ -7,7 +7,9 @@ import type {
7
7
  GraphViewProps,
8
8
  GraphViewRenderedNode,
9
9
  } from '../../../../../types/graph-view';
10
+ import { GRAPH_VIEW_EVENTS } from '../../../constants/events';
10
11
  import { createGraphRuntime, type GraphRuntime } from './createGraphRuntime';
12
+ import { createGraphViewDomCallbacks } from './createGraphViewDomCallbacks';
11
13
  import { GraphNodeOverlay } from './GraphNodeOverlay';
12
14
 
13
15
  type GraphContainer = NonNullable<CytoscapeOptions['container']>;
@@ -20,16 +22,22 @@ type GraphContainer = NonNullable<CytoscapeOptions['container']>;
20
22
  * text. Keep padding in styleRules. Compound and rich-node dimensions retain their existing owners.
21
23
  * minReadableLabelSize Expand the manual zoom ceiling until plain labels reach this CSS-pixel size.
22
24
  * maxFitLabelSize Cap automatic fitting at this plain-label CSS-pixel size, leaving whitespace for tiny graphs.
25
+ * Browser lifecycle events named by public GRAPH_VIEW_EVENTS bubble from the root
26
+ * with `detail.controller`: the same public GraphViewController delivered to React.
27
+ * Browser integrations can call `measureNodes` without accessing the Cytoscape runtime.
28
+ * The standalone browser artifact exposes the same names as `GraphView.events`.
23
29
  * onSpacingFactorChange Receive the spacing accepted by explicit fit({ optimizeSpacing: true }).
24
30
  * Ordinary zoom never adjusts spacing or relayouts. Explicit optimization preserves layout ordering,
25
31
  * stops once effective fit reaches maxFitLabelSize, keeps labels and unrelated compound boundaries
26
32
  * collision-safe, and permits only a small rendered background-overlap fallback for dense graphs.
27
33
  */
28
34
  export function GraphView(props: GraphViewProps) {
29
- const { containerRef, renderedNodes, runtimeRef } = useGraphViewRuntime(props);
35
+ const hostRef = React.useRef<HTMLDivElement | null>(null);
36
+ const { containerRef, renderedNodes, runtimeRef } = useGraphViewRuntime(props, hostRef);
30
37
 
31
38
  return (
32
39
  <div
40
+ ref={hostRef}
33
41
  aria-label={props.ariaLabel ?? 'Graph'}
34
42
  className={props.className}
35
43
  role="application"
@@ -58,11 +66,17 @@ export function GraphView(props: GraphViewProps) {
58
66
  );
59
67
  }
60
68
 
69
+ /*** Publish the same canonical event registry on the standalone GraphView artifact. */
70
+ GraphView.events = GRAPH_VIEW_EVENTS;
71
+
61
72
  /*** Compose React synchronization around the single imperative graph runtime. */
62
- function useGraphViewRuntime(props: GraphViewProps) {
73
+ function useGraphViewRuntime(
74
+ props: GraphViewProps,
75
+ hostRef: React.RefObject<HTMLDivElement | null>,
76
+ ) {
63
77
  const containerRef = React.useRef<GraphContainer | null>(null);
64
78
  const runtimeRef = React.useRef<GraphRuntime | null>(null);
65
- const callbacksRef = useGraphCallbacksRef(props);
79
+ const callbacksRef = useGraphCallbacksRef(props, hostRef);
66
80
  const [renderedNodes, setRenderedNodes] = React.useState<readonly GraphViewRenderedNode[]>([]);
67
81
 
68
82
  useRuntimeLifecycle(containerRef, runtimeRef, callbacksRef);
@@ -75,8 +89,13 @@ function useGraphViewRuntime(props: GraphViewProps) {
75
89
  }
76
90
 
77
91
  /*** Keep the callback ref current without mutating refs during render. */
78
- function useGraphCallbacksRef(props: GraphViewProps) {
79
- const callbacksRef = React.useRef<GraphViewCallbacks>(readCallbacks(props));
92
+ function useGraphCallbacksRef(
93
+ props: GraphViewProps,
94
+ hostRef: React.RefObject<HTMLDivElement | null>,
95
+ ) {
96
+ const callbacksRef = React.useRef<GraphViewCallbacks>(
97
+ createGraphViewDomCallbacks(readCallbacks(props), hostRef),
98
+ );
80
99
  const {
81
100
  onEdgeEvent,
82
101
  onLayoutComplete,
@@ -87,14 +106,17 @@ function useGraphCallbacksRef(props: GraphViewProps) {
87
106
  } = props;
88
107
 
89
108
  React.useEffect(() => {
90
- callbacksRef.current = {
91
- onEdgeEvent,
92
- onLayoutComplete,
93
- onNodeEvent,
94
- onReady,
95
- onViewportChange,
96
- onSpacingFactorChange,
97
- };
109
+ callbacksRef.current = createGraphViewDomCallbacks(
110
+ {
111
+ onEdgeEvent,
112
+ onLayoutComplete,
113
+ onNodeEvent,
114
+ onReady,
115
+ onViewportChange,
116
+ onSpacingFactorChange,
117
+ },
118
+ hostRef,
119
+ );
98
120
  }, [
99
121
  onEdgeEvent,
100
122
  onLayoutComplete,
@@ -102,6 +124,7 @@ function useGraphCallbacksRef(props: GraphViewProps) {
102
124
  onReady,
103
125
  onViewportChange,
104
126
  onSpacingFactorChange,
127
+ hostRef,
105
128
  ]);
106
129
 
107
130
  return callbacksRef;
@@ -0,0 +1,77 @@
1
+ import { expect, test } from 'bun:test';
2
+
3
+ import { GRAPH_VIEW_EVENTS } from '../../../constants/events';
4
+ import { createGraphViewDomCallbacks } from './createGraphViewDomCallbacks';
5
+
6
+ test('exposes public controller on bubbling ready and layout events without changing React callbacks', () => {
7
+ const host = new EventTarget();
8
+ const controller = createController();
9
+ const observed: string[] = [];
10
+ const browserEvents: string[] = [];
11
+ for (const name of [GRAPH_VIEW_EVENTS.ready, GRAPH_VIEW_EVENTS.layoutComplete]) {
12
+ host.addEventListener(name, (event) => {
13
+ if (!(event instanceof CustomEvent)) throw new Error('Expected a CustomEvent.');
14
+ const detail: unknown = event.detail;
15
+ if (typeof detail !== 'object' || detail === null || !('controller' in detail))
16
+ throw new Error('Expected a public controller in the browser event.');
17
+ expect(detail.controller).toBe(controller);
18
+ expect(event.bubbles).toBe(true);
19
+ browserEvents.push(name);
20
+ });
21
+ }
22
+
23
+ const callbacks = createGraphViewDomCallbacks(
24
+ {
25
+ onReady: (received) => {
26
+ expect(received).toBe(controller);
27
+ observed.push('ready');
28
+ },
29
+ onLayoutComplete: (received) => {
30
+ expect(received).toBe(controller);
31
+ observed.push('layout');
32
+ },
33
+ },
34
+ { current: host },
35
+ );
36
+ callbacks.onReady?.(controller);
37
+ callbacks.onLayoutComplete?.(controller);
38
+
39
+ expect(observed).toEqual(['ready', 'layout']);
40
+ expect(browserEvents).toEqual([GRAPH_VIEW_EVENTS.ready, GRAPH_VIEW_EVENTS.layoutComplete]);
41
+ expect(controller.measureNodes(['src']).nodes[0]?.id).toBe('src');
42
+ });
43
+
44
+ test('preserves React callbacks when the browser host is unmounted', () => {
45
+ const controller = createController();
46
+ const observed: string[] = [];
47
+ const callbacks = createGraphViewDomCallbacks(
48
+ {
49
+ onReady: () => observed.push('ready'),
50
+ onLayoutComplete: () => observed.push('layout'),
51
+ },
52
+ { current: null },
53
+ );
54
+ callbacks.onReady?.(controller);
55
+ callbacks.onLayoutComplete?.(controller);
56
+ expect(observed).toEqual(['ready', 'layout']);
57
+ });
58
+
59
+ /*** Supplies public viewport and rendered geometry without a Cytoscape instance. */
60
+ function createController() {
61
+ return {
62
+ fit: () => undefined,
63
+ measureNodes: (ids: readonly string[]) => ({
64
+ nodes: ids.map((id) => ({
65
+ id,
66
+ position: { x: 120, y: 80 },
67
+ size: { width: 100, height: 48 },
68
+ })),
69
+ viewport: { width: 800, height: 600 },
70
+ }),
71
+ getViewport: () => ({ pan: { x: 0, y: 0 }, zoom: 1 }),
72
+ getZoomRange: () => ({ min: 0.5, max: 2 }),
73
+ setPan: () => undefined,
74
+ setZoom: () => undefined,
75
+ zoomBy: () => undefined,
76
+ };
77
+ }
@@ -0,0 +1,55 @@
1
+ import type {
2
+ GraphViewCallbacks,
3
+ GraphViewController,
4
+ GraphViewDomControllerEventDetail,
5
+ } from '../../../../../types/graph-view';
6
+ import { GRAPH_VIEW_EVENTS, type GraphViewDomControllerEventName } from '../../../constants/events';
7
+
8
+ /***
9
+ * Mirror public GraphView controller callbacks as bubbling browser lifecycle events.
10
+ * Browser hosts can measure actual rendered nodes without reaching into the graph engine.
11
+ */
12
+ export function createGraphViewDomCallbacks(
13
+ callbacks: GraphViewCallbacks,
14
+ hostRef: { readonly current: unknown },
15
+ ): GraphViewCallbacks {
16
+ return {
17
+ ...callbacks,
18
+ onReady(controller) {
19
+ callbacks.onReady?.(controller);
20
+ dispatchGraphViewControllerEvent(hostRef, GRAPH_VIEW_EVENTS.ready, controller);
21
+ },
22
+ onLayoutComplete(controller) {
23
+ callbacks.onLayoutComplete?.(controller);
24
+ dispatchGraphViewControllerEvent(hostRef, GRAPH_VIEW_EVENTS.layoutComplete, controller);
25
+ },
26
+ };
27
+ }
28
+
29
+ /*** Deliver the existing public controller reference on the GraphView root event channel. */
30
+ function dispatchGraphViewControllerEvent(
31
+ hostRef: { readonly current: unknown },
32
+ name: GraphViewDomControllerEventName,
33
+ controller: GraphViewController,
34
+ ): void {
35
+ const host = hostRef.current;
36
+ if (!canDispatchBrowserEvent(host)) return;
37
+ host.dispatchEvent(
38
+ new CustomEvent<GraphViewDomControllerEventDetail>(name, {
39
+ bubbles: true,
40
+ detail: { controller },
41
+ }),
42
+ );
43
+ }
44
+
45
+ /*** Narrow an untrusted browser host to the standard event-dispatch method. */
46
+ function canDispatchBrowserEvent(
47
+ value: unknown,
48
+ ): value is { dispatchEvent(event: Event): boolean } {
49
+ return (
50
+ typeof value === 'object' &&
51
+ value !== null &&
52
+ 'dispatchEvent' in value &&
53
+ typeof value.dispatchEvent === 'function'
54
+ );
55
+ }
@@ -0,0 +1,12 @@
1
+ /***
2
+ * Canonical public GraphView browser lifecycle event names.
3
+ * Candidate for a shared Ankhorage event strategy: define common naming, typed
4
+ * payloads and dispatch conventions before extracting a cross-component contract.
5
+ */
6
+ export const GRAPH_VIEW_EVENTS = {
7
+ ready: 'zora-graph-view-ready',
8
+ layoutComplete: 'zora-graph-view-layout-complete',
9
+ } as const;
10
+
11
+ export type GraphViewDomControllerEventName =
12
+ (typeof GRAPH_VIEW_EVENTS)[keyof typeof GRAPH_VIEW_EVENTS];
@@ -1,6 +1,7 @@
1
1
  export type {
2
2
  GraphViewCallbacks,
3
3
  GraphViewController,
4
+ GraphViewDomControllerEventDetail,
4
5
  GraphViewEdge,
5
6
  GraphViewElementEvent,
6
7
  GraphViewElementEventType,
@@ -18,3 +19,4 @@ export type {
18
19
  GraphViewViewport,
19
20
  } from '../../types/graph-view';
20
21
  export { GraphView } from './adapters/inbound/web-artifact/GraphView';
22
+ export { GRAPH_VIEW_EVENTS } from './constants/events';
@@ -94,6 +94,11 @@ export interface GraphViewController {
94
94
  zoomBy(factor: number): void;
95
95
  }
96
96
 
97
+ /*** The browser lifecycle events carry only the existing public graph controller. */
98
+ export interface GraphViewDomControllerEventDetail {
99
+ readonly controller: GraphViewController;
100
+ }
101
+
97
102
  export interface GraphViewProps {
98
103
  readonly nodes: readonly GraphViewNode[];
99
104
  readonly edges: readonly GraphViewEdge[];
@@ -10,10 +10,21 @@ import type { GraphViewProps } from './GraphView.types-0';
10
10
  * text. Keep padding in styleRules. Compound and rich-node dimensions retain their existing owners.
11
11
  * minReadableLabelSize Expand the manual zoom ceiling until plain labels reach this CSS-pixel size.
12
12
  * maxFitLabelSize Cap automatic fitting at this plain-label CSS-pixel size, leaving whitespace for tiny graphs.
13
+ * Browser lifecycle events named by public GRAPH_VIEW_EVENTS bubble from the root
14
+ * with `detail.controller`: the same public GraphViewController delivered to React.
15
+ * Browser integrations can call `measureNodes` without accessing the Cytoscape runtime.
16
+ * The standalone browser artifact exposes the same names as `GraphView.events`.
13
17
  * onSpacingFactorChange Receive the spacing accepted by explicit fit({ optimizeSpacing: true }).
14
18
  * Ordinary zoom never adjusts spacing or relayouts. Explicit optimization preserves layout ordering,
15
19
  * stops once effective fit reaches maxFitLabelSize, keeps labels and unrelated compound boundaries
16
20
  * collision-safe, and permits only a small rendered background-overlap fallback for dense graphs.
17
21
  */
18
22
  export declare function GraphView(props: GraphViewProps): React.JSX.Element;
19
- export type { GraphViewCallbacks, GraphViewController, GraphViewEdge, GraphViewElementEvent, GraphViewElementEventType, GraphViewFitOptions, GraphViewLayoutName, GraphViewMeasurement, GraphViewNode, GraphViewNodeMeasurement, GraphViewNodeRenderContext, GraphViewPoint, GraphViewProps, GraphViewRenderedNode, GraphViewSize, GraphViewStyleRule, GraphViewViewport } from './GraphView.types-0';
23
+
24
+ export declare namespace GraphView {
25
+ var events: {
26
+ readonly ready: "zora-graph-view-ready";
27
+ readonly layoutComplete: "zora-graph-view-layout-complete";
28
+ };
29
+ }
30
+ export type { GraphViewCallbacks, GraphViewController, GraphViewDomControllerEventDetail, GraphViewEdge, GraphViewElementEvent, GraphViewElementEventType, GraphViewFitOptions, GraphViewLayoutName, GraphViewMeasurement, GraphViewNode, GraphViewNodeMeasurement, GraphViewNodeRenderContext, GraphViewPoint, GraphViewProps, GraphViewRenderedNode, GraphViewSize, GraphViewStyleRule, GraphViewViewport } from './GraphView.types-0';
@@ -91964,6 +91964,12 @@ Consider installing the package or pass your own 'workerFactory' to ELK's constr
91964
91964
  // src/features/graph-view/adapters/inbound/web-artifact/GraphView.tsx
91965
91965
  import React2 from "react";
91966
91966
 
91967
+ // src/features/graph-view/constants/events.ts
91968
+ var GRAPH_VIEW_EVENTS = {
91969
+ ready: "zora-graph-view-ready",
91970
+ layoutComplete: "zora-graph-view-layout-complete"
91971
+ };
91972
+
91967
91973
  // node_modules/cytoscape/dist/cytoscape.esm.mjs
91968
91974
  function _arrayLikeToArray(r, a) {
91969
91975
  (a == null || a > r.length) && (a = r.length);
@@ -123615,6 +123621,33 @@ function destroyRuntime(state) {
123615
123621
  }
123616
123622
  registerGraphLayouts();
123617
123623
 
123624
+ // src/features/graph-view/adapters/inbound/web-artifact/createGraphViewDomCallbacks.ts
123625
+ function createGraphViewDomCallbacks(callbacks, hostRef) {
123626
+ return {
123627
+ ...callbacks,
123628
+ onReady(controller) {
123629
+ callbacks.onReady?.(controller);
123630
+ dispatchGraphViewControllerEvent(hostRef, GRAPH_VIEW_EVENTS.ready, controller);
123631
+ },
123632
+ onLayoutComplete(controller) {
123633
+ callbacks.onLayoutComplete?.(controller);
123634
+ dispatchGraphViewControllerEvent(hostRef, GRAPH_VIEW_EVENTS.layoutComplete, controller);
123635
+ }
123636
+ };
123637
+ }
123638
+ function dispatchGraphViewControllerEvent(hostRef, name, controller) {
123639
+ const host = hostRef.current;
123640
+ if (!canDispatchBrowserEvent(host))
123641
+ return;
123642
+ host.dispatchEvent(new CustomEvent(name, {
123643
+ bubbles: true,
123644
+ detail: { controller }
123645
+ }));
123646
+ }
123647
+ function canDispatchBrowserEvent(value) {
123648
+ return typeof value === "object" && value !== null && "dispatchEvent" in value && typeof value.dispatchEvent === "function";
123649
+ }
123650
+
123618
123651
  // src/features/graph-view/adapters/inbound/web-artifact/GraphNodeOverlay.tsx
123619
123652
  import React from "react";
123620
123653
  import { jsx } from "react/jsx-runtime";
@@ -123700,8 +123733,10 @@ function readResizeObserverConstructor2() {
123700
123733
  // src/features/graph-view/adapters/inbound/web-artifact/GraphView.tsx
123701
123734
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
123702
123735
  function GraphView(props) {
123703
- const { containerRef, renderedNodes, runtimeRef } = useGraphViewRuntime(props);
123736
+ const hostRef = React2.useRef(null);
123737
+ const { containerRef, renderedNodes, runtimeRef } = useGraphViewRuntime(props, hostRef);
123704
123738
  return /* @__PURE__ */ jsxs("div", {
123739
+ ref: hostRef,
123705
123740
  "aria-label": props.ariaLabel ?? "Graph",
123706
123741
  className: props.className,
123707
123742
  role: "application",
@@ -123728,10 +123763,11 @@ function GraphView(props) {
123728
123763
  ]
123729
123764
  });
123730
123765
  }
123731
- function useGraphViewRuntime(props) {
123766
+ GraphView.events = GRAPH_VIEW_EVENTS;
123767
+ function useGraphViewRuntime(props, hostRef) {
123732
123768
  const containerRef = React2.useRef(null);
123733
123769
  const runtimeRef = React2.useRef(null);
123734
- const callbacksRef = useGraphCallbacksRef(props);
123770
+ const callbacksRef = useGraphCallbacksRef(props, hostRef);
123735
123771
  const [renderedNodes, setRenderedNodes] = React2.useState([]);
123736
123772
  useRuntimeLifecycle(containerRef, runtimeRef, callbacksRef);
123737
123773
  useRuntimeGraphUpdate(runtimeRef, props);
@@ -123740,8 +123776,8 @@ function useGraphViewRuntime(props) {
123740
123776
  useKnownNodeSizes(runtimeRef, props.nodes, props.getNodeSize);
123741
123777
  return { containerRef, renderedNodes, runtimeRef };
123742
123778
  }
123743
- function useGraphCallbacksRef(props) {
123744
- const callbacksRef = React2.useRef(readCallbacks(props));
123779
+ function useGraphCallbacksRef(props, hostRef) {
123780
+ const callbacksRef = React2.useRef(createGraphViewDomCallbacks(readCallbacks(props), hostRef));
123745
123781
  const {
123746
123782
  onEdgeEvent,
123747
123783
  onLayoutComplete,
@@ -123751,21 +123787,22 @@ function useGraphCallbacksRef(props) {
123751
123787
  onSpacingFactorChange
123752
123788
  } = props;
123753
123789
  React2.useEffect(() => {
123754
- callbacksRef.current = {
123790
+ callbacksRef.current = createGraphViewDomCallbacks({
123755
123791
  onEdgeEvent,
123756
123792
  onLayoutComplete,
123757
123793
  onNodeEvent,
123758
123794
  onReady,
123759
123795
  onViewportChange,
123760
123796
  onSpacingFactorChange
123761
- };
123797
+ }, hostRef);
123762
123798
  }, [
123763
123799
  onEdgeEvent,
123764
123800
  onLayoutComplete,
123765
123801
  onNodeEvent,
123766
123802
  onReady,
123767
123803
  onViewportChange,
123768
- onSpacingFactorChange
123804
+ onSpacingFactorChange,
123805
+ hostRef
123769
123806
  ]);
123770
123807
  return callbacksRef;
123771
123808
  }
@@ -81,6 +81,10 @@ export interface GraphViewController {
81
81
  setZoom(zoom: number): void;
82
82
  zoomBy(factor: number): void;
83
83
  }
84
+ /*** The browser lifecycle events carry only the existing public graph controller. */
85
+ export interface GraphViewDomControllerEventDetail {
86
+ readonly controller: GraphViewController;
87
+ }
84
88
  export interface GraphViewProps {
85
89
  readonly nodes: readonly GraphViewNode[];
86
90
  readonly edges: readonly GraphViewEdge[];