@markup-canvas/react 1.1.7 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,31 @@
1
- import type { MarkupCanvas, Transform } from "@markup-canvas/core";
2
- import { type RefObject, useCallback, useEffect, useRef, useState } from "react";
3
- import type { MarkupCanvasRef, UseMarkupCanvasOptions } from "../types/index.js";
1
+ import type { Transform, WindowAPI } from "@markup-canvas/core";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import type { UseMarkupCanvasOptions } from "../types/index.js";
4
4
 
5
- export function useMarkupCanvas(canvasRef: RefObject<MarkupCanvasRef | null>, options: UseMarkupCanvasOptions = {}) {
6
- const [instance, setInstance] = useState<MarkupCanvas | null>(null);
5
+ interface UseMarkupCanvasHookOptions extends Omit<UseMarkupCanvasOptions, "onReady"> {
6
+ canvasName?: string;
7
+ onError?: () => void;
8
+ onReady?: (canvas: WindowAPI) => void;
9
+ }
10
+
11
+ export function useMarkupCanvas(options: UseMarkupCanvasHookOptions = {}) {
12
+ const { canvasName = "markupCanvas" } = options;
13
+
14
+ const [canvas, setCanvas] = useState<WindowAPI | null>(null);
7
15
  const [transform, setTransform] = useState<Transform>({ scale: 1, translateX: 0, translateY: 0 });
8
16
  const [zoom, setZoom] = useState(1);
9
17
  const [pan, setPan] = useState({ x: 0, y: 0 });
10
18
  const [isReady, setIsReady] = useState(false);
11
19
  const [themeMode, setThemeModeState] = useState<"light" | "dark">("light");
20
+ const [showRulersState, setShowRulersState] = useState(false);
21
+ const [showGridState, setShowGridState] = useState(false);
12
22
 
13
23
  const optionsRef = useRef(options);
14
24
  optionsRef.current = options;
15
25
 
16
- const handleCanvasInstance = useCallback((canvas: MarkupCanvas) => {
17
- setInstance(canvas);
18
- optionsRef.current.onReady?.(canvas);
26
+ const handleCanvasInstance = useCallback((canvasInstance: WindowAPI) => {
27
+ setCanvas(canvasInstance);
28
+ optionsRef.current.onReady?.(canvasInstance);
19
29
  }, []);
20
30
 
21
31
  const handleTransform = useCallback((newTransform: Transform) => {
@@ -33,121 +43,228 @@ export function useMarkupCanvas(canvasRef: RefObject<MarkupCanvasRef | null>, op
33
43
  optionsRef.current.onPanChange?.(newPan);
34
44
  }, []);
35
45
 
36
- const handleReady = useCallback((canvas: MarkupCanvas) => {
46
+ const handleReady = useCallback((canvasInstance: WindowAPI) => {
37
47
  setIsReady(true);
38
- optionsRef.current.onReady?.(canvas);
48
+ optionsRef.current.onReady?.(canvasInstance);
39
49
  }, []);
40
50
 
41
- useEffect(() => {
42
- console.log("useEffect", instance);
51
+ const handleRulersVisibilityChange = useCallback((isVisible: boolean) => {
52
+ setShowRulersState(isVisible);
53
+ }, []);
54
+
55
+ const handleGridVisibilityChange = useCallback((isVisible: boolean) => {
56
+ setShowGridState(isVisible);
57
+ }, []);
43
58
 
44
- if (!instance) {
59
+ // Get instance from window binding
60
+ useEffect(() => {
61
+ if (typeof window === "undefined") {
62
+ optionsRef.current.onError?.();
45
63
  return;
46
64
  }
47
65
 
48
- instance.on("transform", handleTransform);
49
- instance.on("zoom", handleZoom);
50
- instance.on("pan", handlePan);
51
- instance.on("ready", handleReady);
66
+ const windowCanvas = (window as unknown as Record<string, unknown>)[canvasName] as WindowAPI;
67
+
68
+ if (windowCanvas && typeof windowCanvas === "object") {
69
+ handleCanvasInstance(windowCanvas);
70
+ optionsRef.current.onReady?.(windowCanvas);
71
+
72
+ // Set initial state from instance
73
+ if (windowCanvas.state?.transform) {
74
+ const transform = windowCanvas.state.transform;
75
+ setTransform(transform);
76
+ setZoom(transform.scale);
77
+ setPan({ x: transform.translateX, y: transform.translateY });
78
+ }
79
+
80
+ if (windowCanvas.state?.isReady) {
81
+ setIsReady(true);
82
+ optionsRef.current.onReady?.(windowCanvas);
83
+ }
84
+
85
+ setThemeModeState((windowCanvas?.theme?.current as "light" | "dark" | undefined) || "light");
52
86
 
53
- if (instance.transform) {
54
- setTransform(instance.transform);
55
- setZoom(instance.transform.scale);
56
- setPan({ x: instance.transform.translateX, y: instance.transform.translateY });
87
+ if (windowCanvas.rulers?.isVisible?.()) {
88
+ setShowRulersState(true);
89
+ }
90
+ if (windowCanvas.grid?.isVisible?.()) {
91
+ setShowGridState(true);
92
+ }
93
+ } else {
94
+ optionsRef.current.onError?.();
57
95
  }
96
+ }, [canvasName, handleCanvasInstance]);
58
97
 
59
- if (instance.isReady) {
60
- setIsReady(true);
61
- optionsRef.current.onReady?.(instance);
98
+ // Set up event listeners on canvas instance
99
+ useEffect(() => {
100
+ if (!canvas) {
101
+ return;
62
102
  }
63
103
 
104
+ canvas.event.on("transform", handleTransform);
105
+ canvas.event.on("zoom", handleZoom);
106
+ canvas.event.on("pan", handlePan);
107
+ canvas.event.on("ready", handleReady);
108
+ canvas.event.on("rulersVisibility", handleRulersVisibilityChange);
109
+ canvas.event.on("gridVisibility", handleGridVisibilityChange);
110
+
64
111
  return () => {
65
- instance.off("transform", handleTransform);
66
- instance.off("zoom", handleZoom);
67
- instance.off("pan", handlePan);
68
- instance.off("ready", handleReady);
112
+ canvas.event.off("transform", handleTransform);
113
+ canvas.event.off("zoom", handleZoom);
114
+ canvas.event.off("pan", handlePan);
115
+ canvas.event.off("ready", handleReady);
116
+ canvas.event.off("rulersVisibility", handleRulersVisibilityChange);
117
+ canvas.event.off("gridVisibility", handleGridVisibilityChange);
69
118
  };
70
- }, [instance, handleTransform, handleZoom, handlePan, handleReady]);
119
+ }, [canvas, handleTransform, handleZoom, handlePan, handleReady, handleRulersVisibilityChange, handleGridVisibilityChange]);
71
120
 
72
- const zoomIn = useCallback(() => {
73
- canvasRef.current?.zoomIn();
74
- }, [canvasRef]);
121
+ // Listen to window messages for state updates
122
+ useEffect(() => {
123
+ const handleMessage = (event: MessageEvent) => {
124
+ if (event.data.source === "markup-canvas" && event.data.canvasName === canvasName) {
125
+ switch (event.data.event) {
126
+ case "transform":
127
+ handleTransform(event.data.data);
128
+ break;
129
+ case "zoom":
130
+ console.log("zoom", event.data.data);
131
+ handleZoom(event.data.data);
132
+ break;
133
+ case "pan":
134
+ handlePan(event.data.data);
135
+ break;
136
+ case "ready":
137
+ handleReady(event.data.data);
138
+ break;
139
+ default:
140
+ break;
141
+ }
142
+ }
143
+ };
75
144
 
76
- const zoomOut = useCallback(() => {
77
- canvasRef.current?.zoomOut();
78
- }, [canvasRef]);
145
+ window.addEventListener("message", handleMessage);
146
+
147
+ return () => {
148
+ window.removeEventListener("message", handleMessage);
149
+ };
150
+ }, [canvasName, handleTransform, handleZoom, handlePan, handleReady]);
151
+
152
+ // Action methods
153
+ const zoomIn = useCallback(
154
+ (factor = 0.5) => {
155
+ canvas?.zoom?.in?.(factor);
156
+ },
157
+ [canvas]
158
+ );
159
+
160
+ const zoomOut = useCallback(
161
+ (factor = 0.5) => {
162
+ canvas?.zoom?.out?.(factor);
163
+ },
164
+ [canvas]
165
+ );
79
166
 
80
167
  const resetZoom = useCallback(() => {
81
- canvasRef.current?.resetZoom();
82
- }, [canvasRef]);
168
+ canvas?.zoom?.reset?.();
169
+ }, [canvas]);
170
+
171
+ const panLeft = useCallback(
172
+ (distance?: number) => {
173
+ canvas?.pan?.left?.(distance);
174
+ },
175
+ [canvas]
176
+ );
83
177
 
84
- const panTo = useCallback(
85
- (x: number, y: number) => {
86
- canvasRef.current?.panTo(x, y);
178
+ const panRight = useCallback(
179
+ (distance?: number) => {
180
+ canvas?.pan?.right?.(distance);
87
181
  },
88
- [canvasRef]
182
+ [canvas]
183
+ );
184
+
185
+ const panUp = useCallback(
186
+ (distance?: number) => {
187
+ canvas?.pan?.up?.(distance);
188
+ },
189
+ [canvas]
190
+ );
191
+
192
+ const panDown = useCallback(
193
+ (distance?: number) => {
194
+ canvas?.pan?.down?.(distance);
195
+ },
196
+ [canvas]
89
197
  );
90
198
 
91
199
  const fitToContent = useCallback(() => {
92
- canvasRef.current?.fitToContent();
93
- }, [canvasRef]);
200
+ canvas?.zoom?.fitToScreen?.();
201
+ }, [canvas]);
94
202
 
95
203
  const centerContent = useCallback(() => {
96
- canvasRef.current?.centerContent();
97
- }, [canvasRef]);
204
+ canvas?.pan?.toCenter?.();
205
+ }, [canvas]);
206
+
207
+ const resetView = useCallback(() => {
208
+ canvas?.zoom?.resetToCenter?.();
209
+ }, [canvas]);
98
210
 
99
211
  const setTransitionMode = useCallback(
100
212
  (enabled: boolean) => {
101
- if (canvasRef.current?.canvas) {
102
- canvasRef.current.canvas.updateConfig({ enableTransition: enabled });
103
- }
213
+ canvas?.transition?.set?.(enabled);
104
214
  },
105
- [canvasRef]
215
+ [canvas]
106
216
  );
107
217
 
108
218
  const toggleTransitionMode = useCallback(() => {
109
- if (canvasRef.current?.canvas) {
110
- const currentConfig = canvasRef.current.canvas.getConfig();
111
- const newEnableTransition = !currentConfig.enableTransition;
112
- canvasRef.current.canvas.updateConfig({ enableTransition: newEnableTransition });
113
- return newEnableTransition;
114
- }
115
- return false;
116
- }, [canvasRef]);
219
+ return canvas?.transition?.toggle?.() ?? false;
220
+ }, [canvas]);
117
221
 
118
222
  const updateThemeMode = useCallback(
119
223
  (mode: "light" | "dark") => {
120
224
  setThemeModeState(mode);
121
- canvasRef.current?.updateThemeMode(mode);
225
+ canvas?.theme?.update?.(mode);
122
226
  },
123
- [canvasRef]
227
+ [canvas]
124
228
  );
125
229
 
126
230
  const toggleThemeMode = useCallback(() => {
127
- const newMode = themeMode === "light" ? "dark" : "light";
128
- updateThemeMode(newMode);
129
- return newMode;
130
- }, [themeMode, updateThemeMode]);
231
+ canvas?.theme?.toggle?.();
232
+ }, [canvas]);
233
+
234
+ const showRulers = useCallback(() => {
235
+ canvas?.rulers?.show?.();
236
+ }, [canvas]);
237
+
238
+ const hideRulers = useCallback(() => {
239
+ canvas?.rulers?.hide?.();
240
+ }, [canvas]);
131
241
 
132
242
  const toggleRulers = useCallback(() => {
133
- canvasRef.current?.toggleRulers();
134
- }, [canvasRef]);
243
+ canvas?.rulers?.toggle?.();
244
+ }, [canvas]);
135
245
 
