@prnt/dagr-explorer 0.1.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.
Files changed (135) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/LICENSE +21 -0
  3. package/README.md +441 -0
  4. package/dist/base.d.ts +63 -0
  5. package/dist/base.d.ts.map +1 -0
  6. package/dist/base.js +20 -0
  7. package/dist/base.js.map +1 -0
  8. package/dist/camera.d.ts +61 -0
  9. package/dist/camera.d.ts.map +1 -0
  10. package/dist/camera.js +142 -0
  11. package/dist/camera.js.map +1 -0
  12. package/dist/context.d.ts +110 -0
  13. package/dist/context.d.ts.map +1 -0
  14. package/dist/context.js +71 -0
  15. package/dist/context.js.map +1 -0
  16. package/dist/dagr-explorer.d.ts +41 -0
  17. package/dist/dagr-explorer.d.ts.map +1 -0
  18. package/dist/dagr-explorer.js +13 -0
  19. package/dist/dagr-explorer.js.map +1 -0
  20. package/dist/errors.d.ts +55 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/errors.js +57 -0
  23. package/dist/errors.js.map +1 -0
  24. package/dist/explorer-details.d.ts +44 -0
  25. package/dist/explorer-details.d.ts.map +1 -0
  26. package/dist/explorer-details.js +76 -0
  27. package/dist/explorer-details.js.map +1 -0
  28. package/dist/explorer-search.d.ts +31 -0
  29. package/dist/explorer-search.d.ts.map +1 -0
  30. package/dist/explorer-search.js +86 -0
  31. package/dist/explorer-search.js.map +1 -0
  32. package/dist/explorer-toolbar.d.ts +17 -0
  33. package/dist/explorer-toolbar.d.ts.map +1 -0
  34. package/dist/explorer-toolbar.js +44 -0
  35. package/dist/explorer-toolbar.js.map +1 -0
  36. package/dist/explorer-trace-toggle.d.ts +16 -0
  37. package/dist/explorer-trace-toggle.d.ts.map +1 -0
  38. package/dist/explorer-trace-toggle.js +8 -0
  39. package/dist/explorer-trace-toggle.js.map +1 -0
  40. package/dist/explorer-viewport.d.ts +69 -0
  41. package/dist/explorer-viewport.d.ts.map +1 -0
  42. package/dist/explorer-viewport.js +69 -0
  43. package/dist/explorer-viewport.js.map +1 -0
  44. package/dist/explorer-views.d.ts +24 -0
  45. package/dist/explorer-views.d.ts.map +1 -0
  46. package/dist/explorer-views.js +16 -0
  47. package/dist/explorer-views.js.map +1 -0
  48. package/dist/index.d.ts +48 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +33 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/isomorphic-layout-effect.d.ts +7 -0
  53. package/dist/isomorphic-layout-effect.d.ts.map +1 -0
  54. package/dist/isomorphic-layout-effect.js +7 -0
  55. package/dist/isomorphic-layout-effect.js.map +1 -0
  56. package/dist/labels.d.ts +67 -0
  57. package/dist/labels.d.ts.map +1 -0
  58. package/dist/labels.js +50 -0
  59. package/dist/labels.js.map +1 -0
  60. package/dist/layout.d.ts +82 -0
  61. package/dist/layout.d.ts.map +1 -0
  62. package/dist/layout.js +244 -0
  63. package/dist/layout.js.map +1 -0
  64. package/dist/navigation.d.ts +20 -0
  65. package/dist/navigation.d.ts.map +1 -0
  66. package/dist/navigation.js +44 -0
  67. package/dist/navigation.js.map +1 -0
  68. package/dist/root.d.ts +89 -0
  69. package/dist/root.d.ts.map +1 -0
  70. package/dist/root.js +401 -0
  71. package/dist/root.js.map +1 -0
  72. package/dist/search.d.ts +19 -0
  73. package/dist/search.d.ts.map +1 -0
  74. package/dist/search.js +31 -0
  75. package/dist/search.js.map +1 -0
  76. package/dist/size.d.ts +12 -0
  77. package/dist/size.d.ts.map +1 -0
  78. package/dist/size.js +20 -0
  79. package/dist/size.js.map +1 -0
  80. package/dist/svg-base.d.ts +33 -0
  81. package/dist/svg-base.d.ts.map +1 -0
  82. package/dist/svg-base.js +89 -0
  83. package/dist/svg-base.js.map +1 -0
  84. package/dist/types.d.ts +61 -0
  85. package/dist/types.d.ts.map +1 -0
  86. package/dist/types.js +2 -0
  87. package/dist/types.js.map +1 -0
  88. package/dist/use-explorer-camera.d.ts +56 -0
  89. package/dist/use-explorer-camera.d.ts.map +1 -0
  90. package/dist/use-explorer-camera.js +641 -0
  91. package/dist/use-explorer-camera.js.map +1 -0
  92. package/dist/use-explorer.d.ts +23 -0
  93. package/dist/use-explorer.d.ts.map +1 -0
  94. package/dist/use-explorer.js +26 -0
  95. package/dist/use-explorer.js.map +1 -0
  96. package/dist/validate.d.ts +20 -0
  97. package/dist/validate.d.ts.map +1 -0
  98. package/dist/validate.js +99 -0
  99. package/dist/validate.js.map +1 -0
  100. package/dist/viewport-surface.d.ts +90 -0
  101. package/dist/viewport-surface.d.ts.map +1 -0
  102. package/dist/viewport-surface.js +485 -0
  103. package/dist/viewport-surface.js.map +1 -0
  104. package/dist/visible-set.d.ts +65 -0
  105. package/dist/visible-set.d.ts.map +1 -0
  106. package/dist/visible-set.js +168 -0
  107. package/dist/visible-set.js.map +1 -0
  108. package/package.json +62 -0
  109. package/src/base.ts +67 -0
  110. package/src/camera.ts +206 -0
  111. package/src/context.ts +168 -0
  112. package/src/dagr-explorer.tsx +85 -0
  113. package/src/errors.ts +85 -0
  114. package/src/explorer-details.tsx +156 -0
  115. package/src/explorer-search.tsx +140 -0
  116. package/src/explorer-toolbar.tsx +77 -0
  117. package/src/explorer-trace-toggle.tsx +34 -0
  118. package/src/explorer-viewport.tsx +181 -0
  119. package/src/explorer-views.tsx +61 -0
  120. package/src/index.ts +76 -0
  121. package/src/isomorphic-layout-effect.ts +7 -0
  122. package/src/labels.ts +109 -0
  123. package/src/layout.ts +298 -0
  124. package/src/navigation.ts +51 -0
  125. package/src/root.tsx +544 -0
  126. package/src/search.ts +36 -0
  127. package/src/size.ts +23 -0
  128. package/src/svg-base.tsx +173 -0
  129. package/src/types.ts +69 -0
  130. package/src/use-explorer-camera.ts +675 -0
  131. package/src/use-explorer.ts +32 -0
  132. package/src/validate.ts +173 -0
  133. package/src/viewport-surface.tsx +650 -0
  134. package/src/visible-set.ts +225 -0
  135. package/styles.css +253 -0
