@visualli/react 0.1.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.
Files changed (38) hide show
  1. package/README.md +335 -0
  2. package/dist/index.cjs +3262 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +375 -0
  5. package/dist/index.d.ts +375 -0
  6. package/dist/index.js +3289 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +48 -0
  9. package/src/VisualliCanvas.tsx +1087 -0
  10. package/src/VisualliRenderer.tsx +427 -0
  11. package/src/animations/index.ts +2 -0
  12. package/src/animations/konvaLayerTransition.ts +223 -0
  13. package/src/components/KonvaContainer.tsx +255 -0
  14. package/src/components/KonvaContainerLayer.tsx +63 -0
  15. package/src/components/KonvaEdge.tsx +254 -0
  16. package/src/components/KonvaEdgeLayer.tsx +72 -0
  17. package/src/components/KonvaNode.tsx +177 -0
  18. package/src/components/KonvaNodeLayer.tsx +61 -0
  19. package/src/components/KonvaStage.tsx +80 -0
  20. package/src/components/NavigationStack.tsx +184 -0
  21. package/src/components/SketchyBoxKonva.tsx +121 -0
  22. package/src/components/ZoomControls.tsx +110 -0
  23. package/src/config/blobShapes.ts +204 -0
  24. package/src/config/index.ts +21 -0
  25. package/src/config/textScaling.ts +69 -0
  26. package/src/context/VisualliContext.tsx +50 -0
  27. package/src/hooks/index.ts +7 -0
  28. package/src/hooks/useKonvaLayerTransition.ts +221 -0
  29. package/src/hooks/useKonvaRenderer.ts +185 -0
  30. package/src/hooks/useViewportNodes.ts +59 -0
  31. package/src/index.ts +81 -0
  32. package/src/konvaCompat.ts +29 -0
  33. package/src/stores/index.ts +3 -0
  34. package/src/stores/stores.ts +17 -0
  35. package/src/stores/useNodeStore.ts +101 -0
  36. package/src/stores/useViewportStore.ts +95 -0
  37. package/src/utils/index.ts +1 -0
  38. package/src/utils/layerNavigation.ts +15 -0