136
246
  const areRulersVisible = useCallback(() => {
137
- return canvasRef.current?.areRulersVisible() ?? false;
138
- }, [canvasRef]);
247
+ return canvas?.rulers?.isVisible?.() ?? false;
248
+ }, [canvas]);
249
+
250
+ const showGrid = useCallback(() => {
251
+ canvas?.grid?.show?.();
252
+ }, [canvas]);
253
+
254
+ const hideGrid = useCallback(() => {
255
+ canvas?.grid?.hide?.();
256
+ }, [canvas]);
139
257
 
140
258
  const toggleGrid = useCallback(() => {
141
- canvasRef.current?.toggleGrid();
142
- }, [canvasRef]);
259
+ canvas?.grid?.toggle?.();
260
+ }, [canvas]);
143
261
 
144
262
  const isGridVisible = useCallback(() => {
145
- return canvasRef.current?.isGridVisible() ?? false;
146
- }, [canvasRef]);
263
+ return canvas?.grid?.isVisible?.() ?? false;
264
+ }, [canvas]);
147
265
 
148
266
  return {
149
- canvas: canvasRef.current?.canvas || null,
150
- initCanvasUtils: handleCanvasInstance,
267
+ canvas: canvas,
151
268
  transform,
152
269
  zoom,
153
270
  pan,
@@ -155,17 +272,27 @@ export function useMarkupCanvas(canvasRef: RefObject<MarkupCanvasRef | null>, op
155
272
  zoomIn,
156
273
  zoomOut,
157
274
  resetZoom,
158
- panTo,
275
+ panLeft,
276
+ panRight,
277
+ panUp,
278
+ panDown,
159
279
  fitToContent,
160
280
  centerContent,
281
+ resetView,
161
282
  setTransitionMode,
162
283
  toggleTransitionMode,
163
284
  themeMode,
164
285
  updateThemeMode,
165
286
  toggleThemeMode,
166
287
  toggleRulers,
288
+ showRulers,
289
+ hideRulers,
167
290
  areRulersVisible,
291
+ showRulersState,
168
292
  toggleGrid,
293
+ showGrid,
294
+ hideGrid,
169
295
  isGridVisible,
296
+ showGridState,
170
297
  };
171
298
  }
