@fieldnotes/react 0.4.2 → 0.6.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/index.js CHANGED
@@ -15,7 +15,18 @@ var ViewportContext = createContext(null);
15
15
  // src/field-notes-canvas.tsx
16
16
  import { jsx } from "react/jsx-runtime";
17
17
  var FieldNotesCanvas = forwardRef(
18
- function FieldNotesCanvas2({ options, tools, defaultTool, className, style, children, onReady }, ref) {
18
+ function FieldNotesCanvas2({
19
+ options,
20
+ tools,
21
+ defaultTool,
22
+ tool,
23
+ onToolChange,
24
+ snapToGrid,
25
+ className,
26
+ style,
27
+ children,
28
+ onReady
29
+ }, ref) {
19
30
  const containerRef = useRef(null);
20
31
  const [viewport, setViewport] = useState(null);
21
32
  useImperativeHandle(ref, () => ({ viewport }), [viewport]);
@@ -24,12 +35,13 @@ var FieldNotesCanvas = forwardRef(
24
35
  if (!el) return;
25
36
  const vp = new Viewport(el, options);
26
37
  if (tools) {
27
- for (const tool of tools) {
28
- vp.toolManager.register(tool);
38
+ for (const t of tools) {
39
+ vp.toolManager.register(t);
29
40
  }
30
41
  }
31
- if (defaultTool) {
32
- vp.toolManager.setTool(defaultTool, vp.toolContext);
42
+ const initialTool = tool ?? defaultTool;
43
+ if (initialTool) {
44
+ vp.setTool(initialTool);
33
45
  }
34
46
  setViewport(vp);
35
47
  onReady?.(vp);
@@ -38,6 +50,28 @@ var FieldNotesCanvas = forwardRef(
38
50
  setViewport(null);
39
51
  };
40
52
  }, []);
53
+ useEffect(() => {
54
+ if (!viewport || tool === void 0) return;
55
+ if (viewport.toolManager.activeTool?.name !== tool) {
56
+ viewport.setTool(tool);
57
+ }
58
+ }, [viewport, tool]);
59
+ useEffect(() => {
60
+ if (!viewport || !onToolChange) return;
61
+ return viewport.toolManager.onChange(onToolChange);
62
+ }, [viewport, onToolChange]);
63
+ useEffect(() => {
64
+ if (!viewport || snapToGrid === void 0) return;
65
+ viewport.setSnapToGrid(snapToGrid);
66
+ }, [viewport, snapToGrid]);
67
+ useEffect(() => {
68
+ if (!viewport || !tools) return;
69
+ for (const t of tools) {
70
+ if (!viewport.toolManager.getTool(t.name)) {
71
+ viewport.toolManager.register(t);
72
+ }
73
+ }
74
+ }, [viewport, tools]);
41
75
  return /* @__PURE__ */ jsx("div", { ref: containerRef, className, style, children: viewport && /* @__PURE__ */ jsx(ViewportContext.Provider, { value: viewport, children }) });
42
76
  }
43
77
  );
@@ -98,15 +132,12 @@ import { useCallback, useSyncExternalStore } from "react";
98
132
  function useActiveTool() {
99
133
  const viewport = useViewport();
100
134
  const subscribe = useCallback(
101
- (onStoreChange) => viewport.toolManager.onChange(() => onStoreChange()),
135
+ (onStoreChange) => viewport.toolManager.onChange(onStoreChange),
102
136
  [viewport]
103
137
  );
104
138
  const getSnapshot = useCallback(() => viewport.toolManager.activeTool?.name ?? "", [viewport]);
105
139
  const toolName = useSyncExternalStore(subscribe, getSnapshot);
106
- const setTool = useCallback(
107
- (name) => viewport.toolManager.setTool(name, viewport.toolContext),
108
- [viewport]
109
- );
140
+ const setTool = useCallback((name) => viewport.setTool(name), [viewport]);
110
141
  return [toolName, setTool];
111
142
  }
112
143
 
@@ -295,24 +326,94 @@ function useHistory() {
295
326
 
296
327
  // src/hooks/use-elements.ts
297
328
  import { useCallback as useCallback6, useRef as useRef7, useSyncExternalStore as useSyncExternalStore6 } from "react";
298
- function useElements(type) {
329
+ function shallowEqual2(a, b) {
330
+ if (Object.is(a, b)) return true;
331
+ if (Array.isArray(a) && Array.isArray(b)) {
332
+ return a.length === b.length && a.every((v, i) => Object.is(v, b[i]));
333
+ }
334
+ if (typeof a === "object" && a !== null && typeof b === "object" && b !== null && !Array.isArray(a) && !Array.isArray(b)) {
335
+ const ka = Object.keys(a);
336
+ const kb = Object.keys(b);
337
+ return ka.length === kb.length && ka.every(
338
+ (k) => k in b && Object.is(a[k], b[k])
339
+ );
340
+ }
341
+ return false;
342
+ }
343
+ function useElements(arg, isEqual) {
299
344
  const viewport = useViewport();
300
345
  const cachedRef = useRef7([]);
346
+ const selectedRef = useRef7(null);
301
347
  const subscribe = useCallback6(
302
348
  (onStoreChange) => viewport.store.onChange(onStoreChange),
303
349
  [viewport]
304
350
  );
305
351
  const getSnapshot = useCallback6(() => {
306
- const next = type ? viewport.store.getElementsByType(type) : viewport.store.getAll();
352
+ if (typeof arg === "function") {
353
+ const next2 = arg(viewport.store.getAll());
354
+ const prev = selectedRef.current;
355
+ const equal = isEqual ?? shallowEqual2;
356
+ if (prev !== null && equal(prev.value, next2)) {
357
+ return prev.value;
358
+ }
359
+ selectedRef.current = { value: next2 };
360
+ return next2;
361
+ }
362
+ const next = arg ? viewport.store.getElementsByType(arg) : viewport.store.getAll();
307
363
  const cached = cachedRef.current;
308
364
  if (cached.length === next.length && cached.every((el, i) => el === next[i])) {
309
365
  return cached;
310
366
  }
311
367
  cachedRef.current = next;
312
368
  return next;
313
- }, [viewport, type]);
369
+ }, [viewport, arg, isEqual]);
314
370
  return useSyncExternalStore6(subscribe, getSnapshot);
315
371
  }
372
+
373
+ // src/hooks/use-selection.ts
374
+ import { useCallback as useCallback7, useSyncExternalStore as useSyncExternalStore7 } from "react";
375
+ function useSelection() {
376
+ const viewport = useViewport();
377
+ const subscribe = useCallback7(
378
+ (onChange) => viewport.onSelectionChange(onChange),
379
+ [viewport]
380
+ );
381
+ const getSnapshot = useCallback7(() => viewport.getSelectedIds(), [viewport]);
382
+ return useSyncExternalStore7(subscribe, getSnapshot, getSnapshot);
383
+ }
384
+
385
+ // src/hooks/use-selection-style.ts
386
+ import { useCallback as useCallback8, useRef as useRef8, useSyncExternalStore as useSyncExternalStore8 } from "react";
387
+ function styleEqual(a, b) {
388
+ if (a === b) return true;
389
+ if (a === null || b === null) return false;
390
+ const keys = ["color", "fillColor", "strokeWidth", "opacity", "fontSize"];
391
+ return keys.every((k) => a[k] === b[k]);
392
+ }
393
+ function useSelectionStyle() {
394
+ const viewport = useViewport();
395
+ const cacheRef = useRef8(null);
396
+ const subscribe = useCallback8(
397
+ (onChange) => {
398
+ const offSel = viewport.onSelectionChange(onChange);
399
+ const offStore = viewport.store.onChange(onChange);
400
+ return () => {
401
+ offSel();
402
+ offStore();
403
+ };
404
+ },
405
+ [viewport]
406
+ );
407
+ const getSnapshot = useCallback8(() => {
408
+ const next = viewport.getSelectionStyle();
409
+ if (styleEqual(cacheRef.current, next)) return cacheRef.current;
410
+ cacheRef.current = next;
411
+ return next;
412
+ }, [viewport]);
413
+ const style = useSyncExternalStore8(subscribe, getSnapshot, getSnapshot);
414
+ const apply = useCallback8((s) => viewport.applyStyleToSelection(s), [viewport]);
415
+ return [style, apply];
416
+ }
316
417
  export {
317
418
  CanvasElement,
318
419
  FieldNotesCanvas,
@@ -322,6 +423,8 @@ export {
322
423
  useElements,
323
424
  useHistory,
324
425
  useLayers,
426
+ useSelection,
427
+ useSelectionStyle,
325
428
  useToolOptions,
326
429
  useViewport
327
430
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/field-notes-canvas.tsx","../src/context.ts","../src/canvas-element.tsx","../src/hooks/use-viewport.ts","../src/hooks/use-active-tool.ts","../src/hooks/use-camera.ts","../src/hooks/use-tool-options.ts","../src/hooks/use-layers.ts","../src/hooks/use-history.ts","../src/hooks/use-elements.ts"],"sourcesContent":["import {\r\n useRef,\r\n useEffect,\r\n useState,\r\n forwardRef,\r\n useImperativeHandle,\r\n type ReactNode,\r\n type CSSProperties,\r\n} from 'react';\r\nimport { Viewport } from '@fieldnotes/core';\r\nimport type { ViewportOptions, Tool } from '@fieldnotes/core';\r\nimport { ViewportContext } from './context';\r\n\r\nexport interface FieldNotesCanvasProps {\r\n options?: ViewportOptions;\r\n tools?: Tool[];\r\n defaultTool?: string;\r\n className?: string;\r\n style?: CSSProperties;\r\n children?: ReactNode;\r\n onReady?: (viewport: Viewport) => void;\r\n}\r\n\r\nexport interface FieldNotesCanvasRef {\r\n viewport: Viewport | null;\r\n}\r\n\r\nexport const FieldNotesCanvas = forwardRef<FieldNotesCanvasRef, FieldNotesCanvasProps>(\r\n function FieldNotesCanvas(\r\n { options, tools, defaultTool, className, style, children, onReady },\r\n ref,\r\n ) {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const [viewport, setViewport] = useState<Viewport | null>(null);\r\n\r\n useImperativeHandle(ref, () => ({ viewport }), [viewport]);\r\n\r\n useEffect(() => {\r\n const el = containerRef.current;\r\n if (!el) return;\r\n\r\n const vp = new Viewport(el, options);\r\n\r\n if (tools) {\r\n for (const tool of tools) {\r\n vp.toolManager.register(tool);\r\n }\r\n }\r\n\r\n if (defaultTool) {\r\n vp.toolManager.setTool(defaultTool, vp.toolContext);\r\n }\r\n\r\n setViewport(vp);\r\n onReady?.(vp);\r\n\r\n return () => {\r\n vp.destroy();\r\n setViewport(null);\r\n };\r\n }, []);\r\n\r\n return (\r\n <div ref={containerRef} className={className} style={style}>\r\n {viewport && (\r\n <ViewportContext.Provider value={viewport}>{children}</ViewportContext.Provider>\r\n )}\r\n </div>\r\n );\r\n },\r\n);\r\n","import { createContext } from 'react';\r\nimport type { Viewport } from '@fieldnotes/core';\r\n\r\nexport const ViewportContext = createContext<Viewport | null>(null);\r\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\r\nimport { createPortal } from 'react-dom';\r\nimport { useViewport } from './hooks/use-viewport';\r\n\r\nexport interface CanvasElementProps {\r\n position: { x: number; y: number };\r\n size?: { w: number; h: number };\r\n children: ReactNode;\r\n}\r\n\r\nexport function CanvasElement({ position, size, children }: CanvasElementProps) {\r\n const viewport = useViewport();\r\n const [portalTarget, setPortalTarget] = useState<HTMLElement | null>(null);\r\n const elementIdRef = useRef<string | null>(null);\r\n\r\n useEffect(() => {\r\n const container = document.createElement('div');\r\n Object.assign(container.style, {\r\n width: '100%',\r\n height: '100%',\r\n });\r\n\r\n // Append to domLayer immediately so portal children are queryable in the document\r\n // before the viewport render loop fires via requestAnimationFrame.\r\n viewport.domLayer.appendChild(container);\r\n\r\n const id = viewport.addHtmlElement(container, position, size);\r\n elementIdRef.current = id;\r\n setPortalTarget(container);\r\n\r\n return () => {\r\n if (elementIdRef.current) {\r\n viewport.store.remove(elementIdRef.current);\r\n viewport.requestRender();\r\n elementIdRef.current = null;\r\n }\r\n setPortalTarget(null);\r\n };\r\n }, [viewport]);\r\n\r\n useEffect(() => {\r\n const id = elementIdRef.current;\r\n if (!id) return;\r\n viewport.store.update(id, { position });\r\n if (size) {\r\n viewport.store.update(id, { size });\r\n }\r\n viewport.requestRender();\r\n }, [viewport, position.x, position.y, size?.w, size?.h]);\r\n\r\n if (!portalTarget) return null;\r\n return createPortal(children, portalTarget);\r\n}\r\n","import { useContext } from 'react';\r\nimport type { Viewport } from '@fieldnotes/core';\r\nimport { ViewportContext } from '../context';\r\n\r\nexport function useViewport(): Viewport {\r\n const viewport = useContext(ViewportContext);\r\n if (!viewport) {\r\n throw new Error('useViewport must be used inside <FieldNotesCanvas>');\r\n }\r\n return viewport;\r\n}\r\n","import { useCallback, useSyncExternalStore } from 'react';\r\nimport { useViewport } from './use-viewport';\r\n\r\nexport function useActiveTool(): [toolName: string, setTool: (name: string) => void] {\r\n const viewport = useViewport();\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => viewport.toolManager.onChange(() => onStoreChange()),\r\n [viewport],\r\n );\r\n\r\n const getSnapshot = useCallback(() => viewport.toolManager.activeTool?.name ?? '', [viewport]);\r\n\r\n const toolName = useSyncExternalStore(subscribe, getSnapshot);\r\n\r\n const setTool = useCallback(\r\n (name: string) => viewport.toolManager.setTool(name, viewport.toolContext),\r\n [viewport],\r\n );\r\n\r\n return [toolName, setTool];\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport { useViewport } from './use-viewport';\r\n\r\nexport interface CameraState {\r\n x: number;\r\n y: number;\r\n zoom: number;\r\n}\r\n\r\nexport function useCamera(): CameraState {\r\n const viewport = useViewport();\r\n const cachedRef = useRef<CameraState>({ x: 0, y: 0, zoom: 1 });\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => viewport.camera.onChange(onStoreChange),\r\n [viewport],\r\n );\r\n\r\n const getSnapshot = useCallback((): CameraState => {\r\n const { position, zoom } = viewport.camera;\r\n const cached = cachedRef.current;\r\n if (cached.x === position.x && cached.y === position.y && cached.zoom === zoom) {\r\n return cached;\r\n }\r\n const next = { x: position.x, y: position.y, zoom };\r\n cachedRef.current = next;\r\n return next;\r\n }, [viewport]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot);\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport { useViewport } from './use-viewport';\r\n\r\nfunction noop() {\r\n /* unsubscribe placeholder */\r\n}\r\n\r\nexport function useToolOptions<T extends Record<string, unknown>>(\r\n toolName: string,\r\n): [options: T | null, setOptions: (partial: Partial<T>) => void] {\r\n const viewport = useViewport();\r\n const cachedRef = useRef<T | null>(null);\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const tool = viewport.toolManager.getTool(toolName);\r\n if (!tool?.onOptionsChange) return noop;\r\n return tool.onOptionsChange(onStoreChange);\r\n },\r\n [viewport, toolName],\r\n );\r\n\r\n const getSnapshot = useCallback((): T | null => {\r\n const tool = viewport.toolManager.getTool(toolName);\r\n if (!tool?.getOptions) return null;\r\n const next = tool.getOptions() as T;\r\n const cached = cachedRef.current;\r\n if (cached && shallowEqual(cached, next)) return cached;\r\n cachedRef.current = next;\r\n return next;\r\n }, [viewport, toolName]);\r\n\r\n const options = useSyncExternalStore(subscribe, getSnapshot);\r\n\r\n const setOptions = useCallback(\r\n (partial: Partial<T>) => {\r\n const tool = viewport.toolManager.getTool(toolName);\r\n tool?.setOptions?.(partial as Record<string, unknown>);\r\n },\r\n [viewport, toolName],\r\n );\r\n\r\n return [options, setOptions];\r\n}\r\n\r\nfunction shallowEqual(a: Record<string, unknown>, b: Record<string, unknown>): boolean {\r\n const keysA = Object.keys(a);\r\n const keysB = Object.keys(b);\r\n if (keysA.length !== keysB.length) return false;\r\n for (const key of keysA) {\r\n const valA = a[key];\r\n const valB = b[key];\r\n if (valA === valB) continue;\r\n if (\r\n typeof valA === 'object' &&\r\n valA !== null &&\r\n typeof valB === 'object' &&\r\n valB !== null &&\r\n !Array.isArray(valA) &&\r\n !Array.isArray(valB)\r\n ) {\r\n const objA = valA as Record<string, unknown>;\r\n const objB = valB as Record<string, unknown>;\r\n const innerKeys = Object.keys(objA);\r\n if (\r\n innerKeys.length === Object.keys(objB).length &&\r\n innerKeys.every((k) => objA[k] === objB[k])\r\n ) {\r\n continue;\r\n }\r\n }\r\n return false;\r\n }\r\n return true;\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport type { Layer } from '@fieldnotes/core';\r\nimport { useViewport } from './use-viewport';\r\n\r\ninterface LayersSnapshot {\r\n layers: Layer[];\r\n activeLayerId: string;\r\n}\r\n\r\nexport interface UseLayersResult {\r\n layers: Layer[];\r\n activeLayerId: string;\r\n createLayer: (name?: string) => Layer;\r\n removeLayer: (id: string) => void;\r\n renameLayer: (id: string, name: string) => void;\r\n reorderLayer: (id: string, newOrder: number) => void;\r\n setVisible: (id: string, visible: boolean) => void;\r\n setLocked: (id: string, locked: boolean) => void;\r\n setOpacity: (id: string, opacity: number) => void;\r\n setActiveLayer: (id: string) => void;\r\n moveElement: (elementId: string, layerId: string) => void;\r\n}\r\n\r\nexport function useLayers(): UseLayersResult {\r\n const viewport = useViewport();\r\n const cachedRef = useRef<LayersSnapshot>({ layers: [], activeLayerId: '' });\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => viewport.layerManager.on('change', onStoreChange),\r\n [viewport],\r\n );\r\n\r\n const getSnapshot = useCallback((): LayersSnapshot => {\r\n const layers = viewport.layerManager.getLayers();\r\n const activeLayerId = viewport.layerManager.activeLayerId;\r\n const cached = cachedRef.current;\r\n if (\r\n cached.activeLayerId === activeLayerId &&\r\n cached.layers.length === layers.length &&\r\n cached.layers.every((l, i) => {\r\n const next = layers[i];\r\n return (\r\n next !== undefined &&\r\n l.id === next.id &&\r\n l.name === next.name &&\r\n l.visible === next.visible &&\r\n l.locked === next.locked &&\r\n l.order === next.order &&\r\n l.opacity === next.opacity\r\n );\r\n })\r\n ) {\r\n return cached;\r\n }\r\n const next = { layers, activeLayerId };\r\n cachedRef.current = next;\r\n return next;\r\n }, [viewport]);\r\n\r\n const snapshot = useSyncExternalStore(subscribe, getSnapshot);\r\n\r\n const createLayer = useCallback(\r\n (name?: string) => viewport.layerManager.createLayer(name),\r\n [viewport],\r\n );\r\n\r\n const removeLayer = useCallback(\r\n (id: string) => viewport.layerManager.removeLayer(id),\r\n [viewport],\r\n );\r\n\r\n const renameLayer = useCallback(\r\n (id: string, name: string) => viewport.layerManager.renameLayer(id, name),\r\n [viewport],\r\n );\r\n\r\n const reorderLayer = useCallback(\r\n (id: string, newOrder: number) => viewport.layerManager.reorderLayer(id, newOrder),\r\n [viewport],\r\n );\r\n\r\n const setVisible = useCallback(\r\n (id: string, visible: boolean) => viewport.layerManager.setLayerVisible(id, visible),\r\n [viewport],\r\n );\r\n\r\n const setLocked = useCallback(\r\n (id: string, locked: boolean) => viewport.layerManager.setLayerLocked(id, locked),\r\n [viewport],\r\n );\r\n\r\n const setOpacity = useCallback(\r\n (id: string, opacity: number) => viewport.layerManager.setLayerOpacity(id, opacity),\r\n [viewport],\r\n );\r\n\r\n const setActiveLayer = useCallback(\r\n (id: string) => viewport.layerManager.setActiveLayer(id),\r\n [viewport],\r\n );\r\n\r\n const moveElement = useCallback(\r\n (elementId: string, layerId: string) =>\r\n viewport.layerManager.moveElementToLayer(elementId, layerId),\r\n [viewport],\r\n );\r\n\r\n return {\r\n layers: snapshot.layers,\r\n activeLayerId: snapshot.activeLayerId,\r\n createLayer,\r\n removeLayer,\r\n renameLayer,\r\n reorderLayer,\r\n setVisible,\r\n setLocked,\r\n setOpacity,\r\n setActiveLayer,\r\n moveElement,\r\n };\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport { useViewport } from './use-viewport';\r\n\r\ninterface HistorySnapshot {\r\n canUndo: boolean;\r\n canRedo: boolean;\r\n}\r\n\r\nexport interface UseHistoryResult {\r\n canUndo: boolean;\r\n canRedo: boolean;\r\n undo: () => void;\r\n redo: () => void;\r\n}\r\n\r\nexport function useHistory(): UseHistoryResult {\r\n const viewport = useViewport();\r\n const cachedRef = useRef<HistorySnapshot>({ canUndo: false, canRedo: false });\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => viewport.history.onChange(onStoreChange),\r\n [viewport],\r\n );\r\n\r\n const getSnapshot = useCallback((): HistorySnapshot => {\r\n const canUndo = viewport.history.canUndo;\r\n const canRedo = viewport.history.canRedo;\r\n const cached = cachedRef.current;\r\n if (cached.canUndo === canUndo && cached.canRedo === canRedo) {\r\n return cached;\r\n }\r\n const next = { canUndo, canRedo };\r\n cachedRef.current = next;\r\n return next;\r\n }, [viewport]);\r\n\r\n const snapshot = useSyncExternalStore(subscribe, getSnapshot);\r\n\r\n const undo = useCallback(() => viewport.undo(), [viewport]);\r\n const redo = useCallback(() => viewport.redo(), [viewport]);\r\n\r\n return {\r\n canUndo: snapshot.canUndo,\r\n canRedo: snapshot.canRedo,\r\n undo,\r\n redo,\r\n };\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport type { CanvasElement, ElementType } from '@fieldnotes/core';\r\nimport { useViewport } from './use-viewport';\r\n\r\nexport function useElements(): CanvasElement[];\r\nexport function useElements<T extends ElementType>(type: T): Extract<CanvasElement, { type: T }>[];\r\nexport function useElements(type?: ElementType): CanvasElement[] {\r\n const viewport = useViewport();\r\n const cachedRef = useRef<CanvasElement[]>([]);\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => viewport.store.onChange(onStoreChange),\r\n [viewport],\r\n );\r\n\r\n const getSnapshot = useCallback((): CanvasElement[] => {\r\n const next = type ? viewport.store.getElementsByType(type) : viewport.store.getAll();\r\n const cached = cachedRef.current;\r\n if (cached.length === next.length && cached.every((el, i) => el === next[i])) {\r\n return cached;\r\n }\r\n cachedRef.current = next;\r\n return next;\r\n }, [viewport, type]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot);\r\n}\r\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,gBAAgB;;;ACTzB,SAAS,qBAAqB;AAGvB,IAAM,kBAAkB,cAA+B,IAAI;;;AD8DxD;AAtCH,IAAM,mBAAmB;AAAA,EAC9B,SAASA,kBACP,EAAE,SAAS,OAAO,aAAa,WAAW,OAAO,UAAU,QAAQ,GACnE,KACA;AACA,UAAM,eAAe,OAAuB,IAAI;AAChD,UAAM,CAAC,UAAU,WAAW,IAAI,SAA0B,IAAI;AAE9D,wBAAoB,KAAK,OAAO,EAAE,SAAS,IAAI,CAAC,QAAQ,CAAC;AAEzD,cAAU,MAAM;AACd,YAAM,KAAK,aAAa;AACxB,UAAI,CAAC,GAAI;AAET,YAAM,KAAK,IAAI,SAAS,IAAI,OAAO;AAEnC,UAAI,OAAO;AACT,mBAAW,QAAQ,OAAO;AACxB,aAAG,YAAY,SAAS,IAAI;AAAA,QAC9B;AAAA,MACF;AAEA,UAAI,aAAa;AACf,WAAG,YAAY,QAAQ,aAAa,GAAG,WAAW;AAAA,MACpD;AAEA,kBAAY,EAAE;AACd,gBAAU,EAAE;AAEZ,aAAO,MAAM;AACX,WAAG,QAAQ;AACX,oBAAY,IAAI;AAAA,MAClB;AAAA,IACF,GAAG,CAAC,CAAC;AAEL,WACE,oBAAC,SAAI,KAAK,cAAc,WAAsB,OAC3C,sBACC,oBAAC,gBAAgB,UAAhB,EAAyB,OAAO,UAAW,UAAS,GAEzD;AAAA,EAEJ;AACF;;;AEtEA,SAAS,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgC;AAC5D,SAAS,oBAAoB;;;ACD7B,SAAS,kBAAkB;AAIpB,SAAS,cAAwB;AACtC,QAAM,WAAW,WAAW,eAAe;AAC3C,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AACA,SAAO;AACT;;;ADAO,SAAS,cAAc,EAAE,UAAU,MAAM,SAAS,GAAuB;AAC9E,QAAM,WAAW,YAAY;AAC7B,QAAM,CAAC,cAAc,eAAe,IAAIC,UAA6B,IAAI;AACzE,QAAM,eAAeC,QAAsB,IAAI;AAE/C,EAAAC,WAAU,MAAM;AACd,UAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,WAAO,OAAO,UAAU,OAAO;AAAA,MAC7B,OAAO;AAAA,MACP,QAAQ;AAAA,IACV,CAAC;AAID,aAAS,SAAS,YAAY,SAAS;AAEvC,UAAM,KAAK,SAAS,eAAe,WAAW,UAAU,IAAI;AAC5D,iBAAa,UAAU;AACvB,oBAAgB,SAAS;AAEzB,WAAO,MAAM;AACX,UAAI,aAAa,SAAS;AACxB,iBAAS,MAAM,OAAO,aAAa,OAAO;AAC1C,iBAAS,cAAc;AACvB,qBAAa,UAAU;AAAA,MACzB;AACA,sBAAgB,IAAI;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAEb,EAAAA,WAAU,MAAM;AACd,UAAM,KAAK,aAAa;AACxB,QAAI,CAAC,GAAI;AACT,aAAS,MAAM,OAAO,IAAI,EAAE,SAAS,CAAC;AACtC,QAAI,MAAM;AACR,eAAS,MAAM,OAAO,IAAI,EAAE,KAAK,CAAC;AAAA,IACpC;AACA,aAAS,cAAc;AAAA,EACzB,GAAG,CAAC,UAAU,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC;AAEvD,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO,aAAa,UAAU,YAAY;AAC5C;;;AEpDA,SAAS,aAAa,4BAA4B;AAG3C,SAAS,gBAAqE;AACnF,QAAM,WAAW,YAAY;AAE7B,QAAM,YAAY;AAAA,IAChB,CAAC,kBAA8B,SAAS,YAAY,SAAS,MAAM,cAAc,CAAC;AAAA,IAClF,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,cAAc,YAAY,MAAM,SAAS,YAAY,YAAY,QAAQ,IAAI,CAAC,QAAQ,CAAC;AAE7F,QAAM,WAAW,qBAAqB,WAAW,WAAW;AAE5D,QAAM,UAAU;AAAA,IACd,CAAC,SAAiB,SAAS,YAAY,QAAQ,MAAM,SAAS,WAAW;AAAA,IACzE,CAAC,QAAQ;AAAA,EACX;AAEA,SAAO,CAAC,UAAU,OAAO;AAC3B;;;ACrBA,SAAS,eAAAC,cAAa,UAAAC,SAAQ,wBAAAC,6BAA4B;AASnD,SAAS,YAAyB;AACvC,QAAM,WAAW,YAAY;AAC7B,QAAM,YAAYC,QAAoB,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,EAAE,CAAC;AAE7D,QAAM,YAAYC;AAAA,IAChB,CAAC,kBAA8B,SAAS,OAAO,SAAS,aAAa;AAAA,IACrE,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,cAAcA,aAAY,MAAmB;AACjD,UAAM,EAAE,UAAU,KAAK,IAAI,SAAS;AACpC,UAAM,SAAS,UAAU;AACzB,QAAI,OAAO,MAAM,SAAS,KAAK,OAAO,MAAM,SAAS,KAAK,OAAO,SAAS,MAAM;AAC9E,aAAO;AAAA,IACT;AACA,UAAM,OAAO,EAAE,GAAG,SAAS,GAAG,GAAG,SAAS,GAAG,KAAK;AAClD,cAAU,UAAU;AACpB,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,CAAC;AAEb,SAAOC,sBAAqB,WAAW,WAAW;AACpD;;;AC9BA,SAAS,eAAAC,cAAa,UAAAC,SAAQ,wBAAAC,6BAA4B;AAG1D,SAAS,OAAO;AAEhB;AAEO,SAAS,eACd,UACgE;AAChE,QAAM,WAAW,YAAY;AAC7B,QAAM,YAAYC,QAAiB,IAAI;AAEvC,QAAM,YAAYC;AAAA,IAChB,CAAC,kBAA8B;AAC7B,YAAM,OAAO,SAAS,YAAY,QAAQ,QAAQ;AAClD,UAAI,CAAC,MAAM,gBAAiB,QAAO;AACnC,aAAO,KAAK,gBAAgB,aAAa;AAAA,IAC3C;AAAA,IACA,CAAC,UAAU,QAAQ;AAAA,EACrB;AAEA,QAAM,cAAcA,aAAY,MAAgB;AAC9C,UAAM,OAAO,SAAS,YAAY,QAAQ,QAAQ;AAClD,QAAI,CAAC,MAAM,WAAY,QAAO;AAC9B,UAAM,OAAO,KAAK,WAAW;AAC7B,UAAM,SAAS,UAAU;AACzB,QAAI,UAAU,aAAa,QAAQ,IAAI,EAAG,QAAO;AACjD,cAAU,UAAU;AACpB,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,QAAQ,CAAC;AAEvB,QAAM,UAAUC,sBAAqB,WAAW,WAAW;AAE3D,QAAM,aAAaD;AAAA,IACjB,CAAC,YAAwB;AACvB,YAAM,OAAO,SAAS,YAAY,QAAQ,QAAQ;AAClD,YAAM,aAAa,OAAkC;AAAA,IACvD;AAAA,IACA,CAAC,UAAU,QAAQ;AAAA,EACrB;AAEA,SAAO,CAAC,SAAS,UAAU;AAC7B;AAEA,SAAS,aAAa,GAA4B,GAAqC;AACrF,QAAM,QAAQ,OAAO,KAAK,CAAC;AAC3B,QAAM,QAAQ,OAAO,KAAK,CAAC;AAC3B,MAAI,MAAM,WAAW,MAAM,OAAQ,QAAO;AAC1C,aAAW,OAAO,OAAO;AACvB,UAAM,OAAO,EAAE,GAAG;AAClB,UAAM,OAAO,EAAE,GAAG;AAClB,QAAI,SAAS,KAAM;AACnB,QACE,OAAO,SAAS,YAChB,SAAS,QACT,OAAO,SAAS,YAChB,SAAS,QACT,CAAC,MAAM,QAAQ,IAAI,KACnB,CAAC,MAAM,QAAQ,IAAI,GACnB;AACA,YAAM,OAAO;AACb,YAAM,OAAO;AACb,YAAM,YAAY,OAAO,KAAK,IAAI;AAClC,UACE,UAAU,WAAW,OAAO,KAAK,IAAI,EAAE,UACvC,UAAU,MAAM,CAAC,MAAM,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,GAC1C;AACA;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;AC1EA,SAAS,eAAAE,cAAa,UAAAC,SAAQ,wBAAAC,6BAA4B;AAuBnD,SAAS,YAA6B;AAC3C,QAAM,WAAW,YAAY;AAC7B,QAAM,YAAYC,QAAuB,EAAE,QAAQ,CAAC,GAAG,eAAe,GAAG,CAAC;AAE1E,QAAM,YAAYC;AAAA,IAChB,CAAC,kBAA8B,SAAS,aAAa,GAAG,UAAU,aAAa;AAAA,IAC/E,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,cAAcA,aAAY,MAAsB;AACpD,UAAM,SAAS,SAAS,aAAa,UAAU;AAC/C,UAAM,gBAAgB,SAAS,aAAa;AAC5C,UAAM,SAAS,UAAU;AACzB,QACE,OAAO,kBAAkB,iBACzB,OAAO,OAAO,WAAW,OAAO,UAChC,OAAO,OAAO,MAAM,CAAC,GAAG,MAAM;AAC5B,YAAMC,QAAO,OAAO,CAAC;AACrB,aACEA,UAAS,UACT,EAAE,OAAOA,MAAK,MACd,EAAE,SAASA,MAAK,QAChB,EAAE,YAAYA,MAAK,WACnB,EAAE,WAAWA,MAAK,UAClB,EAAE,UAAUA,MAAK,SACjB,EAAE,YAAYA,MAAK;AAAA,IAEvB,CAAC,GACD;AACA,aAAO;AAAA,IACT;AACA,UAAM,OAAO,EAAE,QAAQ,cAAc;AACrC,cAAU,UAAU;AACpB,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,WAAWC,sBAAqB,WAAW,WAAW;AAE5D,QAAM,cAAcF;AAAA,IAClB,CAAC,SAAkB,SAAS,aAAa,YAAY,IAAI;AAAA,IACzD,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,cAAcA;AAAA,IAClB,CAAC,OAAe,SAAS,aAAa,YAAY,EAAE;AAAA,IACpD,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,cAAcA;AAAA,IAClB,CAAC,IAAY,SAAiB,SAAS,aAAa,YAAY,IAAI,IAAI;AAAA,IACxE,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,eAAeA;AAAA,IACnB,CAAC,IAAY,aAAqB,SAAS,aAAa,aAAa,IAAI,QAAQ;AAAA,IACjF,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,aAAaA;AAAA,IACjB,CAAC,IAAY,YAAqB,SAAS,aAAa,gBAAgB,IAAI,OAAO;AAAA,IACnF,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,YAAYA;AAAA,IAChB,CAAC,IAAY,WAAoB,SAAS,aAAa,eAAe,IAAI,MAAM;AAAA,IAChF,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,aAAaA;AAAA,IACjB,CAAC,IAAY,YAAoB,SAAS,aAAa,gBAAgB,IAAI,OAAO;AAAA,IAClF,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,iBAAiBA;AAAA,IACrB,CAAC,OAAe,SAAS,aAAa,eAAe,EAAE;AAAA,IACvD,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,cAAcA;AAAA,IAClB,CAAC,WAAmB,YAClB,SAAS,aAAa,mBAAmB,WAAW,OAAO;AAAA,IAC7D,CAAC,QAAQ;AAAA,EACX;AAEA,SAAO;AAAA,IACL,QAAQ,SAAS;AAAA,IACjB,eAAe,SAAS;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACxHA,SAAS,eAAAG,cAAa,UAAAC,SAAQ,wBAAAC,6BAA4B;AAenD,SAAS,aAA+B;AAC7C,QAAM,WAAW,YAAY;AAC7B,QAAM,YAAYC,QAAwB,EAAE,SAAS,OAAO,SAAS,MAAM,CAAC;AAE5E,QAAM,YAAYC;AAAA,IAChB,CAAC,kBAA8B,SAAS,QAAQ,SAAS,aAAa;AAAA,IACtE,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,cAAcA,aAAY,MAAuB;AACrD,UAAM,UAAU,SAAS,QAAQ;AACjC,UAAM,UAAU,SAAS,QAAQ;AACjC,UAAM,SAAS,UAAU;AACzB,QAAI,OAAO,YAAY,WAAW,OAAO,YAAY,SAAS;AAC5D,aAAO;AAAA,IACT;AACA,UAAM,OAAO,EAAE,SAAS,QAAQ;AAChC,cAAU,UAAU;AACpB,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,WAAWC,sBAAqB,WAAW,WAAW;AAE5D,QAAM,OAAOD,aAAY,MAAM,SAAS,KAAK,GAAG,CAAC,QAAQ,CAAC;AAC1D,QAAM,OAAOA,aAAY,MAAM,SAAS,KAAK,GAAG,CAAC,QAAQ,CAAC;AAE1D,SAAO;AAAA,IACL,SAAS,SAAS;AAAA,IAClB,SAAS,SAAS;AAAA,IAClB;AAAA,IACA;AAAA,EACF;AACF;;;AC/CA,SAAS,eAAAE,cAAa,UAAAC,SAAQ,wBAAAC,6BAA4B;AAMnD,SAAS,YAAY,MAAqC;AAC/D,QAAM,WAAW,YAAY;AAC7B,QAAM,YAAYC,QAAwB,CAAC,CAAC;AAE5C,QAAM,YAAYC;AAAA,IAChB,CAAC,kBAA8B,SAAS,MAAM,SAAS,aAAa;AAAA,IACpE,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,cAAcA,aAAY,MAAuB;AACrD,UAAM,OAAO,OAAO,SAAS,MAAM,kBAAkB,IAAI,IAAI,SAAS,MAAM,OAAO;AACnF,UAAM,SAAS,UAAU;AACzB,QAAI,OAAO,WAAW,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC,GAAG;AAC5E,aAAO;AAAA,IACT;AACA,cAAU,UAAU;AACpB,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,IAAI,CAAC;AAEnB,SAAOC,sBAAqB,WAAW,WAAW;AACpD;","names":["FieldNotesCanvas","useEffect","useRef","useState","useState","useRef","useEffect","useCallback","useRef","useSyncExternalStore","useRef","useCallback","useSyncExternalStore","useCallback","useRef","useSyncExternalStore","useRef","useCallback","useSyncExternalStore","useCallback","useRef","useSyncExternalStore","useRef","useCallback","next","useSyncExternalStore","useCallback","useRef","useSyncExternalStore","useRef","useCallback","useSyncExternalStore","useCallback","useRef","useSyncExternalStore","useRef","useCallback","useSyncExternalStore"]}
1
+ {"version":3,"sources":["../src/field-notes-canvas.tsx","../src/context.ts","../src/canvas-element.tsx","../src/hooks/use-viewport.ts","../src/hooks/use-active-tool.ts","../src/hooks/use-camera.ts","../src/hooks/use-tool-options.ts","../src/hooks/use-layers.ts","../src/hooks/use-history.ts","../src/hooks/use-elements.ts","../src/hooks/use-selection.ts","../src/hooks/use-selection-style.ts"],"sourcesContent":["import {\r\n useRef,\r\n useEffect,\r\n useState,\r\n forwardRef,\r\n useImperativeHandle,\r\n type ReactNode,\r\n type CSSProperties,\r\n} from 'react';\r\nimport { Viewport } from '@fieldnotes/core';\r\nimport type { ViewportOptions, Tool } from '@fieldnotes/core';\r\nimport { ViewportContext } from './context';\r\n\r\nexport interface FieldNotesCanvasProps {\r\n /** Constructor-only: changes after mount are ignored (the canvas is stateful). */\r\n options?: ViewportOptions;\r\n /**\r\n * Reactive (append-only): tools present here are registered; removal is not supported.\r\n * Hoist the array out of render — inline arrays reconstruct Tool instances and re-run\r\n * registration on every render.\r\n */\r\n tools?: Tool[];\r\n /** Uncontrolled initial tool. Ignored when `tool` is set. */\r\n defaultTool?: string;\r\n /** Controlled active tool. Pair with `onToolChange`. */\r\n tool?: string;\r\n /**\r\n * Fires whenever the active tool changes (keyboard, API, or `tool` prop).\r\n * Memoize with useCallback to avoid re-subscribing each render.\r\n */\r\n onToolChange?: (name: string) => void;\r\n /** Reactive: toggles grid snapping. */\r\n snapToGrid?: boolean;\r\n className?: string;\r\n style?: CSSProperties;\r\n children?: ReactNode;\r\n onReady?: (viewport: Viewport) => void;\r\n}\r\n\r\nexport interface FieldNotesCanvasRef {\r\n viewport: Viewport | null;\r\n}\r\n\r\nexport const FieldNotesCanvas = forwardRef<FieldNotesCanvasRef, FieldNotesCanvasProps>(\r\n function FieldNotesCanvas(\r\n {\r\n options,\r\n tools,\r\n defaultTool,\r\n tool,\r\n onToolChange,\r\n snapToGrid,\r\n className,\r\n style,\r\n children,\r\n onReady,\r\n },\r\n ref,\r\n ) {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const [viewport, setViewport] = useState<Viewport | null>(null);\r\n\r\n useImperativeHandle(ref, () => ({ viewport }), [viewport]);\r\n\r\n useEffect(() => {\r\n const el = containerRef.current;\r\n if (!el) return;\r\n\r\n const vp = new Viewport(el, options);\r\n\r\n if (tools) {\r\n for (const t of tools) {\r\n vp.toolManager.register(t);\r\n }\r\n }\r\n\r\n const initialTool = tool ?? defaultTool;\r\n if (initialTool) {\r\n vp.setTool(initialTool);\r\n }\r\n\r\n setViewport(vp);\r\n onReady?.(vp);\r\n\r\n return () => {\r\n vp.destroy();\r\n setViewport(null);\r\n };\r\n // Constructor effect: options/tools/defaultTool are mount-only by design.\r\n }, []);\r\n\r\n useEffect(() => {\r\n if (!viewport || tool === undefined) return;\r\n if (viewport.toolManager.activeTool?.name !== tool) {\r\n viewport.setTool(tool);\r\n }\r\n }, [viewport, tool]);\r\n\r\n useEffect(() => {\r\n if (!viewport || !onToolChange) return;\r\n return viewport.toolManager.onChange(onToolChange);\r\n }, [viewport, onToolChange]);\r\n\r\n useEffect(() => {\r\n if (!viewport || snapToGrid === undefined) return;\r\n viewport.setSnapToGrid(snapToGrid);\r\n }, [viewport, snapToGrid]);\r\n\r\n useEffect(() => {\r\n if (!viewport || !tools) return;\r\n for (const t of tools) {\r\n if (!viewport.toolManager.getTool(t.name)) {\r\n viewport.toolManager.register(t);\r\n }\r\n }\r\n }, [viewport, tools]);\r\n\r\n return (\r\n <div ref={containerRef} className={className} style={style}>\r\n {viewport && (\r\n <ViewportContext.Provider value={viewport}>{children}</ViewportContext.Provider>\r\n )}\r\n </div>\r\n );\r\n },\r\n);\r\n","import { createContext } from 'react';\r\nimport type { Viewport } from '@fieldnotes/core';\r\n\r\nexport const ViewportContext = createContext<Viewport | null>(null);\r\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\r\nimport { createPortal } from 'react-dom';\r\nimport { useViewport } from './hooks/use-viewport';\r\n\r\n/** Props for {@link CanvasElement}. */\r\nexport interface CanvasElementProps {\r\n /** Canvas-space position of the element's top-left corner. */\r\n position: { x: number; y: number };\r\n /** Optional fixed size; omit to let the element size to its content. */\r\n size?: { w: number; h: number };\r\n children: ReactNode;\r\n}\r\n\r\n/**\r\n * Renders a React subtree as an HTML element embedded in the canvas coordinate space.\r\n * Must be used inside a `<FieldNotesCanvas>`.\r\n */\r\nexport function CanvasElement({ position, size, children }: CanvasElementProps) {\r\n const viewport = useViewport();\r\n const [portalTarget, setPortalTarget] = useState<HTMLElement | null>(null);\r\n const elementIdRef = useRef<string | null>(null);\r\n\r\n useEffect(() => {\r\n const container = document.createElement('div');\r\n Object.assign(container.style, {\r\n width: '100%',\r\n height: '100%',\r\n });\r\n\r\n // Append to domLayer immediately so portal children are queryable in the document\r\n // before the viewport render loop fires via requestAnimationFrame.\r\n viewport.domLayer.appendChild(container);\r\n\r\n const id = viewport.addHtmlElement(container, position, size);\r\n elementIdRef.current = id;\r\n setPortalTarget(container);\r\n\r\n return () => {\r\n if (elementIdRef.current) {\r\n viewport.store.remove(elementIdRef.current);\r\n viewport.requestRender();\r\n elementIdRef.current = null;\r\n }\r\n setPortalTarget(null);\r\n };\r\n }, [viewport]);\r\n\r\n useEffect(() => {\r\n const id = elementIdRef.current;\r\n if (!id) return;\r\n viewport.store.update(id, { position });\r\n if (size) {\r\n viewport.store.update(id, { size });\r\n }\r\n viewport.requestRender();\r\n }, [viewport, position.x, position.y, size?.w, size?.h]);\r\n\r\n if (!portalTarget) return null;\r\n return createPortal(children, portalTarget);\r\n}\r\n","import { useContext } from 'react';\r\nimport type { Viewport } from '@fieldnotes/core';\r\nimport { ViewportContext } from '../context';\r\n\r\n/** The Viewport from the nearest `<FieldNotesCanvas>`. Throws outside one. */\r\nexport function useViewport(): Viewport {\r\n const viewport = useContext(ViewportContext);\r\n if (!viewport) {\r\n throw new Error('useViewport must be used inside <FieldNotesCanvas>');\r\n }\r\n return viewport;\r\n}\r\n","import { useCallback, useSyncExternalStore } from 'react';\r\nimport { useViewport } from './use-viewport';\r\n\r\n/** `[activeToolName, setTool]` — re-renders on tool switches. */\r\nexport function useActiveTool(): [toolName: string, setTool: (name: string) => void] {\r\n const viewport = useViewport();\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => viewport.toolManager.onChange(onStoreChange),\r\n [viewport],\r\n );\r\n\r\n const getSnapshot = useCallback(() => viewport.toolManager.activeTool?.name ?? '', [viewport]);\r\n\r\n const toolName = useSyncExternalStore(subscribe, getSnapshot);\r\n\r\n const setTool = useCallback((name: string) => viewport.setTool(name), [viewport]);\r\n\r\n return [toolName, setTool];\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport { useViewport } from './use-viewport';\r\n\r\nexport interface CameraState {\r\n x: number;\r\n y: number;\r\n zoom: number;\r\n}\r\n\r\n/** Current camera position and zoom level — re-renders whenever the camera moves or zoom changes. */\r\nexport function useCamera(): CameraState {\r\n const viewport = useViewport();\r\n const cachedRef = useRef<CameraState>({ x: 0, y: 0, zoom: 1 });\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => viewport.camera.onChange(onStoreChange),\r\n [viewport],\r\n );\r\n\r\n const getSnapshot = useCallback((): CameraState => {\r\n const { position, zoom } = viewport.camera;\r\n const cached = cachedRef.current;\r\n if (cached.x === position.x && cached.y === position.y && cached.zoom === zoom) {\r\n return cached;\r\n }\r\n const next = { x: position.x, y: position.y, zoom };\r\n cachedRef.current = next;\r\n return next;\r\n }, [viewport]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot);\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport { useViewport } from './use-viewport';\r\n\r\nfunction noop() {\r\n /* unsubscribe placeholder */\r\n}\r\n\r\n/**\r\n * `[options, setOptions]` for the named tool — re-renders when the tool's options change.\r\n * Returns `null` for options if the tool is not registered or does not expose options.\r\n */\r\nexport function useToolOptions<T extends Record<string, unknown>>(\r\n toolName: string,\r\n): [options: T | null, setOptions: (partial: Partial<T>) => void] {\r\n const viewport = useViewport();\r\n const cachedRef = useRef<T | null>(null);\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const tool = viewport.toolManager.getTool(toolName);\r\n if (!tool?.onOptionsChange) return noop;\r\n return tool.onOptionsChange(onStoreChange);\r\n },\r\n [viewport, toolName],\r\n );\r\n\r\n const getSnapshot = useCallback((): T | null => {\r\n const tool = viewport.toolManager.getTool(toolName);\r\n if (!tool?.getOptions) return null;\r\n const next = tool.getOptions() as T;\r\n const cached = cachedRef.current;\r\n if (cached && shallowEqual(cached, next)) return cached;\r\n cachedRef.current = next;\r\n return next;\r\n }, [viewport, toolName]);\r\n\r\n const options = useSyncExternalStore(subscribe, getSnapshot);\r\n\r\n const setOptions = useCallback(\r\n (partial: Partial<T>) => {\r\n const tool = viewport.toolManager.getTool(toolName);\r\n tool?.setOptions?.(partial as Record<string, unknown>);\r\n },\r\n [viewport, toolName],\r\n );\r\n\r\n return [options, setOptions];\r\n}\r\n\r\nfunction shallowEqual(a: Record<string, unknown>, b: Record<string, unknown>): boolean {\r\n const keysA = Object.keys(a);\r\n const keysB = Object.keys(b);\r\n if (keysA.length !== keysB.length) return false;\r\n for (const key of keysA) {\r\n const valA = a[key];\r\n const valB = b[key];\r\n if (valA === valB) continue;\r\n if (\r\n typeof valA === 'object' &&\r\n valA !== null &&\r\n typeof valB === 'object' &&\r\n valB !== null &&\r\n !Array.isArray(valA) &&\r\n !Array.isArray(valB)\r\n ) {\r\n const objA = valA as Record<string, unknown>;\r\n const objB = valB as Record<string, unknown>;\r\n const innerKeys = Object.keys(objA);\r\n if (\r\n innerKeys.length === Object.keys(objB).length &&\r\n innerKeys.every((k) => objA[k] === objB[k])\r\n ) {\r\n continue;\r\n }\r\n }\r\n return false;\r\n }\r\n return true;\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport type { Layer } from '@fieldnotes/core';\r\nimport { useViewport } from './use-viewport';\r\n\r\ninterface LayersSnapshot {\r\n layers: Layer[];\r\n activeLayerId: string;\r\n}\r\n\r\nexport interface UseLayersResult {\r\n layers: Layer[];\r\n activeLayerId: string;\r\n createLayer: (name?: string) => Layer;\r\n removeLayer: (id: string) => void;\r\n renameLayer: (id: string, name: string) => void;\r\n reorderLayer: (id: string, newOrder: number) => void;\r\n setVisible: (id: string, visible: boolean) => void;\r\n setLocked: (id: string, locked: boolean) => void;\r\n setOpacity: (id: string, opacity: number) => void;\r\n setActiveLayer: (id: string) => void;\r\n moveElement: (elementId: string, layerId: string) => void;\r\n}\r\n\r\n/**\r\n * Current layer list and mutation helpers — re-renders whenever a layer is added, removed,\r\n * reordered, renamed, or its visibility/lock/opacity/active state changes.\r\n */\r\nexport function useLayers(): UseLayersResult {\r\n const viewport = useViewport();\r\n const cachedRef = useRef<LayersSnapshot>({ layers: [], activeLayerId: '' });\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => viewport.layerManager.on('change', onStoreChange),\r\n [viewport],\r\n );\r\n\r\n const getSnapshot = useCallback((): LayersSnapshot => {\r\n const layers = viewport.layerManager.getLayers();\r\n const activeLayerId = viewport.layerManager.activeLayerId;\r\n const cached = cachedRef.current;\r\n if (\r\n cached.activeLayerId === activeLayerId &&\r\n cached.layers.length === layers.length &&\r\n cached.layers.every((l, i) => {\r\n const next = layers[i];\r\n return (\r\n next !== undefined &&\r\n l.id === next.id &&\r\n l.name === next.name &&\r\n l.visible === next.visible &&\r\n l.locked === next.locked &&\r\n l.order === next.order &&\r\n l.opacity === next.opacity\r\n );\r\n })\r\n ) {\r\n return cached;\r\n }\r\n const next = { layers, activeLayerId };\r\n cachedRef.current = next;\r\n return next;\r\n }, [viewport]);\r\n\r\n const snapshot = useSyncExternalStore(subscribe, getSnapshot);\r\n\r\n const createLayer = useCallback(\r\n (name?: string) => viewport.layerManager.createLayer(name),\r\n [viewport],\r\n );\r\n\r\n const removeLayer = useCallback(\r\n (id: string) => viewport.layerManager.removeLayer(id),\r\n [viewport],\r\n );\r\n\r\n const renameLayer = useCallback(\r\n (id: string, name: string) => viewport.layerManager.renameLayer(id, name),\r\n [viewport],\r\n );\r\n\r\n const reorderLayer = useCallback(\r\n (id: string, newOrder: number) => viewport.layerManager.reorderLayer(id, newOrder),\r\n [viewport],\r\n );\r\n\r\n const setVisible = useCallback(\r\n (id: string, visible: boolean) => viewport.layerManager.setLayerVisible(id, visible),\r\n [viewport],\r\n );\r\n\r\n const setLocked = useCallback(\r\n (id: string, locked: boolean) => viewport.layerManager.setLayerLocked(id, locked),\r\n [viewport],\r\n );\r\n\r\n const setOpacity = useCallback(\r\n (id: string, opacity: number) => viewport.layerManager.setLayerOpacity(id, opacity),\r\n [viewport],\r\n );\r\n\r\n const setActiveLayer = useCallback(\r\n (id: string) => viewport.layerManager.setActiveLayer(id),\r\n [viewport],\r\n );\r\n\r\n const moveElement = useCallback(\r\n (elementId: string, layerId: string) =>\r\n viewport.layerManager.moveElementToLayer(elementId, layerId),\r\n [viewport],\r\n );\r\n\r\n return {\r\n layers: snapshot.layers,\r\n activeLayerId: snapshot.activeLayerId,\r\n createLayer,\r\n removeLayer,\r\n renameLayer,\r\n reorderLayer,\r\n setVisible,\r\n setLocked,\r\n setOpacity,\r\n setActiveLayer,\r\n moveElement,\r\n };\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport { useViewport } from './use-viewport';\r\n\r\ninterface HistorySnapshot {\r\n canUndo: boolean;\r\n canRedo: boolean;\r\n}\r\n\r\nexport interface UseHistoryResult {\r\n canUndo: boolean;\r\n canRedo: boolean;\r\n undo: () => void;\r\n redo: () => void;\r\n}\r\n\r\n/** `{ canUndo, canRedo, undo, redo }` — re-renders when undo/redo availability changes. */\r\nexport function useHistory(): UseHistoryResult {\r\n const viewport = useViewport();\r\n const cachedRef = useRef<HistorySnapshot>({ canUndo: false, canRedo: false });\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => viewport.history.onChange(onStoreChange),\r\n [viewport],\r\n );\r\n\r\n const getSnapshot = useCallback((): HistorySnapshot => {\r\n const canUndo = viewport.history.canUndo;\r\n const canRedo = viewport.history.canRedo;\r\n const cached = cachedRef.current;\r\n if (cached.canUndo === canUndo && cached.canRedo === canRedo) {\r\n return cached;\r\n }\r\n const next = { canUndo, canRedo };\r\n cachedRef.current = next;\r\n return next;\r\n }, [viewport]);\r\n\r\n const snapshot = useSyncExternalStore(subscribe, getSnapshot);\r\n\r\n const undo = useCallback(() => viewport.undo(), [viewport]);\r\n const redo = useCallback(() => viewport.redo(), [viewport]);\r\n\r\n return {\r\n canUndo: snapshot.canUndo,\r\n canRedo: snapshot.canRedo,\r\n undo,\r\n redo,\r\n };\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport type { CanvasElement, ElementType } from '@fieldnotes/core';\r\nimport { useViewport } from './use-viewport';\r\n\r\nfunction shallowEqual(a: unknown, b: unknown): boolean {\r\n if (Object.is(a, b)) return true;\r\n if (Array.isArray(a) && Array.isArray(b)) {\r\n return a.length === b.length && a.every((v, i) => Object.is(v, b[i]));\r\n }\r\n if (\r\n typeof a === 'object' &&\r\n a !== null &&\r\n typeof b === 'object' &&\r\n b !== null &&\r\n !Array.isArray(a) &&\r\n !Array.isArray(b)\r\n ) {\r\n const ka = Object.keys(a);\r\n const kb = Object.keys(b);\r\n return (\r\n ka.length === kb.length &&\r\n ka.every(\r\n (k) =>\r\n k in (b as Record<string, unknown>) &&\r\n Object.is((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]),\r\n )\r\n );\r\n }\r\n return false;\r\n}\r\n\r\n/** All elements; re-renders on every store mutation (use a selector for hot paths). */\r\nexport function useElements(): CanvasElement[];\r\n/** Elements of one type; re-renders on every store mutation. */\r\nexport function useElements<T extends ElementType>(type: T): Extract<CanvasElement, { type: T }>[];\r\n/**\r\n * Derived value; re-renders only when the selected value changes (per `isEqual`,\r\n * default: one-level shallow compare — arrays by index, plain objects by key, primitives by\r\n * Object.is). Keep the selector referentially stable (useCallback or module scope).\r\n *\r\n * Supply `isEqual` for deeper structures. Note: selectors returning nested fresh objects\r\n * (e.g. `els => els.map(e => ({ ...e.position }))`) still need a custom comparator because\r\n * the default only compares one level deep.\r\n */\r\nexport function useElements<R>(\r\n selector: (elements: CanvasElement[]) => R,\r\n isEqual?: (a: R, b: R) => boolean,\r\n): R;\r\nexport function useElements<R>(\r\n arg?: ElementType | ((elements: CanvasElement[]) => R),\r\n isEqual?: (a: R, b: R) => boolean,\r\n): CanvasElement[] | R {\r\n const viewport = useViewport();\r\n const cachedRef = useRef<CanvasElement[]>([]);\r\n const selectedRef = useRef<{ value: R } | null>(null);\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => viewport.store.onChange(onStoreChange),\r\n [viewport],\r\n );\r\n\r\n const getSnapshot = useCallback((): CanvasElement[] | R => {\r\n if (typeof arg === 'function') {\r\n const next = arg(viewport.store.getAll());\r\n const prev = selectedRef.current;\r\n const equal: (a: R, b: R) => boolean = isEqual ?? (shallowEqual as (a: R, b: R) => boolean);\r\n if (prev !== null && equal(prev.value, next)) {\r\n return prev.value;\r\n }\r\n selectedRef.current = { value: next };\r\n return next;\r\n }\r\n\r\n const next = arg ? viewport.store.getElementsByType(arg) : viewport.store.getAll();\r\n const cached = cachedRef.current;\r\n if (cached.length === next.length && cached.every((el, i) => el === next[i])) {\r\n return cached;\r\n }\r\n cachedRef.current = next;\r\n return next;\r\n }, [viewport, arg, isEqual]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot);\r\n}\r\n","import { useCallback, useSyncExternalStore } from 'react';\nimport { useViewport } from './use-viewport';\n\n/** Reactive selected element ids. Stable array reference — only changes when the selection changes. */\nexport function useSelection(): string[] {\n const viewport = useViewport();\n\n const subscribe = useCallback(\n (onChange: () => void) => viewport.onSelectionChange(onChange),\n [viewport],\n );\n\n const getSnapshot = useCallback(() => viewport.getSelectedIds(), [viewport]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\nimport type { ElementStyle } from '@fieldnotes/core';\nimport { useViewport } from './use-viewport';\n\nfunction styleEqual(a: ElementStyle | null, b: ElementStyle | null): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n const keys: (keyof ElementStyle)[] = ['color', 'fillColor', 'strokeWidth', 'opacity', 'fontSize'];\n return keys.every((k) => a[k] === b[k]);\n}\n\n/**\n * Reactive normalized style of the current selection.\n * Returns `[style, applyPatch]` — `style` is `null` when nothing is selected.\n * `applyPatch` applies an `ElementStyle` patch to every selected element.\n */\nexport function useSelectionStyle(): [ElementStyle | null, (style: ElementStyle) => void] {\n const viewport = useViewport();\n const cacheRef = useRef<ElementStyle | null>(null);\n\n const subscribe = useCallback(\n (onChange: () => void) => {\n const offSel = viewport.onSelectionChange(onChange);\n const offStore = viewport.store.onChange(onChange);\n return () => {\n offSel();\n offStore();\n };\n },\n [viewport],\n );\n\n const getSnapshot = useCallback(() => {\n const next = viewport.getSelectionStyle();\n if (styleEqual(cacheRef.current, next)) return cacheRef.current;\n cacheRef.current = next;\n return next;\n }, [viewport]);\n\n const style = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n const apply = useCallback((s: ElementStyle) => viewport.applyStyleToSelection(s), [viewport]);\n\n return [style, apply];\n}\n"],"mappings":"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","names":["FieldNotesCanvas","useEffect","useRef","useState","useState","useRef","useEffect","useCallback","useRef","useSyncExternalStore","useRef","useCallback","useSyncExternalStore","useCallback","useRef","useSyncExternalStore","useRef","useCallback","useSyncExternalStore","useCallback","useRef","useSyncExternalStore","useRef","useCallback","next","useSyncExternalStore","useCallback","useRef","useSyncExternalStore","useRef","useCallback","useSyncExternalStore","useCallback","useRef","useSyncExternalStore","shallowEqual","useRef","useCallback","next","useSyncExternalStore","useCallback","useSyncExternalStore","useCallback","useSyncExternalStore","useCallback","useRef","useSyncExternalStore","useRef","useCallback","useSyncExternalStore"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fieldnotes/react",
3
- "version": "0.4.2",
3
+ "version": "0.6.0",
4
4
  "description": "React wrapper for Field Notes canvas SDK",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -36,7 +36,7 @@
36
36
  "fieldnotes"
37
37
  ],
38
38
  "peerDependencies": {
39
- "@fieldnotes/core": ">=0.18.0 <1.0.0",
39
+ "@fieldnotes/core": ">=0.27.0 <1.0.0",
40
40
  "react": ">=18 <20",
41
41
  "react-dom": ">=18 <20"
42
42
  },
@@ -50,7 +50,7 @@
50
50
  "react-dom": "^19.2.4",
51
51
  "tsup": "^8.5.1",
52
52
  "vitest": "^4.1.0",
53
- "@fieldnotes/core": "0.19.0"
53
+ "@fieldnotes/core": "0.27.0"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "tsup",