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