@bwp-web/canvas 0.5.1 → 0.6.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.
- package/dist/Canvas/Canvas.d.ts +12 -1
- package/dist/Canvas/Canvas.d.ts.map +1 -1
- package/dist/background.d.ts.map +1 -1
- package/dist/constants.d.ts +2 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/fabricAugmentation.d.ts +3 -1
- package/dist/fabricAugmentation.d.ts.map +1 -1
- package/dist/history.d.ts +32 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/hooks/shared.d.ts +6 -4
- package/dist/hooks/shared.d.ts.map +1 -1
- package/dist/hooks/useEditCanvas.d.ts +22 -0
- package/dist/hooks/useEditCanvas.d.ts.map +1 -1
- package/dist/hooks/useObjectOverlay.d.ts +4 -3
- package/dist/hooks/useObjectOverlay.d.ts.map +1 -1
- package/dist/hooks/useViewCanvas.d.ts +7 -0
- package/dist/hooks/useViewCanvas.d.ts.map +1 -1
- package/dist/index.cjs +363 -150
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +326 -111
- package/dist/index.js.map +1 -1
- package/dist/interactions/drawToCreate.d.ts +8 -3
- package/dist/interactions/drawToCreate.d.ts.map +1 -1
- package/dist/interactions/vertexEdit.d.ts +5 -1
- package/dist/interactions/vertexEdit.d.ts.map +1 -1
- package/dist/serialization.d.ts +13 -3
- package/dist/serialization.d.ts.map +1 -1
- package/dist/shapes/polygon.d.ts +2 -2
- package/dist/shapes/polygon.d.ts.map +1 -1
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/viewport.d.ts +12 -2
- package/dist/viewport.d.ts.map +1 -1
- package/package.json +1 -1
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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,
|
|
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, ...
|
|
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,
|
|
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, ...
|
|
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
|
|
1855
|
+
const obj = options?.factory ? options.factory(canvas, [...points]) : createPolygonFromVertices(canvas, points, options?.style);
|
|
1809
1856
|
if (options?.data) {
|
|
1810
|
-
|
|
1857
|
+
obj.data = options.data;
|
|
1811
1858
|
}
|
|
1812
1859
|
canvas.selection = previousSelection;
|
|
1813
1860
|
canvas.requestRenderAll();
|
|
1814
1861
|
restoreViewport(options?.viewport);
|
|
1815
|
-
options?.onCreated?.(
|
|
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
|
|
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 =
|
|
2164
|
-
const ry =
|
|
2211
|
+
const rx = radius / (obj.scaleX ?? 1);
|
|
2212
|
+
const ry = radius / (obj.scaleY ?? 1);
|
|
2165
2213
|
obj.set({ rx, ry });
|
|
2166
2214
|
});
|
|
2167
2215
|
}
|
|
@@ -2224,6 +2272,20 @@ function serializeCanvas(canvas, options) {
|
|
|
2224
2272
|
async function loadCanvas(canvas, json, options) {
|
|
2225
2273
|
await canvas.loadFromJSON(json);
|
|
2226
2274
|
canvas.backgroundColor = "";
|
|
2275
|
+
delete canvas.backgroundFilters;
|
|
2276
|
+
const bg = canvas.backgroundImage;
|
|
2277
|
+
if (bg instanceof import_fabric16.FabricImage) {
|
|
2278
|
+
if (bg.originX !== "center" || bg.originY !== "center") {
|
|
2279
|
+
const center = bg.getCenterPoint();
|
|
2280
|
+
bg.set({
|
|
2281
|
+
originX: "center",
|
|
2282
|
+
originY: "center",
|
|
2283
|
+
left: center.x,
|
|
2284
|
+
top: center.y
|
|
2285
|
+
});
|
|
2286
|
+
bg.setCoords();
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2227
2289
|
if (options?.filter) {
|
|
2228
2290
|
const toRemove = [];
|
|
2229
2291
|
canvas.forEachObject((obj) => {
|
|
@@ -2243,14 +2305,19 @@ async function loadCanvas(canvas, json, options) {
|
|
|
2243
2305
|
obj.setCoords();
|
|
2244
2306
|
});
|
|
2245
2307
|
canvas.forEachObject((obj) => {
|
|
2308
|
+
const data = obj.data;
|
|
2309
|
+
if (data?.strokeWidthBase !== void 0) {
|
|
2310
|
+
delete data.strokeWidthBase;
|
|
2311
|
+
}
|
|
2246
2312
|
obj.set(DEFAULT_CONTROL_STYLE);
|
|
2247
2313
|
if (obj.shapeType === "circle" && obj instanceof import_fabric16.Rect) {
|
|
2248
2314
|
restoreCircleConstraints(obj);
|
|
2249
2315
|
}
|
|
2250
|
-
|
|
2316
|
+
const borderRadius = options?.borderRadius ?? DEFAULT_VIEW_BORDER_RADIUS;
|
|
2317
|
+
if (borderRadius !== false && obj instanceof import_fabric16.Rect && obj.shapeType !== "circle" && obj.data?.type !== "DEVICE") {
|
|
2251
2318
|
borderRadiusBaseMap.set(obj, { rx: obj.rx ?? 0, ry: obj.ry ?? 0 });
|
|
2252
|
-
const rx =
|
|
2253
|
-
const ry =
|
|
2319
|
+
const rx = borderRadius / (obj.scaleX ?? 1);
|
|
2320
|
+
const ry = borderRadius / (obj.scaleY ?? 1);
|
|
2254
2321
|
obj.set({ rx, ry });
|
|
2255
2322
|
}
|
|
2256
2323
|
});
|
|
@@ -2258,21 +2325,110 @@ async function loadCanvas(canvas, json, options) {
|
|
|
2258
2325
|
return canvas.getObjects();
|
|
2259
2326
|
}
|
|
2260
2327
|
|
|
2328
|
+
// src/history.ts
|
|
2329
|
+
function createHistoryTracker(canvas, options) {
|
|
2330
|
+
const maxSize = options?.maxSize ?? 50;
|
|
2331
|
+
const debounceMs = options?.debounce ?? 300;
|
|
2332
|
+
const snapshots = [];
|
|
2333
|
+
let currentIndex = -1;
|
|
2334
|
+
let isUndoRedo = false;
|
|
2335
|
+
let debounceTimer = null;
|
|
2336
|
+
function captureSnapshot() {
|
|
2337
|
+
if (isUndoRedo) return;
|
|
2338
|
+
const snapshot = serializeCanvas(canvas);
|
|
2339
|
+
if (currentIndex < snapshots.length - 1) {
|
|
2340
|
+
snapshots.length = currentIndex + 1;
|
|
2341
|
+
}
|
|
2342
|
+
snapshots.push(snapshot);
|
|
2343
|
+
if (snapshots.length > maxSize) {
|
|
2344
|
+
snapshots.shift();
|
|
2345
|
+
}
|
|
2346
|
+
currentIndex = snapshots.length - 1;
|
|
2347
|
+
}
|
|
2348
|
+
function debouncedCapture() {
|
|
2349
|
+
if (debounceTimer !== null) {
|
|
2350
|
+
clearTimeout(debounceTimer);
|
|
2351
|
+
}
|
|
2352
|
+
debounceTimer = setTimeout(() => {
|
|
2353
|
+
debounceTimer = null;
|
|
2354
|
+
captureSnapshot();
|
|
2355
|
+
}, debounceMs);
|
|
2356
|
+
}
|
|
2357
|
+
const onChange = () => {
|
|
2358
|
+
if (!isUndoRedo) debouncedCapture();
|
|
2359
|
+
};
|
|
2360
|
+
canvas.on("object:added", onChange);
|
|
2361
|
+
canvas.on("object:modified", onChange);
|
|
2362
|
+
canvas.on("object:removed", onChange);
|
|
2363
|
+
async function loadSnapshot(index) {
|
|
2364
|
+
if (index < 0 || index >= snapshots.length) return;
|
|
2365
|
+
isUndoRedo = true;
|
|
2366
|
+
currentIndex = index;
|
|
2367
|
+
try {
|
|
2368
|
+
await loadCanvas(canvas, snapshots[index]);
|
|
2369
|
+
} finally {
|
|
2370
|
+
isUndoRedo = false;
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
return {
|
|
2374
|
+
async undo() {
|
|
2375
|
+
if (currentIndex <= 0) return;
|
|
2376
|
+
await loadSnapshot(currentIndex - 1);
|
|
2377
|
+
},
|
|
2378
|
+
async redo() {
|
|
2379
|
+
if (currentIndex >= snapshots.length - 1) return;
|
|
2380
|
+
await loadSnapshot(currentIndex + 1);
|
|
2381
|
+
},
|
|
2382
|
+
canUndo() {
|
|
2383
|
+
return currentIndex > 0;
|
|
2384
|
+
},
|
|
2385
|
+
canRedo() {
|
|
2386
|
+
return currentIndex < snapshots.length - 1;
|
|
2387
|
+
},
|
|
2388
|
+
pushSnapshot() {
|
|
2389
|
+
if (debounceTimer !== null) {
|
|
2390
|
+
clearTimeout(debounceTimer);
|
|
2391
|
+
debounceTimer = null;
|
|
2392
|
+
}
|
|
2393
|
+
captureSnapshot();
|
|
2394
|
+
},
|
|
2395
|
+
cleanup() {
|
|
2396
|
+
if (debounceTimer !== null) {
|
|
2397
|
+
clearTimeout(debounceTimer);
|
|
2398
|
+
debounceTimer = null;
|
|
2399
|
+
}
|
|
2400
|
+
canvas.off("object:added", onChange);
|
|
2401
|
+
canvas.off("object:modified", onChange);
|
|
2402
|
+
canvas.off("object:removed", onChange);
|
|
2403
|
+
snapshots.length = 0;
|
|
2404
|
+
currentIndex = -1;
|
|
2405
|
+
}
|
|
2406
|
+
};
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2261
2409
|
// src/hooks/useEditCanvas.ts
|
|
2262
2410
|
function useEditCanvas(options) {
|
|
2263
|
-
const canvasRef = (0,
|
|
2264
|
-
const viewportRef = (0,
|
|
2265
|
-
const alignmentCleanupRef = (0,
|
|
2266
|
-
const rotationSnapCleanupRef = (0,
|
|
2267
|
-
const modeCleanupRef = (0,
|
|
2268
|
-
const vertexEditCleanupRef = (0,
|
|
2269
|
-
const keyboardCleanupRef = (0,
|
|
2270
|
-
const
|
|
2271
|
-
const
|
|
2272
|
-
|
|
2273
|
-
const
|
|
2274
|
-
|
|
2275
|
-
|
|
2411
|
+
const canvasRef = (0, import_react3.useRef)(null);
|
|
2412
|
+
const viewportRef = (0, import_react3.useRef)(null);
|
|
2413
|
+
const alignmentCleanupRef = (0, import_react3.useRef)(null);
|
|
2414
|
+
const rotationSnapCleanupRef = (0, import_react3.useRef)(null);
|
|
2415
|
+
const modeCleanupRef = (0, import_react3.useRef)(null);
|
|
2416
|
+
const vertexEditCleanupRef = (0, import_react3.useRef)(null);
|
|
2417
|
+
const keyboardCleanupRef = (0, import_react3.useRef)(null);
|
|
2418
|
+
const historyRef = (0, import_react3.useRef)(null);
|
|
2419
|
+
const optionsRef = (0, import_react3.useRef)(options);
|
|
2420
|
+
optionsRef.current = options;
|
|
2421
|
+
const savedSelectabilityRef = (0, import_react3.useRef)(
|
|
2422
|
+
/* @__PURE__ */ new WeakMap()
|
|
2423
|
+
);
|
|
2424
|
+
const [zoom, setZoom] = (0, import_react3.useState)(1);
|
|
2425
|
+
const [selected, setSelected] = (0, import_react3.useState)([]);
|
|
2426
|
+
const [viewportMode, setViewportModeState] = (0, import_react3.useState)("select");
|
|
2427
|
+
const [isEditingVertices, setIsEditingVertices] = (0, import_react3.useState)(false);
|
|
2428
|
+
const [isDirty, setIsDirty] = (0, import_react3.useState)(false);
|
|
2429
|
+
const [canUndo, setCanUndo] = (0, import_react3.useState)(false);
|
|
2430
|
+
const [canRedo, setCanRedo] = (0, import_react3.useState)(false);
|
|
2431
|
+
const setMode = (0, import_react3.useCallback)((setup) => {
|
|
2276
2432
|
vertexEditCleanupRef.current?.();
|
|
2277
2433
|
vertexEditCleanupRef.current = null;
|
|
2278
2434
|
setIsEditingVertices(false);
|
|
@@ -2283,10 +2439,17 @@ function useEditCanvas(options) {
|
|
|
2283
2439
|
if (setup === null) {
|
|
2284
2440
|
canvas.selection = true;
|
|
2285
2441
|
canvas.forEachObject((obj) => {
|
|
2286
|
-
|
|
2287
|
-
obj.
|
|
2442
|
+
const saved = savedSelectabilityRef.current.get(obj);
|
|
2443
|
+
obj.selectable = saved?.selectable ?? true;
|
|
2444
|
+
obj.evented = saved?.evented ?? true;
|
|
2288
2445
|
});
|
|
2289
2446
|
} else {
|
|
2447
|
+
canvas.forEachObject((obj) => {
|
|
2448
|
+
savedSelectabilityRef.current.set(obj, {
|
|
2449
|
+
selectable: obj.selectable,
|
|
2450
|
+
evented: obj.evented
|
|
2451
|
+
});
|
|
2452
|
+
});
|
|
2290
2453
|
canvas.selection = false;
|
|
2291
2454
|
canvas.forEachObject((obj) => {
|
|
2292
2455
|
obj.selectable = false;
|
|
@@ -2298,37 +2461,41 @@ function useEditCanvas(options) {
|
|
|
2298
2461
|
}
|
|
2299
2462
|
}
|
|
2300
2463
|
}, []);
|
|
2301
|
-
const onReady = (0,
|
|
2464
|
+
const onReady = (0, import_react3.useCallback)(
|
|
2302
2465
|
(canvas) => {
|
|
2303
2466
|
canvasRef.current = canvas;
|
|
2304
|
-
|
|
2467
|
+
const opts = optionsRef.current;
|
|
2468
|
+
if (opts?.scaledStrokes !== false) {
|
|
2305
2469
|
enableScaledStrokes(canvas);
|
|
2306
2470
|
}
|
|
2307
|
-
|
|
2308
|
-
|
|
2471
|
+
if (opts?.borderRadius !== false) {
|
|
2472
|
+
const borderRadiusOpts = typeof opts?.borderRadius === "number" ? { radius: opts.borderRadius } : void 0;
|
|
2473
|
+
enableScaledBorderRadius(canvas, borderRadiusOpts);
|
|
2474
|
+
}
|
|
2475
|
+
if (opts?.keyboardShortcuts !== false) {
|
|
2309
2476
|
keyboardCleanupRef.current = enableKeyboardShortcuts(canvas);
|
|
2310
2477
|
}
|
|
2311
|
-
setCanvasAlignmentEnabled(canvas,
|
|
2312
|
-
if (
|
|
2478
|
+
setCanvasAlignmentEnabled(canvas, opts?.enableAlignment);
|
|
2479
|
+
if (opts?.panAndZoom !== false) {
|
|
2313
2480
|
viewportRef.current = enablePanAndZoom(
|
|
2314
2481
|
canvas,
|
|
2315
|
-
typeof
|
|
2482
|
+
typeof opts?.panAndZoom === "object" ? opts.panAndZoom : void 0
|
|
2316
2483
|
);
|
|
2317
2484
|
}
|
|
2318
2485
|
const alignmentEnabled = resolveAlignmentEnabled(
|
|
2319
|
-
|
|
2320
|
-
|
|
2486
|
+
opts?.enableAlignment,
|
|
2487
|
+
opts?.alignment
|
|
2321
2488
|
);
|
|
2322
2489
|
if (alignmentEnabled) {
|
|
2323
2490
|
alignmentCleanupRef.current = enableObjectAlignment(
|
|
2324
2491
|
canvas,
|
|
2325
|
-
typeof
|
|
2492
|
+
typeof opts?.alignment === "object" ? opts.alignment : void 0
|
|
2326
2493
|
);
|
|
2327
2494
|
}
|
|
2328
|
-
if (
|
|
2495
|
+
if (opts?.rotationSnap !== false) {
|
|
2329
2496
|
rotationSnapCleanupRef.current = enableRotationSnap(
|
|
2330
2497
|
canvas,
|
|
2331
|
-
typeof
|
|
2498
|
+
typeof opts?.rotationSnap === "object" ? opts.rotationSnap : void 0
|
|
2332
2499
|
);
|
|
2333
2500
|
}
|
|
2334
2501
|
canvas.on("mouse:wheel", () => {
|
|
@@ -2343,44 +2510,63 @@ function useEditCanvas(options) {
|
|
|
2343
2510
|
canvas.on("selection:cleared", () => {
|
|
2344
2511
|
setSelected([]);
|
|
2345
2512
|
});
|
|
2346
|
-
if (
|
|
2513
|
+
if (opts?.trackChanges) {
|
|
2347
2514
|
canvas.on("object:added", () => setIsDirty(true));
|
|
2348
2515
|
canvas.on("object:removed", () => setIsDirty(true));
|
|
2349
2516
|
canvas.on("object:modified", () => setIsDirty(true));
|
|
2350
2517
|
}
|
|
2351
|
-
if (
|
|
2352
|
-
const
|
|
2518
|
+
if (opts?.history) {
|
|
2519
|
+
const syncHistoryState = () => {
|
|
2520
|
+
const h = historyRef.current;
|
|
2521
|
+
if (!h) return;
|
|
2522
|
+
setTimeout(() => {
|
|
2523
|
+
setCanUndo(h.canUndo());
|
|
2524
|
+
setCanRedo(h.canRedo());
|
|
2525
|
+
}, 350);
|
|
2526
|
+
};
|
|
2527
|
+
canvas.on("object:added", syncHistoryState);
|
|
2528
|
+
canvas.on("object:removed", syncHistoryState);
|
|
2529
|
+
canvas.on("object:modified", syncHistoryState);
|
|
2530
|
+
}
|
|
2531
|
+
if (opts?.vertexEdit !== false) {
|
|
2532
|
+
const vertexOpts = typeof opts?.vertexEdit === "object" ? opts.vertexEdit : void 0;
|
|
2353
2533
|
canvas.on("mouse:dblclick", (e) => {
|
|
2354
2534
|
if (e.target && e.target instanceof import_fabric17.Polygon) {
|
|
2355
2535
|
vertexEditCleanupRef.current?.();
|
|
2356
|
-
vertexEditCleanupRef.current = enableVertexEdit(
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
vertexOpts,
|
|
2360
|
-
() => {
|
|
2536
|
+
vertexEditCleanupRef.current = enableVertexEdit(canvas, e.target, {
|
|
2537
|
+
...vertexOpts,
|
|
2538
|
+
onExit: () => {
|
|
2361
2539
|
vertexEditCleanupRef.current = null;
|
|
2362
2540
|
setIsEditingVertices(false);
|
|
2363
2541
|
}
|
|
2364
|
-
);
|
|
2542
|
+
});
|
|
2365
2543
|
setIsEditingVertices(true);
|
|
2366
2544
|
}
|
|
2367
2545
|
});
|
|
2368
2546
|
}
|
|
2369
|
-
|
|
2370
|
-
|
|
2547
|
+
if (opts?.history) {
|
|
2548
|
+
const historyOpts = typeof opts.history === "object" ? opts.history : void 0;
|
|
2549
|
+
historyRef.current = createHistoryTracker(canvas, historyOpts);
|
|
2550
|
+
}
|
|
2551
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2552
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2371
2553
|
Promise.resolve(onReadyResult).then(() => {
|
|
2372
2554
|
if (canvas.backgroundImage) {
|
|
2373
2555
|
fitViewportToBackground(canvas);
|
|
2374
2556
|
syncZoom(canvasRef, setZoom);
|
|
2375
2557
|
}
|
|
2558
|
+
historyRef.current?.pushSnapshot();
|
|
2559
|
+
});
|
|
2560
|
+
} else {
|
|
2561
|
+
Promise.resolve(onReadyResult).then(() => {
|
|
2562
|
+
historyRef.current?.pushSnapshot();
|
|
2376
2563
|
});
|
|
2377
2564
|
}
|
|
2378
2565
|
},
|
|
2379
2566
|
// onReady and panAndZoom are intentionally excluded — we only initialize once
|
|
2380
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2381
2567
|
[]
|
|
2382
2568
|
);
|
|
2383
|
-
(0,
|
|
2569
|
+
(0, import_react3.useEffect)(() => {
|
|
2384
2570
|
const canvas = canvasRef.current;
|
|
2385
2571
|
if (!canvas) return;
|
|
2386
2572
|
setCanvasAlignmentEnabled(canvas, options?.enableAlignment);
|
|
@@ -2398,28 +2584,24 @@ function useEditCanvas(options) {
|
|
|
2398
2584
|
alignmentCleanupRef.current = null;
|
|
2399
2585
|
}
|
|
2400
2586
|
}, [options?.enableAlignment]);
|
|
2401
|
-
const setViewportMode = (0,
|
|
2587
|
+
const setViewportMode = (0, import_react3.useCallback)((mode) => {
|
|
2402
2588
|
viewportRef.current?.setMode(mode);
|
|
2403
2589
|
setViewportModeState(mode);
|
|
2404
2590
|
}, []);
|
|
2405
|
-
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } =
|
|
2406
|
-
|
|
2407
|
-
viewportRef,
|
|
2408
|
-
setZoom
|
|
2409
|
-
);
|
|
2410
|
-
const setBackground = (0, import_react2.useCallback)(
|
|
2591
|
+
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
|
|
2592
|
+
const setBackground = (0, import_react3.useCallback)(
|
|
2411
2593
|
async (url, bgOpts) => {
|
|
2412
2594
|
const canvas = canvasRef.current;
|
|
2413
2595
|
if (!canvas) throw new Error("Canvas not ready");
|
|
2414
|
-
const
|
|
2596
|
+
const opts = optionsRef.current;
|
|
2597
|
+
const resizeOpts = opts?.backgroundResize !== false ? typeof opts?.backgroundResize === "object" ? { ...opts.backgroundResize, ...bgOpts } : { ...bgOpts } : bgOpts?.preserveContrast ? { preserveContrast: true } : void 0;
|
|
2415
2598
|
const img = await setBackgroundImage(canvas, url, resizeOpts);
|
|
2416
|
-
if (
|
|
2599
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2417
2600
|
fitViewportToBackground(canvas);
|
|
2418
2601
|
syncZoom(canvasRef, setZoom);
|
|
2419
2602
|
}
|
|
2420
2603
|
return img;
|
|
2421
2604
|
},
|
|
2422
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2423
2605
|
[]
|
|
2424
2606
|
);
|
|
2425
2607
|
return {
|
|
@@ -2444,7 +2626,9 @@ function useEditCanvas(options) {
|
|
|
2444
2626
|
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2445
2627
|
zoomOut,
|
|
2446
2628
|
/** Pan the viewport to center on a specific object. */
|
|
2447
|
-
panToObject
|
|
2629
|
+
panToObject,
|
|
2630
|
+
/** Zoom and pan to fit a specific object in the viewport. */
|
|
2631
|
+
zoomToFit
|
|
2448
2632
|
},
|
|
2449
2633
|
/** Whether vertex edit mode is currently active (reactive). */
|
|
2450
2634
|
isEditingVertices,
|
|
@@ -2476,12 +2660,32 @@ function useEditCanvas(options) {
|
|
|
2476
2660
|
/** Whether the canvas has been modified since the last `resetDirty()` call. Requires `trackChanges: true`. */
|
|
2477
2661
|
isDirty,
|
|
2478
2662
|
/** Reset the dirty flag (e.g., after a successful save). */
|
|
2479
|
-
resetDirty: (0,
|
|
2663
|
+
resetDirty: (0, import_react3.useCallback)(() => setIsDirty(false), []),
|
|
2664
|
+
/** Undo the last change. Requires `history: true`. */
|
|
2665
|
+
undo: (0, import_react3.useCallback)(async () => {
|
|
2666
|
+
const h = historyRef.current;
|
|
2667
|
+
if (!h) return;
|
|
2668
|
+
await h.undo();
|
|
2669
|
+
setCanUndo(h.canUndo());
|
|
2670
|
+
setCanRedo(h.canRedo());
|
|
2671
|
+
}, []),
|
|
2672
|
+
/** Redo a previously undone change. Requires `history: true`. */
|
|
2673
|
+
redo: (0, import_react3.useCallback)(async () => {
|
|
2674
|
+
const h = historyRef.current;
|
|
2675
|
+
if (!h) return;
|
|
2676
|
+
await h.redo();
|
|
2677
|
+
setCanUndo(h.canUndo());
|
|
2678
|
+
setCanRedo(h.canRedo());
|
|
2679
|
+
}, []),
|
|
2680
|
+
/** Whether an undo operation is available (reactive). Requires `history: true`. */
|
|
2681
|
+
canUndo,
|
|
2682
|
+
/** Whether a redo operation is available (reactive). Requires `history: true`. */
|
|
2683
|
+
canRedo
|
|
2480
2684
|
};
|
|
2481
2685
|
}
|
|
2482
2686
|
|
|
2483
2687
|
// src/hooks/useViewCanvas.ts
|
|
2484
|
-
var
|
|
2688
|
+
var import_react4 = require("react");
|
|
2485
2689
|
function lockCanvas(canvas) {
|
|
2486
2690
|
canvas.selection = false;
|
|
2487
2691
|
canvas.forEachObject((obj) => {
|
|
@@ -2489,18 +2693,24 @@ function lockCanvas(canvas) {
|
|
|
2489
2693
|
});
|
|
2490
2694
|
}
|
|
2491
2695
|
function useViewCanvas(options) {
|
|
2492
|
-
const canvasRef = (0,
|
|
2493
|
-
const viewportRef = (0,
|
|
2494
|
-
const
|
|
2495
|
-
|
|
2696
|
+
const canvasRef = (0, import_react4.useRef)(null);
|
|
2697
|
+
const viewportRef = (0, import_react4.useRef)(null);
|
|
2698
|
+
const optionsRef = (0, import_react4.useRef)(options);
|
|
2699
|
+
optionsRef.current = options;
|
|
2700
|
+
const [zoom, setZoom] = (0, import_react4.useState)(1);
|
|
2701
|
+
const onReady = (0, import_react4.useCallback)(
|
|
2496
2702
|
(canvas) => {
|
|
2497
2703
|
canvasRef.current = canvas;
|
|
2498
|
-
|
|
2704
|
+
const opts = optionsRef.current;
|
|
2705
|
+
if (opts?.scaledStrokes !== false) {
|
|
2499
2706
|
enableScaledStrokes(canvas);
|
|
2500
2707
|
}
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2708
|
+
if (opts?.borderRadius !== false) {
|
|
2709
|
+
const borderRadiusOpts = typeof opts?.borderRadius === "number" ? { radius: opts.borderRadius } : void 0;
|
|
2710
|
+
enableScaledBorderRadius(canvas, borderRadiusOpts);
|
|
2711
|
+
}
|
|
2712
|
+
if (opts?.panAndZoom !== false) {
|
|
2713
|
+
const panAndZoomOpts = typeof opts?.panAndZoom === "object" ? opts.panAndZoom : {};
|
|
2504
2714
|
viewportRef.current = enablePanAndZoom(canvas, {
|
|
2505
2715
|
...panAndZoomOpts,
|
|
2506
2716
|
initialMode: "pan"
|
|
@@ -2513,8 +2723,8 @@ function useViewCanvas(options) {
|
|
|
2513
2723
|
canvas.on("mouse:wheel", () => {
|
|
2514
2724
|
setZoom(canvas.getZoom());
|
|
2515
2725
|
});
|
|
2516
|
-
const onReadyResult =
|
|
2517
|
-
if (
|
|
2726
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2727
|
+
if (opts?.autoFitToBackground !== false) {
|
|
2518
2728
|
Promise.resolve(onReadyResult).then(() => {
|
|
2519
2729
|
if (canvas.backgroundImage) {
|
|
2520
2730
|
fitViewportToBackground(canvas);
|
|
@@ -2524,26 +2734,21 @@ function useViewCanvas(options) {
|
|
|
2524
2734
|
}
|
|
2525
2735
|
},
|
|
2526
2736
|
// onReady and panAndZoom are intentionally excluded — we only initialize once
|
|
2527
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2528
2737
|
[]
|
|
2529
2738
|
);
|
|
2530
|
-
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject } =
|
|
2531
|
-
canvasRef,
|
|
2532
|
-
viewportRef,
|
|
2533
|
-
setZoom
|
|
2534
|
-
);
|
|
2739
|
+
const { resetViewport: resetViewport2, zoomIn, zoomOut, panToObject, zoomToFit } = useViewportActions(canvasRef, viewportRef, setZoom);
|
|
2535
2740
|
const findObject = (id) => {
|
|
2536
2741
|
const c = canvasRef.current;
|
|
2537
2742
|
if (!c) return void 0;
|
|
2538
2743
|
return c.getObjects().find((o) => o.data?.id === id);
|
|
2539
2744
|
};
|
|
2540
|
-
const setObjectStyle = (0,
|
|
2745
|
+
const setObjectStyle = (0, import_react4.useCallback)((id, style) => {
|
|
2541
2746
|
const obj = findObject(id);
|
|
2542
2747
|
if (!obj) return;
|
|
2543
2748
|
obj.set(style);
|
|
2544
2749
|
canvasRef.current.requestRenderAll();
|
|
2545
2750
|
}, []);
|
|
2546
|
-
const setObjectStyles = (0,
|
|
2751
|
+
const setObjectStyles = (0, import_react4.useCallback)(
|
|
2547
2752
|
(styles) => {
|
|
2548
2753
|
const c = canvasRef.current;
|
|
2549
2754
|
if (!c) return;
|
|
@@ -2564,7 +2769,7 @@ function useViewCanvas(options) {
|
|
|
2564
2769
|
},
|
|
2565
2770
|
[]
|
|
2566
2771
|
);
|
|
2567
|
-
const setObjectStyleByType = (0,
|
|
2772
|
+
const setObjectStyleByType = (0, import_react4.useCallback)(
|
|
2568
2773
|
(type, style) => {
|
|
2569
2774
|
const c = canvasRef.current;
|
|
2570
2775
|
if (!c) return;
|
|
@@ -2595,7 +2800,9 @@ function useViewCanvas(options) {
|
|
|
2595
2800
|
/** Zoom out from the canvas center. Default step: 0.2. */
|
|
2596
2801
|
zoomOut,
|
|
2597
2802
|
/** Pan the viewport to center on a specific object. */
|
|
2598
|
-
panToObject
|
|
2803
|
+
panToObject,
|
|
2804
|
+
/** Zoom and pan to fit a specific object in the viewport. */
|
|
2805
|
+
zoomToFit
|
|
2599
2806
|
},
|
|
2600
2807
|
/** Update a single object's visual style by its `data.id`. */
|
|
2601
2808
|
setObjectStyle,
|
|
@@ -2607,11 +2814,11 @@ function useViewCanvas(options) {
|
|
|
2607
2814
|
}
|
|
2608
2815
|
|
|
2609
2816
|
// src/hooks/useCanvasEvents.ts
|
|
2610
|
-
var
|
|
2817
|
+
var import_react5 = require("react");
|
|
2611
2818
|
function useCanvasEvents(canvasRef, events) {
|
|
2612
|
-
const eventsRef = (0,
|
|
2819
|
+
const eventsRef = (0, import_react5.useRef)(events);
|
|
2613
2820
|
eventsRef.current = events;
|
|
2614
|
-
(0,
|
|
2821
|
+
(0, import_react5.useEffect)(() => {
|
|
2615
2822
|
const canvas = canvasRef.current;
|
|
2616
2823
|
if (!canvas) return;
|
|
2617
2824
|
const wrappers = /* @__PURE__ */ new Map();
|
|
@@ -2632,17 +2839,17 @@ function useCanvasEvents(canvasRef, events) {
|
|
|
2632
2839
|
}
|
|
2633
2840
|
|
|
2634
2841
|
// src/hooks/useCanvasTooltip.ts
|
|
2635
|
-
var
|
|
2842
|
+
var import_react6 = require("react");
|
|
2636
2843
|
function useCanvasTooltip(canvasRef, options) {
|
|
2637
|
-
const [state, setState] = (0,
|
|
2844
|
+
const [state, setState] = (0, import_react6.useState)({
|
|
2638
2845
|
visible: false,
|
|
2639
2846
|
content: null,
|
|
2640
2847
|
position: { x: 0, y: 0 }
|
|
2641
2848
|
});
|
|
2642
|
-
const hoveredObjectRef = (0,
|
|
2643
|
-
const optionsRef = (0,
|
|
2849
|
+
const hoveredObjectRef = (0, import_react6.useRef)(null);
|
|
2850
|
+
const optionsRef = (0, import_react6.useRef)(options);
|
|
2644
2851
|
optionsRef.current = options;
|
|
2645
|
-
(0,
|
|
2852
|
+
(0, import_react6.useEffect)(() => {
|
|
2646
2853
|
const canvas = canvasRef.current;
|
|
2647
2854
|
if (!canvas) return;
|
|
2648
2855
|
function calculatePosition(target) {
|
|
@@ -2696,13 +2903,13 @@ function useCanvasTooltip(canvasRef, options) {
|
|
|
2696
2903
|
}
|
|
2697
2904
|
|
|
2698
2905
|
// src/hooks/useCanvasClick.ts
|
|
2699
|
-
var
|
|
2906
|
+
var import_react7 = require("react");
|
|
2700
2907
|
function useCanvasClick(canvasRef, onClick, options) {
|
|
2701
|
-
const onClickRef = (0,
|
|
2908
|
+
const onClickRef = (0, import_react7.useRef)(onClick);
|
|
2702
2909
|
onClickRef.current = onClick;
|
|
2703
|
-
const optionsRef = (0,
|
|
2910
|
+
const optionsRef = (0, import_react7.useRef)(options);
|
|
2704
2911
|
optionsRef.current = options;
|
|
2705
|
-
(0,
|
|
2912
|
+
(0, import_react7.useEffect)(() => {
|
|
2706
2913
|
const canvas = canvasRef.current;
|
|
2707
2914
|
if (!canvas) return;
|
|
2708
2915
|
let mouseDown = null;
|
|
@@ -2747,13 +2954,13 @@ function useCanvasClick(canvasRef, onClick, options) {
|
|
|
2747
2954
|
}
|
|
2748
2955
|
|
|
2749
2956
|
// src/hooks/useObjectOverlay.ts
|
|
2750
|
-
var
|
|
2957
|
+
var import_react8 = require("react");
|
|
2751
2958
|
var import_fabric18 = require("fabric");
|
|
2752
2959
|
function useObjectOverlay(canvasRef, object, options) {
|
|
2753
|
-
const containerRef = (0,
|
|
2754
|
-
const optionsRef = (0,
|
|
2960
|
+
const containerRef = (0, import_react8.useRef)(null);
|
|
2961
|
+
const optionsRef = (0, import_react8.useRef)(options);
|
|
2755
2962
|
optionsRef.current = options;
|
|
2756
|
-
(0,
|
|
2963
|
+
(0, import_react8.useEffect)(() => {
|
|
2757
2964
|
const canvas = canvasRef.current;
|
|
2758
2965
|
if (!canvas || !object) return;
|
|
2759
2966
|
function update() {
|
|
@@ -2768,36 +2975,41 @@ function useObjectOverlay(canvasRef, object, options) {
|
|
|
2768
2975
|
const screenCoords = import_fabric18.util.transformPoint(center, vt);
|
|
2769
2976
|
const screenWidth = actualWidth * zoom;
|
|
2770
2977
|
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
2978
|
const angle = object.angle ?? 0;
|
|
2776
|
-
el.style.rotate = angle !== 0 ? `${angle}deg` : "";
|
|
2777
2979
|
const opts = optionsRef.current;
|
|
2778
|
-
if (opts?.autoScaleContent) {
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
el.style.
|
|
2782
|
-
|
|
2783
|
-
|
|
2980
|
+
if (opts?.autoScaleContent !== false) {
|
|
2981
|
+
el.style.left = `${screenCoords.x - actualWidth / 2}px`;
|
|
2982
|
+
el.style.top = `${screenCoords.y - actualHeight / 2}px`;
|
|
2983
|
+
el.style.width = `${actualWidth}px`;
|
|
2984
|
+
el.style.height = `${actualHeight}px`;
|
|
2985
|
+
el.style.transformOrigin = "center center";
|
|
2986
|
+
el.style.transform = angle !== 0 ? `scale(${zoom}) rotate(${angle}deg)` : `scale(${zoom})`;
|
|
2987
|
+
el.style.rotate = "";
|
|
2988
|
+
el.style.setProperty("--overlay-scale", String(zoom));
|
|
2989
|
+
if (opts?.textSelector) {
|
|
2990
|
+
const textMinScale = opts?.textMinScale ?? 0.5;
|
|
2784
2991
|
const textEls = el.querySelectorAll(opts.textSelector);
|
|
2785
|
-
const display =
|
|
2992
|
+
const display = zoom < textMinScale ? "none" : "";
|
|
2786
2993
|
textEls.forEach((t) => {
|
|
2787
2994
|
t.style.display = display;
|
|
2788
2995
|
});
|
|
2789
2996
|
}
|
|
2997
|
+
} else {
|
|
2998
|
+
el.style.left = `${screenCoords.x - screenWidth / 2}px`;
|
|
2999
|
+
el.style.top = `${screenCoords.y - screenHeight / 2}px`;
|
|
3000
|
+
el.style.width = `${screenWidth}px`;
|
|
3001
|
+
el.style.height = `${screenHeight}px`;
|
|
3002
|
+
el.style.transform = "";
|
|
3003
|
+
el.style.rotate = angle !== 0 ? `${angle}deg` : "";
|
|
2790
3004
|
}
|
|
2791
3005
|
}
|
|
2792
3006
|
update();
|
|
2793
3007
|
canvas.on("after:render", update);
|
|
2794
|
-
canvas.on("mouse:wheel", update);
|
|
2795
3008
|
object.on("moving", update);
|
|
2796
3009
|
object.on("scaling", update);
|
|
2797
3010
|
object.on("rotating", update);
|
|
2798
3011
|
return () => {
|
|
2799
3012
|
canvas.off("after:render", update);
|
|
2800
|
-
canvas.off("mouse:wheel", update);
|
|
2801
3013
|
object.off("moving", update);
|
|
2802
3014
|
object.off("scaling", update);
|
|
2803
3015
|
object.off("rotating", update);
|
|
@@ -2825,6 +3037,7 @@ var import_fabric19 = require("fabric");
|
|
|
2825
3037
|
Rect,
|
|
2826
3038
|
createCircle,
|
|
2827
3039
|
createCircleAtPoint,
|
|
3040
|
+
createHistoryTracker,
|
|
2828
3041
|
createPolygon,
|
|
2829
3042
|
createPolygonAtPoint,
|
|
2830
3043
|
createPolygonFromDrag,
|