@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
@@ -0,0 +1,23 @@
1
+ import type { ExplorerApi, ExplorerState } from './context.js';
2
+ import type { ExplorerEdge, ExplorerNode } from './types.js';
3
+ /**
4
+ * The explorer's state and methods, for a part of the host's own. It is what
5
+ * the built-in parts read and call, so a custom part can do what they do.
6
+ *
7
+ * `N` and `E` are a claim about the root's data that nothing checks: a
8
+ * context erases type parameters. They default to the base types, so an
9
+ * unannotated call sees only the fields the explorer guarantees.
10
+ *
11
+ * @throws {ExplorerContextError} `OUTSIDE_EXPLORER` outside an `ExplorerRoot`.
12
+ */
13
+ export declare function useExplorer<N extends ExplorerNode = ExplorerNode, E extends ExplorerEdge = ExplorerEdge>(): ExplorerState<N, E>;
14
+ /**
15
+ * The explorer's methods alone: the same stable functions `apiRef` and
16
+ * `useExplorer()` hand out. A component that only calls them should use this,
17
+ * because it never re-renders for a change of state: not a keystroke, not a
18
+ * selection, not a trace toggle.
19
+ *
20
+ * @throws {ExplorerContextError} `OUTSIDE_EXPLORER` outside an `ExplorerRoot`.
21
+ */
22
+ export declare function useExplorerApi(): ExplorerApi;
23
+ //# sourceMappingURL=use-explorer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-explorer.d.ts","sourceRoot":"","sources":["../src/use-explorer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE7D;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,CAAC,SAAS,YAAY,GAAG,YAAY,EACrC,CAAC,SAAS,YAAY,GAAG,YAAY,KAClC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAEvB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAE5C"}
@@ -0,0 +1,26 @@
1
+ import { useExplorerApiContext, useExplorerContext } from './context.js';
2
+ /**
3
+ * The explorer's state and methods, for a part of the host's own. It is what
4
+ * the built-in parts read and call, so a custom part can do what they do.
5
+ *
6
+ * `N` and `E` are a claim about the root's data that nothing checks: a
7
+ * context erases type parameters. They default to the base types, so an
8
+ * unannotated call sees only the fields the explorer guarantees.
9
+ *
10
+ * @throws {ExplorerContextError} `OUTSIDE_EXPLORER` outside an `ExplorerRoot`.
11
+ */
12
+ export function useExplorer() {
13
+ return useExplorerContext('useExplorer()').state;
14
+ }
15
+ /**
16
+ * The explorer's methods alone: the same stable functions `apiRef` and
17
+ * `useExplorer()` hand out. A component that only calls them should use this,
18
+ * because it never re-renders for a change of state: not a keystroke, not a
19
+ * selection, not a trace toggle.
20
+ *
21
+ * @throws {ExplorerContextError} `OUTSIDE_EXPLORER` outside an `ExplorerRoot`.
22
+ */
23
+ export function useExplorerApi() {
24
+ return useExplorerApiContext('useExplorerApi()');
25
+ }
26
+ //# sourceMappingURL=use-explorer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-explorer.js","sourceRoot":"","sources":["../src/use-explorer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAIzE;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW;IAIzB,OAAO,kBAAkB,CAAC,eAAe,CAAC,CAAC,KAAuC,CAAC;AACrF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { ExplorerEdge, ExplorerNode, ExplorerView } from './types.js';
2
+ /**
3
+ * Throws {@link ExplorerDataError} for a view that cannot be drawn honestly.
4
+ *
5
+ * Sizes are checked here rather than left to the layout engine, which accepts
6
+ * a zero-size node and reports `NaN` as a fault in its own config. Neither
7
+ * tells the caller which node to fix.
8
+ *
9
+ * Layout options are checked for the same reason: the engine rejects a bad
10
+ * spacing with an error that names no view, the renderer throws a bare
11
+ * `RangeError` for an unknown edge style, and an unknown direction would be
12
+ * read as `'down'` without a word.
13
+ *
14
+ * A self loop and parallel edges are valid data. What happens to them is
15
+ * `layout.ts`'s business.
16
+ */
17
+ export declare function validateView<N extends ExplorerNode, E extends ExplorerEdge>(view: ExplorerView<N, E>): void;
18
+ /** Validates every view, and that no two share an id. Ids may repeat across views. */
19
+ export declare function validateViews<N extends ExplorerNode, E extends ExplorerEdge>(views: readonly ExplorerView<N, E>[]): void;
20
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI3E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,YAAY,EACzE,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,GACvB,IAAI,CAuIN;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,YAAY,EAC1E,KAAK,EAAE,SAAS,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GACnC,IAAI,CASN"}
@@ -0,0 +1,99 @@
1
+ import { ExplorerDataError } from './errors.js';
2
+ import { resolveNodeSize } from './size.js';
3
+ const positive = (value) => Number.isFinite(value) && value > 0;
4
+ /**
5
+ * Throws {@link ExplorerDataError} for a view that cannot be drawn honestly.
6
+ *
7
+ * Sizes are checked here rather than left to the layout engine, which accepts
8
+ * a zero-size node and reports `NaN` as a fault in its own config. Neither
9
+ * tells the caller which node to fix.
10
+ *
11
+ * Layout options are checked for the same reason: the engine rejects a bad
12
+ * spacing with an error that names no view, the renderer throws a bare
13
+ * `RangeError` for an unknown edge style, and an unknown direction would be
14
+ * read as `'down'` without a word.
15
+ *
16
+ * A self loop and parallel edges are valid data. What happens to them is
17
+ * `layout.ts`'s business.
18
+ */
19
+ export function validateView(view) {
20
+ const where = `view "${view.id}"`;
21
+ if (view.id === '') {
22
+ throw new ExplorerDataError('INVALID_ID', 'A view has an empty id. Ids must not be empty', '');
23
+ }
24
+ const layoutOptions = view.layout;
25
+ if (layoutOptions !== undefined) {
26
+ for (const key of ['nodeSep', 'rankSep']) {
27
+ const value = layoutOptions[key];
28
+ if (value !== undefined && !(Number.isFinite(value) && value >= 0)) {
29
+ throw new ExplorerDataError('INVALID_LAYOUT_OPTION', `View "${view.id}" has layout.${key} ${String(value)}. It must be a finite number that is zero or greater`, view.id);
30
+ }
31
+ }
32
+ const { direction, edgeStyle } = layoutOptions;
33
+ if (direction !== undefined && direction !== 'right' && direction !== 'down') {
34
+ throw new ExplorerDataError('INVALID_LAYOUT_OPTION', `View "${view.id}" has layout.direction ${JSON.stringify(direction)}. It must be "right" or "down"`, view.id);
35
+ }
36
+ if (edgeStyle !== undefined && edgeStyle !== 'smooth' && edgeStyle !== 'orthogonal') {
37
+ throw new ExplorerDataError('INVALID_LAYOUT_OPTION', `View "${view.id}" has layout.edgeStyle ${JSON.stringify(edgeStyle)}. It must be "smooth" or "orthogonal"`, view.id);
38
+ }
39
+ }
40
+ const nodeIds = new Set();
41
+ for (const node of view.nodes) {
42
+ if (node.id === '') {
43
+ throw new ExplorerDataError('INVALID_ID', `A node in view "${view.id}" has an empty id. Ids must not be empty`, '', view.id);
44
+ }
45
+ if (nodeIds.has(node.id)) {
46
+ throw new ExplorerDataError('DUPLICATE_NODE_ID', `Duplicate node id "${node.id}" in ${where}`, node.id, view.id);
47
+ }
48
+ nodeIds.add(node.id);
49
+ const size = resolveNodeSize(view.layout, node);
50
+ if (!positive(size.width) || !positive(size.height)) {
51
+ throw new ExplorerDataError('INVALID_NODE_SIZE', `Node "${node.id}" in ${where} has size ${String(size.width)} by ${String(size.height)}. Width and height must be finite and greater than zero`, node.id, view.id);
52
+ }
53
+ }
54
+ const edgeIds = new Set();
55
+ for (const edge of view.edges) {
56
+ if (edge.id === '') {
57
+ throw new ExplorerDataError('INVALID_ID', `An edge in view "${view.id}" has an empty id. Ids must not be empty`, '', view.id);
58
+ }
59
+ if (edgeIds.has(edge.id)) {
60
+ throw new ExplorerDataError('DUPLICATE_EDGE_ID', `Duplicate edge id "${edge.id}" in ${where}`, edge.id, view.id);
61
+ }
62
+ edgeIds.add(edge.id);
63
+ for (const end of [edge.source, edge.target]) {
64
+ if (!nodeIds.has(end)) {
65
+ throw new ExplorerDataError('MISSING_EDGE_ENDPOINT', `Edge "${edge.id}" in ${where} names missing node "${end}"`, edge.id, view.id);
66
+ }
67
+ }
68
+ }
69
+ const groupIds = new Set();
70
+ for (const group of view.groups ?? []) {
71
+ if (group.id === '') {
72
+ throw new ExplorerDataError('INVALID_ID', `A group in view "${view.id}" has an empty id. Ids must not be empty`, '', view.id);
73
+ }
74
+ if (groupIds.has(group.id)) {
75
+ throw new ExplorerDataError('DUPLICATE_GROUP_ID', `Duplicate group id "${group.id}" in ${where}`, group.id, view.id);
76
+ }
77
+ groupIds.add(group.id);
78
+ if (group.nodeIds.length === 0) {
79
+ throw new ExplorerDataError('EMPTY_GROUP', `Group "${group.id}" in ${where} has no nodes`, group.id, view.id);
80
+ }
81
+ for (const id of group.nodeIds) {
82
+ if (!nodeIds.has(id)) {
83
+ throw new ExplorerDataError('MISSING_GROUP_MEMBER', `Group "${group.id}" in ${where} names missing node "${id}"`, group.id, view.id);
84
+ }
85
+ }
86
+ }
87
+ }
88
+ /** Validates every view, and that no two share an id. Ids may repeat across views. */
89
+ export function validateViews(views) {
90
+ const viewIds = new Set();
91
+ for (const view of views) {
92
+ if (viewIds.has(view.id)) {
93
+ throw new ExplorerDataError('DUPLICATE_VIEW_ID', `Duplicate view id "${view.id}"`, view.id);
94
+ }
95
+ viewIds.add(view.id);
96
+ validateView(view);
97
+ }
98
+ }
99
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The explorer's viewport: a pannable, zoomable surface with a base layer
3
+ * and a windowed DOM overlay, driven by props.
4
+ *
5
+ * Internal. M5.6d wraps it with the root's context as the public
6
+ * `ExplorerViewport` part, which is why everything here arrives as a prop.
7
+ *
8
+ * **React renders only when the visible set changes, or a prop does.**
9
+ * `useExplorerCamera` writes the plane's transform on each frame and calls
10
+ * back here. The callback computes the visible set and keeps the previous
11
+ * one, by reference, when `sameVisibleSet` says nothing changed, so a pan
12
+ * inside the overscan margin is a style write and no React work. A node
13
+ * button is placed in world coordinates once, when it mounts, and the plane
14
+ * carries it.
15
+ *
16
+ * **Most frames skip the scan.** The set was computed over the view plus a
17
+ * margin on every side, so while the view stays inside half that margin, at
18
+ * the same scale, every node it can show is already in the set. A frame
19
+ * like that does not call `computeVisibleSet` at all.
20
+ *
21
+ * **One listener per gesture, on the viewport.** No node has a listener of
22
+ * its own. A click on a button resolves to that node, a click anywhere else
23
+ * resolves through the layout, so a base mark (which has no element the
24
+ * overlay owns) is clickable at every tier.
25
+ *
26
+ * The plane is a composited layer only while the camera moves, which the
27
+ * camera hook decides, and never through `translateZ`. A cached raster of
28
+ * text enlarged by the camera goes blurry, so at rest there is none.
29
+ *
30
+ * **The graph is one tab stop.** Exactly one node button has `tabIndex` 0:
31
+ * the selected node, else the last node focused from the keyboard, else the
32
+ * node nearest the viewport center as of the last scan, else (before there
33
+ * is a camera) the first node. It is pinned, so it is always mounted, and so
34
+ * is the node that has focus and the node an arrow is moving focus to. An
35
+ * arrow on a focused node moves focus to `nearestInDirection`: the target is
36
+ * pinned, mounted, focused, and only then does the old node lose its pin, so
37
+ * focus never falls to the page in between. A node focused from the keyboard
38
+ * is revealed by the least pan; one focused by a pointer moves nothing.
39
+ *
40
+ * **Nothing reads the DOM in render,** so the surface renders on a server:
41
+ * the base draws every mark, the plane is hidden, and no node has a button.
42
+ */
43
+ import type { CSSProperties, MutableRefObject, ReactElement, ReactNode } from 'react';
44
+ import type { ExplorerBase, ExplorerCameraSource } from './base.js';
45
+ import type { ExplorerLayout } from './layout.js';
46
+ import type { ExplorerEdge, ExplorerGroup, ExplorerNode, ExplorerView } from './types.js';
47
+ import type { ExplorerCameraControls } from './use-explorer-camera.js';
48
+ import type { ExplorerTier, ExplorerTiers } from './visible-set.js';
49
+ export interface ViewportSurfaceProps<N extends ExplorerNode, E extends ExplorerEdge> {
50
+ /** Accessible name of the region. */
51
+ readonly label: string;
52
+ readonly view: ExplorerView<N, E>;
53
+ readonly layout: ExplorerLayout;
54
+ readonly selectedId?: string | null | undefined;
55
+ readonly dimmed?: ReadonlySet<string> | undefined;
56
+ /** Always mounted. `selectedId` is pinned as well. */
57
+ readonly pinned?: readonly string[] | undefined;
58
+ readonly tiers?: ExplorerTiers | undefined;
59
+ readonly maxOverlayNodes?: number | undefined;
60
+ /** Default: the SVG base. */
61
+ readonly base?: ExplorerBase | undefined;
62
+ readonly renderNode?: ((node: N, context: {
63
+ tier: ExplorerTier;
64
+ selected: boolean;
65
+ dimmed: boolean;
66
+ }) => ReactNode) | undefined;
67
+ readonly nodeAriaLabel?: ((node: N, context: {
68
+ groups: readonly ExplorerGroup[];
69
+ }) => string) | undefined;
70
+ /** One group in the default accessible name. Default: `in <group>`. */
71
+ readonly inGroup?: ((groupLabel: string) => string) | undefined;
72
+ /** Click, Enter, Space. `trigger` is the node's button, or `null` for a mark. */
73
+ readonly onNodeActivate?: ((id: string, trigger: HTMLElement | null) => void) | undefined;
74
+ /** Double click. */
75
+ readonly onNodeZoom?: ((id: string) => void) | undefined;
76
+ readonly controlsRef?: MutableRefObject<ExplorerCameraControls | null> | undefined;
77
+ /**
78
+ * The camera on screen and every drawn frame, the same source the base
79
+ * gets, for a part outside the viewport that follows the camera without
80
+ * re-rendering (the toolbar's zoom readout).
81
+ */
82
+ readonly cameraSourceRef?: MutableRefObject<ExplorerCameraSource | null> | undefined;
83
+ /** The id of the element that describes the region. */
84
+ readonly describedBy?: string | undefined;
85
+ readonly className?: string | undefined;
86
+ /** Sizing and decoration pass through. `position` and `overflow` stay the viewport's own, because the graph needs them. */
87
+ readonly style?: CSSProperties | undefined;
88
+ }
89
+ export declare function ViewportSurface<N extends ExplorerNode, E extends ExplorerEdge>(props: ViewportSurfaceProps<N, E>): ReactElement;
90
+ //# sourceMappingURL=viewport-surface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport-surface.d.ts","sourceRoot":"","sources":["../src/viewport-surface.tsx"],"names":[],"mappings":"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"}