@loradb/lora-graph-canvas 0.10.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/LICENSE +87 -0
- package/README.md +191 -0
- package/THIRD_PARTY.md +40 -0
- package/dist/LoraGraphCanvas.d.ts +9 -0
- package/dist/LoraGraphCanvas.stories.d.ts +23 -0
- package/dist/engines/3d-force-graph/index.d.ts +1 -0
- package/dist/engines/3d-force-graph/kapsule.d.ts +12 -0
- package/dist/engines/createEngineUnified.d.ts +13 -0
- package/dist/engines/propBindings.d.ts +29 -0
- package/dist/engines/rafAnim.d.ts +13 -0
- package/dist/engines/types.d.ts +86 -0
- package/dist/hooks/useAccessorOverrides.d.ts +62 -0
- package/dist/hooks/useAutoIndexNeighbors.d.ts +6 -0
- package/dist/hooks/useClickToleranceShim.d.ts +46 -0
- package/dist/hooks/useGraphClipboard.d.ts +47 -0
- package/dist/hooks/useGraphData.d.ts +42 -0
- package/dist/hooks/useGraphEngine.d.ts +23 -0
- package/dist/hooks/useGraphForces.d.ts +12 -0
- package/dist/hooks/useGraphKeybindings.d.ts +27 -0
- package/dist/hooks/useGraphSelection.d.ts +14 -0
- package/dist/hooks/useHoverState.d.ts +67 -0
- package/dist/hooks/useImperativeGraphHandle.d.ts +22 -0
- package/dist/hooks/useLabelRenderer.d.ts +47 -0
- package/dist/hooks/useLinkLabelRenderer.d.ts +56 -0
- package/dist/hooks/useMarqueeAndCursor.d.ts +59 -0
- package/dist/hooks/usePerfTierDefaults.d.ts +13 -0
- package/dist/hooks/useResizeObserver.d.ts +7 -0
- package/dist/hooks/useShiftHeld.d.ts +5 -0
- package/dist/index.cjs +685 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +11309 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/accessor-fn.d.ts +2 -0
- package/dist/internal/bezier.d.ts +16 -0
- package/dist/internal/canvas-color-tracker.d.ts +13 -0
- package/dist/internal/debounce.d.ts +5 -0
- package/dist/internal/float-tooltip.d.ts +14 -0
- package/dist/internal/kapsule-link.d.ts +24 -0
- package/dist/internal/kapsule.d.ts +43 -0
- package/dist/internal/throttle.d.ts +6 -0
- package/dist/internal/tween.d.ts +31 -0
- package/dist/style.css +1 -0
- package/dist/theme/presets.d.ts +8 -0
- package/dist/tools/ContextMenu.d.ts +17 -0
- package/dist/tools/GraphToolbar.d.ts +18 -0
- package/dist/tools/GroupLegend.d.ts +11 -0
- package/dist/tools/HoverTooltip.d.ts +15 -0
- package/dist/tools/MarqueeOverlay.d.ts +13 -0
- package/dist/tools/ModeToggle.d.ts +10 -0
- package/dist/tools/OptionsMenu.d.ts +28 -0
- package/dist/tools/SelectionPanel.d.ts +18 -0
- package/dist/tools/icons.d.ts +23 -0
- package/dist/tools/tools.d.ts +37 -0
- package/dist/types.d.ts +375 -0
- package/dist/utils/accessor.d.ts +36 -0
- package/dist/utils/download.d.ts +4 -0
- package/dist/utils/geometry.d.ts +8 -0
- package/dist/utils/grid.d.ts +9 -0
- package/dist/utils/ids.d.ts +3 -0
- package/dist/utils/perfTier.d.ts +29 -0
- package/dist/utils/spriteLabel.d.ts +61 -0
- package/dist/utils/themeStyle.d.ts +5 -0
- package/package.json +105 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare class Bezier {
|
|
2
|
+
#private;
|
|
3
|
+
constructor(...pts: number[]);
|
|
4
|
+
/** Point on the curve at parameter `t` ∈ [0, 1]. Implementation
|
|
5
|
+
* uses the explicit cubic / quadratic forms (faster than
|
|
6
|
+
* de Casteljau for two/three subdivisions). */
|
|
7
|
+
get(t: number): {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
};
|
|
11
|
+
/** Arc length via Gauss-Legendre quadrature would be cleaner, but
|
|
12
|
+
* this consumer only uses the length to position arrowheads and
|
|
13
|
+
* photons — a 40-step polyline approximation is plenty accurate.
|
|
14
|
+
* Allocation-free in the steady state (one number per step). */
|
|
15
|
+
length(): number;
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default class CanvasColorTracker<T = unknown> {
|
|
2
|
+
#private;
|
|
3
|
+
constructor(csBits?: number);
|
|
4
|
+
reset(): void;
|
|
5
|
+
/** Allocate a fresh colour for `obj` and return its hex string,
|
|
6
|
+
* or `null` once the registry is full. */
|
|
7
|
+
register(obj: T): string | null;
|
|
8
|
+
/** Reverse a hex string (as returned by `register`) or an [r,g,b]
|
|
9
|
+
* triple (as read from `ImageData.data`) back to its registered
|
|
10
|
+
* object. Returns `null` if the colour doesn't decode to a valid
|
|
11
|
+
* entry or fails the checksum. */
|
|
12
|
+
lookup(color: string | [number, number, number]): T | null;
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type TooltipContent = string | HTMLElement | false | null;
|
|
2
|
+
export interface TooltipOptions {
|
|
3
|
+
style?: Partial<CSSStyleDeclaration>;
|
|
4
|
+
}
|
|
5
|
+
export default class Tooltip {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(host: HTMLElement, options?: TooltipOptions);
|
|
8
|
+
content(): TooltipContent;
|
|
9
|
+
content(value: TooltipContent): this;
|
|
10
|
+
offsetX(): number | string | null;
|
|
11
|
+
offsetX(value: number | string | null): this;
|
|
12
|
+
offsetY(): number | string | null;
|
|
13
|
+
offsetY(value: number | string | null): this;
|
|
14
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type ChainableKapsule = {
|
|
2
|
+
_destructor?: () => void;
|
|
3
|
+
[k: string]: unknown;
|
|
4
|
+
};
|
|
5
|
+
type KapsuleConstructor = new () => ChainableKapsule;
|
|
6
|
+
interface KapsulePropConfig {
|
|
7
|
+
default: unknown;
|
|
8
|
+
onChange: (v: unknown, state: Record<string, ChainableKapsule>) => void;
|
|
9
|
+
triggerUpdate: false;
|
|
10
|
+
}
|
|
11
|
+
interface KapsuleLinker {
|
|
12
|
+
/** Build a kapsule prop config that forwards the prop to one or more
|
|
13
|
+
* named inner kapsules on every change. The `default` is read from a
|
|
14
|
+
* throwaway instance of the inner kapsule type so it stays in sync
|
|
15
|
+
* with upstream. */
|
|
16
|
+
linkProp: (prop: string) => KapsulePropConfig;
|
|
17
|
+
/** Build a method shim that calls `method(...args)` on every named
|
|
18
|
+
* inner kapsule and returns either the inner's return value (when
|
|
19
|
+
* it's not the kapsule itself, i.e. a getter) or `this` for chain
|
|
20
|
+
* continuity. */
|
|
21
|
+
linkMethod: (method: string) => (state: Record<string, ChainableKapsule>, ...args: unknown[]) => unknown;
|
|
22
|
+
}
|
|
23
|
+
export default function kapsuleLink(kapsulePropNames: string | string[], kapsuleType: KapsuleConstructor): KapsuleLinker;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface KapsuleState {
|
|
2
|
+
initialised: boolean;
|
|
3
|
+
_rerender: () => void;
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
}
|
|
6
|
+
export interface PropConfig {
|
|
7
|
+
/** Default prop value, applied at construction time. */
|
|
8
|
+
default?: unknown;
|
|
9
|
+
/** Fire `update()` after this prop changes. Default true. */
|
|
10
|
+
triggerUpdate?: boolean;
|
|
11
|
+
/** Callback fired synchronously when the prop changes. */
|
|
12
|
+
onChange?: (newVal: unknown, state: KapsuleState, prevVal: unknown) => void;
|
|
13
|
+
}
|
|
14
|
+
export type KapsuleMethod = (state: KapsuleState, ...args: any[]) => unknown;
|
|
15
|
+
export interface KapsuleConfig {
|
|
16
|
+
props?: Record<string, PropConfig>;
|
|
17
|
+
methods?: Record<string, KapsuleMethod>;
|
|
18
|
+
/** Alias one prop/method name to another (for backwards compat). */
|
|
19
|
+
aliases?: Record<string, string>;
|
|
20
|
+
/** Initial state factory. Run once at construction. */
|
|
21
|
+
stateInit?: (options?: Record<string, unknown>) => Partial<KapsuleState>;
|
|
22
|
+
/** Constructor-time initialiser. Receives the host element (or
|
|
23
|
+
* whatever was passed to `new Kapsule(...)`). Typed `any` so
|
|
24
|
+
* consumers can declare a narrower DOM type without an extra cast. */
|
|
25
|
+
init?: (constructorItem: any, state: KapsuleState, options?: Record<string, unknown>) => void;
|
|
26
|
+
/** Trailing-edge digest. Fired after each batch of prop changes. */
|
|
27
|
+
update?: (state: KapsuleState, changedProps: Record<string, unknown>) => void;
|
|
28
|
+
}
|
|
29
|
+
/** A kapsule instance — every prop/method ends up as a chainable
|
|
30
|
+
* property on the result. We type it loosely (any-keyed) because
|
|
31
|
+
* the concrete shape depends on the caller's `props`/`methods`;
|
|
32
|
+
* consumers can narrow by declaring their own interface and casting
|
|
33
|
+
* the return through `unknown`. */
|
|
34
|
+
export interface KapsuleInstance {
|
|
35
|
+
(constructorItem?: unknown): KapsuleInstance;
|
|
36
|
+
resetProps: () => KapsuleInstance;
|
|
37
|
+
[propOrMethod: string]: (...args: unknown[]) => unknown;
|
|
38
|
+
}
|
|
39
|
+
export interface KapsuleClassCtor {
|
|
40
|
+
new (element?: unknown, options?: Record<string, unknown>): KapsuleInstance;
|
|
41
|
+
(element?: unknown, options?: Record<string, unknown>): KapsuleInstance;
|
|
42
|
+
}
|
|
43
|
+
export default function Kapsule(cfg?: KapsuleConfig): KapsuleClassCtor;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface Throttled<F extends (...args: never[]) => unknown> {
|
|
2
|
+
(...args: Parameters<F>): ReturnType<F> | undefined;
|
|
3
|
+
flush: () => ReturnType<F> | undefined;
|
|
4
|
+
cancel: () => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function throttle<F extends (...args: never[]) => unknown>(fn: F, wait: number): Throttled<F>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
type Numbers = Record<string, number>;
|
|
2
|
+
type EasingFn = (k: number) => number;
|
|
3
|
+
export declare const Easing: {
|
|
4
|
+
readonly Linear: {
|
|
5
|
+
readonly None: (k: number) => number;
|
|
6
|
+
};
|
|
7
|
+
readonly Quadratic: {
|
|
8
|
+
readonly In: (k: number) => number;
|
|
9
|
+
readonly Out: (k: number) => number;
|
|
10
|
+
readonly InOut: (k: number) => number;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export declare class Group {
|
|
14
|
+
#private;
|
|
15
|
+
add<T extends Numbers>(t: Tween<T>): void;
|
|
16
|
+
remove<T extends Numbers>(t: Tween<T>): void;
|
|
17
|
+
/** Step every active tween. Should be called once per animation
|
|
18
|
+
* frame; finished tweens self-remove via their `onComplete`. */
|
|
19
|
+
update(time?: number): void;
|
|
20
|
+
}
|
|
21
|
+
export declare class Tween<T extends Numbers> {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(initial: T);
|
|
24
|
+
to(target: Partial<T>, duration: number): this;
|
|
25
|
+
easing(fn: EasingFn): this;
|
|
26
|
+
onUpdate(cb: (obj: T) => void): this;
|
|
27
|
+
onComplete(cb: (this: Tween<T>, obj: T) => void): this;
|
|
28
|
+
start(time?: number): this;
|
|
29
|
+
update(time: number): boolean;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.graph-info-msg{top:50%;width:100%;text-align:center;color:#e6e6fa;opacity:.7;font-size:22px;position:absolute;font-family:Sans-serif}.scene-container .clickable{cursor:pointer}.scene-container .grabbable{cursor:move;cursor:grab;cursor:-moz-grab;cursor:-webkit-grab}.scene-container .grabbable:active{cursor:grabbing;cursor:-moz-grabbing;cursor:-webkit-grabbing}.lora-graph-canvas{--lgc-bg: transparent;--lgc-fg: #1c1f23;--lgc-border: #d8dde3;--lgc-accent: #4f8ef7;--lgc-toolbar-bg: rgba(255, 255, 255, .92);--lgc-toolbar-fg: var(--lgc-fg);--lgc-toolbar-border: var(--lgc-border);--lgc-tool-active-bg: rgba(79, 142, 247, .18);--lgc-tool-hover-bg: rgba(0, 0, 0, .05);--lgc-tooltip-bg: rgba(28, 31, 35, .9);--lgc-tooltip-fg: #ffffff;--lgc-menu-bg: #ffffff;--lgc-menu-fg: var(--lgc-fg);--lgc-menu-hover-bg: rgba(0, 0, 0, .06);--lgc-font: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;--lgc-font-size: 13px;position:relative;overflow:hidden;background:var(--lgc-bg);color:var(--lgc-fg);font-family:var(--lgc-font);font-size:var(--lgc-font-size)}.lora-graph-canvas .lgc-engine-mount{position:absolute;inset:0}.lora-graph-canvas .lgc-engine-mount>canvas{display:block}.lora-graph-canvas .lgc-toolbar{position:absolute;display:flex;gap:2px;padding:4px;background:var(--lgc-toolbar-bg);color:var(--lgc-toolbar-fg);border:1px solid var(--lgc-toolbar-border);border-radius:8px;backdrop-filter:blur(8px);box-shadow:0 1px 2px #0000000f;z-index:2}.lora-graph-canvas .lgc-toolbar--top,.lora-graph-canvas .lgc-toolbar--top-right,.lora-graph-canvas .lgc-toolbar--top-left{top:12px}.lora-graph-canvas .lgc-toolbar--top{left:50%;transform:translate(-50%)}.lora-graph-canvas .lgc-toolbar--top-right{right:12px}.lora-graph-canvas .lgc-toolbar--top-left{left:12px}.lora-graph-canvas .lgc-toolbar--bottom{bottom:12px;left:50%;transform:translate(-50%)}.lora-graph-canvas .lgc-tool{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:0;border-radius:6px;background:transparent;color:inherit;cursor:pointer;transition:background-color 90ms ease,color 90ms ease}.lora-graph-canvas .lgc-tool:hover{background:var(--lgc-tool-hover-bg)}.lora-graph-canvas .lgc-tool--active{background:var(--lgc-tool-active-bg);color:var(--lgc-accent)}.lora-graph-canvas .lgc-tool--disabled,.lora-graph-canvas .lgc-tool:disabled{opacity:.35;cursor:not-allowed}.lora-graph-canvas .lgc-tool--disabled:hover,.lora-graph-canvas .lgc-tool:disabled:hover{background:transparent}.lora-graph-canvas .lgc-tool:focus-visible{outline:2px solid var(--lgc-accent);outline-offset:1px}.lora-graph-canvas .lgc-selpanel{position:absolute;top:12px;left:12px;display:flex;align-items:center;gap:4px;padding:4px 6px 4px 10px;background:var(--lgc-toolbar-bg);color:var(--lgc-toolbar-fg);border:1px solid var(--lgc-toolbar-border);border-radius:8px;backdrop-filter:blur(8px);box-shadow:0 1px 2px #0000000f;font-size:12px;z-index:2}.lora-graph-canvas .lgc-selpanel-label{font-weight:500;white-space:nowrap}.lora-graph-canvas .lgc-selpanel-divider{width:1px;height:16px;background:var(--lgc-toolbar-border);margin:0 2px}.lora-graph-canvas .lgc-selpanel-btn{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:0;border-radius:4px;background:transparent;color:inherit;cursor:pointer;transition:background-color 90ms ease}.lora-graph-canvas .lgc-selpanel-btn:hover{background:var(--lgc-tool-hover-bg)}.lora-graph-canvas .lgc-selpanel-btn:disabled{opacity:.35;cursor:not-allowed}.lora-graph-canvas .lgc-selpanel-btn:disabled:hover{background:transparent}.lora-graph-canvas .lgc-options-menu{position:absolute;bottom:52px;right:12px;z-index:2}.lora-graph-canvas .lgc-options-trigger{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;background:var(--lgc-toolbar-bg);color:var(--lgc-toolbar-fg);border:1px solid var(--lgc-toolbar-border);border-radius:8px;backdrop-filter:blur(8px);box-shadow:0 1px 2px #0000000f;cursor:pointer;transition:background-color 90ms ease}.lora-graph-canvas .lgc-options-trigger:hover{background:var(--lgc-tool-hover-bg)}.lora-graph-canvas .lgc-options-trigger:focus-visible{outline:2px solid var(--lgc-accent);outline-offset:1px}.lora-graph-canvas .lgc-options-panel{position:absolute;bottom:36px;right:0;min-width:220px;padding:6px;background:var(--lgc-menu-bg);color:var(--lgc-menu-fg);border:1px solid var(--lgc-toolbar-border);border-radius:8px;box-shadow:0 8px 24px #00000029;font-size:12px;line-height:1.3}.lora-graph-canvas .lgc-options-item{display:flex;align-items:flex-start;gap:8px;padding:6px 8px;border-radius:4px;cursor:pointer}.lora-graph-canvas .lgc-options-item:hover{background:var(--lgc-menu-hover-bg)}.lora-graph-canvas .lgc-options-item input[type=checkbox]{margin-top:2px}.lora-graph-canvas .lgc-options-item-text{display:flex;flex-direction:column;gap:2px}.lora-graph-canvas .lgc-options-item-label{font-weight:500}.lora-graph-canvas .lgc-options-item-hint{opacity:.65;font-size:11px}.lora-graph-canvas .lgc-options-select{margin-left:auto;font:inherit;font-size:11px;padding:2px 4px;background:var(--lgc-toolbar-bg);color:var(--lgc-toolbar-fg);border:1px solid var(--lgc-toolbar-border);border-radius:4px}.lora-graph-canvas .lgc-mode-toggle{position:absolute;bottom:12px;right:12px;display:inline-flex;align-items:center;gap:6px;padding:5px 10px;background:var(--lgc-toolbar-bg);color:var(--lgc-toolbar-fg);border:1px solid var(--lgc-toolbar-border);border-radius:8px;backdrop-filter:blur(8px);box-shadow:0 1px 2px #0000000f;font:inherit;font-size:11px;font-weight:600;letter-spacing:.04em;cursor:pointer;z-index:2;transition:background-color 90ms ease}.lora-graph-canvas .lgc-mode-toggle:hover{background:var(--lgc-tool-hover-bg)}.lora-graph-canvas .lgc-mode-toggle:focus-visible{outline:2px solid var(--lgc-accent);outline-offset:1px}.lora-graph-canvas .lgc-mode-toggle-label{font-variant-numeric:tabular-nums}.lora-graph-canvas .lgc-legend{position:absolute;bottom:12px;left:12px;display:flex;flex-direction:column;gap:2px;padding:6px;background:var(--lgc-toolbar-bg);color:var(--lgc-toolbar-fg);border:1px solid var(--lgc-toolbar-border);border-radius:8px;backdrop-filter:blur(8px);font-size:12px;z-index:2;max-height:40%;overflow-y:auto}.lora-graph-canvas .lgc-legend-item{appearance:none;display:flex;align-items:center;gap:8px;padding:2px 8px 2px 4px;border:0;border-radius:4px;background:transparent;color:inherit;cursor:pointer;text-align:left;font:inherit}.lora-graph-canvas .lgc-legend-item:hover{background:var(--lgc-tool-hover-bg)}.lora-graph-canvas .lgc-legend-item--hidden{opacity:.35;text-decoration:line-through}.lora-graph-canvas .lgc-legend-swatch{display:inline-block;width:12px;height:12px;border-radius:3px}.lora-graph-canvas .lgc-marquee{position:absolute;border:1px dashed var(--lgc-accent);background:color-mix(in srgb,var(--lgc-accent) 12%,transparent);border-radius:2px;z-index:2}.lora-graph-canvas .lgc-tooltip{position:absolute;max-width:280px;padding:4px 8px;background:var(--lgc-tooltip-bg);color:var(--lgc-tooltip-fg);border-radius:4px;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;z-index:4;box-shadow:0 2px 6px #0000001f}.lora-graph-canvas .lgc-menu{position:absolute;min-width:180px;padding:4px;background:var(--lgc-menu-bg);color:var(--lgc-menu-fg);border:1px solid var(--lgc-toolbar-border);border-radius:8px;box-shadow:0 8px 24px #00000029;z-index:3}.lora-graph-canvas .lgc-menu-item{display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:4px;cursor:pointer;user-select:none}.lora-graph-canvas .lgc-menu-item:hover{background:var(--lgc-menu-hover-bg)}.lora-graph-canvas .lgc-menu-separator{height:1px;margin:4px 0;background:var(--lgc-toolbar-border)}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LoraGraphTheme } from '../types';
|
|
2
|
+
/** Light theme — matches the package default. Useful as a reset value
|
|
3
|
+
* when toggling between presets. */
|
|
4
|
+
export declare const lightTheme: LoraGraphTheme;
|
|
5
|
+
/** Dark theme — dark surface, accent kept blue. The engine
|
|
6
|
+
* `backgroundColor` accessor is independent; pair this with a dark
|
|
7
|
+
* `backgroundColor` prop for the full effect. */
|
|
8
|
+
export declare const darkTheme: LoraGraphTheme;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export interface ContextMenuItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: ReactNode;
|
|
5
|
+
shortcut?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
onSelect(): void;
|
|
8
|
+
}
|
|
9
|
+
export interface ContextMenuProps {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
items: Array<ContextMenuItem | {
|
|
13
|
+
separator: true;
|
|
14
|
+
}>;
|
|
15
|
+
onClose(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function ContextMenu({ x, y, items, onClose }: ContextMenuProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ToolId, ToolbarConfig } from '../types';
|
|
2
|
+
export interface GraphToolbarProps {
|
|
3
|
+
/** Tools the user configured. Falsy → none; `true` → defaults;
|
|
4
|
+
* array → that exact order; object → include/exclude + position. */
|
|
5
|
+
config: boolean | ToolId[] | ToolbarConfig;
|
|
6
|
+
/** Currently active *toggleable* tool (the cursor mode). */
|
|
7
|
+
activeTool: ToolId;
|
|
8
|
+
/** Engine paused state, so we can swap pause↔resume into one slot. */
|
|
9
|
+
paused: boolean;
|
|
10
|
+
/** Current view mode, so the toggle-mode button can show the right
|
|
11
|
+
* icon and the right tooltip. */
|
|
12
|
+
mode: "2d" | "3d";
|
|
13
|
+
/** Optional predicate to grey out individual tools (e.g. undo / redo
|
|
14
|
+
* when the corresponding stack is empty). */
|
|
15
|
+
isDisabled?: (id: ToolId) => boolean;
|
|
16
|
+
onSelect(id: ToolId): void;
|
|
17
|
+
}
|
|
18
|
+
export declare function GraphToolbar(props: GraphToolbarProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NodeObject } from '../types';
|
|
2
|
+
export interface GroupLegendProps<N extends NodeObject = NodeObject> {
|
|
3
|
+
nodes: N[];
|
|
4
|
+
/** Same accessor as `nodeAutoColorBy` — string key or function. */
|
|
5
|
+
groupBy?: string | ((n: N) => string | number | null);
|
|
6
|
+
/** Hidden group keys (stringified). Toggled by clicking. */
|
|
7
|
+
hidden: Set<string>;
|
|
8
|
+
onToggle(group: string): void;
|
|
9
|
+
}
|
|
10
|
+
export declare function colorForGroup(group: string): string;
|
|
11
|
+
export declare function GroupLegend<N extends NodeObject = NodeObject>({ nodes, groupBy, hidden, onToggle, }: GroupLegendProps<N>): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface HoverTooltipProps {
|
|
2
|
+
/** Content the tooltip should display. Pass `null` to hide. */
|
|
3
|
+
content: string | HTMLElement | null;
|
|
4
|
+
/** Host element to anchor against (positions are relative to this). */
|
|
5
|
+
hostRef: React.RefObject<HTMLElement | null>;
|
|
6
|
+
}
|
|
7
|
+
/** Floating tooltip that tracks the mouse position inside the host
|
|
8
|
+
* element. Hidden when `content` is null.
|
|
9
|
+
*
|
|
10
|
+
* Perf note: the mousemove listener (and the per-move
|
|
11
|
+
* `getBoundingClientRect`) is only installed while there is content
|
|
12
|
+
* to show. Otherwise we'd be doing a forced layout + React render at
|
|
13
|
+
* 60Hz whenever the user's mouse is over the canvas, even though
|
|
14
|
+
* nothing is being displayed. Themed via --lgc-tooltip-*. */
|
|
15
|
+
export declare function HoverTooltip({ content, hostRef }: HoverTooltipProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface MarqueeRect {
|
|
2
|
+
x0: number;
|
|
3
|
+
y0: number;
|
|
4
|
+
x1: number;
|
|
5
|
+
y1: number;
|
|
6
|
+
}
|
|
7
|
+
export interface MarqueeOverlayProps {
|
|
8
|
+
rect: MarqueeRect | null;
|
|
9
|
+
}
|
|
10
|
+
/** Renders the dashed selection rectangle while the user is dragging a
|
|
11
|
+
* marquee. Positioned absolutely inside the host. Hidden when `rect`
|
|
12
|
+
* is null. */
|
|
13
|
+
export declare function MarqueeOverlay({ rect }: MarqueeOverlayProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ModeToggleProps {
|
|
2
|
+
mode: "2d" | "3d";
|
|
3
|
+
onToggle(): void;
|
|
4
|
+
}
|
|
5
|
+
/** Floating bottom-right pill that switches between 2D and 3D modes.
|
|
6
|
+
* Pulled out of the main toolbar so it stays out of the way and is
|
|
7
|
+
* always findable. Hosts who want the toggle inline can add
|
|
8
|
+
* `"toggle-mode"` to their `tools` array — the inline button keeps
|
|
9
|
+
* working alongside this one. */
|
|
10
|
+
export declare function ModeToggle({ mode, onToggle }: ModeToggleProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** A single entry shown inside the menu. Either a boolean checkbox
|
|
2
|
+
* or a dropdown select. */
|
|
3
|
+
export type OptionItem = {
|
|
4
|
+
kind?: "toggle";
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
checked: boolean;
|
|
8
|
+
onChange(next: boolean): void;
|
|
9
|
+
hint?: string;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "select";
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
value: string;
|
|
15
|
+
options: Array<{
|
|
16
|
+
value: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
}>;
|
|
19
|
+
onChange(next: string): void;
|
|
20
|
+
hint?: string;
|
|
21
|
+
};
|
|
22
|
+
export interface OptionsMenuProps {
|
|
23
|
+
items: OptionItem[];
|
|
24
|
+
}
|
|
25
|
+
/** Floating bottom-right button + popover. The button toggles a small
|
|
26
|
+
* panel of checkbox-style options. Auto-closes on outside click /
|
|
27
|
+
* Escape. Renders nothing if `items` is empty. */
|
|
28
|
+
export declare function OptionsMenu({ items }: OptionsMenuProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SelectionPanelProps {
|
|
2
|
+
nodeCount: number;
|
|
3
|
+
linkCount: number;
|
|
4
|
+
hasClipboard: boolean;
|
|
5
|
+
enableClipboard: boolean;
|
|
6
|
+
onDelete(): void;
|
|
7
|
+
onDuplicate(): void;
|
|
8
|
+
onAddConnected(): void;
|
|
9
|
+
onCopy(): void;
|
|
10
|
+
onCut(): void;
|
|
11
|
+
onPaste(): void;
|
|
12
|
+
onClear(): void;
|
|
13
|
+
}
|
|
14
|
+
/** Inline summary that sits in the top-left of the canvas while
|
|
15
|
+
* anything is selected. Surfaces the count + the most common
|
|
16
|
+
* selection-scoped actions so the user doesn't have to reach for
|
|
17
|
+
* the right-side toolbar. Hidden when nothing is selected. */
|
|
18
|
+
export declare function SelectionPanel({ nodeCount, linkCount, hasClipboard, enableClipboard, onDelete, onDuplicate, onAddConnected, onCopy, onCut, onPaste, onClear, }: SelectionPanelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SVGProps } from 'react';
|
|
2
|
+
type IconProps = SVGProps<SVGSVGElement>;
|
|
3
|
+
export declare const IconSelect: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export declare const IconPan: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare const IconAddNode: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare const IconAddLink: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare const IconDelete: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare const IconFit: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare const IconZoomIn: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare const IconZoomOut: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare const IconPause: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare const IconResume: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare const IconScreenshot: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare const IconCube: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare const IconSquare: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare const IconDuplicate: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare const IconSelectAll: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export declare const IconExport: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare const IconImport: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
/** Source node on the left, a "+" on the right, joined by an edge —
|
|
21
|
+
* visualises "create a new node connected to the selection". */
|
|
22
|
+
export declare const IconAddConnected: (p: IconProps) => import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ComponentType, SVGProps } from 'react';
|
|
2
|
+
import { ToolId } from '../types';
|
|
3
|
+
export interface ToolDescriptor {
|
|
4
|
+
id: ToolId;
|
|
5
|
+
label: string;
|
|
6
|
+
/** Aria label / tooltip. */
|
|
7
|
+
hint: string;
|
|
8
|
+
icon: ComponentType<SVGProps<SVGSVGElement>>;
|
|
9
|
+
/** Tools that toggle an active mode (select, pan, add-node, add-link).
|
|
10
|
+
* Others are one-shot actions. */
|
|
11
|
+
toggleable?: boolean;
|
|
12
|
+
/** Suggested keybinding. The toolbar surfaces this in tooltips; the
|
|
13
|
+
* actual binding is registered by the component. */
|
|
14
|
+
shortcut?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const TOOL_DESCRIPTORS: Record<ToolId, ToolDescriptor>;
|
|
17
|
+
/** Default toolbar order when `tools={true}`.
|
|
18
|
+
*
|
|
19
|
+
* Tools are grouped by purpose:
|
|
20
|
+
* 1. cursor modes (select, pan, add-node, add-link)
|
|
21
|
+
* 2. view (fit, zoom-in, zoom-out)
|
|
22
|
+
* 3. engine (pause, resume)
|
|
23
|
+
* 4. file ops (screenshot, export-json, import-json)
|
|
24
|
+
*
|
|
25
|
+
* Selection-scoped actions — delete, duplicate, copy, cut, paste,
|
|
26
|
+
* clear — live in the auto-appearing top-left SelectionPanel so the
|
|
27
|
+
* main toolbar doesn't restate the same controls. `select-all` lives
|
|
28
|
+
* with the cursor modes (Cmd+A keybinding) since it bootstraps a
|
|
29
|
+
* selection rather than acting on one. `toggle-mode` is its own
|
|
30
|
+
* bottom-right pill (`<ModeToggle>`). Hosts who want a different
|
|
31
|
+
* layout can pass their own `tools` array. */
|
|
32
|
+
export declare const DEFAULT_TOOL_ORDER: ToolId[];
|
|
33
|
+
/** Icon for the toggle-mode button, depending on the currently active
|
|
34
|
+
* mode (we render a cube when in 2D — "switch to 3D" — and a square
|
|
35
|
+
* when in 3D). The toolbar reads from this rather than the static
|
|
36
|
+
* descriptor for that one button. */
|
|
37
|
+
export declare function toggleModeIcon(currentMode: "2d" | "3d"): (p: SVGProps<SVGSVGElement>) => import("react/jsx-runtime").JSX.Element;
|