@danxbot/ui 12.0.0 → 12.1.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.
@@ -0,0 +1,35 @@
1
+ import type { CanvasNode } from "../node-canvas/types";
2
+ import type { CrossLink, ExplorerNodeRenderer, ExplorerTree, GraphExplorerVariant } from "./types";
3
+ export interface GraphExplorerProps<Extra = unknown> {
4
+ tree: ExplorerTree<Extra>;
5
+ /** Secondary relationships drawn over the fixed layout. Never affects positions. */
6
+ crossLinks?: readonly CrossLink[];
7
+ /** The accessible name of the whole structure. Required — see `NodeCanvasProps.label`. */
8
+ label: string;
9
+ /** The node body. Defaults to the label, wrapped, never truncated. */
10
+ renderNode?: ExplorerNodeRenderer<Extra>;
11
+ /** @default "explorer" */
12
+ variant?: GraphExplorerVariant;
13
+ /** Which node the camera centres on, with a "you are here" highlight. Controlled. */
14
+ focusId?: string | null;
15
+ /** Initial `focusId` for uncontrolled use. */
16
+ defaultFocusId?: string | null;
17
+ /** Fired when the controlled `focusId` would change — a node click, or `flyTo` via the keyboard. Never fired with `null`: nothing in this component ever clears focus, only moves it. */
18
+ onFocusIdChange?: (id: string) => void;
19
+ /** Nodes styled as already seen. */
20
+ visitedIds?: ReadonlySet<string> | readonly string[];
21
+ /** Fired when an ordinary node is clicked or activated by keyboard. */
22
+ onNodeClick?: (node: CanvasNode<Extra>) => void;
23
+ /** `variant="minimap"` only: the expand affordance's handler. Defaults to an in-place full-screen dialog. */
24
+ onExpand?: () => void;
25
+ /** Along-axis extent of a node that does not need more, in px. @default LAYOUT_DEFAULTS.nodeWidth */
26
+ nodeWidth?: number;
27
+ /** Cross-axis extent BEFORE a taller node's own content grows it. @default LAYOUT_DEFAULTS.nodeHeight */
28
+ nodeHeight?: number;
29
+ /** `variant="explorer"` only: height of the viewport. @default "32rem" */
30
+ height?: string;
31
+ /** Nothing pans, zooms or clicks. Reading is unaffected. */
32
+ disabled?: boolean;
33
+ className?: string;
34
+ }
35
+ export declare function GraphExplorer<Extra = unknown>({ tree, crossLinks, label, renderNode, variant, focusId: controlledFocusId, defaultFocusId, onFocusIdChange, visitedIds, onNodeClick, onExpand, nodeWidth, nodeHeight, height, disabled, className, }: GraphExplorerProps<Extra>): import("react").JSX.Element;
@@ -0,0 +1,50 @@
1
+ export interface Point {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ export interface Box {
6
+ x: number;
7
+ y: number;
8
+ width: number;
9
+ height: number;
10
+ }
11
+ export interface Size {
12
+ width: number;
13
+ height: number;
14
+ }
15
+ export interface CameraState {
16
+ x: number;
17
+ y: number;
18
+ scale: number;
19
+ }
20
+ export interface ScaleBounds {
21
+ minScale?: number;
22
+ maxScale?: number;
23
+ }
24
+ export declare const CAMERA_DEFAULTS: {
25
+ readonly minScale: 0.15;
26
+ readonly maxScale: 3;
27
+ readonly fitMaxScale: 1;
28
+ readonly fitPadding: 64;
29
+ };
30
+ export declare function clampScale(scale: number, bounds?: ScaleBounds): number;
31
+ /** Screen point → the content point currently sitting under it. */
32
+ export declare function toContentPoint(camera: CameraState, screenPoint: Point): Point;
33
+ /** Content point → where it currently sits on screen. */
34
+ export declare function toScreenPoint(camera: CameraState, contentPoint: Point): Point;
35
+ /**
36
+ * Zoom by `factor`, holding `screenPoint` fixed over the same content point it
37
+ * covered before — the standard "zoom toward the cursor" behaviour a wheel or
38
+ * a pinch both want. `factor` > 1 zooms in.
39
+ */
40
+ export declare function zoomAtPoint(camera: CameraState, screenPoint: Point, factor: number, bounds?: ScaleBounds): CameraState;
41
+ /** A camera that puts `target`'s centre in the middle of `viewport`, at `scale`. */
42
+ export declare function centerOn(target: Box | Point, viewport: Size, scale: number): CameraState;
43
+ export interface FitOptions extends ScaleBounds {
44
+ /** @default CAMERA_DEFAULTS.fitPadding */
45
+ padding?: number;
46
+ /** @default CAMERA_DEFAULTS.fitMaxScale — never zoom IN past this to fit a small box. */
47
+ maxScale?: number;
48
+ }
49
+ /** A camera that fits the whole of `box` inside `viewport`, centred, padded. */
50
+ export declare function fitBox(box: Box, viewport: Size, options?: FitOptions): CameraState;
@@ -0,0 +1,5 @@
1
+ export { GraphExplorer, type GraphExplorerProps } from "./GraphExplorer";
2
+ export { layoutExplorerTree, neighbourhoodTree, edgePath } from "./tree-layout";
3
+ export { CAMERA_DEFAULTS, centerOn, clampScale, fitBox, toContentPoint, toScreenPoint, zoomAtPoint } from "./camera-math";
4
+ export type { Box, CameraState, FitOptions, Point, ScaleBounds, Size } from "./camera-math";
5
+ export type { CrossLink, ExplorerLayout, ExplorerNodeRenderContext, ExplorerNodeRenderer, ExplorerTree, GraphExplorerVariant, TreeNode, } from "./types";
@@ -0,0 +1,29 @@
1
+ import { edgePath } from "../node-canvas/layout";
2
+ import type { LayoutOptions } from "../node-canvas/types";
3
+ import type { CrossLink, ExplorerLayout, ExplorerTree } from "./types";
4
+ /** Re-exported for a consumer drawing `edges`/`crossLinks` into an SVG. */
5
+ export { edgePath };
6
+ /**
7
+ * Lay out a tree, left to right, from a parent backbone alone.
8
+ *
9
+ * `crossLinks` are resolved against the ALREADY-FIXED node positions and
10
+ * returned separately (`ExplorerLayout.crossLinks`) — they never reach
11
+ * `layoutGraph`, so they can never move a node and can never close a cycle
12
+ * for the layout engine to condense. A cross-link naming an id not in the
13
+ * tree, or naming a node as linked to itself, draws nothing — the same
14
+ * "dangling edge draws nothing" rule `node-canvas/layout.ts#resolveScope`
15
+ * uses for a group boundary.
16
+ */
17
+ export declare function layoutExplorerTree<Extra>(tree: ExplorerTree<Extra>, crossLinks?: readonly CrossLink[], options?: LayoutOptions): ExplorerLayout<Extra>;
18
+ /**
19
+ * The subtree a minimap draws: `focusId`'s ancestor chain up to its root,
20
+ * `focusId`'s immediate siblings, and `focusId`'s immediate children — "the
21
+ * focused node's ancestors, siblings and children" the card asks for, kept
22
+ * small enough to read at the minimap's compact size. `focusId` itself is
23
+ * always included. Parent links are REWRITTEN to stay inside this subset: the
24
+ * topmost ancestor kept becomes a root (`parentId: null`) here even if its
25
+ * real parent exists outside the shown set, so `layoutExplorerTree` never
26
+ * sees a dangling parent reference. Returns `null` when `focusId` is not in
27
+ * `tree` at all — nothing sensible to show.
28
+ */
29
+ export declare function neighbourhoodTree<Extra>(tree: ExplorerTree<Extra>, focusId: string): ExplorerTree<Extra> | null;
@@ -0,0 +1,62 @@
1
+ import type { ReactNode } from "react";
2
+ import type { CanvasEdge, CanvasNode, CanvasPlacedEdge, LayoutOptions, PlacedNode } from "../node-canvas/types";
3
+ /**
4
+ * One node of the tree. Same `Extra`-carrying shape as `CanvasNode` — a
5
+ * consumer's own fields come back untouched through `renderNode` — plus the
6
+ * one thing a tree node has that a general graph node does not: its parent.
7
+ */
8
+ export type TreeNode<Extra = unknown> = CanvasNode<Extra> & {
9
+ /** The node this one hangs from. `null` for a root — a tree may have more than one. */
10
+ parentId: string | null;
11
+ };
12
+ /**
13
+ * A secondary relationship drawn OVER the fixed tree layout — never fed to
14
+ * the layout engine, so it can never move a node and can never close a cycle
15
+ * for dagre to condense. `kind` carries no meaning the library assigns, same
16
+ * rule as `CanvasEdge.kind`.
17
+ */
18
+ export interface CrossLink {
19
+ from: string;
20
+ to: string;
21
+ kind?: string;
22
+ }
23
+ /** The whole tree. Never mutated by the component. */
24
+ export interface ExplorerTree<Extra = unknown> {
25
+ nodes: readonly TreeNode<Extra>[];
26
+ }
27
+ /** What `layoutExplorerTree` hands back: the fixed layout, ready to draw. */
28
+ export interface ExplorerLayout<Extra = unknown> {
29
+ nodes: PlacedNode<Extra>[];
30
+ /** The parent → child backbone, drawn as the primary edges. */
31
+ edges: CanvasPlacedEdge[];
32
+ /** `CrossLink`s resolved to drawn curves. Never affects `nodes`' positions. */
33
+ crossLinks: CanvasPlacedEdge[];
34
+ /** Extent of the laid-out tree — the plane the camera's `fitAll` fits to. */
35
+ width: number;
36
+ height: number;
37
+ }
38
+ export type { CanvasEdge, CanvasNode, LayoutOptions, PlacedNode, CanvasPlacedEdge };
39
+ /** Which surface this render is: the full pannable explorer, or the compact strip. */
40
+ export type GraphExplorerVariant = "explorer" | "minimap";
41
+ /**
42
+ * What `renderNode` is told about the node it is drawing — the explorer's own
43
+ * facts (camera focus, visited, hover neighbourhood), analogous to
44
+ * `NodeRenderContext` in `node-canvas/types.ts` but keyed on the camera's
45
+ * `focusId` rather than a drilled-into scope.
46
+ */
47
+ export interface ExplorerNodeRenderContext {
48
+ /** This node carries the canvas's single roving tab stop. */
49
+ isTabStop: boolean;
50
+ /** This IS the controlled `focusId` — the "you are here" node. */
51
+ isFocused: boolean;
52
+ /** In the caller-supplied `visitedIds`. */
53
+ isVisited: boolean;
54
+ /**
55
+ * Hops from whichever node is currently the reference point — the hovered
56
+ * node while one is hovered, else the focused node — along either edge
57
+ * direction (backbone or cross-link). `null` when nothing is being
58
+ * referenced, or this node is further away than the explorer tracks.
59
+ */
60
+ distance: number | null;
61
+ }
62
+ export type ExplorerNodeRenderer<Extra = unknown> = (node: CanvasNode<Extra>, context: ExplorerNodeRenderContext) => ReactNode;
@@ -0,0 +1,31 @@
1
+ import { type Box, type CameraState } from "./camera-math";
2
+ export interface CameraClickEvent {
3
+ /** Client coordinates, for the caller to hit-test against its own elements. */
4
+ target: HTMLElement;
5
+ }
6
+ export interface UseCameraOptions {
7
+ /** Content-space bounding box of the whole drawing. Drives `fitAll`. */
8
+ contentBox: Box;
9
+ /** A node's content-space box, for `flyTo`. `null` for an id not laid out. */
10
+ boxOf: (id: string) => Box | null;
11
+ /** A press that released without travelling past `CLICK_DISTANCE`. */
12
+ onClick?: (event: CameraClickEvent) => void;
13
+ /** Nothing pans, zooms or clicks. Reading the plane still works. */
14
+ disabled?: boolean;
15
+ /** Changing this value re-fits the camera to `contentBox` — a new tree. */
16
+ resetKey?: unknown;
17
+ }
18
+ export interface UseCameraResult {
19
+ camera: CameraState;
20
+ /** A pointer is actively panning or pinching right now. */
21
+ panning: boolean;
22
+ /** True for the duration of a programmatic move — see the module doc. */
23
+ animating: boolean;
24
+ bindViewport: (node: HTMLDivElement | null) => void;
25
+ zoomIn: () => void;
26
+ zoomOut: () => void;
27
+ fitAll: () => void;
28
+ /** Animate the camera to centre node `id`. No-op if `id` isn't laid out yet. */
29
+ flyTo: (id: string, scale?: number) => void;
30
+ }
31
+ export declare function useCamera(options: UseCameraOptions): UseCameraResult;
@@ -630,6 +630,31 @@ export declare const coreIcons: {
630
630
  }], readonly ["path", {
631
631
  readonly d: "M15 3v18";
632
632
  }]];
