@stuly/anode-react 0.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.
package/dist/hooks.js ADDED
@@ -0,0 +1,156 @@
1
+ import { useAnode, useViewport } from "./context.js";
2
+ import React, { useMemo, useSyncExternalStore } from "react";
3
+ import { Entity, Link, Rect } from "@stuly/anode";
4
+
5
+ //#region src/hooks.ts
6
+ const useNodes = () => {
7
+ const ctx = useAnode();
8
+ const store = useMemo(() => {
9
+ let snapshot = Array.from(ctx.entities.values());
10
+ return {
11
+ subscribe: (onStoreChange) => {
12
+ snapshot = Array.from(ctx.entities.values());
13
+ const update = () => {
14
+ snapshot = Array.from(ctx.entities.values());
15
+ onStoreChange();
16
+ };
17
+ const handles = [
18
+ ctx.registerEntityCreateListener(update),
19
+ ctx.registerEntityDropListener(update),
20
+ ctx.registerEntityMoveListener(update),
21
+ ctx.registerBulkChangeListener(update)
22
+ ];
23
+ return () => handles.forEach((h) => ctx.unregisterListener(h));
24
+ },
25
+ getSnapshot: () => snapshot
26
+ };
27
+ }, [ctx]);
28
+ return useSyncExternalStore(store.subscribe, store.getSnapshot);
29
+ };
30
+ const useVisibleNodes = (containerRect) => {
31
+ const ctx = useAnode();
32
+ const { viewport } = useViewport();
33
+ return useMemo(() => {
34
+ const w = (containerRect?.width || window.innerWidth) / viewport.k;
35
+ const h = (containerRect?.height || window.innerHeight) / viewport.k;
36
+ const x = -viewport.x / viewport.k;
37
+ const y = -viewport.y / viewport.k;
38
+ const padding = 300 / viewport.k;
39
+ const queryRect = new Rect(x - padding, y - padding, w + padding * 2, h + padding * 2);
40
+ return Array.from(new Set(ctx.quadTree.query(queryRect))).map((id) => ctx.entities.get(id)).filter((n) => !!n);
41
+ }, [
42
+ ctx,
43
+ viewport,
44
+ containerRect,
45
+ useNodes()
46
+ ]);
47
+ };
48
+ const useEdges = () => {
49
+ const ctx = useAnode();
50
+ const store = useMemo(() => {
51
+ let snapshot = Array.from(ctx.links.values());
52
+ return {
53
+ subscribe: (onStoreChange) => {
54
+ snapshot = Array.from(ctx.links.values());
55
+ const update = () => {
56
+ snapshot = Array.from(ctx.links.values());
57
+ onStoreChange();
58
+ };
59
+ const handles = [
60
+ ctx.registerLinkCreateListener(update),
61
+ ctx.registerLinkDropListener(update),
62
+ ctx.registerEntityMoveListener(update),
63
+ ctx.registerBulkChangeListener(update)
64
+ ];
65
+ return () => handles.forEach((h) => ctx.unregisterListener(h));
66
+ },
67
+ getSnapshot: () => snapshot
68
+ };
69
+ }, [ctx]);
70
+ return useSyncExternalStore(store.subscribe, store.getSnapshot);
71
+ };
72
+ const useEntitySockets = (entityId) => {
73
+ const ctx = useAnode();
74
+ const store = useMemo(() => {
75
+ let snapshot = [];
76
+ const entity = ctx.entities.get(entityId);
77
+ if (entity) snapshot = Array.from(entity.sockets.values());
78
+ return {
79
+ subscribe: (onStoreChange) => {
80
+ const update = () => {
81
+ const e = ctx.entities.get(entityId);
82
+ snapshot = e ? Array.from(e.sockets.values()) : [];
83
+ onStoreChange();
84
+ };
85
+ const h1 = ctx.registerSocketCreateListener((s) => {
86
+ if (s.entityId === entityId) update();
87
+ });
88
+ const h2 = ctx.registerSocketDropListener((s) => {
89
+ if (s.entityId === entityId) update();
90
+ });
91
+ const h3 = ctx.registerSocketMoveListener((s) => {
92
+ if (s.entityId === entityId) update();
93
+ });
94
+ const h4 = ctx.registerBulkChangeListener(update);
95
+ return () => {
96
+ ctx.unregisterListener(h1);
97
+ ctx.unregisterListener(h2);
98
+ ctx.unregisterListener(h3);
99
+ ctx.unregisterListener(h4);
100
+ };
101
+ },
102
+ getSnapshot: () => snapshot
103
+ };
104
+ }, [ctx, entityId]);
105
+ return useSyncExternalStore(store.subscribe, store.getSnapshot);
106
+ };
107
+ function useSocketValue(socketId) {
108
+ const ctx = useAnode();
109
+ const store = useMemo(() => {
110
+ return {
111
+ subscribe: (onStoreChange) => {
112
+ if (socketId === null) return () => {};
113
+ const handle = ctx.registerSocketValueListener((s) => {
114
+ if (s.id === socketId) onStoreChange();
115
+ });
116
+ const bulkHandle = ctx.registerBulkChangeListener(onStoreChange);
117
+ return () => {
118
+ ctx.unregisterListener(handle);
119
+ ctx.unregisterListener(bulkHandle);
120
+ };
121
+ },
122
+ getSnapshot: () => {
123
+ if (socketId === null) return null;
124
+ return ctx.sockets.get(socketId)?.value ?? null;
125
+ }
126
+ };
127
+ }, [ctx, socketId]);
128
+ return useSyncExternalStore(store.subscribe, store.getSnapshot);
129
+ }
130
+ const useGroups = () => {
131
+ const ctx = useAnode();
132
+ const store = useMemo(() => {
133
+ let snapshot = Array.from(ctx.groups.values());
134
+ return {
135
+ subscribe: (onStoreChange) => {
136
+ snapshot = Array.from(ctx.groups.values());
137
+ const update = () => {
138
+ snapshot = Array.from(ctx.groups.values());
139
+ onStoreChange();
140
+ };
141
+ const handles = [
142
+ ctx.registerGroupCreateListener(update),
143
+ ctx.registerGroupDropListener(update),
144
+ ctx.registerEntityMoveListener(update),
145
+ ctx.registerBulkChangeListener(update)
146
+ ];
147
+ return () => handles.forEach((h) => ctx.unregisterListener(h));
148
+ },
149
+ getSnapshot: () => snapshot
150
+ };
151
+ }, [ctx]);
152
+ return useSyncExternalStore(store.subscribe, store.getSnapshot);
153
+ };
154
+
155
+ //#endregion
156
+ export { useEdges, useEntitySockets, useGroups, useNodes, useSocketValue, useVisibleNodes };
@@ -0,0 +1,12 @@
1
+ import { Node, NodeComponentProps } from "./elements/Node.js";
2
+ import { Link, LinkComponentProps } from "./elements/Link.js";
3
+ import { LinkData, NodeData, World } from "./elements/World.js";
4
+ import { Group, GroupProps } from "./elements/Group.js";
5
+ import { Socket } from "./elements/Socket.js";
6
+ import { Background } from "./elements/Background.js";
7
+ import { MiniMap } from "./elements/MiniMap.js";
8
+ import { Controls } from "./elements/Controls.js";
9
+ import { Panel } from "./elements/Panel.js";
10
+ import { AnodeProvider, useAnode, useSelection, useViewport } from "./context.js";
11
+ import { useEdges, useEntitySockets, useGroups, useNodes, useSocketValue, useVisibleNodes } from "./hooks.js";
12
+ export { AnodeProvider, Background, Controls, Group, type GroupProps, Link, type LinkComponentProps, type LinkData, MiniMap, Node, type NodeComponentProps, type NodeData, Panel, Socket, World, useAnode, useEdges, useEntitySockets, useGroups, useNodes, useSelection, useSocketValue, useViewport, useVisibleNodes };
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ import { AnodeProvider, useAnode, useSelection, useViewport } from "./context.js";
2
+ import { useEdges, useEntitySockets, useGroups, useNodes, useSocketValue, useVisibleNodes } from "./hooks.js";
3
+ import { Node } from "./elements/Node.js";
4
+ import { Group } from "./elements/Group.js";
5
+ import { Link } from "./elements/Link.js";
6
+ import { World } from "./elements/World.js";
7
+ import { Socket } from "./elements/Socket.js";
8
+ import { Background } from "./elements/Background.js";
9
+ import { MiniMap } from "./elements/MiniMap.js";
10
+ import { Controls } from "./elements/Controls.js";
11
+ import { Panel } from "./elements/Panel.js";
12
+
13
+ export { AnodeProvider, Background, Controls, Group, Link, MiniMap, Node, Panel, Socket, World, useAnode, useEdges, useEntitySockets, useGroups, useNodes, useSelection, useSocketValue, useViewport, useVisibleNodes };
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@stuly/anode-react",
3
+ "version": "0.1.0",
4
+ "description": "Anode react wrapper",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "keywords": [],
15
+ "author": "",
16
+ "type": "module",
17
+ "license": "MIT",
18
+ "peerDependencies": {
19
+ "react": "^19.2.4",
20
+ "@stuly/anode": "^0.1.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^19.2.14",
24
+ "tsdown": "^0.20.3",
25
+ "typescript": "^5.9.3"
26
+ },
27
+ "scripts": {
28
+ "build": "pnpm tsdown",
29
+ "lint": "tsc"
30
+ }
31
+ }