package/dist/size.js ADDED
@@ -0,0 +1,20 @@
1
+ /** The size of a node that declares none, in world pixels. */
2
+ export const DEFAULT_NODE_SIZE = Object.freeze({ width: 240, height: 120 });
3
+ /**
4
+ * A node's size: its own `size`, else the view's `nodeSize`, else the default.
5
+ *
6
+ * Sizes are declared and never measured, because a virtualized node has no
7
+ * element to measure. This is the one place that order is written down, so
8
+ * validation, layout and the shape key cannot disagree about it.
9
+ */
10
+ export function resolveNodeSize(layout, node) {
11
+ if (node.size !== undefined && node.size !== null)
12
+ return node.size;
13
+ const configured = layout?.nodeSize;
14
+ if (configured === undefined)
15
+ return DEFAULT_NODE_SIZE;
16
+ // The `??` is for a function that returns nothing: a JavaScript caller, or a
17
+ // branch the type checker was told not to look at.
18
+ return (typeof configured === 'function' ? configured(node) : configured) ?? DEFAULT_NODE_SIZE;
19
+ }
20
+ //# sourceMappingURL=size.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"size.js","sourceRoot":"","sources":["../src/size.ts"],"names":[],"mappings":"AAEA,8DAA8D;AAC9D,MAAM,CAAC,MAAM,iBAAiB,GAAS,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;AAElF;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA4C,EAC5C,IAAO;IAEP,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACpE,MAAM,UAAU,GAAG,MAAM,EAAE,QAAQ,CAAC;IACpC,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,iBAAiB,CAAC;IACvD,6EAA6E;IAC7E,mDAAmD;IACnD,OAAO,CAAC,OAAO,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,iBAAiB,CAAC;AACjG,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The default base layer: marks, edges and group outlines as one SVG.
3
+ *
4
+ * It sits inside the transformed plane, in world coordinates, so the camera
5
+ * never touches it: a pan or a zoom is the plane's transform and nothing
6
+ * here. It renders when the visible set or the emphasis changes, which is
7
+ * when the viewport hands it a new prop, and at no other time.
8
+ *
9
+ * It is decoration for sighted users, so it is `aria-hidden`. Every node a
10
+ * user can reach is reachable through the overlay and search, and a mark
11
+ * is clicked through the viewport's delegated listener, which resolves the
12
+ * point against the layout rather than against these elements.
13
+ *
14
+ * **One arrowhead marker per edge color.** An SVG marker cannot take the
15
+ * stroke of the path that uses it without `context-stroke`, which not every
16
+ * browser this package supports draws. So the arrowhead for a red edge is a
17
+ * red marker, and the markers are as many as the colors in view.
18
+ *
19
+ * **A marker's id is its color, escaped.** Ids live in the document, not
20
+ * in a React root, and two roots (two explorers, or two copies of React)
21
+ * can count `useId` the same way. Named by color, a marker one root reaches
22
+ * in another root's tree is the same arrowhead.
23
+ *
24
+ * **Strokes keep their width at any zoom** (`vector-effect`), so an edge
25
+ * never thins to nothing zoomed out or thickens to a bar zoomed in.
26
+ * Arrowheads are not strokes: the marker is in user space, so they scale
27
+ * with the camera like the marks they point at.
28
+ *
29
+ * Internal to the package. Nothing here is exported from the entry.
30
+ */
31
+ import type { ExplorerBase } from './base.js';
32
+ export declare const svgBase: ExplorerBase;
33
+ //# sourceMappingURL=svg-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg-base.d.ts","sourceRoot":"","sources":["../src/svg-base.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAqB,MAAM,WAAW,CAAC;AA2IjE,eAAO,MAAM,OAAO,EAAE,YAAqE,CAAC"}
@@ -0,0 +1,89 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The default base layer: marks, edges and group outlines as one SVG.
4
+ *
5
+ * It sits inside the transformed plane, in world coordinates, so the camera
6
+ * never touches it: a pan or a zoom is the plane's transform and nothing
7
+ * here. It renders when the visible set or the emphasis changes, which is
8
+ * when the viewport hands it a new prop, and at no other time.
9
+ *
10
+ * It is decoration for sighted users, so it is `aria-hidden`. Every node a
11
+ * user can reach is reachable through the overlay and search, and a mark
12
+ * is clicked through the viewport's delegated listener, which resolves the
13
+ * point against the layout rather than against these elements.
14
+ *
15
+ * **One arrowhead marker per edge color.** An SVG marker cannot take the
16
+ * stroke of the path that uses it without `context-stroke`, which not every
17
+ * browser this package supports draws. So the arrowhead for a red edge is a
18
+ * red marker, and the markers are as many as the colors in view.
19
+ *
20
+ * **A marker's id is its color, escaped.** Ids live in the document, not
21
+ * in a React root, and two roots (two explorers, or two copies of React)
22
+ * can count `useId` the same way. Named by color, a marker one root reaches
23
+ * in another root's tree is the same arrowhead.
24
+ *
25
+ * **Strokes keep their width at any zoom** (`vector-effect`), so an edge
26
+ * never thins to nothing zoomed out or thickens to a bar zoomed in.
27
+ * Arrowheads are not strokes: the marker is in user space, so they scale
28
+ * with the camera like the marks they point at.
29
+ *
30
+ * Internal to the package. Nothing here is exported from the entry.
31
+ */
32
+ import { useMemo } from 'react';
33
+ const DEFAULT_COLOR = 'currentColor';
34
+ const DASH = '6 4';
35
+ /**
36
+ * The marker id for a color: letters and digits as they are, and every other
37
+ * character, `-` included, as `-<hex code point>-`. Escaped rather than
38
+ * stripped, so two colors never share an id (`rgb(1, 23, 4)` and
39
+ * `rgb(12, 3, 4)` strip to the same characters), and the result is a valid
40
+ * `url(#...)` fragment for any CSS color.
41
+ */
42
+ function markerIdOf(color) {
43
+ const escaped = color.replace(/[^A-Za-z0-9]/gu, (char) => `-${(char.codePointAt(0) ?? 0).toString(16)}-`);
44
+ return `dagr-explorer-arrow-${escaped}`;
45
+ }
46
+ function SvgBaseLayer({ view, layout, visible, emphasis, }) {
47
+ const nodes = useMemo(() => new Map(view.nodes.map((node) => [node.id, node])), [view.nodes]);
48
+ const edges = useMemo(() => new Map(view.edges.map((edge) => [edge.id, edge])), [view.edges]);
49
+ const markers = new Map();
50
+ const markerFor = (color) => {
51
+ let id = markers.get(color);
52
+ if (id === undefined) {
53
+ id = markerIdOf(color);
54
+ markers.set(color, id);
55
+ }
56
+ return id;
57
+ };
58
+ const paths = [];
59
+ for (const id of visible.edges) {
60
+ const edge = edges.get(id);
61
+ const route = layout.routes.get(id);
62
+ const first = route?.[0];
63
+ if (edge === undefined || route === undefined || first === undefined)
64
+ continue;
65
+ const d = route.map((p, i) => `${i === 0 ? 'M' : 'L'}${String(p.x)} ${String(p.y)}`).join(' ');
66
+ const color = edge.color ?? DEFAULT_COLOR;
67
+ const dimmed = emphasis.dimmed.has(edge.source) || emphasis.dimmed.has(edge.target);
68
+ paths.push(_jsx("path", { "data-edge-id": id, "data-dimmed": dimmed ? 'true' : undefined, d: d, fill: "none", stroke: color, strokeWidth: 1.5, vectorEffect: "non-scaling-stroke", strokeDasharray: edge.dash === true ? DASH : undefined, markerEnd: `url(#${markerFor(color)})` }, id));
69
+ }
70
+ const marks = [];
71
+ for (const id of visible.baseNodes) {
72
+ const node = nodes.get(id);
73
+ const box = layout.boxes.get(id);
74
+ if (node === undefined || box === undefined)
75
+ continue;
76
+ marks.push(_jsx("rect", { "data-node-id": id, "data-dimmed": emphasis.dimmed.has(id) ? 'true' : undefined, x: box.x, y: box.y, width: box.width, height: box.height, rx: 4, fill: node.color ?? DEFAULT_COLOR }, id));
77
+ }
78
+ const groups = [];
79
+ for (const group of view.groups ?? []) {
80
+ const box = layout.groups.get(group.id);
81
+ if (box === undefined)
82
+ continue;
83
+ const color = group.color ?? DEFAULT_COLOR;
84
+ groups.push(_jsxs("g", { "data-group-id": group.id, children: [_jsx("rect", { x: box.x, y: box.y, width: box.width, height: box.height, rx: 8, fill: "none", stroke: color, vectorEffect: "non-scaling-stroke", strokeDasharray: "4 4" }), _jsx("text", { x: box.x + 12, y: box.y + 17, fill: color, fontSize: 12, children: group.label })] }, group.id));
85
+ }
86
+ return (_jsxs("svg", { "aria-hidden": "true", width: layout.width, height: layout.height, viewBox: `0 0 ${String(layout.width)} ${String(layout.height)}`, style: { position: 'absolute', left: 0, top: 0, overflow: 'visible' }, children: [_jsx("defs", { children: [...markers].map(([color, id]) => (_jsx("marker", { id: id, viewBox: "0 0 10 10", refX: 10, refY: 5, markerWidth: 8, markerHeight: 8, markerUnits: "userSpaceOnUse", orient: "auto", children: _jsx("path", { d: "M0 0L10 5L0 10z", fill: color }) }, id))) }), groups, paths, marks] }));
87
+ }
88
+ export const svgBase = Object.freeze({ Layer: SvgBaseLayer, space: 'plane' });
89
+ //# sourceMappingURL=svg-base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg-base.js","sourceRoot":"","sources":["../src/svg-base.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Size } from '@prnt/dagr-render/core';
3
+ export type { Size };
4
+ /**
5
+ * One node. `id` and `label` are all the explorer itself reads: the label is
6
+ * the accessible name, the search result text, and the default content.
7
+ * Everything else about a node is the caller's own fields on a type that
8
+ * extends this one, and every slot receives that type.
9
+ *
10
+ * Optional fields are spelled `T | undefined` so a caller compiling with
11
+ * `exactOptionalPropertyTypes` can pass a value it computed, including
12
+ * `undefined`, without a conditional spread.
13
+ */
14
+ export interface ExplorerNode {
15
+ readonly id: string;
16
+ readonly label: string;
17
+ /** World size in CSS pixels at zoom 1. Declared, never measured. */
18
+ readonly size?: Size | undefined;
19
+ /** Any CSS color. */
20
+ readonly color?: string | undefined;
21
+ }
22
+ export interface ExplorerEdge {
23
+ readonly id: string;
24
+ readonly source: string;
25
+ readonly target: string;
26
+ readonly label?: string | undefined;
27
+ /** Any CSS color. */
28
+ readonly color?: string | undefined;
29
+ /** Draw the edge dashed. */
30
+ readonly dash?: boolean | undefined;
31
+ }
32
+ /** A labeled outline around its members. An annotation: it moves no node. */
33
+ export interface ExplorerGroup {
34
+ readonly id: string;
35
+ readonly label: string;
36
+ readonly nodeIds: readonly string[];
37
+ /** Any CSS color. */
38
+ readonly color?: string | undefined;
39
+ }
40
+ export interface ExplorerLayoutOptions<N extends ExplorerNode = ExplorerNode> {
41
+ /** Which way edges flow. Default `'right'`. */
42
+ readonly direction?: 'right' | 'down' | undefined;
43
+ /** Size for nodes with no `size` of their own. Default 240 by 120. */
44
+ readonly nodeSize?: Size | ((node: N) => Size) | undefined;
45
+ /** Gap between neighbors across the flow. Default 40. */
46
+ readonly nodeSep?: number | undefined;
47
+ /** Gap between ranks along the flow. Default 120. */
48
+ readonly rankSep?: number | undefined;
49
+ /** Default `'smooth'`. */
50
+ readonly edgeStyle?: 'smooth' | 'orthogonal' | undefined;
51
+ }
52
+ export interface ExplorerView<N extends ExplorerNode = ExplorerNode, E extends ExplorerEdge = ExplorerEdge> {
53
+ readonly id: string;
54
+ readonly label: string;
55
+ readonly description?: ReactNode | undefined;
56
+ readonly nodes: readonly N[];
57
+ readonly edges: readonly E[];
58
+ readonly groups?: readonly ExplorerGroup[] | undefined;
59
+ readonly layout?: ExplorerLayoutOptions<N> | undefined;
60
+ }
61
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEnD,YAAY,EAAE,IAAI,EAAE,CAAC;AAErB;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACjC,qBAAqB;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,qBAAqB;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,4BAA4B;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,qBAAqB;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY;IAC1E,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IAClD,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3D,yDAAyD;IACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,qDAAqD;IACrD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,0BAA0B;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAC;CAC1D;AAED,MAAM,WAAW,YAAY,CAC3B,CAAC,SAAS,YAAY,GAAG,YAAY,EACrC,CAAC,SAAS,YAAY,GAAG,YAAY;IAErC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACxD"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The explorer's camera: one animation frame loop, a resize observer and the
3
+ * gestures, bound to a viewport element and the plane inside it.
4
+ *
5
+ * A port of the docs' `useGraphCamera` with its arithmetic taken out: every
6
+ * camera this file holds comes from `camera.ts`. What is left here is time
7
+ * (the frame loop), input (wheel, pointer, keys) and the DOM writes.
8
+ *
9
+ * **The camera never causes a React render.** A frame is one
10
+ * `style.transform` write on the plane, and `onFrame` is how the owner
11
+ * learns the camera moved. Whether that is worth a render (the visible set
12
+ * changed) is the owner's decision, not this hook's.
13
+ *
14
+ * **One engine per effect.** Everything mutable lives in a closure the
15
+ * effect creates and its cleanup disposes, so StrictMode's mount, cleanup,
16
+ * mount leaves exactly one loop and one set of listeners, and a frame that
17
+ * fires after cleanup finds the engine disposed and writes nothing. The
18
+ * controls the hook returns are stable for the component's lifetime and
19
+ * forward to whichever engine is live, or do nothing.
20
+ *
21
+ * **No size, no camera.** A viewport measured at zero (a hidden tab,
22
+ * `display: none`, the server) has no limits, so there is no camera to
23
+ * draw: the controls are no-ops and the plane stays hidden. The first real
24
+ * size places the fitted camera at once, without a flight.
25
+ *
26
+ * **A resize or a new layout keeps the user's place:** the world point at
27
+ * the center and the scale, under the new limits. Only a camera that was at
28
+ * fit is fitted again, so a user zoomed in on a node keeps it.
29
+ *
30
+ * Internal to the package. Nothing here is exported from the entry.
31
+ */
32
+ import type { RefObject } from 'react';
33
+ import type { Vec2 } from '@prnt/dagr-render/core';
34
+ import type { ExplorerCamera, ExplorerViewportSize } from './camera.js';
35
+ import type { ExplorerBox, ExplorerLayout } from './layout.js';
36
+ export interface ExplorerCameraControls {
37
+ fit(): void;
38
+ /** Anchored at the viewport center. */
39
+ zoomBy(factor: number): void;
40
+ /** A flight to fit the box. */
41
+ focusBox(box: ExplorerBox): void;
42
+ /** The least pan that brings the box into view, at the current scale. */
43
+ revealBox(box: ExplorerBox): void;
44
+ /** The camera on screen, or `null` before the first fit. */
45
+ getCamera(): ExplorerCamera | null;
46
+ screenToWorld(point: Vec2): Vec2 | null;
47
+ }
48
+ export interface UseExplorerCameraOptions {
49
+ readonly viewportRef: RefObject<HTMLElement | null>;
50
+ readonly planeRef: RefObject<HTMLElement | null>;
51
+ readonly layout: ExplorerLayout;
52
+ /** Called on every drawn frame, after the transform is written. */
53
+ readonly onFrame: (camera: ExplorerCamera, viewport: ExplorerViewportSize) => void;
54
+ }
55
+ export declare function useExplorerCamera(options: UseExplorerCameraOptions): ExplorerCameraControls;
56
+ //# sourceMappingURL=use-explorer-camera.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-explorer-camera.d.ts","sourceRoot":"","sources":["../src/use-explorer-camera.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAYnD,OAAO,KAAK,EAAgB,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,sBAAsB;IACrC,GAAG,IAAI,IAAI,CAAC;IACZ,uCAAuC;IACvC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,+BAA+B;IAC/B,QAAQ,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,yEAAyE;IACzE,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAAC;IAClC,4DAA4D;IAC5D,SAAS,IAAI,cAAc,GAAG,IAAI,CAAC;IACnC,aAAa,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACpD,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACpF;AAqjBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,sBAAsB,CAyC3F"}