@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/Navigator/index.tsx"],"names":[],"mappings":"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","file":"Navigator.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Navigator` — `<GraphNavigator>`, the\n * bounded semantic navigator (spec §15.1; S7-T11/T19).\n *\n * The navigator is the keyboard/screen-reader equivalent surface for the\n * canvas: a visually compact panel with `role=\"listbox\"` semantics driven by\n * a roving tabindex — exactly one item is tabbable at any time, and\n * `aria-activedescendant` on the listbox container mirrors the active item's\n * stable DOM id. It is bounded by design: every list is paged by\n * `accessibility.navigatorWindow` (default 50), so the DOM never grows one\n * row per entity.\n *\n * Sections:\n * - **Search results** (S11-T15) — the last completed §16.5 search\n * (`store.search`), shown whenever the slice is non-null (it clears on\n * `clearSearch()` / dataset swap). Paged like every other list; Enter\n * routes through `instance.activateSearchResult` (§16.5 result contract:\n * an in-scene, mask-visible id focuses; anything else is classification\n * only — the navigator never mutates scope/filters). Row text prefers the\n * service-supplied `label`.\n * - **Focused node** — the transiently focused root plus its paged 1-hop\n * neighborhood.\n * - **Selection** — the current node selection, paged.\n * - **All nodes** — the entry list (first `navigatorWindow` nodes in\n * accepted-base order, paged over the full set), shown when nothing is\n * focused.\n *\n * ## Keyboard model — `keyboardMode: 'topology'` (the v0.4 default and only mode)\n *\n * | Key | Action |\n * |---------------------|-------------------------------------------------------------------|\n * | ArrowDown / ArrowUp | move the active item within the current list |\n * | Home / End | move to the bounds of the current list |\n * | PageDown / PageUp | next / previous page of the active item's list |\n * | Enter | focus the node (`instance.focusNode` — camera fly) and re-root the neighborhood at it |\n * | Space | toggle the node's selection membership |\n * | Escape | clear the transient focus (returns to the entry list) |\n *\n * `keyboardMode: 'spatial'` (arrows move by screen direction) is a\n * documented post-v0.4 mode and intentionally NOT part of the accepted prop\n * union yet — only `'topology'` type-checks in v0.4.\n *\n * Text names resolve via `accessibility.getAccessibleLabel`, falling back to\n * `attrs.label` (when a string) and then the node id — always rendered as\n * TEXT NODES (§14 untrusted-content rule; markup in labels stays literal).\n * Each item exposes selection state (`aria-selected`), its neighbor count\n * when known, and pinned/hidden state in text (e.g. `hub · 5 neighbors ·\n * pinned`).\n *\n * Neighborhood semantics (v0.4): direction is UNDIRECTED — `focusNode`\n * resolves the 1-hop neighbor ids through the engine's adjacency when the\n * adapter provides one, else through the core CSR adjacency\n * (`buildAdjacency`/`neighborsOf`) built lazily per accepted model revision\n * and invalidated on model change; self-loops are excluded and parallel\n * edges deduplicate. The navigator caches the resolved neighborhood and the\n * per-node neighbor counts it has learned in refs keyed by\n * `revisions.model`; a model change drops the (now stale) transient root and\n * returns to the entry list. Neighbor counts therefore appear on items whose\n * neighborhood has been resolved (the current and previously visited roots);\n * a public O(1) degree read is a post-v0.4 core surface.\n *\n * Focus never lives only in WebGL pixels (§15.1): the active item carries a\n * visible DOM focus outline and receives real DOM focus on keyboard moves.\n * Sync is navigator → graph (Enter → `focusNode` camera fly); graph →\n * navigator focus sync is deferred past v0.4. Space routes through\n * `instance.selectNodes`, so a controlled selection surfaces intent only\n * (§6.4) — `aria-selected` always reflects the store.\n */\n\nimport { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, KeyboardEvent, ReactElement } from 'react';\nimport type {\n AccessibilityConfig,\n GraphNode,\n NodeId,\n SearchResult,\n SelectionState,\n} from '@modernrelay/orbit-core';\nimport { useResolvedInstance } from '../shared';\nimport type { AnyGraphInstance } from '../../GraphProvider';\n\n/** §15.1 default page size when `accessibility.navigatorWindow` is unset. */\nconst DEFAULT_NAVIGATOR_WINDOW = 50;\n\nexport interface GraphNavigatorProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /**\n * Keyboard model. v0.4 ships `'topology'` (the default; documented above);\n * `'spatial'` (arrows-by-screen-direction) is post-v0.4 and not accepted\n * by this union yet.\n */\n keyboardMode?: 'topology';\n /** Accessible name of the navigator panel. Default 'Graph navigator'. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n}\n\n/** Which paged id list a row belongs to (drives PageUp/PageDown targeting). */\ntype PagedListKey = 'search' | 'neighbors' | 'selection' | 'entry';\n\ninterface NavRow {\n /** Stable identity across renders: `${list}:${nodeId}` (`root:` for the root row). */\n key: string;\n /** Stable DOM id (aria-activedescendant target). */\n domId: string;\n nodeId: NodeId;\n listKey: PagedListKey;\n text: string;\n selected: boolean;\n}\n\ninterface NavSection {\n key: 'search' | 'focused' | 'selection' | 'entry';\n listKey: PagedListKey;\n label: string;\n /** Group accessible name; carries the 'page x of y' state. */\n groupLabel: string;\n rows: NavRow[];\n page: number;\n pageCount: number;\n /** Total pageable items (excludes the focused section's root chrome row). */\n total: number;\n}\n\n/** Transient navigator focus: the neighborhood root (§15.1). */\ninterface NavRoot {\n id: NodeId;\n neighbors: readonly NodeId[];\n /** Model revision the neighborhood was resolved against. */\n model: number;\n}\n\ninterface NavStoreSlice {\n selection: SelectionState;\n pins: ReadonlyMap<NodeId, readonly [number, number]>;\n hidden: ReadonlySet<NodeId>;\n model: number;\n nodeCount: number;\n /** Last completed §16.5 search (S11-T15); null hides the section. */\n search: { query: string; results: readonly SearchResult[] } | null;\n}\n\n/** Narrow store subscription: re-renders only when a navigator-relevant slice\n * changes (never on per-frame viewport writes). */\nfunction useNavStoreSlice(instance: AnyGraphInstance): NavStoreSlice {\n const cacheRef = useRef<NavStoreSlice | null>(null);\n const subscribe = useCallback(\n (cb: () => void) => instance.store.subscribe(cb),\n [instance],\n );\n const getSnapshot = useCallback((): NavStoreSlice => {\n const s = instance.store.getState();\n const prev = cacheRef.current;\n if (\n prev !== null &&\n prev.selection === s.selection &&\n prev.pins === s.pins &&\n prev.hidden === s.hiddenNodeIds &&\n prev.model === s.revisions.model &&\n prev.nodeCount === s.nodeCount &&\n prev.search === s.search\n ) {\n return prev;\n }\n const next: NavStoreSlice = {\n selection: s.selection,\n pins: s.pins,\n hidden: s.hiddenNodeIds,\n model: s.revisions.model,\n nodeCount: s.nodeCount,\n search: s.search,\n };\n cacheRef.current = next;\n return next;\n }, [instance]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\ninterface PageWindow {\n page: number;\n pageCount: number;\n items: readonly NodeId[];\n}\n\n/** Clamp a raw page into `[0, pageCount)` and slice that page's ids. */\nfunction pageWindow(all: readonly NodeId[], rawPage: number, size: number): PageWindow {\n const pageCount = Math.max(1, Math.ceil(all.length / size));\n const page = Math.min(Math.max(rawPage, 0), pageCount - 1);\n return { page, pageCount, items: all.slice(page * size, page * size + size) };\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst PANEL_STYLE: CSSProperties = {\n pointerEvents: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n maxWidth: 320,\n fontSize: 12,\n};\nconst LIST_STYLE: CSSProperties = { overflowY: 'auto', maxHeight: 320 };\nconst HEADER_STYLE: CSSProperties = { fontWeight: 600, padding: '4px 6px' };\nconst ITEM_STYLE: CSSProperties = {\n padding: '2px 6px',\n cursor: 'default',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n/** §15.1 visible DOM focus ring on the active item. */\nconst ACTIVE_ITEM_STYLE: CSSProperties = {\n ...ITEM_STYLE,\n outline: '2px solid currentColor',\n outlineOffset: -2,\n};\nconst PAGING_STYLE: CSSProperties = { display: 'flex', alignItems: 'center', gap: 8 };\nconst EMPTY_STYLE: CSSProperties = { padding: '4px 6px', fontStyle: 'italic' };\n\nexport function GraphNavigator(props: GraphNavigatorProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphNavigator>');\n const slice = useNavStoreSlice(instance);\n\n // 'topology' is the only v0.4 mode; the prop exists so the shape is stable\n // when 'spatial' lands post-v0.4 (no behavioral switch yet).\n void props.keyboardMode;\n\n const baseId = useId();\n const [root, setRoot] = useState<NavRoot | null>(null);\n const [pages, setPages] = useState<Record<PagedListKey, number>>({\n search: 0,\n neighbors: 0,\n selection: 0,\n entry: 0,\n });\n /** Active row identity (roving target); null → first row. */\n const [activeKey, setActiveKey] = useState<string | null>(null);\n\n /** Stable per-node DOM-id tokens (node ids may contain arbitrary text). */\n const tokensRef = useRef<Map<NodeId, number>>(new Map());\n /** Learned neighbor counts, cached per model revision (see module JSDoc). */\n const degreesRef = useRef<{ model: number; counts: Map<NodeId, number> }>({\n model: -1,\n counts: new Map(),\n });\n const itemsRef = useRef<Map<string, HTMLElement>>(new Map());\n /** Set by keyboard handlers so the focus effect moves DOM focus once. */\n const pendingFocusRef = useRef(false);\n\n // A model change invalidates the resolved neighborhood: drop the transient\n // root (back to the entry list) rather than rendering stale rows.\n useEffect(() => {\n if (root !== null && root.model !== slice.model) setRoot(null);\n }, [root, slice.model]);\n\n // §13: release the keyboard ring — but ONLY when no pointer hover owns the\n // shared channel (blur while the pointer rests on a canvas node must not\n // erase that node's hover ring; hover already superseded the sticky\n // keyboard target inside core, so there is nothing else to clear).\n const releaseEmphasis = useCallback((): void => {\n if (instance.store.getState().hover.nodeId === null) instance.emphasizeNode(null);\n }, [instance]);\n\n // A ring left by keyboard navigation must not outlive the navigator.\n useEffect(() => () => releaseEmphasis(), [releaseEmphasis]);\n\n // Idempotent render-time cache reset (ref only — safe under StrictMode).\n if (degreesRef.current.model !== slice.model) {\n degreesRef.current = { model: slice.model, counts: new Map() };\n }\n\n const accessibility = instance.getAccessibility() as\n | AccessibilityConfig<Record<string, unknown>>\n | undefined;\n const rawWindow = accessibility?.navigatorWindow;\n const windowSize =\n typeof rawWindow === 'number' && Number.isFinite(rawWindow) && rawWindow >= 1\n ? Math.floor(rawWindow)\n : DEFAULT_NAVIGATOR_WINDOW;\n\n const getAccessibleLabel = accessibility?.getAccessibleLabel;\n const nameOf = (id: NodeId): string => {\n const node = instance.getNode(id) as GraphNode<Record<string, unknown>> | undefined;\n if (node === undefined) return id;\n if (getAccessibleLabel !== undefined) {\n try {\n return getAccessibleLabel(node);\n } catch {\n // fall through to the built-in resolution\n }\n }\n const label = node.attrs?.['label'];\n return typeof label === 'string' ? label : node.id;\n };\n\n const selectedSet = new Set<NodeId>(slice.selection.nodeIds);\n\n /** '{name} · {n} neighbors · pinned · hidden' — always a single text node. */\n const rowText = (id: NodeId): string => {\n const parts = [nameOf(id)];\n const count = degreesRef.current.counts.get(id);\n if (count !== undefined) parts.push(count === 1 ? '1 neighbor' : `${count} neighbors`);\n if (slice.pins.has(id)) parts.push('pinned');\n if (slice.hidden.has(id)) parts.push('hidden');\n return parts.join(' · ');\n };\n\n const domIdFor = (listKey: string, id: NodeId): string => {\n const tokens = tokensRef.current;\n let token = tokens.get(id);\n if (token === undefined) {\n token = tokens.size;\n tokens.set(id, token);\n }\n return `${baseId}nav-${listKey}-${token}`;\n };\n\n const makeRow = (listKey: PagedListKey, id: NodeId): NavRow => ({\n key: `${listKey}:${id}`,\n domId: domIdFor(listKey, id),\n nodeId: id,\n listKey,\n text: rowText(id),\n selected: selectedSet.has(id),\n });\n\n // --- section/row model (all O(navigatorWindow) work) ---\n const searchResults: readonly SearchResult[] = slice.search === null ? [] : slice.search.results;\n const listItems: Record<PagedListKey, readonly NodeId[]> = {\n search: searchResults.map((r) => r.id),\n neighbors: root !== null ? root.neighbors : [],\n selection: slice.selection.nodeIds,\n // §15.1: the entry list is the SCENE roster (scope applied, mask not) —\n // hidden/filtered nodes stay listed with their state exposed in text.\n entry: root === null ? instance.getSceneNodeIds() : [],\n };\n\n const sections: NavSection[] = [];\n if (slice.search !== null) {\n const view = pageWindow(listItems.search, pages.search, windowSize);\n const count = searchResults.length;\n const start = view.page * windowSize;\n sections.push({\n key: 'search',\n listKey: 'search',\n label: 'Search results',\n groupLabel: `Search results for ${slice.search.query}: ${count} ${\n count === 1 ? 'result' : 'results'\n }, page ${view.page + 1} of ${view.pageCount}`,\n // §16.5: row text prefers the service-supplied label (still a TEXT\n // NODE); results may name ids outside the model — nameOf falls back to\n // the id for those.\n rows: view.items.map((id, i) => {\n const row = makeRow('search', id);\n const label = searchResults[start + i]?.label;\n return label === undefined ? row : { ...row, text: label };\n }),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (root !== null) {\n const view = pageWindow(listItems.neighbors, pages.neighbors, windowSize);\n const rootRow: NavRow = {\n key: `root:${root.id}`,\n domId: domIdFor('root', root.id),\n nodeId: root.id,\n listKey: 'neighbors',\n text: rowText(root.id),\n selected: selectedSet.has(root.id),\n };\n const count = root.neighbors.length;\n sections.push({\n key: 'focused',\n listKey: 'neighbors',\n label: 'Focused node',\n groupLabel: `Focused node ${nameOf(root.id)}: ${count} ${\n count === 1 ? 'neighbor' : 'neighbors'\n }, page ${view.page + 1} of ${view.pageCount}`,\n rows: [rootRow, ...view.items.map((id) => makeRow('neighbors', id))],\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (slice.selection.nodeIds.length > 0) {\n const view = pageWindow(listItems.selection, pages.selection, windowSize);\n const count = slice.selection.nodeIds.length;\n sections.push({\n key: 'selection',\n listKey: 'selection',\n label: 'Selection',\n groupLabel: `Selection: ${count} ${count === 1 ? 'node' : 'nodes'}, page ${\n view.page + 1\n } of ${view.pageCount}`,\n rows: view.items.map((id) => makeRow('selection', id)),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (root === null) {\n const view = pageWindow(listItems.entry, pages.entry, windowSize);\n const count = listItems.entry.length;\n sections.push({\n key: 'entry',\n listKey: 'entry',\n label: 'All nodes',\n groupLabel: `All nodes: ${count} ${count === 1 ? 'node' : 'nodes'}, page ${\n view.page + 1\n } of ${view.pageCount}`,\n rows: view.items.map((id) => makeRow('entry', id)),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n\n const rows: NavRow[] = sections.flatMap((s) => s.rows);\n let activeIndex = activeKey === null ? -1 : rows.findIndex((r) => r.key === activeKey);\n if (activeIndex === -1 && rows.length > 0) activeIndex = 0;\n const activeRow = activeIndex >= 0 ? rows[activeIndex] : undefined;\n const activeSection =\n activeRow !== undefined ? sections.find((s) => s.listKey === activeRow.listKey) : undefined;\n\n // Move real DOM focus to the active item after keyboard-driven changes so\n // the focus ring is a genuine DOM focus, never only a WebGL highlight.\n useEffect(() => {\n if (!pendingFocusRef.current) return;\n pendingFocusRef.current = false;\n if (activeRow === undefined) return;\n itemsRef.current.get(activeRow.domId)?.focus();\n });\n\n // --- keyboard model ('topology'; see module JSDoc) ---\n const focusItemSoon = (): void => {\n pendingFocusRef.current = true;\n };\n\n const moveTo = (index: number): void => {\n if (rows.length === 0) return;\n const row = rows[Math.min(Math.max(index, 0), rows.length - 1)];\n if (row === undefined) return;\n setActiveKey(row.key);\n // §13/§15.1 keyboard parity with pointer hover: ring the active row\n // WITHOUT flying the camera (that stays on Enter via focusNode).\n instance.emphasizeNode(row.nodeId);\n focusItemSoon();\n };\n\n const pageBy = (delta: number): void => {\n if (activeRow === undefined) return;\n const listKey = activeRow.listKey;\n const items = listItems[listKey];\n const pageCount = Math.max(1, Math.ceil(items.length / windowSize));\n const current = Math.min(Math.max(pages[listKey], 0), pageCount - 1);\n const next = Math.min(Math.max(current + delta, 0), pageCount - 1);\n if (next === current) return;\n setPages((p) => ({ ...p, [listKey]: next }));\n const first = items[next * windowSize];\n if (first !== undefined) {\n setActiveKey(`${listKey}:${first}`);\n instance.emphasizeNode(first);\n }\n focusItemSoon();\n };\n\n /** Enter: focus the node in the graph (camera fly) and re-root the\n * neighborhood. The returned ids come from the core adjacency resolution\n * (undirected; see module JSDoc). Search rows route through the §16.5\n * result contract instead: `activateSearchResult` focuses an in-scene,\n * mask-visible id and otherwise only CLASSIFIES — the navigator never\n * mutates scope/filters (the host reacts through its own channels). */\n const activate = (row: NavRow): void => {\n if (row.listKey === 'search') {\n const result = searchResults.find((r) => r.id === row.nodeId);\n if (result !== undefined) instance.activateSearchResult(result);\n return;\n }\n const neighbors = instance.focusNode(row.nodeId);\n degreesRef.current.counts.set(row.nodeId, neighbors.length);\n setRoot({ id: row.nodeId, neighbors, model: slice.model });\n setPages((p) => ({ ...p, neighbors: 0 }));\n setActiveKey(`root:${row.nodeId}`);\n focusItemSoon();\n };\n\n /** Space: toggle membership through the §6.4 selection ownership path.\n * Search rows may name ids outside the accepted model — those no-op. */\n const toggleSelection = (row: NavRow): void => {\n if (row.listKey === 'search' && instance.getNode(row.nodeId) === undefined) return;\n const current = slice.selection.nodeIds;\n const next = current.includes(row.nodeId)\n ? current.filter((id) => id !== row.nodeId)\n : [...current, row.nodeId];\n instance.selectNodes(next);\n };\n\n const clearTransientFocus = (): void => {\n setRoot(null);\n setActiveKey(null);\n releaseEmphasis();\n focusItemSoon();\n };\n\n const onKeyDown = (e: KeyboardEvent<HTMLDivElement>): void => {\n if (activeRow === undefined) return;\n switch (e.key) {\n case 'ArrowDown':\n moveTo(activeIndex + 1);\n break;\n case 'ArrowUp':\n moveTo(activeIndex - 1);\n break;\n case 'Home':\n moveTo(0);\n break;\n case 'End':\n moveTo(rows.length - 1);\n break;\n case 'PageDown':\n pageBy(1);\n break;\n case 'PageUp':\n pageBy(-1);\n break;\n case 'Enter':\n activate(activeRow);\n break;\n case ' ':\n toggleSelection(activeRow);\n break;\n case 'Escape':\n if (root === null) return; // nothing transient — let it bubble\n clearTransientFocus();\n break;\n default:\n return;\n }\n e.preventDefault();\n };\n\n const panelLabel = props.label ?? 'Graph navigator';\n\n return (\n <div\n data-orbit-navigator=\"\"\n role=\"group\"\n aria-label={panelLabel}\n className={props.className}\n style={{ ...PANEL_STYLE, ...props.style }}\n onBlur={(e) => {\n // Focus left the panel entirely (paging buttons stay inside): the\n // keyboard ring clears, mirroring pointer hover leaving the canvas.\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {\n releaseEmphasis();\n }\n }}\n >\n <div\n role=\"listbox\"\n aria-label={`${panelLabel} items`}\n aria-activedescendant={activeRow?.domId}\n aria-keyshortcuts=\"ArrowUp ArrowDown Home End PageUp PageDown Enter Space Escape\"\n onKeyDown={onKeyDown}\n style={LIST_STYLE}\n >\n {rows.length === 0 ? (\n <div role=\"presentation\" style={EMPTY_STYLE}>\n No nodes\n </div>\n ) : (\n sections.map((section) => (\n <div key={section.key} role=\"group\" aria-label={section.groupLabel}>\n <div role=\"presentation\" style={HEADER_STYLE}>\n {section.label}\n </div>\n {section.rows.map((row) => {\n const isActive = row.key === activeRow?.key;\n return (\n <div\n key={row.key}\n id={row.domId}\n role=\"option\"\n aria-selected={row.selected}\n tabIndex={isActive ? 0 : -1}\n ref={(el) => {\n if (el === null) itemsRef.current.delete(row.domId);\n else itemsRef.current.set(row.domId, el);\n }}\n onClick={() => {\n setActiveKey(row.key);\n instance.emphasizeNode(row.nodeId); // active row = ringed row\n focusItemSoon();\n }}\n style={isActive ? ACTIVE_ITEM_STYLE : ITEM_STYLE}\n >\n {row.text}\n </div>\n );\n })}\n </div>\n ))\n )}\n </div>\n {activeSection !== undefined ? (\n <div data-orbit-navigator-paging=\"\" style={PAGING_STYLE}>\n <button\n type=\"button\"\n onClick={() => pageBy(-1)}\n disabled={activeSection.page === 0}\n aria-keyshortcuts=\"PageUp\"\n >\n Previous page\n </button>\n <span role=\"status\" data-orbit-navigator-page-status=\"\">\n {`${activeSection.label}: page ${activeSection.page + 1} of ${\n activeSection.pageCount\n } (${activeSection.total} ${activeSection.total === 1 ? 'item' : 'items'})`}\n </span>\n <button\n type=\"button\"\n onClick={() => pageBy(1)}\n disabled={activeSection.page >= activeSection.pageCount - 1}\n aria-keyshortcuts=\"PageDown\"\n >\n Next page\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Navigator/index.tsx"],"names":[],"mappings":"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","file":"Navigator.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Navigator` — `<GraphNavigator>`, the\n * bounded semantic navigator.\n *\n * The navigator is the keyboard/screen-reader equivalent surface for the\n * canvas: a visually compact panel with `role=\"listbox\"` semantics driven by\n * a roving tabindex — exactly one item is tabbable at any time, and\n * `aria-activedescendant` on the listbox container mirrors the active item's\n * stable DOM id. It is bounded by design: every list is paged by\n * `accessibility.navigatorWindow` (default 50), so the DOM never grows one\n * row per entity.\n *\n * Sections:\n * - **Search results** — the last completed search\n * (`store.search`), shown whenever the slice is non-null (it clears on\n * `clearSearch` / dataset swap). Paged like every other list; Enter\n * routes through `instance.activateSearchResult` (result contract:\n * an in-scene, mask-visible id focuses; anything else is classification\n * only — the navigator never mutates scope/filters). Row text prefers the\n * service-supplied `label`.\n * - **Focused node** — the transiently focused root plus its paged 1-hop\n * neighborhood.\n * - **Selection** — the current node selection, paged.\n * - **All nodes** — the entry list (first `navigatorWindow` nodes in\n * accepted-base order, paged over the full set), shown when nothing is\n * focused.\n *\n * ## Keyboard model — `keyboardMode: 'topology'` (the v0.4 default and only mode)\n *\n * | Key | Action |\n * |---------------------|-------------------------------------------------------------------|\n * | ArrowDown / ArrowUp | move the active item within the current list |\n * | Home / End | move to the bounds of the current list |\n * | PageDown / PageUp | next / previous page of the active item's list |\n * | Enter | focus the node (`instance.focusNode` — camera fly) and re-root the neighborhood at it |\n * | Space | toggle the node's selection membership |\n * | Escape | clear the transient focus (returns to the entry list) |\n *\n * `keyboardMode: 'spatial'` (arrows move by screen direction) is a\n * documented post-v0.4 mode and intentionally NOT part of the accepted prop\n * union yet — only `'topology'` type-checks in v0.4.\n *\n * Text names resolve via `accessibility.getAccessibleLabel`, falling back to\n * `attrs.label` (when a string) and then the node id — always rendered as\n * TEXT NODES.\n * Each item exposes selection state (`aria-selected`), its neighbor count\n * when known, and pinned/hidden state in text (e.g. `hub · 5 neighbors ·\n * pinned`).\n *\n * Neighborhood semantics (v0.4): direction is UNDIRECTED — `focusNode`\n * resolves the 1-hop neighbor ids through the engine's adjacency when the\n * adapter provides one, else through the core CSR adjacency\n * (`buildAdjacency`/`neighborsOf`) built lazily per accepted model revision\n * and invalidated on model change; self-loops are excluded and parallel\n * edges deduplicate. The navigator caches the resolved neighborhood and the\n * per-node neighbor counts it has learned in refs keyed by\n * `revisions.model`; a model change drops the (now stale) transient root and\n * returns to the entry list. Neighbor counts therefore appear on items whose\n * neighborhood has been resolved (the current and previously visited roots);\n * a public O(1) degree read is a post-v0.4 core surface.\n *\n * Focus never lives only in WebGL pixels: the active item carries a\n * visible DOM focus outline and receives real DOM focus on keyboard moves.\n * Sync is navigator → graph (Enter → `focusNode` camera fly); graph →\n * navigator focus sync is deferred past v0.4. Space routes through\n * `instance.selectNodes`, so a controlled selection surfaces intent only\n * — `aria-selected` always reflects the store.\n */\n\nimport { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, KeyboardEvent, ReactElement } from 'react';\nimport type {\n AccessibilityConfig,\n GraphNode,\n NodeId,\n SearchResult,\n SelectionState,\n} from '@modernrelay/orbit-core';\nimport { useResolvedInstance } from '../shared';\nimport type { AnyGraphInstance } from '../../GraphProvider';\n\n/** default page size when `accessibility.navigatorWindow` is unset. */\nconst DEFAULT_NAVIGATOR_WINDOW = 50;\n\nexport interface GraphNavigatorProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /**\n * Keyboard model. v0.4 ships `'topology'` (the default; documented above);\n * `'spatial'` (arrows-by-screen-direction) is post-v0.4 and not accepted\n * by this union yet.\n */\n keyboardMode?: 'topology';\n /** Accessible name of the navigator panel. Default 'Graph navigator'. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n}\n\n/** Which paged id list a row belongs to (drives PageUp/PageDown targeting). */\ntype PagedListKey = 'search' | 'neighbors' | 'selection' | 'entry';\n\ninterface NavRow {\n /** Stable identity across renders: `${list}:${nodeId}` (`root:` for the root row). */\n key: string;\n /** Stable DOM id (aria-activedescendant target). */\n domId: string;\n nodeId: NodeId;\n listKey: PagedListKey;\n text: string;\n selected: boolean;\n}\n\ninterface NavSection {\n key: 'search' | 'focused' | 'selection' | 'entry';\n listKey: PagedListKey;\n label: string;\n /** Group accessible name; carries the 'page x of y' state. */\n groupLabel: string;\n rows: NavRow[];\n page: number;\n pageCount: number;\n /** Total pageable items (excludes the focused section's root chrome row). */\n total: number;\n}\n\n/** Transient navigator focus: the neighborhood root. */\ninterface NavRoot {\n id: NodeId;\n neighbors: readonly NodeId[];\n /** Model revision the neighborhood was resolved against. */\n model: number;\n}\n\ninterface NavStoreSlice {\n selection: SelectionState;\n pins: ReadonlyMap<NodeId, readonly [number, number]>;\n hidden: ReadonlySet<NodeId>;\n model: number;\n nodeCount: number;\n /** Last completed search; null hides the section. */\n search: { query: string; results: readonly SearchResult[] } | null;\n}\n\n/** Narrow store subscription: re-renders only when a navigator-relevant slice\n * changes (never on per-frame viewport writes). */\nfunction useNavStoreSlice(instance: AnyGraphInstance): NavStoreSlice {\n const cacheRef = useRef<NavStoreSlice | null>(null);\n const subscribe = useCallback(\n (cb: () => void) => instance.store.subscribe(cb),\n [instance],\n );\n const getSnapshot = useCallback((): NavStoreSlice => {\n const s = instance.store.getState();\n const prev = cacheRef.current;\n if (\n prev !== null &&\n prev.selection === s.selection &&\n prev.pins === s.pins &&\n prev.hidden === s.hiddenNodeIds &&\n prev.model === s.revisions.model &&\n prev.nodeCount === s.nodeCount &&\n prev.search === s.search\n ) {\n return prev;\n }\n const next: NavStoreSlice = {\n selection: s.selection,\n pins: s.pins,\n hidden: s.hiddenNodeIds,\n model: s.revisions.model,\n nodeCount: s.nodeCount,\n search: s.search,\n };\n cacheRef.current = next;\n return next;\n }, [instance]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\ninterface PageWindow {\n page: number;\n pageCount: number;\n items: readonly NodeId[];\n}\n\n/** Clamp a raw page into `[0, pageCount)` and slice that page's ids. */\nfunction pageWindow(all: readonly NodeId[], rawPage: number, size: number): PageWindow {\n const pageCount = Math.max(1, Math.ceil(all.length / size));\n const page = Math.min(Math.max(rawPage, 0), pageCount - 1);\n return { page, pageCount, items: all.slice(page * size, page * size + size) };\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst PANEL_STYLE: CSSProperties = {\n pointerEvents: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n maxWidth: 320,\n fontSize: 12,\n};\nconst LIST_STYLE: CSSProperties = { overflowY: 'auto', maxHeight: 320 };\nconst HEADER_STYLE: CSSProperties = { fontWeight: 600, padding: '4px 6px' };\nconst ITEM_STYLE: CSSProperties = {\n padding: '2px 6px',\n cursor: 'default',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n/** visible DOM focus ring on the active item. */\nconst ACTIVE_ITEM_STYLE: CSSProperties = {\n ...ITEM_STYLE,\n outline: '2px solid currentColor',\n outlineOffset: -2,\n};\nconst PAGING_STYLE: CSSProperties = { display: 'flex', alignItems: 'center', gap: 8 };\nconst EMPTY_STYLE: CSSProperties = { padding: '4px 6px', fontStyle: 'italic' };\n\nexport function GraphNavigator(props: GraphNavigatorProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphNavigator>');\n const slice = useNavStoreSlice(instance);\n\n // 'topology' is the only v0.4 mode; the prop exists so the shape is stable\n // when 'spatial' lands post-v0.4 (no behavioral switch yet).\n void props.keyboardMode;\n\n const baseId = useId();\n const [root, setRoot] = useState<NavRoot | null>(null);\n const [pages, setPages] = useState<Record<PagedListKey, number>>({\n search: 0,\n neighbors: 0,\n selection: 0,\n entry: 0,\n });\n /** Active row identity (roving target); null → first row. */\n const [activeKey, setActiveKey] = useState<string | null>(null);\n\n /** Stable per-node DOM-id tokens (node ids may contain arbitrary text). */\n const tokensRef = useRef<Map<NodeId, number>>(new Map());\n /** Learned neighbor counts, cached per model revision (see module JSDoc). */\n const degreesRef = useRef<{ model: number; counts: Map<NodeId, number> }>({\n model: -1,\n counts: new Map(),\n });\n const itemsRef = useRef<Map<string, HTMLElement>>(new Map());\n /** Set by keyboard handlers so the focus effect moves DOM focus once. */\n const pendingFocusRef = useRef(false);\n\n // A model change invalidates the resolved neighborhood: drop the transient\n // root (back to the entry list) rather than rendering stale rows.\n useEffect(() => {\n if (root !== null && root.model !== slice.model) setRoot(null);\n }, [root, slice.model]);\n\n // release the keyboard ring — but ONLY when no pointer hover owns the\n // shared channel (blur while the pointer rests on a canvas node must not\n // erase that node's hover ring; hover already superseded the sticky\n // keyboard target inside core, so there is nothing else to clear).\n const releaseEmphasis = useCallback((): void => {\n if (instance.store.getState().hover.nodeId === null) instance.emphasizeNode(null);\n }, [instance]);\n\n // A ring left by keyboard navigation must not outlive the navigator.\n useEffect(() => () => releaseEmphasis(), [releaseEmphasis]);\n\n // Idempotent render-time cache reset (ref only — safe under StrictMode).\n if (degreesRef.current.model !== slice.model) {\n degreesRef.current = { model: slice.model, counts: new Map() };\n }\n\n const accessibility = instance.getAccessibility() as\n | AccessibilityConfig<Record<string, unknown>>\n | undefined;\n const rawWindow = accessibility?.navigatorWindow;\n const windowSize =\n typeof rawWindow === 'number' && Number.isFinite(rawWindow) && rawWindow >= 1\n ? Math.floor(rawWindow)\n : DEFAULT_NAVIGATOR_WINDOW;\n\n const getAccessibleLabel = accessibility?.getAccessibleLabel;\n const nameOf = (id: NodeId): string => {\n const node = instance.getNode(id) as GraphNode<Record<string, unknown>> | undefined;\n if (node === undefined) return id;\n if (getAccessibleLabel !== undefined) {\n try {\n return getAccessibleLabel(node);\n } catch {\n // fall through to the built-in resolution\n }\n }\n const label = node.attrs?.['label'];\n return typeof label === 'string' ? label : node.id;\n };\n\n const selectedSet = new Set<NodeId>(slice.selection.nodeIds);\n\n /** '{name} · {n} neighbors · pinned · hidden' — always a single text node. */\n const rowText = (id: NodeId): string => {\n const parts = [nameOf(id)];\n const count = degreesRef.current.counts.get(id);\n if (count !== undefined) parts.push(count === 1 ? '1 neighbor' : `${count} neighbors`);\n if (slice.pins.has(id)) parts.push('pinned');\n if (slice.hidden.has(id)) parts.push('hidden');\n return parts.join(' · ');\n };\n\n const domIdFor = (listKey: string, id: NodeId): string => {\n const tokens = tokensRef.current;\n let token = tokens.get(id);\n if (token === undefined) {\n token = tokens.size;\n tokens.set(id, token);\n }\n return `${baseId}nav-${listKey}-${token}`;\n };\n\n const makeRow = (listKey: PagedListKey, id: NodeId): NavRow => ({\n key: `${listKey}:${id}`,\n domId: domIdFor(listKey, id),\n nodeId: id,\n listKey,\n text: rowText(id),\n selected: selectedSet.has(id),\n });\n\n // --- section/row model (all O(navigatorWindow) work) ---\n const searchResults: readonly SearchResult[] = slice.search === null ? [] : slice.search.results;\n const listItems: Record<PagedListKey, readonly NodeId[]> = {\n search: searchResults.map((r) => r.id),\n neighbors: root !== null ? root.neighbors : [],\n selection: slice.selection.nodeIds,\n // the entry list is the SCENE roster (scope applied, mask not)\n // hidden/filtered nodes stay listed with their state exposed in text.\n entry: root === null ? instance.getSceneNodeIds() : [],\n };\n\n const sections: NavSection[] = [];\n if (slice.search !== null) {\n const view = pageWindow(listItems.search, pages.search, windowSize);\n const count = searchResults.length;\n const start = view.page * windowSize;\n sections.push({\n key: 'search',\n listKey: 'search',\n label: 'Search results',\n groupLabel: `Search results for ${slice.search.query}: ${count} ${\n count === 1 ? 'result' : 'results'\n }, page ${view.page + 1} of ${view.pageCount}`,\n // row text prefers the service-supplied label (still a TEXT\n // NODE); results may name ids outside the model — nameOf falls back to\n // the id for those.\n rows: view.items.map((id, i) => {\n const row = makeRow('search', id);\n const label = searchResults[start + i]?.label;\n return label === undefined ? row : { ...row, text: label };\n }),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (root !== null) {\n const view = pageWindow(listItems.neighbors, pages.neighbors, windowSize);\n const rootRow: NavRow = {\n key: `root:${root.id}`,\n domId: domIdFor('root', root.id),\n nodeId: root.id,\n listKey: 'neighbors',\n text: rowText(root.id),\n selected: selectedSet.has(root.id),\n };\n const count = root.neighbors.length;\n sections.push({\n key: 'focused',\n listKey: 'neighbors',\n label: 'Focused node',\n groupLabel: `Focused node ${nameOf(root.id)}: ${count} ${\n count === 1 ? 'neighbor' : 'neighbors'\n }, page ${view.page + 1} of ${view.pageCount}`,\n rows: [rootRow, ...view.items.map((id) => makeRow('neighbors', id))],\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (slice.selection.nodeIds.length > 0) {\n const view = pageWindow(listItems.selection, pages.selection, windowSize);\n const count = slice.selection.nodeIds.length;\n sections.push({\n key: 'selection',\n listKey: 'selection',\n label: 'Selection',\n groupLabel: `Selection: ${count} ${count === 1 ? 'node' : 'nodes'}, page ${\n view.page + 1\n } of ${view.pageCount}`,\n rows: view.items.map((id) => makeRow('selection', id)),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (root === null) {\n const view = pageWindow(listItems.entry, pages.entry, windowSize);\n const count = listItems.entry.length;\n sections.push({\n key: 'entry',\n listKey: 'entry',\n label: 'All nodes',\n groupLabel: `All nodes: ${count} ${count === 1 ? 'node' : 'nodes'}, page ${\n view.page + 1\n } of ${view.pageCount}`,\n rows: view.items.map((id) => makeRow('entry', id)),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n\n const rows: NavRow[] = sections.flatMap((s) => s.rows);\n let activeIndex = activeKey === null ? -1 : rows.findIndex((r) => r.key === activeKey);\n if (activeIndex === -1 && rows.length > 0) activeIndex = 0;\n const activeRow = activeIndex >= 0 ? rows[activeIndex] : undefined;\n const activeSection =\n activeRow !== undefined ? sections.find((s) => s.listKey === activeRow.listKey) : undefined;\n\n // Move real DOM focus to the active item after keyboard-driven changes so\n // the focus ring is a genuine DOM focus, never only a WebGL highlight.\n useEffect(() => {\n if (!pendingFocusRef.current) return;\n pendingFocusRef.current = false;\n if (activeRow === undefined) return;\n itemsRef.current.get(activeRow.domId)?.focus();\n });\n\n // --- keyboard model ('topology'; see module JSDoc) ---\n const focusItemSoon = (): void => {\n pendingFocusRef.current = true;\n };\n\n const moveTo = (index: number): void => {\n if (rows.length === 0) return;\n const row = rows[Math.min(Math.max(index, 0), rows.length - 1)];\n if (row === undefined) return;\n setActiveKey(row.key);\n // Keyboard parity with pointer hover: ring the active row\n // WITHOUT flying the camera (that stays on Enter via focusNode).\n instance.emphasizeNode(row.nodeId);\n focusItemSoon();\n };\n\n const pageBy = (delta: number): void => {\n if (activeRow === undefined) return;\n const listKey = activeRow.listKey;\n const items = listItems[listKey];\n const pageCount = Math.max(1, Math.ceil(items.length / windowSize));\n const current = Math.min(Math.max(pages[listKey], 0), pageCount - 1);\n const next = Math.min(Math.max(current + delta, 0), pageCount - 1);\n if (next === current) return;\n setPages((p) => ({ ...p, [listKey]: next }));\n const first = items[next * windowSize];\n if (first !== undefined) {\n setActiveKey(`${listKey}:${first}`);\n instance.emphasizeNode(first);\n }\n focusItemSoon();\n };\n\n /** Enter: focus the node in the graph (camera fly) and re-root the\n * neighborhood. The returned ids come from the core adjacency resolution\n * (undirected; see module JSDoc). Search rows route through the\n * result contract instead: `activateSearchResult` focuses an in-scene,\n * mask-visible id and otherwise only CLASSIFIES — the navigator never\n * mutates scope/filters (the host reacts through its own channels). */\n const activate = (row: NavRow): void => {\n if (row.listKey === 'search') {\n const result = searchResults.find((r) => r.id === row.nodeId);\n if (result !== undefined) instance.activateSearchResult(result);\n return;\n }\n const neighbors = instance.focusNode(row.nodeId);\n degreesRef.current.counts.set(row.nodeId, neighbors.length);\n setRoot({ id: row.nodeId, neighbors, model: slice.model });\n setPages((p) => ({ ...p, neighbors: 0 }));\n setActiveKey(`root:${row.nodeId}`);\n focusItemSoon();\n };\n\n /** Space: toggle membership through the selection ownership path.\n * Search rows may name ids outside the accepted model — those no-op. */\n const toggleSelection = (row: NavRow): void => {\n if (row.listKey === 'search' && instance.getNode(row.nodeId) === undefined) return;\n const current = slice.selection.nodeIds;\n const next = current.includes(row.nodeId)\n ? current.filter((id) => id !== row.nodeId)\n : [...current, row.nodeId];\n instance.selectNodes(next);\n };\n\n const clearTransientFocus = (): void => {\n setRoot(null);\n setActiveKey(null);\n releaseEmphasis();\n focusItemSoon();\n };\n\n const onKeyDown = (e: KeyboardEvent<HTMLDivElement>): void => {\n if (activeRow === undefined) return;\n switch (e.key) {\n case 'ArrowDown':\n moveTo(activeIndex + 1);\n break;\n case 'ArrowUp':\n moveTo(activeIndex - 1);\n break;\n case 'Home':\n moveTo(0);\n break;\n case 'End':\n moveTo(rows.length - 1);\n break;\n case 'PageDown':\n pageBy(1);\n break;\n case 'PageUp':\n pageBy(-1);\n break;\n case 'Enter':\n activate(activeRow);\n break;\n case ' ':\n toggleSelection(activeRow);\n break;\n case 'Escape':\n if (root === null) return; // nothing transient — let it bubble\n clearTransientFocus();\n break;\n default:\n return;\n }\n e.preventDefault();\n };\n\n const panelLabel = props.label ?? 'Graph navigator';\n\n return (\n <div\n data-orbit-navigator=\"\"\n role=\"group\"\n aria-label={panelLabel}\n className={props.className}\n style={{ ...PANEL_STYLE, ...props.style }}\n onBlur={(e) => {\n // Focus left the panel entirely (paging buttons stay inside): the\n // keyboard ring clears, mirroring pointer hover leaving the canvas.\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {\n releaseEmphasis();\n }\n }}\n >\n <div\n role=\"listbox\"\n aria-label={`${panelLabel} items`}\n aria-activedescendant={activeRow?.domId}\n aria-keyshortcuts=\"ArrowUp ArrowDown Home End PageUp PageDown Enter Space Escape\"\n onKeyDown={onKeyDown}\n style={LIST_STYLE}\n >\n {rows.length === 0 ? (\n <div role=\"presentation\" style={EMPTY_STYLE}>\n No nodes\n </div>\n ) : (\n sections.map((section) => (\n <div key={section.key} role=\"group\" aria-label={section.groupLabel}>\n <div role=\"presentation\" style={HEADER_STYLE}>\n {section.label}\n </div>\n {section.rows.map((row) => {\n const isActive = row.key === activeRow?.key;\n return (\n <div\n key={row.key}\n id={row.domId}\n role=\"option\"\n aria-selected={row.selected}\n tabIndex={isActive ? 0 : -1}\n ref={(el) => {\n if (el === null) itemsRef.current.delete(row.domId);\n else itemsRef.current.set(row.domId, el);\n }}\n onClick={() => {\n setActiveKey(row.key);\n instance.emphasizeNode(row.nodeId); // active row = ringed row\n focusItemSoon();\n }}\n style={isActive ? ACTIVE_ITEM_STYLE : ITEM_STYLE}\n >\n {row.text}\n </div>\n );\n })}\n </div>\n ))\n )}\n </div>\n {activeSection !== undefined ? (\n <div data-orbit-navigator-paging=\"\" style={PAGING_STYLE}>\n <button\n type=\"button\"\n onClick={() => pageBy(-1)}\n disabled={activeSection.page === 0}\n aria-keyshortcuts=\"PageUp\"\n >\n Previous page\n </button>\n <span role=\"status\" data-orbit-navigator-page-status=\"\">\n {`${activeSection.label}: page ${activeSection.page + 1} of ${\n activeSection.pageCount\n } (${activeSection.total} ${activeSection.total === 1 ? 'item' : 'items'})`}\n </span>\n <button\n type=\"button\"\n onClick={() => pageBy(1)}\n disabled={activeSection.page >= activeSection.pageCount - 1}\n aria-keyshortcuts=\"PageDown\"\n >\n Next page\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n"]}
|
|
@@ -3,44 +3,44 @@ import { SearchResult, SearchUnavailableReason } from '@modernrelay/orbit-core';
|
|
|
3
3
|
import { A as AnyGraphInstance } from '../GraphProvider-B8ITDXS0.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* `@modernrelay/orbit-react/components/Search` — `<GraphSearch>`, the
|
|
7
|
-
* search box
|
|
6
|
+
* `@modernrelay/orbit-react/components/Search` — `<GraphSearch>`, the
|
|
7
|
+
* search box.
|
|
8
8
|
*
|
|
9
|
-
* Owns exactly the UI half of the
|
|
9
|
+
* Owns exactly the UI half of the split: the text input, the debounce
|
|
10
10
|
* window, and the result list. Everything correctness-critical lives in the
|
|
11
11
|
* instance (`instance.search` creates the RequestContext, caches by declared
|
|
12
|
-
* revisions, cancels superseded work, and rejects stale results at admission
|
|
13
|
-
*
|
|
12
|
+
* revisions, cancels superseded work, and rejects stale results at admission);
|
|
13
|
+
* this component just issues ONE `instance.search` per settled
|
|
14
14
|
* debounce window and renders `store.search`.
|
|
15
15
|
*
|
|
16
16
|
* Behavior:
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
17
|
+
* - typing schedules `instance.search(query, {limit})` after `debounceMs`
|
|
18
|
+
* (default 200); a keystroke inside the window replaces the pending call, so
|
|
19
|
+
* there is one service call per settled window, never one per keystroke;
|
|
20
|
+
* - emptying the input cancels the pending call and clears the shared slice
|
|
21
|
+
* (`instance.clearSearch`), so the navigator section disappears too;
|
|
22
|
+
* - the result list reads `store.search`, the single source of truth; bespoke
|
|
23
|
+
* UIs and the navigator observe the same slice. `score` renders
|
|
24
|
+
* when the service supplied one;
|
|
25
|
+
* - Enter / click activates via `instance.activateSearchResult`: 'focused'
|
|
26
|
+
* closes the listbox and clears the active-option highlight; otherwise
|
|
27
|
+
* `onResultUnavailable(result, reason)` fires — falling back to the
|
|
28
|
+
* `<Graph onSearchResultUnavailable>` default when no local prop is given
|
|
29
|
+
* (the HOST reacts; search never mutates
|
|
30
|
+
* scope/filters behind the user's back);
|
|
31
|
+
* - I6 (query coherence): Enter only activates when `store.search.query`
|
|
32
|
+
* matches the current input — after an edit, the previous query's rows may
|
|
33
|
+
* keep showing through the debounce window, but Enter is a no-op until the
|
|
34
|
+
* edited query's publication lands (clicks on visible rows stay live);
|
|
35
|
+
* - Escape clears: input text, pending debounce, active option, and the
|
|
36
|
+
* store slice (`instance.clearSearch`).
|
|
37
37
|
*
|
|
38
38
|
* ARIA: the combobox pattern — `role="combobox"` input with
|
|
39
39
|
* `aria-expanded`/`aria-controls`/`aria-activedescendant` over a
|
|
40
40
|
* `role="listbox"` of `role="option"` rows; ArrowDown/ArrowUp move the active
|
|
41
41
|
* option (no wrap), Home/End jump. Option DOM ids are index-derived (node ids
|
|
42
42
|
* are untrusted text). All result-derived text (label, id, score) renders as
|
|
43
|
-
* TEXT NODES only
|
|
43
|
+
* TEXT NODES only — `renderResult` is the customization escape hatch.
|
|
44
44
|
*/
|
|
45
45
|
|
|
46
46
|
interface GraphSearchProps {
|
|
@@ -49,10 +49,10 @@ interface GraphSearchProps {
|
|
|
49
49
|
placeholder?: string;
|
|
50
50
|
/** Debounce window in ms — one `instance.search` per settled window. Default 200. */
|
|
51
51
|
debounceMs?: number;
|
|
52
|
-
/** Result cap forwarded to the service
|
|
52
|
+
/** Result cap forwarded to the service. Default 20. */
|
|
53
53
|
limit?: number;
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
55
|
+
* result contract: fired when an activated result cannot be focused
|
|
56
56
|
* ('not-loaded' | 'out-of-scope' | 'filtered'). Defaults to the
|
|
57
57
|
* `<Graph onSearchResultUnavailable>` prop when omitted.
|
|
58
58
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getSearchResultUnavailableCallback } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import { useResolvedInstance } from '../chunk-
|
|
1
|
+
import { getSearchResultUnavailableCallback } from '../chunk-ODOPSUI7.js';
|
|
2
|
+
import '../chunk-S36LHSNX.js';
|
|
3
|
+
import { useResolvedInstance } from '../chunk-NPOV5GA3.js';
|
|
4
4
|
import { useId, useState, useRef, useEffect, useCallback, useSyncExternalStore } from 'react';
|
|
5
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Search/index.tsx"],"names":[],"mappings":"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","file":"Search.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Search` — `<GraphSearch>`, the §16.5\n * search box (S11-T08).\n *\n * Owns exactly the UI half of the §16.5 split: the text input, the debounce\n * window, and the result list. Everything correctness-critical lives in the\n * instance (`instance.search` creates the RequestContext, caches by declared\n * revisions, cancels superseded work, and rejects stale results at admission\n * — §9.2); this component just issues ONE `instance.search` per settled\n * debounce window and renders `store.search`.\n *\n * Behavior:\n * - typing schedules `instance.search(query, {limit})` after `debounceMs`\n * (default 200); a keystroke inside the window replaces the pending call —\n * one service call per settled window, never one per keystroke;\n * - emptying the input cancels the pending call and clears the shared slice\n * (`instance.clearSearch()`), so the navigator section disappears too;\n * - the result list reads `store.search` (the §16.5 single source of truth —\n * bespoke UIs and the navigator observe the same slice); `score` renders\n * when the service supplied one;\n * - Enter / click activates via `instance.activateSearchResult`: 'focused'\n * closes the listbox and clears the active-option highlight; otherwise\n * `onResultUnavailable(result, reason)` fires — falling back to the\n * `<Graph onSearchResultUnavailable>` default when no local prop is given\n * (spec §16.5 result contract: the HOST reacts; search never mutates\n * scope/filters behind the user's back);\n * - I6 (query coherence): Enter only activates when `store.search.query`\n * matches the current input — after an edit, the previous query's rows may\n * keep showing through the debounce window, but Enter is a no-op until the\n * edited query's publication lands (clicks on visible rows stay live);\n * - Escape clears: input text, pending debounce, active option, and the\n * store slice (`instance.clearSearch`).\n *\n * ARIA: the combobox pattern — `role=\"combobox\"` input with\n * `aria-expanded`/`aria-controls`/`aria-activedescendant` over a\n * `role=\"listbox\"` of `role=\"option\"` rows; ArrowDown/ArrowUp move the active\n * option (no wrap), Home/End jump. Option DOM ids are index-derived (node ids\n * are untrusted text). All result-derived text (label, id, score) renders as\n * TEXT NODES only (§14) — `renderResult` is the customization escape hatch.\n */\n\nimport { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport type {\n ChangeEvent,\n CSSProperties,\n KeyboardEvent as ReactKeyboardEvent,\n ReactElement,\n ReactNode,\n} from 'react';\nimport type { GraphStoreState, SearchResult, SearchUnavailableReason } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\nimport { getSearchResultUnavailableCallback } from '../../hooks';\n\n/** Store slice subscription local to this component (the ambient hooks in\n * ../../hooks require the provider; here the instance may be an explicit\n * prop). `store.search` is a stable reference per publication, so the raw\n * selector needs no memo. */\nfunction useSearchSlice(\n instance: AnyGraphInstance,\n): { query: string; results: readonly SearchResult[] } | null {\n const subscribe = useCallback(\n (onChange: () => void) => instance.store.subscribe(onChange),\n [instance],\n );\n const getSnapshot = useCallback(\n (): GraphStoreState['search'] => instance.store.getState().search,\n [instance],\n );\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** §16.5 defaults (mirror the instance's SEARCH_LIMIT_DEFAULT). */\nconst DEBOUNCE_MS_DEFAULT = 200;\nconst LIMIT_DEFAULT = 20;\n\nexport interface GraphSearchProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n placeholder?: string;\n /** Debounce window in ms — one `instance.search` per settled window. Default 200. */\n debounceMs?: number;\n /** Result cap forwarded to the service (§16.5). Default 20. */\n limit?: number;\n /**\n * §16.5 result contract: fired when an activated result cannot be focused\n * ('not-loaded' | 'out-of-scope' | 'filtered'). Defaults to the\n * `<Graph onSearchResultUnavailable>` prop when omitted.\n */\n onResultUnavailable?: (result: SearchResult, reason: SearchUnavailableReason) => void;\n /** Custom option content (rendered INSIDE the option row). Default: label\n * text node + score (when present). */\n renderResult?: (r: SearchResult) => ReactNode;\n /** Accessible name of the search box. Default 'Search graph'. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst ROOT_STYLE: CSSProperties = {\n pointerEvents: 'auto',\n position: 'relative',\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n maxWidth: 320,\n font: '13px/1.4 system-ui, sans-serif',\n};\nconst LIST_STYLE: CSSProperties = {\n margin: 0,\n padding: 4,\n listStyle: 'none',\n overflowY: 'auto',\n maxHeight: 240,\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};\nconst OPTION_STYLE: CSSProperties = {\n display: 'flex',\n justifyContent: 'space-between',\n gap: 8,\n padding: '4px 8px',\n borderRadius: 6,\n cursor: 'pointer',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\nconst ACTIVE_OPTION_STYLE: CSSProperties = {\n ...OPTION_STYLE,\n outline: '2px solid currentColor',\n outlineOffset: -2,\n};\nconst SCORE_STYLE: CSSProperties = { opacity: 0.65, fontVariantNumeric: 'tabular-nums' };\nconst EMPTY_STYLE: CSSProperties = { padding: '4px 8px', fontStyle: 'italic', opacity: 0.8 };\n\nexport function GraphSearch(props: GraphSearchProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphSearch>');\n const search = useSearchSlice(instance);\n\n const debounceMs = props.debounceMs ?? DEBOUNCE_MS_DEFAULT;\n const limit = props.limit ?? LIMIT_DEFAULT;\n\n const baseId = useId();\n const listboxId = `${baseId}search-listbox`;\n\n const [query, setQuery] = useState('');\n /** Listbox visibility — closed by 'focused' activation / Escape until the\n * next keystroke reopens it. */\n const [open, setOpen] = useState(false);\n /** Active option index; -1 = no active option (highlight cleared). */\n const [activeIndex, setActiveIndex] = useState(-1);\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const cancelPending = (): void => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n // Unmount/instance-swap hygiene: never fire a search for a dead widget.\n useEffect(() => cancelPending, [instance]);\n\n const results: readonly SearchResult[] = search === null ? [] : search.results;\n /** I6 query coherence: the published slice answers the CURRENT input only\n * when its query matches what this box sent to `instance.search` (the raw\n * input value — neither side normalizes). Stale results may keep showing\n * through the debounce window (combobox convention: don't blank the\n * listbox), but implicit activation is gated on this. */\n const current = search !== null && search.query === query;\n const expanded = open && search !== null;\n const activeOptionId =\n expanded && activeIndex >= 0 && activeIndex < results.length\n ? `${baseId}search-opt-${activeIndex}`\n : undefined;\n\n const onChange = (e: ChangeEvent<HTMLInputElement>): void => {\n const value = e.target.value;\n setQuery(value);\n setActiveIndex(-1);\n cancelPending();\n if (value === '') {\n // Input emptied: clear the shared slice immediately (§16.5) — no\n // trailing search may resurrect stale results.\n setOpen(false);\n instance.clearSearch();\n return;\n }\n setOpen(true);\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n // Supersede/staleness rejections are §16.5 by-design outcomes — the\n // diagnostics channel carries the why; the box just shows the latest\n // completed publication via store.search.\n void instance.search(value, { limit }).catch(() => {});\n }, debounceMs);\n };\n\n const clearAll = (): void => {\n cancelPending();\n setQuery('');\n setOpen(false);\n setActiveIndex(-1);\n instance.clearSearch();\n };\n\n const activate = (result: SearchResult): void => {\n const activation = instance.activateSearchResult(result);\n if (activation.status === 'focused') {\n // Focused: close the listbox and clear the highlight (§16.5).\n setOpen(false);\n setActiveIndex(-1);\n return;\n }\n const cb = props.onResultUnavailable ?? getSearchResultUnavailableCallback(instance);\n cb?.(result, activation.reason);\n };\n\n const onKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>): void => {\n switch (e.key) {\n case 'ArrowDown':\n if (results.length === 0) return;\n setOpen(true);\n setActiveIndex((i) => Math.min(i + 1, results.length - 1));\n break;\n case 'ArrowUp':\n if (results.length === 0) return;\n setActiveIndex((i) => Math.max(i - 1, 0));\n break;\n case 'Home':\n if (!expanded || results.length === 0) return;\n setActiveIndex(0);\n break;\n case 'End':\n if (!expanded || results.length === 0) return;\n setActiveIndex(results.length - 1);\n break;\n case 'Enter': {\n // I6: Enter (and its first-result fallback) activates only against a\n // publication answering the CURRENT input — while the edited query is\n // still in its debounce window, Enter is a no-op (input and listbox\n // untouched). Pointer clicks on visible rows stay unguarded: the user\n // activates exactly what they saw.\n if (!current) return;\n const target = results[activeIndex] ?? results[0];\n if (target === undefined) return;\n activate(target);\n break;\n }\n case 'Escape':\n clearAll();\n break;\n default:\n return;\n }\n e.preventDefault();\n };\n\n const boxLabel = props.label ?? 'Search graph';\n\n return (\n <div\n data-orbit-search=\"\"\n className={props.className}\n style={{ ...ROOT_STYLE, ...props.style }}\n >\n <input\n data-orbit-search-input=\"\"\n type=\"text\"\n role=\"combobox\"\n aria-label={boxLabel}\n aria-expanded={expanded}\n aria-controls={listboxId}\n aria-activedescendant={activeOptionId}\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n spellCheck={false}\n placeholder={props.placeholder}\n value={query}\n onChange={onChange}\n onKeyDown={onKeyDown}\n />\n {expanded ? (\n <ul id={listboxId} role=\"listbox\" aria-label={`${boxLabel} results`} style={LIST_STYLE}>\n {results.length === 0 ? (\n <li data-orbit-search-empty=\"\" role=\"presentation\" style={EMPTY_STYLE}>\n No results\n </li>\n ) : (\n results.map((r, index) => (\n <li\n key={`${index}:${r.id}`}\n id={`${baseId}search-opt-${index}`}\n role=\"option\"\n aria-selected={index === activeIndex}\n data-orbit-search-result={r.id}\n style={index === activeIndex ? ACTIVE_OPTION_STYLE : OPTION_STYLE}\n onPointerEnter={() => {\n setActiveIndex(index);\n }}\n onClick={() => {\n activate(r);\n }}\n >\n {props.renderResult !== undefined ? (\n props.renderResult(r)\n ) : (\n <>\n {/* Result text is untrusted — TEXT NODES only (§14). */}\n <span data-orbit-search-result-label=\"\">{r.label ?? r.id}</span>\n {r.score !== undefined ? (\n <span data-orbit-search-result-score=\"\" style={SCORE_STYLE}>\n {String(r.score)}\n </span>\n ) : null}\n </>\n )}\n </li>\n ))\n )}\n </ul>\n ) : null}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Search/index.tsx"],"names":[],"mappings":"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","file":"Search.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Search` — `<GraphSearch>`, the\n * search box.\n *\n * Owns exactly the UI half of the split: the text input, the debounce\n * window, and the result list. Everything correctness-critical lives in the\n * instance (`instance.search` creates the RequestContext, caches by declared\n * revisions, cancels superseded work, and rejects stale results at admission);\n * this component just issues ONE `instance.search` per settled\n * debounce window and renders `store.search`.\n *\n * Behavior:\n * - typing schedules `instance.search(query, {limit})` after `debounceMs`\n * (default 200); a keystroke inside the window replaces the pending call, so\n * there is one service call per settled window, never one per keystroke;\n * - emptying the input cancels the pending call and clears the shared slice\n * (`instance.clearSearch`), so the navigator section disappears too;\n * - the result list reads `store.search`, the single source of truth; bespoke\n * UIs and the navigator observe the same slice. `score` renders\n * when the service supplied one;\n * - Enter / click activates via `instance.activateSearchResult`: 'focused'\n * closes the listbox and clears the active-option highlight; otherwise\n * `onResultUnavailable(result, reason)` fires — falling back to the\n * `<Graph onSearchResultUnavailable>` default when no local prop is given\n * (the HOST reacts; search never mutates\n * scope/filters behind the user's back);\n * - I6 (query coherence): Enter only activates when `store.search.query`\n * matches the current input — after an edit, the previous query's rows may\n * keep showing through the debounce window, but Enter is a no-op until the\n * edited query's publication lands (clicks on visible rows stay live);\n * - Escape clears: input text, pending debounce, active option, and the\n * store slice (`instance.clearSearch`).\n *\n * ARIA: the combobox pattern — `role=\"combobox\"` input with\n * `aria-expanded`/`aria-controls`/`aria-activedescendant` over a\n * `role=\"listbox\"` of `role=\"option\"` rows; ArrowDown/ArrowUp move the active\n * option (no wrap), Home/End jump. Option DOM ids are index-derived (node ids\n * are untrusted text). All result-derived text (label, id, score) renders as\n * TEXT NODES only — `renderResult` is the customization escape hatch.\n */\n\nimport { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport type {\n ChangeEvent,\n CSSProperties,\n KeyboardEvent as ReactKeyboardEvent,\n ReactElement,\n ReactNode,\n} from 'react';\nimport type { GraphStoreState, SearchResult, SearchUnavailableReason } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\nimport { getSearchResultUnavailableCallback } from '../../hooks';\n\n/** Store slice subscription local to this component (the ambient hooks in\n * ../../hooks require the provider; here the instance may be an explicit\n * prop). `store.search` is a stable reference per publication, so the raw\n * selector needs no memo. */\nfunction useSearchSlice(\n instance: AnyGraphInstance,\n): { query: string; results: readonly SearchResult[] } | null {\n const subscribe = useCallback(\n (onChange: () => void) => instance.store.subscribe(onChange),\n [instance],\n );\n const getSnapshot = useCallback(\n (): GraphStoreState['search'] => instance.store.getState().search,\n [instance],\n );\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** defaults (mirror the instance's SEARCH_LIMIT_DEFAULT). */\nconst DEBOUNCE_MS_DEFAULT = 200;\nconst LIMIT_DEFAULT = 20;\n\nexport interface GraphSearchProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n placeholder?: string;\n /** Debounce window in ms — one `instance.search` per settled window. Default 200. */\n debounceMs?: number;\n /** Result cap forwarded to the service. Default 20. */\n limit?: number;\n /**\n * result contract: fired when an activated result cannot be focused\n * ('not-loaded' | 'out-of-scope' | 'filtered'). Defaults to the\n * `<Graph onSearchResultUnavailable>` prop when omitted.\n */\n onResultUnavailable?: (result: SearchResult, reason: SearchUnavailableReason) => void;\n /** Custom option content (rendered INSIDE the option row). Default: label\n * text node + score (when present). */\n renderResult?: (r: SearchResult) => ReactNode;\n /** Accessible name of the search box. Default 'Search graph'. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst ROOT_STYLE: CSSProperties = {\n pointerEvents: 'auto',\n position: 'relative',\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n maxWidth: 320,\n font: '13px/1.4 system-ui, sans-serif',\n};\nconst LIST_STYLE: CSSProperties = {\n margin: 0,\n padding: 4,\n listStyle: 'none',\n overflowY: 'auto',\n maxHeight: 240,\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};\nconst OPTION_STYLE: CSSProperties = {\n display: 'flex',\n justifyContent: 'space-between',\n gap: 8,\n padding: '4px 8px',\n borderRadius: 6,\n cursor: 'pointer',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\nconst ACTIVE_OPTION_STYLE: CSSProperties = {\n ...OPTION_STYLE,\n outline: '2px solid currentColor',\n outlineOffset: -2,\n};\nconst SCORE_STYLE: CSSProperties = { opacity: 0.65, fontVariantNumeric: 'tabular-nums' };\nconst EMPTY_STYLE: CSSProperties = { padding: '4px 8px', fontStyle: 'italic', opacity: 0.8 };\n\nexport function GraphSearch(props: GraphSearchProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphSearch>');\n const search = useSearchSlice(instance);\n\n const debounceMs = props.debounceMs ?? DEBOUNCE_MS_DEFAULT;\n const limit = props.limit ?? LIMIT_DEFAULT;\n\n const baseId = useId();\n const listboxId = `${baseId}search-listbox`;\n\n const [query, setQuery] = useState('');\n /** Listbox visibility — closed by 'focused' activation / Escape until the\n * next keystroke reopens it. */\n const [open, setOpen] = useState(false);\n /** Active option index; -1 = no active option (highlight cleared). */\n const [activeIndex, setActiveIndex] = useState(-1);\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const cancelPending = (): void => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n // Unmount/instance-swap hygiene: never fire a search for a dead widget.\n useEffect(() => cancelPending, [instance]);\n\n const results: readonly SearchResult[] = search === null ? [] : search.results;\n /** I6 query coherence: the published slice answers the CURRENT input only\n * when its query matches what this box sent to `instance.search` (the raw\n * input value — neither side normalizes). Stale results may keep showing\n * through the debounce window (combobox convention: don't blank the\n * listbox), but implicit activation is gated on this. */\n const current = search !== null && search.query === query;\n const expanded = open && search !== null;\n const activeOptionId =\n expanded && activeIndex >= 0 && activeIndex < results.length\n ? `${baseId}search-opt-${activeIndex}`\n : undefined;\n\n const onChange = (e: ChangeEvent<HTMLInputElement>): void => {\n const value = e.target.value;\n setQuery(value);\n setActiveIndex(-1);\n cancelPending();\n if (value === '') {\n // Input emptied: clear the shared slice immediately — no\n // trailing search may resurrect stale results.\n setOpen(false);\n instance.clearSearch();\n return;\n }\n setOpen(true);\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n // Supersede/staleness rejections are by-design outcomes — the\n // diagnostics channel carries the why; the box just shows the latest\n // completed publication via store.search.\n void instance.search(value, { limit }).catch(() => {});\n }, debounceMs);\n };\n\n const clearAll = (): void => {\n cancelPending();\n setQuery('');\n setOpen(false);\n setActiveIndex(-1);\n instance.clearSearch();\n };\n\n const activate = (result: SearchResult): void => {\n const activation = instance.activateSearchResult(result);\n if (activation.status === 'focused') {\n // Focused: close the listbox and clear the highlight.\n setOpen(false);\n setActiveIndex(-1);\n return;\n }\n const cb = props.onResultUnavailable ?? getSearchResultUnavailableCallback(instance);\n cb?.(result, activation.reason);\n };\n\n const onKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>): void => {\n switch (e.key) {\n case 'ArrowDown':\n if (results.length === 0) return;\n setOpen(true);\n setActiveIndex((i) => Math.min(i + 1, results.length - 1));\n break;\n case 'ArrowUp':\n if (results.length === 0) return;\n setActiveIndex((i) => Math.max(i - 1, 0));\n break;\n case 'Home':\n if (!expanded || results.length === 0) return;\n setActiveIndex(0);\n break;\n case 'End':\n if (!expanded || results.length === 0) return;\n setActiveIndex(results.length - 1);\n break;\n case 'Enter': {\n // I6: Enter (and its first-result fallback) activates only against a\n // publication answering the CURRENT input — while the edited query is\n // still in its debounce window, Enter is a no-op (input and listbox\n // untouched). Pointer clicks on visible rows stay unguarded: the user\n // activates exactly what they saw.\n if (!current) return;\n const target = results[activeIndex] ?? results[0];\n if (target === undefined) return;\n activate(target);\n break;\n }\n case 'Escape':\n clearAll();\n break;\n default:\n return;\n }\n e.preventDefault();\n };\n\n const boxLabel = props.label ?? 'Search graph';\n\n return (\n <div\n data-orbit-search=\"\"\n className={props.className}\n style={{ ...ROOT_STYLE, ...props.style }}\n >\n <input\n data-orbit-search-input=\"\"\n type=\"text\"\n role=\"combobox\"\n aria-label={boxLabel}\n aria-expanded={expanded}\n aria-controls={listboxId}\n aria-activedescendant={activeOptionId}\n aria-autocomplete=\"list\"\n autoComplete=\"off\"\n spellCheck={false}\n placeholder={props.placeholder}\n value={query}\n onChange={onChange}\n onKeyDown={onKeyDown}\n />\n {expanded ? (\n <ul id={listboxId} role=\"listbox\" aria-label={`${boxLabel} results`} style={LIST_STYLE}>\n {results.length === 0 ? (\n <li data-orbit-search-empty=\"\" role=\"presentation\" style={EMPTY_STYLE}>\n No results\n </li>\n ) : (\n results.map((r, index) => (\n <li\n key={`${index}:${r.id}`}\n id={`${baseId}search-opt-${index}`}\n role=\"option\"\n aria-selected={index === activeIndex}\n data-orbit-search-result={r.id}\n style={index === activeIndex ? ACTIVE_OPTION_STYLE : OPTION_STYLE}\n onPointerEnter={() => {\n setActiveIndex(index);\n }}\n onClick={() => {\n activate(r);\n }}\n >\n {props.renderResult !== undefined ? (\n props.renderResult(r)\n ) : (\n <>\n {/* Result text is untrusted — TEXT NODES only. */}\n <span data-orbit-search-result-label=\"\">{r.label ?? r.id}</span>\n {r.score !== undefined ? (\n <span data-orbit-search-result-score=\"\" style={SCORE_STYLE}>\n {String(r.score)}\n </span>\n ) : null}\n </>\n )}\n </li>\n ))\n )}\n </ul>\n ) : null}\n </div>\n );\n}\n"]}
|
|
@@ -1,15 +1,15 @@
|
|
|
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
|
-
* <GraphSelectionActions> —
|
|
7
|
+
* <GraphSelectionActions> — selection action panel.
|
|
8
8
|
*
|
|
9
9
|
* Renders ONLY while the selection is non-empty: a count plus Select
|
|
10
10
|
* neighbors / Invert / Clear / Hide selected / Pin selected / Unpin selected,
|
|
11
11
|
* all driven through the instance mutators so it stays correct under
|
|
12
|
-
* controlled selection
|
|
12
|
+
* controlled selection. Headless-
|
|
13
13
|
* styleable via `className`/`buttonClassName` (providing them drops the
|
|
14
14
|
* default dark-theme inline styles).
|
|
15
15
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useResolvedInstance, mergeStyle, cornerStyle } from '../chunk-
|
|
1
|
+
import { useResolvedInstance, mergeStyle, cornerStyle } from '../chunk-NPOV5GA3.js';
|
|
2
2
|
import { useCallback, useSyncExternalStore } from 'react';
|
|
3
3
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/SelectionActions/index.tsx"],"names":[],"mappings":"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","file":"SelectionActions.js","sourcesContent":["/**\n * <GraphSelectionActions> —
|
|
1
|
+
{"version":3,"sources":["../../src/components/SelectionActions/index.tsx"],"names":[],"mappings":"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","file":"SelectionActions.js","sourcesContent":["/**\n * <GraphSelectionActions> — selection action panel.\n *\n * Renders ONLY while the selection is non-empty: a count plus Select\n * neighbors / Invert / Clear / Hide selected / Pin selected / Unpin selected,\n * all driven through the instance mutators so it stays correct under\n * controlled selection. Headless-\n * styleable via `className`/`buttonClassName` (providing them drops the\n * default dark-theme inline styles).\n */\n\nimport { useCallback, useSyncExternalStore } from 'react';\nimport type { CSSProperties, ReactElement } from 'react';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { cornerStyle, mergeStyle, useResolvedInstance } from '../shared';\nimport type { GraphCorner } from '../shared';\n\nexport interface GraphSelectionActionsProps {\n /** Explicit instance; defaults to the ambient <Graph>/<GraphProvider> one. */\n instance?: AnyGraphInstance;\n /** Class hook for the panel root; providing it drops the default styles. */\n /** Corner this panel anchors to. Default 'bottom-left'. */\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 /** Class hook for each button; providing it drops the default styles. */\n buttonClassName?: string;\n}\n\ninterface SelectionAction {\n id: string;\n label: string;\n run(): void;\n}\n\n// --- default dark-theme styling (dropped when class hooks are provided) ---\n\nconst PANEL_STYLE: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 2,\n padding: 4,\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.92)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n boxShadow: '0 4px 16px rgba(0, 0, 0, 0.4)',\n color: '#e8eaf0',\n font: '12px/1.4 system-ui, sans-serif',\n};\n\nconst COUNT_STYLE: CSSProperties = {\n padding: '2px 8px',\n opacity: 0.75,\n whiteSpace: 'nowrap',\n};\n\nconst BUTTON_STYLE: CSSProperties = {\n appearance: 'none',\n background: 'transparent',\n border: 'none',\n borderRadius: 6,\n color: 'inherit',\n cursor: 'pointer',\n font: 'inherit',\n minHeight: 26,\n padding: '2px 8px',\n whiteSpace: 'nowrap',\n};\n\nexport function GraphSelectionActions(props: GraphSelectionActionsProps): ReactElement | null {\n const instance = useResolvedInstance(props.instance, '<GraphSelectionActions>');\n\n // The store keeps `selection` referentially stable across unrelated\n // publishes, so the raw field is a valid useSyncExternalStore snapshot.\n const subscribe = useCallback(\n (onStoreChange: () => void) => instance.store.subscribe(onStoreChange),\n [instance],\n );\n const getSelection = useCallback(() => instance.store.getState().selection, [instance]);\n const selection = useSyncExternalStore(subscribe, getSelection, getSelection);\n\n const count = selection.nodeIds.length + selection.edgeIds.length + selection.groupIds.length;\n if (count === 0) return null;\n\n const nodeIds = selection.nodeIds;\n const actions: SelectionAction[] = [\n {\n id: 'select-neighbors',\n label: 'Select neighbors',\n run: () => {\n instance.selectNeighbors();\n },\n },\n {\n id: 'invert',\n label: 'Invert',\n run: () => {\n instance.invertSelection();\n },\n },\n {\n id: 'clear',\n label: 'Clear',\n run: () => {\n instance.clearSelection();\n },\n },\n {\n id: 'hide',\n label: 'Hide selected',\n run: () => {\n instance.hideNodes(nodeIds);\n },\n },\n {\n id: 'pin',\n label: 'Pin selected',\n run: () => {\n for (const id of nodeIds) instance.pinNode(id);\n },\n },\n {\n id: 'unpin',\n label: 'Unpin selected',\n run: () => {\n for (const id of nodeIds) instance.unpinNode(id);\n },\n },\n ];\n\n return (\n <div\n data-orbit-selection-actions=\"\"\n role=\"group\"\n aria-label=\"Selection actions\"\n className={props.className}\n style={\n props.className !== undefined\n ? props.style\n : mergeStyle(\n { ...cornerStyle(props.position ?? 'bottom-left', props.offset), ...PANEL_STYLE, },\n props.style,\n )\n }\n >\n <span data-orbit-selection-count=\"\" style={props.className !== undefined ? undefined : COUNT_STYLE}>\n {count} selected\n </span>\n {actions.map((action) => (\n <button\n key={action.id}\n type=\"button\"\n data-orbit-selection-action={action.id}\n className={props.buttonClassName}\n style={props.buttonClassName !== undefined ? undefined : BUTTON_STYLE}\n onClick={() => {\n action.run();\n }}\n >\n {action.label}\n </button>\n ))}\n </div>\n );\n}\n"]}
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { ReactNode, CSSProperties, ReactElement } from 'react';
|
|
2
2
|
import { LayoutKind, SimulationConfig } from '@modernrelay/orbit-core';
|
|
3
3
|
import { A as AnyGraphInstance } from '../GraphProvider-B8ITDXS0.js';
|
|
4
|
-
import { G as GraphCorner } from '../shared-
|
|
4
|
+
import { G as GraphCorner } from '../shared-n98tkXJe.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* `@modernrelay/orbit-react/components/SimControls` — `<GraphSimControls>`,
|
|
8
|
-
* the
|
|
8
|
+
* the simulation control panel.
|
|
9
9
|
*
|
|
10
10
|
* Live sliders over the effective `SimulationConfig` fields (gravity,
|
|
11
|
-
* repulsion, friction, linkDistance, linkSpring, collision, decay, center
|
|
11
|
+
* repulsion, friction, linkDistance, linkSpring, collision, decay, center
|
|
12
12
|
* repulsionTheta is omitted: inert on cosmos >= 3.4), a speed
|
|
13
13
|
* preset toggle, and a reheat button. Every control write is ONE atomic
|
|
14
14
|
* host update — `applyHostUpdate({ simulation: merged })`, the same lane the
|
|
15
15
|
* `<Graph>` `simulation` prop uses — which sinks as a single config-only
|
|
16
|
-
* `engine.commit`: no structure, no buffers, no restart, positions untouched
|
|
17
|
-
* (R-16.11-02, E1).
|
|
16
|
+
* `engine.commit`: no structure, no buffers, no restart, positions untouched.
|
|
18
17
|
*
|
|
19
18
|
* ## Current-value resolution (v0.10, investigated)
|
|
20
19
|
* The store publishes NO simulation/layout lane and the instance exposes no
|
|
@@ -23,13 +22,13 @@ import { G as GraphCorner } from '../shared-Bj3Gk219.js';
|
|
|
23
22
|
* The panel therefore mirrors the caller's `simulation` prop (the same
|
|
24
23
|
* object handed to `<Graph>`) and merges control writes over that mirror; a
|
|
25
24
|
* prop identity change re-seeds the mirror (the external writer wins). Like
|
|
26
|
-
* `subgraph
|
|
25
|
+
* `subgraph`, the lane is last-writer-wins: the panel and the
|
|
27
26
|
* `<Graph>` prop write the same state. `onSimulationChange` fires with the
|
|
28
27
|
* full merged config after every write so controlled callers can loop it
|
|
29
28
|
* back through their `simulation` prop (the spec's `onLayoutChange` seam
|
|
30
29
|
* traffics in the LayoutSpec object form and is post-v0.10).
|
|
31
30
|
*
|
|
32
|
-
* ## Applicability
|
|
31
|
+
* ## Applicability
|
|
33
32
|
* Rendered ONLY under layout `'force'` — the `layout` prop mirrors the
|
|
34
33
|
* caller's `<Graph>` prop for the same no-observable-lane reason, defaulting
|
|
35
34
|
* to `'force'` (the instance construction default). Under `'fixed'` the
|
|
@@ -40,12 +39,12 @@ import { G as GraphCorner } from '../shared-Bj3Gk219.js';
|
|
|
40
39
|
*
|
|
41
40
|
* ## Reheat
|
|
42
41
|
* The spec names `reheat(alpha)`; the shipped instance surface is
|
|
43
|
-
* `resumeSimulation
|
|
44
|
-
* `engine.start
|
|
42
|
+
* `resumeSimulation` (simulation controls — sinks to
|
|
43
|
+
* `engine.start` and publishes `simulationRunning`). The button calls
|
|
45
44
|
* that; the alpha parameter follows when core grows the reheat API.
|
|
46
45
|
*
|
|
47
46
|
* ## Speed toggle
|
|
48
|
-
* A preset over the `decay` lane — the
|
|
47
|
+
* A preset over the `decay` lane — the cool-down tunable (cosmos
|
|
49
48
|
* `simulationDecay`), which core exposes as of v0.10.2. ON writes a smaller
|
|
50
49
|
* coefficient so the run cools SLOWER and stays lively; OFF restores the
|
|
51
50
|
* pre-toggle value, or the engine default when the field was unset. The
|
|
@@ -54,10 +53,10 @@ import { G as GraphCorner } from '../shared-Bj3Gk219.js';
|
|
|
54
53
|
* every slider. (Before v0.10.2 this proxied through `friction` because
|
|
55
54
|
* `decay` had no core lane — that workaround is now retired.)
|
|
56
55
|
*
|
|
57
|
-
* ## Keyboard & ARIA
|
|
56
|
+
* ## Keyboard & ARIA
|
|
58
57
|
* Native `<input type="range">`/checkbox controls with `<label htmlFor>`
|
|
59
58
|
* wiring. Arrow/Home/End/Page keys are handled in `onKeyDown` with
|
|
60
|
-
* `preventDefault
|
|
59
|
+
* `preventDefault` so stepping is deterministic both in browsers (never
|
|
61
60
|
* double-steps against the native range keyboard model) and under jsdom
|
|
62
61
|
* (which has no native range keyboard model at all).
|
|
63
62
|
*/
|
|
@@ -75,13 +74,13 @@ interface SimFieldDescriptor {
|
|
|
75
74
|
* WRITTEN unless the user moves the control. */
|
|
76
75
|
fallback: number;
|
|
77
76
|
}
|
|
78
|
-
/**
|
|
77
|
+
/** slider roster = the full v0.10 `SimulationConfig` field list. */
|
|
79
78
|
declare const SIM_FIELDS: readonly SimFieldDescriptor[];
|
|
80
79
|
/** Speed-preset friction values (module JSDoc: the toggle's documented
|
|
81
80
|
* simulation field is `friction` until core ships `decay`). */
|
|
82
81
|
declare const SPEED_NORMAL_DECAY = 5000;
|
|
83
|
-
/** A smaller decay coefficient cools SLOWER, so the run keeps moving
|
|
84
|
-
* "fast" here means "stays lively", matching the
|
|
82
|
+
/** A smaller decay coefficient cools SLOWER, so the run keeps moving
|
|
83
|
+
* "fast" here means "stays lively", matching the semantics. */
|
|
85
84
|
declare const SPEED_FAST_DECAY = 1000;
|
|
86
85
|
interface GraphSimControlsProps {
|
|
87
86
|
/** Explicit instance (multi-instance pages); ambient context otherwise. */
|
|
@@ -118,7 +117,7 @@ interface GraphSimControlsProps {
|
|
|
118
117
|
style?: CSSProperties;
|
|
119
118
|
}
|
|
120
119
|
declare function GraphSimControls(props: GraphSimControlsProps): ReactElement | null;
|
|
121
|
-
/**
|
|
120
|
+
/** tree-shake probe — must never reach a root-only consumer bundle. */
|
|
122
121
|
declare const __ORBIT_SIMCONTROLS_SENTINEL__ = "orbit-react/components/SimControls";
|
|
123
122
|
|
|
124
123
|
export { GraphSimControls, type GraphSimControlsProps, SIM_FIELDS, SPEED_FAST_DECAY, SPEED_NORMAL_DECAY, type SimFieldDescriptor, type SimFieldKey, __ORBIT_SIMCONTROLS_SENTINEL__ };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useResolvedInstance, mergeStyle, cornerStyle } from '../chunk-
|
|
1
|
+
import { useResolvedInstance, mergeStyle, cornerStyle } from '../chunk-NPOV5GA3.js';
|
|
2
2
|
import { useId, useState, useRef, useCallback, useSyncExternalStore, Fragment } from 'react';
|
|
3
3
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ var SIM_FIELDS = [
|
|
|
8
8
|
{ key: "friction", label: "Friction", min: 0, max: 1, step: 0.01, fallback: 0.85 },
|
|
9
9
|
{ key: "linkDistance", label: "Link distance", min: 1, max: 100, step: 1, fallback: 10 },
|
|
10
10
|
{ key: "linkSpring", label: "Link spring", min: 0, max: 2, step: 0.01, fallback: 1 },
|
|
11
|
-
// v0.10.2: the rest of the
|
|
11
|
+
// v0.10.2: the rest of the tunable set. `collision` and `center` ship
|
|
12
12
|
// OFF at the engine default, so their sliders start at 0 — moving one is
|
|
13
13
|
// what turns the force on.
|
|
14
14
|
{ key: "collision", label: "Collision", min: 0, max: 2, step: 0.01, fallback: 0 },
|