@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,101 @@
1
+ // ─── Node Store ───────────────────────────────────────────────────────────────
2
+
3
+ import { create } from 'zustand';
4
+ import type { FlatNode, NodeMap } from '@visualli/core';
5
+
6
+ export interface INodeStore {
7
+ nodes: NodeMap;
8
+ childrenIndex: Map<string | null, Set<string>>;
9
+
10
+ setNodes: (nodes: NodeMap) => void;
11
+ updateNode: (nodeId: string, updates: Partial<FlatNode>) => void;
12
+ updateNodePosition: (nodeId: string, x: number, y: number) => void;
13
+ addNode: (node: FlatNode) => void;
14
+ removeNode: (nodeId: string) => void;
15
+ setNodeExpanded: (nodeId: string, expanded: boolean) => void;
16
+
17
+ getNode: (nodeId: string) => FlatNode | undefined;
18
+ getChildren: (parentId: string | null) => FlatNode[];
19
+ getChildrenIds: (parentId: string | null) => Set<string>;
20
+ getAllNodes: () => FlatNode[];
21
+ }
22
+
23
+ function buildChildrenIndex(nodes: NodeMap): Map<string | null, Set<string>> {
24
+ const index = new Map<string | null, Set<string>>();
25
+ for (const node of nodes.values()) {
26
+ const pid = node.parentId ?? null;
27
+ if (!index.has(pid)) index.set(pid, new Set());
28
+ index.get(pid)!.add(node.id);
29
+ }
30
+ return index;
31
+ }
32
+
33
+ export const useNodeStore = create<INodeStore>((set, get) => ({
34
+ nodes: new Map(),
35
+ childrenIndex: new Map(),
36
+
37
+ setNodes: (nodes) =>
38
+ set({ nodes, childrenIndex: buildChildrenIndex(nodes) }),
39
+
40
+ updateNode: (nodeId, updates) =>
41
+ set((s) => {
42
+ const node = s.nodes.get(nodeId);
43
+ if (!node) return s;
44
+ const next = new Map(s.nodes);
45
+ next.set(nodeId, { ...node, ...updates, updatedAt: new Date() });
46
+ return { nodes: next };
47
+ }),
48
+
49
+ updateNodePosition: (nodeId, x, y) =>
50
+ set((s) => {
51
+ const node = s.nodes.get(nodeId);
52
+ if (!node || (node.x === x && node.y === y)) return s;
53
+ const next = new Map(s.nodes);
54
+ next.set(nodeId, { ...node, x, y });
55
+ return { nodes: next };
56
+ }),
57
+
58
+ addNode: (node) =>
59
+ set((s) => {
60
+ const next = new Map(s.nodes);
61
+ next.set(node.id, node);
62
+ const idx = new Map(s.childrenIndex);
63
+ const pid = node.parentId ?? null;
64
+ if (!idx.has(pid)) idx.set(pid, new Set());
65
+ idx.get(pid)!.add(node.id);
66
+ return { nodes: next, childrenIndex: idx };
67
+ }),
68
+
69
+ removeNode: (nodeId) =>
70
+ set((s) => {
71
+ const node = s.nodes.get(nodeId);
72
+ if (!node) return s;
73
+ const next = new Map(s.nodes);
74
+ next.delete(nodeId);
75
+ const idx = new Map(s.childrenIndex);
76
+ const pid = node.parentId ?? null;
77
+ if (idx.has(pid)) {
78
+ const set = new Set(idx.get(pid)!);
79
+ set.delete(nodeId);
80
+ if (set.size === 0) idx.delete(pid); else idx.set(pid, set);
81
+ }
82
+ return { nodes: next, childrenIndex: idx };
83
+ }),
84
+
85
+ setNodeExpanded: (nodeId, expanded) =>
86
+ set((s) => {
87
+ const node = s.nodes.get(nodeId);
88
+ if (!node) return s;
89
+ const next = new Map(s.nodes);
90
+ next.set(nodeId, { ...node, isExpanded: expanded, updatedAt: new Date() });
91
+ return { nodes: next };
92
+ }),
93
+
94
+ getNode: (nodeId) => get().nodes.get(nodeId),
95
+ getChildren: (pid) => {
96
+ const { nodes, childrenIndex } = get();
97
+ return Array.from(childrenIndex.get(pid) ?? []).map(id => nodes.get(id)!).filter(Boolean);
98
+ },
99
+ getChildrenIds: (pid) => get().childrenIndex.get(pid) ?? new Set(),
100
+ getAllNodes: () => Array.from(get().nodes.values()),
101
+ }));
@@ -0,0 +1,95 @@
1
+ // ─── Viewport Store ───────────────────────────────────────────────────────────
2
+
3
+ import { create } from 'zustand';
4
+ import type { ViewportState } from '@visualli/core';
5
+ import {
6
+ panViewport,
7
+ zoomViewport,
8
+ setViewportCenter,
9
+ recalculateViewportBounds,
10
+ clampZoom,
11
+ DEFAULT_CANVAS_WIDTH,
12
+ DEFAULT_CANVAS_HEIGHT,
13
+ worldToScreen,
14
+ screenToWorld,
15
+ ZOOM_DEFAULT,
16
+ } from '@visualli/core';
17
+
18
+ export interface IViewportStore extends ViewportState {
19
+ canvasWidth: number;
20
+ canvasHeight: number;
21
+
22
+ pan: (deltaX: number, deltaY: number) => void;
23
+ zoom: (delta: number, pivotX?: number, pivotY?: number) => void;
24
+ setCenter: (x: number, y: number) => void;
25
+ setZoom: (level: number) => void;
26
+ updateCanvasSize: (width: number, height: number) => void;
27
+
28
+ getVisibleBounds: () => ViewportState['visibleBounds'];
29
+ worldToScreen: (x: number, y: number) => { x: number; y: number };
30
+ screenToWorld: (x: number, y: number) => { x: number; y: number };
31
+ }
32
+
33
+ function makeInitial(w: number, h: number): ViewportState {
34
+ const vp: ViewportState = {
35
+ centerX: 0, centerY: 0,
36
+ zoomLevel: ZOOM_DEFAULT,
37
+ rotation: 0,
38
+ visibleBounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },
39
+ };
40
+ return recalculateViewportBounds(vp, w, h);
41
+ }
42
+
43
+ const getVP = (s: IViewportStore): ViewportState => ({
44
+ centerX: s.centerX, centerY: s.centerY,
45
+ zoomLevel: s.zoomLevel, rotation: s.rotation,
46
+ visibleBounds: s.visibleBounds,
47
+ });
48
+
49
+ export const useViewportStore = create<IViewportStore>((set, get) => {
50
+ const init = makeInitial(DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT);
51
+ return {
52
+ ...init,
53
+ canvasWidth: DEFAULT_CANVAS_WIDTH,
54
+ canvasHeight: DEFAULT_CANVAS_HEIGHT,
55
+
56
+ pan: (dx, dy) => set((s) => {
57
+ const updated = recalculateViewportBounds(panViewport(dx, dy, getVP(s)), s.canvasWidth, s.canvasHeight);
58
+ return { centerX: updated.centerX, centerY: updated.centerY, visibleBounds: updated.visibleBounds };
59
+ }),
60
+
61
+ zoom: (delta, pivotX, pivotY) => set((s) => {
62
+ const updated = recalculateViewportBounds(
63
+ zoomViewport(delta, getVP(s), pivotX, pivotY, s.canvasWidth, s.canvasHeight),
64
+ s.canvasWidth, s.canvasHeight,
65
+ );
66
+ return { centerX: updated.centerX, centerY: updated.centerY, zoomLevel: updated.zoomLevel, visibleBounds: updated.visibleBounds };
67
+ }),
68
+
69
+ setCenter: (x, y) => set((s) => {
70
+ const updated = recalculateViewportBounds(setViewportCenter(x, y, getVP(s)), s.canvasWidth, s.canvasHeight);
71
+ return { centerX: updated.centerX, centerY: updated.centerY, visibleBounds: updated.visibleBounds };
72
+ }),
73
+
74
+ setZoom: (level) => set((s) => {
75
+ const clamped = clampZoom(level);
76
+ const updated = recalculateViewportBounds({ ...getVP(s), zoomLevel: clamped }, s.canvasWidth, s.canvasHeight);
77
+ return { zoomLevel: updated.zoomLevel, visibleBounds: updated.visibleBounds };
78
+ }),
79
+
80
+ updateCanvasSize: (w, h) => set((s) => {
81
+ const updated = recalculateViewportBounds(getVP(s), w, h);
82
+ return { canvasWidth: w, canvasHeight: h, visibleBounds: updated.visibleBounds };
83
+ }),
84
+
85
+ getVisibleBounds: () => get().visibleBounds,
86
+ worldToScreen: (x, y) => {
87
+ const s = get();
88
+ return worldToScreen(x, y, getVP(s), s.canvasWidth, s.canvasHeight);
89
+ },
90
+ screenToWorld: (x, y) => {
91
+ const s = get();
92
+ return screenToWorld(x, y, getVP(s), s.canvasWidth, s.canvasHeight);
93
+ },
94
+ };
95
+ });
@@ -0,0 +1 @@
1
+ export * from './layerNavigation';
@@ -0,0 +1,15 @@
1
+ // ─── Layer Navigation Utilities ──────────────────────────────────────────────
2
+ //
3
+ // React-specific re-exports or wrappers of core navigation logic.
4
+
5
+ export {
6
+ getConnectionsForLayer,
7
+ getChildLayerForNode,
8
+ getLayerForNavigation,
9
+ calculateFitZoom,
10
+ calculateFitCenter,
11
+ getContainersForLayer,
12
+ calculateFitView,
13
+ } from '@visualli/core';
14
+
15
+ export type { FitResult, ContainerGroupInfo } from '@visualli/core';