@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
|
@@ -104,5 +104,5 @@ function sliderValueText(brush, domain) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
export { brushRangePct, sliderValueNow, sliderValueText, toggleCategoryExclusion, useRangeBrush };
|
|
107
|
-
//# sourceMappingURL=chunk-
|
|
108
|
-
//# sourceMappingURL=chunk-
|
|
107
|
+
//# sourceMappingURL=chunk-DTXFQLNP.js.map
|
|
108
|
+
//# sourceMappingURL=chunk-DTXFQLNP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/_shared/brush.ts"],"names":[],"mappings":"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","file":"chunk-DTXFQLNP.js","sourcesContent":["/**\n * Shared range-brush interaction: pointer-drag over a plot maps x → a\n * {min,max} window in the dimension's domain units. Any drag REPLACES the\n * brush; double-click clears; ArrowLeft/ArrowRight nudge the window by one\n * bin; Escape clears. The plot element itself is focusable. Used by the\n * packaged Histogram and Timeline components.\n */\n\nimport { useCallback, useRef } from 'react';\nimport type { KeyboardEvent as ReactKeyboardEvent, PointerEvent as ReactPointerEvent } from 'react';\nimport type { BrushState } from '@modernrelay/orbit-core';\n\nexport interface RangeDomain {\n min: number;\n max: number;\n}\n\nexport function clampValue(v: number, min: number, max: number): number {\n return v < min ? min : v > max ? max : v;\n}\n\n/** Map a pointer clientX over the plot rect to a domain value (clamped). */\nexport function xToDomainValue(\n clientX: number,\n rect: { left: number; width: number },\n domain: RangeDomain,\n): number {\n if (rect.width <= 0) return domain.min;\n const t = clampValue((clientX - rect.left) / rect.width, 0, 1);\n return domain.min + t * (domain.max - domain.min);\n}\n\n/** Range brush → left/width percentages inside the domain (null when the\n * brush is absent, categorical, or the domain is degenerate). */\nexport function brushRangePct(\n brush: BrushState,\n domain: RangeDomain | null,\n): { leftPct: number; widthPct: number } | null {\n if (brush === null || !('min' in brush) || domain === null) return null;\n const span = domain.max - domain.min;\n if (span <= 0) return null;\n const lo = clampValue(brush.min, domain.min, domain.max);\n const hi = clampValue(brush.max, domain.min, domain.max);\n if (hi < lo) return null;\n return { leftPct: ((lo - domain.min) / span) * 100, widthPct: ((hi - lo) / span) * 100 };\n}\n\n/**\n * Shift a {min,max} brush by ±one bin (domain span / binCount), clamped so\n * the window keeps its width. Returns the INPUT reference when there is\n * nothing to nudge (no range brush / degenerate domain) — callers skip the\n * write on reference equality.\n */\nexport function nudgeRangeBrush(\n brush: BrushState,\n domain: RangeDomain,\n binCount: number,\n direction: -1 | 1,\n): BrushState {\n if (brush === null || !('min' in brush)) return brush;\n const span = domain.max - domain.min;\n if (span <= 0 || binCount <= 0) return brush;\n const stepSize = span / binCount;\n const width = Math.min(brush.max - brush.min, span);\n const min = clampValue(brush.min + direction * stepSize, domain.min, domain.max - width);\n return { min, max: min + width };\n}\n\n/** Toggle one category exclusion; an empty exclusion list clears the brush. */\nexport function toggleCategoryExclusion(brush: BrushState, key: string): BrushState {\n const excluded = brush !== null && 'excluded' in brush ? brush.excluded : [];\n const next = excluded.includes(key) ? excluded.filter((k) => k !== key) : [...excluded, key];\n return next.length === 0 ? null : { excluded: next };\n}\n\nexport interface RangeBrushOptions {\n /** Numeric/temporal domain; null disables pointer/arrow interaction. */\n domain: RangeDomain | null;\n /** Bin count — one arrow nudge moves the window by span/binCount. */\n binCount: number;\n brush: BrushState;\n setBrush(brush: BrushState): void;\n}\n\nexport interface RangeBrushHandlers {\n onPointerDown(e: ReactPointerEvent<HTMLElement>): void;\n onPointerMove(e: ReactPointerEvent<HTMLElement>): void;\n onPointerUp(e: ReactPointerEvent<HTMLElement>): void;\n onPointerCancel(e: ReactPointerEvent<HTMLElement>): void;\n onDoubleClick(): void;\n onKeyDown(e: ReactKeyboardEvent<HTMLElement>): void;\n}\n\n/**\n * The drag/keyboard brush behavior over a plot element. Handler identities\n * are stable across renders (options are read through a ref), so spreading\n * them onto the plot never churns DOM listeners.\n */\nexport function useRangeBrush(options: RangeBrushOptions): RangeBrushHandlers {\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const dragRef = useRef<{ pointerId: number; startValue: number } | null>(null);\n\n const onPointerDown = useCallback((e: ReactPointerEvent<HTMLElement>): void => {\n const { domain } = optionsRef.current;\n if (domain === null || e.button !== 0) return;\n const start = xToDomainValue(e.clientX, e.currentTarget.getBoundingClientRect(), domain);\n dragRef.current = { pointerId: e.pointerId, startValue: start };\n const el = e.currentTarget;\n if (typeof el.setPointerCapture === 'function') {\n try {\n el.setPointerCapture(e.pointerId);\n } catch {\n // jsdom / detached element: capture is an optimization only.\n }\n }\n }, []);\n\n const onPointerMove = useCallback((e: ReactPointerEvent<HTMLElement>): void => {\n const drag = dragRef.current;\n const { domain, setBrush } = optionsRef.current;\n if (drag === null || domain === null || e.pointerId !== drag.pointerId) return;\n const value = xToDomainValue(e.clientX, e.currentTarget.getBoundingClientRect(), domain);\n setBrush(\n value < drag.startValue\n ? { min: value, max: drag.startValue }\n : { min: drag.startValue, max: value },\n );\n }, []);\n\n const endDrag = useCallback((e: ReactPointerEvent<HTMLElement>): void => {\n if (dragRef.current === null || dragRef.current.pointerId !== e.pointerId) return;\n dragRef.current = null;\n const el = e.currentTarget;\n if (typeof el.releasePointerCapture === 'function') {\n try {\n el.releasePointerCapture(e.pointerId);\n } catch {\n // Capture may never have been established.\n }\n }\n }, []);\n\n const onDoubleClick = useCallback((): void => {\n optionsRef.current.setBrush(null);\n }, []);\n\n const onKeyDown = useCallback((e: ReactKeyboardEvent<HTMLElement>): void => {\n const { domain, binCount, brush, setBrush } = optionsRef.current;\n if (e.key === 'Escape') {\n if (brush !== null) setBrush(null);\n e.preventDefault();\n return;\n }\n if (domain === null || (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight')) return;\n const next = nudgeRangeBrush(brush, domain, binCount, e.key === 'ArrowLeft' ? -1 : 1);\n if (next !== brush) setBrush(next);\n e.preventDefault();\n }, []);\n\n return {\n onPointerDown,\n onPointerMove,\n onPointerUp: endDrag,\n onPointerCancel: endDrag,\n onDoubleClick,\n onKeyDown,\n };\n}\n\n/** aria-valuenow for a brush \"slider\": the window's center (or domain min). */\nexport function sliderValueNow(brush: BrushState, domain: RangeDomain | undefined): number {\n if (domain === undefined) return 0;\n if (brush !== null && 'min' in brush) return (brush.min + brush.max) / 2;\n return domain.min;\n}\n\n/** aria-valuetext: human-readable brush window (or 'no filter'). */\nexport function sliderValueText(brush: BrushState, domain: RangeDomain | undefined): string {\n if (domain === undefined || brush === null || !('min' in brush)) return 'no filter';\n return `filtering ${brush.min} to ${brush.max}`;\n}\n"]}
|
|
@@ -65,5 +65,5 @@ var overlayLayerStyle = {
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
export { DEFAULT_CORNER_OFFSET, GraphProvider, cornerStyle, mergeStyle, overlayLayerStyle, overlaySurface, useAmbientGraphInstance, useResolvedInstance, visuallyHiddenStyle };
|
|
68
|
-
//# sourceMappingURL=chunk-
|
|
69
|
-
//# sourceMappingURL=chunk-
|
|
68
|
+
//# sourceMappingURL=chunk-NPOV5GA3.js.map
|
|
69
|
+
//# sourceMappingURL=chunk-NPOV5GA3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/GraphProvider.tsx","../src/components/shared.ts"],"names":["useContext"],"mappings":";;;;AAgBO,IAAM,oBAAA,GAAuB,cAAuC,IAAI,CAAA;AAMxE,SAAS,wBAAwB,QAAA,EAAoC;AAC1E,EAAA,MAAM,QAAA,GAAW,WAAW,oBAAoB,CAAA;AAChD,EAAA,IAAI,aAAa,IAAA,EAAM;AACrB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,gBAAgB,QAAQ,CAAA,2DAAA;AAAA,KAC1B;AAAA,EACF;AACA,EAAA,OAAO,QAAA;AACT;AAOO,SAAS,cAAc,KAAA,EAAyC;AACrE,EAAA,uBACE,GAAA,CAAC,qBAAqB,QAAA,EAArB,EAA8B,OAAO,KAAA,CAAM,QAAA,EACzC,gBAAM,QAAA,EACT,CAAA;AAEJ;ACxBO,SAAS,mBAAA,CACd,QAAA,EACA,aAAA,GAAgB,gBAAA,EACE;AAClB,EAAA,MAAM,OAAA,GAAUA,WAAW,oBAAoB,CAAA;AAC/C,EAAA,MAAM,WAAW,QAAA,IAAY,OAAA;AAC7B,EAAA,IAAI,QAAA,KAAa,IAAA,IAAQ,QAAA,KAAa,MAAA,EAAW;AAC/C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,gBAAgB,aAAa,CAAA,8HAAA;AAAA,KAE/B;AAAA,EACF;AACA,EAAA,OAAO,QAAA;AACT;AAWO,IAAM,qBAAA,GAAwB;AAO9B,SAAS,WAAA,CACd,MAAA,EACA,MAAA,GAAiB,qBAAA,EACF;AACf,EAAA,MAAM,CAAC,QAAA,EAAU,UAAU,CAAA,GAAI,MAAA,CAAO,MAAM,GAAG,CAAA;AAC/C,EAAA,OAAO,EAAE,QAAA,EAAU,UAAA,EAAY,CAAC,QAAQ,GAAG,MAAA,EAAQ,CAAC,UAAU,GAAG,MAAA,EAAO;AAC1E;AAEA,IAAM,cAAA,GAAiB;AAAA,EACrB,GAAA,EAAK,QAAA;AAAA,EACL,MAAA,EAAQ,KAAA;AAAA,EACR,IAAA,EAAM,OAAA;AAAA,EACN,KAAA,EAAO;AACT,CAAA;AAaO,SAAS,UAAA,CAAW,MAAqB,QAAA,EAAyC;AACvF,EAAA,IAAI,QAAA,KAAa,QAAW,OAAO,IAAA;AACnC,EAAA,MAAM,GAAA,GAAqB,EAAE,GAAG,IAAA,EAAK;AACrC,EAAA,KAAA,MAAW,SAAS,CAAC,KAAA,EAAO,QAAA,EAAU,MAAA,EAAQ,OAAO,CAAA,EAAY;AAC/D,IAAA,IAAI,QAAA,CAAS,KAAK,CAAA,KAAM,MAAA,SAAkB,GAAA,CAAI,cAAA,CAAe,KAAK,CAAC,CAAA;AAAA,EACrE;AACA,EAAA,OAAO,EAAE,GAAG,GAAA,EAAK,GAAG,QAAA,EAAS;AAC/B;AAgCO,SAAS,eAAe,QAAA,EAAiD;AAC9E,EAAA,OAAO,QAAA;AACT;AAOO,IAAM,mBAAA,GAAqC;AAAA,EAChD,QAAA,EAAU,UAAA;AAAA,EACV,KAAA,EAAO,CAAA;AAAA,EACP,MAAA,EAAQ,CAAA;AAAA,EACR,OAAA,EAAS,CAAA;AAAA,EACT,MAAA,EAAQ,EAAA;AAAA,EACR,QAAA,EAAU,QAAA;AAAA,EACV,IAAA,EAAM,eAAA;AAAA,EACN,UAAA,EAAY,QAAA;AAAA,EACZ,MAAA,EAAQ;AACV;AAIO,IAAM,iBAAA,GAAmC;AAAA,EAC9C,QAAA,EAAU,UAAA;AAAA,EACV,KAAA,EAAO,CAAA;AAAA,EACP,aAAA,EAAe;AACjB","file":"chunk-NPOV5GA3.js","sourcesContent":["/**\n * GraphProvider — context plumbing for the React binding.\n *\n * `<Graph>` renders this internally around its children; advanced hosts can\n * use it directly to compose against an externally created GraphInstance.\n */\n\nimport { createContext, useContext } from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport type { GraphInstance } from '@modernrelay/orbit-core';\n\n// GraphInstance is invariant in its attribute generics (applyHostUpdate is\n// contravariant, getNode covariant), so the context stores an `any`-erased\n// instance; typed surfaces (hooks, GraphHandle) re-narrow at the edges.\nexport type AnyGraphInstance = GraphInstance<any, any>;\n\nexport const GraphInstanceContext = createContext<AnyGraphInstance | null>(null);\n\n/**\n * Internal: resolve the ambient instance or throw a descriptive error naming\n * the calling hook. Must be called unconditionally (it is itself a hook).\n */\nexport function useAmbientGraphInstance(hookName: string): AnyGraphInstance {\n const instance = useContext(GraphInstanceContext);\n if (instance === null) {\n throw new Error(\n `orbit-react: ${hookName}() must be used within a <Graph> or <GraphProvider> subtree`,\n );\n }\n return instance;\n}\n\nexport interface GraphProviderProps {\n instance: AnyGraphInstance;\n children?: ReactNode;\n}\n\nexport function GraphProvider(props: GraphProviderProps): ReactElement {\n return (\n <GraphInstanceContext.Provider value={props.instance}>\n {props.children}\n </GraphInstanceContext.Provider>\n );\n}\n","/**\n * Shared component foundation: instance resolution + tiny styling\n * primitives every packaged component (`Toolbar`, `ContextMenu`,\n * `SelectionActions`, `Navigator`) and the built-in overlays use.\n */\n\nimport { useContext } from 'react';\nimport type { CSSProperties } from 'react';\nimport type { LabelPlacement } from '@modernrelay/orbit-core';\nimport { GraphInstanceContext } from '../GraphProvider';\nimport type { AnyGraphInstance } from '../GraphProvider';\n\n/**\n * Resolve the GraphInstance a component operates on: an explicit `instance`\n * prop wins, else the ambient <Graph>/<GraphProvider> context. Throws a\n * descriptive error naming the component when neither exists.\n *\n * Must be called unconditionally (it is itself a hook).\n */\nexport function useResolvedInstance(\n explicit?: AnyGraphInstance,\n componentName = 'This component',\n): AnyGraphInstance {\n const ambient = useContext(GraphInstanceContext);\n const resolved = explicit ?? ambient;\n if (resolved === null || resolved === undefined) {\n throw new Error(\n `orbit-react: ${componentName} requires a GraphInstance — render it inside a ` +\n '<Graph> or <GraphProvider> subtree, or pass an explicit `instance` prop.',\n );\n }\n return resolved;\n}\n\n// ---------------------------------------------------------------------------\n// Placement. Floating components are positioned by a\n// PROP, not by CSS the host has to reverse-engineer.\n// ---------------------------------------------------------------------------\n\n/** Corner a floating component anchors to inside the graph container. */\nexport type GraphCorner = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n\n/** Default distance from the two anchored edges, in CSS px. */\nexport const DEFAULT_CORNER_OFFSET = 12;\n\n/**\n * Absolute-position style for one corner. Only the TWO insets that corner\n * needs are emitted — never all four — so a caller-supplied inset in `style`\n * has nothing to fight with.\n */\nexport function cornerStyle(\n corner: GraphCorner,\n offset: number = DEFAULT_CORNER_OFFSET,\n): CSSProperties {\n const [vertical, horizontal] = corner.split('-') as ['top' | 'bottom', 'left' | 'right'];\n return { position: 'absolute', [vertical]: offset, [horizontal]: offset };\n}\n\nconst OPPOSITE_INSET = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n} as const;\n\n/**\n * Merge a component's default style UNDER a caller override, resolving inset\n * conflicts per AXIS: an inset the caller supplies drops the default's\n * opposite.\n *\n * Plain spreading layers them instead, which is a silent partial application\n * `{ right: 8 }` over a default `left: 12` leaves BOTH live, `left` wins in\n * LTR, and the caller's value sits in the DOM doing nothing. Per-axis (rather\n * than dropping every inset) keeps the perpendicular anchor, so a lone\n * `{ top: 20 }` nudges vertically instead of jumping to the container edge.\n */\nexport function mergeStyle(base: CSSProperties, override?: CSSProperties): CSSProperties {\n if (override === undefined) return base;\n const out: CSSProperties = { ...base };\n for (const inset of ['top', 'bottom', 'left', 'right'] as const) {\n if (override[inset] !== undefined) delete out[OPPOSITE_INSET[inset]];\n }\n return { ...out, ...override };\n}\n\n// ---------------------------------------------------------------------------\n// Pinned core↔react overlay interface. These members live on\n// GraphInstance; until the core type surface catches up, components reach\n// them through this structural view with optional access, which stays exactly\n// signature-compatible once the members are required on GraphInstance.\n// ---------------------------------------------------------------------------\n\n/** `instance.labels` — the DOM label lane subscription surface. */\nexport interface GraphLabelsSurface {\n /** Fires ONLY when the candidate SET changes (throttled re-rank; React\n * re-renders label content). Replays current state on subscribe. */\n subscribeCandidates(cb: (list: readonly LabelPlacement[]) => void): () => void;\n /** Fires on scheduler ticks with fresh x/y for the SAME set (imperative\n * transform writes — never a React re-render). Replays on subscribe. */\n subscribePositions(cb: (list: readonly LabelPlacement[]) => void): () => void;\n}\n\n/** Instance members components rely on beyond the v0.3 GraphInstance type. */\nexport interface GraphOverlaySurface {\n labels?: GraphLabelsSurface;\n /** Binding-detected reduced-motion media preference. */\n setReducedMotion?(v: boolean | undefined): void;\n pauseSimulation?(): void;\n resumeSimulation?(): void;\n isSimulationRunning?(): boolean;\n /** Delegates to the engine; resolves null when unsupported/not ready. */\n captureScreenshot?(): Promise<Blob | null>;\n}\n\n/** Structural view of the overlay surface on a GraphInstance. */\nexport function overlaySurface(instance: AnyGraphInstance): GraphOverlaySurface {\n return instance as unknown as GraphOverlaySurface;\n}\n\n// ---------------------------------------------------------------------------\n// Styling primitives\n// ---------------------------------------------------------------------------\n\n/** Screen-reader-only content (the classic visually-hidden clip pattern). */\nexport const visuallyHiddenStyle: CSSProperties = {\n position: 'absolute',\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: 'hidden',\n clip: 'rect(0 0 0 0)',\n whiteSpace: 'nowrap',\n border: 0,\n};\n\n/** A full-bleed overlay layer above the canvas that never eats pointevents\n * unless a child opts back in with `pointerEvents: 'auto'`. */\nexport const overlayLayerStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n pointerEvents: 'none',\n};\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useCrossfilterDimension, useSessionSetBrush } from './chunk-
|
|
2
|
-
import { useAmbientGraphInstance } from './chunk-
|
|
1
|
+
import { useCrossfilterDimension, useSessionSetBrush } from './chunk-S36LHSNX.js';
|
|
2
|
+
import { useAmbientGraphInstance } from './chunk-NPOV5GA3.js';
|
|
3
3
|
import { useRef, useCallback, useSyncExternalStore } from 'react';
|
|
4
4
|
|
|
5
5
|
function useStoreSelector(instance, selector) {
|
|
@@ -115,5 +115,5 @@ function useGraphCrossfilter(key) {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
export { getSearchResultUnavailableCallback, setSearchResultUnavailableCallback, useGraphCrossfilter, useGraphDiagnostics, useGraphEdgeHover, useGraphHistory, useGraphHover, useGraphInstance, useGraphOverlays, useGraphPendingExpansions, useGraphPins, useGraphScope, useGraphSearch, useGraphSelection, useGraphSimulationRunning, useGraphStatus, useGraphTheme, useGraphTimeline, useGraphViewport, useGraphVisible, useScaleInfoSubscription };
|
|
118
|
-
//# sourceMappingURL=chunk-
|
|
119
|
-
//# sourceMappingURL=chunk-
|
|
118
|
+
//# sourceMappingURL=chunk-ODOPSUI7.js.map
|
|
119
|
+
//# sourceMappingURL=chunk-ODOPSUI7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks.ts"],"names":[],"mappings":";;;;AAqCA,SAAS,gBAAA,CAAoB,UAA4B,QAAA,EAAwC;AAC/F,EAAA,MAAM,WAAA,GAAc,OAAO,QAAQ,CAAA;AACnC,EAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AACtB,EAAA,MAAM,WAAW,MAAA,CAAyB,EAAE,UAAU,KAAA,EAAO,KAAA,EAAO,QAAgB,CAAA;AAEpF,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,aAAA,KAA8B,QAAA,CAAS,KAAA,CAAM,UAAU,aAAa,CAAA;AAAA,IACrE,CAAC,QAAQ;AAAA,GACX;AACA,EAAA,MAAM,WAAA,GAAc,YAAY,MAAM;AACpC,IAAA,MAAM,OAAO,WAAA,CAAY,OAAA,CAAQ,QAAA,CAAS,KAAA,CAAM,UAAU,CAAA;AAC1D,IAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AACvB,IAAA,IAAI,KAAA,CAAM,YAAY,MAAA,CAAO,EAAA,CAAG,MAAM,KAAA,EAAO,IAAI,CAAA,EAAG,OAAO,KAAA,CAAM,KAAA;AACjE,IAAA,KAAA,CAAM,QAAA,GAAW,IAAA;AACjB,IAAA,KAAA,CAAM,KAAA,GAAQ,IAAA;AACd,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,OAAO,oBAAA,CAAqB,SAAA,EAAW,WAAA,EAAa,WAAW,CAAA;AACjE;AAEA,IAAM,eAAA,GAAkB,CAAC,CAAA,KAAuC,CAAA,CAAE,SAAA;AAGlE,IAAM,uBAAA,GAA0B,CAAC,CAAA,KAC9B,CAAA,CAAwD,iBAAA,IAAqB,KAAA;AAChF,IAAM,WAAA,GAAc,CAAC,CAAA,KAAsC,CAAA,CAAE,KAAA,CAAM,MAAA;AACnE,IAAM,eAAA,GAAkB,CAAC,CAAA,KAAsC,CAAA,CAAE,KAAA,CAAM,MAAA;AACvE,IAAM,UAAA,GAAa,CAAC,CAAA,KAAuE,CAAA,CAAE,IAAA;AAC7F,IAAM,cAAA,GAAiB,CAAC,CAAA,KAA6C,CAAA,CAAE,QAAA;AACvE,IAAM,YAAA,GAAe,CAAC,CAAA,KAAuC,CAAA,CAAE,MAAA;AAC/D,IAAM,iBAAA,GAAoB,CAAC,CAAA,KAAmD,CAAA,CAAE,WAAA;AAChF,IAAM,WAAA,GAAc,CAAC,CAAA,KAA4C,CAAA,CAAE,KAAA;AACnE,IAAM,uBAAA,GAA0B,CAAC,CAAA,KAA4C,CAAA,CAAE,iBAAA;AAC/E,IAAM,gBAAA,GAAmB,CAAC,CAAA,KAA0C,CAAA,CAAE,UAAA;AACtE,IAAM,aAAA,GAAgB,CAAC,CAAA,KAAyD,CAAA,CAAE,OAAA;AAClF,IAAM,cAAA,GAAiB,CAAC,CAAA,KAAsD,CAAA,CAAE,QAAA;AAChF,IAAM,aAAA,GAAgB,CAAC,CAAA,KAAiE,CAAA,CAAE,OAAA;AAE1F,IAAM,WAAA,GAAc,CAAC,CAAA,KAAmC,CAAA,CAAE,KAAA;AAC1D,IAAM,kBAAA,GAAqB,CAAC,CAAA,KAC1B,CAAA,EAAG,EAAE,SAAA,CAAU,KAAK,CAAA,CAAA,EAAI,CAAA,CAAE,SAAA,CAAU,KAAK,CAAA,CAAA,EAAI,CAAA,CAAE,UAAU,MAAM,CAAA,CAAA;AAE1D,SAAS,gBAAA,GAGS;AACvB,EAAA,OAAO,wBAAwB,kBAAkB,CAAA;AACnD;AAIO,SAAS,iBAAA,GAAoC;AAClD,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,mBAAmB,CAAA,EAAG,eAAe,CAAA;AACvF;AAEO,SAAS,aAAA,GAA+B;AAC7C,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,eAAe,CAAA,EAAG,WAAW,CAAA;AAC/E;AAEO,SAAS,iBAAA,GAAmC;AACjD,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,mBAAmB,CAAA,EAAG,eAAe,CAAA;AACvF;AAGO,SAAS,YAAA,GAA+D;AAC7E,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,cAAc,CAAA,EAAG,UAAU,CAAA;AAC7E;AAEO,SAAS,gBAAA,GAAyC;AACvD,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,kBAAkB,CAAA,EAAG,cAAc,CAAA;AACrF;AAEO,SAAS,cAAA,GAAiC;AAC/C,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,gBAAgB,CAAA,EAAG,YAAY,CAAA;AACjF;AAEO,SAAS,mBAAA,GAAkD;AAChE,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,qBAAqB,CAAA,EAAG,iBAAiB,CAAA;AAC3F;AAIO,SAAS,yBAAA,GAAqC;AACnD,EAAA,OAAO,gBAAA;AAAA,IACL,wBAAwB,2BAA2B,CAAA;AAAA,IACnD;AAAA,GACF;AACF;AAKO,SAAS,aAAA,GAAqC;AACnD,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,eAAe,CAAA,EAAG,WAAW,CAAA;AAC/E;AAIO,SAAS,yBAAA,GAAiD;AAC/D,EAAA,OAAO,gBAAA;AAAA,IACL,wBAAwB,2BAA2B,CAAA;AAAA,IACnD;AAAA,GACF;AACF;AAGO,SAAS,gBAAA,GAAsC;AACpD,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,kBAAkB,CAAA,EAAG,gBAAgB,CAAA;AACvF;AAIO,SAAS,eAAA,GAAoD;AAClE,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,iBAAiB,CAAA,EAAG,aAAa,CAAA;AACnF;AAGO,SAAS,gBAAA,GAAkD;AAChE,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,kBAAkB,CAAA,EAAG,cAAc,CAAA;AACrF;AAGO,SAAS,eAAA,GAA4D;AAC1E,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,iBAAiB,CAAA,EAAG,aAAa,CAAA;AACnF;AAKO,SAAS,aAAA,GAA4B;AAC1C,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,eAAe,CAAA,EAAG,WAAW,CAAA;AAC/E;AAUO,SAAS,yBAAyB,QAAA,EAGvC;AACA,EAAA,MAAM,OAAA,GAAU,gBAAA,CAAiB,QAAA,EAAU,kBAAkB,CAAA;AAC7D,EAAA,MAAM,KAAA,GAAQ,gBAAA,CAAiB,QAAA,EAAU,WAAW,CAAA;AACpD,EAAA,OAAO,EAAE,SAAS,KAAA,EAAM;AAC1B;AAMA,IAAM,YAAA,GAAe,CACnB,CAAA,KAC+D,CAAA,CAAE,MAAA;AAM5D,SAAS,cAAA,GAA6E;AAC3F,EAAA,OAAO,gBAAA,CAAiB,uBAAA,CAAwB,gBAAgB,CAAA,EAAG,YAAY,CAAA;AACjF;AAqBA,IAAM,0BAAA,uBAAiC,OAAA,EAA2D;AAI3F,SAAS,kCAAA,CACd,UACA,EAAA,EACM;AACN,EAAA,IAAI,EAAA,KAAO,MAAA,EAAW,0BAAA,CAA2B,MAAA,CAAO,QAAQ,CAAA;AAAA,OAC3D,0BAAA,CAA2B,GAAA,CAAI,QAAA,EAAU,EAAE,CAAA;AAClD;AAGO,SAAS,mCACd,QAAA,EAC6C;AAC7C,EAAA,OAAO,0BAAA,CAA2B,IAAI,QAAQ,CAAA;AAChD;AAgBO,SAAS,oBAAoB,GAAA,EAAwC;AAC1E,EAAA,MAAM,QAAA,GAAW,wBAAwB,qBAAqB,CAAA;AAC9D,EAAA,MAAM,QAAA,GAAW,uBAAA,CAAwB,QAAA,EAAU,GAAG,CAAA;AACtD,EAAA,MAAM,QAAA,GAAW,kBAAA,CAAmB,QAAA,EAAU,GAAG,CAAA;AACjD,EAAA,OAAO,EAAE,OAAA,EAAS,QAAA,CAAS,SAAS,KAAA,EAAO,QAAA,CAAS,OAAO,QAAA,EAAS;AACtE","file":"chunk-ODOPSUI7.js","sourcesContent":["/**\n * Public hooks — thin useSyncExternalStore selectors over the ambient\n * GraphInstance's vanilla zustand store. All throw a descriptive error when\n * used outside a <Graph>/<GraphProvider> subtree.\n */\n\nimport { useCallback, useRef, useSyncExternalStore } from 'react';\nimport type {\n BrushState,\n DimensionSummary,\n EdgeId,\n GraphDiagnostic,\n GraphInstance,\n GraphStoreState,\n GraphTheme,\n InstanceStatus,\n NodeId,\n SearchResult,\n SearchUnavailableReason,\n SelectionState,\n SubgraphSpec,\n ViewportState,\n} from '@modernrelay/orbit-core';\nimport { useAmbientGraphInstance } from './GraphProvider';\nimport type { AnyGraphInstance } from './GraphProvider';\nimport { useCrossfilterDimension, useSessionSetBrush } from './components/_shared/crossfilter';\n\ninterface SelectorCache<T> {\n hasValue: boolean;\n value: T;\n}\n\n/**\n * Subscribe to a projection of the store. The snapshot is memoized with\n * Object.is so selectors that recompute to the same value keep a stable\n * reference (required by useSyncExternalStore to avoid render loops).\n */\nfunction useStoreSelector<T>(instance: AnyGraphInstance, selector: (s: GraphStoreState) => T): T {\n const selectorRef = useRef(selector);\n selectorRef.current = selector;\n const cacheRef = useRef<SelectorCache<T>>({ hasValue: false, value: undefined as T });\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => instance.store.subscribe(onStoreChange),\n [instance],\n );\n const getSnapshot = useCallback(() => {\n const next = selectorRef.current(instance.store.getState());\n const cache = cacheRef.current;\n if (cache.hasValue && Object.is(cache.value, next)) return cache.value;\n cache.hasValue = true;\n cache.value = next;\n return next;\n }, [instance]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nconst selectSelection = (s: GraphStoreState): SelectionState => s.selection;\n// Read `simulationRunning` tolerantly so\n// the hook is correct both before and after the core field lands.\nconst selectSimulationRunning = (s: GraphStoreState): boolean =>\n (s as GraphStoreState & { simulationRunning?: boolean }).simulationRunning ?? false;\nconst selectHover = (s: GraphStoreState): NodeId | null => s.hover.nodeId;\nconst selectEdgeHover = (s: GraphStoreState): EdgeId | null => s.hover.edgeId;\nconst selectPins = (s: GraphStoreState): ReadonlyMap<NodeId, readonly [number, number]> => s.pins;\nconst selectViewport = (s: GraphStoreState): ViewportState | null => s.viewport;\nconst selectStatus = (s: GraphStoreState): InstanceStatus => s.status;\nconst selectDiagnostics = (s: GraphStoreState): readonly GraphDiagnostic[] => s.diagnostics;\nconst selectScope = (s: GraphStoreState): SubgraphSpec | null => s.scope;\nconst selectPendingExpansions = (s: GraphStoreState): ReadonlySet<NodeId> => s.pendingExpansions;\nconst selectOverlayIds = (s: GraphStoreState): readonly string[] => s.overlayIds;\nconst selectVisible = (s: GraphStoreState): { nodes: number; edges: number } => s.visible;\nconst selectTimeline = (s: GraphStoreState): { playingKey: string | null } => s.timeline;\nconst selectHistory = (s: GraphStoreState): { undoDepth: number; redoDepth: number } => s.history;\n// Resolved theme tokens + the scale-info invalidation signal.\nconst selectTheme = (s: GraphStoreState): GraphTheme => s.theme;\nconst selectScaleVersion = (s: GraphStoreState): string =>\n `${s.revisions.model}:${s.revisions.scope}:${s.revisions.render}`;\n\nexport function useGraphInstance<\n N = Record<string, unknown>,\n E = Record<string, unknown>,\n>(): GraphInstance<N, E> {\n return useAmbientGraphInstance('useGraphInstance') as GraphInstance<N, E>;\n}\n\n/** The full namespaced SelectionState; was `readonly NodeId[]` before\n * v0.3 — read `.nodeIds` for the node namespace. */\nexport function useGraphSelection(): SelectionState {\n return useStoreSelector(useAmbientGraphInstance('useGraphSelection'), selectSelection);\n}\n\nexport function useGraphHover(): NodeId | null {\n return useStoreSelector(useAmbientGraphInstance('useGraphHover'), selectHover);\n}\n\nexport function useGraphEdgeHover(): EdgeId | null {\n return useStoreSelector(useAmbientGraphInstance('useGraphEdgeHover'), selectEdgeHover);\n}\n\n/** The pin slice: node id → pinned space position. */\nexport function useGraphPins(): ReadonlyMap<NodeId, readonly [number, number]> {\n return useStoreSelector(useAmbientGraphInstance('useGraphPins'), selectPins);\n}\n\nexport function useGraphViewport(): ViewportState | null {\n return useStoreSelector(useAmbientGraphInstance('useGraphViewport'), selectViewport);\n}\n\nexport function useGraphStatus(): InstanceStatus {\n return useStoreSelector(useAmbientGraphInstance('useGraphStatus'), selectStatus);\n}\n\nexport function useGraphDiagnostics(): readonly GraphDiagnostic[] {\n return useStoreSelector(useAmbientGraphInstance('useGraphDiagnostics'), selectDiagnostics);\n}\n\n/** Whether the engine force simulation is currently running (drives the\n * toolbar pause/resume affordance). */\nexport function useGraphSimulationRunning(): boolean {\n return useStoreSelector(\n useAmbientGraphInstance('useGraphSimulationRunning'),\n selectSimulationRunning,\n );\n}\n\n/** The active hard scope (`subgraph`); null while the full accepted\n * model is in view. Uncontrolled-only in v0.5: the `subgraph` prop and\n * isolateSelection/resetIsolation write this same instance-owned state. */\nexport function useGraphScope(): SubgraphSpec | null {\n return useStoreSelector(useAmbientGraphInstance('useGraphScope'), selectScope);\n}\n\n/** Node ids with an expansion in flight (loading affordance — drive\n * spinners/ghost skeletons from this set). */\nexport function useGraphPendingExpansions(): ReadonlySet<NodeId> {\n return useStoreSelector(\n useAmbientGraphInstance('useGraphPendingExpansions'),\n selectPendingExpansions,\n );\n}\n\n/** Committed overlay ids for the current dataset. */\nexport function useGraphOverlays(): readonly string[] {\n return useStoreSelector(useAmbientGraphInstance('useGraphOverlays'), selectOverlayIds);\n}\n\n/** soft-mask visibility counts: scene entities with zero hide-failures\n * (equals the accepted counts when nothing masks). */\nexport function useGraphVisible(): { nodes: number; edges: number } {\n return useStoreSelector(useAmbientGraphInstance('useGraphVisible'), selectVisible);\n}\n\n/** timeline playback state — at most one playing dimension key. */\nexport function useGraphTimeline(): { playingKey: string | null } {\n return useStoreSelector(useAmbientGraphInstance('useGraphTimeline'), selectTimeline);\n}\n\n/** History kernel depths for the full undo/redo walk. */\nexport function useGraphHistory(): { undoDepth: number; redoDepth: number } {\n return useStoreSelector(useAmbientGraphInstance('useGraphHistory'), selectHistory);\n}\n\n/** Resolved theme tokens: the merged GraphTheme currently driving\n * engine config, projection fallbacks, and mask dim alpha (`store.theme`).\n * Style legend chrome, tooltips, and other host overlays from these tokens. */\nexport function useGraphTheme(): GraphTheme {\n return useStoreSelector(useAmbientGraphInstance('useGraphTheme'), selectTheme);\n}\n\n/**\n * INTERNAL: subscription surface for `instance.getScaleInfo` readers\n * (<GraphLegend>, the `renderLegend` slot). `version` changes whenever a\n * store publication could change scale info (model/scope/render revisions);\n * `theme` is the stable-by-value theme reference. Consumers recompute\n * getScaleInfo in a memo keyed on both. Not re-exported from the package\n * root — subject to change without notice.\n */\nexport function useScaleInfoSubscription(instance: AnyGraphInstance): {\n version: string;\n theme: GraphTheme;\n} {\n const version = useStoreSelector(instance, selectScaleVersion);\n const theme = useStoreSelector(instance, selectTheme);\n return { version, theme };\n}\n\n// ---------------------------------------------------------------------------\n// search\n// ---------------------------------------------------------------------------\n\nconst selectSearch = (\n s: GraphStoreState,\n): { query: string; results: readonly SearchResult[] } | null => s.search;\n\n/** The last completed search (`store.search`): `{query, results}` after\n * a successful `instance.search`, null after `clearSearch` / a dataset\n * swap. Drives `<GraphSearch>`'s result list and the navigator's\n * search-results section. */\nexport function useGraphSearch(): { query: string; results: readonly SearchResult[] } | null {\n return useStoreSelector(useAmbientGraphInstance('useGraphSearch'), selectSearch);\n}\n\n/** `onSearchResultUnavailable` callback type: fired when an activated result\n * cannot be focused; `reason` is\n * 'not-loaded' | 'out-of-scope' | 'filtered'. The host reacts explicitly;\n * Orbit never changes scope/filters behind the user's back. */\nexport type SearchResultUnavailableCallback = (\n result: SearchResult,\n reason: SearchUnavailableReason,\n) => void;\n\n/**\n * INTERNAL: the `<Graph onSearchResultUnavailable>` → `<GraphSearch>`\n * default-callback channel. `<Graph>` registers its prop here per instance;\n * `<GraphSearch>` (and other activation surfaces) fall back to it when they\n * have no local `onResultUnavailable` prop. A WeakMap keyed by the instance\n * not a React context — so packaged component entries can resolve it without\n * `<Graph>` in their import graph (treeshake sentinels stay clean) and\n * registration never re-renders subscribers. Not re-exported from the package\n * root — subject to change without notice.\n */\nconst searchUnavailableCallbacks = new WeakMap<AnyGraphInstance, SearchResultUnavailableCallback>();\n\n/** INTERNAL: register/clear the instance's default unavailable-result\n * callback (undefined clears). See {@link searchUnavailableCallbacks}. */\nexport function setSearchResultUnavailableCallback(\n instance: AnyGraphInstance,\n cb: SearchResultUnavailableCallback | undefined,\n): void {\n if (cb === undefined) searchUnavailableCallbacks.delete(instance);\n else searchUnavailableCallbacks.set(instance, cb);\n}\n\n/** INTERNAL: resolve the instance's default unavailable-result callback. */\nexport function getSearchResultUnavailableCallback(\n instance: AnyGraphInstance,\n): SearchResultUnavailableCallback | undefined {\n return searchUnavailableCallbacks.get(instance);\n}\n\n/** One crossfilter dimension: live summary + brush + a setter. */\nexport interface GraphCrossfilterDimension {\n /** Null until the `crossfilter` prop builds the dimension over data. */\n summary: DimensionSummary | null;\n brush: BrushState;\n setBrush: (brush: BrushState) => void;\n}\n\n/**\n * Subscribe to one crossfilter dimension by key. Re-reads\n * summarize/getBrush on session notify; resolves the session lazily (null\n * summary until the `crossfilter` prop builds dimensions over an accepted\n * base — the store publishes when they do).\n */\nexport function useGraphCrossfilter(key: string): GraphCrossfilterDimension {\n const instance = useAmbientGraphInstance('useGraphCrossfilter');\n const snapshot = useCrossfilterDimension(instance, key);\n const setBrush = useSessionSetBrush(instance, key);\n return { summary: snapshot.summary, brush: snapshot.brush, setBrush };\n}\n"]}
|
|
@@ -89,5 +89,5 @@ function useSessionSetBrush(instance, key) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
export { useCrossfilterDimension, useSessionSetBrush };
|
|
92
|
-
//# sourceMappingURL=chunk-
|
|
93
|
-
//# sourceMappingURL=chunk-
|
|
92
|
+
//# sourceMappingURL=chunk-S36LHSNX.js.map
|
|
93
|
+
//# sourceMappingURL=chunk-S36LHSNX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/_shared/crossfilter.ts"],"names":[],"mappings":"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","file":"chunk-S36LHSNX.js","sourcesContent":["/**\n * Internal crossfilter subscription: shared by the public\n * useGraphCrossfilter hook and the packaged Histogram and Timeline\n * components.\n *\n * Subscribes to the SESSION (session.subscribe) and re-reads\n * summarize/getBrush on notify. The session resolves lazily — null until the\n * `crossfilter` prop has built dimensions over an accepted base — polled via\n * a store subscription (the store publishes when dimensions build). A\n * dimension rebuild swaps the underlying engine WITHOUT changing the session\n * facade identity (and clears the old engine's subscribers), so every store\n * publication re-attaches the session callback; snapshots are structurally\n * cached, making the extra notifications free.\n */\n\nimport { useCallback, useRef, useSyncExternalStore } from 'react';\nimport type { BrushState, DimensionSummary } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\n\nexport interface CrossfilterSnapshot {\n /** Null until the dimension exists (no session yet / unknown key). */\n summary: DimensionSummary | null;\n brush: BrushState;\n}\n\nconst EMPTY_SNAPSHOT: CrossfilterSnapshot = { summary: null, brush: null };\n\n/** Structural brush equality (brushes are tiny). */\nexport function sameBrush(a: BrushState, b: BrushState): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n if ('excluded' in a) {\n return (\n 'excluded' in b &&\n a.excluded.length === b.excluded.length &&\n a.excluded.every((k, i) => k === b.excluded[i])\n );\n }\n return !('excluded' in b) && a.min === b.min && a.max === b.max;\n}\n\nfunction sameSummary(a: DimensionSummary | null, b: DimensionSummary | null): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n if (a.key !== b.key || a.kind !== b.kind || a.excludedRows !== b.excludedRows) return false;\n if (a.domain?.min !== b.domain?.min || a.domain?.max !== b.domain?.max) return false;\n if (a.bins.length !== b.bins.length || a.categories.length !== b.categories.length) {\n return false;\n }\n for (let i = 0; i < a.bins.length; i++) {\n const x = a.bins[i]!;\n const y = b.bins[i]!;\n if (x.x0 !== y.x0 || x.x1 !== y.x1 || x.total !== y.total || x.filtered !== y.filtered) {\n return false;\n }\n }\n for (let i = 0; i < a.categories.length; i++) {\n const x = a.categories[i]!;\n const y = b.categories[i]!;\n if (\n x.key !== y.key ||\n x.total !== y.total ||\n x.filtered !== y.filtered ||\n x.excluded !== y.excluded\n ) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Live {summary, brush} for one dimension key on an explicit instance.\n * Returns a referentially stable snapshot until the content actually changes.\n */\nexport function useCrossfilterDimension(\n instance: AnyGraphInstance,\n key: string,\n): CrossfilterSnapshot {\n const subscribe = useCallback(\n (onChange: () => void) => {\n let sessionUnsub: (() => void) | null = null;\n const attach = (): void => {\n sessionUnsub?.();\n sessionUnsub = null;\n const session = instance.getCrossfilterSession();\n if (session !== null) sessionUnsub = session.subscribe(onChange);\n };\n attach();\n const storeUnsub = instance.store.subscribe(() => {\n attach();\n onChange();\n });\n return () => {\n storeUnsub();\n sessionUnsub?.();\n sessionUnsub = null;\n };\n },\n [instance],\n );\n\n const cacheRef = useRef<{ key: string; value: CrossfilterSnapshot } | null>(null);\n const getSnapshot = useCallback((): CrossfilterSnapshot => {\n const session = instance.getCrossfilterSession();\n let next = EMPTY_SNAPSHOT;\n if (session !== null) {\n try {\n next = { summary: session.summarize(key), brush: session.getBrush(key) };\n } catch {\n // Unknown key / dimension mid-rebuild: absent, never a crash.\n next = EMPTY_SNAPSHOT;\n }\n }\n const cache = cacheRef.current;\n if (\n cache !== null &&\n cache.key === key &&\n sameSummary(cache.value.summary, next.summary) &&\n sameBrush(cache.value.brush, next.brush)\n ) {\n return cache.value;\n }\n cacheRef.current = { key, value: next };\n return next;\n }, [instance, key]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** Stable setter routing through the session (no-op until it exists). */\nexport function useSessionSetBrush(\n instance: AnyGraphInstance,\n key: string,\n): (brush: BrushState) => void {\n return useCallback(\n (brush: BrushState): void => {\n const session = instance.getCrossfilterSession();\n if (session === null) return;\n // Rejections only occur on teardown races (destroyed instance).\n void session.setBrush(key, brush).catch(() => undefined);\n },\n [instance, key],\n );\n}\n"]}
|
|
@@ -3,7 +3,7 @@ import { GraphNode, NodeId } from '@modernrelay/orbit-core';
|
|
|
3
3
|
import { A as AnyGraphInstance } from '../GraphProvider-B8ITDXS0.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* <GraphContextMenu> —
|
|
6
|
+
* <GraphContextMenu> — right-click / long-press menu.
|
|
7
7
|
*
|
|
8
8
|
* Listens to the instance's typed 'contextMenu' event (the ONLY channel — the
|
|
9
9
|
* instance has no openContextMenu method) and renders a portal-free,
|
|
@@ -17,7 +17,7 @@ import { A as AnyGraphInstance } from '../GraphProvider-B8ITDXS0.js';
|
|
|
17
17
|
* pointerdown and window blur. All target-derived text (the node label
|
|
18
18
|
* heading) renders as TEXT NODES — never markup.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
20
|
+
* path pair: with `onFindPath` provided, node menus carry
|
|
21
21
|
* 'Find path from here' / 'Find path to here'. The pending 'from' anchor is
|
|
22
22
|
* stashed locally in the component (session-local); completing the pair
|
|
23
23
|
* fires `onFindPath(anchorId, targetId)` and clears the anchor.
|
|
@@ -41,7 +41,7 @@ interface GraphContextMenuItem {
|
|
|
41
41
|
disabled?: boolean;
|
|
42
42
|
onSelect(): void;
|
|
43
43
|
}
|
|
44
|
-
/** Pinned-interface alias
|
|
44
|
+
/** Pinned-interface alias. */
|
|
45
45
|
type MenuItem = GraphContextMenuItem;
|
|
46
46
|
interface GraphContextMenuProps {
|
|
47
47
|
/** Explicit instance; defaults to the ambient <Graph>/<GraphProvider> one. */
|
|
@@ -59,7 +59,7 @@ interface GraphContextMenuProps {
|
|
|
59
59
|
defaultItems: readonly GraphContextMenuItem[];
|
|
60
60
|
}) => readonly GraphContextMenuItem[];
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* path pair: providing this adds 'Find path from here' /
|
|
63
63
|
* 'Find path to here' to node menus. 'From here' stashes the anchor node
|
|
64
64
|
* id locally (component state — session-local, never serialized); 'to
|
|
65
65
|
* here' appears once an anchor exists on a DIFFERENT node, fires
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useResolvedInstance } from '../chunk-
|
|
1
|
+
import { useResolvedInstance } from '../chunk-NPOV5GA3.js';
|
|
2
2
|
import { useState, useRef, useCallback, useEffect, useSyncExternalStore } from 'react';
|
|
3
3
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -331,7 +331,7 @@ function GraphContextMenu(props) {
|
|
|
331
331
|
onKeyDown,
|
|
332
332
|
children: [
|
|
333
333
|
heading !== null ? (
|
|
334
|
-
// Target-derived text renders as a TEXT NODE
|
|
334
|
+
// Target-derived text renders as a TEXT NODE.
|
|
335
335
|
/* @__PURE__ */ jsx("div", { "data-orbit-context-menu-heading": "", role: "presentation", style: HEADING_STYLE, children: heading })
|
|
336
336
|
) : null,
|
|
337
337
|
items.map((item, index) => /* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/ContextMenu/index.tsx"],"names":[],"mappings":"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","file":"ContextMenu.js","sourcesContent":["/**\n * <GraphContextMenu> — §16.4 right-click / long-press menu (S7-T08).\n *\n * Listens to the instance's typed 'contextMenu' event (the ONLY channel — the\n * instance has no openContextMenu method) and renders a portal-free,\n * absolutely-positioned menu at the event's container-relative screen\n * coordinates, so it must be composed inside the <Graph> container (or any\n * positioned ancestor sharing the canvas coordinate space).\n *\n * Fully keyboard reachable: the menu opens focused on its first item,\n * ArrowUp/ArrowDown rove (wrapping), Enter/Space activates, Escape closes and\n * returns focus to the previously focused element; it also closes on outside\n * pointerdown and window blur. All target-derived text (the node label\n * heading) renders as TEXT NODES — never markup.\n *\n * §16.2 path pair (S12-T08): with `onFindPath` provided, node menus carry\n * 'Find path from here' / 'Find path to here'. The pending 'from' anchor is\n * stashed locally in the component (session-local); completing the pair\n * fires `onFindPath(anchorId, targetId)` and clears the anchor.\n */\n\nimport { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, KeyboardEvent as ReactKeyboardEvent, ReactElement } from 'react';\nimport type { GraphNode, GraphStoreState, NodeId, SubgraphSpec } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\n\n/** The typed 'contextMenu' event target (generics erased like AnyGraphInstance). */\nexport type GraphContextMenuTarget =\n | { kind: 'node'; node: GraphNode<any> }\n | { kind: 'background' };\n\nexport interface GraphContextMenuItem {\n /** Stable key; default items use 'focus' | 'select-neighbors' | 'expand' |\n * 'isolate' | 'fold' | 'unfold' | 'pin' | 'unpin' | 'hide' | 'copy-id' |\n * 'find-path-from' | 'find-path-to' | 'select-all' | 'clear-selection' |\n * 'fit-view' | 'reset-isolation'. */\n id: string;\n /** Rendered as a text node. */\n label: string;\n disabled?: boolean;\n onSelect(): void;\n}\n\n/** Pinned-interface alias (§16.4 render-prop item type). */\nexport type MenuItem = GraphContextMenuItem;\n\nexport interface GraphContextMenuProps {\n /** Explicit instance; defaults to the ambient <Graph>/<GraphProvider> one. */\n instance?: AnyGraphInstance;\n /** Class hook for the menu root; providing it drops the default chrome\n * (positioning is always applied). */\n className?: string;\n style?: CSSProperties;\n /** Class hook for each item; providing it drops the default item styles. */\n itemClassName?: string;\n /** Render-prop REPLACEMENT for the item list; receives the default items\n * so extension is `[...defaultItems, mine]`. */\n renderItems?: (ctx: {\n target: GraphContextMenuTarget;\n defaultItems: readonly GraphContextMenuItem[];\n }) => readonly GraphContextMenuItem[];\n /**\n * §16.2 path pair (S12-T08): providing this adds 'Find path from here' /\n * 'Find path to here' to node menus. 'From here' stashes the anchor node\n * id locally (component state — session-local, never serialized); 'to\n * here' appears once an anchor exists on a DIFFERENT node, fires\n * `(anchorId, targetId)`, and clears the anchor. The component never\n * resolves paths itself — wire this to the ref API's findPath.\n */\n onFindPath?: (sourceId: NodeId, targetId: NodeId) => void;\n}\n\ninterface OpenState {\n target: GraphContextMenuTarget;\n /** Container-relative CSS px (event contract §15). */\n screen: readonly [number, number];\n}\n\n// --- default dark-theme styling (dropped when class hooks are provided) ---\n\nconst MENU_CHROME: CSSProperties = {\n minWidth: 160,\n display: 'flex',\n flexDirection: 'column',\n padding: 4,\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.96)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n boxShadow: '0 8px 24px rgba(0, 0, 0, 0.5)',\n color: '#e8eaf0',\n font: '13px/1.4 system-ui, sans-serif',\n zIndex: 10,\n};\n\nconst HEADING_STYLE: CSSProperties = {\n padding: '4px 10px',\n fontSize: 11,\n opacity: 0.65,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n maxWidth: 240,\n};\n\nconst ITEM_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: '6px 10px',\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\nfunction legacyCopy(text: string): void {\n if (typeof document === 'undefined') return;\n const ta = document.createElement('textarea');\n ta.value = text;\n ta.setAttribute('readonly', '');\n ta.style.position = 'fixed';\n ta.style.left = '-9999px';\n document.body.appendChild(ta);\n ta.select();\n try {\n document.execCommand('copy');\n } catch {\n /* copy unavailable in this environment */\n }\n ta.remove();\n}\n\n/** navigator.clipboard.writeText with the execCommand('copy') fallback. */\nfunction copyText(text: string): void {\n const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clipboard !== undefined && typeof clipboard.writeText === 'function') {\n void clipboard.writeText(text).catch(() => {\n legacyCopy(text);\n });\n return;\n }\n legacyCopy(text);\n}\n\n/** §16.2 path-pair state threaded into the node items when the host\n * provided `onFindPath` (null otherwise — the items don't render). */\ninterface PathPairContext {\n onFindPath: (sourceId: NodeId, targetId: NodeId) => void;\n anchorId: NodeId | null;\n setAnchor: (id: NodeId | null) => void;\n}\n\n/** Default node-target items. Pin/Unpin toggles on the store's pin slice at\n * open/render time; Expand shows a busy state while the node's expansion is\n * in flight (§9.2 pendingExpansions); Isolate hard-scopes to the node's\n * 1-hop ego network (§9.2); Collapse/Expand folds the node's neighbourhood\n * behind it (§16.3 — containment, distinct from Expand neighbors, which\n * fetches). */\nfunction defaultNodeItems(\n instance: AnyGraphInstance,\n node: GraphNode<any>,\n pendingExpansions: ReadonlySet<NodeId>,\n pathPair: PathPairContext | null,\n): GraphContextMenuItem[] {\n const pinned = instance.store.getState().pins.has(node.id);\n const expanding = pendingExpansions.has(node.id);\n const folded = instance.getFold(node.id) !== null;\n const items: GraphContextMenuItem[] = [\n {\n id: 'focus',\n label: 'Focus',\n onSelect: () => {\n instance.focusNode(node.id);\n },\n },\n {\n id: 'select-neighbors',\n label: 'Select neighbors',\n onSelect: () => {\n instance.selectNeighbors(node.id);\n },\n },\n {\n id: 'expand',\n label: expanding ? 'Expanding…' : 'Expand neighbors',\n disabled: expanding,\n onSelect: () => {\n // A discard/collapse rejects the promise by design (§9.2); the menu\n // fires-and-forgets, so swallow it here — diagnostics carry the why.\n void instance.expandNode(node.id).catch(() => {});\n },\n },\n {\n id: 'isolate',\n label: 'Isolate',\n onSelect: () => {\n instance.applyHostUpdate({ subgraph: { seedIds: [node.id], hops: 1 } });\n },\n },\n folded\n ? {\n id: 'unfold',\n label: 'Expand neighborhood',\n onSelect: () => {\n instance.unfoldNode(node.id);\n },\n }\n : {\n id: 'fold',\n label: 'Collapse neighborhood',\n onSelect: () => {\n instance.foldNode(node.id);\n },\n },\n pinned\n ? {\n id: 'unpin',\n label: 'Unpin',\n onSelect: () => {\n instance.unpinNode(node.id);\n },\n }\n : {\n id: 'pin',\n label: 'Pin',\n onSelect: () => {\n instance.pinNode(node.id);\n },\n },\n {\n id: 'hide',\n label: 'Hide',\n onSelect: () => {\n instance.hideNodes([node.id]);\n },\n },\n {\n id: 'copy-id',\n label: 'Copy id',\n onSelect: () => {\n copyText(String(node.id));\n },\n },\n ];\n if (pathPair !== null) {\n items.push({\n id: 'find-path-from',\n label: 'Find path from here',\n onSelect: () => {\n // Re-selecting on another node re-anchors (last 'from' wins).\n pathPair.setAnchor(node.id);\n },\n });\n if (pathPair.anchorId !== null && pathPair.anchorId !== node.id) {\n const anchorId = pathPair.anchorId;\n items.push({\n id: 'find-path-to',\n label: 'Find path to here',\n onSelect: () => {\n // A completed pair consumes the anchor (one findPath per pair).\n pathPair.setAnchor(null);\n pathPair.onFindPath(anchorId, node.id);\n },\n });\n }\n }\n return items;\n}\n\nfunction defaultBackgroundItems(\n instance: AnyGraphInstance,\n scope: SubgraphSpec | null,\n): GraphContextMenuItem[] {\n const items: GraphContextMenuItem[] = [\n {\n id: 'select-all',\n label: 'Select all',\n onSelect: () => {\n instance.selectAll();\n },\n },\n {\n id: 'clear-selection',\n label: 'Clear selection',\n onSelect: () => {\n instance.clearSelection();\n },\n },\n {\n id: 'fit-view',\n label: 'Fit view',\n onSelect: () => {\n instance.fitView();\n },\n },\n ];\n if (scope !== null) {\n items.push({\n id: 'reset-isolation',\n label: 'Reset isolation',\n onSelect: () => {\n instance.resetIsolation();\n },\n });\n }\n return items;\n}\n\nconst selectScope = (s: GraphStoreState): SubgraphSpec | null => s.scope;\nconst selectPendingExpansions = (s: GraphStoreState): ReadonlySet<NodeId> => s.pendingExpansions;\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 prop). */\nfunction useInstanceStore<T>(instance: AnyGraphInstance, selector: (s: GraphStoreState) => T): T {\n const subscribe = useCallback(\n (onChange: () => void) => instance.store.subscribe(onChange),\n [instance],\n );\n const selectorRef = useRef(selector);\n selectorRef.current = selector;\n const getSnapshot = useCallback(() => selectorRef.current(instance.store.getState()), [instance]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport function GraphContextMenu(props: GraphContextMenuProps): ReactElement | null {\n const instance = useResolvedInstance(props.instance, '<GraphContextMenu>');\n // §9.2 state the default items reflect live while the menu is open: the\n // expand item's busy flag and the background reset-isolation gate.\n const scope = useInstanceStore(instance, selectScope);\n const pendingExpansions = useInstanceStore(instance, selectPendingExpansions);\n\n const [open, setOpen] = useState<OpenState | null>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const rootRef = useRef<HTMLDivElement | null>(null);\n const itemRefs = useRef<(HTMLElement | null)[]>([]);\n /** Element focused when the menu opened; Escape/activation restores it. */\n const restoreFocusRef = useRef<HTMLElement | null>(null);\n /** §16.2 pending 'Find path from here' anchor. A ref, not state: it only\n * changes via item activation, which closes the menu — the next open\n * re-renders and reads it fresh. Session-local, never serialized. */\n const pathAnchorRef = useRef<NodeId | null>(null);\n\n const close = useCallback((restoreFocus: boolean): void => {\n setOpen(null);\n const el = restoreFocusRef.current;\n restoreFocusRef.current = null;\n if (restoreFocus && el !== null && el.isConnected) el.focus();\n }, []);\n\n // The typed 'contextMenu' event is the only open channel.\n useEffect(() => {\n const off = instance.on('contextMenu', (payload) => {\n restoreFocusRef.current =\n typeof document !== 'undefined' && document.activeElement instanceof HTMLElement\n ? document.activeElement\n : null;\n setOpen({ target: payload.target, screen: payload.screen });\n setActiveIndex(0);\n });\n return off;\n }, [instance]);\n\n // Outside pointerdown (capture, so it runs before anything swallows it) and\n // window blur both dismiss without stealing focus back.\n useEffect(() => {\n if (open === null) return undefined;\n const onDocPointerDown = (e: Event): void => {\n const root = rootRef.current;\n if (root !== null && e.target instanceof Node && root.contains(e.target)) return;\n close(false);\n };\n const onWindowBlur = (): void => {\n close(false);\n };\n document.addEventListener('pointerdown', onDocPointerDown, true);\n window.addEventListener('blur', onWindowBlur);\n return () => {\n document.removeEventListener('pointerdown', onDocPointerDown, true);\n window.removeEventListener('blur', onWindowBlur);\n };\n }, [open, close]);\n\n // The menu opens focused; roving focus follows activeIndex.\n useEffect(() => {\n if (open === null) return;\n const el = itemRefs.current[activeIndex] ?? itemRefs.current[0] ?? rootRef.current;\n el?.focus();\n }, [open, activeIndex]);\n\n if (open === null) return null;\n\n const onFindPath = props.onFindPath;\n const defaultItems =\n open.target.kind === 'node'\n ? defaultNodeItems(\n instance,\n open.target.node,\n pendingExpansions,\n onFindPath !== undefined\n ? {\n onFindPath,\n anchorId: pathAnchorRef.current,\n setAnchor: (id) => {\n pathAnchorRef.current = id;\n },\n }\n : null,\n )\n : defaultBackgroundItems(instance, scope);\n const items =\n props.renderItems !== undefined\n ? props.renderItems({ target: open.target, defaultItems })\n : defaultItems;\n const heading = open.target.kind === 'node' ? nodeLabelText(open.target.node) : null;\n\n const activate = (item: GraphContextMenuItem): void => {\n close(true);\n item.onSelect();\n };\n\n const onKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>): void => {\n const count = items.length;\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n setActiveIndex((i) => (count === 0 ? 0 : (i + 1) % count));\n break;\n case 'ArrowUp':\n e.preventDefault();\n setActiveIndex((i) => (count === 0 ? 0 : (i - 1 + count) % count));\n break;\n case 'Home':\n e.preventDefault();\n setActiveIndex(0);\n break;\n case 'End':\n e.preventDefault();\n setActiveIndex(count === 0 ? 0 : count - 1);\n break;\n case 'Enter':\n case ' ': {\n e.preventDefault();\n const item = items[activeIndex];\n if (item !== undefined && item.disabled !== true) activate(item);\n break;\n }\n case 'Escape':\n e.preventDefault();\n close(true);\n break;\n case 'Tab':\n e.preventDefault(); // focus stays inside the menu while open\n break;\n default:\n break;\n }\n };\n\n const positionStyle: CSSProperties = {\n position: 'absolute',\n left: open.screen[0],\n top: open.screen[1],\n };\n\n return (\n <div\n ref={rootRef}\n data-orbit-context-menu=\"\"\n role=\"menu\"\n aria-label={heading ?? 'Graph'}\n tabIndex={-1}\n className={props.className}\n style={\n props.className !== undefined\n ? { ...positionStyle, ...props.style }\n : { ...MENU_CHROME, ...positionStyle, ...props.style }\n }\n onKeyDown={onKeyDown}\n >\n {heading !== null ? (\n // Target-derived text renders as a TEXT NODE (§14/§15 injection ban).\n <div data-orbit-context-menu-heading=\"\" role=\"presentation\" style={HEADING_STYLE}>\n {heading}\n </div>\n ) : null}\n {items.map((item, index) => (\n <button\n key={item.id}\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n data-orbit-context-menu-item={item.id}\n tabIndex={index === activeIndex ? 0 : -1}\n disabled={item.disabled === true}\n className={props.itemClassName}\n style={props.itemClassName !== undefined ? undefined : ITEM_STYLE}\n onClick={() => {\n if (item.disabled !== true) activate(item);\n }}\n onPointerEnter={() => {\n setActiveIndex(index);\n }}\n >\n {item.label}\n </button>\n ))}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/ContextMenu/index.tsx"],"names":[],"mappings":"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","file":"ContextMenu.js","sourcesContent":["/**\n * <GraphContextMenu> — right-click / long-press menu.\n *\n * Listens to the instance's typed 'contextMenu' event (the ONLY channel — the\n * instance has no openContextMenu method) and renders a portal-free,\n * absolutely-positioned menu at the event's container-relative screen\n * coordinates, so it must be composed inside the <Graph> container (or any\n * positioned ancestor sharing the canvas coordinate space).\n *\n * Fully keyboard reachable: the menu opens focused on its first item,\n * ArrowUp/ArrowDown rove (wrapping), Enter/Space activates, Escape closes and\n * returns focus to the previously focused element; it also closes on outside\n * pointerdown and window blur. All target-derived text (the node label\n * heading) renders as TEXT NODES — never markup.\n *\n * path pair: with `onFindPath` provided, node menus carry\n * 'Find path from here' / 'Find path to here'. The pending 'from' anchor is\n * stashed locally in the component (session-local); completing the pair\n * fires `onFindPath(anchorId, targetId)` and clears the anchor.\n */\n\nimport { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, KeyboardEvent as ReactKeyboardEvent, ReactElement } from 'react';\nimport type { GraphNode, GraphStoreState, NodeId, SubgraphSpec } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\n\n/** The typed 'contextMenu' event target (generics erased like AnyGraphInstance). */\nexport type GraphContextMenuTarget =\n | { kind: 'node'; node: GraphNode<any> }\n | { kind: 'background' };\n\nexport interface GraphContextMenuItem {\n /** Stable key; default items use 'focus' | 'select-neighbors' | 'expand' |\n * 'isolate' | 'fold' | 'unfold' | 'pin' | 'unpin' | 'hide' | 'copy-id' |\n * 'find-path-from' | 'find-path-to' | 'select-all' | 'clear-selection' |\n * 'fit-view' | 'reset-isolation'. */\n id: string;\n /** Rendered as a text node. */\n label: string;\n disabled?: boolean;\n onSelect(): void;\n}\n\n/** Pinned-interface alias. */\nexport type MenuItem = GraphContextMenuItem;\n\nexport interface GraphContextMenuProps {\n /** Explicit instance; defaults to the ambient <Graph>/<GraphProvider> one. */\n instance?: AnyGraphInstance;\n /** Class hook for the menu root; providing it drops the default chrome\n * (positioning is always applied). */\n className?: string;\n style?: CSSProperties;\n /** Class hook for each item; providing it drops the default item styles. */\n itemClassName?: string;\n /** Render-prop REPLACEMENT for the item list; receives the default items\n * so extension is `[...defaultItems, mine]`. */\n renderItems?: (ctx: {\n target: GraphContextMenuTarget;\n defaultItems: readonly GraphContextMenuItem[];\n }) => readonly GraphContextMenuItem[];\n /**\n * path pair: providing this adds 'Find path from here' /\n * 'Find path to here' to node menus. 'From here' stashes the anchor node\n * id locally (component state — session-local, never serialized); 'to\n * here' appears once an anchor exists on a DIFFERENT node, fires\n * `(anchorId, targetId)`, and clears the anchor. The component never\n * resolves paths itself — wire this to the ref API's findPath.\n */\n onFindPath?: (sourceId: NodeId, targetId: NodeId) => void;\n}\n\ninterface OpenState {\n target: GraphContextMenuTarget;\n /** Container-relative CSS pixels from the context-menu event. */\n screen: readonly [number, number];\n}\n\n// --- default dark-theme styling (dropped when class hooks are provided) ---\n\nconst MENU_CHROME: CSSProperties = {\n minWidth: 160,\n display: 'flex',\n flexDirection: 'column',\n padding: 4,\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.96)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n boxShadow: '0 8px 24px rgba(0, 0, 0, 0.5)',\n color: '#e8eaf0',\n font: '13px/1.4 system-ui, sans-serif',\n zIndex: 10,\n};\n\nconst HEADING_STYLE: CSSProperties = {\n padding: '4px 10px',\n fontSize: 11,\n opacity: 0.65,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n maxWidth: 240,\n};\n\nconst ITEM_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: '6px 10px',\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\nfunction legacyCopy(text: string): void {\n if (typeof document === 'undefined') return;\n const ta = document.createElement('textarea');\n ta.value = text;\n ta.setAttribute('readonly', '');\n ta.style.position = 'fixed';\n ta.style.left = '-9999px';\n document.body.appendChild(ta);\n ta.select();\n try {\n document.execCommand('copy');\n } catch {\n /* copy unavailable in this environment */\n }\n ta.remove();\n}\n\n/** navigator.clipboard.writeText with the execCommand('copy') fallback. */\nfunction copyText(text: string): void {\n const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clipboard !== undefined && typeof clipboard.writeText === 'function') {\n void clipboard.writeText(text).catch(() => {\n legacyCopy(text);\n });\n return;\n }\n legacyCopy(text);\n}\n\n/** path-pair state threaded into the node items when the host\n * provided `onFindPath` (null otherwise — the items don't render). */\ninterface PathPairContext {\n onFindPath: (sourceId: NodeId, targetId: NodeId) => void;\n anchorId: NodeId | null;\n setAnchor: (id: NodeId | null) => void;\n}\n\n/** Default node-target items. Pin/Unpin toggles on the store's pin slice at\n * open/render time; Expand shows a busy state while the node's expansion is\n * in flight; Isolate hard-scopes to the node's\n * 1-hop ego network; Collapse/Expand folds the node's neighbourhood\n * behind it (containment, distinct from Expand neighbors, which\n * fetches). */\nfunction defaultNodeItems(\n instance: AnyGraphInstance,\n node: GraphNode<any>,\n pendingExpansions: ReadonlySet<NodeId>,\n pathPair: PathPairContext | null,\n): GraphContextMenuItem[] {\n const pinned = instance.store.getState().pins.has(node.id);\n const expanding = pendingExpansions.has(node.id);\n const folded = instance.getFold(node.id) !== null;\n const items: GraphContextMenuItem[] = [\n {\n id: 'focus',\n label: 'Focus',\n onSelect: () => {\n instance.focusNode(node.id);\n },\n },\n {\n id: 'select-neighbors',\n label: 'Select neighbors',\n onSelect: () => {\n instance.selectNeighbors(node.id);\n },\n },\n {\n id: 'expand',\n label: expanding ? 'Expanding…' : 'Expand neighbors',\n disabled: expanding,\n onSelect: () => {\n // A discard/collapse rejects the promise by design; the menu\n // fires-and-forgets, so swallow it here — diagnostics carry the why.\n void instance.expandNode(node.id).catch(() => {});\n },\n },\n {\n id: 'isolate',\n label: 'Isolate',\n onSelect: () => {\n instance.applyHostUpdate({ subgraph: { seedIds: [node.id], hops: 1 } });\n },\n },\n folded\n ? {\n id: 'unfold',\n label: 'Expand neighborhood',\n onSelect: () => {\n instance.unfoldNode(node.id);\n },\n }\n : {\n id: 'fold',\n label: 'Collapse neighborhood',\n onSelect: () => {\n instance.foldNode(node.id);\n },\n },\n pinned\n ? {\n id: 'unpin',\n label: 'Unpin',\n onSelect: () => {\n instance.unpinNode(node.id);\n },\n }\n : {\n id: 'pin',\n label: 'Pin',\n onSelect: () => {\n instance.pinNode(node.id);\n },\n },\n {\n id: 'hide',\n label: 'Hide',\n onSelect: () => {\n instance.hideNodes([node.id]);\n },\n },\n {\n id: 'copy-id',\n label: 'Copy id',\n onSelect: () => {\n copyText(String(node.id));\n },\n },\n ];\n if (pathPair !== null) {\n items.push({\n id: 'find-path-from',\n label: 'Find path from here',\n onSelect: () => {\n // Re-selecting on another node re-anchors (last 'from' wins).\n pathPair.setAnchor(node.id);\n },\n });\n if (pathPair.anchorId !== null && pathPair.anchorId !== node.id) {\n const anchorId = pathPair.anchorId;\n items.push({\n id: 'find-path-to',\n label: 'Find path to here',\n onSelect: () => {\n // A completed pair consumes the anchor (one findPath per pair).\n pathPair.setAnchor(null);\n pathPair.onFindPath(anchorId, node.id);\n },\n });\n }\n }\n return items;\n}\n\nfunction defaultBackgroundItems(\n instance: AnyGraphInstance,\n scope: SubgraphSpec | null,\n): GraphContextMenuItem[] {\n const items: GraphContextMenuItem[] = [\n {\n id: 'select-all',\n label: 'Select all',\n onSelect: () => {\n instance.selectAll();\n },\n },\n {\n id: 'clear-selection',\n label: 'Clear selection',\n onSelect: () => {\n instance.clearSelection();\n },\n },\n {\n id: 'fit-view',\n label: 'Fit view',\n onSelect: () => {\n instance.fitView();\n },\n },\n ];\n if (scope !== null) {\n items.push({\n id: 'reset-isolation',\n label: 'Reset isolation',\n onSelect: () => {\n instance.resetIsolation();\n },\n });\n }\n return items;\n}\n\nconst selectScope = (s: GraphStoreState): SubgraphSpec | null => s.scope;\nconst selectPendingExpansions = (s: GraphStoreState): ReadonlySet<NodeId> => s.pendingExpansions;\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 prop). */\nfunction useInstanceStore<T>(instance: AnyGraphInstance, selector: (s: GraphStoreState) => T): T {\n const subscribe = useCallback(\n (onChange: () => void) => instance.store.subscribe(onChange),\n [instance],\n );\n const selectorRef = useRef(selector);\n selectorRef.current = selector;\n const getSnapshot = useCallback(() => selectorRef.current(instance.store.getState()), [instance]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport function GraphContextMenu(props: GraphContextMenuProps): ReactElement | null {\n const instance = useResolvedInstance(props.instance, '<GraphContextMenu>');\n // state the default items reflect live while the menu is open: the\n // expand item's busy flag and the background reset-isolation gate.\n const scope = useInstanceStore(instance, selectScope);\n const pendingExpansions = useInstanceStore(instance, selectPendingExpansions);\n\n const [open, setOpen] = useState<OpenState | null>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const rootRef = useRef<HTMLDivElement | null>(null);\n const itemRefs = useRef<(HTMLElement | null)[]>([]);\n /** Element focused when the menu opened; Escape/activation restores it. */\n const restoreFocusRef = useRef<HTMLElement | null>(null);\n /** pending 'Find path from here' anchor. A ref, not state: it only\n * changes via item activation, which closes the menu — the next open\n * re-renders and reads it fresh. Session-local, never serialized. */\n const pathAnchorRef = useRef<NodeId | null>(null);\n\n const close = useCallback((restoreFocus: boolean): void => {\n setOpen(null);\n const el = restoreFocusRef.current;\n restoreFocusRef.current = null;\n if (restoreFocus && el !== null && el.isConnected) el.focus();\n }, []);\n\n // The typed 'contextMenu' event is the only open channel.\n useEffect(() => {\n const off = instance.on('contextMenu', (payload) => {\n restoreFocusRef.current =\n typeof document !== 'undefined' && document.activeElement instanceof HTMLElement\n ? document.activeElement\n : null;\n setOpen({ target: payload.target, screen: payload.screen });\n setActiveIndex(0);\n });\n return off;\n }, [instance]);\n\n // Outside pointerdown (capture, so it runs before anything swallows it) and\n // window blur both dismiss without stealing focus back.\n useEffect(() => {\n if (open === null) return undefined;\n const onDocPointerDown = (e: Event): void => {\n const root = rootRef.current;\n if (root !== null && e.target instanceof Node && root.contains(e.target)) return;\n close(false);\n };\n const onWindowBlur = (): void => {\n close(false);\n };\n document.addEventListener('pointerdown', onDocPointerDown, true);\n window.addEventListener('blur', onWindowBlur);\n return () => {\n document.removeEventListener('pointerdown', onDocPointerDown, true);\n window.removeEventListener('blur', onWindowBlur);\n };\n }, [open, close]);\n\n // The menu opens focused; roving focus follows activeIndex.\n useEffect(() => {\n if (open === null) return;\n const el = itemRefs.current[activeIndex] ?? itemRefs.current[0] ?? rootRef.current;\n el?.focus();\n }, [open, activeIndex]);\n\n if (open === null) return null;\n\n const onFindPath = props.onFindPath;\n const defaultItems =\n open.target.kind === 'node'\n ? defaultNodeItems(\n instance,\n open.target.node,\n pendingExpansions,\n onFindPath !== undefined\n ? {\n onFindPath,\n anchorId: pathAnchorRef.current,\n setAnchor: (id) => {\n pathAnchorRef.current = id;\n },\n }\n : null,\n )\n : defaultBackgroundItems(instance, scope);\n const items =\n props.renderItems !== undefined\n ? props.renderItems({ target: open.target, defaultItems })\n : defaultItems;\n const heading = open.target.kind === 'node' ? nodeLabelText(open.target.node) : null;\n\n const activate = (item: GraphContextMenuItem): void => {\n close(true);\n item.onSelect();\n };\n\n const onKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>): void => {\n const count = items.length;\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n setActiveIndex((i) => (count === 0 ? 0 : (i + 1) % count));\n break;\n case 'ArrowUp':\n e.preventDefault();\n setActiveIndex((i) => (count === 0 ? 0 : (i - 1 + count) % count));\n break;\n case 'Home':\n e.preventDefault();\n setActiveIndex(0);\n break;\n case 'End':\n e.preventDefault();\n setActiveIndex(count === 0 ? 0 : count - 1);\n break;\n case 'Enter':\n case ' ': {\n e.preventDefault();\n const item = items[activeIndex];\n if (item !== undefined && item.disabled !== true) activate(item);\n break;\n }\n case 'Escape':\n e.preventDefault();\n close(true);\n break;\n case 'Tab':\n e.preventDefault(); // focus stays inside the menu while open\n break;\n default:\n break;\n }\n };\n\n const positionStyle: CSSProperties = {\n position: 'absolute',\n left: open.screen[0],\n top: open.screen[1],\n };\n\n return (\n <div\n ref={rootRef}\n data-orbit-context-menu=\"\"\n role=\"menu\"\n aria-label={heading ?? 'Graph'}\n tabIndex={-1}\n className={props.className}\n style={\n props.className !== undefined\n ? { ...positionStyle, ...props.style }\n : { ...MENU_CHROME, ...positionStyle, ...props.style }\n }\n onKeyDown={onKeyDown}\n >\n {heading !== null ? (\n // Target-derived text renders as a TEXT NODE.\n <div data-orbit-context-menu-heading=\"\" role=\"presentation\" style={HEADING_STYLE}>\n {heading}\n </div>\n ) : null}\n {items.map((item, index) => (\n <button\n key={item.id}\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n data-orbit-context-menu-item={item.id}\n tabIndex={index === activeIndex ? 0 : -1}\n disabled={item.disabled === true}\n className={props.itemClassName}\n style={props.itemClassName !== undefined ? undefined : ITEM_STYLE}\n onClick={() => {\n if (item.disabled !== true) activate(item);\n }}\n onPointerEnter={() => {\n setActiveIndex(index);\n }}\n >\n {item.label}\n </button>\n ))}\n </div>\n );\n}\n"]}
|
|
@@ -3,7 +3,7 @@ import { A as AnyGraphInstance } from '../GraphProvider-B8ITDXS0.js';
|
|
|
3
3
|
import '@modernrelay/orbit-core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* <GraphHistogram> —
|
|
6
|
+
* <GraphHistogram> — crossfilter histogram.
|
|
7
7
|
*
|
|
8
8
|
* SVG dual-layer bar chart over one crossfilter dimension: total bars
|
|
9
9
|
* (muted) with the joint-filtered layer (accent) overlaid. Numeric/temporal
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useRangeBrush, brushRangePct, sliderValueText, sliderValueNow, toggleCategoryExclusion } from '../chunk-
|
|
2
|
-
import { useCrossfilterDimension, useSessionSetBrush } from '../chunk-
|
|
3
|
-
import { useResolvedInstance } from '../chunk-
|
|
1
|
+
import { useRangeBrush, brushRangePct, sliderValueText, sliderValueNow, toggleCategoryExclusion } from '../chunk-DTXFQLNP.js';
|
|
2
|
+
import { useCrossfilterDimension, useSessionSetBrush } from '../chunk-S36LHSNX.js';
|
|
3
|
+
import { useResolvedInstance } from '../chunk-NPOV5GA3.js';
|
|
4
4
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
var ROOT_STYLE = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Histogram/index.tsx"],"names":[],"mappings":"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","file":"Histogram.js","sourcesContent":["/**\n * <GraphHistogram> — §16.6 crossfilter histogram (S9).\n *\n * SVG dual-layer bar chart over one crossfilter dimension: total bars\n * (muted) with the joint-filtered layer (accent) overlaid. Numeric/temporal\n * dimensions brush by pointer-drag over the plot (any drag REPLACES the\n * brush), double-click clears, ArrowLeft/ArrowRight nudge the window by one\n * bin, Escape clears (the plot is focusable). Categorical dimensions render\n * clickable rows toggling category exclusions. All text values (dimension\n * key, category keys) render as TEXT NODES. Headless-styleable: providing\n * `className` drops the built-in dark-theme inline styles.\n */\n\nimport type { CSSProperties, ReactElement } from 'react';\nimport type { CategoryBin } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\nimport { brushRangePct, sliderValueNow, sliderValueText, toggleCategoryExclusion, useRangeBrush } from '../_shared/brush';\nimport { useCrossfilterDimension, useSessionSetBrush } from '../_shared/crossfilter';\n\nexport interface GraphHistogramProps {\n /** Crossfilter dimension key (must appear in the `crossfilter` prop). */\n dimension: string;\n /** Plot height in CSS px. Default 96. */\n height?: number;\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 /** Class hook for each categorical row; providing it drops its defaults. */\n categoryClassName?: string;\n}\n\n// --- default dark-theme styling (dropped when `className` is provided) ---\n\nconst ROOT_STYLE: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n padding: 8,\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.92)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n color: '#e8eaf0',\n font: '12px/1.4 system-ui, sans-serif',\n};\n\nconst TITLE_STYLE: CSSProperties = { opacity: 0.75 };\n\nconst PLOT_STYLE: CSSProperties = {\n position: 'relative',\n cursor: 'crosshair',\n touchAction: 'none',\n userSelect: 'none',\n};\n\nconst SVG_STYLE: CSSProperties = { display: 'block', width: '100%' };\n\nconst CATEGORY_ROW_STYLE: CSSProperties = {\n appearance: 'none',\n display: 'flex',\n justifyContent: 'space-between',\n gap: 8,\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 TOTAL_FILL = 'rgba(148, 163, 184, 0.35)';\nconst FILTERED_FILL = '#7aa2ff';\nconst BRUSH_FILL = 'rgba(122, 162, 255, 0.18)';\nconst BRUSH_STROKE = 'rgba(122, 162, 255, 0.85)';\n\nexport function GraphHistogram(props: GraphHistogramProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphHistogram>');\n const { summary, brush } = useCrossfilterDimension(instance, props.dimension);\n const setBrush = useSessionSetBrush(instance, props.dimension);\n\n const domain = summary?.domain ?? null;\n const handlers = useRangeBrush({\n domain,\n binCount: summary?.bins.length ?? 0,\n brush,\n setBrush,\n });\n\n const styled = props.className === undefined;\n const height = props.height ?? 96;\n const categorical = summary !== null && summary.kind === 'categorical';\n const range = brushRangePct(brush, domain);\n\n const renderCategory = (cat: CategoryBin, maxTotal: number): ReactElement => {\n const pct = maxTotal > 0 ? Math.round((cat.filtered / maxTotal) * 100) : 0;\n return (\n <button\n key={cat.key}\n type=\"button\"\n data-orbit-histogram-category={cat.key}\n aria-pressed={cat.excluded}\n aria-label={`Toggle category ${cat.key}`}\n className={props.categoryClassName}\n style={\n props.categoryClassName !== undefined\n ? undefined\n : cat.excluded\n ? EXCLUDED_ROW_STYLE\n : CATEGORY_ROW_STYLE\n }\n onClick={() => {\n setBrush(toggleCategoryExclusion(brush, cat.key));\n }}\n >\n <span data-orbit-histogram-category-label=\"\">{cat.key}</span>\n <span\n data-orbit-histogram-category-count=\"\"\n aria-hidden=\"true\"\n style={\n props.categoryClassName !== undefined\n ? undefined\n : {\n background: `linear-gradient(to right, ${FILTERED_FILL}33 ${pct}%, transparent ${pct}%)`,\n borderRadius: 2,\n padding: '0 4px',\n }\n }\n >\n {cat.filtered}/{cat.total}\n </span>\n </button>\n );\n };\n\n let body: ReactElement;\n if (summary === null) {\n body = (\n <div data-orbit-histogram-empty=\"\" style={styled ? TITLE_STYLE : undefined}>\n No data\n </div>\n );\n } else if (categorical) {\n const maxTotal = summary.categories.reduce((m, c) => (c.total > m ? c.total : m), 0);\n body = (\n <div\n data-orbit-histogram-categories=\"\"\n style={styled ? { display: 'flex', flexDirection: 'column' } : undefined}\n >\n {summary.categories.map((cat) => renderCategory(cat, maxTotal))}\n </div>\n );\n } else {\n const bins = summary.bins;\n const maxTotal = bins.reduce((m, b) => (b.total > m ? b.total : m), 0);\n const barW = bins.length > 0 ? 100 / bins.length : 100;\n body = (\n <div\n data-orbit-histogram-plot=\"\"\n tabIndex={0}\n aria-label={`${props.dimension} brush: drag to filter, arrow keys nudge, Escape clears`}\n role=\"slider\"\n aria-orientation=\"horizontal\"\n aria-valuemin={summary.domain?.min ?? 0}\n aria-valuemax={summary.domain?.max ?? 0}\n aria-valuenow={sliderValueNow(brush, summary.domain)}\n aria-valuetext={sliderValueText(brush, summary.domain)}\n style={styled ? PLOT_STYLE : undefined}\n onPointerDown={handlers.onPointerDown}\n onPointerMove={handlers.onPointerMove}\n onPointerUp={handlers.onPointerUp}\n onPointerCancel={handlers.onPointerCancel}\n onDoubleClick={handlers.onDoubleClick}\n onKeyDown={handlers.onKeyDown}\n >\n <svg\n width=\"100%\"\n height={height}\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n focusable=\"false\"\n style={styled ? SVG_STYLE : undefined}\n >\n {bins.map((bin, i) => {\n const x = i * barW + barW * 0.05;\n const w = barW * 0.9;\n const totalH = maxTotal > 0 ? (bin.total / maxTotal) * 100 : 0;\n const filteredH = maxTotal > 0 ? (bin.filtered / maxTotal) * 100 : 0;\n return (\n <g key={i}>\n <rect\n data-orbit-histogram-bar-total=\"\"\n x={x}\n y={100 - totalH}\n width={w}\n height={totalH}\n fill={TOTAL_FILL}\n />\n <rect\n data-orbit-histogram-bar-filtered=\"\"\n x={x}\n y={100 - filteredH}\n width={w}\n height={filteredH}\n fill={FILTERED_FILL}\n />\n </g>\n );\n })}\n {range !== null ? (\n <rect\n data-orbit-histogram-brush=\"\"\n x={range.leftPct}\n y={0}\n width={range.widthPct}\n height={100}\n fill={BRUSH_FILL}\n stroke={BRUSH_STROKE}\n strokeWidth={0.5}\n pointerEvents=\"none\"\n />\n ) : null}\n </svg>\n </div>\n );\n }\n\n return (\n <div\n data-orbit-histogram={props.dimension}\n role=\"group\"\n aria-label={`${props.dimension} histogram`}\n className={props.className}\n style={styled ? { ...ROOT_STYLE, ...props.style } : props.style}\n >\n <div data-orbit-histogram-title=\"\" style={styled ? TITLE_STYLE : undefined}>\n {props.dimension}\n </div>\n {body}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Histogram/index.tsx"],"names":[],"mappings":";;;;;AAoCA,IAAM,UAAA,GAA4B;AAAA,EAChC,OAAA,EAAS,MAAA;AAAA,EACT,aAAA,EAAe,QAAA;AAAA,EACf,GAAA,EAAK,CAAA;AAAA,EACL,OAAA,EAAS,CAAA;AAAA,EACT,YAAA,EAAc,CAAA;AAAA,EACd,UAAA,EAAY,wBAAA;AAAA,EACZ,MAAA,EAAQ,qCAAA;AAAA,EACR,KAAA,EAAO,SAAA;AAAA,EACP,IAAA,EAAM;AACR,CAAA;AAEA,IAAM,WAAA,GAA6B,EAAE,OAAA,EAAS,IAAA,EAAK;AAEnD,IAAM,UAAA,GAA4B;AAAA,EAChC,QAAA,EAAU,UAAA;AAAA,EACV,MAAA,EAAQ,WAAA;AAAA,EACR,WAAA,EAAa,MAAA;AAAA,EACb,UAAA,EAAY;AACd,CAAA;AAEA,IAAM,SAAA,GAA2B,EAAE,OAAA,EAAS,OAAA,EAAS,OAAO,MAAA,EAAO;AAEnE,IAAM,kBAAA,GAAoC;AAAA,EACxC,UAAA,EAAY,MAAA;AAAA,EACZ,OAAA,EAAS,MAAA;AAAA,EACT,cAAA,EAAgB,eAAA;AAAA,EAChB,GAAA,EAAK,CAAA;AAAA,EACL,UAAA,EAAY,aAAA;AAAA,EACZ,MAAA,EAAQ,MAAA;AAAA,EACR,YAAA,EAAc,CAAA;AAAA,EACd,KAAA,EAAO,SAAA;AAAA,EACP,MAAA,EAAQ,SAAA;AAAA,EACR,IAAA,EAAM,SAAA;AAAA,EACN,OAAA,EAAS,SAAA;AAAA,EACT,SAAA,EAAW;AACb,CAAA;AAEA,IAAM,kBAAA,GAAoC;AAAA,EACxC,GAAG,kBAAA;AAAA,EACH,OAAA,EAAS,IAAA;AAAA,EACT,cAAA,EAAgB;AAClB,CAAA;AAEA,IAAM,UAAA,GAAa,2BAAA;AACnB,IAAM,aAAA,GAAgB,SAAA;AACtB,IAAM,UAAA,GAAa,2BAAA;AACnB,IAAM,YAAA,GAAe,2BAAA;AAEd,SAAS,eAAe,KAAA,EAA0C;AACvE,EAAA,MAAM,QAAA,GAAW,mBAAA,CAAoB,KAAA,CAAM,QAAA,EAAU,kBAAkB,CAAA;AACvE,EAAA,MAAM,EAAE,OAAA,EAAS,KAAA,KAAU,uBAAA,CAAwB,QAAA,EAAU,MAAM,SAAS,CAAA;AAC5E,EAAA,MAAM,QAAA,GAAW,kBAAA,CAAmB,QAAA,EAAU,KAAA,CAAM,SAAS,CAAA;AAE7D,EAAA,MAAM,MAAA,GAAS,SAAS,MAAA,IAAU,IAAA;AAClC,EAAA,MAAM,WAAW,aAAA,CAAc;AAAA,IAC7B,MAAA;AAAA,IACA,QAAA,EAAU,OAAA,EAAS,IAAA,CAAK,MAAA,IAAU,CAAA;AAAA,IAClC,KAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,MAAA,GAAS,MAAM,SAAA,KAAc,MAAA;AACnC,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,IAAU,EAAA;AAC/B,EAAA,MAAM,WAAA,GAAc,OAAA,KAAY,IAAA,IAAQ,OAAA,CAAQ,IAAA,KAAS,aAAA;AACzD,EAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,KAAA,EAAO,MAAM,CAAA;AAEzC,EAAA,MAAM,cAAA,GAAiB,CAAC,GAAA,EAAkB,QAAA,KAAmC;AAC3E,IAAA,MAAM,GAAA,GAAM,WAAW,CAAA,GAAI,IAAA,CAAK,MAAO,GAAA,CAAI,QAAA,GAAW,QAAA,GAAY,GAAG,CAAA,GAAI,CAAA;AACzE,IAAA,uBACE,IAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QAEC,IAAA,EAAK,QAAA;AAAA,QACL,iCAA+B,GAAA,CAAI,GAAA;AAAA,QACnC,gBAAc,GAAA,CAAI,QAAA;AAAA,QAClB,YAAA,EAAY,CAAA,gBAAA,EAAmB,GAAA,CAAI,GAAG,CAAA,CAAA;AAAA,QACtC,WAAW,KAAA,CAAM,iBAAA;AAAA,QACjB,OACE,KAAA,CAAM,iBAAA,KAAsB,SACxB,MAAA,GACA,GAAA,CAAI,WACF,kBAAA,GACA,kBAAA;AAAA,QAER,SAAS,MAAM;AACb,UAAA,QAAA,CAAS,uBAAA,CAAwB,KAAA,EAAO,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,QAClD,CAAA;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,qCAAA,EAAoC,EAAA,EAAI,QAAA,EAAA,GAAA,CAAI,GAAA,EAAI,CAAA;AAAA,0BACtD,IAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,qCAAA,EAAoC,EAAA;AAAA,cACpC,aAAA,EAAY,MAAA;AAAA,cACZ,KAAA,EACE,KAAA,CAAM,iBAAA,KAAsB,MAAA,GACxB,MAAA,GACA;AAAA,gBACE,YAAY,CAAA,0BAAA,EAA6B,aAAa,CAAA,GAAA,EAAM,GAAG,kBAAkB,GAAG,CAAA,EAAA,CAAA;AAAA,gBACpF,YAAA,EAAc,CAAA;AAAA,gBACd,OAAA,EAAS;AAAA,eACX;AAAA,cAGL,QAAA,EAAA;AAAA,gBAAA,GAAA,CAAI,QAAA;AAAA,gBAAS,GAAA;AAAA,gBAAE,GAAA,CAAI;AAAA;AAAA;AAAA;AACtB;AAAA,OAAA;AAAA,MAhCK,GAAA,CAAI;AAAA,KAiCX;AAAA,EAEJ,CAAA;AAEA,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI,YAAY,IAAA,EAAM;AACpB,IAAA,IAAA,mBACE,GAAA,CAAC,SAAI,4BAAA,EAA2B,EAAA,EAAG,OAAO,MAAA,GAAS,WAAA,GAAc,QAAW,QAAA,EAAA,SAAA,EAE5E,CAAA;AAAA,EAEJ,WAAW,WAAA,EAAa;AACtB,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,UAAA,CAAW,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAO,CAAA,CAAE,KAAA,GAAQ,CAAA,GAAI,CAAA,CAAE,KAAA,GAAQ,GAAI,CAAC,CAAA;AACnF,IAAA,IAAA,mBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,iCAAA,EAAgC,EAAA;AAAA,QAChC,OAAO,MAAA,GAAS,EAAE,SAAS,MAAA,EAAQ,aAAA,EAAe,UAAS,GAAI,MAAA;AAAA,QAE9D,QAAA,EAAA,OAAA,CAAQ,WAAW,GAAA,CAAI,CAAC,QAAQ,cAAA,CAAe,GAAA,EAAK,QAAQ,CAAC;AAAA;AAAA,KAChE;AAAA,EAEJ,CAAA,MAAO;AACL,IAAA,MAAM,OAAO,OAAA,CAAQ,IAAA;AACrB,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAO,CAAA,CAAE,KAAA,GAAQ,CAAA,GAAI,CAAA,CAAE,KAAA,GAAQ,CAAA,EAAI,CAAC,CAAA;AACrE,IAAA,MAAM,OAAO,IAAA,CAAK,MAAA,GAAS,CAAA,GAAI,GAAA,GAAM,KAAK,MAAA,GAAS,GAAA;AACnD,IAAA,IAAA,mBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,2BAAA,EAA0B,EAAA;AAAA,QAC1B,QAAA,EAAU,CAAA;AAAA,QACV,YAAA,EAAY,CAAA,EAAG,KAAA,CAAM,SAAS,CAAA,uDAAA,CAAA;AAAA,QAC9B,IAAA,EAAK,QAAA;AAAA,QACL,kBAAA,EAAiB,YAAA;AAAA,QACjB,eAAA,EAAe,OAAA,CAAQ,MAAA,EAAQ,GAAA,IAAO,CAAA;AAAA,QACtC,eAAA,EAAe,OAAA,CAAQ,MAAA,EAAQ,GAAA,IAAO,CAAA;AAAA,QACtC,eAAA,EAAe,cAAA,CAAe,KAAA,EAAO,OAAA,CAAQ,MAAM,CAAA;AAAA,QACnD,gBAAA,EAAgB,eAAA,CAAgB,KAAA,EAAO,OAAA,CAAQ,MAAM,CAAA;AAAA,QACrD,KAAA,EAAO,SAAS,UAAA,GAAa,MAAA;AAAA,QAC7B,eAAe,QAAA,CAAS,aAAA;AAAA,QACxB,eAAe,QAAA,CAAS,aAAA;AAAA,QACxB,aAAa,QAAA,CAAS,WAAA;AAAA,QACtB,iBAAiB,QAAA,CAAS,eAAA;AAAA,QAC1B,eAAe,QAAA,CAAS,aAAA;AAAA,QACxB,WAAW,QAAA,CAAS,SAAA;AAAA,QAEpB,QAAA,kBAAA,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,MAAA;AAAA,YACN,MAAA;AAAA,YACA,OAAA,EAAQ,aAAA;AAAA,YACR,mBAAA,EAAoB,MAAA;AAAA,YACpB,aAAA,EAAY,MAAA;AAAA,YACZ,SAAA,EAAU,OAAA;AAAA,YACV,KAAA,EAAO,SAAS,SAAA,GAAY,MAAA;AAAA,YAE3B,QAAA,EAAA;AAAA,cAAA,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,EAAK,CAAA,KAAM;AACpB,gBAAA,MAAM,CAAA,GAAI,CAAA,GAAI,IAAA,GAAO,IAAA,GAAO,IAAA;AAC5B,gBAAA,MAAM,IAAI,IAAA,GAAO,GAAA;AACjB,gBAAA,MAAM,SAAS,QAAA,GAAW,CAAA,GAAK,GAAA,CAAI,KAAA,GAAQ,WAAY,GAAA,GAAM,CAAA;AAC7D,gBAAA,MAAM,YAAY,QAAA,GAAW,CAAA,GAAK,GAAA,CAAI,QAAA,GAAW,WAAY,GAAA,GAAM,CAAA;AACnE,gBAAA,4BACG,GAAA,EAAA,EACC,QAAA,EAAA;AAAA,kCAAA,GAAA;AAAA,oBAAC,MAAA;AAAA,oBAAA;AAAA,sBACC,gCAAA,EAA+B,EAAA;AAAA,sBAC/B,CAAA;AAAA,sBACA,GAAG,GAAA,GAAM,MAAA;AAAA,sBACT,KAAA,EAAO,CAAA;AAAA,sBACP,MAAA,EAAQ,MAAA;AAAA,sBACR,IAAA,EAAM;AAAA;AAAA,mBACR;AAAA,kCACA,GAAA;AAAA,oBAAC,MAAA;AAAA,oBAAA;AAAA,sBACC,mCAAA,EAAkC,EAAA;AAAA,sBAClC,CAAA;AAAA,sBACA,GAAG,GAAA,GAAM,SAAA;AAAA,sBACT,KAAA,EAAO,CAAA;AAAA,sBACP,MAAA,EAAQ,SAAA;AAAA,sBACR,IAAA,EAAM;AAAA;AAAA;AACR,iBAAA,EAAA,EAhBM,CAiBR,CAAA;AAAA,cAEJ,CAAC,CAAA;AAAA,cACA,UAAU,IAAA,mBACT,GAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,4BAAA,EAA2B,EAAA;AAAA,kBAC3B,GAAG,KAAA,CAAM,OAAA;AAAA,kBACT,CAAA,EAAG,CAAA;AAAA,kBACH,OAAO,KAAA,CAAM,QAAA;AAAA,kBACb,MAAA,EAAQ,GAAA;AAAA,kBACR,IAAA,EAAM,UAAA;AAAA,kBACN,MAAA,EAAQ,YAAA;AAAA,kBACR,WAAA,EAAa,GAAA;AAAA,kBACb,aAAA,EAAc;AAAA;AAAA,eAChB,GACE;AAAA;AAAA;AAAA;AACN;AAAA,KACF;AAAA,EAEJ;AAEA,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,wBAAsB,KAAA,CAAM,SAAA;AAAA,MAC5B,IAAA,EAAK,OAAA;AAAA,MACL,YAAA,EAAY,CAAA,EAAG,KAAA,CAAM,SAAS,CAAA,UAAA,CAAA;AAAA,MAC9B,WAAW,KAAA,CAAM,SAAA;AAAA,MACjB,KAAA,EAAO,SAAS,EAAE,GAAG,YAAY,GAAG,KAAA,CAAM,KAAA,EAAM,GAAI,KAAA,CAAM,KAAA;AAAA,MAE1D,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,8BAA2B,EAAA,EAAG,KAAA,EAAO,SAAS,WAAA,GAAc,MAAA,EAC9D,gBAAM,SAAA,EACT,CAAA;AAAA,QACC;AAAA;AAAA;AAAA,GACH;AAEJ","file":"Histogram.js","sourcesContent":["/**\n * <GraphHistogram> — crossfilter histogram.\n *\n * SVG dual-layer bar chart over one crossfilter dimension: total bars\n * (muted) with the joint-filtered layer (accent) overlaid. Numeric/temporal\n * dimensions brush by pointer-drag over the plot (any drag REPLACES the\n * brush), double-click clears, ArrowLeft/ArrowRight nudge the window by one\n * bin, Escape clears (the plot is focusable). Categorical dimensions render\n * clickable rows toggling category exclusions. All text values (dimension\n * key, category keys) render as TEXT NODES. Headless-styleable: providing\n * `className` drops the built-in dark-theme inline styles.\n */\n\nimport type { CSSProperties, ReactElement } from 'react';\nimport type { CategoryBin } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\nimport { brushRangePct, sliderValueNow, sliderValueText, toggleCategoryExclusion, useRangeBrush } from '../_shared/brush';\nimport { useCrossfilterDimension, useSessionSetBrush } from '../_shared/crossfilter';\n\nexport interface GraphHistogramProps {\n /** Crossfilter dimension key (must appear in the `crossfilter` prop). */\n dimension: string;\n /** Plot height in CSS px. Default 96. */\n height?: number;\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 /** Class hook for each categorical row; providing it drops its defaults. */\n categoryClassName?: string;\n}\n\n// --- default dark-theme styling (dropped when `className` is provided) ---\n\nconst ROOT_STYLE: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n padding: 8,\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.92)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n color: '#e8eaf0',\n font: '12px/1.4 system-ui, sans-serif',\n};\n\nconst TITLE_STYLE: CSSProperties = { opacity: 0.75 };\n\nconst PLOT_STYLE: CSSProperties = {\n position: 'relative',\n cursor: 'crosshair',\n touchAction: 'none',\n userSelect: 'none',\n};\n\nconst SVG_STYLE: CSSProperties = { display: 'block', width: '100%' };\n\nconst CATEGORY_ROW_STYLE: CSSProperties = {\n appearance: 'none',\n display: 'flex',\n justifyContent: 'space-between',\n gap: 8,\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 TOTAL_FILL = 'rgba(148, 163, 184, 0.35)';\nconst FILTERED_FILL = '#7aa2ff';\nconst BRUSH_FILL = 'rgba(122, 162, 255, 0.18)';\nconst BRUSH_STROKE = 'rgba(122, 162, 255, 0.85)';\n\nexport function GraphHistogram(props: GraphHistogramProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphHistogram>');\n const { summary, brush } = useCrossfilterDimension(instance, props.dimension);\n const setBrush = useSessionSetBrush(instance, props.dimension);\n\n const domain = summary?.domain ?? null;\n const handlers = useRangeBrush({\n domain,\n binCount: summary?.bins.length ?? 0,\n brush,\n setBrush,\n });\n\n const styled = props.className === undefined;\n const height = props.height ?? 96;\n const categorical = summary !== null && summary.kind === 'categorical';\n const range = brushRangePct(brush, domain);\n\n const renderCategory = (cat: CategoryBin, maxTotal: number): ReactElement => {\n const pct = maxTotal > 0 ? Math.round((cat.filtered / maxTotal) * 100) : 0;\n return (\n <button\n key={cat.key}\n type=\"button\"\n data-orbit-histogram-category={cat.key}\n aria-pressed={cat.excluded}\n aria-label={`Toggle category ${cat.key}`}\n className={props.categoryClassName}\n style={\n props.categoryClassName !== undefined\n ? undefined\n : cat.excluded\n ? EXCLUDED_ROW_STYLE\n : CATEGORY_ROW_STYLE\n }\n onClick={() => {\n setBrush(toggleCategoryExclusion(brush, cat.key));\n }}\n >\n <span data-orbit-histogram-category-label=\"\">{cat.key}</span>\n <span\n data-orbit-histogram-category-count=\"\"\n aria-hidden=\"true\"\n style={\n props.categoryClassName !== undefined\n ? undefined\n : {\n background: `linear-gradient(to right, ${FILTERED_FILL}33 ${pct}%, transparent ${pct}%)`,\n borderRadius: 2,\n padding: '0 4px',\n }\n }\n >\n {cat.filtered}/{cat.total}\n </span>\n </button>\n );\n };\n\n let body: ReactElement;\n if (summary === null) {\n body = (\n <div data-orbit-histogram-empty=\"\" style={styled ? TITLE_STYLE : undefined}>\n No data\n </div>\n );\n } else if (categorical) {\n const maxTotal = summary.categories.reduce((m, c) => (c.total > m ? c.total : m), 0);\n body = (\n <div\n data-orbit-histogram-categories=\"\"\n style={styled ? { display: 'flex', flexDirection: 'column' } : undefined}\n >\n {summary.categories.map((cat) => renderCategory(cat, maxTotal))}\n </div>\n );\n } else {\n const bins = summary.bins;\n const maxTotal = bins.reduce((m, b) => (b.total > m ? b.total : m), 0);\n const barW = bins.length > 0 ? 100 / bins.length : 100;\n body = (\n <div\n data-orbit-histogram-plot=\"\"\n tabIndex={0}\n aria-label={`${props.dimension} brush: drag to filter, arrow keys nudge, Escape clears`}\n role=\"slider\"\n aria-orientation=\"horizontal\"\n aria-valuemin={summary.domain?.min ?? 0}\n aria-valuemax={summary.domain?.max ?? 0}\n aria-valuenow={sliderValueNow(brush, summary.domain)}\n aria-valuetext={sliderValueText(brush, summary.domain)}\n style={styled ? PLOT_STYLE : undefined}\n onPointerDown={handlers.onPointerDown}\n onPointerMove={handlers.onPointerMove}\n onPointerUp={handlers.onPointerUp}\n onPointerCancel={handlers.onPointerCancel}\n onDoubleClick={handlers.onDoubleClick}\n onKeyDown={handlers.onKeyDown}\n >\n <svg\n width=\"100%\"\n height={height}\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n focusable=\"false\"\n style={styled ? SVG_STYLE : undefined}\n >\n {bins.map((bin, i) => {\n const x = i * barW + barW * 0.05;\n const w = barW * 0.9;\n const totalH = maxTotal > 0 ? (bin.total / maxTotal) * 100 : 0;\n const filteredH = maxTotal > 0 ? (bin.filtered / maxTotal) * 100 : 0;\n return (\n <g key={i}>\n <rect\n data-orbit-histogram-bar-total=\"\"\n x={x}\n y={100 - totalH}\n width={w}\n height={totalH}\n fill={TOTAL_FILL}\n />\n <rect\n data-orbit-histogram-bar-filtered=\"\"\n x={x}\n y={100 - filteredH}\n width={w}\n height={filteredH}\n fill={FILTERED_FILL}\n />\n </g>\n );\n })}\n {range !== null ? (\n <rect\n data-orbit-histogram-brush=\"\"\n x={range.leftPct}\n y={0}\n width={range.widthPct}\n height={100}\n fill={BRUSH_FILL}\n stroke={BRUSH_STROKE}\n strokeWidth={0.5}\n pointerEvents=\"none\"\n />\n ) : null}\n </svg>\n </div>\n );\n }\n\n return (\n <div\n data-orbit-histogram={props.dimension}\n role=\"group\"\n aria-label={`${props.dimension} histogram`}\n className={props.className}\n style={styled ? { ...ROOT_STYLE, ...props.style } : props.style}\n >\n <div data-orbit-histogram-title=\"\" style={styled ? TITLE_STYLE : undefined}>\n {props.dimension}\n </div>\n {body}\n </div>\n );\n}\n"]}
|
|
@@ -4,31 +4,31 @@ import { A as AnyGraphInstance } from '../GraphProvider-B8ITDXS0.js';
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* `@modernrelay/orbit-react/components/Inspector` — `<GraphInspector>`, the
|
|
7
|
-
*
|
|
7
|
+
* docked detail panel.
|
|
8
8
|
*
|
|
9
9
|
* Subject: the SELECTED-SINGLE node (`store.selection.nodeIds.length === 1`)
|
|
10
|
-
* — the
|
|
10
|
+
* — the node namespace is the focus proxy the store actually publishes.
|
|
11
11
|
* Anything else (empty or multi selection) renders the empty state.
|
|
12
12
|
*
|
|
13
13
|
* Sections:
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
14
|
+
* - **Attrs table** — every attr key/value as TEXT NODES (untrusted
|
|
15
|
+
* content; hostile strings render literally); `renderAttrs` replaces it.
|
|
16
|
+
* - **Neighbor list** — click → `instance.focusNode(neighborId)` (camera
|
|
17
|
+
* fly). v0.9 resolution compromise: the public instance
|
|
18
|
+
* surface resolves a 1-hop neighborhood ONLY through `focusNode`'s return
|
|
19
|
+
* value (the same seam the navigator uses on Enter) — a passive
|
|
20
|
+
* adjacency read is a post-v0.9 core surface. The section therefore
|
|
21
|
+
* populates from a per-model-revision cache learned by focus operations,
|
|
22
|
+
* with a 'Show neighbors' affordance that focuses the subject to resolve
|
|
23
|
+
* its neighborhood on demand (flying to the inspected node is coherent
|
|
24
|
+
* behavior, and it is the user's explicit action). A model change
|
|
25
|
+
* drops the (now stale) cache.
|
|
26
|
+
* - **Quick actions** — wired to the same public mutators as the
|
|
27
|
+
* context menu: Expand (`expandNode`, busy while pending), Isolate
|
|
28
|
+
* (1-hop ego hard scope via the `subgraph` path), Pin/Unpin (store pin
|
|
29
|
+
* slice toggle), Hide (`hideNodes`). `quickActions` is a render-prop
|
|
30
|
+
* REPLACEMENT receiving the defaults, so extension is
|
|
31
|
+
* `[...defaultActions, mine]`.
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
interface GraphInspectorAction {
|