@loradb/lora-graph-canvas 0.10.1 → 0.11.1
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/README.md +31 -0
- package/dist/LoraGraphCanvas.stories.d.ts +1 -0
- package/dist/engines/createEngineUnified.d.ts +19 -0
- package/dist/engines/rafAnim.d.ts +12 -1
- package/dist/engines/types.d.ts +25 -0
- package/dist/hooks/useGraphClipboard.d.ts +7 -1
- package/dist/hooks/useGraphData.d.ts +1 -1
- package/dist/hooks/useGraphDeleteGate.d.ts +38 -0
- package/dist/hooks/useGraphEngine.d.ts +2 -2
- package/dist/hooks/useGraphKeybindings.d.ts +7 -0
- package/dist/hooks/useGraphSelection.d.ts +11 -2
- package/dist/hooks/useImperativeGraphHandle.d.ts +13 -1
- package/dist/hooks/useMarqueeAndCursor.d.ts +5 -0
- package/dist/hooks/usePrefersReducedMotion.d.ts +10 -0
- package/dist/index.cjs +45 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5933 -5159
- package/dist/index.js.map +1 -1
- package/dist/internal/runGuard.d.ts +7 -0
- package/dist/style.css +1 -1
- package/dist/tools/MarqueeOverlay.d.ts +8 -2
- package/dist/types.d.ts +68 -4
- package/package.json +2 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DeletionGuard, DeletionSource } from '../types';
|
|
2
|
+
/** Runs an optional deletion guard, returning `true` when the mutation
|
|
3
|
+
* should proceed. Empty batches short-circuit to `true` so callers can
|
|
4
|
+
* use the same code path whether or not anything is actually selected.
|
|
5
|
+
* A thrown error in the guard is swallowed as a cancel — a throwing
|
|
6
|
+
* host should not silently destroy data. */
|
|
7
|
+
export declare function runGuard<T>(guard: DeletionGuard<T> | undefined, items: T[], source: DeletionSource): Promise<boolean>;
|
package/dist/style.css
CHANGED
|
@@ -1 +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)}
|
|
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[data-tool=pan] .lgc-engine-mount{cursor:grab}.lora-graph-canvas[data-tool=pan] .lgc-engine-mount:active{cursor:grabbing}.lora-graph-canvas[data-tool=add-node] .lgc-engine-mount,.lora-graph-canvas[data-tool=add-link] .lgc-engine-mount{cursor:crosshair}.lora-graph-canvas[data-paused=true]{box-shadow:inset 0 2px 0 var(--lgc-accent)}.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-marquee-count{position:absolute;right:-2px;bottom:-22px;display:inline-flex;align-items:center;justify-content:center;padding:1px 6px;font-size:11px;font-weight:600;color:#fff;background:var(--lgc-accent);border-radius:10px;pointer-events:none;font-variant-numeric:tabular-nums}.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)}
|
|
@@ -6,8 +6,14 @@ export interface MarqueeRect {
|
|
|
6
6
|
}
|
|
7
7
|
export interface MarqueeOverlayProps {
|
|
8
8
|
rect: MarqueeRect | null;
|
|
9
|
+
/** Live count of nodes whose projected position falls inside the
|
|
10
|
+
* rectangle. Hidden when 0 or undefined — small rectangles in dead
|
|
11
|
+
* space shouldn't render an empty "0 nodes" pill. */
|
|
12
|
+
count?: number;
|
|
9
13
|
}
|
|
10
14
|
/** Renders the dashed selection rectangle while the user is dragging a
|
|
11
15
|
* marquee. Positioned absolutely inside the host. Hidden when `rect`
|
|
12
|
-
* is null.
|
|
13
|
-
|
|
16
|
+
* is null. Shows a live node-count badge in the bottom-right of the
|
|
17
|
+
* rectangle so the user can tell how many nodes they'd grab before
|
|
18
|
+
* releasing. */
|
|
19
|
+
export declare function MarqueeOverlay({ rect, count }: MarqueeOverlayProps): import("react/jsx-runtime").JSX.Element | null;
|
package/dist/types.d.ts
CHANGED
|
@@ -35,6 +35,16 @@ export interface GraphData<N extends NodeObject = NodeObject, L extends LinkObje
|
|
|
35
35
|
links: L[];
|
|
36
36
|
}
|
|
37
37
|
export type GraphMode = "2d" | "3d";
|
|
38
|
+
/** Where a delete originated. Hosts often want to gate UI-driven deletes
|
|
39
|
+
* (show a confirm modal) but trust `imperative` calls they made
|
|
40
|
+
* themselves, so the source string lets the guard short-circuit. */
|
|
41
|
+
export type DeletionSource = "keyboard" | "toolbar" | "selectionPanel" | "contextMenu" | "cut" | "imperative";
|
|
42
|
+
/** Returns `true` to allow the deletion, `false` to cancel. Can be
|
|
43
|
+
* async — the caller awaits it before mutating data. A thrown error
|
|
44
|
+
* is treated as a cancel. Called once per batch (not per item). */
|
|
45
|
+
export type DeletionGuard<T> = (items: T[], ctx: {
|
|
46
|
+
source: DeletionSource;
|
|
47
|
+
}) => boolean | Promise<boolean>;
|
|
38
48
|
/** Built-in tool identifiers. */
|
|
39
49
|
export type ToolId = "select" | "pan" | "add-node" | "add-link" | "delete" | "duplicate" | "select-all" | "fit" | "zoom-in" | "zoom-out" | "pause" | "resume" | "screenshot" | "export-json" | "import-json" | "toggle-mode";
|
|
40
50
|
/** Accepts a literal value, a property accessor string, or a function. */
|
|
@@ -190,6 +200,12 @@ export interface LoraGraphCanvasProps<N extends NodeObject = NodeObject, L exten
|
|
|
190
200
|
* and pins them as a group on release. Set to `false` to keep the
|
|
191
201
|
* default simulation-pull behaviour. */
|
|
192
202
|
fixOnDrop?: boolean;
|
|
203
|
+
/** When `true` (default `false`), the camera animates to fit the
|
|
204
|
+
* current selection any time it changes — like the toolbar "fit"
|
|
205
|
+
* button, but scoped to the selected nodes (and the endpoints of
|
|
206
|
+
* any selected links). No-ops when the selection becomes empty;
|
|
207
|
+
* the user keeps their view instead of being snapped back. */
|
|
208
|
+
fitOnSelect?: boolean;
|
|
193
209
|
/** Auto-tune renderer / simulation settings based on graph size so
|
|
194
210
|
* the canvas stays responsive into the 50k-100k-node range.
|
|
195
211
|
*
|
|
@@ -253,6 +269,22 @@ export interface LoraGraphCanvasProps<N extends NodeObject = NodeObject, L exten
|
|
|
253
269
|
}) => void;
|
|
254
270
|
onRenderFramePre?: (ctx: CanvasRenderingContext2D, globalScale: number) => void;
|
|
255
271
|
onRenderFramePost?: (ctx: CanvasRenderingContext2D, globalScale: number) => void;
|
|
272
|
+
/** Optional async gate fired before nodes are removed. Receives every
|
|
273
|
+
* node in the batch (single-node deletes get a 1-length array). Resolve
|
|
274
|
+
* `true` to proceed, `false` to cancel. Throws are treated as cancel.
|
|
275
|
+
* Fires for: keyboard, toolbar, selection-panel, context-menu, cut,
|
|
276
|
+
* and `handle.removeNode(s)` calls — discriminate via `ctx.source`. */
|
|
277
|
+
onBeforeNodeDelete?: DeletionGuard<N>;
|
|
278
|
+
/** Same shape as `onBeforeNodeDelete` for links. */
|
|
279
|
+
onBeforeLinkDelete?: DeletionGuard<L>;
|
|
280
|
+
/** Fires after a node delete has been applied to the data. */
|
|
281
|
+
onNodeDeleted?: (nodes: N[], ctx: {
|
|
282
|
+
source: DeletionSource;
|
|
283
|
+
}) => void;
|
|
284
|
+
/** Fires after a link delete has been applied to the data. */
|
|
285
|
+
onLinkDeleted?: (links: L[], ctx: {
|
|
286
|
+
source: DeletionSource;
|
|
287
|
+
}) => void;
|
|
256
288
|
/** Fires when the user copies nodes (⌘C) — receives the snapshot. */
|
|
257
289
|
onCopy?: (nodes: N[]) => void;
|
|
258
290
|
/** Fires when the user cuts nodes (⌘X) — receives the snapshot
|
|
@@ -309,8 +341,14 @@ export interface LoraGraphCanvasHandle<N extends NodeObject = NodeObject, L exte
|
|
|
309
341
|
id?: string | number;
|
|
310
342
|
}>): N[];
|
|
311
343
|
updateNode(id: string | number, patch: Partial<N>): void;
|
|
312
|
-
|
|
313
|
-
|
|
344
|
+
/** Returns a promise that resolves to `true` when the node was removed.
|
|
345
|
+
* When the host passes `onBeforeNodeDelete`, the guard runs with
|
|
346
|
+
* `source: "imperative"` and may cancel — the promise resolves
|
|
347
|
+
* `false` and the graph is unchanged. Hosts that don't pass a guard
|
|
348
|
+
* can ignore the promise; the data mutation is observable on the
|
|
349
|
+
* same tick (only the resolved promise itself is async). */
|
|
350
|
+
removeNode(id: string | number): Promise<boolean>;
|
|
351
|
+
removeNodes(ids: Array<string | number>): Promise<boolean>;
|
|
314
352
|
addLink(link: {
|
|
315
353
|
source: string | number;
|
|
316
354
|
target: string | number;
|
|
@@ -320,7 +358,9 @@ export interface LoraGraphCanvasHandle<N extends NodeObject = NodeObject, L exte
|
|
|
320
358
|
source: string | number;
|
|
321
359
|
target: string | number;
|
|
322
360
|
} & Partial<L>>): L[];
|
|
323
|
-
|
|
361
|
+
/** Same gate semantics as `removeNode` — `onBeforeLinkDelete` runs with
|
|
362
|
+
* `source: "imperative"` and may cancel. */
|
|
363
|
+
removeLink(predicate: (l: L) => boolean): Promise<boolean>;
|
|
324
364
|
clear(): void;
|
|
325
365
|
getSelection(): Array<string | number>;
|
|
326
366
|
setSelection(ids: Array<string | number>): void;
|
|
@@ -333,8 +373,32 @@ export interface LoraGraphCanvasHandle<N extends NodeObject = NodeObject, L exte
|
|
|
333
373
|
zoom(scale: number, durationMs?: number): void;
|
|
334
374
|
zoomIn(step?: number): void;
|
|
335
375
|
zoomOut(step?: number): void;
|
|
376
|
+
/** Translate the view by a world-space delta. In 2D the z component
|
|
377
|
+
* is ignored (the camera height is locked). */
|
|
378
|
+
panBy(delta: {
|
|
379
|
+
x?: number;
|
|
380
|
+
y?: number;
|
|
381
|
+
z?: number;
|
|
382
|
+
}, durationMs?: number): void;
|
|
383
|
+
/** Animate to a world coordinate, preserving the current viewing
|
|
384
|
+
* direction. Use for "jump to coordinates" UI. */
|
|
385
|
+
goTo(target: {
|
|
386
|
+
x: number;
|
|
387
|
+
y: number;
|
|
388
|
+
z?: number;
|
|
389
|
+
}, opts?: {
|
|
390
|
+
durationMs?: number;
|
|
391
|
+
}): void;
|
|
392
|
+
/** Fit the view to a subset of nodes. Empty array → full fit. */
|
|
393
|
+
fitToNodes(nodeIds: ReadonlyArray<string | number>, durationMs?: number, padding?: number): void;
|
|
394
|
+
/** Fit to the current selection (nodes + endpoints of selected links).
|
|
395
|
+
* No-op if nothing is selected. */
|
|
396
|
+
fitToSelection(durationMs?: number, padding?: number): void;
|
|
336
397
|
copy(): N[];
|
|
337
|
-
|
|
398
|
+
/** Cut funnels through the same delete-gate as keyboard / toolbar
|
|
399
|
+
* cuts. A rejected guard resolves to an empty array and leaves the
|
|
400
|
+
* graph + clipboard untouched. */
|
|
401
|
+
cut(): Promise<N[]>;
|
|
338
402
|
paste(opts?: {
|
|
339
403
|
at?: {
|
|
340
404
|
x: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loradb/lora-graph-canvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "React graph canvas for LoraDB. Unified 2D/3D force-directed component with a built-in tool palette for adding, connecting, selecting, and removing nodes.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"packageManager": "yarn@4.5.1",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"test": "vitest run",
|
|
58
58
|
"test:watch": "vitest",
|
|
59
59
|
"lint": "eslint src test",
|
|
60
|
+
"lint:fix": "eslint src test --fix",
|
|
60
61
|
"format": "prettier --write src test",
|
|
61
62
|
"format:check": "prettier --check src test",
|
|
62
63
|
"storybook": "storybook dev -p 6007",
|