@grida/svg-editor 1.0.0-alpha.2 → 1.0.0-alpha.3

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/react.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import { d as EditorState, f as EditorStyle, k as Providers, o as SvgEditor, t as Commands } from "./editor-klT8wu-x.js";
1
+ import { S as EditorStyle, o as SvgEditor, t as Commands, x as EditorState, z as Providers } from "./editor-Da446SPO.js";
2
+ import { DomSurfaceHandle } from "./dom.js";
3
+ import cmath from "@grida/cmath";
2
4
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
3
5
  import { ReactNode } from "react";
4
6
 
@@ -22,17 +24,40 @@ declare function SvgEditorProvider({
22
24
  type SvgEditorCanvasProps = {
23
25
  className?: string;
24
26
  style?: React.CSSProperties;
27
+ /**
28
+ * Install the default gesture set. Default `true`. See
29
+ * `DomSurfaceOptions.gestures`.
30
+ */
31
+ gestures?: boolean;
32
+ /**
33
+ * Auto-fit the document on initial attach. Default `false`. See
34
+ * `DomSurfaceOptions.fit`.
35
+ */
36
+ fit?: boolean; /** Initial camera transform. Default identity. */
37
+ initial_camera?: cmath.Transform;
38
+ /**
39
+ * Receives the `DomSurfaceHandle` once the surface is attached, and
40
+ * `null` on unmount/detach. Use this to thread `handle.camera` /
41
+ * `handle.gestures` into surrounding chrome (toolbars, badges, etc.).
42
+ */
43
+ onAttach?: (handle: DomSurfaceHandle | null) => void;
25
44
  };
26
45
  /**
27
46
  * Renders the editor's SVG into a `div` and wires it to the DOM surface.
28
47
  *
29
- * Internally calls `attach_dom_surface(editor, { container })` on mount and
30
- * `handle.detach()` on unmount. This is the only UI component the package
31
- * ships; everything else (toolbar, property panel, etc.) is consumer-built.
48
+ * Internally calls `attach_dom_surface(editor, { container, ... })` on
49
+ * mount and `handle.detach()` on unmount. Surface-scoped concerns (camera,
50
+ * gestures) are reached via the `onAttach` callback — there is no global
51
+ * context for them, because a host may mount multiple canvases in the
52
+ * same editor session.
32
53
  */
33
54
  declare function SvgEditorCanvas({
34
55
  className,
35
- style
56
+ style,
57
+ gestures,
58
+ fit,
59
+ initial_camera,
60
+ onAttach
36
61
  }: SvgEditorCanvasProps): _$react_jsx_runtime0.JSX.Element;
37
62
  declare function useSvgEditor(): SvgEditor;
38
63
  /**
@@ -45,5 +70,23 @@ declare function useEditorState<T>(selector: (state: EditorState) => T, equals?:
45
70
  * re-renders (commands themselves don't change identity).
46
71
  */
47
72
  declare function useCommands(): Commands;
73
+ /**
74
+ * Subscribe to a slice of `handle.camera` from a `DomSurfaceHandle`. Pass
75
+ * the handle (or null if it isn't attached yet) and a selector that reads
76
+ * what you need from the camera. The returned value updates on every
77
+ * camera mutation — does NOT bump `editor.state.version`.
78
+ *
79
+ * Typical use: zoom badge in a toolbar.
80
+ *
81
+ * ```tsx
82
+ * const zoom = useCameraSnapshot(handle, (c) => c.zoom, 1);
83
+ * return <div>{Math.round(zoom * 100)}%</div>;
84
+ * ```
85
+ *
86
+ * The `fallback` is what's returned when `handle` is `null` (before mount /
87
+ * after detach). It's also the SSR snapshot value — anything that won't
88
+ * mismatch with the first client render.
89
+ */
90
+ declare function useCameraSnapshot<T>(handle: DomSurfaceHandle | null, selector: (camera: DomSurfaceHandle["camera"]) => T, fallback: T): T;
48
91
  //#endregion
49
- export { SvgEditorCanvas, SvgEditorCanvasProps, SvgEditorProvider, SvgEditorProviderProps, useCommands, useEditorState, useSvgEditor };
92
+ export { SvgEditorCanvas, SvgEditorCanvasProps, SvgEditorProvider, SvgEditorProviderProps, useCameraSnapshot, useCommands, useEditorState, useSvgEditor };
package/dist/react.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_dom = require("./dom-CfP_ZURh.js");
4
- const require_editor = require("./editor-DllAMsDu.js");
3
+ const require_dom = require("./dom-BlJZWpR_.js");
4
+ const require_editor = require("./editor-Eon0043Z.js");
5
5
  let react = require("react");
6
6
  let react_jsx_runtime = require("react/jsx-runtime");
7
7
  //#region src/react.tsx
@@ -38,19 +38,38 @@ function SvgEditorProvider({ svg, providers, style, children }) {
38
38
  /**
39
39
  * Renders the editor's SVG into a `div` and wires it to the DOM surface.
40
40
  *
41
- * Internally calls `attach_dom_surface(editor, { container })` on mount and
42
- * `handle.detach()` on unmount. This is the only UI component the package
43
- * ships; everything else (toolbar, property panel, etc.) is consumer-built.
41
+ * Internally calls `attach_dom_surface(editor, { container, ... })` on
42
+ * mount and `handle.detach()` on unmount. Surface-scoped concerns (camera,
43
+ * gestures) are reached via the `onAttach` callback — there is no global
44
+ * context for them, because a host may mount multiple canvases in the
45
+ * same editor session.
44
46
  */
45
- function SvgEditorCanvas({ className, style }) {
47
+ function SvgEditorCanvas({ className, style, gestures, fit, initial_camera, onAttach }) {
46
48
  const editor = useSvgEditor();
47
49
  const ref = (0, react.useRef)(null);
50
+ const on_attach_ref = (0, react.useRef)(onAttach);
51
+ on_attach_ref.current = onAttach;
52
+ const initial_camera_ref = (0, react.useRef)(initial_camera);
53
+ initial_camera_ref.current = initial_camera;
48
54
  (0, react.useEffect)(() => {
49
55
  const container = ref.current;
50
56
  if (!container) return;
51
- const handle = require_dom.attach_dom_surface(editor, { container });
52
- return () => handle.detach();
53
- }, [editor]);
57
+ const handle = require_dom.attach_dom_surface(editor, {
58
+ container,
59
+ gestures,
60
+ fit,
61
+ initial_camera: initial_camera_ref.current
62
+ });
63
+ on_attach_ref.current?.(handle);
64
+ return () => {
65
+ on_attach_ref.current?.(null);
66
+ handle.detach();
67
+ };
68
+ }, [
69
+ editor,
70
+ gestures,
71
+ fit
72
+ ]);
54
73
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55
74
  ref,
56
75
  className,
@@ -89,9 +108,30 @@ function useCommands() {
89
108
  const editor = useSvgEditor();
90
109
  return (0, react.useMemo)(() => editor.commands, [editor]);
91
110
  }
111
+ /**
112
+ * Subscribe to a slice of `handle.camera` from a `DomSurfaceHandle`. Pass
113
+ * the handle (or null if it isn't attached yet) and a selector that reads
114
+ * what you need from the camera. The returned value updates on every
115
+ * camera mutation — does NOT bump `editor.state.version`.
116
+ *
117
+ * Typical use: zoom badge in a toolbar.
118
+ *
119
+ * ```tsx
120
+ * const zoom = useCameraSnapshot(handle, (c) => c.zoom, 1);
121
+ * return <div>{Math.round(zoom * 100)}%</div>;
122
+ * ```
123
+ *
124
+ * The `fallback` is what's returned when `handle` is `null` (before mount /
125
+ * after detach). It's also the SSR snapshot value — anything that won't
126
+ * mismatch with the first client render.
127
+ */
128
+ function useCameraSnapshot(handle, selector, fallback) {
129
+ return (0, react.useSyncExternalStore)((cb) => handle?.camera.subscribe(cb) ?? (() => {}), () => handle ? selector(handle.camera) : fallback, () => fallback);
130
+ }
92
131
  //#endregion
93
132
  exports.SvgEditorCanvas = SvgEditorCanvas;
94
133
  exports.SvgEditorProvider = SvgEditorProvider;
134
+ exports.useCameraSnapshot = useCameraSnapshot;
95
135
  exports.useCommands = useCommands;
96
136
  exports.useEditorState = useEditorState;
97
137
  exports.useSvgEditor = useSvgEditor;
package/dist/react.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { t as createSvgEditor } from "./editor-M6j8XGO5.mjs";
3
- import { t as attach_dom_surface } from "./dom-kA8NDuVh.mjs";
2
+ import { t as createSvgEditor } from "./editor-DP36h-SE.mjs";
3
+ import { t as attach_dom_surface } from "./dom-D-5D_3o0.mjs";
4
4
  import { createContext, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/react.tsx
@@ -37,19 +37,38 @@ function SvgEditorProvider({ svg, providers, style, children }) {
37
37
  /**
38
38
  * Renders the editor's SVG into a `div` and wires it to the DOM surface.
39
39
  *
40
- * Internally calls `attach_dom_surface(editor, { container })` on mount and
41
- * `handle.detach()` on unmount. This is the only UI component the package
42
- * ships; everything else (toolbar, property panel, etc.) is consumer-built.
40
+ * Internally calls `attach_dom_surface(editor, { container, ... })` on
41
+ * mount and `handle.detach()` on unmount. Surface-scoped concerns (camera,
42
+ * gestures) are reached via the `onAttach` callback — there is no global
43
+ * context for them, because a host may mount multiple canvases in the
44
+ * same editor session.
43
45
  */
44
- function SvgEditorCanvas({ className, style }) {
46
+ function SvgEditorCanvas({ className, style, gestures, fit, initial_camera, onAttach }) {
45
47
  const editor = useSvgEditor();
46
48
  const ref = useRef(null);
49
+ const on_attach_ref = useRef(onAttach);
50
+ on_attach_ref.current = onAttach;
51
+ const initial_camera_ref = useRef(initial_camera);
52
+ initial_camera_ref.current = initial_camera;
47
53
  useEffect(() => {
48
54
  const container = ref.current;
49
55
  if (!container) return;
50
- const handle = attach_dom_surface(editor, { container });
51
- return () => handle.detach();
52
- }, [editor]);
56
+ const handle = attach_dom_surface(editor, {
57
+ container,
58
+ gestures,
59
+ fit,
60
+ initial_camera: initial_camera_ref.current
61
+ });
62
+ on_attach_ref.current?.(handle);
63
+ return () => {
64
+ on_attach_ref.current?.(null);
65
+ handle.detach();
66
+ };
67
+ }, [
68
+ editor,
69
+ gestures,
70
+ fit
71
+ ]);
53
72
  return /* @__PURE__ */ jsx("div", {
54
73
  ref,
55
74
  className,
@@ -88,5 +107,25 @@ function useCommands() {
88
107
  const editor = useSvgEditor();
89
108
  return useMemo(() => editor.commands, [editor]);
90
109
  }
110
+ /**
111
+ * Subscribe to a slice of `handle.camera` from a `DomSurfaceHandle`. Pass
112
+ * the handle (or null if it isn't attached yet) and a selector that reads
113
+ * what you need from the camera. The returned value updates on every
114
+ * camera mutation — does NOT bump `editor.state.version`.
115
+ *
116
+ * Typical use: zoom badge in a toolbar.
117
+ *
118
+ * ```tsx
119
+ * const zoom = useCameraSnapshot(handle, (c) => c.zoom, 1);
120
+ * return <div>{Math.round(zoom * 100)}%</div>;
121
+ * ```
122
+ *
123
+ * The `fallback` is what's returned when `handle` is `null` (before mount /
124
+ * after detach). It's also the SSR snapshot value — anything that won't
125
+ * mismatch with the first client render.
126
+ */
127
+ function useCameraSnapshot(handle, selector, fallback) {
128
+ return useSyncExternalStore((cb) => handle?.camera.subscribe(cb) ?? (() => {}), () => handle ? selector(handle.camera) : fallback, () => fallback);
129
+ }
91
130
  //#endregion
92
- export { SvgEditorCanvas, SvgEditorProvider, useCommands, useEditorState, useSvgEditor };
131
+ export { SvgEditorCanvas, SvgEditorProvider, useCameraSnapshot, useCommands, useEditorState, useSvgEditor };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grida/svg-editor",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.3",
4
4
  "description": "Headless SVG editor (experimental).",
5
5
  "license": "MIT",
6
6
  "author": "Grida",
@@ -35,10 +35,10 @@
35
35
  "dependencies": {
36
36
  "svg-pathdata": "^7.2.0",
37
37
  "@grida/cmath": "0.1.0",
38
- "@grida/history": "0.1.0",
39
- "@grida/text-editor": "0.1.0",
40
38
  "@grida/hud": "0.1.0",
41
- "@grida/keybinding": "0.1.0"
39
+ "@grida/keybinding": "0.1.0",
40
+ "@grida/history": "0.1.0",
41
+ "@grida/text-editor": "0.1.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/react": "^19",