633
+ readonly crosshair: readonly [readonly ["circle", {
634
+ readonly cx: "12";
635
+ readonly cy: "12";
636
+ readonly r: "10";
637
+ }], readonly ["line", {
638
+ readonly x1: "22";
639
+ readonly x2: "18";
640
+ readonly y1: "12";
641
+ readonly y2: "12";
642
+ }], readonly ["line", {
643
+ readonly x1: "6";
644
+ readonly x2: "2";
645
+ readonly y1: "12";
646
+ readonly y2: "12";
647
+ }], readonly ["line", {
648
+ readonly x1: "12";
649
+ readonly x2: "12";
650
+ readonly y1: "6";
651
+ readonly y2: "2";
652
+ }], readonly ["line", {
653
+ readonly x1: "12";
654
+ readonly x2: "12";
655
+ readonly y1: "22";
656
+ readonly y2: "18";
657
+ }]];
633
658
  readonly "delete-column": readonly [readonly ["path", {
634
659
  readonly d: "M2 12h6";
635
660
  }], readonly ["path", {
@@ -129,6 +129,7 @@ export { ProvenanceDisclosure, ProvenanceDetail, ProvenanceBadge, ConfidenceRead
129
129
  export { confidenceLevel, CONFIDENCE_LEVEL_LABELS, type ConfidenceLevel } from "./lib/confidence";
130
130
  export { createSimStore, resetSimStores, useSimSettings, setSimSettings, getNetworkSpeed, getDataSource, simulateLatency, SimNetworkError, type SimStore, type SimStoreOptions, type SimSettings, type NetworkSpeed, type DataSource, type SimNamespace, } from "./lib/sim";
131
131
  export { NodeCanvas, LAYOUT_DEFAULTS, edgePath, layoutGraph, neighbourhood, type NodeCanvasProps, type CanvasCycleScope, type CanvasEdge, type CanvasGraph, type CanvasGroup, type CanvasLayout, type CanvasNode, type CanvasOrientation, type CanvasPlacedEdge, type CanvasScope, type LayoutOptions, type NodeRenderContext, type NodeRenderer, type PlacedNode, } from "./components/node-canvas";
132
+ export { GraphExplorer, layoutExplorerTree, neighbourhoodTree, CAMERA_DEFAULTS, centerOn, clampScale, fitBox, toContentPoint, toScreenPoint, zoomAtPoint, type GraphExplorerProps, type Box, type CameraState, type FitOptions, type Point, type ScaleBounds, type Size, type CrossLink, type ExplorerLayout, type ExplorerNodeRenderContext, type ExplorerNodeRenderer, type ExplorerTree, type GraphExplorerVariant, type TreeNode as ExplorerTreeNode, } from "./components/graph-explorer";
132
133
  export { defineRecordType, resetRecordTypes, useRecord, useRecords, useStaleness, write, writeAll, peekRecord, readState, readStaleness, subscribeToKey, subscribeToStaleness, retainKeys, sweepRecords, setRecordStoreTenant, getRecordStoreTenant, flushRecordNotifications, resetRecordStore, recordStoreStats, UNKNOWN_STATE, normalizeTypeName, normalizeTimestamp, recordKey, isRecordRef, type RecordRef, type ReadOptions, type RecordPayload, type RecordState, type RecordTypeOptions, type RecordTypeToken, type StalenessEntry, type WriteAllResult, type WriteOptions, type WriteResult, type WriteSource, type WriteVerdict, } from "./lib/records";
133
134
  export { RealtimeProvider, useRealtime, useRealtimeEpoch, useRealtimeHealth, useRealtimeRegistry, SubscriptionRegistry, normalizeScope, scopeKey, subscriptionKey, readRealtimeEpoch, subscribeToRealtimeEpoch, subscribeToRealtimeEvents, resetRealtimeSignals, type RealtimeChannel, type RealtimeConnectionState, type RealtimeDiagnostic, type RealtimeEvent, type RealtimeHealth, type RealtimeProviderProps, type RealtimeScope, type RealtimeScopeInput, type RealtimeStatus, type RealtimeTransport, type RegistryOptions, type SubscriptionHealth, type SubscriptionLease, type SubscriptionRequest, } from "./lib/realtime";
134
135
  export { useRecordRead, useRecordListRead, useRecordStoreSweep, applyMutationResult, optimisticWrite, beginOptimisticWrite, resetOptimisticWrites, type OptimisticHandle, type OptimisticWriteOptions, type OptimisticWriteSpec, type SlotDescriptor, acquireSlot, appendCreated, emptySlotSnapshot, readSlot, resetSlots, slotKeys, subscribeToSlot, writeSlot, RefetchRunner, registerActiveRead, resetActiveReads, type ActiveRead, type ListFetch, type ListFetchResult, type ListReadOptions, type ListReadResult, type ReadError, type RecordFetch, type RecordReadOptions, type RecordReadResult, type RunnerOptions, type SlotSnapshot, } from "./lib/reads";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danxbot/ui",
3
- "version": "12.0.0",
3
+ "version": "12.1.0",
4
4
  "type": "module",
5
5
  "description": "Danxbot — a domain-agnostic React design system with motion as a first-class primitive.",
6
6
  "license": "MIT",