package/src/index.ts CHANGED
@@ -3,6 +3,6 @@ export type { MarkupCanvas as CoreMarkupCanvas, MarkupCanvasConfig, Transform }
3
3
  export type { MarkupCanvasProps } from "./components/index.js";
4
4
  export { MarkupCanvas } from "./components/index.js";
5
5
  // Hooks
6
- export { useMarkupCanvas, useMarkupCanvasWindow } from "./hooks/index.js";
6
+ export { useMarkupCanvas } from "./hooks/index.js";
7
7
  // Types
8
8
  export type { CanvasEventHandlers, MarkupCanvasRef, UseMarkupCanvasOptions } from "./types/index.js";
@@ -1,43 +0,0 @@
1
- import type { MarkupCanvas, Transform } from "@markup-canvas/core";
2
- import type { UseMarkupCanvasOptions } from "../types/index.js";
3
- interface UseMarkupCanvasWindowOptions extends UseMarkupCanvasOptions {
4
- canvasName?: string;
5
- onCanvasReady?: (canvas: MarkupCanvas) => void;
6
- onCanvasUnavailable?: () => void;
7
- }
8
- export declare function useMarkupCanvasWindow(options?: UseMarkupCanvasWindowOptions): {
9
- canvas: MarkupCanvas | null;
10
- transform: Transform;
11
- zoom: number;
12
- pan: {
13
- x: number;
14
- y: number;
15
- };
16
- isReady: boolean;
17
- zoomIn: (factor?: number) => void;
18
- zoomOut: (factor?: number) => void;
19
- resetZoom: () => void;
20
- panLeft: (distance?: number) => void;
21
- panRight: (distance?: number) => void;
22
- panUp: (distance?: number) => void;
23
- panDown: (distance?: number) => void;
24
- fitToContent: () => void;
25
- centerContent: () => void;
26
- resetView: () => void;
27
- setTransitionMode: (enabled: boolean) => void;
28
- toggleTransitionMode: () => boolean;
29
- themeMode: "light" | "dark";
30
- updateThemeMode: (mode: "light" | "dark") => void;
31
- toggleThemeMode: () => "light" | "dark";
32
- toggleRulers: () => void;
33
- showRulers: () => void;
34
- hideRulers: () => void;
35
- areRulersVisible: () => boolean;
36
- showRulersState: boolean;
37
- toggleGrid: () => void;
38
- showGrid: () => void;
39
- hideGrid: () => void;
40
- isGridVisible: () => boolean;
41
- showGridState: boolean;
42
- };
43
- export {};