@@ -0,0 +1,375 @@
1
+ import React$1 from 'react';
2
+ import { VisualliDocument, FlatNode, VisualliLayer, NodeMap, ViewportState, IRenderConfigStore, ISelectionStore, MindMapConnection } from '@visualli/core';
3
+ export { ACTIVE_BLOB_TYPES, ALL_BLOB_SHAPES, BLOB_LAYER_CONFIG, BLOB_TEXT_OFFSETS, DESCRIPTION_TEXT_BASE_FONT_PX, FitResult, NODE_LAYER_CONFIG, NODE_TEXT_BASE_FONT_PX, calculateFitCenter, calculateFitView, calculateFitZoom, computeEdgeLabelScale, computeNodeTextScreenScale, computeNodeTextWorldScale, computeOverlayScale, drawBlobPath, getBlobTypeForLayer, getChildLayerForNode, getConnectionsForLayer, getLayerForNavigation } from '@visualli/core';
4
+ import * as zustand from 'zustand';
5
+ import Konva from 'konva';
6
+ import * as konva_lib_Stage from 'konva/lib/Stage';
7
+
8
+ type VisualliTheme = 'dark' | 'light' | 'auto';
9
+ interface VisualliRendererProps {
10
+ /**
11
+ * Raw JSONL string — the content of a .visualli file.
12
+ * Simply pass the string data directly, no preprocessing needed.
13
+ * Example: visualliString="{\"type\":\"meta\"...}\n{\"type\":\"layer\"...}"
14
+ */
15
+ visualliString?: string;
16
+ /**
17
+ * A .visualli file — can be:
18
+ * - File object (e.g. from <input type="file">)
19
+ * - String path to file (e.g. '/data/myfile.visualli' or './src/data/data.visualli')
20
+ * The renderer automatically fetches, reads, and parses it.
21
+ * No user code required for conversion or processing.
22
+ */
23
+ visualliFile?: File | string;
24
+ /**
25
+ * When true, parsing is offloaded to a Web Worker so the main thread never
26
+ * blocks during JSON.parse. Recommended for large documents (>500 nodes).
27
+ * Has no effect when `document` is supplied (already parsed).
28
+ * @default true
29
+ */
30
+ useWorker?: boolean;
31
+ /**
32
+ * Colour theme.
33
+ * - 'dark' → always dark background
34
+ * - 'light' → always light background
35
+ * - 'auto' → follows the OS/browser prefers-color-scheme
36
+ * @default 'light'
37
+ */
38
+ theme?: VisualliTheme;
39
+ /**
40
+ * Enable chromatic immersion background effect.
41
+ * When true:
42
+ * - Root layer (level 0) shows base background color
43
+ * - Child layers show parent node's color as semi-transparent background
44
+ * @default false
45
+ */
46
+ chromaticImmersion?: boolean;
47
+ /**
48
+ * Width of the renderer. Accepts any CSS length string ('100%', '800px', '100vw')
49
+ * or a plain number treated as pixels.
50
+ * @default '100%'
51
+ */
52
+ width?: string | number;
53
+ /**
54
+ * Height of the renderer. Accepts any CSS length string ('100%', '600px', '100vh')
55
+ * or a plain number treated as pixels.
56
+ * @default '100%'
57
+ */
58
+ height?: string | number;
59
+ /** Additional CSS class on the root element. */
60
+ className?: string;
61
+ /** Additional inline styles merged onto the root element. */
62
+ style?: React$1.CSSProperties;
63
+ }
64
+ declare function VisualliRenderer({ visualliString, visualliFile, theme, width, height, useWorker, chromaticImmersion, className, style, }: VisualliRendererProps): React$1.JSX.Element;
65
+
66
+ interface VisualliCanvasProps {
67
+ preParsedVisualli?: VisualliDocument;
68
+ /**
69
+ * Raw JSONL string — just pass the string data directly.
70
+ * No preprocessing needed by user.
71
+ */
72
+ visualliString?: string;
73
+ /**
74
+ * A .visualli file — can be:
75
+ * - File object (from file input)
76
+ * - String path to file (e.g. '/data/file.visualli')
77
+ * Component handles all fetching and parsing automatically.
78
+ */
79
+ visualliFile?: File | string;
80
+ isDark?: boolean;
81
+ chromaticImmersion?: boolean;
82
+ onNodeClick?: (node: FlatNode) => void;
83
+ onLayerChange?: (layerId: string, layer: VisualliLayer) => void;
84
+ renderOverlay?: (params: {
85
+ isDark: boolean;
86
+ containerWidth: number;
87
+ containerHeight: number;
88
+ }) => React$1.ReactNode;
89
+ renderNodeContent?: (params: {
90
+ summary: string;
91
+ nodeId: string;
92
+ nodeColor?: string;
93
+ zoom: number;
94
+ }) => React$1.ReactNode;
95
+ navigationStackTop?: string;
96
+ navigationStackLeft?: string;
97
+ className?: string;
98
+ style?: React$1.CSSProperties;
99
+ }
100
+ declare function VisualliCanvas(props: VisualliCanvasProps): React$1.JSX.Element;
101
+
102
+ interface VisualliContextValue {
103
+ }
104
+ interface VisualliProviderProps {
105
+ children: React$1.ReactNode;
106
+ }
107
+ /**
108
+ * Provider for Visualli context
109
+ *
110
+ * @example
111
+ * ```tsx
112
+ * <VisualliProvider>
113
+ * <VisualliCanvas document={doc} />
114
+ * </VisualliProvider>
115
+ * ```
116
+ */
117
+ declare function VisualliProvider({ children, }: VisualliProviderProps): React$1.JSX.Element;
118
+ /**
119
+ * Hook to access Visualli context
120
+ *
121
+ * Returns default empty values if used outside of VisualliProvider
122
+ */
123
+ declare function useVisualli(): VisualliContextValue;
124
+
125
+ interface INodeStore {
126
+ nodes: NodeMap;
127
+ childrenIndex: Map<string | null, Set<string>>;
128
+ setNodes: (nodes: NodeMap) => void;
129
+ updateNode: (nodeId: string, updates: Partial<FlatNode>) => void;
130
+ updateNodePosition: (nodeId: string, x: number, y: number) => void;
131
+ addNode: (node: FlatNode) => void;
132
+ removeNode: (nodeId: string) => void;
133
+ setNodeExpanded: (nodeId: string, expanded: boolean) => void;
134
+ getNode: (nodeId: string) => FlatNode | undefined;
135
+ getChildren: (parentId: string | null) => FlatNode[];
136
+ getChildrenIds: (parentId: string | null) => Set<string>;
137
+ getAllNodes: () => FlatNode[];
138
+ }
139
+ declare const useNodeStore: zustand.UseBoundStore<zustand.StoreApi<INodeStore>>;
140
+
141
+ interface IViewportStore extends ViewportState {
142
+ canvasWidth: number;
143
+ canvasHeight: number;
144
+ pan: (deltaX: number, deltaY: number) => void;
145
+ zoom: (delta: number, pivotX?: number, pivotY?: number) => void;
146
+ setCenter: (x: number, y: number) => void;
147
+ setZoom: (level: number) => void;
148
+ updateCanvasSize: (width: number, height: number) => void;
149
+ getVisibleBounds: () => ViewportState['visibleBounds'];
150
+ worldToScreen: (x: number, y: number) => {
151
+ x: number;
152
+ y: number;
153
+ };
154
+ screenToWorld: (x: number, y: number) => {
155
+ x: number;
156
+ y: number;
157
+ };
158
+ }
159
+ declare const useViewportStore: zustand.UseBoundStore<zustand.StoreApi<IViewportStore>>;
160
+
161
+ declare const useSelectionStore: zustand.UseBoundStore<zustand.StoreApi<ISelectionStore>>;
162
+ declare const useRenderConfigStore: zustand.UseBoundStore<zustand.StoreApi<IRenderConfigStore>>;
163
+
164
+ interface UseKonvaRendererOptions {
165
+ containerRef: React.RefObject<HTMLDivElement | null>;
166
+ stageRef?: React.RefObject<Konva.Stage | null>;
167
+ enabled?: boolean;
168
+ }
169
+ interface UseKonvaRendererReturn {
170
+ handleWheel: (e: Konva.KonvaEventObject<WheelEvent>) => void;
171
+ handleMouseDown: (e: Konva.KonvaEventObject<MouseEvent>) => void;
172
+ handleMouseMove: (e: Konva.KonvaEventObject<MouseEvent>) => void;
173
+ handleMouseUp: (e: Konva.KonvaEventObject<MouseEvent>) => void;
174
+ canvasWidth: number;
175
+ canvasHeight: number;
176
+ }
177
+ declare function useKonvaRenderer({ containerRef, stageRef, enabled, }: UseKonvaRendererOptions): UseKonvaRendererReturn;
178
+
179
+ interface AnimatorViewport {
180
+ centerX: number;
181
+ centerY: number;
182
+ zoomLevel: number;
183
+ }
184
+ interface NodeBounds {
185
+ x: number;
186
+ y: number;
187
+ width: number;
188
+ height: number;
189
+ }
190
+ interface TransitionCallbacks {
191
+ onUpdate: (state: {
192
+ viewport: AnimatorViewport;
193
+ opacity: number;
194
+ backgroundColor: string;
195
+ }) => void;
196
+ onComplete: () => void;
197
+ onPhaseChange?: (phase: string) => void;
198
+ }
199
+ declare class KonvaLayerTransitionAnimator {
200
+ private rafId;
201
+ private timers;
202
+ private _active;
203
+ get active(): boolean;
204
+ cancel(): void;
205
+ animateZoomIntoLayer(current: AnimatorViewport, targetNode: NodeBounds, childCenter: {
206
+ x: number;
207
+ y: number;
208
+ }, fromColor: string, toColor: string, cb: TransitionCallbacks, targetZoom?: number, canvasWidth?: number, canvasHeight?: number): void;
209
+ animateZoomOutToParent(current: AnimatorViewport, parent: AnimatorViewport, fromColor: string, toColor: string, cb: TransitionCallbacks): void;
210
+ private _complete;
211
+ }
212
+ declare const konvaLayerTransitionAnimator: KonvaLayerTransitionAnimator;
213
+
214
+ interface LayerTransitionOptions {
215
+ /** Ref to the Konva Stage for imperative position/scale updates each frame. */
216
+ stageRef: React.RefObject<Konva.Stage | null>;
217
+ /** Ref to the wrapping div whose CSS `opacity` is driven during transitions. */
218
+ canvasWrapperRef: React.RefObject<HTMLDivElement | null>;
219
+ /** Ref to the background color div. */
220
+ bgRef?: React.RefObject<HTMLDivElement | null>;
221
+ /** Called at the content-swap point — use to switch the displayed layer. */
222
+ onSwapLayer: (newLayerId: string) => void;
223
+ /** Called when a zoom-out swap happens — use to update the nav stack. */
224
+ onSwapBack: () => void;
225
+ /** Called after the transition fully completes. */
226
+ onComplete: () => void;
227
+ /**
228
+ * GPU CSS-transform mode: called each rAF frame instead of applyViewport.
229
+ * When provided, the caller is responsible for moving the canvas content.
230
+ */
231
+ onViewportFrame?: (vp: AnimatorViewport) => void;
232
+ /**
233
+ * Called once when the animation reaches its final viewport.
234
+ * When provided, replaces the default applyViewport call at completion.
235
+ */
236
+ onViewportFinal?: (vp: AnimatorViewport) => void;
237
+ /**
238
+ * Called at phase-change points ('swap', 'zoom-back') in addition to the
239
+ * internal content-swap logic. Use to reset CSS transforms before Konva redraws.
240
+ */
241
+ onPhaseChange?: (phase: string) => void;
242
+ /**
243
+ * Called with 'start' before the animator fires its first frame and with
244
+ * 'end' after onComplete. Use to set up / tear down GPU hints (willChange, etc.).
245
+ */
246
+ onTransitionLifecycle?: (phase: 'start' | 'end') => void;
247
+ }
248
+ interface LayerTransitionHandlers {
249
+ zoomIntoLayer: (doc: VisualliDocument, targetNode: FlatNode, childLayerId: string, childNodes: FlatNode[], fromColor: string, toColor: string) => void;
250
+ zoomOutToParent: (parentViewport: AnimatorViewport, fromColor: string, toColor: string) => void;
251
+ isTransitioning: () => boolean;
252
+ }
253
+ declare function useKonvaLayerTransition(opts: LayerTransitionOptions): LayerTransitionHandlers;
254
+
255
+ declare function useViewportNodes(nodes: FlatNode[], levelFilter?: number, skipCulling?: boolean,
256
+ /** When true, skip culling so dragged nodes are always visible and the
257
+ * spatial index (keyed on layout positions) is not consulted. */
258
+ isDragging?: boolean): FlatNode[];
259
+
260
+ interface BlobPoint {
261
+ x: number;
262
+ y: number;
263
+ }
264
+ type BlobShape = ReadonlyArray<BlobPoint>;
265
+ declare function buildBlobPathData(_shape: BlobShape, cx: number, cy: number, width: number, height: number): string;
266
+
267
+ interface KonvaStageProps {
268
+ children: React$1.ReactNode;
269
+ onWheel?: (e: Konva.KonvaEventObject<WheelEvent>) => void;
270
+ onMouseDown?: (e: Konva.KonvaEventObject<MouseEvent>) => void;
271
+ onMouseMove?: (e: Konva.KonvaEventObject<MouseEvent>) => void;
272
+ onMouseUp?: (e: Konva.KonvaEventObject<MouseEvent>) => void;
273
+ onContextMenu?: (e: Konva.KonvaEventObject<MouseEvent>) => void;
274
+ onTouchStart?: (e: Konva.KonvaEventObject<TouchEvent>) => void;
275
+ onTouchMove?: (e: Konva.KonvaEventObject<TouchEvent>) => void;
276
+ onTouchEnd?: (e: Konva.KonvaEventObject<TouchEvent>) => void;
277
+ onStageReady?: (stage: Konva.Stage) => void;
278
+ }
279
+ declare const KonvaStage: React$1.ForwardRefExoticComponent<KonvaStageProps & React$1.RefAttributes<konva_lib_Stage.Stage>>;
280
+
281
+ interface KonvaNodeProps {
282
+ node: FlatNode;
283
+ zoomLevel: number;
284
+ /** True when the pointer is currently over this node (stage-level hit test). */
285
+ isExternallyHovered?: boolean;
286
+ /** True while this node is being pressed/dragged. */
287
+ isExternallyPressed?: boolean;
288
+ /** True while any node is being dragged — disables expensive shadows. */
289
+ isDragging?: boolean;
290
+ }
291
+ declare const KonvaNode: React$1.NamedExoticComponent<KonvaNodeProps>;
292
+
293
+ interface KonvaEdgeProps {
294
+ sourceNode: FlatNode;
295
+ targetNode: FlatNode;
296
+ connection?: MindMapConnection;
297
+ isDark?: boolean;
298
+ colorOverride?: string;
299
+ zoomLevel?: number;
300
+ }
301
+ declare const KonvaEdge: React$1.NamedExoticComponent<KonvaEdgeProps>;
302
+
303
+ interface KonvaNodeLayerProps {
304
+ nodes?: FlatNode[];
305
+ isTransitioning?: boolean;
306
+ isDragging?: boolean;
307
+ hoveredNodeId?: string | null;
308
+ pressedNodeId?: string | null;
309
+ }
310
+ declare function KonvaNodeLayer({ nodes: propNodes, isTransitioning, isDragging, hoveredNodeId, pressedNodeId, }: KonvaNodeLayerProps): React$1.JSX.Element;
311
+
312
+ interface KonvaEdgeLayerProps {
313
+ nodes: FlatNode[];
314
+ connections: MindMapConnection[];
315
+ currentLevel?: number;
316
+ isDark?: boolean;
317
+ /** Pass true while a node is being dragged to skip viewport culling. */
318
+ isDragging?: boolean;
319
+ }
320
+ declare function KonvaEdgeLayer({ nodes, connections, currentLevel, isDark, isDragging, }: KonvaEdgeLayerProps): React$1.JSX.Element;
321
+
322
+ interface KonvaContainerProps {
323
+ /** All nodes in this container group (already resolved from nodeMap). */
324
+ nodes: FlatNode[];
325
+ /** Label text shown on the pill badge. */
326
+ label?: string;
327
+ /** Unused – kept for API compat; stroke is always #747270. */
328
+ color?: string;
329
+ isDark?: boolean;
330
+ }
331
+ declare const KonvaContainer: React$1.NamedExoticComponent<KonvaContainerProps>;
332
+
333
+ interface ContainerGroup {
334
+ id: string;
335
+ label?: string;
336
+ nodeIds: string[];
337
+ level: number;
338
+ }
339
+ interface KonvaContainerLayerProps {
340
+ /** Layout-snapshot nodes — used only as a seed for initial IDs. */
341
+ nodes: FlatNode[];
342
+ containers: ContainerGroup[];
343
+ isDark?: boolean;
344
+ }
345
+ declare function KonvaContainerLayer({ nodes, containers, isDark }: KonvaContainerLayerProps): React$1.JSX.Element | null;
346
+
347
+ interface NavStackEntry {
348
+ layerId: string;
349
+ layer: VisualliLayer;
350
+ label: string;
351
+ }
352
+ interface NavigationStackProps {
353
+ stack: NavStackEntry[];
354
+ onNavigateBack: (index: number) => void;
355
+ isDark?: boolean;
356
+ top?: string;
357
+ left?: string;
358
+ }
359
+ declare const NavigationStack: React$1.NamedExoticComponent<NavigationStackProps>;
360
+
361
+ interface ZoomControlsProps {
362
+ isDark?: boolean;
363
+ }
364
+ declare const ZoomControls: React$1.NamedExoticComponent<ZoomControlsProps>;
365
+
366
+ interface SketchyBoxKonvaProps {
367
+ children: React$1.ReactNode;
368
+ fill: string;
369
+ stroke: string;
370
+ backStroke: string;
371
+ padding?: string;
372
+ }
373
+ declare const SketchyBoxKonva: React$1.FC<SketchyBoxKonvaProps>;
374
+
375
+ export { type AnimatorViewport, type ContainerGroup, type INodeStore, type IViewportStore, KonvaContainer, KonvaContainerLayer, KonvaEdge, KonvaEdgeLayer, type KonvaEdgeProps, KonvaLayerTransitionAnimator, KonvaNode, KonvaNodeLayer, type KonvaNodeProps, KonvaStage, type KonvaStageProps, type LayerTransitionHandlers, type NavStackEntry, NavigationStack, SketchyBoxKonva, type TransitionCallbacks, type UseKonvaRendererOptions, type UseKonvaRendererReturn, VisualliCanvas, type VisualliCanvasProps, type VisualliContextValue, VisualliProvider, type VisualliProviderProps, VisualliRenderer, type VisualliRendererProps, type VisualliTheme, ZoomControls, buildBlobPathData, konvaLayerTransitionAnimator, useKonvaLayerTransition, useKonvaRenderer, useNodeStore, useRenderConfigStore, useSelectionStore, useViewportNodes, useViewportStore, useVisualli };