@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,44 @@
1
+ /**
2
+ * `ExplorerDetails`: the drawer that shows the inspected node.
3
+ *
4
+ * It renders nothing while closed. Open, it is an overlay at every width,
5
+ * positioned against the nearest positioned ancestor (the viewport's stage
6
+ * when it is `ExplorerViewport`'s child, else the root or a host's own
7
+ * wrapper), so opening it never resizes the graph.
8
+ *
9
+ * The body is a keyboard scroll stop and scrolls to the top when the
10
+ * inspected node changes. Its content is the slot's, or by default the
11
+ * node's label and its connections as buttons, each inspecting the node at
12
+ * the other end. Those buttons keep the drawer's original opener, so
13
+ * `Escape` after following three edges still returns where the reader began.
14
+ *
15
+ * Closing restores focus, and the root does it, because every way the drawer
16
+ * closes (this part's `Escape` and close button, the api, the selected node
17
+ * leaving the data) ends in the same place. See `ExplorerRoot`.
18
+ */
19
+ import type { CSSProperties, ReactElement, ReactNode } from 'react';
20
+ import type { ExplorerEdge, ExplorerNode } from './types.js';
21
+ /** An edge touching the inspected node, and the node at its other end. */
22
+ export interface ExplorerConnection<N extends ExplorerNode = ExplorerNode, E extends ExplorerEdge = ExplorerEdge> {
23
+ readonly edge: E;
24
+ /** The other end. For a self loop, the node itself. */
25
+ readonly node: N;
26
+ }
27
+ /** What the drawer's slot receives. */
28
+ export interface ExplorerDetailsContext<N extends ExplorerNode = ExplorerNode, E extends ExplorerEdge = ExplorerEdge> {
29
+ readonly node: N;
30
+ /** Every edge touching `node`, in data order. */
31
+ readonly connections: readonly ExplorerConnection<N, E>[];
32
+ /** Inspects another node, keeping the drawer's opener. */
33
+ readonly inspect: (id: string) => void;
34
+ }
35
+ export interface ExplorerDetailsProps<N extends ExplorerNode = ExplorerNode, E extends ExplorerEdge = ExplorerEdge> {
36
+ /** The drawer's content. Default: the label and the connection list. */
37
+ readonly children?: ((context: ExplorerDetailsContext<N, E>) => ReactNode) | undefined;
38
+ /** One connection's content in the default list. Default: the other node's label. */
39
+ readonly renderConnection?: ((edge: E, otherNode: N) => ReactNode) | undefined;
40
+ readonly className?: string | undefined;
41
+ readonly style?: CSSProperties | undefined;
42
+ }
43
+ export declare function ExplorerDetails<N extends ExplorerNode = ExplorerNode, E extends ExplorerEdge = ExplorerEdge>(props: ExplorerDetailsProps<N, E>): ReactElement | null;
44
+ //# sourceMappingURL=explorer-details.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-details.d.ts","sourceRoot":"","sources":["../src/explorer-details.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnF,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE7D,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,CAAC,SAAS,YAAY,GAAG,YAAY;IAC9G,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;CAClB;AAED,uCAAuC;AACvC,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,CAAC,SAAS,YAAY,GAAG,YAAY;IAClH,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,iDAAiD;IACjD,QAAQ,CAAC,WAAW,EAAE,SAAS,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAC1D,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,CAAC,SAAS,YAAY,GAAG,YAAY;IAChH,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IACvF,qFAAqF;IACrF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IAC/E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CAC5C;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,CAAC,SAAS,YAAY,GAAG,YAAY,EAC1G,KAAK,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,GAChC,YAAY,GAAG,IAAI,CAwGrB"}
@@ -0,0 +1,76 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * `ExplorerDetails`: the drawer that shows the inspected node.
4
+ *
5
+ * It renders nothing while closed. Open, it is an overlay at every width,
6
+ * positioned against the nearest positioned ancestor (the viewport's stage
7
+ * when it is `ExplorerViewport`'s child, else the root or a host's own
8
+ * wrapper), so opening it never resizes the graph.
9
+ *
10
+ * The body is a keyboard scroll stop and scrolls to the top when the
11
+ * inspected node changes. Its content is the slot's, or by default the
12
+ * node's label and its connections as buttons, each inspecting the node at
13
+ * the other end. Those buttons keep the drawer's original opener, so
14
+ * `Escape` after following three edges still returns where the reader began.
15
+ *
16
+ * Closing restores focus, and the root does it, because every way the drawer
17
+ * closes (this part's `Escape` and close button, the api, the selected node
18
+ * leaving the data) ends in the same place. See `ExplorerRoot`.
19
+ */
20
+ import { useEffect, useId, useMemo, useRef } from 'react';
21
+ import { useExplorerContext } from './context.js';
22
+ export function ExplorerDetails(props) {
23
+ const { children, renderConnection, className, style } = props;
24
+ const { state } = useExplorerContext('ExplorerDetails');
25
+ const { labels, activeView, detailsOpen } = state;
26
+ // The part's type parameters are a claim about the root's data. See context.ts.
27
+ const node = detailsOpen ? state.selectedNode : null;
28
+ const nodeId = node?.id ?? null;
29
+ const titleId = useId();
30
+ const connectionsId = useId();
31
+ const bodyRef = useRef(null);
32
+ useEffect(() => {
33
+ if (nodeId !== null && bodyRef.current !== null)
34
+ bodyRef.current.scrollTop = 0;
35
+ }, [nodeId]);
36
+ const connections = useMemo(() => {
37
+ if (nodeId === null || activeView === null)
38
+ return [];
39
+ const byId = new Map(activeView.nodes.map((each) => [each.id, each]));
40
+ const list = [];
41
+ for (const edge of activeView.edges) {
42
+ if (edge.source !== nodeId && edge.target !== nodeId)
43
+ continue;
44
+ const other = byId.get(edge.source === nodeId ? edge.target : edge.source);
45
+ if (other !== undefined)
46
+ list.push({ edge, node: other });
47
+ }
48
+ return list;
49
+ }, [nodeId, activeView]);
50
+ if (node === null)
51
+ return null;
52
+ // No trigger, and the drawer is open: the root keeps the opener it has.
53
+ const inspect = (id) => state.inspect(id);
54
+ const onKeyDown = (event) => {
55
+ if (event.key !== 'Escape')
56
+ return;
57
+ event.preventDefault();
58
+ state.closeDetails();
59
+ };
60
+ return (_jsxs("aside", { "data-dagr-explorer": "details", "aria-labelledby": titleId, className: className, onKeyDown: onKeyDown, style: {
61
+ position: 'absolute',
62
+ top: 0,
63
+ right: 0,
64
+ bottom: 0,
65
+ width: 'min(360px, 100%)',
66
+ display: 'flex',
67
+ flexDirection: 'column',
68
+ zIndex: 1,
69
+ // The stylesheet's fallback, so the drawer looks the same with or without it.
70
+ background: 'var(--dagr-explorer-bg, #ffffff)',
71
+ ...style,
72
+ }, children: [_jsxs("div", { "data-dagr-explorer": "details-header", style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', flex: 'none' }, children: [_jsx("span", { id: titleId, children: labels.drawerTitle }), _jsx("button", { type: "button", "data-dagr-explorer": "details-close", onClick: () => state.closeDetails(), children: labels.close })] }), _jsx("div", { ref: bodyRef, "data-dagr-explorer": "details-body", tabIndex: 0,
73
+ // A keyboard scroll stop is announced, so it needs a name: the drawer's title.
74
+ role: "region", "aria-labelledby": titleId, style: { overflow: 'auto', flex: '1 1 auto', minHeight: 0 }, children: children !== undefined ? (children({ node, connections, inspect })) : (_jsxs(_Fragment, { children: [_jsx("p", { "data-dagr-explorer": "details-label", children: node.label }), connections.length === 0 ? null : (_jsxs(_Fragment, { children: [_jsx("p", { id: connectionsId, children: labels.connections }), _jsx("ul", { "data-dagr-explorer": "connections", "aria-labelledby": connectionsId, style: { listStyle: 'none', margin: 0, padding: 0 }, children: connections.map(({ edge, node: other }) => (_jsx("li", { children: _jsx("button", { type: "button", "data-node-id": other.id, onClick: () => inspect(other.id), children: renderConnection === undefined ? other.label : renderConnection(edge, other) }) }, edge.id))) })] }))] })) })] }));
75
+ }
76
+ //# sourceMappingURL=explorer-details.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-details.js","sourceRoot":"","sources":["../src/explorer-details.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `ExplorerSearch`: a labeled field, a live match count, and the matches as
3
+ * a list of buttons.
4
+ *
5
+ * Search is the complete way to every node. A virtualized node has no
6
+ * element, so the graph alone cannot reach it by keyboard or screen reader,
7
+ * and search can: every match is counted, and `Enter` reaches the first.
8
+ *
9
+ * **The list is capped,** at `maxResults` (default 50), in data order, with
10
+ * a plain line from `labels.moreMatches` for the rest, so a short query on a
11
+ * large graph does not mount thousands of buttons. A longer query narrows
12
+ * it. The list is memoized on the matches, which the root keeps by identity
13
+ * while the same nodes match.
14
+ *
15
+ * `Enter` inspects the first match and flies the camera to it, and choosing
16
+ * a result does the same for that result. The list stays mounted while a
17
+ * node is inspected, so its scroll position survives.
18
+ *
19
+ * `Escape` in the field closes the drawer if it is open, and only then
20
+ * clears the query, so a reader who opened a result from here can close it
21
+ * without losing the list.
22
+ */
23
+ import type { CSSProperties, ReactElement } from 'react';
24
+ export interface ExplorerSearchProps {
25
+ /** The most results listed, in data order. Default 50. The count and `Enter` cover every match. */
26
+ readonly maxResults?: number | undefined;
27
+ readonly className?: string | undefined;
28
+ readonly style?: CSSProperties | undefined;
29
+ }
30
+ export declare function ExplorerSearch(props: ExplorerSearchProps): ReactElement;
31
+ //# sourceMappingURL=explorer-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-search.d.ts","sourceRoot":"","sources":["../src/explorer-search.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAC;AAMxE,MAAM,WAAW,mBAAmB;IAClC,mGAAmG;IACnG,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CAC5C;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,YAAY,CAsGvE"}
@@ -0,0 +1,86 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `ExplorerSearch`: a labeled field, a live match count, and the matches as
4
+ * a list of buttons.
5
+ *
6
+ * Search is the complete way to every node. A virtualized node has no
7
+ * element, so the graph alone cannot reach it by keyboard or screen reader,
8
+ * and search can: every match is counted, and `Enter` reaches the first.
9
+ *
10
+ * **The list is capped,** at `maxResults` (default 50), in data order, with
11
+ * a plain line from `labels.moreMatches` for the rest, so a short query on a
12
+ * large graph does not mount thousands of buttons. A longer query narrows
13
+ * it. The list is memoized on the matches, which the root keeps by identity
14
+ * while the same nodes match.
15
+ *
16
+ * `Enter` inspects the first match and flies the camera to it, and choosing
17
+ * a result does the same for that result. The list stays mounted while a
18
+ * node is inspected, so its scroll position survives.
19
+ *
20
+ * `Escape` in the field closes the drawer if it is open, and only then
21
+ * clears the query, so a reader who opened a result from here can close it
22
+ * without losing the list.
23
+ */
24
+ import { useEffect, useId, useMemo, useRef } from 'react';
25
+ import { useExplorerContext } from './context.js';
26
+ /** How many results the list shows before it says how many more there are. */
27
+ const DEFAULT_MAX_RESULTS = 50;
28
+ export function ExplorerSearch(props) {
29
+ const { state, internals } = useExplorerContext('ExplorerSearch');
30
+ const { labels, query, matches, detailsOpen, selectedId, inspect, focusNode } = state;
31
+ const { maxResults } = props;
32
+ const cap = maxResults !== undefined && Number.isFinite(maxResults) && maxResults >= 0
33
+ ? Math.floor(maxResults)
34
+ : DEFAULT_MAX_RESULTS;
35
+ const inputId = useId();
36
+ const countId = useId();
37
+ const inputRef = useRef(null);
38
+ // The field is where focus goes when the drawer closes and its opener is gone.
39
+ useEffect(() => {
40
+ const field = inputRef.current;
41
+ internals.searchInputRef.current = field;
42
+ return () => {
43
+ if (internals.searchInputRef.current === field)
44
+ internals.searchInputRef.current = null;
45
+ };
46
+ }, [internals]);
47
+ const blank = query.trim() === '';
48
+ // `inspect` and `focusNode` are the root's stable methods.
49
+ const pick = (id, trigger) => {
50
+ inspect(id, trigger);
51
+ focusNode(id);
52
+ };
53
+ const onKeyDown = (event) => {
54
+ if (event.key === 'Enter') {
55
+ const first = matches[0];
56
+ if (first === undefined)
57
+ return;
58
+ event.preventDefault();
59
+ pick(first.id, event.currentTarget);
60
+ }
61
+ else if (event.key === 'Escape') {
62
+ if (detailsOpen)
63
+ state.closeDetails();
64
+ else if (query !== '')
65
+ state.setQuery('');
66
+ else
67
+ return;
68
+ // A search field clears itself on Escape in some browsers, which would
69
+ // skip the drawer's turn.
70
+ event.preventDefault();
71
+ }
72
+ };
73
+ const { searchResults, moreMatches } = labels;
74
+ const list = useMemo(() => {
75
+ if (matches.length === 0)
76
+ return null;
77
+ const shown = cap < matches.length ? matches.slice(0, cap) : matches;
78
+ const more = matches.length - shown.length;
79
+ return (_jsxs(_Fragment, { children: [shown.length === 0 ? null : (_jsx("ul", { "data-dagr-explorer": "search-results", "aria-label": searchResults, style: { listStyle: 'none', margin: 0, padding: 0, overflow: 'auto' }, children: shown.map((node) => (_jsx("li", { children: _jsx("button", { type: "button", "data-node-id": node.id, "data-selected": node.id === selectedId ? 'true' : undefined, "aria-current": node.id === selectedId ? 'true' : undefined, onClick: (event) => {
80
+ inspect(node.id, event.currentTarget);
81
+ focusNode(node.id);
82
+ }, children: node.label }) }, node.id))) })), more === 0 ? null : _jsx("p", { "data-dagr-explorer": "search-more", children: moreMatches(more) })] }));
83
+ }, [matches, cap, selectedId, searchResults, moreMatches, inspect, focusNode]);
84
+ return (_jsxs("div", { "data-dagr-explorer": "search", role: "search", className: props.className, style: props.style, children: [_jsx("label", { htmlFor: inputId, children: labels.search }), _jsx("input", { ref: inputRef, id: inputId, type: "search", value: query, placeholder: labels.searchPlaceholder, autoComplete: "off", spellCheck: false, "aria-describedby": countId, onChange: (event) => state.setQuery(event.currentTarget.value), onKeyDown: onKeyDown }), _jsx("p", { id: countId, "data-dagr-explorer": "search-count", "aria-live": "polite", children: blank ? '' : labels.matches(matches.length) }), list] }));
85
+ }
86
+ //# sourceMappingURL=explorer-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-search.js","sourceRoot":"","sources":["../src/explorer-search.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `ExplorerToolbar`: zoom out, the zoom readout, zoom in, fit, and zoom to
3
+ * the selected node.
4
+ *
5
+ * **The readout follows the camera without a render.** It subscribes to the
6
+ * root's camera source and writes its own text when the whole percent
7
+ * changes, so a zoom that runs for twenty frames re-renders nothing. React
8
+ * renders the element with no children, so it never overwrites that text.
9
+ * It is not a live region: announcing every frame of a zoom is noise.
10
+ */
11
+ import type { CSSProperties, ReactElement } from 'react';
12
+ export interface ExplorerToolbarProps {
13
+ readonly className?: string | undefined;
14
+ readonly style?: CSSProperties | undefined;
15
+ }
16
+ export declare function ExplorerToolbar(props: ExplorerToolbarProps): ReactElement;
17
+ //# sourceMappingURL=explorer-toolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-toolbar.d.ts","sourceRoot":"","sources":["../src/explorer-toolbar.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAQzD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CAC5C;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,CAmDzE"}
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `ExplorerToolbar`: zoom out, the zoom readout, zoom in, fit, and zoom to
4
+ * the selected node.
5
+ *
6
+ * **The readout follows the camera without a render.** It subscribes to the
7
+ * root's camera source and writes its own text when the whole percent
8
+ * changes, so a zoom that runs for twenty frames re-renders nothing. React
9
+ * renders the element with no children, so it never overwrites that text.
10
+ * It is not a live region: announcing every frame of a zoom is noise.
11
+ */
12
+ import { useEffect, useRef } from 'react';
13
+ import { useExplorerContext } from './context.js';
14
+ /** The same steps as the `+` and `-` keys. */
15
+ const ZOOM_IN = 1.25;
16
+ const ZOOM_OUT = 0.8;
17
+ export function ExplorerToolbar(props) {
18
+ const { state } = useExplorerContext('ExplorerToolbar');
19
+ const { labels, selectedNode, camera } = state;
20
+ const readoutRef = useRef(null);
21
+ const { zoomLevel } = labels;
22
+ useEffect(() => {
23
+ const readout = readoutRef.current;
24
+ if (readout === null)
25
+ return undefined;
26
+ let shown = null;
27
+ const write = (now) => {
28
+ if (now === null)
29
+ return;
30
+ const percent = Math.round(now.scale * 100);
31
+ if (percent === shown)
32
+ return;
33
+ shown = percent;
34
+ readout.textContent = zoomLevel(percent);
35
+ };
36
+ write(camera.get());
37
+ return camera.subscribe(write);
38
+ }, [camera, zoomLevel]);
39
+ return (_jsxs("div", { "data-dagr-explorer": "toolbar", role: "group", "aria-label": labels.zoomControls, className: props.className, style: props.style, children: [_jsx("button", { type: "button", "data-action": "zoom-out", onClick: () => state.zoomBy(ZOOM_OUT), children: labels.zoomOut }), _jsx("span", { ref: readoutRef, "data-dagr-explorer": "zoom-level" }), _jsx("button", { type: "button", "data-action": "zoom-in", onClick: () => state.zoomBy(ZOOM_IN), children: labels.zoomIn }), _jsx("button", { type: "button", "data-action": "fit", onClick: () => state.fit(), children: labels.fit }), _jsx("button", { type: "button", "data-action": "zoom-to-selected", disabled: selectedNode === null, onClick: () => {
40
+ if (selectedNode !== null)
41
+ state.focusNode(selectedNode.id);
42
+ }, children: selectedNode === null ? labels.zoomToSelected : labels.zoomTo(selectedNode.label) })] }));
43
+ }
44
+ //# sourceMappingURL=explorer-toolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-toolbar.js","sourceRoot":"","sources":["../src/explorer-toolbar.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,8CAA8C;AAC9C,MAAM,OAAO,GAAG,IAAI,CAAC;AACrB,MAAM,QAAQ,GAAG,GAAG,CAAC;AAOrB,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,MAAM,EAAE,KAAK,EAAE,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAC;IACxD,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAC/C,MAAM,UAAU,GAAG,MAAM,CAAkB,IAAI,CAAC,CAAC;IACjD,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;QACnC,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QACvC,IAAI,KAAK,GAAkB,IAAI,CAAC;QAChC,MAAM,KAAK,GAAG,CAAC,GAA0B,EAAQ,EAAE;YACjD,IAAI,GAAG,KAAK,IAAI;gBAAE,OAAO;YACzB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;YAC5C,IAAI,OAAO,KAAK,KAAK;gBAAE,OAAO;YAC9B,KAAK,GAAG,OAAO,CAAC;YAChB,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC,CAAC;QACF,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;QACpB,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAExB,OAAO,CACL,qCACqB,SAAS,EAC5B,IAAI,EAAC,OAAO,gBACA,MAAM,CAAC,YAAY,EAC/B,SAAS,EAAE,KAAK,CAAC,SAAS,EAC1B,KAAK,EAAE,KAAK,CAAC,KAAK,aAElB,iBAAQ,IAAI,EAAC,QAAQ,iBAAa,UAAU,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,YAC/E,MAAM,CAAC,OAAO,GACR,EACT,eAAM,GAAG,EAAE,UAAU,wBAAqB,YAAY,GAAG,EACzD,iBAAQ,IAAI,EAAC,QAAQ,iBAAa,SAAS,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,YAC7E,MAAM,CAAC,MAAM,GACP,EACT,iBAAQ,IAAI,EAAC,QAAQ,iBAAa,KAAK,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,YAC/D,MAAM,CAAC,GAAG,GACJ,EACT,iBACE,IAAI,EAAC,QAAQ,iBACD,kBAAkB,EAC9B,QAAQ,EAAE,YAAY,KAAK,IAAI,EAC/B,OAAO,EAAE,GAAG,EAAE;oBACZ,IAAI,YAAY,KAAK,IAAI;wBAAE,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBAC9D,CAAC,YAEA,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,GAC3E,IACL,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `ExplorerTraceToggle`: trace on and off. With trace on and a node
3
+ * selected, every node not adjacent to it is dimmed.
4
+ *
5
+ * The text names what a press does (`labels.traceOn` while off), so the
6
+ * button is not also `aria-pressed`: a toggle whose name flips and that
7
+ * reports a pressed state reads its state twice. `data-active` is the
8
+ * styling hook.
9
+ */
10
+ import type { CSSProperties, ReactElement } from 'react';
11
+ export interface ExplorerTraceToggleProps {
12
+ readonly className?: string | undefined;
13
+ readonly style?: CSSProperties | undefined;
14
+ }
15
+ export declare function ExplorerTraceToggle(props: ExplorerTraceToggleProps): ReactElement;
16
+ //# sourceMappingURL=explorer-trace-toggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-trace-toggle.d.ts","sourceRoot":"","sources":["../src/explorer-trace-toggle.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAGzD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CAC5C;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,YAAY,CAejF"}
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useExplorerContext } from './context.js';
3
+ export function ExplorerTraceToggle(props) {
4
+ const { state } = useExplorerContext('ExplorerTraceToggle');
5
+ const { trace, labels } = state;
6
+ return (_jsx("button", { type: "button", "data-dagr-explorer": "trace", "data-active": trace ? 'true' : undefined, className: props.className, style: props.style, onClick: () => state.setTrace(!trace), children: trace ? labels.traceOff : labels.traceOn }));
7
+ }
8
+ //# sourceMappingURL=explorer-trace-toggle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-trace-toggle.js","sourceRoot":"","sources":["../src/explorer-trace-toggle.tsx"],"names":[],"mappings":";AAWA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAOlD,MAAM,UAAU,mBAAmB,CAAC,KAA+B;IACjE,MAAM,EAAE,KAAK,EAAE,GAAG,kBAAkB,CAAC,qBAAqB,CAAC,CAAC;IAC5D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAChC,OAAO,CACL,iBACE,IAAI,EAAC,QAAQ,wBACM,OAAO,iBACb,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EACvC,SAAS,EAAE,KAAK,CAAC,SAAS,EAC1B,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,YAEpC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,GAClC,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * `ExplorerViewport`: the graph surface, wired to the root.
3
+ *
4
+ * It is the internal `ViewportSurface` with its props filled from the root:
5
+ * the active view and its layout, the selection, the dimming, and the
6
+ * activation callbacks (a click inspects, a double click flies to the node).
7
+ * The camera controls and the camera source go back up to the root, so
8
+ * `ExplorerApi`'s camera methods and the toolbar's readout reach them.
9
+ *
10
+ * **One per root, counted in an effect.** The viewport registers with its
11
+ * root in an effect with cleanup, and the second registration throws.
12
+ * Counting in render would misfire under StrictMode's double render, and
13
+ * when Suspense keeps an old tree alive beside a new one.
14
+ *
15
+ * **A view switch remounts the surface,** keyed by view id. That is the
16
+ * camera reset the spec asks of a switch: a new surface fits its layout on
17
+ * its first measure, with no flight from a camera framed on other content.
18
+ *
19
+ * **The surface renders only for what it shows.** It is memoized, and given
20
+ * only the state it draws (the view, the layout, the selection, the dimming)
21
+ * and the root's stable methods, so a keystroke that changes neither the
22
+ * dimming nor the selection does not render the graph.
23
+ *
24
+ * **A stage for overlays.** The surface sits in a positioned element with
25
+ * the part's children, such as `ExplorerDetails`, which position against it.
26
+ * The hint, the graph's description, comes after the stage, so an overlay
27
+ * never covers it.
28
+ */
29
+ import type { CSSProperties, ReactElement, ReactNode } from 'react';
30
+ import type { ExplorerBase } from './base.js';
31
+ import type { ExplorerEdge, ExplorerGroup, ExplorerNode } from './types.js';
32
+ import type { ExplorerTier, ExplorerTiers } from './visible-set.js';
33
+ /** Only the node type: nothing here takes an edge. */
34
+ export interface ExplorerViewportProps<N extends ExplorerNode = ExplorerNode> {
35
+ /**
36
+ * A node's content, for nodes large enough on screen to read. It sits
37
+ * inside the explorer's own node button, so it must not be interactive.
38
+ * Default: the label.
39
+ */
40
+ readonly renderNode?: ((node: N, context: {
41
+ readonly tier: ExplorerTier;
42
+ readonly selected: boolean;
43
+ readonly dimmed: boolean;
44
+ }) => ReactNode) | undefined;
45
+ /** A node's accessible name. Default: its label, then `labels.inGroup(group)` for each group. */
46
+ readonly nodeAriaLabel?: ((node: N, context: {
47
+ readonly groups: readonly ExplorerGroup[];
48
+ }) => string) | undefined;
49
+ /** On-screen widths, in CSS pixels, at which a node becomes a summary and then rich. Default 56 and 200. */
50
+ readonly tiers?: ExplorerTiers | undefined;
51
+ /** The most node elements mounted at once, before pins. Default 200. */
52
+ readonly maxOverlayNodes?: number | undefined;
53
+ /** What draws the nodes that have no element. Default: the SVG base. Experimental. */
54
+ readonly base?: ExplorerBase | undefined;
55
+ readonly className?: string | undefined;
56
+ /**
57
+ * Sizing and decoration pass through: `{ height: 600 }` replaces
58
+ * `--dagr-explorer-height`. `position` and `overflow` stay the viewport's
59
+ * own, because the graph needs them.
60
+ */
61
+ readonly style?: CSSProperties | undefined;
62
+ /**
63
+ * Overlays positioned against the graph, such as `ExplorerDetails`. They
64
+ * share a positioned stage with the graph, and the hint comes after it.
65
+ */
66
+ readonly children?: ReactNode;
67
+ }
68
+ export declare function ExplorerViewport<N extends ExplorerNode = ExplorerNode, E extends ExplorerEdge = ExplorerEdge>(props: ExplorerViewportProps<N>): ReactElement;
69
+ //# sourceMappingURL=explorer-viewport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-viewport.d.ts","sourceRoot":"","sources":["../src/explorer-viewport.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAwB,MAAM,WAAW,CAAC;AAIpE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAgB,MAAM,YAAY,CAAC;AAE1F,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEpE,sDAAsD;AACtD,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY;IAC1E;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAChB,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;KAAE,KAAK,SAAS,CAAC,GACxH,SAAS,CAAC;IACd,iGAAiG;IACjG,QAAQ,CAAC,aAAa,CAAC,EACnB,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAA;KAAE,KAAK,MAAM,CAAC,GAC7E,SAAS,CAAC;IACd,4GAA4G;IAC5G,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC3C,wEAAwE;IACxE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC3C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC/B;AA6CD,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,CAAC,SAAS,YAAY,GAAG,YAAY,EAC3G,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAC9B,YAAY,CA8Dd"}
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * `ExplorerViewport`: the graph surface, wired to the root.
4
+ *
5
+ * It is the internal `ViewportSurface` with its props filled from the root:
6
+ * the active view and its layout, the selection, the dimming, and the
7
+ * activation callbacks (a click inspects, a double click flies to the node).
8
+ * The camera controls and the camera source go back up to the root, so
9
+ * `ExplorerApi`'s camera methods and the toolbar's readout reach them.
10
+ *
11
+ * **One per root, counted in an effect.** The viewport registers with its
12
+ * root in an effect with cleanup, and the second registration throws.
13
+ * Counting in render would misfire under StrictMode's double render, and
14
+ * when Suspense keeps an old tree alive beside a new one.
15
+ *
16
+ * **A view switch remounts the surface,** keyed by view id. That is the
17
+ * camera reset the spec asks of a switch: a new surface fits its layout on
18
+ * its first measure, with no flight from a camera framed on other content.
19
+ *
20
+ * **The surface renders only for what it shows.** It is memoized, and given
21
+ * only the state it draws (the view, the layout, the selection, the dimming)
22
+ * and the root's stable methods, so a keystroke that changes neither the
23
+ * dimming nor the selection does not render the graph.
24
+ *
25
+ * **A stage for overlays.** The surface sits in a positioned element with
26
+ * the part's children, such as `ExplorerDetails`, which position against it.
27
+ * The hint, the graph's description, comes after the stage, so an overlay
28
+ * never covers it.
29
+ */
30
+ import { memo, useEffect, useId, useRef } from 'react';
31
+ import { useExplorerApiContext, useExplorerContext } from './context.js';
32
+ import { ViewportSurface } from './viewport-surface.js';
33
+ /**
34
+ * One surface for one view, attached to the root's camera hub while it lives.
35
+ * Memoized: every prop is stable unless what it draws changes.
36
+ */
37
+ const ViewportPane = memo(function ViewportPane(props) {
38
+ const { api, internals, describedBy, ...rest } = props;
39
+ const sourceRef = useRef(null);
40
+ // The surface's own effects have run by now, so its source is in the ref.
41
+ useEffect(() => {
42
+ const source = sourceRef.current;
43
+ return source === null ? undefined : internals.camera.attach(source);
44
+ }, [internals]);
45
+ return (_jsx(ViewportSurface, { ...rest, onNodeActivate: (id, trigger) => api.inspect(id, trigger), onNodeZoom: (id) => api.focusNode(id), controlsRef: internals.controlsRef, cameraSourceRef: sourceRef, describedBy: describedBy }));
46
+ });
47
+ const STAGE = { position: 'relative' };
48
+ export function ExplorerViewport(props) {
49
+ const { children, ...rest } = props;
50
+ const { state, internals } = useExplorerContext('ExplorerViewport');
51
+ const api = useExplorerApiContext('ExplorerViewport');
52
+ const describedBy = useId();
53
+ useEffect(() => internals.registerViewport(), [internals]);
54
+ // The part's type parameters are a claim about the root's data. See context.ts.
55
+ const view = state.activeView;
56
+ const { layout, labels } = state;
57
+ const empty = view === null || layout === null || view.nodes.length === 0;
58
+ // One shape for both states, so the stage and its children survive a view
59
+ // that empties and fills again.
60
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { "data-dagr-explorer": "stage", style: STAGE, children: [empty ? (_jsx("div", { "data-dagr-explorer": "viewport", "data-empty": "true", role: "region", "aria-label": state.label, className: rest.className, style: {
61
+ position: 'relative',
62
+ height: 'var(--dagr-explorer-height, 480px)',
63
+ display: 'flex',
64
+ alignItems: 'center',
65
+ justifyContent: 'center',
66
+ ...rest.style,
67
+ }, children: view === null ? labels.noViews : labels.emptyView })) : (_jsx(ViewportPane, { ...rest, label: state.label, inGroup: labels.inGroup, view: view, layout: layout, selectedId: state.selectedId, dimmed: state.dimmed, api: api, internals: internals, describedBy: describedBy }, view.id)), children] }), empty ? null : (_jsxs("p", { id: describedBy, "data-dagr-explorer": "hint", children: [_jsx("span", { children: labels.stats({ nodes: view.nodes.length, edges: view.edges.length }) }), ' ', _jsx("span", { children: labels.hint })] }))] }));
68
+ }
69
+ //# sourceMappingURL=explorer-viewport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-viewport.js","sourceRoot":"","sources":["../src/explorer-viewport.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `ExplorerViews`: the view switcher. It renders nothing for a single view,
3
+ * slot or not, because there is nothing to switch to.
4
+ *
5
+ * The default is a group of buttons, the active one `aria-pressed`. Buttons
6
+ * rather than tabs: tabs promise arrow-key movement and a tab panel, and the
7
+ * panel here is the whole explorer.
8
+ */
9
+ import type { CSSProperties, ReactElement, ReactNode } from 'react';
10
+ import type { ExplorerEdge, ExplorerNode, ExplorerView } from './types.js';
11
+ /** What the switcher's slot receives. */
12
+ export interface ExplorerViewsContext<N extends ExplorerNode = ExplorerNode, E extends ExplorerEdge = ExplorerEdge> {
13
+ readonly views: readonly ExplorerView<N, E>[];
14
+ readonly activeView: ExplorerView<N, E>;
15
+ readonly selectView: (id: string) => void;
16
+ }
17
+ export interface ExplorerViewsProps<N extends ExplorerNode = ExplorerNode, E extends ExplorerEdge = ExplorerEdge> {
18
+ /** The switcher's content. Default: a button per view. */
19
+ readonly children?: ((context: ExplorerViewsContext<N, E>) => ReactNode) | undefined;
20
+ readonly className?: string | undefined;
21
+ readonly style?: CSSProperties | undefined;
22
+ }
23
+ export declare function ExplorerViews<N extends ExplorerNode = ExplorerNode, E extends ExplorerEdge = ExplorerEdge>(props: ExplorerViewsProps<N, E>): ReactElement | null;
24
+ //# sourceMappingURL=explorer-views.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-views.d.ts","sourceRoot":"","sources":["../src/explorer-views.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3E,yCAAyC;AACzC,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,CAAC,SAAS,YAAY,GAAG,YAAY;IAChH,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxC,QAAQ,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,CAAC,SAAS,YAAY,GAAG,YAAY;IAC9G,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IACrF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CAC5C;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,CAAC,SAAS,YAAY,GAAG,YAAY,EACxG,KAAK,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC9B,YAAY,GAAG,IAAI,CA+BrB"}
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useExplorerContext } from './context.js';
3
+ export function ExplorerViews(props) {
4
+ const { children, className, style } = props;
5
+ const { state } = useExplorerContext('ExplorerViews');
6
+ // The part's type parameters are a claim about the root's data. See context.ts.
7
+ const views = state.views;
8
+ const activeView = state.activeView;
9
+ if (views.length < 2 || activeView === null)
10
+ return null;
11
+ if (children !== undefined) {
12
+ return (_jsx("div", { "data-dagr-explorer": "views", className: className, style: style, children: children({ views, activeView, selectView: state.selectView }) }));
13
+ }
14
+ return (_jsx("div", { "data-dagr-explorer": "views", role: "group", "aria-label": state.labels.views, className: className, style: style, children: views.map((view) => (_jsx("button", { type: "button", "data-view-id": view.id, "aria-pressed": view.id === activeView.id, onClick: () => state.selectView(view.id), children: view.label }, view.id))) }));
15
+ }
16
+ //# sourceMappingURL=explorer-views.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer-views.js","sourceRoot":"","sources":["../src/explorer-views.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAiBlD,MAAM,UAAU,aAAa,CAC3B,KAA+B;IAE/B,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC7C,MAAM,EAAE,KAAK,EAAE,GAAG,kBAAkB,CAAC,eAAe,CAAC,CAAC;IACtD,gFAAgF;IAChF,MAAM,KAAK,GAAG,KAAK,CAAC,KAAiD,CAAC;IACtE,MAAM,UAAU,GAAG,KAAK,CAAC,UAAuC,CAAC;IACjE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAEzD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,CACL,oCAAwB,OAAO,EAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,YAC/D,QAAQ,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,GAC1D,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,oCAAwB,OAAO,EAAC,IAAI,EAAC,OAAO,gBAAa,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,YAC5G,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,iBAEE,IAAI,EAAC,QAAQ,kBACC,IAAI,CAAC,EAAE,kBACP,IAAI,CAAC,EAAE,KAAK,UAAU,CAAC,EAAE,EACvC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,YAEvC,IAAI,CAAC,KAAK,IANN,IAAI,CAAC,EAAE,CAOL,CACV,CAAC,GACE,CACP,CAAC;AACJ,CAAC"}