@bwp-web/canvas 0.5.1 → 0.6.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.
package/dist/index.cjs CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  Rect: () => import_fabric19.Rect,
36
36
  createCircle: () => createCircle,
37
37
  createCircleAtPoint: () => createCircleAtPoint,
38
+ createHistoryTracker: () => createHistoryTracker,
38
39
  createPolygon: () => createPolygon,
39
40
  createPolygonAtPoint: () => createPolygonAtPoint,
40
41
  createPolygonFromDrag: () => createPolygonFromDrag,
@@ -115,7 +116,9 @@ function Canvas({
115
116
  height,
116
117
  className,
117
118
  style,
118
- onReady
119
+ onReady,
120
+ keyboardShortcuts,
121
+ fabricOptions
119
122
  }) {
120
123
  const canvasRef = (0, import_react.useRef)(null);
121
124
  const wrapperRef = (0, import_react.useRef)(null);
@@ -127,11 +130,12 @@ function Canvas({
127
130
  const initialWidth = isFixedSize ? width : wrapper.clientWidth || 800;
128
131
  const initialHeight = isFixedSize ? height : wrapper.clientHeight || 600;
129
132
  const fabricCanvas = new import_fabric2.Canvas(el, {
133
+ ...fabricOptions,
130
134
  width: initialWidth,
131
135
  height: initialHeight
132
136
  });
133
137
  onReady?.(fabricCanvas);
134
- const cleanupShortcuts = enableKeyboardShortcuts(fabricCanvas);
138
+ const cleanupShortcuts = keyboardShortcuts ? enableKeyboardShortcuts(fabricCanvas) : void 0;
135
139
  let observer;
136
140
  let rafId = 0;
137
141
  if (!isFixedSize) {
@@ -155,7 +159,7 @@ function Canvas({
155
159
  return () => {
156
160
  cancelAnimationFrame(rafId);
157
161
  observer?.disconnect();
158
- cleanupShortcuts();
162
+ cleanupShortcuts?.();
159
163
  fabricCanvas.dispose();
160
164
  };
161
165
  }, []);
@@ -164,7 +168,7 @@ function Canvas({
164
168
  }
165
169
 
166
170
  // src/hooks/useEditCanvas.ts
167
- var import_react2 = require("react");
171
+ var import_react3 = require("react");
168
172
  var import_fabric17 = require("fabric");
169
173
 
170
174
  // src/viewport.ts
@@ -173,7 +177,7 @@ var import_fabric3 = require("fabric");
173
177
  // src/constants.ts
174
178
  var DEFAULT_MIN_ZOOM = 0.2;
175
179
  var DEFAULT_MAX_ZOOM = 10;
176
- var DEFAULT_ZOOM_FACTOR = 1.03;
180
+ var DEFAULT_ZOOM_FACTOR = 0.999;
177
181
  var DEFAULT_ZOOM_STEP = 1.2;
178
182
  var DEFAULT_VIEWPORT_PADDING = 0.05;
179
183
  var BASE_CANVAS_SIZE = 1e3;
@@ -216,7 +220,7 @@ function setupWheelZoom(canvas, bounds, zoomFactor, isEnabled) {
216
220
  e.stopPropagation();
217
221
  const delta = e.deltaY;
218
222
  let zoom = canvas.getZoom();
219
- zoom = delta < 0 ? zoom * zoomFactor : zoom / zoomFactor;
223
+ zoom *= zoomFactor ** delta;
220
224
  zoom = Math.min(Math.max(zoom, bounds.minZoom), bounds.maxZoom);
221
225
  canvas.zoomToPoint(new import_fabric3.Point(e.offsetX, e.offsetY), zoom);
222
226
  };
@@ -324,8 +328,15 @@ function enablePanAndZoom(canvas, options) {
324
328
  const zoomFactor = options?.zoomFactor ?? DEFAULT_ZOOM_FACTOR;
325
329
  let mode = options?.initialMode ?? "select";
326
330
  let enabled = true;
331
+ let currentAnimRafId = null;
327
332
  const isEnabled = () => enabled;
328
333
  const getMode = () => mode;
334
+ function cancelAnimation() {
335
+ if (currentAnimRafId !== null) {
336
+ cancelAnimationFrame(currentAnimRafId);
337
+ currentAnimRafId = null;
338
+ }
339
+ }
329
340
  const handleWheel = setupWheelZoom(canvas, bounds, zoomFactor, isEnabled);
330
341
  const panHandlers = setupMousePan(canvas, getMode, isEnabled);
331
342
  const cleanupPinch = setupPinchZoom(canvas, bounds, isEnabled);
@@ -371,6 +382,7 @@ function enablePanAndZoom(canvas, options) {
371
382
  );
372
383
  },
373
384
  panToObject(object, panOpts) {
385
+ cancelAnimation();
374
386
  const zoom = canvas.getZoom();
375
387
  const objectCenter = object.getCenterPoint();
376
388
  const canvasCenterX = canvas.getWidth() / 2;
@@ -413,12 +425,37 @@ function enablePanAndZoom(canvas, options) {
413
425
  currentY
414
426
  ]);
415
427
  if (t < 1) {
416
- requestAnimationFrame(step);
428
+ currentAnimRafId = requestAnimationFrame(step);
429
+ } else {
430
+ currentAnimRafId = null;
417
431
  }
418
432
  }
419
- requestAnimationFrame(step);
433
+ currentAnimRafId = requestAnimationFrame(step);
434
+ },
435
+ zoomToFit(object, fitOpts) {
436
+ cancelAnimation();
437
+ const padding = fitOpts?.padding ?? 0.1;
438
+ const objWidth = (object.width ?? 0) * (object.scaleX ?? 1);
439
+ const objHeight = (object.height ?? 0) * (object.scaleY ?? 1);
440
+ if (!objWidth || !objHeight) return;
441
+ const canvasWidth = canvas.getWidth();
442
+ const canvasHeight = canvas.getHeight();
443
+ const availableWidth = canvasWidth * (1 - padding * 2);
444
+ const availableHeight = canvasHeight * (1 - padding * 2);
445
+ const zoom = Math.min(
446
+ Math.max(
447
+ Math.min(availableWidth / objWidth, availableHeight / objHeight),
448
+ bounds.minZoom
449
+ ),
450
+ bounds.maxZoom
451
+ );
452
+ const objectCenter = object.getCenterPoint();
453
+ const offsetX = canvasWidth / 2 - objectCenter.x * zoom;
454
+ const offsetY = canvasHeight / 2 - objectCenter.y * zoom;
455
+ canvas.setViewportTransform([zoom, 0, 0, zoom, offsetX, offsetY]);
420
456
  },
421
457
  cleanup() {
458
+ cancelAnimation();
422
459
  canvas.off("mouse:wheel", handleWheel);
423
460
  canvas.off("mouse:down", panHandlers.handleMouseDown);
424
461
  canvas.off("mouse:move", panHandlers.handleMouseMove);
@@ -431,6 +468,9 @@ function resetViewport(canvas) {
431
468
  canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);
432
469
  }
433
470
 
471
+ // src/hooks/shared.ts
472
+ var import_react2 = require("react");
473
+
434
474
  // src/background.ts
435
475
  var import_fabric4 = require("fabric");
436
476
  function getBackgroundImage(canvas) {
@@ -561,7 +601,8 @@ function resizeImageUrl(url, options) {
561
601
  async function setBackgroundImage(canvas, url, options) {
562
602
  const prevContrast = options?.preserveContrast ? getBackgroundContrast(canvas) : void 0;
563
603
  let imageUrl = url;
564
- if (options !== void 0) {
604
+ const hasResizeOptions = options?.maxSize !== void 0 || options?.minSize !== void 0;
605
+ if (hasResizeOptions) {
565
606
  const result = await resizeImageUrl(url, options);
566
607
  imageUrl = result.url;
567
608
  }
@@ -579,29 +620,35 @@ function syncZoom(canvasRef, setZoom) {
579
620
  const canvas = canvasRef.current;
580
621
  if (canvas) setZoom(canvas.getZoom());
581
622
  }
582
- function createViewportActions(canvasRef, viewportRef, setZoom) {
583
- const resetViewport2 = () => {
584
- const canvas = canvasRef.current;
585
- if (!canvas) return;
586
- if (canvas.backgroundImage) {
587
- fitViewportToBackground(canvas);
588
- } else {
589
- resetViewport(canvas);
590
- }
591
- setZoom(canvas.getZoom());
592
- };
593
- const zoomIn = (step) => {
594
- viewportRef.current?.zoomIn(step);
595
- syncZoom(canvasRef, setZoom);
596
- };
597
- const zoomOut = (step) => {
598
- viewportRef.current?.zoomOut(step);
599
- syncZoom(canvasRef, setZoom);
600
- };
601
- const panToObject = (object, panOpts) => {
602
- viewportRef.current?.panToObject(object, panOpts);
603
- };
604
- return { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject };
623
+ function useViewportActions(canvasRef, viewportRef, setZoom) {
624
+ return (0, import_react2.useMemo)(() => {
625
+ const resetViewport2 = () => {
626
+ const canvas = canvasRef.current;
627
+ if (!canvas) return;
628
+ if (canvas.backgroundImage) {
629
+ fitViewportToBackground(canvas);
630
+ } else {
631
+ resetViewport(canvas);
632
+ }
633
+ setZoom(canvas.getZoom());
634
+ };
635
+ const zoomIn = (step) => {
636
+ viewportRef.current?.zoomIn(step);
637
+ syncZoom(canvasRef, setZoom);
638
+ };
639
+ const zoomOut = (step) => {
640
+ viewportRef.current?.zoomOut(step);
641
+ syncZoom(canvasRef, setZoom);
642
+ };
643
+ const panToObject = (object, panOpts) => {
644
+ viewportRef.current?.panToObject(object, panOpts);
645
+ };
646
+ const zoomToFit = (object, fitOpts) => {
647
+ viewportRef.current?.zoomToFit(object, fitOpts);
648
+ syncZoom(canvasRef, setZoom);
649
+ };
650
+ return { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit };
651
+ }, []);
605
652
  }
606
653
  function resolveAlignmentEnabled(enableAlignment, alignmentProp) {
607
654
  if (enableAlignment !== void 0) return enableAlignment;
@@ -1660,7 +1707,7 @@ function createPolygonAtPoint(canvas, point, options) {
1660
1707
  canvas.requestRenderAll();
1661
1708
  return polygon;
1662
1709
  }
1663
- function createPolygonFromDrag(canvas, start, end, style) {
1710
+ function createPolygonFromDrag(canvas, start, end, options) {
1664
1711
  const width = Math.abs(end.x - start.x);
1665
1712
  const height = Math.abs(end.y - start.y);
1666
1713
  const left = Math.min(start.x, end.x) + width / 2;
@@ -1672,16 +1719,16 @@ function createPolygonFromDrag(canvas, start, end, style) {
1672
1719
  { x: width, y: height },
1673
1720
  { x: 0, y: height }
1674
1721
  ],
1675
- { ...DEFAULT_SHAPE_STYLE, left, top, ...style }
1722
+ { ...DEFAULT_SHAPE_STYLE, left, top, ...options }
1676
1723
  );
1677
1724
  canvas.add(polygon);
1678
1725
  canvas.requestRenderAll();
1679
1726
  return polygon;
1680
1727
  }
1681
- function createPolygonFromVertices(canvas, points, style) {
1728
+ function createPolygonFromVertices(canvas, points, options) {
1682
1729
  const polygon = new import_fabric13.Polygon(
1683
1730
  points.map((p) => ({ x: p.x, y: p.y })),
1684
- { ...DEFAULT_SHAPE_STYLE, ...style }
1731
+ { ...DEFAULT_SHAPE_STYLE, ...options }
1685
1732
  );
1686
1733
  canvas.add(polygon);
1687
1734
  canvas.requestRenderAll();
@@ -1805,14 +1852,14 @@ function enableDrawToCreate(canvas, options) {
1805
1852
  const finalize = () => {
1806
1853
  removePreviewElements();
1807
1854
  snapping.clearSnapResult();
1808
- const polygon = createPolygonFromVertices(canvas, points, options?.style);
1855
+ const obj = options?.factory ? options.factory(canvas, [...points]) : createPolygonFromVertices(canvas, points, options?.style);
1809
1856
  if (options?.data) {
1810
- polygon.data = options.data;
1857
+ obj.data = options.data;
1811
1858
  }
1812
1859
  canvas.selection = previousSelection;
1813
1860
  canvas.requestRenderAll();
1814
1861
  restoreViewport(options?.viewport);
1815
- options?.onCreated?.(polygon);
1862
+ options?.onCreated?.(obj);
1816
1863
  points.length = 0;
1817
1864
  };
1818
1865
  const handleMouseDown = (event) => {
@@ -2121,7 +2168,7 @@ function enableVertexEdit(canvas, polygon, options, onExit) {
2121
2168
  });
2122
2169
  canvas.discardActiveObject();
2123
2170
  canvas.requestRenderAll();
2124
- onExit?.();
2171
+ (options?.onExit ?? onExit)?.();
2125
2172
  }
2126
2173
  return cleanup;
2127
2174
  }
@@ -2130,7 +2177,7 @@ function enableVertexEdit(canvas, polygon, options, onExit) {
2130
2177
  var import_fabric16 = require("fabric");
2131
2178
  var strokeBaseMap = /* @__PURE__ */ new WeakMap();
2132
2179
  var borderRadiusBaseMap = /* @__PURE__ */ new WeakMap();
2133
- var VIEW_BORDER_RADIUS = 4;
2180
+ var DEFAULT_VIEW_BORDER_RADIUS = 4;
2134
2181
  function enableScaledStrokes(canvas) {
2135
2182
  function applyScaledStrokes() {
2136
2183
  const zoom = canvas.getZoom();
@@ -2155,13 +2202,14 @@ function enableScaledStrokes(canvas) {
2155
2202
  });
2156
2203
  };
2157
2204
  }
2158
- function enableScaledBorderRadius(canvas) {
2205
+ function enableScaledBorderRadius(canvas, options) {
2206
+ const radius = options?.radius ?? DEFAULT_VIEW_BORDER_RADIUS;
2159
2207
  function applyScaledBorderRadius() {
2160
2208
  canvas.forEachObject((obj) => {
2161
2209
  if (!(obj instanceof import_fabric16.Rect)) return;
2162
2210
  if (!borderRadiusBaseMap.has(obj)) return;
2163
- const rx = VIEW_BORDER_RADIUS / (obj.scaleX ?? 1);
2164
- const ry = VIEW_BORDER_RADIUS / (obj.scaleY ?? 1);
2211
+ const rx = radius / (obj.scaleX ?? 1);
2212
+ const ry = radius / (obj.scaleY ?? 1);
2165
2213
  obj.set({ rx, ry });
2166
2214
  });
2167
2215
  }
@@ -2211,19 +2259,81 @@ function serializeCanvas(canvas, options) {
2211
2259
  obj.set({ rx: base.rx, ry: base.ry });
2212
2260
  }
2213
2261
  });
2262
+ const savedOrigins = /* @__PURE__ */ new Map();
2263
+ canvas.forEachObject((obj) => {
2264
+ if (obj.originX === "left" && obj.originY === "top") return;
2265
+ savedOrigins.set(obj, {
2266
+ originX: obj.originX,
2267
+ originY: obj.originY,
2268
+ left: obj.left ?? 0,
2269
+ top: obj.top ?? 0
2270
+ });
2271
+ const leftTop = obj.getPositionByOrigin("left", "top");
2272
+ obj.set({ originX: "left", originY: "top", left: leftTop.x, top: leftTop.y });
2273
+ });
2274
+ const bg = canvas.backgroundImage;
2275
+ let savedBgOrigin = null;
2276
+ if (bg instanceof import_fabric16.FabricImage && (bg.originX !== "left" || bg.originY !== "top")) {
2277
+ savedBgOrigin = {
2278
+ originX: bg.originX,
2279
+ originY: bg.originY,
2280
+ left: bg.left ?? 0,
2281
+ top: bg.top ?? 0
2282
+ };
2283
+ const leftTop = bg.getPositionByOrigin("left", "top");
2284
+ bg.set({ originX: "left", originY: "top", left: leftTop.x, top: leftTop.y });
2285
+ }
2286
+ const savedData = /* @__PURE__ */ new Map();
2287
+ canvas.forEachObject((obj) => {
2288
+ const base = strokeBaseMap.get(obj) ?? obj.strokeWidth;
2289
+ if (base !== void 0 && base !== 0 && obj.data) {
2290
+ savedData.set(obj, obj.data);
2291
+ obj.data = {
2292
+ ...obj.data,
2293
+ strokeWidthBase: base
2294
+ };
2295
+ }
2296
+ });
2214
2297
  const json = canvas.toObject(properties);
2215
2298
  delete json.backgroundColor;
2299
+ json.backgroundFilters = {
2300
+ opacity: getBackgroundContrast(canvas),
2301
+ inverted: getBackgroundInverted(canvas)
2302
+ };
2216
2303
  scaledWidths.forEach((scaled, obj) => {
2217
2304
  obj.strokeWidth = scaled;
2218
2305
  });
2219
2306
  appliedRadii.forEach((radii, obj) => {
2220
2307
  obj.set({ rx: radii.rx, ry: radii.ry });
2221
2308
  });
2309
+ savedOrigins.forEach((saved, obj) => {
2310
+ obj.set(saved);
2311
+ });
2312
+ if (savedBgOrigin && bg instanceof import_fabric16.FabricImage) {
2313
+ bg.set(savedBgOrigin);
2314
+ }
2315
+ savedData.forEach((originalData, obj) => {
2316
+ obj.data = originalData;
2317
+ });
2222
2318
  return json;
2223
2319
  }
2224
2320
  async function loadCanvas(canvas, json, options) {
2225
2321
  await canvas.loadFromJSON(json);
2226
2322
  canvas.backgroundColor = "";
2323
+ delete canvas.backgroundFilters;
2324
+ const bg = canvas.backgroundImage;
2325
+ if (bg instanceof import_fabric16.FabricImage) {
2326
+ if (bg.originX !== "center" || bg.originY !== "center") {
2327
+ const center = bg.getCenterPoint();
2328
+ bg.set({
2329
+ originX: "center",
2330
+ originY: "center",
2331
+ left: center.x,
2332
+ top: center.y
2333
+ });
2334
+ bg.setCoords();
2335
+ }
2336
+ }
2227
2337
  if (options?.filter) {
2228
2338
  const toRemove = [];
2229
2339
  canvas.forEachObject((obj) => {
@@ -2243,14 +2353,19 @@ async function loadCanvas(canvas, json, options) {
2243
2353
  obj.setCoords();
2244
2354
  });
2245
2355
  canvas.forEachObject((obj) => {
2356
+ const data = obj.data;
2357
+ if (data?.strokeWidthBase !== void 0) {
2358
+ delete data.strokeWidthBase;
2359
+ }
2246
2360
  obj.set(DEFAULT_CONTROL_STYLE);
2247
2361
  if (obj.shapeType === "circle" && obj instanceof import_fabric16.Rect) {
2248
2362
  restoreCircleConstraints(obj);
2249
2363
  }
2250
- if (obj instanceof import_fabric16.Rect && obj.shapeType !== "circle" && obj.data?.type !== "DEVICE") {
2364
+ const borderRadius = options?.borderRadius ?? DEFAULT_VIEW_BORDER_RADIUS;
2365
+ if (borderRadius !== false && obj instanceof import_fabric16.Rect && obj.shapeType !== "circle" && obj.data?.type !== "DEVICE") {
2251
2366
  borderRadiusBaseMap.set(obj, { rx: obj.rx ?? 0, ry: obj.ry ?? 0 });
2252
- const rx = VIEW_BORDER_RADIUS / (obj.scaleX ?? 1);
2253
- const ry = VIEW_BORDER_RADIUS / (obj.scaleY ?? 1);
2367
+ const rx = borderRadius / (obj.scaleX ?? 1);
2368
+ const ry = borderRadius / (obj.scaleY ?? 1);
2254
2369
  obj.set({ rx, ry });
2255
2370
  }
2256
2371
  });
@@ -2258,21 +2373,110 @@ async function loadCanvas(canvas, json, options) {
2258
2373
  return canvas.getObjects();
2259
2374
  }
2260
2375
 
2376
+ // src/history.ts
2377
+ function createHistoryTracker(canvas, options) {
2378
+ const maxSize = options?.maxSize ?? 50;
2379
+ const debounceMs = options?.debounce ?? 300;
2380
+ const snapshots = [];
2381
+ let currentIndex = -1;
2382
+ let isUndoRedo = false;
2383
+ let debounceTimer = null;
2384
+ function captureSnapshot() {
2385
+ if (isUndoRedo) return;
2386
+ const snapshot = serializeCanvas(canvas);
2387
+ if (currentIndex < snapshots.length - 1) {
2388
+ snapshots.length = currentIndex + 1;
2389
+ }
2390
+ snapshots.push(snapshot);
2391
+ if (snapshots.length > maxSize) {
2392
+ snapshots.shift();
2393
+ }
2394
+ currentIndex = snapshots.length - 1;
2395
+ }
2396
+ function debouncedCapture() {
2397
+ if (debounceTimer !== null) {
2398
+ clearTimeout(debounceTimer);
2399
+ }
2400
+ debounceTimer = setTimeout(() => {
2401
+ debounceTimer = null;
2402
+ captureSnapshot();
2403
+ }, debounceMs);
2404
+ }
2405
+ const onChange = () => {
2406
+ if (!isUndoRedo) debouncedCapture();
2407
+ };
2408
+ canvas.on("object:added", onChange);
2409
+ canvas.on("object:modified", onChange);
2410
+ canvas.on("object:removed", onChange);
2411
+ async function loadSnapshot(index) {
2412
+ if (index < 0 || index >= snapshots.length) return;
2413
+ isUndoRedo = true;
2414
+ currentIndex = index;
2415
+ try {
2416
+ await loadCanvas(canvas, snapshots[index]);
2417
+ } finally {
2418
+ isUndoRedo = false;
2419
+ }
2420
+ }
2421
+ return {
2422
+ async undo() {
2423
+ if (currentIndex <= 0) return;
2424
+ await loadSnapshot(currentIndex - 1);
2425
+ },
2426
+ async redo() {
2427
+ if (currentIndex >= snapshots.length - 1) return;
2428
+ await loadSnapshot(currentIndex + 1);
2429
+ },
2430
+ canUndo() {
2431
+ return currentIndex > 0;
2432
+ },
2433
+ canRedo() {
2434
+ return currentIndex < snapshots.length - 1;
2435
+ },
2436
+ pushSnapshot() {
2437
+ if (debounceTimer !== null) {
2438
+ clearTimeout(debounceTimer);
2439
+ debounceTimer = null;
2440
+ }
2441
+ captureSnapshot();
2442
+ },
2443
+ cleanup() {
2444
+ if (debounceTimer !== null) {
2445
+ clearTimeout(debounceTimer);
2446
+ debounceTimer = null;
2447
+ }
2448
+ canvas.off("object:added", onChange);
2449
+ canvas.off("object:modified", onChange);
2450
+ canvas.off("object:removed", onChange);
2451
+ snapshots.length = 0;
2452
+ currentIndex = -1;
2453
+ }
2454
+ };
2455
+ }
2456
+
2261
2457
  // src/hooks/useEditCanvas.ts
2262
2458
  function useEditCanvas(options) {
2263
- const canvasRef = (0, import_react2.useRef)(null);
2264
- const viewportRef = (0, import_react2.useRef)(null);
2265
- const alignmentCleanupRef = (0, import_react2.useRef)(null);
2266
- const rotationSnapCleanupRef = (0, import_react2.useRef)(null);
2267
- const modeCleanupRef = (0, import_react2.useRef)(null);
2268
- const vertexEditCleanupRef = (0, import_react2.useRef)(null);
2269
- const keyboardCleanupRef = (0, import_react2.useRef)(null);
2270
- const [zoom, setZoom] = (0, import_react2.useState)(1);
2271
- const [selected, setSelected] = (0, import_react2.useState)([]);
2272
- const [viewportMode, setViewportModeState] = (0, import_react2.useState)("select");
2273
- const [isEditingVertices, setIsEditingVertices] = (0, import_react2.useState)(false);
2274
- const [isDirty, setIsDirty] = (0, import_react2.useState)(false);
2275
- const setMode = (0, import_react2.useCallback)((setup) => {
2459
+ const canvasRef = (0, import_react3.useRef)(null);
2460
+ const viewportRef = (0, import_react3.useRef)(null);
2461
+ const alignmentCleanupRef = (0, import_react3.useRef)(null);
2462
+ const rotationSnapCleanupRef = (0, import_react3.useRef)(null);
2463
+ const modeCleanupRef = (0, import_react3.useRef)(null);
2464
+ const vertexEditCleanupRef = (0, import_react3.useRef)(null);
2465
+ const keyboardCleanupRef = (0, import_react3.useRef)(null);
2466
+ const historyRef = (0, import_react3.useRef)(null);
2467
+ const optionsRef = (0, import_react3.useRef)(options);
2468
+ optionsRef.current = options;
2469
+ const savedSelectabilityRef = (0, import_react3.useRef)(
2470
+ /* @__PURE__ */ new WeakMap()
2471
+ );
2472
+ const [zoom, setZoom] = (0, import_react3.useState)(1);
2473
+ const [selected, setSelected] = (0, import_react3.useState)([]);
2474
+ const [viewportMode, setViewportModeState] = (0, import_react3.useState)("select");
2475
+ const [isEditingVertices, setIsEditingVertices] = (0, import_react3.useState)(false);
2476
+ const [isDirty, setIsDirty] = (0, import_react3.useState)(false);
2477
+ const [canUndo, setCanUndo] = (0, import_react3.useState)(false);
2478
+ const [canRedo, setCanRedo] = (0, import_react3.useState)(false);
2479
+ const setMode = (0, import_react3.useCallback)((setup) => {
2276
2480
  vertexEditCleanupRef.current?.();
2277
2481
  vertexEditCleanupRef.current = null;
2278
2482
  setIsEditingVertices(false);
@@ -2283,10 +2487,17 @@ function useEditCanvas(options) {
2283
2487
  if (setup === null) {
2284
2488
  canvas.selection = true;
2285
2489
  canvas.forEachObject((obj) => {
2286
- obj.selectable = true;
2287
- obj.evented = true;
2490
+ const saved = savedSelectabilityRef.current.get(obj);
2491
+ obj.selectable = saved?.selectable ?? true;
2492
+ obj.evented = saved?.evented ?? true;
2288
2493
  });
2289
2494
  } else {
2495
+ canvas.forEachObject((obj) => {
2496
+ savedSelectabilityRef.current.set(obj, {
2497
+ selectable: obj.selectable,
2498
+ evented: obj.evented
2499
+ });
2500
+ });
2290
2501
  canvas.selection = false;
2291
2502
  canvas.forEachObject((obj) => {
2292
2503
  obj.selectable = false;
@@ -2298,37 +2509,41 @@ function useEditCanvas(options) {
2298
2509
  }
2299
2510
  }
2300
2511
  }, []);
2301
- const onReady = (0, import_react2.useCallback)(
2512
+ const onReady = (0, import_react3.useCallback)(
2302
2513
  (canvas) => {
2303
2514
  canvasRef.current = canvas;
2304
- if (options?.scaledStrokes !== false) {
2515
+ const opts = optionsRef.current;
2516
+ if (opts?.scaledStrokes !== false) {
2305
2517
  enableScaledStrokes(canvas);
2306
2518
  }
2307
- enableScaledBorderRadius(canvas);
2308
- if (options?.keyboardShortcuts !== false) {
2519
+ if (opts?.borderRadius !== false) {
2520
+ const borderRadiusOpts = typeof opts?.borderRadius === "number" ? { radius: opts.borderRadius } : void 0;
2521
+ enableScaledBorderRadius(canvas, borderRadiusOpts);
2522
+ }
2523
+ if (opts?.keyboardShortcuts !== false) {
2309
2524
  keyboardCleanupRef.current = enableKeyboardShortcuts(canvas);
2310
2525
  }
2311
- setCanvasAlignmentEnabled(canvas, options?.enableAlignment);
2312
- if (options?.panAndZoom !== false) {
2526
+ setCanvasAlignmentEnabled(canvas, opts?.enableAlignment);
2527
+ if (opts?.panAndZoom !== false) {
2313
2528
  viewportRef.current = enablePanAndZoom(
2314
2529
  canvas,
2315
- typeof options?.panAndZoom === "object" ? options.panAndZoom : void 0
2530
+ typeof opts?.panAndZoom === "object" ? opts.panAndZoom : void 0
2316
2531
  );
2317
2532
  }
2318
2533
  const alignmentEnabled = resolveAlignmentEnabled(
2319
- options?.enableAlignment,
2320
- options?.alignment
2534
+ opts?.enableAlignment,
2535
+ opts?.alignment
2321
2536
  );
2322
2537
  if (alignmentEnabled) {
2323
2538
  alignmentCleanupRef.current = enableObjectAlignment(
2324
2539
  canvas,
2325
- typeof options?.alignment === "object" ? options.alignment : void 0
2540
+ typeof opts?.alignment === "object" ? opts.alignment : void 0
2326
2541
  );
2327
2542
  }
2328
- if (options?.rotationSnap !== false) {
2543
+ if (opts?.rotationSnap !== false) {
2329
2544
  rotationSnapCleanupRef.current = enableRotationSnap(
2330
2545
  canvas,
2331
- typeof options?.rotationSnap === "object" ? options.rotationSnap : void 0
2546
+ typeof opts?.rotationSnap === "object" ? opts.rotationSnap : void 0
2332
2547
  );
2333
2548
  }
2334
2549
  canvas.on("mouse:wheel", () => {
@@ -2343,44 +2558,63 @@ function useEditCanvas(options) {
2343
2558
  canvas.on("selection:cleared", () => {
2344
2559
  setSelected([]);
2345
2560
  });
2346
- if (options?.trackChanges) {
2561
+ if (opts?.trackChanges) {
2347
2562
  canvas.on("object:added", () => setIsDirty(true));
2348
2563
  canvas.on("object:removed", () => setIsDirty(true));
2349
2564
  canvas.on("object:modified", () => setIsDirty(true));
2350
2565
  }
2351
- if (options?.vertexEdit !== false) {
2352
- const vertexOpts = typeof options?.vertexEdit === "object" ? options.vertexEdit : void 0;
2566
+ if (opts?.history) {
2567
+ const syncHistoryState = () => {
2568
+ const h = historyRef.current;
2569
+ if (!h) return;
2570
+ setTimeout(() => {
2571
+ setCanUndo(h.canUndo());
2572
+ setCanRedo(h.canRedo());
2573
+ }, 350);
2574
+ };
2575
+ canvas.on("object:added", syncHistoryState);
2576
+ canvas.on("object:removed", syncHistoryState);
2577
+ canvas.on("object:modified", syncHistoryState);
2578
+ }
2579
+ if (opts?.vertexEdit !== false) {
2580
+ const vertexOpts = typeof opts?.vertexEdit === "object" ? opts.vertexEdit : void 0;
2353
2581
  canvas.on("mouse:dblclick", (e) => {
2354
2582
  if (e.target && e.target instanceof import_fabric17.Polygon) {
2355
2583
  vertexEditCleanupRef.current?.();
2356
- vertexEditCleanupRef.current = enableVertexEdit(
2357
- canvas,
2358
- e.target,
2359
- vertexOpts,
2360
- () => {
2584
+ vertexEditCleanupRef.current = enableVertexEdit(canvas, e.target, {
2585
+ ...vertexOpts,
2586
+ onExit: () => {
2361
2587
  vertexEditCleanupRef.current = null;
2362
2588
  setIsEditingVertices(false);
2363
2589
  }
2364
- );
2590
+ });
2365
2591
  setIsEditingVertices(true);
2366
2592
  }
2367
2593
  });
2368
2594
  }
2369
- const onReadyResult = options?.onReady?.(canvas);
2370
- if (options?.autoFitToBackground !== false) {
2595
+ if (opts?.history) {
2596
+ const historyOpts = typeof opts.history === "object" ? opts.history : void 0;
2597
+ historyRef.current = createHistoryTracker(canvas, historyOpts);
2598
+ }
2599
+ const onReadyResult = opts?.onReady?.(canvas);
2600
+ if (opts?.autoFitToBackground !== false) {
2371
2601
  Promise.resolve(onReadyResult).then(() => {
2372
2602
  if (canvas.backgroundImage) {
2373
2603
  fitViewportToBackground(canvas);
2374
2604
  syncZoom(canvasRef, setZoom);
2375
2605
  }
2606
+ historyRef.current?.pushSnapshot();
2607
+ });
2608
+ } else {
2609
+ Promise.resolve(onReadyResult).then(() => {
2610
+ historyRef.current?.pushSnapshot();
2376
2611
  });
2377
2612
  }
2378
2613
  },
2379
2614
  // onReady and panAndZoom are intentionally excluded — we only initialize once
2380
- // eslint-disable-next-line react-hooks/exhaustive-deps
2381
2615
  []
2382
2616
  );
2383
- (0, import_react2.useEffect)(() => {
2617
+ (0, import_react3.useEffect)(() => {
2384
2618
  const canvas = canvasRef.current;
2385
2619
  if (!canvas) return;
2386
2620
  setCanvasAlignmentEnabled(canvas, options?.enableAlignment);
@@ -2398,28 +2632,24 @@ function useEditCanvas(options) {
2398
2632
  alignmentCleanupRef.current = null;
2399
2633
  }
2400
2634
  }, [options?.enableAlignment]);
2401
- const setViewportMode = (0, import_react2.useCallback)((mode) => {
2635
+ const setViewportMode = (0, import_react3.useCallback)((mode) => {
2402
2636
  viewportRef.current?.setMode(mode);
2403
2637
  setViewportModeState(mode);
2404
2638
  }, []);
2405
- const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } = createViewportActions(
2406
- canvasRef,
2407
- viewportRef,
2408
- setZoom
2409
- );
2410
- const setBackground = (0, import_react2.useCallback)(
2639
+ const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
2640
+ const setBackground = (0, import_react3.useCallback)(
2411
2641
  async (url, bgOpts) => {
2412
2642
  const canvas = canvasRef.current;
2413
2643
  if (!canvas) throw new Error("Canvas not ready");
2414
- const resizeOpts = options?.backgroundResize !== false ? typeof options?.backgroundResize === "object" ? { ...options.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
2644
+ const opts = optionsRef.current;
2645
+ const resizeOpts = opts?.backgroundResize !== false ? typeof opts?.backgroundResize === "object" ? { ...opts.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
2415
2646
  const img = await setBackgroundImage(canvas, url, resizeOpts);
2416
- if (options?.autoFitToBackground !== false) {
2647
+ if (opts?.autoFitToBackground !== false) {
2417
2648
  fitViewportToBackground(canvas);
2418
2649
  syncZoom(canvasRef, setZoom);
2419
2650
  }
2420
2651
  return img;
2421
2652
  },
2422
- // eslint-disable-next-line react-hooks/exhaustive-deps
2423
2653
  []
2424
2654
  );
2425
2655
  return {
@@ -2444,7 +2674,9 @@ function useEditCanvas(options) {
2444
2674
  /** Zoom out from the canvas center. Default step: 0.2. */
2445
2675
  zoomOut,
2446
2676
  /** Pan the viewport to center on a specific object. */
2447
- panToObject
2677
+ panToObject,
2678
+ /** Zoom and pan to fit a specific object in the viewport. */
2679
+ zoomToFit
2448
2680
  },
2449
2681
  /** Whether vertex edit mode is currently active (reactive). */
2450
2682
  isEditingVertices,
@@ -2476,12 +2708,32 @@ function useEditCanvas(options) {
2476
2708
  /** Whether the canvas has been modified since the last `resetDirty()` call. Requires `trackChanges: true`. */
2477
2709
  isDirty,
2478
2710
  /** Reset the dirty flag (e.g., after a successful save). */
2479
- resetDirty: (0, import_react2.useCallback)(() => setIsDirty(false), [])
2711
+ resetDirty: (0, import_react3.useCallback)(() => setIsDirty(false), []),
2712
+ /** Undo the last change. Requires `history: true`. */
2713
+ undo: (0, import_react3.useCallback)(async () => {
2714
+ const h = historyRef.current;
2715
+ if (!h) return;
2716
+ await h.undo();
2717
+ setCanUndo(h.canUndo());
2718
+ setCanRedo(h.canRedo());
2719
+ }, []),
2720
+ /** Redo a previously undone change. Requires `history: true`. */
2721
+ redo: (0, import_react3.useCallback)(async () => {
2722
+ const h = historyRef.current;
2723
+ if (!h) return;
2724
+ await h.redo();
2725
+ setCanUndo(h.canUndo());
2726
+ setCanRedo(h.canRedo());
2727
+ }, []),
2728
+ /** Whether an undo operation is available (reactive). Requires `history: true`. */
2729
+ canUndo,
2730
+ /** Whether a redo operation is available (reactive). Requires `history: true`. */
2731
+ canRedo
2480
2732
  };
2481
2733
  }
2482
2734
 
2483
2735
  // src/hooks/useViewCanvas.ts
2484
- var import_react3 = require("react");
2736
+ var import_react4 = require("react");
2485
2737
  function lockCanvas(canvas) {
2486
2738
  canvas.selection = false;
2487
2739
  canvas.forEachObject((obj) => {
@@ -2489,18 +2741,24 @@ function lockCanvas(canvas) {
2489
2741
  });
2490
2742
  }
2491
2743
  function useViewCanvas(options) {
2492
- const canvasRef = (0, import_react3.useRef)(null);
2493
- const viewportRef = (0, import_react3.useRef)(null);
2494
- const [zoom, setZoom] = (0, import_react3.useState)(1);
2495
- const onReady = (0, import_react3.useCallback)(
2744
+ const canvasRef = (0, import_react4.useRef)(null);
2745
+ const viewportRef = (0, import_react4.useRef)(null);
2746
+ const optionsRef = (0, import_react4.useRef)(options);
2747
+ optionsRef.current = options;
2748
+ const [zoom, setZoom] = (0, import_react4.useState)(1);
2749
+ const onReady = (0, import_react4.useCallback)(
2496
2750
  (canvas) => {
2497
2751
  canvasRef.current = canvas;
2498
- if (options?.scaledStrokes !== false) {
2752
+ const opts = optionsRef.current;
2753
+ if (opts?.scaledStrokes !== false) {
2499
2754
  enableScaledStrokes(canvas);
2500
2755
  }
2501
- enableScaledBorderRadius(canvas);
2502
- if (options?.panAndZoom !== false) {
2503
- const panAndZoomOpts = typeof options?.panAndZoom === "object" ? options.panAndZoom : {};
2756
+ if (opts?.borderRadius !== false) {
2757
+ const borderRadiusOpts = typeof opts?.borderRadius === "number" ? { radius: opts.borderRadius } : void 0;
2758
+ enableScaledBorderRadius(canvas, borderRadiusOpts);
2759
+ }
2760
+ if (opts?.panAndZoom !== false) {
2761
+ const panAndZoomOpts = typeof opts?.panAndZoom === "object" ? opts.panAndZoom : {};
2504
2762
  viewportRef.current = enablePanAndZoom(canvas, {
2505
2763
  ...panAndZoomOpts,
2506
2764
  initialMode: "pan"
@@ -2513,8 +2771,8 @@ function useViewCanvas(options) {
2513
2771
  canvas.on("mouse:wheel", () => {
2514
2772
  setZoom(canvas.getZoom());
2515
2773
  });
2516
- const onReadyResult = options?.onReady?.(canvas);
2517
- if (options?.autoFitToBackground !== false) {
2774
+ const onReadyResult = opts?.onReady?.(canvas);
2775
+ if (opts?.autoFitToBackground !== false) {
2518
2776
  Promise.resolve(onReadyResult).then(() => {
2519
2777
  if (canvas.backgroundImage) {
2520
2778
  fitViewportToBackground(canvas);
@@ -2524,26 +2782,21 @@ function useViewCanvas(options) {
2524
2782
  }
2525
2783
  },
2526
2784
  // onReady and panAndZoom are intentionally excluded — we only initialize once
2527
- // eslint-disable-next-line react-hooks/exhaustive-deps
2528
2785
  []
2529
2786
  );
2530
- const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } = createViewportActions(
2531
- canvasRef,
2532
- viewportRef,
2533
- setZoom
2534
- );
2787
+ const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
2535
2788
  const findObject = (id) => {
2536
2789
  const c = canvasRef.current;
2537
2790
  if (!c) return void 0;
2538
2791
  return c.getObjects().find((o) => o.data?.id === id);
2539
2792
  };
2540
- const setObjectStyle = (0, import_react3.useCallback)((id, style) => {
2793
+ const setObjectStyle = (0, import_react4.useCallback)((id, style) => {
2541
2794
  const obj = findObject(id);
2542
2795
  if (!obj) return;
2543
2796
  obj.set(style);
2544
2797
  canvasRef.current.requestRenderAll();
2545
2798
  }, []);
2546
- const setObjectStyles = (0, import_react3.useCallback)(
2799
+ const setObjectStyles = (0, import_react4.useCallback)(
2547
2800
  (styles) => {
2548
2801
  const c = canvasRef.current;
2549
2802
  if (!c) return;
@@ -2564,7 +2817,7 @@ function useViewCanvas(options) {
2564
2817
  },
2565
2818
  []
2566
2819
  );
2567
- const setObjectStyleByType = (0, import_react3.useCallback)(
2820
+ const setObjectStyleByType = (0, import_react4.useCallback)(
2568
2821
  (type, style) => {
2569
2822
  const c = canvasRef.current;
2570
2823
  if (!c) return;
@@ -2595,7 +2848,9 @@ function useViewCanvas(options) {
2595
2848
  /** Zoom out from the canvas center. Default step: 0.2. */
2596
2849
  zoomOut,
2597
2850
  /** Pan the viewport to center on a specific object. */
2598
- panToObject
2851
+ panToObject,
2852
+ /** Zoom and pan to fit a specific object in the viewport. */
2853
+ zoomToFit
2599
2854
  },
2600
2855
  /** Update a single object's visual style by its `data.id`. */
2601
2856
  setObjectStyle,
@@ -2607,11 +2862,11 @@ function useViewCanvas(options) {
2607
2862
  }
2608
2863
 
2609
2864
  // src/hooks/useCanvasEvents.ts
2610
- var import_react4 = require("react");
2865
+ var import_react5 = require("react");
2611
2866
  function useCanvasEvents(canvasRef, events) {
2612
- const eventsRef = (0, import_react4.useRef)(events);
2867
+ const eventsRef = (0, import_react5.useRef)(events);
2613
2868
  eventsRef.current = events;
2614
- (0, import_react4.useEffect)(() => {
2869
+ (0, import_react5.useEffect)(() => {
2615
2870
  const canvas = canvasRef.current;
2616
2871
  if (!canvas) return;
2617
2872
  const wrappers = /* @__PURE__ */ new Map();
@@ -2632,17 +2887,17 @@ function useCanvasEvents(canvasRef, events) {
2632
2887
  }
2633
2888
 
2634
2889
  // src/hooks/useCanvasTooltip.ts
2635
- var import_react5 = require("react");
2890
+ var import_react6 = require("react");
2636
2891
  function useCanvasTooltip(canvasRef, options) {
2637
- const [state, setState] = (0, import_react5.useState)({
2892
+ const [state, setState] = (0, import_react6.useState)({
2638
2893
  visible: false,
2639
2894
  content: null,
2640
2895
  position: { x: 0, y: 0 }
2641
2896
  });
2642
- const hoveredObjectRef = (0, import_react5.useRef)(null);
2643
- const optionsRef = (0, import_react5.useRef)(options);
2897
+ const hoveredObjectRef = (0, import_react6.useRef)(null);
2898
+ const optionsRef = (0, import_react6.useRef)(options);
2644
2899
  optionsRef.current = options;
2645
- (0, import_react5.useEffect)(() => {
2900
+ (0, import_react6.useEffect)(() => {
2646
2901
  const canvas = canvasRef.current;
2647
2902
  if (!canvas) return;
2648
2903
  function calculatePosition(target) {
@@ -2696,13 +2951,13 @@ function useCanvasTooltip(canvasRef, options) {
2696
2951
  }
2697
2952
 
2698
2953
  // src/hooks/useCanvasClick.ts
2699
- var import_react6 = require("react");
2954
+ var import_react7 = require("react");
2700
2955
  function useCanvasClick(canvasRef, onClick, options) {
2701
- const onClickRef = (0, import_react6.useRef)(onClick);
2956
+ const onClickRef = (0, import_react7.useRef)(onClick);
2702
2957
  onClickRef.current = onClick;
2703
- const optionsRef = (0, import_react6.useRef)(options);
2958
+ const optionsRef = (0, import_react7.useRef)(options);
2704
2959
  optionsRef.current = options;
2705
- (0, import_react6.useEffect)(() => {
2960
+ (0, import_react7.useEffect)(() => {
2706
2961
  const canvas = canvasRef.current;
2707
2962
  if (!canvas) return;
2708
2963
  let mouseDown = null;
@@ -2747,13 +3002,13 @@ function useCanvasClick(canvasRef, onClick, options) {
2747
3002
  }
2748
3003
 
2749
3004
  // src/hooks/useObjectOverlay.ts
2750
- var import_react7 = require("react");
3005
+ var import_react8 = require("react");
2751
3006
  var import_fabric18 = require("fabric");
2752
3007
  function useObjectOverlay(canvasRef, object, options) {
2753
- const containerRef = (0, import_react7.useRef)(null);
2754
- const optionsRef = (0, import_react7.useRef)(options);
3008
+ const containerRef = (0, import_react8.useRef)(null);
3009
+ const optionsRef = (0, import_react8.useRef)(options);
2755
3010
  optionsRef.current = options;
2756
- (0, import_react7.useEffect)(() => {
3011
+ (0, import_react8.useEffect)(() => {
2757
3012
  const canvas = canvasRef.current;
2758
3013
  if (!canvas || !object) return;
2759
3014
  function update() {
@@ -2768,36 +3023,41 @@ function useObjectOverlay(canvasRef, object, options) {
2768
3023
  const screenCoords = import_fabric18.util.transformPoint(center, vt);
2769
3024
  const screenWidth = actualWidth * zoom;
2770
3025
  const screenHeight = actualHeight * zoom;
2771
- el.style.left = `${screenCoords.x - screenWidth / 2}px`;
2772
- el.style.top = `${screenCoords.y - screenHeight / 2}px`;
2773
- el.style.width = `${screenWidth}px`;
2774
- el.style.height = `${screenHeight}px`;
2775
3026
  const angle = object.angle ?? 0;
2776
- el.style.rotate = angle !== 0 ? `${angle}deg` : "";
2777
3027
  const opts = optionsRef.current;
2778
- if (opts?.autoScaleContent) {
2779
- const contentScale = Math.min(screenWidth, screenHeight) / 100;
2780
- const clampedScale = Math.max(0.1, Math.min(contentScale, 2));
2781
- el.style.setProperty("--overlay-scale", String(clampedScale));
2782
- if (opts.textSelector) {
2783
- const textMinScale = opts.textMinScale ?? 0.5;
3028
+ if (opts?.autoScaleContent !== false) {
3029
+ el.style.left = `${screenCoords.x - actualWidth / 2}px`;
3030
+ el.style.top = `${screenCoords.y - actualHeight / 2}px`;
3031
+ el.style.width = `${actualWidth}px`;
3032
+ el.style.height = `${actualHeight}px`;
3033
+ el.style.transformOrigin = "center center";
3034
+ el.style.transform = angle !== 0 ? `scale(${zoom}) rotate(${angle}deg)` : `scale(${zoom})`;
3035
+ el.style.rotate = "";
3036
+ el.style.setProperty("--overlay-scale", String(zoom));
3037
+ if (opts?.textSelector) {
3038
+ const textMinScale = opts?.textMinScale ?? 0.5;
2784
3039
  const textEls = el.querySelectorAll(opts.textSelector);
2785
- const display = clampedScale < textMinScale ? "none" : "";
3040
+ const display = zoom < textMinScale ? "none" : "";
2786
3041
  textEls.forEach((t) => {
2787
3042
  t.style.display = display;
2788
3043
  });
2789
3044
  }
3045
+ } else {
3046
+ el.style.left = `${screenCoords.x - screenWidth / 2}px`;
3047
+ el.style.top = `${screenCoords.y - screenHeight / 2}px`;
3048
+ el.style.width = `${screenWidth}px`;
3049
+ el.style.height = `${screenHeight}px`;
3050
+ el.style.transform = "";
3051
+ el.style.rotate = angle !== 0 ? `${angle}deg` : "";
2790
3052
  }
2791
3053
  }
2792
3054
  update();
2793
3055
  canvas.on("after:render", update);
2794
- canvas.on("mouse:wheel", update);
2795
3056
  object.on("moving", update);
2796
3057
  object.on("scaling", update);
2797
3058
  object.on("rotating", update);
2798
3059
  return () => {
2799
3060
  canvas.off("after:render", update);
2800
- canvas.off("mouse:wheel", update);
2801
3061
  object.off("moving", update);
2802
3062
  object.off("scaling", update);
2803
3063
  object.off("rotating", update);
@@ -2825,6 +3085,7 @@ var import_fabric19 = require("fabric");
2825
3085
  Rect,
2826
3086
  createCircle,
2827
3087
  createCircleAtPoint,
3088
+ createHistoryTracker,
2828
3089
  createPolygon,
2829
3090
  createPolygonAtPoint,
2830
3091
  createPolygonFromDrag,