@markup-canvas/react 1.1.7 → 1.2.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.
@@ -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,227 @@ 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
+ handleZoom(event.data.data);
131
+ break;
132
+ case "pan":
133
+ handlePan(event.data.data);
134
+ break;
135
+ case "ready":
136
+ handleReady(event.data.data);
137
+ break;
138
+ default:
139
+ break;
140
+ }
141
+ }
142
+ };
75
143
 
76
- const zoomOut = useCallback(() => {
77
- canvasRef.current?.zoomOut();
78
- }, [canvasRef]);
144
+ window.addEventListener("message", handleMessage);
145
+
146
+ return () => {
147
+ window.removeEventListener("message", handleMessage);
148
+ };
149
+ }, [canvasName, handleTransform, handleZoom, handlePan, handleReady]);
150
+
151
+ // Action methods
152
+ const zoomIn = useCallback(
153
+ (factor = 0.5) => {
154
+ canvas?.zoom?.in?.(factor);
155
+ },
156
+ [canvas]
157
+ );
158
+
159
+ const zoomOut = useCallback(
160
+ (factor = 0.5) => {
161
+ canvas?.zoom?.out?.(factor);
162
+ },
163
+ [canvas]
164
+ );
79
165
 
80
166
  const resetZoom = useCallback(() => {
81
- canvasRef.current?.resetZoom();
82
- }, [canvasRef]);
167
+ canvas?.zoom?.reset?.();
168
+ }, [canvas]);
169
+
170
+ const panLeft = useCallback(
171
+ (distance?: number) => {
172
+ canvas?.pan?.left?.(distance);
173
+ },
174
+ [canvas]
175
+ );
83
176
 
84
- const panTo = useCallback(
85
- (x: number, y: number) => {
86
- canvasRef.current?.panTo(x, y);
177
+ const panRight = useCallback(
178
+ (distance?: number) => {
179
+ canvas?.pan?.right?.(distance);
87
180
  },
88
- [canvasRef]
181
+ [canvas]
182
+ );
183
+
184
+ const panUp = useCallback(
185
+ (distance?: number) => {
186
+ canvas?.pan?.up?.(distance);
187
+ },
188
+ [canvas]
189
+ );
190
+
191
+ const panDown = useCallback(
192
+ (distance?: number) => {
193
+ canvas?.pan?.down?.(distance);
194
+ },
195
+ [canvas]
89
196
  );
90
197
 
91
198
  const fitToContent = useCallback(() => {
92
- canvasRef.current?.fitToContent();
93
- }, [canvasRef]);
199
+ canvas?.zoom?.fitToScreen?.();
200
+ }, [canvas]);
94
201
 
95
202
  const centerContent = useCallback(() => {
96
- canvasRef.current?.centerContent();
97
- }, [canvasRef]);
203
+ canvas?.pan?.toCenter?.();
204
+ }, [canvas]);
205
+
206
+ const resetView = useCallback(() => {
207
+ canvas?.zoom?.resetToCenter?.();
208
+ }, [canvas]);
98
209
 
99
210
  const setTransitionMode = useCallback(
100
211
  (enabled: boolean) => {
101
- if (canvasRef.current?.canvas) {
102
- canvasRef.current.canvas.updateConfig({ enableTransition: enabled });
103
- }
212
+ canvas?.transition?.set?.(enabled);
104
213
  },
105
- [canvasRef]
214
+ [canvas]
106
215
  );
107
216
 
108
217
  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]);
218
+ return canvas?.transition?.toggle?.() ?? false;
219
+ }, [canvas]);
117
220
 
118
221
  const updateThemeMode = useCallback(
119
222
  (mode: "light" | "dark") => {
120
223
  setThemeModeState(mode);
121
- canvasRef.current?.updateThemeMode(mode);
224
+ canvas?.theme?.update?.(mode);
122
225
  },
123
- [canvasRef]
226
+ [canvas]
124
227
  );
125
228
 
126
229
  const toggleThemeMode = useCallback(() => {
127
- const newMode = themeMode === "light" ? "dark" : "light";
128
- updateThemeMode(newMode);
129
- return newMode;
130
- }, [themeMode, updateThemeMode]);
230
+ canvas?.theme?.toggle?.();
231
+ }, [canvas]);
232
+
233
+ const showRulers = useCallback(() => {
234
+ canvas?.rulers?.show?.();
235
+ }, [canvas]);
236
+
237
+ const hideRulers = useCallback(() => {
238
+ canvas?.rulers?.hide?.();
239
+ }, [canvas]);
131
240
 
132
241
  const toggleRulers = useCallback(() => {
133
- canvasRef.current?.toggleRulers();
134
- }, [canvasRef]);
242
+ canvas?.rulers?.toggle?.();
243
+ }, [canvas]);
135
244
 
136
245
  const areRulersVisible = useCallback(() => {
137
- return canvasRef.current?.areRulersVisible() ?? false;
138
- }, [canvasRef]);
246
+ return canvas?.rulers?.isVisible?.() ?? false;
247
+ }, [canvas]);
248
+
249
+ const showGrid = useCallback(() => {
250
+ canvas?.grid?.show?.();
251
+ }, [canvas]);
252
+
253
+ const hideGrid = useCallback(() => {
254
+ canvas?.grid?.hide?.();
255
+ }, [canvas]);
139
256
 
140
257
  const toggleGrid = useCallback(() => {
141
- canvasRef.current?.toggleGrid();
142
- }, [canvasRef]);
258
+ canvas?.grid?.toggle?.();
259
+ }, [canvas]);
143
260
 
144
261
  const isGridVisible = useCallback(() => {
145
- return canvasRef.current?.isGridVisible() ?? false;
146
- }, [canvasRef]);
262
+ return canvas?.grid?.isVisible?.() ?? false;
263
+ }, [canvas]);
147
264
 
148
265
  return {
149
- canvas: canvasRef.current?.canvas || null,
150
- initCanvasUtils: handleCanvasInstance,
266
+ canvas: canvas,
151
267
  transform,
152
268
  zoom,
153
269
  pan,
@@ -155,17 +271,27 @@ export function useMarkupCanvas(canvasRef: RefObject<MarkupCanvasRef | null>, op
155
271
  zoomIn,
156
272
  zoomOut,
157
273
  resetZoom,
158
- panTo,
274
+ panLeft,
275
+ panRight,
276
+ panUp,
277
+ panDown,
159
278
  fitToContent,
160
279
  centerContent,
280
+ resetView,
161
281
  setTransitionMode,
162
282
  toggleTransitionMode,
163
283
  themeMode,
164
284
  updateThemeMode,
165
285
  toggleThemeMode,
166
286
  toggleRulers,
287
+ showRulers,
288
+ hideRulers,
167
289
  areRulersVisible,
290
+ showRulersState,
168
291
  toggleGrid,
292
+ showGrid,
293
+ hideGrid,
169
294
  isGridVisible,
295
+ showGridState,
170
296
  };
171
297
  }
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 {};