@modernrelay/orbit-react 0.13.5 → 0.14.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.
- package/dist/{chunk-DGFLU2SN.js → chunk-DTXFQLNP.js} +2 -2
- package/dist/chunk-DTXFQLNP.js.map +1 -0
- package/dist/{chunk-7XB47YST.js → chunk-NPOV5GA3.js} +2 -2
- package/dist/chunk-NPOV5GA3.js.map +1 -0
- package/dist/{chunk-QJ7JXQVK.js → chunk-ODOPSUI7.js} +4 -4
- package/dist/chunk-ODOPSUI7.js.map +1 -0
- package/dist/{chunk-JN7QCMFW.js → chunk-S36LHSNX.js} +2 -2
- package/dist/chunk-S36LHSNX.js.map +1 -0
- package/dist/components/ContextMenu.d.ts +4 -4
- package/dist/components/ContextMenu.js +2 -2
- package/dist/components/ContextMenu.js.map +1 -1
- package/dist/components/Histogram.d.ts +1 -1
- package/dist/components/Histogram.js +3 -3
- package/dist/components/Histogram.js.map +1 -1
- package/dist/components/Inspector.d.ts +20 -20
- package/dist/components/Inspector.js +1 -1
- package/dist/components/Inspector.js.map +1 -1
- package/dist/components/Legend.d.ts +9 -10
- package/dist/components/Legend.js +3 -3
- package/dist/components/Legend.js.map +1 -1
- package/dist/components/Minimap.d.ts +29 -29
- package/dist/components/Minimap.js +1 -1
- package/dist/components/Minimap.js.map +1 -1
- package/dist/components/Navigator.d.ts +24 -24
- package/dist/components/Navigator.js +3 -3
- package/dist/components/Navigator.js.map +1 -1
- package/dist/components/Search.d.ts +28 -28
- package/dist/components/Search.js +3 -3
- package/dist/components/Search.js.map +1 -1
- package/dist/components/SelectionActions.d.ts +3 -3
- package/dist/components/SelectionActions.js +1 -1
- package/dist/components/SelectionActions.js.map +1 -1
- package/dist/components/SimControls.d.ts +15 -16
- package/dist/components/SimControls.js +2 -2
- package/dist/components/SimControls.js.map +1 -1
- package/dist/components/Table.d.ts +4 -4
- package/dist/components/Table.js +2 -2
- package/dist/components/Table.js.map +1 -1
- package/dist/components/Timeline.d.ts +2 -2
- package/dist/components/Timeline.js +3 -3
- package/dist/components/Timeline.js.map +1 -1
- package/dist/components/Toolbar.d.ts +2 -2
- package/dist/components/Toolbar.js +1 -1
- package/dist/components/Toolbar.js.map +1 -1
- package/dist/components/Tooltip.d.ts +16 -16
- package/dist/components/Tooltip.js +1 -1
- package/dist/components/Tooltip.js.map +1 -1
- package/dist/index.d.ts +105 -106
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/{shared-Bj3Gk219.d.ts → shared-n98tkXJe.d.ts} +3 -3
- package/package.json +2 -2
- package/dist/chunk-7XB47YST.js.map +0 -1
- package/dist/chunk-DGFLU2SN.js.map +0 -1
- package/dist/chunk-JN7QCMFW.js.map +0 -1
- package/dist/chunk-QJ7JXQVK.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Inspector/index.tsx"],"names":["neighbors"],"mappings":"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","file":"Inspector.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Inspector` — `<GraphInspector>`, the\n * §16.9 docked detail panel (S11-T13).\n *\n * Subject: the SELECTED-SINGLE node (`store.selection.nodeIds.length === 1`)\n * — the §16.2 node namespace is the focus proxy the store actually publishes.\n * Anything else (empty or multi selection) renders the empty state.\n *\n * Sections:\n * - **Attrs table** — every attr key/value as TEXT NODES (§14 untrusted\n * content; hostile strings render literally); `renderAttrs` replaces it.\n * - **Neighbor list** — click → `instance.focusNode(neighborId)` (camera\n * fly, §16). v0.9 resolution compromise (documented): the public instance\n * surface resolves a 1-hop neighborhood ONLY through `focusNode`'s return\n * value (the same seam the §15.1 navigator uses on Enter) — a passive\n * adjacency read is a post-v0.9 core surface. The section therefore\n * populates from a per-model-revision cache learned by focus operations,\n * with a 'Show neighbors' affordance that focuses the subject to resolve\n * its neighborhood on demand (flying to the inspected node is coherent\n * §15.1 behavior, and it is the user's explicit action). A model change\n * drops the (now stale) cache.\n * - **Quick actions** — wired to the same public mutators as the §16.4\n * context menu: Expand (`expandNode`, busy while pending §9.2), Isolate\n * (1-hop ego hard scope via the `subgraph` path), Pin/Unpin (store pin\n * slice toggle), Hide (`hideNodes`). `quickActions` is a render-prop\n * REPLACEMENT receiving the defaults, so extension is\n * `[...defaultActions, mine]`.\n */\n\nimport { useCallback, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, ReactElement, ReactNode } from 'react';\nimport type { GraphNode, GraphStoreState, NodeId } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\n\nexport interface GraphInspectorAction {\n /** Stable key; default actions use 'expand' | 'isolate' | 'pin' | 'unpin'\n * | 'hide'. */\n id: string;\n /** Rendered as a text node. */\n label: string;\n disabled?: boolean;\n onSelect(): void;\n}\n\nexport interface GraphInspectorProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Docking side. Default 'right'. */\n dock?: 'left' | 'right';\n /** Replaces the default attrs table. */\n renderAttrs?: (node: GraphNode<any>) => ReactNode;\n /** Render-prop REPLACEMENT for the quick-action row; receives the default\n * actions so extension is `[...defaultActions, mine]`. */\n quickActions?: (ctx: {\n node: GraphNode<any>;\n defaultActions: readonly GraphInspectorAction[];\n }) => readonly GraphInspectorAction[];\n /** Accessible name of the panel. Default 'Graph inspector'. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n}\n\ninterface InspectorSlice {\n selection: readonly NodeId[];\n pinned: boolean;\n hidden: boolean;\n expanding: boolean;\n model: number;\n}\n\n/** Narrow store subscription: subject id + the flags the panel displays. */\nfunction useInspectorSlice(instance: AnyGraphInstance): InspectorSlice {\n const cacheRef = useRef<InspectorSlice | null>(null);\n const subscribe = useCallback(\n (onChange: () => void) => instance.store.subscribe(onChange),\n [instance],\n );\n const getSnapshot = useCallback((): InspectorSlice => {\n const s: GraphStoreState = instance.store.getState();\n const selection = s.selection.nodeIds;\n const id = selection.length === 1 ? selection[0]! : null;\n const next: InspectorSlice = {\n selection,\n pinned: id !== null && s.pins.has(id),\n hidden: id !== null && s.hiddenNodeIds.has(id),\n expanding: id !== null && s.pendingExpansions.has(id),\n model: s.revisions.model,\n };\n const prev = cacheRef.current;\n if (\n prev !== null &&\n prev.selection === next.selection &&\n prev.pinned === next.pinned &&\n prev.hidden === next.hidden &&\n prev.expanding === next.expanding &&\n prev.model === next.model\n ) {\n return prev;\n }\n cacheRef.current = next;\n return next;\n }, [instance]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** Same derivation as the core §14 label default: `attrs.label ?? id`. */\nfunction nodeLabelText(node: GraphNode<any>): string {\n const attrs = node.attrs as Record<string, unknown> | undefined;\n const label = attrs?.['label'];\n return label === undefined || label === null ? String(node.id) : String(label);\n}\n\n/** Attr value → display TEXT (never markup): strings verbatim, structured\n * values JSON (guarded — cycles degrade to the String form). */\nfunction attrValueText(value: unknown): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'object' && value !== null) {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst PANEL_STYLE: CSSProperties = {\n position: 'absolute',\n top: 12,\n bottom: 12,\n width: 280,\n overflowY: 'auto',\n pointerEvents: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n padding: 10,\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.96)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n color: '#e8eaf0',\n font: '13px/1.4 system-ui, sans-serif',\n};\nconst TITLE_STYLE: CSSProperties = {\n fontWeight: 600,\n fontSize: 14,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n};\nconst SECTION_LABEL_STYLE: CSSProperties = { fontSize: 11, opacity: 0.65, marginTop: 4 };\nconst TABLE_STYLE: CSSProperties = { borderCollapse: 'collapse', width: '100%' };\nconst CELL_KEY_STYLE: CSSProperties = {\n textAlign: 'left',\n verticalAlign: 'top',\n padding: '2px 8px 2px 0',\n opacity: 0.65,\n fontWeight: 400,\n whiteSpace: 'nowrap',\n};\nconst CELL_VALUE_STYLE: CSSProperties = { padding: '2px 0', overflowWrap: 'anywhere' };\nconst BUTTON_STYLE: CSSProperties = {\n appearance: 'none',\n display: 'block',\n width: '100%',\n textAlign: 'left',\n background: 'transparent',\n border: 'none',\n borderRadius: 6,\n color: 'inherit',\n cursor: 'pointer',\n font: 'inherit',\n padding: '4px 8px',\n};\nconst ACTIONS_STYLE: CSSProperties = { display: 'flex', flexWrap: 'wrap', gap: 4 };\nconst ACTION_BUTTON_STYLE: CSSProperties = {\n ...BUTTON_STYLE,\n display: 'inline-block',\n width: 'auto',\n border: '1px solid rgba(255, 255, 255, 0.2)',\n};\nconst EMPTY_STYLE: CSSProperties = { fontStyle: 'italic', opacity: 0.8 };\n\nexport function GraphInspector(props: GraphInspectorProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphInspector>');\n const slice = useInspectorSlice(instance);\n const dock = props.dock ?? 'right';\n\n /** Per-model neighbor cache learned from focus operations (module JSDoc). */\n const neighborsRef = useRef<{ model: number; byId: Map<NodeId, readonly NodeId[]> }>({\n model: -1,\n byId: new Map(),\n });\n /** Bump to re-render after imperative cache learns (focus clicks). */\n const [, setLearnTick] = useState(0);\n\n // Idempotent render-time cache reset (ref only — safe under StrictMode).\n if (neighborsRef.current.model !== slice.model) {\n neighborsRef.current = { model: slice.model, byId: new Map() };\n }\n\n const subjectId = slice.selection.length === 1 ? slice.selection[0]! : null;\n const subject =\n subjectId === null ? undefined : (instance.getNode(subjectId) as GraphNode<any> | undefined);\n\n /** Focus + learn: the single v0.9 neighborhood-resolution seam. */\n const focusAndLearn = (id: NodeId): void => {\n const neighbors = instance.focusNode(id);\n neighborsRef.current.byId.set(id, neighbors);\n setLearnTick((t) => t + 1);\n };\n\n const panelLabel = props.label ?? 'Graph inspector';\n const dockStyle: CSSProperties = dock === 'left' ? { left: 12 } : { right: 12 };\n\n if (subjectId === null || subject === undefined) {\n return (\n <div\n data-orbit-inspector=\"\"\n role=\"complementary\"\n aria-label={panelLabel}\n className={props.className}\n style={{ ...PANEL_STYLE, ...dockStyle, ...props.style }}\n >\n <div data-orbit-inspector-empty=\"\" style={EMPTY_STYLE}>\n {slice.selection.length > 1\n ? `${slice.selection.length} nodes selected — select a single node to inspect`\n : 'Select a node to inspect'}\n </div>\n </div>\n );\n }\n\n const attrs = Object.entries((subject.attrs ?? {}) as Record<string, unknown>);\n const neighbors = neighborsRef.current.byId.get(subjectId);\n\n const defaultActions: GraphInspectorAction[] = [\n {\n id: 'expand',\n label: slice.expanding ? 'Expanding…' : 'Expand',\n disabled: slice.expanding,\n onSelect: () => {\n // A discard/collapse rejects the promise by design (§9.2); the panel\n // fires-and-forgets — diagnostics carry the why.\n void instance.expandNode(subjectId).catch(() => {});\n },\n },\n {\n id: 'isolate',\n label: 'Isolate',\n onSelect: () => {\n // §9.2 1-hop ego hard scope — the same path as the §16.4 menu item.\n instance.applyHostUpdate({ subgraph: { seedIds: [subjectId], hops: 1 } });\n },\n },\n slice.pinned\n ? {\n id: 'unpin',\n label: 'Unpin',\n onSelect: () => {\n instance.unpinNode(subjectId);\n },\n }\n : {\n id: 'pin',\n label: 'Pin',\n onSelect: () => {\n instance.pinNode(subjectId);\n },\n },\n {\n id: 'hide',\n label: 'Hide',\n onSelect: () => {\n instance.hideNodes([subjectId]);\n },\n },\n ];\n const actions =\n props.quickActions !== undefined\n ? props.quickActions({ node: subject, defaultActions })\n : defaultActions;\n\n const stateBadges = [\n ...(slice.pinned ? ['pinned'] : []),\n ...(slice.hidden ? ['hidden'] : []),\n ].join(' · ');\n\n return (\n <div\n data-orbit-inspector=\"\"\n role=\"complementary\"\n aria-label={panelLabel}\n className={props.className}\n style={{ ...PANEL_STYLE, ...dockStyle, ...props.style }}\n >\n {/* Untrusted node text renders as TEXT NODES only (§14). */}\n <div data-orbit-inspector-title=\"\" style={TITLE_STYLE}>\n {nodeLabelText(subject)}\n </div>\n {stateBadges !== '' ? (\n <div data-orbit-inspector-state=\"\" style={SECTION_LABEL_STYLE}>\n {stateBadges}\n </div>\n ) : null}\n\n <div data-orbit-inspector-actions=\"\" style={ACTIONS_STYLE}>\n {actions.map((action) => (\n <button\n key={action.id}\n type=\"button\"\n data-orbit-inspector-action={action.id}\n disabled={action.disabled === true}\n style={ACTION_BUTTON_STYLE}\n onClick={() => {\n if (action.disabled !== true) action.onSelect();\n }}\n >\n {action.label}\n </button>\n ))}\n </div>\n\n <div style={SECTION_LABEL_STYLE}>Attributes</div>\n {props.renderAttrs !== undefined ? (\n props.renderAttrs(subject)\n ) : attrs.length === 0 ? (\n <div data-orbit-inspector-no-attrs=\"\" style={EMPTY_STYLE}>\n No attributes\n </div>\n ) : (\n <table data-orbit-inspector-attrs=\"\" style={TABLE_STYLE}>\n <tbody>\n {attrs.map(([key, value]) => (\n <tr key={key} data-orbit-inspector-attr={key}>\n <th scope=\"row\" style={CELL_KEY_STYLE}>\n {key}\n </th>\n <td style={CELL_VALUE_STYLE}>{attrValueText(value)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n )}\n\n <div style={SECTION_LABEL_STYLE}>Neighbors</div>\n {neighbors === undefined ? (\n <button\n type=\"button\"\n data-orbit-inspector-resolve-neighbors=\"\"\n style={BUTTON_STYLE}\n onClick={() => {\n focusAndLearn(subjectId);\n }}\n >\n Show neighbors (focuses the node)\n </button>\n ) : neighbors.length === 0 ? (\n <div data-orbit-inspector-no-neighbors=\"\" style={EMPTY_STYLE}>\n No neighbors\n </div>\n ) : (\n <ul data-orbit-inspector-neighbors=\"\" style={{ margin: 0, padding: 0, listStyle: 'none' }}>\n {neighbors.map((id) => {\n const neighbor = instance.getNode(id) as GraphNode<any> | undefined;\n return (\n <li key={id}>\n <button\n type=\"button\"\n data-orbit-inspector-neighbor={id}\n style={BUTTON_STYLE}\n onClick={() => {\n focusAndLearn(id);\n }}\n >\n {neighbor === undefined ? id : nodeLabelText(neighbor)}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Inspector/index.tsx"],"names":["neighbors"],"mappings":"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","file":"Inspector.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Inspector` — `<GraphInspector>`, the\n * docked detail panel.\n *\n * Subject: the SELECTED-SINGLE node (`store.selection.nodeIds.length === 1`)\n * — the node namespace is the focus proxy the store actually publishes.\n * Anything else (empty or multi selection) renders the empty state.\n *\n * Sections:\n * - **Attrs table** — every attr key/value as TEXT NODES (untrusted\n * content; hostile strings render literally); `renderAttrs` replaces it.\n * - **Neighbor list** — click → `instance.focusNode(neighborId)` (camera\n * fly). v0.9 resolution compromise: the public instance\n * surface resolves a 1-hop neighborhood ONLY through `focusNode`'s return\n * value (the same seam the navigator uses on Enter) — a passive\n * adjacency read is a post-v0.9 core surface. The section therefore\n * populates from a per-model-revision cache learned by focus operations,\n * with a 'Show neighbors' affordance that focuses the subject to resolve\n * its neighborhood on demand (flying to the inspected node is coherent\n * behavior, and it is the user's explicit action). A model change\n * drops the (now stale) cache.\n * - **Quick actions** — wired to the same public mutators as the\n * context menu: Expand (`expandNode`, busy while pending), Isolate\n * (1-hop ego hard scope via the `subgraph` path), Pin/Unpin (store pin\n * slice toggle), Hide (`hideNodes`). `quickActions` is a render-prop\n * REPLACEMENT receiving the defaults, so extension is\n * `[...defaultActions, mine]`.\n */\n\nimport { useCallback, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, ReactElement, ReactNode } from 'react';\nimport type { GraphNode, GraphStoreState, NodeId } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\n\nexport interface GraphInspectorAction {\n /** Stable key; default actions use 'expand' | 'isolate' | 'pin' | 'unpin'\n * | 'hide'. */\n id: string;\n /** Rendered as a text node. */\n label: string;\n disabled?: boolean;\n onSelect(): void;\n}\n\nexport interface GraphInspectorProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Docking side. Default 'right'. */\n dock?: 'left' | 'right';\n /** Replaces the default attrs table. */\n renderAttrs?: (node: GraphNode<any>) => ReactNode;\n /** Render-prop REPLACEMENT for the quick-action row; receives the default\n * actions so extension is `[...defaultActions, mine]`. */\n quickActions?: (ctx: {\n node: GraphNode<any>;\n defaultActions: readonly GraphInspectorAction[];\n }) => readonly GraphInspectorAction[];\n /** Accessible name of the panel. Default 'Graph inspector'. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n}\n\ninterface InspectorSlice {\n selection: readonly NodeId[];\n pinned: boolean;\n hidden: boolean;\n expanding: boolean;\n model: number;\n}\n\n/** Narrow store subscription: subject id + the flags the panel displays. */\nfunction useInspectorSlice(instance: AnyGraphInstance): InspectorSlice {\n const cacheRef = useRef<InspectorSlice | null>(null);\n const subscribe = useCallback(\n (onChange: () => void) => instance.store.subscribe(onChange),\n [instance],\n );\n const getSnapshot = useCallback((): InspectorSlice => {\n const s: GraphStoreState = instance.store.getState();\n const selection = s.selection.nodeIds;\n const id = selection.length === 1 ? selection[0]! : null;\n const next: InspectorSlice = {\n selection,\n pinned: id !== null && s.pins.has(id),\n hidden: id !== null && s.hiddenNodeIds.has(id),\n expanding: id !== null && s.pendingExpansions.has(id),\n model: s.revisions.model,\n };\n const prev = cacheRef.current;\n if (\n prev !== null &&\n prev.selection === next.selection &&\n prev.pinned === next.pinned &&\n prev.hidden === next.hidden &&\n prev.expanding === next.expanding &&\n prev.model === next.model\n ) {\n return prev;\n }\n cacheRef.current = next;\n return next;\n }, [instance]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** Same derivation as the core label default: `attrs.label ?? id`. */\nfunction nodeLabelText(node: GraphNode<any>): string {\n const attrs = node.attrs as Record<string, unknown> | undefined;\n const label = attrs?.['label'];\n return label === undefined || label === null ? String(node.id) : String(label);\n}\n\n/** Attr value → display TEXT (never markup): strings verbatim, structured\n * values JSON (guarded — cycles degrade to the String form). */\nfunction attrValueText(value: unknown): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'object' && value !== null) {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst PANEL_STYLE: CSSProperties = {\n position: 'absolute',\n top: 12,\n bottom: 12,\n width: 280,\n overflowY: 'auto',\n pointerEvents: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n padding: 10,\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.96)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n color: '#e8eaf0',\n font: '13px/1.4 system-ui, sans-serif',\n};\nconst TITLE_STYLE: CSSProperties = {\n fontWeight: 600,\n fontSize: 14,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n};\nconst SECTION_LABEL_STYLE: CSSProperties = { fontSize: 11, opacity: 0.65, marginTop: 4 };\nconst TABLE_STYLE: CSSProperties = { borderCollapse: 'collapse', width: '100%' };\nconst CELL_KEY_STYLE: CSSProperties = {\n textAlign: 'left',\n verticalAlign: 'top',\n padding: '2px 8px 2px 0',\n opacity: 0.65,\n fontWeight: 400,\n whiteSpace: 'nowrap',\n};\nconst CELL_VALUE_STYLE: CSSProperties = { padding: '2px 0', overflowWrap: 'anywhere' };\nconst BUTTON_STYLE: CSSProperties = {\n appearance: 'none',\n display: 'block',\n width: '100%',\n textAlign: 'left',\n background: 'transparent',\n border: 'none',\n borderRadius: 6,\n color: 'inherit',\n cursor: 'pointer',\n font: 'inherit',\n padding: '4px 8px',\n};\nconst ACTIONS_STYLE: CSSProperties = { display: 'flex', flexWrap: 'wrap', gap: 4 };\nconst ACTION_BUTTON_STYLE: CSSProperties = {\n ...BUTTON_STYLE,\n display: 'inline-block',\n width: 'auto',\n border: '1px solid rgba(255, 255, 255, 0.2)',\n};\nconst EMPTY_STYLE: CSSProperties = { fontStyle: 'italic', opacity: 0.8 };\n\nexport function GraphInspector(props: GraphInspectorProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphInspector>');\n const slice = useInspectorSlice(instance);\n const dock = props.dock ?? 'right';\n\n /** Per-model neighbor cache learned from focus operations (module JSDoc). */\n const neighborsRef = useRef<{ model: number; byId: Map<NodeId, readonly NodeId[]> }>({\n model: -1,\n byId: new Map(),\n });\n /** Bump to re-render after imperative cache learns (focus clicks). */\n const [, setLearnTick] = useState(0);\n\n // Idempotent render-time cache reset (ref only — safe under StrictMode).\n if (neighborsRef.current.model !== slice.model) {\n neighborsRef.current = { model: slice.model, byId: new Map() };\n }\n\n const subjectId = slice.selection.length === 1 ? slice.selection[0]! : null;\n const subject =\n subjectId === null ? undefined : (instance.getNode(subjectId) as GraphNode<any> | undefined);\n\n /** Focus + learn: the single v0.9 neighborhood-resolution seam. */\n const focusAndLearn = (id: NodeId): void => {\n const neighbors = instance.focusNode(id);\n neighborsRef.current.byId.set(id, neighbors);\n setLearnTick((t) => t + 1);\n };\n\n const panelLabel = props.label ?? 'Graph inspector';\n const dockStyle: CSSProperties = dock === 'left' ? { left: 12 } : { right: 12 };\n\n if (subjectId === null || subject === undefined) {\n return (\n <div\n data-orbit-inspector=\"\"\n role=\"complementary\"\n aria-label={panelLabel}\n className={props.className}\n style={{ ...PANEL_STYLE, ...dockStyle, ...props.style }}\n >\n <div data-orbit-inspector-empty=\"\" style={EMPTY_STYLE}>\n {slice.selection.length > 1\n ? `${slice.selection.length} nodes selected — select a single node to inspect`\n : 'Select a node to inspect'}\n </div>\n </div>\n );\n }\n\n const attrs = Object.entries((subject.attrs ?? {}) as Record<string, unknown>);\n const neighbors = neighborsRef.current.byId.get(subjectId);\n\n const defaultActions: GraphInspectorAction[] = [\n {\n id: 'expand',\n label: slice.expanding ? 'Expanding…' : 'Expand',\n disabled: slice.expanding,\n onSelect: () => {\n // A discard/collapse rejects the promise by design; the panel\n // fires-and-forgets — diagnostics carry the why.\n void instance.expandNode(subjectId).catch(() => {});\n },\n },\n {\n id: 'isolate',\n label: 'Isolate',\n onSelect: () => {\n // 1-hop ego hard scope — the same path as the menu item.\n instance.applyHostUpdate({ subgraph: { seedIds: [subjectId], hops: 1 } });\n },\n },\n slice.pinned\n ? {\n id: 'unpin',\n label: 'Unpin',\n onSelect: () => {\n instance.unpinNode(subjectId);\n },\n }\n : {\n id: 'pin',\n label: 'Pin',\n onSelect: () => {\n instance.pinNode(subjectId);\n },\n },\n {\n id: 'hide',\n label: 'Hide',\n onSelect: () => {\n instance.hideNodes([subjectId]);\n },\n },\n ];\n const actions =\n props.quickActions !== undefined\n ? props.quickActions({ node: subject, defaultActions })\n : defaultActions;\n\n const stateBadges = [\n ...(slice.pinned ? ['pinned'] : []),\n ...(slice.hidden ? ['hidden'] : []),\n ].join(' · ');\n\n return (\n <div\n data-orbit-inspector=\"\"\n role=\"complementary\"\n aria-label={panelLabel}\n className={props.className}\n style={{ ...PANEL_STYLE, ...dockStyle, ...props.style }}\n >\n {/* Untrusted node text renders as TEXT NODES only. */}\n <div data-orbit-inspector-title=\"\" style={TITLE_STYLE}>\n {nodeLabelText(subject)}\n </div>\n {stateBadges !== '' ? (\n <div data-orbit-inspector-state=\"\" style={SECTION_LABEL_STYLE}>\n {stateBadges}\n </div>\n ) : null}\n\n <div data-orbit-inspector-actions=\"\" style={ACTIONS_STYLE}>\n {actions.map((action) => (\n <button\n key={action.id}\n type=\"button\"\n data-orbit-inspector-action={action.id}\n disabled={action.disabled === true}\n style={ACTION_BUTTON_STYLE}\n onClick={() => {\n if (action.disabled !== true) action.onSelect();\n }}\n >\n {action.label}\n </button>\n ))}\n </div>\n\n <div style={SECTION_LABEL_STYLE}>Attributes</div>\n {props.renderAttrs !== undefined ? (\n props.renderAttrs(subject)\n ) : attrs.length === 0 ? (\n <div data-orbit-inspector-no-attrs=\"\" style={EMPTY_STYLE}>\n No attributes\n </div>\n ) : (\n <table data-orbit-inspector-attrs=\"\" style={TABLE_STYLE}>\n <tbody>\n {attrs.map(([key, value]) => (\n <tr key={key} data-orbit-inspector-attr={key}>\n <th scope=\"row\" style={CELL_KEY_STYLE}>\n {key}\n </th>\n <td style={CELL_VALUE_STYLE}>{attrValueText(value)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n )}\n\n <div style={SECTION_LABEL_STYLE}>Neighbors</div>\n {neighbors === undefined ? (\n <button\n type=\"button\"\n data-orbit-inspector-resolve-neighbors=\"\"\n style={BUTTON_STYLE}\n onClick={() => {\n focusAndLearn(subjectId);\n }}\n >\n Show neighbors (focuses the node)\n </button>\n ) : neighbors.length === 0 ? (\n <div data-orbit-inspector-no-neighbors=\"\" style={EMPTY_STYLE}>\n No neighbors\n </div>\n ) : (\n <ul data-orbit-inspector-neighbors=\"\" style={{ margin: 0, padding: 0, listStyle: 'none' }}>\n {neighbors.map((id) => {\n const neighbor = instance.getNode(id) as GraphNode<any> | undefined;\n return (\n <li key={id}>\n <button\n type=\"button\"\n data-orbit-inspector-neighbor={id}\n style={BUTTON_STYLE}\n onClick={() => {\n focusAndLearn(id);\n }}\n >\n {neighbor === undefined ? id : nodeLabelText(neighbor)}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n}\n"]}
|
|
@@ -3,22 +3,22 @@ import { A as AnyGraphInstance } from '../GraphProvider-B8ITDXS0.js';
|
|
|
3
3
|
import '@modernrelay/orbit-core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* <GraphLegend> —
|
|
7
|
-
* channel
|
|
6
|
+
* <GraphLegend> — built-in legend over one scale-valued styling
|
|
7
|
+
* channel.
|
|
8
8
|
*
|
|
9
9
|
* Reads `instance.getScaleInfo(channel)` and re-reads on store model/scope/
|
|
10
10
|
* render revision advances and theme changes. Renders nothing when the
|
|
11
11
|
* channel is not scale-valued. Forms:
|
|
12
|
-
* - sequential color
|
|
13
|
-
* - diverging color
|
|
14
|
-
* - categorical
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* - sequential color → gradient ramp bar + min/max domain ticks + metric name
|
|
13
|
+
* - diverging color → two-tone bar + min/mid/max ticks
|
|
14
|
+
* - categorical → swatch rows (color chip + value + count); a row click
|
|
15
|
+
* calls `onCategoryClick(value)`, and rows listed in `excludedValues`
|
|
16
|
+
* render dimmed
|
|
17
17
|
* - numeric range (nodeSize scales) → graduated dots (4 steps, value labels)
|
|
18
18
|
*
|
|
19
19
|
* The HOST owns actual filtering: wire `onCategoryClick` to your `filter`
|
|
20
20
|
* prop and feed the same values back through `excludedValues` for the dimmed
|
|
21
|
-
* affordance.
|
|
21
|
+
* affordance. The host owns legend-driven filtering.
|
|
22
22
|
*
|
|
23
23
|
* All data-derived strings (category values) render as TEXT NODES — never
|
|
24
24
|
* markup. Headless-styleable: providing `className` drops the built-in
|
|
@@ -29,8 +29,7 @@ interface GraphLegendProps {
|
|
|
29
29
|
/** Styling channel to describe. Default 'nodeColor'. */
|
|
30
30
|
channel?: 'nodeColor' | 'nodeSize';
|
|
31
31
|
/** Categorical values the host currently filters out — their rows render
|
|
32
|
-
* dimmed
|
|
33
|
-
* toggling). */
|
|
32
|
+
* dimmed; filtering remains host-owned. */
|
|
34
33
|
excludedValues?: readonly string[];
|
|
35
34
|
/** Fired with the clicked categorical row's value. */
|
|
36
35
|
onCategoryClick?: (value: string) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useScaleInfoSubscription } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import { useResolvedInstance } from '../chunk-
|
|
1
|
+
import { useScaleInfoSubscription } from '../chunk-ODOPSUI7.js';
|
|
2
|
+
import '../chunk-S36LHSNX.js';
|
|
3
|
+
import { useResolvedInstance } from '../chunk-NPOV5GA3.js';
|
|
4
4
|
import { useMemo } from 'react';
|
|
5
5
|
import { CATEGORICAL_PALETTE } from '@modernrelay/orbit-core';
|
|
6
6
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Legend/index.tsx"],"names":[],"mappings":"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","file":"Legend.js","sourcesContent":["/**\n * <GraphLegend> — §11/§16.7 built-in legend over one scale-valued styling\n * channel (S10).\n *\n * Reads `instance.getScaleInfo(channel)` and re-reads on store model/scope/\n * render revision advances and theme changes. Renders nothing when the\n * channel is not scale-valued. Forms:\n * - sequential color → gradient ramp bar + min/max domain ticks + metric name\n * - diverging color → two-tone bar + min/mid/max ticks\n * - categorical → swatch rows (color chip + value + count); a row click\n * calls `onCategoryClick(value)`, and rows listed in `excludedValues`\n * render dimmed\n * - numeric range (nodeSize scales) → graduated dots (4 steps, value labels)\n *\n * The HOST owns actual filtering: wire `onCategoryClick` to your `filter`\n * prop and feed the same values back through `excludedValues` for the dimmed\n * affordance. Library-owned legend toggling arrives with S12.\n *\n * All data-derived strings (category values) render as TEXT NODES — never\n * markup. Headless-styleable: providing `className` drops the built-in\n * inline styles (theme-token text/border colors included).\n */\n\nimport { useMemo } from 'react';\nimport type { CSSProperties, ReactElement, ReactNode } from 'react';\nimport { CATEGORICAL_PALETTE } from '@modernrelay/orbit-core';\nimport type { GraphTheme, ScaleChannelInfo, ScaleInfoRow } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\nimport { useScaleInfoSubscription } from '../../hooks';\n\nexport interface GraphLegendProps {\n /** Styling channel to describe. Default 'nodeColor'. */\n channel?: 'nodeColor' | 'nodeSize';\n /** Categorical values the host currently filters out — their rows render\n * dimmed (the host owns the actual filter wiring; S12 adds library-owned\n * toggling). */\n excludedValues?: readonly string[];\n /** Fired with the clicked categorical row's value. */\n onCategoryClick?: (value: string) => void;\n /** Explicit instance; defaults to the ambient <Graph>/<GraphProvider> one. */\n instance?: AnyGraphInstance;\n /** Class hook for the root; providing it drops the default styles. */\n className?: string;\n style?: CSSProperties;\n}\n\n/** Graduated-dot step count for numeric-range (size) legends. */\nconst SIZE_LEGEND_STEPS = 4;\n\n// --- default styling (dropped when `className` is provided). Text/border\n// tokens come from the resolved GraphTheme so the legend follows the theme\n// prop; the translucent panel keeps the §16.7 overlay look. ---\n\nfunction rootStyle(theme: GraphTheme): CSSProperties {\n return {\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n padding: 8,\n borderRadius: 8,\n minWidth: 120,\n background: 'rgba(23, 25, 32, 0.85)',\n border: '1px solid rgba(148, 163, 184, 0.25)',\n color: theme.labelFg,\n font: '12px/1.4 system-ui, sans-serif',\n };\n}\n\nconst TITLE_STYLE: CSSProperties = { opacity: 0.75 };\n\nconst RAMP_STYLE: CSSProperties = { height: 10, borderRadius: 5 };\n\nconst TICK_ROW_STYLE: CSSProperties = {\n display: 'flex',\n justifyContent: 'space-between',\n gap: 8,\n opacity: 0.85,\n};\n\nconst CATEGORY_ROW_STYLE: CSSProperties = {\n appearance: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n background: 'transparent',\n border: 'none',\n borderRadius: 4,\n color: 'inherit',\n cursor: 'pointer',\n font: 'inherit',\n padding: '2px 4px',\n textAlign: 'left',\n};\n\nconst EXCLUDED_ROW_STYLE: CSSProperties = {\n ...CATEGORY_ROW_STYLE,\n opacity: 0.45,\n textDecoration: 'line-through',\n};\n\nconst VALUE_STYLE: CSSProperties = { flex: 1, minWidth: 0 };\n\nconst COUNT_STYLE: CSSProperties = { opacity: 0.7 };\n\nconst DOT_ROW_STYLE: CSSProperties = {\n display: 'flex',\n alignItems: 'flex-end',\n gap: 12,\n};\n\nconst DOT_STEP_STYLE: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: 4,\n};\n\n/** Compact tick label: integers verbatim, else 3 significant digits. */\nfunction formatTick(v: number): string {\n if (!Number.isFinite(v)) return '';\n if (Number.isInteger(v)) return String(v);\n return String(Number(v.toPrecision(3)));\n}\n\nfunction dotStyle(size: number, theme: GraphTheme): CSSProperties {\n const px = Math.max(2, Math.abs(size));\n return {\n width: px,\n height: px,\n borderRadius: '50%',\n background: theme.accent,\n };\n}\n\nexport function GraphLegend(props: GraphLegendProps): ReactElement | null {\n const instance = useResolvedInstance(props.instance, '<GraphLegend>');\n const channel = props.channel ?? 'nodeColor';\n const { version, theme } = useScaleInfoSubscription(instance);\n const info: ScaleChannelInfo | null = useMemo(\n () => instance.getScaleInfo(channel),\n // `version` invalidates: a new store revision can change scale info.\n [instance, channel, version],\n );\n if (info === null) return null;\n\n const styled = props.className === undefined;\n const scale = info.scale;\n const title =\n scale.kind === 'categorical'\n ? typeof scale.by === 'string'\n ? scale.by\n : 'category'\n : scale.metric;\n\n let body: ReactNode;\n if (scale.kind === 'categorical') {\n const palette: readonly (string | number)[] = scale.palette ?? CATEGORICAL_PALETTE;\n const excluded = props.excludedValues ?? [];\n const renderRow = (row: ScaleInfoRow): ReactElement => {\n const isExcluded = excluded.includes(row.value);\n const entry = row.colorIndex >= 0 ? palette[row.colorIndex] : undefined;\n return (\n <button\n key={row.value}\n type=\"button\"\n data-orbit-legend-row={row.value}\n aria-pressed={isExcluded}\n style={styled ? (isExcluded ? EXCLUDED_ROW_STYLE : CATEGORY_ROW_STYLE) : undefined}\n onClick={() => {\n props.onCategoryClick?.(row.value);\n }}\n >\n <span\n data-orbit-legend-swatch=\"\"\n aria-hidden=\"true\"\n style={\n typeof entry === 'number'\n ? dotStyle(entry, theme)\n : {\n width: 10,\n height: 10,\n borderRadius: 2,\n background: entry ?? 'transparent',\n flex: 'none',\n }\n }\n />\n <span data-orbit-legend-value=\"\" style={styled ? VALUE_STYLE : undefined}>\n {row.value}\n </span>\n <span data-orbit-legend-count=\"\" style={styled ? COUNT_STYLE : undefined}>\n {row.count}\n </span>\n </button>\n );\n };\n body = (\n <div\n data-orbit-legend-rows=\"\"\n style={styled ? { display: 'flex', flexDirection: 'column' } : undefined}\n >\n {(info.rows ?? []).map(renderRow)}\n </div>\n );\n } else {\n // sequential | diverging — tuple stops are colors (string) or sizes\n // (number); the domain resolves through the same frozen DomainStore\n // coordinate the projection uses, and may be absent (§12 pending metric).\n const stops = scale.range as readonly (string | number)[];\n const domain = info.domain;\n if (typeof stops[0] === 'number') {\n // Size legend: graduated dots, small→large, with value labels.\n const lo = stops[0];\n const hi = stops[stops.length - 1] as number;\n body = (\n <div data-orbit-legend-dots=\"\" style={styled ? DOT_ROW_STYLE : undefined}>\n {Array.from({ length: SIZE_LEGEND_STEPS }, (_, i) => {\n const t = i / (SIZE_LEGEND_STEPS - 1);\n const size = lo + (hi - lo) * t;\n const label =\n domain !== undefined ? formatTick(domain[0] + (domain[1] - domain[0]) * t) : '';\n return (\n <span key={i} data-orbit-legend-dot-step=\"\" style={styled ? DOT_STEP_STYLE : undefined}>\n <span data-orbit-legend-dot=\"\" aria-hidden=\"true\" style={dotStyle(size, theme)} />\n <span data-orbit-legend-tick=\"\">{label}</span>\n </span>\n );\n })}\n </div>\n );\n } else {\n // Color legend: two-stop ramp (sequential) or three-stop bar with a\n // mid tick (diverging). Stops are host-provided CSS colors.\n const gradient = `linear-gradient(to right, ${stops.join(', ')})`;\n const ticks: string[] =\n domain === undefined\n ? [] // §12 pending metric: no resolvable domain, no ticks\n : scale.kind === 'diverging'\n ? [formatTick(domain[0]), formatTick(scale.mid), formatTick(domain[1])]\n : [formatTick(domain[0]), formatTick(domain[1])];\n body = (\n <div style={styled ? { display: 'flex', flexDirection: 'column', gap: 4 } : undefined}>\n <div\n data-orbit-legend-ramp={scale.kind}\n style={styled ? { ...RAMP_STYLE, background: gradient } : { background: gradient }}\n />\n {ticks.length > 0 ? (\n <div data-orbit-legend-ticks=\"\" style={styled ? TICK_ROW_STYLE : undefined}>\n {ticks.map((tick, i) => (\n <span key={i} data-orbit-legend-tick=\"\">\n {tick}\n </span>\n ))}\n </div>\n ) : null}\n </div>\n );\n }\n }\n\n return (\n <div\n data-orbit-legend={channel}\n role=\"group\"\n aria-label={`${title} legend`}\n className={props.className}\n style={styled ? { ...rootStyle(theme), ...props.style } : props.style}\n >\n <div data-orbit-legend-title=\"\" style={styled ? TITLE_STYLE : undefined}>\n {title}\n </div>\n {body}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Legend/index.tsx"],"names":[],"mappings":"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","file":"Legend.js","sourcesContent":["/**\n * <GraphLegend> — built-in legend over one scale-valued styling\n * channel.\n *\n * Reads `instance.getScaleInfo(channel)` and re-reads on store model/scope/\n * render revision advances and theme changes. Renders nothing when the\n * channel is not scale-valued. Forms:\n * - sequential color → gradient ramp bar + min/max domain ticks + metric name\n * - diverging color → two-tone bar + min/mid/max ticks\n * - categorical → swatch rows (color chip + value + count); a row click\n * calls `onCategoryClick(value)`, and rows listed in `excludedValues`\n * render dimmed\n * - numeric range (nodeSize scales) → graduated dots (4 steps, value labels)\n *\n * The HOST owns actual filtering: wire `onCategoryClick` to your `filter`\n * prop and feed the same values back through `excludedValues` for the dimmed\n * affordance. The host owns legend-driven filtering.\n *\n * All data-derived strings (category values) render as TEXT NODES — never\n * markup. Headless-styleable: providing `className` drops the built-in\n * inline styles (theme-token text/border colors included).\n */\n\nimport { useMemo } from 'react';\nimport type { CSSProperties, ReactElement, ReactNode } from 'react';\nimport { CATEGORICAL_PALETTE } from '@modernrelay/orbit-core';\nimport type { GraphTheme, ScaleChannelInfo, ScaleInfoRow } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\nimport { useScaleInfoSubscription } from '../../hooks';\n\nexport interface GraphLegendProps {\n /** Styling channel to describe. Default 'nodeColor'. */\n channel?: 'nodeColor' | 'nodeSize';\n /** Categorical values the host currently filters out — their rows render\n * dimmed; filtering remains host-owned. */\n excludedValues?: readonly string[];\n /** Fired with the clicked categorical row's value. */\n onCategoryClick?: (value: string) => void;\n /** Explicit instance; defaults to the ambient <Graph>/<GraphProvider> one. */\n instance?: AnyGraphInstance;\n /** Class hook for the root; providing it drops the default styles. */\n className?: string;\n style?: CSSProperties;\n}\n\n/** Graduated-dot step count for numeric-range (size) legends. */\nconst SIZE_LEGEND_STEPS = 4;\n\n// --- default styling (dropped when `className` is provided). Text/border\n// tokens come from the resolved GraphTheme so the legend follows the theme\n// prop; the translucent panel keeps the overlay look. ---\n\nfunction rootStyle(theme: GraphTheme): CSSProperties {\n return {\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n padding: 8,\n borderRadius: 8,\n minWidth: 120,\n background: 'rgba(23, 25, 32, 0.85)',\n border: '1px solid rgba(148, 163, 184, 0.25)',\n color: theme.labelFg,\n font: '12px/1.4 system-ui, sans-serif',\n };\n}\n\nconst TITLE_STYLE: CSSProperties = { opacity: 0.75 };\n\nconst RAMP_STYLE: CSSProperties = { height: 10, borderRadius: 5 };\n\nconst TICK_ROW_STYLE: CSSProperties = {\n display: 'flex',\n justifyContent: 'space-between',\n gap: 8,\n opacity: 0.85,\n};\n\nconst CATEGORY_ROW_STYLE: CSSProperties = {\n appearance: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n background: 'transparent',\n border: 'none',\n borderRadius: 4,\n color: 'inherit',\n cursor: 'pointer',\n font: 'inherit',\n padding: '2px 4px',\n textAlign: 'left',\n};\n\nconst EXCLUDED_ROW_STYLE: CSSProperties = {\n ...CATEGORY_ROW_STYLE,\n opacity: 0.45,\n textDecoration: 'line-through',\n};\n\nconst VALUE_STYLE: CSSProperties = { flex: 1, minWidth: 0 };\n\nconst COUNT_STYLE: CSSProperties = { opacity: 0.7 };\n\nconst DOT_ROW_STYLE: CSSProperties = {\n display: 'flex',\n alignItems: 'flex-end',\n gap: 12,\n};\n\nconst DOT_STEP_STYLE: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: 4,\n};\n\n/** Compact tick label: integers verbatim, else 3 significant digits. */\nfunction formatTick(v: number): string {\n if (!Number.isFinite(v)) return '';\n if (Number.isInteger(v)) return String(v);\n return String(Number(v.toPrecision(3)));\n}\n\nfunction dotStyle(size: number, theme: GraphTheme): CSSProperties {\n const px = Math.max(2, Math.abs(size));\n return {\n width: px,\n height: px,\n borderRadius: '50%',\n background: theme.accent,\n };\n}\n\nexport function GraphLegend(props: GraphLegendProps): ReactElement | null {\n const instance = useResolvedInstance(props.instance, '<GraphLegend>');\n const channel = props.channel ?? 'nodeColor';\n const { version, theme } = useScaleInfoSubscription(instance);\n const info: ScaleChannelInfo | null = useMemo(\n () => instance.getScaleInfo(channel),\n // `version` invalidates: a new store revision can change scale info.\n [instance, channel, version],\n );\n if (info === null) return null;\n\n const styled = props.className === undefined;\n const scale = info.scale;\n const title =\n scale.kind === 'categorical'\n ? typeof scale.by === 'string'\n ? scale.by\n : 'category'\n : scale.metric;\n\n let body: ReactNode;\n if (scale.kind === 'categorical') {\n const palette: readonly (string | number)[] = scale.palette ?? CATEGORICAL_PALETTE;\n const excluded = props.excludedValues ?? [];\n const renderRow = (row: ScaleInfoRow): ReactElement => {\n const isExcluded = excluded.includes(row.value);\n const entry = row.colorIndex >= 0 ? palette[row.colorIndex] : undefined;\n return (\n <button\n key={row.value}\n type=\"button\"\n data-orbit-legend-row={row.value}\n aria-pressed={isExcluded}\n style={styled ? (isExcluded ? EXCLUDED_ROW_STYLE : CATEGORY_ROW_STYLE) : undefined}\n onClick={() => {\n props.onCategoryClick?.(row.value);\n }}\n >\n <span\n data-orbit-legend-swatch=\"\"\n aria-hidden=\"true\"\n style={\n typeof entry === 'number'\n ? dotStyle(entry, theme)\n : {\n width: 10,\n height: 10,\n borderRadius: 2,\n background: entry ?? 'transparent',\n flex: 'none',\n }\n }\n />\n <span data-orbit-legend-value=\"\" style={styled ? VALUE_STYLE : undefined}>\n {row.value}\n </span>\n <span data-orbit-legend-count=\"\" style={styled ? COUNT_STYLE : undefined}>\n {row.count}\n </span>\n </button>\n );\n };\n body = (\n <div\n data-orbit-legend-rows=\"\"\n style={styled ? { display: 'flex', flexDirection: 'column' } : undefined}\n >\n {(info.rows ?? []).map(renderRow)}\n </div>\n );\n } else {\n // sequential | diverging — tuple stops are colors (string) or sizes\n // (number); the domain resolves through the same frozen DomainStore\n // coordinate the projection uses, and may be absent.\n const stops = scale.range as readonly (string | number)[];\n const domain = info.domain;\n if (typeof stops[0] === 'number') {\n // Size legend: graduated dots, small→large, with value labels.\n const lo = stops[0];\n const hi = stops[stops.length - 1] as number;\n body = (\n <div data-orbit-legend-dots=\"\" style={styled ? DOT_ROW_STYLE : undefined}>\n {Array.from({ length: SIZE_LEGEND_STEPS }, (_, i) => {\n const t = i / (SIZE_LEGEND_STEPS - 1);\n const size = lo + (hi - lo) * t;\n const label =\n domain !== undefined ? formatTick(domain[0] + (domain[1] - domain[0]) * t) : '';\n return (\n <span key={i} data-orbit-legend-dot-step=\"\" style={styled ? DOT_STEP_STYLE : undefined}>\n <span data-orbit-legend-dot=\"\" aria-hidden=\"true\" style={dotStyle(size, theme)} />\n <span data-orbit-legend-tick=\"\">{label}</span>\n </span>\n );\n })}\n </div>\n );\n } else {\n // Color legend: two-stop ramp (sequential) or three-stop bar with a\n // mid tick (diverging). Stops are host-provided CSS colors.\n const gradient = `linear-gradient(to right, ${stops.join(', ')})`;\n const ticks: string[] =\n domain === undefined\n ? [] // pending metric: no resolvable domain, no ticks\n : scale.kind === 'diverging'\n ? [formatTick(domain[0]), formatTick(scale.mid), formatTick(domain[1])]\n : [formatTick(domain[0]), formatTick(domain[1])];\n body = (\n <div style={styled ? { display: 'flex', flexDirection: 'column', gap: 4 } : undefined}>\n <div\n data-orbit-legend-ramp={scale.kind}\n style={styled ? { ...RAMP_STYLE, background: gradient } : { background: gradient }}\n />\n {ticks.length > 0 ? (\n <div data-orbit-legend-ticks=\"\" style={styled ? TICK_ROW_STYLE : undefined}>\n {ticks.map((tick, i) => (\n <span key={i} data-orbit-legend-tick=\"\">\n {tick}\n </span>\n ))}\n </div>\n ) : null}\n </div>\n );\n }\n }\n\n return (\n <div\n data-orbit-legend={channel}\n role=\"group\"\n aria-label={`${title} legend`}\n className={props.className}\n style={styled ? { ...rootStyle(theme), ...props.style } : props.style}\n >\n <div data-orbit-legend-title=\"\" style={styled ? TITLE_STYLE : undefined}>\n {title}\n </div>\n {body}\n </div>\n );\n}\n"]}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
import { CSSProperties, ReactElement } from 'react';
|
|
2
2
|
import { A as AnyGraphInstance } from '../GraphProvider-B8ITDXS0.js';
|
|
3
|
-
import { G as GraphCorner } from '../shared-
|
|
3
|
+
import { G as GraphCorner } from '../shared-n98tkXJe.js';
|
|
4
4
|
import '@modernrelay/orbit-core';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* `@modernrelay/orbit-react/components/Minimap` — `<GraphMinimap>`, the
|
|
8
|
-
* whole-graph thumbnail + viewport rectangle
|
|
7
|
+
* `@modernrelay/orbit-react/components/Minimap` — `<GraphMinimap>`, the
|
|
8
|
+
* whole-graph thumbnail + viewport rectangle.
|
|
9
9
|
*
|
|
10
|
-
* v0.9 is CPU-fallback-only (no engine exposes `capabilities.overviewPass`
|
|
11
|
-
* cosmos
|
|
12
|
-
* blits its `rasterize
|
|
13
|
-
* three
|
|
10
|
+
* v0.9 is CPU-fallback-only (no engine exposes `capabilities.overviewPass`
|
|
11
|
+
* cosmos matrix): the component owns an {@link OverviewController} and
|
|
12
|
+
* blits its `rasterize` output into a small canvas via `putImageData`. The
|
|
13
|
+
* three lanes stay strictly decoupled:
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
15
|
+
* 1. **Thumbnail refresh** — a 500 ms `setInterval` probes the controller's
|
|
16
|
+
* `shouldRefresh(now, simulationRunning, epoch)` gate, so the effective
|
|
17
|
+
* cadence is the controller's spec budget (≤ 2 Hz hot, ≤ 1 Hz idle, zero
|
|
18
|
+
* work idle-with-unchanged-epoch) and each `true` is followed by exactly
|
|
19
|
+
* one `rasterize` + blit. The interval — not a per-frame engine lane —
|
|
20
|
+
* is the documented v0.9 cadence: the `onFrame` fan-out only reaches
|
|
21
|
+
* React through the label lane, which is gated on labels being enabled,
|
|
22
|
+
* so a plain timer is the correct always-on ≤ 2 Hz driver. The epoch is
|
|
23
|
+
* `revisions.render` (a positions-bearing superset — over-approximate but
|
|
24
|
+
* bounded by the same throttle).
|
|
25
|
+
* 2. **Viewport rectangle** — an imperative store subscription writes the
|
|
26
|
+
* rect's transform/size O(1) per `store.viewport` publication (CSS
|
|
27
|
+
* transform on a ref; no React re-render, no re-rasterize).
|
|
28
|
+
* 3. **Pan** — pointer down/drag maps minimap px → world via the
|
|
29
|
+
* controller's `minimapToWorld` (exact inverse over the last rasterized
|
|
30
|
+
* frame) and calls `instance.setViewport({x, y})` — real camera pans.
|
|
31
31
|
*
|
|
32
32
|
* Scene source (documented v0.9 trim): the instance does not yet expose the
|
|
33
|
-
*
|
|
34
|
-
* component probes a pinned structural seam `getOverviewScene
|
|
35
|
-
* (
|
|
33
|
+
* posBuf mirror (reserved for the columnar lane), so the
|
|
34
|
+
* component probes a pinned structural seam `getOverviewScene` first
|
|
35
|
+
* (optional access — picked up automatically once core lands
|
|
36
36
|
* it) and falls back to the DECLARED node positions (`GraphNode.x/y`) of the
|
|
37
|
-
* current scene roster — exact for prepared/fixed-position datasets
|
|
38
|
-
* orbit-data
|
|
39
|
-
* rasterize as NaN tombstones (skipped);
|
|
37
|
+
* current scene roster — exact for prepared/fixed-position datasets from
|
|
38
|
+
* orbit-data, and empty for force layouts without a position mirror.
|
|
39
|
+
* Position-less nodes rasterize as NaN tombstones (skipped); mask-hidden nodes draw dimmed
|
|
40
40
|
* via `getVisible`.
|
|
41
41
|
*/
|
|
42
42
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useResolvedInstance, mergeStyle, cornerStyle } from '../chunk-
|
|
1
|
+
import { useResolvedInstance, mergeStyle, cornerStyle } from '../chunk-NPOV5GA3.js';
|
|
2
2
|
import { useRef, useMemo, useEffect } from 'react';
|
|
3
3
|
import { OverviewController, OVERVIEW_HOT_INTERVAL_MS } from '@modernrelay/orbit-core';
|
|
4
4
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Minimap/index.tsx"],"names":[],"mappings":"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","file":"Minimap.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Minimap` — `<GraphMinimap>`, the §16.8\n * whole-graph thumbnail + viewport rectangle (S11-T11).\n *\n * v0.9 is CPU-fallback-only (no engine exposes `capabilities.overviewPass` —\n * cosmos M0 matrix): the component owns an {@link OverviewController} and\n * blits its `rasterize()` output into a small canvas via `putImageData`. The\n * three §16.8 lanes stay strictly decoupled:\n *\n * 1. **Thumbnail refresh** — a 500 ms `setInterval` probes the controller's\n * `shouldRefresh(now, simulationRunning, epoch)` gate, so the effective\n * cadence is the controller's spec budget (≤ 2 Hz hot, ≤ 1 Hz idle, zero\n * work idle-with-unchanged-epoch) and each `true` is followed by exactly\n * one `rasterize()` + blit. The interval — not a per-frame engine lane —\n * is the documented v0.9 cadence: the S7 `onFrame` fan-out only reaches\n * React through the label lane, which is gated on labels being enabled,\n * so a plain timer is the correct always-on ≤ 2 Hz driver. The epoch is\n * `revisions.render` (a positions-bearing superset — over-approximate but\n * bounded by the same throttle).\n * 2. **Viewport rectangle** — an imperative store subscription writes the\n * rect's transform/size O(1) per `store.viewport` publication (CSS\n * transform on a ref; no React re-render, no re-rasterize).\n * 3. **Pan** — pointer down/drag maps minimap px → world via the\n * controller's `minimapToWorld` (exact inverse over the last rasterized\n * frame) and calls `instance.setViewport({x, y})` — real §13 camera pans.\n *\n * Scene source (documented v0.9 trim): the instance does not yet expose the\n * §7.1 posBuf mirror (that read lands with the S13 columnar lane), so the\n * component probes a pinned structural seam `getOverviewScene()` first\n * (S7-precedent optional access — picked up automatically once core lands\n * it) and falls back to the DECLARED node positions (`GraphNode.x/y`) of the\n * current scene roster — exact for prepared/fixed-position datasets (the S11\n * orbit-data lane), empty for force layouts until S13. Position-less nodes\n * rasterize as NaN tombstones (skipped); §9.1 mask-hidden nodes draw dimmed\n * via `getVisible`.\n */\n\nimport { useEffect, useMemo, useRef } from 'react';\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactElement } from 'react';\nimport { OverviewController, OVERVIEW_HOT_INTERVAL_MS } from '@modernrelay/orbit-core';\nimport type { NodeId, OverviewScene, ViewportState } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { cornerStyle, mergeStyle, useResolvedInstance } from '../shared';\nimport type { GraphCorner } from '../shared';\n\n/** Default thumbnail edge (px) — the component default; the §16.8 CPU target\n * default (256²) applies when a caller constructs a bare controller. */\nconst SIZE_DEFAULT = 200;\n\n/** Minimum rendered viewport-rect edge so a tiny/degenerate viewport stays\n * visible as a marker. */\nconst MIN_RECT_PX = 6;\n\n/** Pinned S13 structural seam (S7 shared.ts precedent): the instance-side\n * §7.1 posBuf mirror read. Optional access until core lands it — this view\n * stays signature-compatible when the member becomes required. */\ninterface OverviewSceneSurface {\n getOverviewScene?(): OverviewScene | null;\n}\n\nexport interface GraphMinimapProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Square thumbnail edge in CSS px (also the raster resolution). Default 200. */\n size?: number;\n /** Accessible name. Default 'Graph minimap'. */\n label?: string;\n /** Corner this panel anchors to. Default 'bottom-right'. */\n position?: GraphCorner;\n /** Distance from the two anchored edges in CSS px. Default 12. */\n offset?: number;\n /** Class hook for the root; providing it drops the default styles\n * INCLUDING placement — position it yourself in CSS. */\n className?: string;\n /** Style overrides merged over the defaults; an inset you set here\n * wins over the one `position` implies (resolved per axis). */\n style?: CSSProperties;\n}\n\nconst ROOT_STYLE: CSSProperties = {\n pointerEvents: 'auto',\n overflow: 'hidden',\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.85)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n};\n\nconst CANVAS_STYLE: CSSProperties = {\n display: 'block',\n cursor: 'crosshair',\n touchAction: 'none',\n};\n\nconst RECT_STYLE: CSSProperties = {\n position: 'absolute',\n left: 0,\n top: 0,\n display: 'none',\n border: '1px solid rgba(255, 255, 255, 0.85)',\n background: 'rgba(255, 255, 255, 0.12)',\n pointerEvents: 'none',\n willChange: 'transform',\n boxSizing: 'border-box',\n};\n\n/** Declared-position scene over the current roster, cached by the roster's\n * identity (`scene.idByIndex` is a stable reference per reconciled scene). */\ninterface DeclaredSceneCache {\n ids: readonly NodeId[];\n scene: OverviewScene | null;\n}\n\nexport function GraphMinimap(props: GraphMinimapProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphMinimap>');\n const size = props.size ?? SIZE_DEFAULT;\n\n const rootRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const rectRef = useRef<HTMLDivElement | null>(null);\n const draggingRef = useRef(false);\n const declaredSceneRef = useRef<DeclaredSceneCache>({ ids: [], scene: null });\n /** §9.1 mask lane for `getVisible`, recomputed once per refresh tick. */\n const visibleRef = useRef<{ ids: readonly NodeId[]; set: ReadonlySet<NodeId> | null }>({\n ids: [],\n set: null,\n });\n\n const controller = useMemo(() => {\n const getScene = (): OverviewScene | null => {\n // 1. Pinned core seam (S13): the §7.1 posBuf mirror, once it lands.\n const mirrored = (instance as unknown as OverviewSceneSurface).getOverviewScene?.();\n if (mirrored !== undefined) return mirrored;\n // 2. v0.9 fallback: declared node positions over the scene roster.\n const ids = instance.getSceneNodeIds();\n if (ids.length === 0) return null;\n const cache = declaredSceneRef.current;\n if (cache.ids === ids) return cache.scene;\n const positions = new Float32Array(2 * ids.length);\n let known = 0;\n for (let i = 0; i < ids.length; i++) {\n const node = instance.getNode(ids[i]!);\n const x = node?.x;\n const y = node?.y;\n if (typeof x === 'number' && typeof y === 'number') {\n positions[2 * i] = x;\n positions[2 * i + 1] = y;\n known++;\n } else {\n positions[2 * i] = NaN;\n positions[2 * i + 1] = NaN;\n }\n }\n const scene: OverviewScene | null = known > 0 ? { positions, count: ids.length } : null;\n declaredSceneRef.current = { ids, scene };\n return scene;\n };\n const getVisible = (index: number): boolean => {\n const v = visibleRef.current;\n if (v.set === null) return true;\n const id = v.ids[index];\n return id === undefined ? true : v.set.has(id);\n };\n return new OverviewController({ getScene, getVisible, size });\n }, [instance, size]);\n\n /** O(1) rect update from the store viewport over the LAST rasterized frame.\n * `zoom` is screen px per space unit (§13); the world extent in view is the\n * host container's CSS size / zoom — measured from the positioned ancestor\n * (the <Graph> container). Zero-size hosts (jsdom/unmounted) degrade to a\n * MIN_RECT_PX center marker. */\n const updateRect = (v: ViewportState | null): void => {\n const rect = rectRef.current;\n if (rect === null) return;\n if (v === null || v.zoom <= 0) {\n rect.style.display = 'none';\n return;\n }\n const host = rootRef.current?.offsetParent;\n const hostW = host instanceof HTMLElement ? host.clientWidth : 0;\n const hostH = host instanceof HTMLElement ? host.clientHeight : 0;\n const halfW = hostW > 0 ? hostW / v.zoom / 2 : 0;\n const halfH = hostH > 0 ? hostH / v.zoom / 2 : 0;\n // Two opposite world corners (world y grows up, minimap y grows down).\n const a = controller.worldToMinimap(v.x - halfW, v.y + halfH);\n const b = controller.worldToMinimap(v.x + halfW, v.y - halfH);\n if (a === null || b === null) {\n rect.style.display = 'none'; // nothing rasterized yet\n return;\n }\n const w = Math.max(Math.abs(b[0] - a[0]), MIN_RECT_PX);\n const h = Math.max(Math.abs(b[1] - a[1]), MIN_RECT_PX);\n const x = (a[0] + b[0]) / 2 - w / 2;\n const y = (a[1] + b[1]) / 2 - h / 2;\n rect.style.display = 'block';\n rect.style.width = `${w}px`;\n rect.style.height = `${h}px`;\n rect.style.transform = `translate3d(${x}px, ${y}px, 0)`;\n };\n\n // Lane 1: thumbnail refresh — interval-probed controller throttle.\n useEffect(() => {\n const blit = (): void => {\n const state = instance.store.getState();\n // §9.1 mask lane for the dimmed-alpha rasterization.\n if (state.visible.nodes !== state.nodeCount || state.hiddenNodeIds.size > 0) {\n const ids = instance.getSceneNodeIds();\n visibleRef.current = { ids, set: new Set(instance.getVisibleNodeIds()) };\n } else {\n visibleRef.current = { ids: [], set: null };\n }\n const raster = controller.rasterize();\n const canvas = canvasRef.current;\n if (canvas === null) return;\n const ctx = canvas.getContext('2d');\n if (ctx === null) return;\n if (raster === null) {\n ctx.clearRect(0, 0, size, size);\n updateRect(null);\n return;\n }\n if (typeof ImageData === 'undefined') return; // non-DOM environment\n // Copy: ImageData requires a plain-ArrayBuffer-backed clamped array.\n const pixels = new Uint8ClampedArray(raster.bitmap);\n ctx.putImageData(new ImageData(pixels, controller.size, controller.size), 0, 0);\n // A fresh frame can move the world→minimap mapping: re-derive the rect.\n updateRect(instance.store.getState().viewport);\n };\n const tick = (): void => {\n const state = instance.store.getState();\n // `revisions.render` is the v0.9 positions epoch (see module JSDoc):\n // idle with an unchanged render revision is provably position-stable.\n if (!controller.shouldRefresh(Date.now(), state.simulationRunning, state.revisions.render)) {\n return;\n }\n blit();\n };\n tick(); // first paint without waiting a full interval\n const timer = setInterval(tick, OVERVIEW_HOT_INTERVAL_MS);\n return () => {\n clearInterval(timer);\n };\n // updateRect is stable per (instance, controller) — refs + controller\n // only — so it is deliberately not a dependency (this repo does not\n // configure the react-hooks lint plugin).\n }, [instance, controller, size]);\n\n // Lane 2: viewport rectangle — imperative store subscription, O(1) per\n // viewport publication, no re-rasterize and no React re-render.\n useEffect(() => {\n let prev = instance.store.getState().viewport;\n updateRect(prev);\n return instance.store.subscribe(() => {\n const next = instance.store.getState().viewport;\n if (next === prev) return;\n prev = next;\n updateRect(next);\n });\n // updateRect deliberately not a dependency (see the refresh effect above).\n }, [instance, controller]);\n\n // Lane 3: pan. Down pans immediately; move keeps panning while dragging.\n const panTo = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n const canvas = canvasRef.current;\n if (canvas === null) return;\n const b = canvas.getBoundingClientRect();\n // Canvas CSS size vs raster size (zero-size rects — jsdom — map 1:1).\n const sx = b.width > 0 ? controller.size / b.width : 1;\n const sy = b.height > 0 ? controller.size / b.height : 1;\n const world = controller.minimapToWorld((e.clientX - b.left) * sx, (e.clientY - b.top) * sy);\n if (world === null) return; // nothing rasterized yet — nothing to map\n instance.setViewport({ x: world[0], y: world[1] });\n };\n\n const onPointerDown = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n e.preventDefault();\n draggingRef.current = true;\n try {\n e.currentTarget.setPointerCapture(e.pointerId);\n } catch {\n /* pointer capture unavailable (jsdom/legacy) */\n }\n panTo(e);\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n if (!draggingRef.current) return;\n panTo(e);\n };\n\n const endDrag = (): void => {\n draggingRef.current = false;\n };\n\n return (\n <div\n ref={rootRef}\n data-orbit-minimap=\"\"\n role=\"group\"\n aria-label={props.label ?? 'Graph minimap'}\n className={props.className}\n style={\n props.className !== undefined\n ? // The thumbnail is MECHANICAL, not decorative: dropping its box\n // would collapse the canvas, so size survives the class hook.\n { width: size, height: size, ...props.style }\n : mergeStyle(\n {\n ...cornerStyle(props.position ?? 'bottom-right', props.offset),\n ...ROOT_STYLE,\n width: size,\n height: size,\n },\n props.style,\n )\n }\n >\n <canvas\n ref={canvasRef}\n data-orbit-minimap-canvas=\"\"\n width={size}\n height={size}\n style={CANVAS_STYLE}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n onPointerLeave={endDrag}\n />\n <div ref={rectRef} data-orbit-minimap-viewport=\"\" style={RECT_STYLE} />\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Minimap/index.tsx"],"names":[],"mappings":"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","file":"Minimap.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Minimap` — `<GraphMinimap>`, the\n * whole-graph thumbnail + viewport rectangle.\n *\n * v0.9 is CPU-fallback-only (no engine exposes `capabilities.overviewPass`\n * cosmos matrix): the component owns an {@link OverviewController} and\n * blits its `rasterize` output into a small canvas via `putImageData`. The\n * three lanes stay strictly decoupled:\n *\n * 1. **Thumbnail refresh** — a 500 ms `setInterval` probes the controller's\n * `shouldRefresh(now, simulationRunning, epoch)` gate, so the effective\n * cadence is the controller's spec budget (≤ 2 Hz hot, ≤ 1 Hz idle, zero\n * work idle-with-unchanged-epoch) and each `true` is followed by exactly\n * one `rasterize` + blit. The interval — not a per-frame engine lane —\n * is the documented v0.9 cadence: the `onFrame` fan-out only reaches\n * React through the label lane, which is gated on labels being enabled,\n * so a plain timer is the correct always-on ≤ 2 Hz driver. The epoch is\n * `revisions.render` (a positions-bearing superset — over-approximate but\n * bounded by the same throttle).\n * 2. **Viewport rectangle** — an imperative store subscription writes the\n * rect's transform/size O(1) per `store.viewport` publication (CSS\n * transform on a ref; no React re-render, no re-rasterize).\n * 3. **Pan** — pointer down/drag maps minimap px → world via the\n * controller's `minimapToWorld` (exact inverse over the last rasterized\n * frame) and calls `instance.setViewport({x, y})` — real camera pans.\n *\n * Scene source (documented v0.9 trim): the instance does not yet expose the\n * posBuf mirror (reserved for the columnar lane), so the\n * component probes a pinned structural seam `getOverviewScene` first\n * (optional access — picked up automatically once core lands\n * it) and falls back to the DECLARED node positions (`GraphNode.x/y`) of the\n * current scene roster — exact for prepared/fixed-position datasets from\n * orbit-data, and empty for force layouts without a position mirror.\n * Position-less nodes rasterize as NaN tombstones (skipped); mask-hidden nodes draw dimmed\n * via `getVisible`.\n */\n\nimport { useEffect, useMemo, useRef } from 'react';\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactElement } from 'react';\nimport { OverviewController, OVERVIEW_HOT_INTERVAL_MS } from '@modernrelay/orbit-core';\nimport type { NodeId, OverviewScene, ViewportState } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { cornerStyle, mergeStyle, useResolvedInstance } from '../shared';\nimport type { GraphCorner } from '../shared';\n\n/** Default thumbnail edge (px) — the component default; the CPU target\n * default (256²) applies when a caller constructs a bare controller. */\nconst SIZE_DEFAULT = 200;\n\n/** Minimum rendered viewport-rect edge so a tiny/degenerate viewport stays\n * visible as a marker. */\nconst MIN_RECT_PX = 6;\n\n/** Pinned structural seam: the instance-side\n * posBuf mirror read. Optional access until core lands it — this view\n * stays signature-compatible when the member becomes required. */\ninterface OverviewSceneSurface {\n getOverviewScene?(): OverviewScene | null;\n}\n\nexport interface GraphMinimapProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Square thumbnail edge in CSS px (also the raster resolution). Default 200. */\n size?: number;\n /** Accessible name. Default 'Graph minimap'. */\n label?: string;\n /** Corner this panel anchors to. Default 'bottom-right'. */\n position?: GraphCorner;\n /** Distance from the two anchored edges in CSS px. Default 12. */\n offset?: number;\n /** Class hook for the root; providing it drops the default styles\n * INCLUDING placement — position it yourself in CSS. */\n className?: string;\n /** Style overrides merged over the defaults; an inset you set here\n * wins over the one `position` implies (resolved per axis). */\n style?: CSSProperties;\n}\n\nconst ROOT_STYLE: CSSProperties = {\n pointerEvents: 'auto',\n overflow: 'hidden',\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.85)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n};\n\nconst CANVAS_STYLE: CSSProperties = {\n display: 'block',\n cursor: 'crosshair',\n touchAction: 'none',\n};\n\nconst RECT_STYLE: CSSProperties = {\n position: 'absolute',\n left: 0,\n top: 0,\n display: 'none',\n border: '1px solid rgba(255, 255, 255, 0.85)',\n background: 'rgba(255, 255, 255, 0.12)',\n pointerEvents: 'none',\n willChange: 'transform',\n boxSizing: 'border-box',\n};\n\n/** Declared-position scene over the current roster, cached by the roster's\n * identity (`scene.idByIndex` is a stable reference per reconciled scene). */\ninterface DeclaredSceneCache {\n ids: readonly NodeId[];\n scene: OverviewScene | null;\n}\n\nexport function GraphMinimap(props: GraphMinimapProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphMinimap>');\n const size = props.size ?? SIZE_DEFAULT;\n\n const rootRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const rectRef = useRef<HTMLDivElement | null>(null);\n const draggingRef = useRef(false);\n const declaredSceneRef = useRef<DeclaredSceneCache>({ ids: [], scene: null });\n /** mask lane for `getVisible`, recomputed once per refresh tick. */\n const visibleRef = useRef<{ ids: readonly NodeId[]; set: ReadonlySet<NodeId> | null }>({\n ids: [],\n set: null,\n });\n\n const controller = useMemo(() => {\n const getScene = (): OverviewScene | null => {\n // 1. Pinned core seam: the posBuf mirror, once it lands.\n const mirrored = (instance as unknown as OverviewSceneSurface).getOverviewScene?.();\n if (mirrored !== undefined) return mirrored;\n // 2. v0.9 fallback: declared node positions over the scene roster.\n const ids = instance.getSceneNodeIds();\n if (ids.length === 0) return null;\n const cache = declaredSceneRef.current;\n if (cache.ids === ids) return cache.scene;\n const positions = new Float32Array(2 * ids.length);\n let known = 0;\n for (let i = 0; i < ids.length; i++) {\n const node = instance.getNode(ids[i]!);\n const x = node?.x;\n const y = node?.y;\n if (typeof x === 'number' && typeof y === 'number') {\n positions[2 * i] = x;\n positions[2 * i + 1] = y;\n known++;\n } else {\n positions[2 * i] = NaN;\n positions[2 * i + 1] = NaN;\n }\n }\n const scene: OverviewScene | null = known > 0 ? { positions, count: ids.length } : null;\n declaredSceneRef.current = { ids, scene };\n return scene;\n };\n const getVisible = (index: number): boolean => {\n const v = visibleRef.current;\n if (v.set === null) return true;\n const id = v.ids[index];\n return id === undefined ? true : v.set.has(id);\n };\n return new OverviewController({ getScene, getVisible, size });\n }, [instance, size]);\n\n /** O(1) rect update from the store viewport over the LAST rasterized frame.\n * `zoom` is screen px per space unit; the world extent in view is the\n * host container's CSS size / zoom — measured from the positioned ancestor\n * (the <Graph> container). Zero-size hosts (jsdom/unmounted) degrade to a\n * MIN_RECT_PX center marker. */\n const updateRect = (v: ViewportState | null): void => {\n const rect = rectRef.current;\n if (rect === null) return;\n if (v === null || v.zoom <= 0) {\n rect.style.display = 'none';\n return;\n }\n const host = rootRef.current?.offsetParent;\n const hostW = host instanceof HTMLElement ? host.clientWidth : 0;\n const hostH = host instanceof HTMLElement ? host.clientHeight : 0;\n const halfW = hostW > 0 ? hostW / v.zoom / 2 : 0;\n const halfH = hostH > 0 ? hostH / v.zoom / 2 : 0;\n // Two opposite world corners (world y grows up, minimap y grows down).\n const a = controller.worldToMinimap(v.x - halfW, v.y + halfH);\n const b = controller.worldToMinimap(v.x + halfW, v.y - halfH);\n if (a === null || b === null) {\n rect.style.display = 'none'; // nothing rasterized yet\n return;\n }\n const w = Math.max(Math.abs(b[0] - a[0]), MIN_RECT_PX);\n const h = Math.max(Math.abs(b[1] - a[1]), MIN_RECT_PX);\n const x = (a[0] + b[0]) / 2 - w / 2;\n const y = (a[1] + b[1]) / 2 - h / 2;\n rect.style.display = 'block';\n rect.style.width = `${w}px`;\n rect.style.height = `${h}px`;\n rect.style.transform = `translate3d(${x}px, ${y}px, 0)`;\n };\n\n // Lane 1: thumbnail refresh — interval-probed controller throttle.\n useEffect(() => {\n const blit = (): void => {\n const state = instance.store.getState();\n // mask lane for the dimmed-alpha rasterization.\n if (state.visible.nodes !== state.nodeCount || state.hiddenNodeIds.size > 0) {\n const ids = instance.getSceneNodeIds();\n visibleRef.current = { ids, set: new Set(instance.getVisibleNodeIds()) };\n } else {\n visibleRef.current = { ids: [], set: null };\n }\n const raster = controller.rasterize();\n const canvas = canvasRef.current;\n if (canvas === null) return;\n const ctx = canvas.getContext('2d');\n if (ctx === null) return;\n if (raster === null) {\n ctx.clearRect(0, 0, size, size);\n updateRect(null);\n return;\n }\n if (typeof ImageData === 'undefined') return; // non-DOM environment\n // Copy: ImageData requires a plain-ArrayBuffer-backed clamped array.\n const pixels = new Uint8ClampedArray(raster.bitmap);\n ctx.putImageData(new ImageData(pixels, controller.size, controller.size), 0, 0);\n // A fresh frame can move the world→minimap mapping: re-derive the rect.\n updateRect(instance.store.getState().viewport);\n };\n const tick = (): void => {\n const state = instance.store.getState();\n // `revisions.render` is the v0.9 positions epoch (see module JSDoc):\n // idle with an unchanged render revision is provably position-stable.\n if (!controller.shouldRefresh(Date.now(), state.simulationRunning, state.revisions.render)) {\n return;\n }\n blit();\n };\n tick(); // first paint without waiting a full interval\n const timer = setInterval(tick, OVERVIEW_HOT_INTERVAL_MS);\n return () => {\n clearInterval(timer);\n };\n // updateRect is stable for each (instance, controller) pair — refs + controller\n // only — so it is deliberately not a dependency (this repo does not\n // configure the react-hooks lint plugin).\n }, [instance, controller, size]);\n\n // Lane 2: viewport rectangle — imperative store subscription, O(1) per\n // viewport publication, no re-rasterize and no React re-render.\n useEffect(() => {\n let prev = instance.store.getState().viewport;\n updateRect(prev);\n return instance.store.subscribe(() => {\n const next = instance.store.getState().viewport;\n if (next === prev) return;\n prev = next;\n updateRect(next);\n });\n // updateRect deliberately not a dependency (see the refresh effect above).\n }, [instance, controller]);\n\n // Lane 3: pan. Down pans immediately; move keeps panning while dragging.\n const panTo = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n const canvas = canvasRef.current;\n if (canvas === null) return;\n const b = canvas.getBoundingClientRect();\n // Canvas CSS size vs raster size (zero-size rects — jsdom — map 1:1).\n const sx = b.width > 0 ? controller.size / b.width : 1;\n const sy = b.height > 0 ? controller.size / b.height : 1;\n const world = controller.minimapToWorld((e.clientX - b.left) * sx, (e.clientY - b.top) * sy);\n if (world === null) return; // nothing rasterized yet — nothing to map\n instance.setViewport({ x: world[0], y: world[1] });\n };\n\n const onPointerDown = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n e.preventDefault();\n draggingRef.current = true;\n try {\n e.currentTarget.setPointerCapture(e.pointerId);\n } catch {\n /* pointer capture unavailable (jsdom/legacy) */\n }\n panTo(e);\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n if (!draggingRef.current) return;\n panTo(e);\n };\n\n const endDrag = (): void => {\n draggingRef.current = false;\n };\n\n return (\n <div\n ref={rootRef}\n data-orbit-minimap=\"\"\n role=\"group\"\n aria-label={props.label ?? 'Graph minimap'}\n className={props.className}\n style={\n props.className !== undefined\n ? // The thumbnail is MECHANICAL, not decorative: dropping its box\n // would collapse the canvas, so size survives the class hook.\n { width: size, height: size, ...props.style }\n : mergeStyle(\n {\n ...cornerStyle(props.position ?? 'bottom-right', props.offset),\n ...ROOT_STYLE,\n width: size,\n height: size,\n },\n props.style,\n )\n }\n >\n <canvas\n ref={canvasRef}\n data-orbit-minimap-canvas=\"\"\n width={size}\n height={size}\n style={CANVAS_STYLE}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n onPointerLeave={endDrag}\n />\n <div ref={rectRef} data-orbit-minimap-viewport=\"\" style={RECT_STYLE} />\n </div>\n );\n}\n"]}
|
|
@@ -4,7 +4,7 @@ import '@modernrelay/orbit-core';
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* `@modernrelay/orbit-react/components/Navigator` — `<GraphNavigator>`, the
|
|
7
|
-
* bounded semantic navigator
|
|
7
|
+
* bounded semantic navigator.
|
|
8
8
|
*
|
|
9
9
|
* The navigator is the keyboard/screen-reader equivalent surface for the
|
|
10
10
|
* canvas: a visually compact panel with `role="listbox"` semantics driven by
|
|
@@ -15,30 +15,30 @@ import '@modernrelay/orbit-core';
|
|
|
15
15
|
* row per entity.
|
|
16
16
|
*
|
|
17
17
|
* Sections:
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
18
|
+
* - **Search results** — the last completed search
|
|
19
|
+
* (`store.search`), shown whenever the slice is non-null (it clears on
|
|
20
|
+
* `clearSearch` / dataset swap). Paged like every other list; Enter
|
|
21
|
+
* routes through `instance.activateSearchResult` (result contract:
|
|
22
|
+
* an in-scene, mask-visible id focuses; anything else is classification
|
|
23
|
+
* only — the navigator never mutates scope/filters). Row text prefers the
|
|
24
|
+
* service-supplied `label`.
|
|
25
|
+
* - **Focused node** — the transiently focused root plus its paged 1-hop
|
|
26
|
+
* neighborhood.
|
|
27
|
+
* - **Selection** — the current node selection, paged.
|
|
28
|
+
* - **All nodes** — the entry list (first `navigatorWindow` nodes in
|
|
29
|
+
* accepted-base order, paged over the full set), shown when nothing is
|
|
30
|
+
* focused.
|
|
31
31
|
*
|
|
32
32
|
* ## Keyboard model — `keyboardMode: 'topology'` (the v0.4 default and only mode)
|
|
33
33
|
*
|
|
34
|
-
* | Key
|
|
34
|
+
* | Key | Action |
|
|
35
35
|
* |---------------------|-------------------------------------------------------------------|
|
|
36
|
-
* | ArrowDown / ArrowUp | move the active item within the current list
|
|
37
|
-
* | Home / End
|
|
38
|
-
* | PageDown / PageUp
|
|
39
|
-
* | Enter
|
|
40
|
-
* | Space
|
|
41
|
-
* | Escape
|
|
36
|
+
* | ArrowDown / ArrowUp | move the active item within the current list |
|
|
37
|
+
* | Home / End | move to the bounds of the current list |
|
|
38
|
+
* | PageDown / PageUp | next / previous page of the active item's list |
|
|
39
|
+
* | Enter | focus the node (`instance.focusNode` — camera fly) and re-root the neighborhood at it |
|
|
40
|
+
* | Space | toggle the node's selection membership |
|
|
41
|
+
* | Escape | clear the transient focus (returns to the entry list) |
|
|
42
42
|
*
|
|
43
43
|
* `keyboardMode: 'spatial'` (arrows move by screen direction) is a
|
|
44
44
|
* documented post-v0.4 mode and intentionally NOT part of the accepted prop
|
|
@@ -46,7 +46,7 @@ import '@modernrelay/orbit-core';
|
|
|
46
46
|
*
|
|
47
47
|
* Text names resolve via `accessibility.getAccessibleLabel`, falling back to
|
|
48
48
|
* `attrs.label` (when a string) and then the node id — always rendered as
|
|
49
|
-
* TEXT NODES
|
|
49
|
+
* TEXT NODES.
|
|
50
50
|
* Each item exposes selection state (`aria-selected`), its neighbor count
|
|
51
51
|
* when known, and pinned/hidden state in text (e.g. `hub · 5 neighbors ·
|
|
52
52
|
* pinned`).
|
|
@@ -63,12 +63,12 @@ import '@modernrelay/orbit-core';
|
|
|
63
63
|
* neighborhood has been resolved (the current and previously visited roots);
|
|
64
64
|
* a public O(1) degree read is a post-v0.4 core surface.
|
|
65
65
|
*
|
|
66
|
-
* Focus never lives only in WebGL pixels
|
|
66
|
+
* Focus never lives only in WebGL pixels: the active item carries a
|
|
67
67
|
* visible DOM focus outline and receives real DOM focus on keyboard moves.
|
|
68
68
|
* Sync is navigator → graph (Enter → `focusNode` camera fly); graph →
|
|
69
69
|
* navigator focus sync is deferred past v0.4. Space routes through
|
|
70
70
|
* `instance.selectNodes`, so a controlled selection surfaces intent only
|
|
71
|
-
*
|
|
71
|
+
* — `aria-selected` always reflects the store.
|
|
72
72
|
*/
|
|
73
73
|
|
|
74
74
|
interface GraphNavigatorProps {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useResolvedInstance } from '../chunk-
|
|
1
|
+
import { useResolvedInstance } from '../chunk-NPOV5GA3.js';
|
|
2
2
|
import { useId, useState, useRef, useEffect, useCallback, useSyncExternalStore } from 'react';
|
|
3
3
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -134,7 +134,7 @@ function GraphNavigator(props) {
|
|
|
134
134
|
search: searchResults.map((r) => r.id),
|
|
135
135
|
neighbors: root !== null ? root.neighbors : [],
|
|
136
136
|
selection: slice.selection.nodeIds,
|
|
137
|
-
//
|
|
137
|
+
// the entry list is the SCENE roster (scope applied, mask not)
|
|
138
138
|
// hidden/filtered nodes stay listed with their state exposed in text.
|
|
139
139
|
entry: root === null ? instance.getSceneNodeIds() : []
|
|
140
140
|
};
|
|
@@ -148,7 +148,7 @@ function GraphNavigator(props) {
|
|
|
148
148
|
listKey: "search",
|
|
149
149
|
label: "Search results",
|
|
150
150
|
groupLabel: `Search results for ${slice.search.query}: ${count} ${count === 1 ? "result" : "results"}, page ${view.page + 1} of ${view.pageCount}`,
|
|
151
|
-
//
|
|
151
|
+
// row text prefers the service-supplied label (still a TEXT
|
|
152
152
|
// NODE); results may name ids outside the model — nameOf falls back to
|
|
153
153
|
// the id for those.
|
|
154
154
|
rows: view.items.map((id, i) => {
|