@fieldnotes/react 0.14.0 → 0.14.2
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 +1 -1
- package/dist/index.cjs +84 -352
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -8
- package/dist/index.d.ts +3 -8
- package/dist/index.js +120 -388
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ React bindings for the [Field Notes](https://github.com/IrakliDevelop/fieldnotes
|
|
|
8
8
|
npm install @fieldnotes/core @fieldnotes/react
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Requires React 18+.
|
|
11
|
+
Requires React 18+. Using Vue or Svelte? See [`@fieldnotes/vue`](../vue) and [`@fieldnotes/svelte`](../svelte).
|
|
12
12
|
|
|
13
13
|
## Quick Start
|
|
14
14
|
|
package/dist/index.cjs
CHANGED
|
@@ -67,74 +67,37 @@ var FieldNotesCanvas = (0, import_react2.forwardRef)(
|
|
|
67
67
|
}, ref) {
|
|
68
68
|
const containerRef = (0, import_react2.useRef)(null);
|
|
69
69
|
const [viewport, setViewport] = (0, import_react2.useState)(null);
|
|
70
|
+
const mountedRef = (0, import_react2.useRef)(null);
|
|
70
71
|
const changeRef = (0, import_react2.useRef)({ onChange, changeDebounceMs });
|
|
71
72
|
changeRef.current = { onChange, changeDebounceMs };
|
|
72
73
|
(0, import_react2.useImperativeHandle)(ref, () => ({ viewport }), [viewport]);
|
|
73
74
|
(0, import_react2.useEffect)(() => {
|
|
74
75
|
const el = containerRef.current;
|
|
75
76
|
if (!el) return;
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
else if (initialData) vp.loadState(initialData);
|
|
89
|
-
} catch (error) {
|
|
90
|
-
vp.destroy();
|
|
91
|
-
throw error;
|
|
92
|
-
}
|
|
93
|
-
let timer;
|
|
94
|
-
const emit = () => {
|
|
95
|
-
timer = void 0;
|
|
96
|
-
changeRef.current.onChange?.(vp.exportState());
|
|
97
|
-
};
|
|
98
|
-
const unsubscribe = vp.onStateChange(() => {
|
|
99
|
-
clearTimeout(timer);
|
|
100
|
-
timer = setTimeout(emit, changeRef.current.changeDebounceMs);
|
|
101
|
-
});
|
|
102
|
-
try {
|
|
103
|
-
onReady?.(vp);
|
|
104
|
-
if (deferDefaults) {
|
|
105
|
-
const registerDefaults = vp.toolManager.toolNames.length === 0;
|
|
106
|
-
if (registerDefaults) {
|
|
107
|
-
for (const t of (0, import_core.createDefaultTools)()) vp.toolManager.register(t);
|
|
108
|
-
}
|
|
109
|
-
activate(registerDefaults ? "select" : void 0);
|
|
77
|
+
const mounted = (0, import_core.mountViewport)(el, {
|
|
78
|
+
options,
|
|
79
|
+
tools,
|
|
80
|
+
initialData,
|
|
81
|
+
tool,
|
|
82
|
+
defaultTool,
|
|
83
|
+
onReady,
|
|
84
|
+
get onChange() {
|
|
85
|
+
return changeRef.current.onChange;
|
|
86
|
+
},
|
|
87
|
+
get changeDebounceMs() {
|
|
88
|
+
return changeRef.current.changeDebounceMs;
|
|
110
89
|
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
vp.destroy();
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
setViewport(vp);
|
|
90
|
+
});
|
|
91
|
+
mountedRef.current = mounted;
|
|
92
|
+
setViewport(mounted.viewport);
|
|
118
93
|
return () => {
|
|
119
|
-
|
|
120
|
-
clearTimeout(timer);
|
|
121
|
-
try {
|
|
122
|
-
emit();
|
|
123
|
-
} catch (error) {
|
|
124
|
-
if (typeof reportError === "function") reportError(error);
|
|
125
|
-
else console.error("[fieldnotes] onChange failed during unmount", error);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
unsubscribe();
|
|
129
|
-
vp.destroy();
|
|
94
|
+
mounted.destroy();
|
|
130
95
|
setViewport(null);
|
|
131
96
|
};
|
|
132
97
|
}, []);
|
|
133
98
|
(0, import_react2.useEffect)(() => {
|
|
134
99
|
if (!viewport || tool === void 0) return;
|
|
135
|
-
|
|
136
|
-
viewport.setTool(tool);
|
|
137
|
-
}
|
|
100
|
+
mountedRef.current?.setTool(tool);
|
|
138
101
|
}, [viewport, tool]);
|
|
139
102
|
(0, import_react2.useEffect)(() => {
|
|
140
103
|
if (!viewport || !onToolChange) return;
|
|
@@ -146,11 +109,7 @@ var FieldNotesCanvas = (0, import_react2.forwardRef)(
|
|
|
146
109
|
}, [viewport, snapToGrid]);
|
|
147
110
|
(0, import_react2.useEffect)(() => {
|
|
148
111
|
if (!viewport || !Array.isArray(tools)) return;
|
|
149
|
-
|
|
150
|
-
if (!viewport.toolManager.getTool(t.name)) {
|
|
151
|
-
viewport.toolManager.register(t);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
112
|
+
mountedRef.current?.addTools(tools);
|
|
154
113
|
}, [viewport, tools]);
|
|
155
114
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: containerRef, className, style, children: viewport && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewportContext.Provider, { value: viewport, children }) });
|
|
156
115
|
}
|
|
@@ -159,6 +118,7 @@ var FieldNotesCanvas = (0, import_react2.forwardRef)(
|
|
|
159
118
|
// src/canvas-element.tsx
|
|
160
119
|
var import_react4 = require("react");
|
|
161
120
|
var import_react_dom = require("react-dom");
|
|
121
|
+
var import_core2 = require("@fieldnotes/core");
|
|
162
122
|
|
|
163
123
|
// src/hooks/use-viewport.ts
|
|
164
124
|
var import_react3 = require("react");
|
|
@@ -171,11 +131,10 @@ function useViewport() {
|
|
|
171
131
|
}
|
|
172
132
|
|
|
173
133
|
// src/canvas-element.tsx
|
|
174
|
-
var HOST_ORIGIN = "host";
|
|
175
134
|
function CanvasElement({ position, size, children }) {
|
|
176
135
|
const viewport = useViewport();
|
|
177
136
|
const [portalTarget, setPortalTarget] = (0, import_react4.useState)(null);
|
|
178
|
-
const
|
|
137
|
+
const hostRef = (0, import_react4.useRef)(null);
|
|
179
138
|
const geometryRef = (0, import_react4.useRef)({ position, size });
|
|
180
139
|
(0, import_react4.useEffect)(() => {
|
|
181
140
|
const container = document.createElement("div");
|
|
@@ -183,51 +142,17 @@ function CanvasElement({ position, size, children }) {
|
|
|
183
142
|
width: "100%",
|
|
184
143
|
height: "100%"
|
|
185
144
|
});
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
const { position: current, size: currentSize } = geometryRef.current;
|
|
189
|
-
return viewport.addHtmlElement(container, current, currentSize, {
|
|
190
|
-
transient: true,
|
|
191
|
-
origin: HOST_ORIGIN
|
|
192
|
-
});
|
|
193
|
-
};
|
|
194
|
-
elementIdRef.current = register();
|
|
145
|
+
const host = (0, import_core2.attachHostElement)(viewport, container, geometryRef.current);
|
|
146
|
+
hostRef.current = host;
|
|
195
147
|
setPortalTarget(container);
|
|
196
|
-
let disposed = false;
|
|
197
|
-
const handleStoreReplacement = () => {
|
|
198
|
-
const clearedId = elementIdRef.current;
|
|
199
|
-
if (clearedId === null) return;
|
|
200
|
-
queueMicrotask(() => {
|
|
201
|
-
if (disposed || elementIdRef.current !== clearedId) return;
|
|
202
|
-
if (viewport.store.getById(clearedId)?.type === "html") {
|
|
203
|
-
viewport.updateHtmlElement(clearedId, container);
|
|
204
|
-
} else {
|
|
205
|
-
elementIdRef.current = register();
|
|
206
|
-
}
|
|
207
|
-
viewport.requestRender();
|
|
208
|
-
});
|
|
209
|
-
};
|
|
210
|
-
const unsubscribeClear = viewport.store.on("clear", handleStoreReplacement);
|
|
211
|
-
const unsubscribeBatch = viewport.store.on("batch", handleStoreReplacement);
|
|
212
148
|
return () => {
|
|
213
|
-
|
|
214
|
-
unsubscribeClear();
|
|
215
|
-
unsubscribeBatch();
|
|
216
|
-
if (elementIdRef.current) {
|
|
217
|
-
viewport.store.remove(elementIdRef.current, { origin: HOST_ORIGIN });
|
|
218
|
-
viewport.requestRender();
|
|
219
|
-
elementIdRef.current = null;
|
|
220
|
-
}
|
|
221
|
-
if (container.parentNode === viewport.domLayer) container.remove();
|
|
149
|
+
host.destroy();
|
|
222
150
|
setPortalTarget(null);
|
|
223
151
|
};
|
|
224
152
|
}, [viewport]);
|
|
225
153
|
(0, import_react4.useEffect)(() => {
|
|
226
154
|
geometryRef.current = { position, size };
|
|
227
|
-
|
|
228
|
-
if (!id) return;
|
|
229
|
-
viewport.store.update(id, { position, ...size ? { size } : {} }, { origin: HOST_ORIGIN });
|
|
230
|
-
viewport.requestRender();
|
|
155
|
+
hostRef.current?.setGeometry({ position, size });
|
|
231
156
|
}, [viewport, position.x, position.y, size?.w, size?.h]);
|
|
232
157
|
if (!portalTarget) return null;
|
|
233
158
|
return (0, import_react_dom.createPortal)(children, portalTarget);
|
|
@@ -235,65 +160,40 @@ function CanvasElement({ position, size, children }) {
|
|
|
235
160
|
|
|
236
161
|
// src/hooks/use-active-tool.ts
|
|
237
162
|
var import_react5 = require("react");
|
|
163
|
+
var import_core3 = require("@fieldnotes/core");
|
|
238
164
|
function useActiveTool() {
|
|
239
165
|
const viewport = useViewport();
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
[viewport]
|
|
243
|
-
);
|
|
244
|
-
const getSnapshot = (0, import_react5.useCallback)(() => viewport.toolManager.activeTool?.name ?? "", [viewport]);
|
|
245
|
-
const toolName = (0, import_react5.useSyncExternalStore)(subscribe, getSnapshot);
|
|
166
|
+
const source = (0, import_react5.useMemo)(() => (0, import_core3.createActiveToolSource)(viewport), [viewport]);
|
|
167
|
+
const toolName = (0, import_react5.useSyncExternalStore)(source.subscribe, source.getSnapshot);
|
|
246
168
|
const setTool = (0, import_react5.useCallback)((name) => viewport.setTool(name), [viewport]);
|
|
247
169
|
return [toolName, setTool];
|
|
248
170
|
}
|
|
249
171
|
|
|
250
172
|
// src/hooks/use-camera.ts
|
|
251
173
|
var import_react6 = require("react");
|
|
174
|
+
var import_core4 = require("@fieldnotes/core");
|
|
252
175
|
function useCamera() {
|
|
253
176
|
const viewport = useViewport();
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
(onStoreChange) => viewport.camera.onChange(onStoreChange),
|
|
257
|
-
[viewport]
|
|
258
|
-
);
|
|
259
|
-
const getSnapshot = (0, import_react6.useCallback)(() => {
|
|
260
|
-
const { position, zoom } = viewport.camera;
|
|
261
|
-
const cached = cachedRef.current;
|
|
262
|
-
if (cached.x === position.x && cached.y === position.y && cached.zoom === zoom) {
|
|
263
|
-
return cached;
|
|
264
|
-
}
|
|
265
|
-
const next = { x: position.x, y: position.y, zoom };
|
|
266
|
-
cachedRef.current = next;
|
|
267
|
-
return next;
|
|
268
|
-
}, [viewport]);
|
|
269
|
-
return (0, import_react6.useSyncExternalStore)(subscribe, getSnapshot);
|
|
177
|
+
const source = (0, import_react6.useMemo)(() => (0, import_core4.createCameraSource)(viewport), [viewport]);
|
|
178
|
+
return (0, import_react6.useSyncExternalStore)(source.subscribe, source.getSnapshot);
|
|
270
179
|
}
|
|
271
180
|
|
|
272
181
|
// src/hooks/use-tool-options.ts
|
|
273
182
|
var import_react7 = require("react");
|
|
274
|
-
|
|
275
|
-
}
|
|
183
|
+
var import_core5 = require("@fieldnotes/core");
|
|
276
184
|
function useToolOptions(toolName) {
|
|
277
185
|
const viewport = useViewport();
|
|
278
|
-
const
|
|
186
|
+
const nameRef = (0, import_react7.useRef)(toolName);
|
|
187
|
+
nameRef.current = toolName;
|
|
188
|
+
const source = (0, import_react7.useMemo)(
|
|
189
|
+
() => (0, import_core5.createToolOptionsSource)(viewport, () => nameRef.current),
|
|
190
|
+
[viewport]
|
|
191
|
+
);
|
|
279
192
|
const subscribe = (0, import_react7.useCallback)(
|
|
280
|
-
(onStoreChange) =>
|
|
281
|
-
|
|
282
|
-
if (!tool?.onOptionsChange) return noop;
|
|
283
|
-
return tool.onOptionsChange(onStoreChange);
|
|
284
|
-
},
|
|
285
|
-
[viewport, toolName]
|
|
193
|
+
(onStoreChange) => source.subscribe(onStoreChange),
|
|
194
|
+
[source, toolName]
|
|
286
195
|
);
|
|
287
|
-
const
|
|
288
|
-
const tool = viewport.toolManager.getTool(toolName);
|
|
289
|
-
if (!tool?.getOptions) return null;
|
|
290
|
-
const next = tool.getOptions();
|
|
291
|
-
const cached = cachedRef.current;
|
|
292
|
-
if (cached && shallowEqual(cached, next)) return cached;
|
|
293
|
-
cachedRef.current = next;
|
|
294
|
-
return next;
|
|
295
|
-
}, [viewport, toolName]);
|
|
296
|
-
const options = (0, import_react7.useSyncExternalStore)(subscribe, getSnapshot);
|
|
196
|
+
const options = (0, import_react7.useSyncExternalStore)(subscribe, source.getSnapshot);
|
|
297
197
|
const setOptions = (0, import_react7.useCallback)(
|
|
298
198
|
(partial) => {
|
|
299
199
|
const tool = viewport.toolManager.getTool(toolName);
|
|
@@ -303,51 +203,14 @@ function useToolOptions(toolName) {
|
|
|
303
203
|
);
|
|
304
204
|
return [options, setOptions];
|
|
305
205
|
}
|
|
306
|
-
function shallowEqual(a, b) {
|
|
307
|
-
const keysA = Object.keys(a);
|
|
308
|
-
const keysB = Object.keys(b);
|
|
309
|
-
if (keysA.length !== keysB.length) return false;
|
|
310
|
-
for (const key of keysA) {
|
|
311
|
-
const valA = a[key];
|
|
312
|
-
const valB = b[key];
|
|
313
|
-
if (valA === valB) continue;
|
|
314
|
-
if (typeof valA === "object" && valA !== null && typeof valB === "object" && valB !== null && !Array.isArray(valA) && !Array.isArray(valB)) {
|
|
315
|
-
const objA = valA;
|
|
316
|
-
const objB = valB;
|
|
317
|
-
const innerKeys = Object.keys(objA);
|
|
318
|
-
if (innerKeys.length === Object.keys(objB).length && innerKeys.every((k) => objA[k] === objB[k])) {
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return false;
|
|
323
|
-
}
|
|
324
|
-
return true;
|
|
325
|
-
}
|
|
326
206
|
|
|
327
207
|
// src/hooks/use-layers.ts
|
|
328
208
|
var import_react8 = require("react");
|
|
209
|
+
var import_core6 = require("@fieldnotes/core");
|
|
329
210
|
function useLayers() {
|
|
330
211
|
const viewport = useViewport();
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
(onStoreChange) => viewport.layerManager.on("change", onStoreChange),
|
|
334
|
-
[viewport]
|
|
335
|
-
);
|
|
336
|
-
const getSnapshot = (0, import_react8.useCallback)(() => {
|
|
337
|
-
const layers = viewport.layerManager.getLayers();
|
|
338
|
-
const activeLayerId = viewport.layerManager.activeLayerId;
|
|
339
|
-
const cached = cachedRef.current;
|
|
340
|
-
if (cached.activeLayerId === activeLayerId && cached.layers.length === layers.length && cached.layers.every((l, i) => {
|
|
341
|
-
const next2 = layers[i];
|
|
342
|
-
return next2 !== void 0 && l.id === next2.id && l.name === next2.name && l.visible === next2.visible && l.locked === next2.locked && l.order === next2.order && l.opacity === next2.opacity;
|
|
343
|
-
})) {
|
|
344
|
-
return cached;
|
|
345
|
-
}
|
|
346
|
-
const next = { layers, activeLayerId };
|
|
347
|
-
cachedRef.current = next;
|
|
348
|
-
return next;
|
|
349
|
-
}, [viewport]);
|
|
350
|
-
const snapshot = (0, import_react8.useSyncExternalStore)(subscribe, getSnapshot);
|
|
212
|
+
const source = (0, import_react8.useMemo)(() => (0, import_core6.createLayersSource)(viewport), [viewport]);
|
|
213
|
+
const snapshot = (0, import_react8.useSyncExternalStore)(source.subscribe, source.getSnapshot);
|
|
351
214
|
const createLayer = (0, import_react8.useCallback)(
|
|
352
215
|
(name) => viewport.layerManager.createLayer(name),
|
|
353
216
|
[viewport]
|
|
@@ -401,25 +264,11 @@ function useLayers() {
|
|
|
401
264
|
|
|
402
265
|
// src/hooks/use-history.ts
|
|
403
266
|
var import_react9 = require("react");
|
|
267
|
+
var import_core7 = require("@fieldnotes/core");
|
|
404
268
|
function useHistory() {
|
|
405
269
|
const viewport = useViewport();
|
|
406
|
-
const
|
|
407
|
-
const
|
|
408
|
-
(onStoreChange) => viewport.history.onChange(onStoreChange),
|
|
409
|
-
[viewport]
|
|
410
|
-
);
|
|
411
|
-
const getSnapshot = (0, import_react9.useCallback)(() => {
|
|
412
|
-
const canUndo = viewport.history.canUndo;
|
|
413
|
-
const canRedo = viewport.history.canRedo;
|
|
414
|
-
const cached = cachedRef.current;
|
|
415
|
-
if (cached.canUndo === canUndo && cached.canRedo === canRedo) {
|
|
416
|
-
return cached;
|
|
417
|
-
}
|
|
418
|
-
const next = { canUndo, canRedo };
|
|
419
|
-
cachedRef.current = next;
|
|
420
|
-
return next;
|
|
421
|
-
}, [viewport]);
|
|
422
|
-
const snapshot = (0, import_react9.useSyncExternalStore)(subscribe, getSnapshot);
|
|
270
|
+
const source = (0, import_react9.useMemo)(() => (0, import_core7.createHistorySource)(viewport), [viewport]);
|
|
271
|
+
const snapshot = (0, import_react9.useSyncExternalStore)(source.subscribe, source.getSnapshot);
|
|
423
272
|
const undo = (0, import_react9.useCallback)(() => viewport.undo(), [viewport]);
|
|
424
273
|
const redo = (0, import_react9.useCallback)(() => viewport.redo(), [viewport]);
|
|
425
274
|
return {
|
|
@@ -432,186 +281,69 @@ function useHistory() {
|
|
|
432
281
|
|
|
433
282
|
// src/hooks/use-elements.ts
|
|
434
283
|
var import_react10 = require("react");
|
|
435
|
-
|
|
436
|
-
if (Object.is(a, b)) return true;
|
|
437
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
438
|
-
return a.length === b.length && a.every((v, i) => Object.is(v, b[i]));
|
|
439
|
-
}
|
|
440
|
-
if (typeof a === "object" && a !== null && typeof b === "object" && b !== null && !Array.isArray(a) && !Array.isArray(b)) {
|
|
441
|
-
const ka = Object.keys(a);
|
|
442
|
-
const kb = Object.keys(b);
|
|
443
|
-
return ka.length === kb.length && ka.every(
|
|
444
|
-
(k) => k in b && Object.is(a[k], b[k])
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
return false;
|
|
448
|
-
}
|
|
284
|
+
var import_core8 = require("@fieldnotes/core");
|
|
449
285
|
function useElements(arg, isEqual) {
|
|
450
286
|
const viewport = useViewport();
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
const
|
|
454
|
-
(
|
|
287
|
+
const latest = (0, import_react10.useRef)({ arg, isEqual });
|
|
288
|
+
latest.current = { arg, isEqual };
|
|
289
|
+
const elementsSource = (0, import_react10.useMemo)(
|
|
290
|
+
() => (0, import_core8.createElementsSource)(viewport, () => {
|
|
291
|
+
const a = latest.current.arg;
|
|
292
|
+
return typeof a === "function" ? void 0 : a;
|
|
293
|
+
}),
|
|
455
294
|
[viewport]
|
|
456
295
|
);
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const next = arg ? viewport.store.getElementsByType(arg) : viewport.store.getAll();
|
|
469
|
-
const cached = cachedRef.current;
|
|
470
|
-
if (cached.length === next.length && cached.every((el, i) => el === next[i])) {
|
|
471
|
-
return cached;
|
|
472
|
-
}
|
|
473
|
-
cachedRef.current = next;
|
|
474
|
-
return next;
|
|
475
|
-
}, [viewport, arg, isEqual]);
|
|
476
|
-
return (0, import_react10.useSyncExternalStore)(subscribe, getSnapshot);
|
|
296
|
+
const selectorSource = (0, import_react10.useMemo)(
|
|
297
|
+
() => (0, import_core8.createElementsSelectorSource)(
|
|
298
|
+
viewport,
|
|
299
|
+
// Only read while `arg` is a selector: the source is picked below by `typeof arg`.
|
|
300
|
+
() => latest.current.arg,
|
|
301
|
+
() => latest.current.isEqual
|
|
302
|
+
),
|
|
303
|
+
[viewport]
|
|
304
|
+
);
|
|
305
|
+
const source = typeof arg === "function" ? selectorSource : elementsSource;
|
|
306
|
+
return (0, import_react10.useSyncExternalStore)(source.subscribe, source.getSnapshot);
|
|
477
307
|
}
|
|
478
308
|
|
|
479
309
|
// src/hooks/use-selection.ts
|
|
480
310
|
var import_react11 = require("react");
|
|
311
|
+
var import_core9 = require("@fieldnotes/core");
|
|
481
312
|
function useSelection() {
|
|
482
313
|
const viewport = useViewport();
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
[viewport]
|
|
486
|
-
);
|
|
487
|
-
const getSnapshot = (0, import_react11.useCallback)(() => viewport.getSelectedIds(), [viewport]);
|
|
488
|
-
return (0, import_react11.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
314
|
+
const source = (0, import_react11.useMemo)(() => (0, import_core9.createSelectionSource)(viewport), [viewport]);
|
|
315
|
+
return (0, import_react11.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot);
|
|
489
316
|
}
|
|
490
317
|
|
|
491
318
|
// src/hooks/use-selection-style.ts
|
|
492
319
|
var import_react12 = require("react");
|
|
493
|
-
|
|
494
|
-
// src/hooks/style-keys.ts
|
|
495
|
-
var STYLE_KEYS = [
|
|
496
|
-
"color",
|
|
497
|
-
"fillColor",
|
|
498
|
-
"strokeWidth",
|
|
499
|
-
"opacity",
|
|
500
|
-
"fontSize",
|
|
501
|
-
"strokeStyle"
|
|
502
|
-
];
|
|
503
|
-
|
|
504
|
-
// src/hooks/use-selection-style.ts
|
|
505
|
-
function styleEqual(a, b) {
|
|
506
|
-
if (a === b) return true;
|
|
507
|
-
if (a === null || b === null) return false;
|
|
508
|
-
return STYLE_KEYS.every((k) => a[k] === b[k]);
|
|
509
|
-
}
|
|
320
|
+
var import_core10 = require("@fieldnotes/core");
|
|
510
321
|
function useSelectionStyle() {
|
|
511
322
|
const viewport = useViewport();
|
|
512
|
-
const
|
|
513
|
-
const
|
|
514
|
-
(onChange) => {
|
|
515
|
-
const offSel = viewport.onSelectionChange(onChange);
|
|
516
|
-
const offStore = viewport.store.onChange(onChange);
|
|
517
|
-
return () => {
|
|
518
|
-
offSel();
|
|
519
|
-
offStore();
|
|
520
|
-
};
|
|
521
|
-
},
|
|
522
|
-
[viewport]
|
|
523
|
-
);
|
|
524
|
-
const getSnapshot = (0, import_react12.useCallback)(() => {
|
|
525
|
-
const next = viewport.getSelectionStyle();
|
|
526
|
-
if (styleEqual(cacheRef.current, next)) return cacheRef.current;
|
|
527
|
-
cacheRef.current = next;
|
|
528
|
-
return next;
|
|
529
|
-
}, [viewport]);
|
|
530
|
-
const style = (0, import_react12.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
323
|
+
const source = (0, import_react12.useMemo)(() => (0, import_core10.createSelectionStyleSource)(viewport), [viewport]);
|
|
324
|
+
const style = (0, import_react12.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot);
|
|
531
325
|
const apply = (0, import_react12.useCallback)((s) => viewport.applyStyleToSelection(s), [viewport]);
|
|
532
326
|
return [style, apply];
|
|
533
327
|
}
|
|
534
328
|
|
|
535
329
|
// src/hooks/use-selection-style-details.ts
|
|
536
330
|
var import_react13 = require("react");
|
|
537
|
-
|
|
538
|
-
return a.length === b.length && a.every((k, i) => k === b[i]);
|
|
539
|
-
}
|
|
540
|
-
function detailsEqual(a, b) {
|
|
541
|
-
if (a === b) return true;
|
|
542
|
-
if (a === null || b === null) return false;
|
|
543
|
-
return keysEqual(a.applicable, b.applicable) && keysEqual(a.mixed, b.mixed) && STYLE_KEYS.every((k) => a.common[k] === b.common[k]);
|
|
544
|
-
}
|
|
331
|
+
var import_core11 = require("@fieldnotes/core");
|
|
545
332
|
function useSelectionStyleDetails() {
|
|
546
333
|
const viewport = useViewport();
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
(onChange) => {
|
|
550
|
-
const offSel = viewport.onSelectionChange(onChange);
|
|
551
|
-
const offStore = viewport.store.onChange(onChange);
|
|
552
|
-
return () => {
|
|
553
|
-
offSel();
|
|
554
|
-
offStore();
|
|
555
|
-
};
|
|
556
|
-
},
|
|
557
|
-
[viewport]
|
|
558
|
-
);
|
|
559
|
-
const getSnapshot = (0, import_react13.useCallback)(() => {
|
|
560
|
-
const next = viewport.getSelectionStyleDetails();
|
|
561
|
-
if (detailsEqual(cacheRef.current, next)) return cacheRef.current;
|
|
562
|
-
cacheRef.current = next;
|
|
563
|
-
return next;
|
|
564
|
-
}, [viewport]);
|
|
565
|
-
const details = (0, import_react13.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
334
|
+
const source = (0, import_react13.useMemo)(() => (0, import_core11.createSelectionStyleDetailsSource)(viewport), [viewport]);
|
|
335
|
+
const details = (0, import_react13.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot);
|
|
566
336
|
const apply = (0, import_react13.useCallback)((s) => viewport.applyStyleToSelection(s), [viewport]);
|
|
567
337
|
return [details, apply];
|
|
568
338
|
}
|
|
569
339
|
|
|
570
340
|
// src/hooks/use-selection-ops.ts
|
|
571
341
|
var import_react14 = require("react");
|
|
572
|
-
|
|
573
|
-
return a.selectedIds === b.selectedIds && a.canGroup === b.canGroup && a.canUngroup === b.canUngroup && a.canAlign === b.canAlign && a.canDistribute === b.canDistribute && a.isLocked === b.isLocked;
|
|
574
|
-
}
|
|
342
|
+
var import_core12 = require("@fieldnotes/core");
|
|
575
343
|
function useSelectionOps() {
|
|
576
344
|
const viewport = useViewport();
|
|
577
|
-
const
|
|
578
|
-
const
|
|
579
|
-
(onChange) => {
|
|
580
|
-
const offSel = viewport.onSelectionChange(onChange);
|
|
581
|
-
const offStore = viewport.store.onChange(onChange);
|
|
582
|
-
return () => {
|
|
583
|
-
offSel();
|
|
584
|
-
offStore();
|
|
585
|
-
};
|
|
586
|
-
},
|
|
587
|
-
[viewport]
|
|
588
|
-
);
|
|
589
|
-
const getSnapshot = (0, import_react14.useCallback)(() => {
|
|
590
|
-
const ids = viewport.getSelectedIds();
|
|
591
|
-
const n = ids.length;
|
|
592
|
-
let canUngroup = false;
|
|
593
|
-
let lockedCount = 0;
|
|
594
|
-
for (const id of ids) {
|
|
595
|
-
const el = viewport.store.getById(id);
|
|
596
|
-
if (!el) continue;
|
|
597
|
-
if (el.groupId !== void 0) canUngroup = true;
|
|
598
|
-
if (el.locked) lockedCount++;
|
|
599
|
-
}
|
|
600
|
-
const isLocked = n === 0 ? null : lockedCount === n ? true : lockedCount === 0 ? false : null;
|
|
601
|
-
const next = {
|
|
602
|
-
selectedIds: ids,
|
|
603
|
-
canGroup: n >= 2,
|
|
604
|
-
canUngroup,
|
|
605
|
-
canAlign: n >= 2,
|
|
606
|
-
canDistribute: n >= 3,
|
|
607
|
-
isLocked
|
|
608
|
-
};
|
|
609
|
-
const prev = cacheRef.current;
|
|
610
|
-
if (prev && snapshotEqual(prev, next)) return prev;
|
|
611
|
-
cacheRef.current = next;
|
|
612
|
-
return next;
|
|
613
|
-
}, [viewport]);
|
|
614
|
-
const snap = (0, import_react14.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
345
|
+
const source = (0, import_react14.useMemo)(() => (0, import_core12.createSelectionOpsSource)(viewport), [viewport]);
|
|
346
|
+
const snap = (0, import_react14.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot);
|
|
615
347
|
const group = (0, import_react14.useCallback)(() => viewport.groupSelection(), [viewport]);
|
|
616
348
|
const ungroup = (0, import_react14.useCallback)(() => viewport.ungroupSelection(), [viewport]);
|
|
617
349
|
const toggleLock = (0, import_react14.useCallback)(() => viewport.toggleLockSelection(), [viewport]);
|
|
@@ -642,7 +374,7 @@ function useSelectionOps() {
|
|
|
642
374
|
|
|
643
375
|
// src/hooks/use-element-rects.ts
|
|
644
376
|
var import_react15 = require("react");
|
|
645
|
-
var
|
|
377
|
+
var import_core13 = require("@fieldnotes/core");
|
|
646
378
|
function useElementRects(match) {
|
|
647
379
|
const viewport = useViewport();
|
|
648
380
|
const matchRef = (0, import_react15.useRef)(match);
|
|
@@ -652,11 +384,11 @@ function useElementRects(match) {
|
|
|
652
384
|
const trackerRef = (0, import_react15.useRef)(null);
|
|
653
385
|
const subscribe = (0, import_react15.useCallback)(
|
|
654
386
|
(onStoreChange) => {
|
|
655
|
-
const tracker = new
|
|
387
|
+
const tracker = new import_core13.ElementRectTracker(viewport, { match: stableMatch });
|
|
656
388
|
trackerRef.current = tracker;
|
|
657
389
|
const next = tracker.getRects();
|
|
658
390
|
const previous = cacheRef.current;
|
|
659
|
-
if (previous === null || !(0,
|
|
391
|
+
if (previous === null || !(0, import_core13.elementRectsEqual)(previous, next)) {
|
|
660
392
|
cacheRef.current = next;
|
|
661
393
|
onStoreChange();
|
|
662
394
|
}
|
|
@@ -674,7 +406,7 @@ function useElementRects(match) {
|
|
|
674
406
|
);
|
|
675
407
|
const getSnapshot = (0, import_react15.useCallback)(() => {
|
|
676
408
|
if (cacheRef.current === null) {
|
|
677
|
-
cacheRef.current = (0,
|
|
409
|
+
cacheRef.current = (0, import_core13.computeElementRects)(viewport.store, stableMatch);
|
|
678
410
|
}
|
|
679
411
|
return cacheRef.current;
|
|
680
412
|
}, [viewport, stableMatch]);
|
|
@@ -687,7 +419,7 @@ function useElementRects(match) {
|
|
|
687
419
|
|
|
688
420
|
// src/minimap.tsx
|
|
689
421
|
var import_react16 = require("react");
|
|
690
|
-
var
|
|
422
|
+
var import_core14 = require("@fieldnotes/core");
|
|
691
423
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
692
424
|
var toggleStyle = {
|
|
693
425
|
position: "absolute",
|
|
@@ -719,7 +451,7 @@ function Minimap({
|
|
|
719
451
|
if (collapsed) return;
|
|
720
452
|
const canvas = canvasRef.current;
|
|
721
453
|
if (!canvas) return;
|
|
722
|
-
const controller = new
|
|
454
|
+
const controller = new import_core14.MinimapController(viewport, canvas, {
|
|
723
455
|
width,
|
|
724
456
|
height,
|
|
725
457
|
background,
|