@bwp-web/canvas 0.6.1 → 0.7.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/alignment/cursorSnapping.d.ts.map +1 -1
- package/dist/alignment/objectAlignment.d.ts.map +1 -1
- package/dist/background.d.ts.map +1 -1
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +0 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useEditCanvas.d.ts.map +1 -1
- package/dist/index.cjs +433 -241
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +430 -240
- package/dist/index.js.map +1 -1
- package/dist/overlay/FixedSizeContent.d.ts +51 -0
- package/dist/overlay/FixedSizeContent.d.ts.map +1 -0
- package/dist/overlay/ObjectOverlay.d.ts +33 -0
- package/dist/overlay/ObjectOverlay.d.ts.map +1 -0
- package/dist/overlay/OverlayContent.d.ts +26 -0
- package/dist/overlay/OverlayContent.d.ts.map +1 -0
- package/dist/overlay/index.d.ts +7 -0
- package/dist/overlay/index.d.ts.map +1 -0
- package/dist/serialization.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/alignment/objectAlignmentRendering.d.ts +0 -21
- package/dist/alignment/objectAlignmentRendering.d.ts.map +0 -1
- package/dist/hooks/useObjectOverlay.d.ts +0 -50
- package/dist/hooks/useObjectOverlay.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,9 @@ __export(index_exports, {
|
|
|
30
30
|
FabricCanvas: () => import_fabric19.Canvas,
|
|
31
31
|
FabricImage: () => import_fabric19.FabricImage,
|
|
32
32
|
FabricObject: () => import_fabric19.FabricObject,
|
|
33
|
+
FixedSizeContent: () => FixedSizeContent,
|
|
34
|
+
ObjectOverlay: () => ObjectOverlay,
|
|
35
|
+
OverlayContent: () => OverlayContent,
|
|
33
36
|
Point: () => import_fabric19.Point,
|
|
34
37
|
Polygon: () => import_fabric19.Polygon,
|
|
35
38
|
Rect: () => import_fabric19.Rect,
|
|
@@ -75,7 +78,6 @@ __export(index_exports, {
|
|
|
75
78
|
useCanvasEvents: () => useCanvasEvents,
|
|
76
79
|
useCanvasTooltip: () => useCanvasTooltip,
|
|
77
80
|
useEditCanvas: () => useEditCanvas,
|
|
78
|
-
useObjectOverlay: () => useObjectOverlay,
|
|
79
81
|
useViewCanvas: () => useViewCanvas,
|
|
80
82
|
util: () => import_fabric19.util
|
|
81
83
|
});
|
|
@@ -183,6 +185,10 @@ var DEFAULT_VIEWPORT_PADDING = 0.05;
|
|
|
183
185
|
var BASE_CANVAS_SIZE = 1e3;
|
|
184
186
|
var DEFAULT_SNAP_MARGIN = 6;
|
|
185
187
|
var DEFAULT_ANGLE_SNAP_INTERVAL = 15;
|
|
188
|
+
function computeSnapMargin(canvasWidth, canvasHeight, zoom, baseMargin = DEFAULT_SNAP_MARGIN, scaleWithCanvasSize = true) {
|
|
189
|
+
const sizeScale = scaleWithCanvasSize ? Math.max(canvasWidth || 800, canvasHeight || 600) / BASE_CANVAS_SIZE : 1;
|
|
190
|
+
return baseMargin * sizeScale / zoom;
|
|
191
|
+
}
|
|
186
192
|
var MIN_DRAG_SIZE = 3;
|
|
187
193
|
var POLYGON_CLOSE_THRESHOLD = 10;
|
|
188
194
|
var DEFAULT_IMAGE_MAX_SIZE = 4096;
|
|
@@ -390,23 +396,23 @@ function enablePanAndZoom(canvas, options) {
|
|
|
390
396
|
const targetX = canvasCenterX - objectCenter.x * zoom;
|
|
391
397
|
const targetY = canvasCenterY - objectCenter.y * zoom;
|
|
392
398
|
if (!panOpts?.animate) {
|
|
393
|
-
const
|
|
394
|
-
if (!
|
|
399
|
+
const viewportTransform2 = canvas.viewportTransform;
|
|
400
|
+
if (!viewportTransform2) return;
|
|
395
401
|
canvas.setViewportTransform([
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
402
|
+
viewportTransform2[0],
|
|
403
|
+
viewportTransform2[1],
|
|
404
|
+
viewportTransform2[2],
|
|
405
|
+
viewportTransform2[3],
|
|
400
406
|
targetX,
|
|
401
407
|
targetY
|
|
402
408
|
]);
|
|
403
409
|
return;
|
|
404
410
|
}
|
|
405
411
|
const duration = panOpts.duration ?? 300;
|
|
406
|
-
const
|
|
407
|
-
if (!
|
|
408
|
-
const startX =
|
|
409
|
-
const startY =
|
|
412
|
+
const viewportTransform = canvas.viewportTransform;
|
|
413
|
+
if (!viewportTransform) return;
|
|
414
|
+
const startX = viewportTransform[4];
|
|
415
|
+
const startY = viewportTransform[5];
|
|
410
416
|
const startTime = performance.now();
|
|
411
417
|
function step(now) {
|
|
412
418
|
const elapsed = now - startTime;
|
|
@@ -414,13 +420,13 @@ function enablePanAndZoom(canvas, options) {
|
|
|
414
420
|
const eased = 1 - Math.pow(1 - t, 3);
|
|
415
421
|
const currentX = startX + (targetX - startX) * eased;
|
|
416
422
|
const currentY = startY + (targetY - startY) * eased;
|
|
417
|
-
const
|
|
418
|
-
if (!
|
|
423
|
+
const currentTransform = canvas.viewportTransform;
|
|
424
|
+
if (!currentTransform) return;
|
|
419
425
|
canvas.setViewportTransform([
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
426
|
+
currentTransform[0],
|
|
427
|
+
currentTransform[1],
|
|
428
|
+
currentTransform[2],
|
|
429
|
+
currentTransform[3],
|
|
424
430
|
currentX,
|
|
425
431
|
currentY
|
|
426
432
|
]);
|
|
@@ -607,6 +613,7 @@ async function setBackgroundImage(canvas, url, options) {
|
|
|
607
613
|
imageUrl = result.url;
|
|
608
614
|
}
|
|
609
615
|
const img = await import_fabric4.FabricImage.fromURL(imageUrl, { crossOrigin: "anonymous" });
|
|
616
|
+
img.set({ left: img.width / 2, top: img.height / 2 });
|
|
610
617
|
canvas.backgroundImage = img;
|
|
611
618
|
if (prevContrast !== void 0 && prevContrast !== 1) {
|
|
612
619
|
setBackgroundContrast(canvas, prevContrast);
|
|
@@ -800,63 +807,6 @@ registerSnapPointExtractor(
|
|
|
800
807
|
// src/alignment/objectAlignment.ts
|
|
801
808
|
var import_fabric7 = require("fabric");
|
|
802
809
|
|
|
803
|
-
// src/alignment/objectAlignmentRendering.ts
|
|
804
|
-
function drawAlignmentLine(config, origin, target) {
|
|
805
|
-
const ctx = config.canvas.getTopContext();
|
|
806
|
-
const vt = config.canvas.viewportTransform;
|
|
807
|
-
const zoom = config.canvas.getZoom();
|
|
808
|
-
ctx.save();
|
|
809
|
-
ctx.transform(...vt);
|
|
810
|
-
ctx.lineWidth = config.width / zoom;
|
|
811
|
-
if (config.lineDash) ctx.setLineDash(config.lineDash);
|
|
812
|
-
ctx.strokeStyle = config.color;
|
|
813
|
-
ctx.beginPath();
|
|
814
|
-
ctx.moveTo(origin.x, origin.y);
|
|
815
|
-
ctx.lineTo(target.x, target.y);
|
|
816
|
-
ctx.stroke();
|
|
817
|
-
if (config.lineDash) ctx.setLineDash([]);
|
|
818
|
-
drawXMarker(ctx, origin, config.xSize / zoom);
|
|
819
|
-
drawXMarker(ctx, target, config.xSize / zoom);
|
|
820
|
-
ctx.restore();
|
|
821
|
-
}
|
|
822
|
-
function drawXMarker(ctx, point, size) {
|
|
823
|
-
ctx.save();
|
|
824
|
-
ctx.translate(point.x, point.y);
|
|
825
|
-
ctx.beginPath();
|
|
826
|
-
ctx.moveTo(-size, -size);
|
|
827
|
-
ctx.lineTo(size, size);
|
|
828
|
-
ctx.moveTo(size, -size);
|
|
829
|
-
ctx.lineTo(-size, size);
|
|
830
|
-
ctx.stroke();
|
|
831
|
-
ctx.restore();
|
|
832
|
-
}
|
|
833
|
-
function drawMarkerList(config, lines) {
|
|
834
|
-
const ctx = config.canvas.getTopContext();
|
|
835
|
-
const vt = config.canvas.viewportTransform;
|
|
836
|
-
const zoom = config.canvas.getZoom();
|
|
837
|
-
const markerSize = config.xSize / zoom;
|
|
838
|
-
ctx.save();
|
|
839
|
-
ctx.transform(...vt);
|
|
840
|
-
ctx.lineWidth = config.width / zoom;
|
|
841
|
-
ctx.strokeStyle = config.color;
|
|
842
|
-
for (const item of lines) drawXMarker(ctx, item.target, markerSize);
|
|
843
|
-
ctx.restore();
|
|
844
|
-
}
|
|
845
|
-
function drawVerticalAlignmentLines(config, lines) {
|
|
846
|
-
for (const v of lines) {
|
|
847
|
-
const { origin, target } = JSON.parse(v);
|
|
848
|
-
const from = { x: target.x, y: origin.y };
|
|
849
|
-
drawAlignmentLine(config, from, target);
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
function drawHorizontalAlignmentLines(config, lines) {
|
|
853
|
-
for (const h of lines) {
|
|
854
|
-
const { origin, target } = JSON.parse(h);
|
|
855
|
-
const from = { x: origin.x, y: target.y };
|
|
856
|
-
drawAlignmentLine(config, from, target);
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
|
|
860
810
|
// src/alignment/objectAlignmentMath.ts
|
|
861
811
|
var OPPOSITE_ORIGIN_MAP = {
|
|
862
812
|
tl: ["right", "bottom"],
|
|
@@ -980,6 +930,61 @@ function collectHorizontalSnapOffset(props) {
|
|
|
980
930
|
}
|
|
981
931
|
|
|
982
932
|
// src/alignment/objectAlignment.ts
|
|
933
|
+
function drawXMarker(ctx, point, size) {
|
|
934
|
+
ctx.save();
|
|
935
|
+
ctx.translate(point.x, point.y);
|
|
936
|
+
ctx.beginPath();
|
|
937
|
+
ctx.moveTo(-size, -size);
|
|
938
|
+
ctx.lineTo(size, size);
|
|
939
|
+
ctx.moveTo(size, -size);
|
|
940
|
+
ctx.lineTo(-size, size);
|
|
941
|
+
ctx.stroke();
|
|
942
|
+
ctx.restore();
|
|
943
|
+
}
|
|
944
|
+
function drawAlignmentLine(config, origin, target) {
|
|
945
|
+
const ctx = config.canvas.getTopContext();
|
|
946
|
+
const vt = config.canvas.viewportTransform;
|
|
947
|
+
const zoom = config.canvas.getZoom();
|
|
948
|
+
ctx.save();
|
|
949
|
+
ctx.transform(...vt);
|
|
950
|
+
ctx.lineWidth = config.width / zoom;
|
|
951
|
+
if (config.lineDash) ctx.setLineDash(config.lineDash);
|
|
952
|
+
ctx.strokeStyle = config.color;
|
|
953
|
+
ctx.beginPath();
|
|
954
|
+
ctx.moveTo(origin.x, origin.y);
|
|
955
|
+
ctx.lineTo(target.x, target.y);
|
|
956
|
+
ctx.stroke();
|
|
957
|
+
if (config.lineDash) ctx.setLineDash([]);
|
|
958
|
+
drawXMarker(ctx, origin, config.xSize / zoom);
|
|
959
|
+
drawXMarker(ctx, target, config.xSize / zoom);
|
|
960
|
+
ctx.restore();
|
|
961
|
+
}
|
|
962
|
+
function drawMarkerList(config, lines) {
|
|
963
|
+
const ctx = config.canvas.getTopContext();
|
|
964
|
+
const vt = config.canvas.viewportTransform;
|
|
965
|
+
const zoom = config.canvas.getZoom();
|
|
966
|
+
const markerSize = config.xSize / zoom;
|
|
967
|
+
ctx.save();
|
|
968
|
+
ctx.transform(...vt);
|
|
969
|
+
ctx.lineWidth = config.width / zoom;
|
|
970
|
+
ctx.strokeStyle = config.color;
|
|
971
|
+
for (const item of lines) drawXMarker(ctx, item.target, markerSize);
|
|
972
|
+
ctx.restore();
|
|
973
|
+
}
|
|
974
|
+
function drawVerticalAlignmentLines(config, lines) {
|
|
975
|
+
for (const v of lines) {
|
|
976
|
+
const { origin, target } = JSON.parse(v);
|
|
977
|
+
const from = { x: target.x, y: origin.y };
|
|
978
|
+
drawAlignmentLine(config, from, target);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
function drawHorizontalAlignmentLines(config, lines) {
|
|
982
|
+
for (const h of lines) {
|
|
983
|
+
const { origin, target } = JSON.parse(h);
|
|
984
|
+
const from = { x: origin.x, y: target.y };
|
|
985
|
+
drawAlignmentLine(config, from, target);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
983
988
|
function adjustCornerForFlip(corner, target) {
|
|
984
989
|
let adjusted = corner;
|
|
985
990
|
if (target.flipX) {
|
|
@@ -1034,9 +1039,13 @@ var ObjectAlignmentGuides = class {
|
|
|
1034
1039
|
}
|
|
1035
1040
|
// --- Margin calculation ---
|
|
1036
1041
|
computeMargin() {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1042
|
+
return computeSnapMargin(
|
|
1043
|
+
this.canvas.width ?? 800,
|
|
1044
|
+
this.canvas.height ?? 600,
|
|
1045
|
+
this.canvas.getZoom(),
|
|
1046
|
+
this.margin,
|
|
1047
|
+
this.scaleWithCanvasSize
|
|
1048
|
+
);
|
|
1040
1049
|
}
|
|
1041
1050
|
// --- Snap point caching ---
|
|
1042
1051
|
getCachedSnapPoints(object) {
|
|
@@ -1172,10 +1181,13 @@ function enableRotationSnap(canvas, options) {
|
|
|
1172
1181
|
// src/alignment/cursorSnapping.ts
|
|
1173
1182
|
var import_fabric8 = require("fabric");
|
|
1174
1183
|
function snapCursorPoint(canvas, rawPoint, options) {
|
|
1175
|
-
const
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1184
|
+
const margin = computeSnapMargin(
|
|
1185
|
+
canvas.width ?? 800,
|
|
1186
|
+
canvas.height ?? 600,
|
|
1187
|
+
canvas.getZoom(),
|
|
1188
|
+
options?.margin ?? DEFAULT_SNAP_MARGIN,
|
|
1189
|
+
options?.scaleWithCanvasSize !== false
|
|
1190
|
+
);
|
|
1179
1191
|
const exclude = options?.exclude ?? /* @__PURE__ */ new Set();
|
|
1180
1192
|
let targetPoints;
|
|
1181
1193
|
if (options?.targetPoints) {
|
|
@@ -2228,20 +2240,7 @@ function enableScaledBorderRadius(canvas, options) {
|
|
|
2228
2240
|
function getBaseStrokeWidth(obj) {
|
|
2229
2241
|
return strokeBaseMap.get(obj) ?? obj.strokeWidth ?? 0;
|
|
2230
2242
|
}
|
|
2231
|
-
function
|
|
2232
|
-
const properties = [
|
|
2233
|
-
"data",
|
|
2234
|
-
"shapeType",
|
|
2235
|
-
// Control styling — absent from Fabric's default toObject output
|
|
2236
|
-
"borderColor",
|
|
2237
|
-
"cornerColor",
|
|
2238
|
-
"cornerStrokeColor",
|
|
2239
|
-
"transparentCorners",
|
|
2240
|
-
// Interaction locks — absent from Fabric's default toObject output
|
|
2241
|
-
"lockRotation",
|
|
2242
|
-
"lockUniScaling",
|
|
2243
|
-
...options?.properties ?? []
|
|
2244
|
-
];
|
|
2243
|
+
function prepareStrokeWidths(canvas) {
|
|
2245
2244
|
const scaledWidths = /* @__PURE__ */ new Map();
|
|
2246
2245
|
canvas.forEachObject((obj) => {
|
|
2247
2246
|
const base = strokeBaseMap.get(obj);
|
|
@@ -2250,6 +2249,11 @@ function serializeCanvas(canvas, options) {
|
|
|
2250
2249
|
obj.strokeWidth = base;
|
|
2251
2250
|
}
|
|
2252
2251
|
});
|
|
2252
|
+
return () => scaledWidths.forEach((scaled, obj) => {
|
|
2253
|
+
obj.strokeWidth = scaled;
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
function prepareBorderRadii(canvas) {
|
|
2253
2257
|
const appliedRadii = /* @__PURE__ */ new Map();
|
|
2254
2258
|
canvas.forEachObject((obj) => {
|
|
2255
2259
|
if (!(obj instanceof import_fabric16.Rect)) return;
|
|
@@ -2259,6 +2263,11 @@ function serializeCanvas(canvas, options) {
|
|
|
2259
2263
|
obj.set({ rx: base.rx, ry: base.ry });
|
|
2260
2264
|
}
|
|
2261
2265
|
});
|
|
2266
|
+
return () => appliedRadii.forEach((radii, obj) => {
|
|
2267
|
+
obj.set(radii);
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2270
|
+
function prepareObjectOrigins(canvas) {
|
|
2262
2271
|
const savedOrigins = /* @__PURE__ */ new Map();
|
|
2263
2272
|
canvas.forEachObject((obj) => {
|
|
2264
2273
|
if (obj.originX === "left" && obj.originY === "top") return;
|
|
@@ -2269,20 +2278,36 @@ function serializeCanvas(canvas, options) {
|
|
|
2269
2278
|
top: obj.top ?? 0
|
|
2270
2279
|
});
|
|
2271
2280
|
const leftTop = obj.getPositionByOrigin("left", "top");
|
|
2272
|
-
obj.set({
|
|
2281
|
+
obj.set({
|
|
2282
|
+
originX: "left",
|
|
2283
|
+
originY: "top",
|
|
2284
|
+
left: leftTop.x,
|
|
2285
|
+
top: leftTop.y
|
|
2286
|
+
});
|
|
2273
2287
|
});
|
|
2288
|
+
return () => savedOrigins.forEach((saved, obj) => {
|
|
2289
|
+
obj.set(saved);
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
function prepareBackgroundOrigin(canvas) {
|
|
2274
2293
|
const bg = canvas.backgroundImage;
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
savedBgOrigin = {
|
|
2278
|
-
originX: bg.originX,
|
|
2279
|
-
originY: bg.originY,
|
|
2280
|
-
left: bg.left ?? 0,
|
|
2281
|
-
top: bg.top ?? 0
|
|
2294
|
+
if (!(bg instanceof import_fabric16.FabricImage) || bg.originX === "left" && bg.originY === "top") {
|
|
2295
|
+
return () => {
|
|
2282
2296
|
};
|
|
2283
|
-
const leftTop = bg.getPositionByOrigin("left", "top");
|
|
2284
|
-
bg.set({ originX: "left", originY: "top", left: leftTop.x, top: leftTop.y });
|
|
2285
2297
|
}
|
|
2298
|
+
const saved = {
|
|
2299
|
+
originX: bg.originX,
|
|
2300
|
+
originY: bg.originY,
|
|
2301
|
+
left: bg.left ?? 0,
|
|
2302
|
+
top: bg.top ?? 0
|
|
2303
|
+
};
|
|
2304
|
+
const leftTop = bg.getPositionByOrigin("left", "top");
|
|
2305
|
+
bg.set({ originX: "left", originY: "top", left: leftTop.x, top: leftTop.y });
|
|
2306
|
+
return () => {
|
|
2307
|
+
bg.set(saved);
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
2310
|
+
function prepareStrokeWidthBaseData(canvas) {
|
|
2286
2311
|
const savedData = /* @__PURE__ */ new Map();
|
|
2287
2312
|
canvas.forEachObject((obj) => {
|
|
2288
2313
|
const base = strokeBaseMap.get(obj) ?? obj.strokeWidth;
|
|
@@ -2294,33 +2319,53 @@ function serializeCanvas(canvas, options) {
|
|
|
2294
2319
|
};
|
|
2295
2320
|
}
|
|
2296
2321
|
});
|
|
2322
|
+
return () => savedData.forEach((originalData, obj) => {
|
|
2323
|
+
obj.data = originalData;
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
function serializeCanvas(canvas, options) {
|
|
2327
|
+
const properties = [
|
|
2328
|
+
"data",
|
|
2329
|
+
"shapeType",
|
|
2330
|
+
// Control styling — absent from Fabric's default toObject output
|
|
2331
|
+
"borderColor",
|
|
2332
|
+
"cornerColor",
|
|
2333
|
+
"cornerStrokeColor",
|
|
2334
|
+
"transparentCorners",
|
|
2335
|
+
// Interaction locks — absent from Fabric's default toObject output
|
|
2336
|
+
"lockRotation",
|
|
2337
|
+
"lockUniScaling",
|
|
2338
|
+
...options?.properties ?? []
|
|
2339
|
+
];
|
|
2340
|
+
const restoreStrokeWidths = prepareStrokeWidths(canvas);
|
|
2341
|
+
const restoreBorderRadii = prepareBorderRadii(canvas);
|
|
2342
|
+
const restoreOrigins = prepareObjectOrigins(canvas);
|
|
2343
|
+
const restoreBgOrigin = prepareBackgroundOrigin(canvas);
|
|
2344
|
+
const restoreData = prepareStrokeWidthBaseData(canvas);
|
|
2297
2345
|
const json = canvas.toObject(properties);
|
|
2298
2346
|
delete json.backgroundColor;
|
|
2299
2347
|
json.backgroundFilters = {
|
|
2300
2348
|
opacity: getBackgroundContrast(canvas),
|
|
2301
2349
|
inverted: getBackgroundInverted(canvas)
|
|
2302
2350
|
};
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
});
|
|
2306
|
-
appliedRadii.forEach((radii, obj) => {
|
|
2307
|
-
obj.set({ rx: radii.rx, ry: radii.ry });
|
|
2308
|
-
});
|
|
2309
|
-
savedOrigins.forEach((saved, obj) => {
|
|
2310
|
-
obj.set(saved);
|
|
2311
|
-
});
|
|
2312
|
-
if (savedBgOrigin && bg instanceof import_fabric16.FabricImage) {
|
|
2313
|
-
bg.set(savedBgOrigin);
|
|
2351
|
+
if (canvas.lockLightMode !== void 0) {
|
|
2352
|
+
json.lockLightMode = canvas.lockLightMode;
|
|
2314
2353
|
}
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2354
|
+
restoreStrokeWidths();
|
|
2355
|
+
restoreBorderRadii();
|
|
2356
|
+
restoreOrigins();
|
|
2357
|
+
restoreBgOrigin();
|
|
2358
|
+
restoreData();
|
|
2318
2359
|
return json;
|
|
2319
2360
|
}
|
|
2320
2361
|
async function loadCanvas(canvas, json, options) {
|
|
2321
2362
|
await canvas.loadFromJSON(json);
|
|
2322
2363
|
canvas.backgroundColor = "";
|
|
2323
2364
|
delete canvas.backgroundFilters;
|
|
2365
|
+
const rawCanvas = canvas;
|
|
2366
|
+
if (rawCanvas.lockLightMode !== void 0) {
|
|
2367
|
+
canvas.lockLightMode = rawCanvas.lockLightMode;
|
|
2368
|
+
}
|
|
2324
2369
|
const bg = canvas.backgroundImage;
|
|
2325
2370
|
if (bg instanceof import_fabric16.FabricImage) {
|
|
2326
2371
|
if (bg.originX !== "center" || bg.originY !== "center") {
|
|
@@ -2513,105 +2558,104 @@ function useEditCanvas(options) {
|
|
|
2513
2558
|
(canvas) => {
|
|
2514
2559
|
canvasRef.current = canvas;
|
|
2515
2560
|
const opts = optionsRef.current;
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
)
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
typeof opts?.alignment === "object" ? opts.alignment : void 0
|
|
2541
|
-
);
|
|
2542
|
-
}
|
|
2543
|
-
if (opts?.rotationSnap !== false) {
|
|
2544
|
-
rotationSnapCleanupRef.current = enableRotationSnap(
|
|
2545
|
-
canvas,
|
|
2546
|
-
typeof opts?.rotationSnap === "object" ? opts.rotationSnap : void 0
|
|
2561
|
+
setupFeatures();
|
|
2562
|
+
setupEventListeners();
|
|
2563
|
+
invokeOnReady();
|
|
2564
|
+
function setupFeatures() {
|
|
2565
|
+
if (opts?.scaledStrokes !== false) {
|
|
2566
|
+
enableScaledStrokes(canvas);
|
|
2567
|
+
}
|
|
2568
|
+
if (opts?.borderRadius !== false) {
|
|
2569
|
+
const borderRadiusOpts = typeof opts?.borderRadius === "number" ? { radius: opts.borderRadius } : void 0;
|
|
2570
|
+
enableScaledBorderRadius(canvas, borderRadiusOpts);
|
|
2571
|
+
}
|
|
2572
|
+
if (opts?.keyboardShortcuts !== false) {
|
|
2573
|
+
keyboardCleanupRef.current = enableKeyboardShortcuts(canvas);
|
|
2574
|
+
}
|
|
2575
|
+
setCanvasAlignmentEnabled(canvas, opts?.enableAlignment);
|
|
2576
|
+
if (opts?.panAndZoom !== false) {
|
|
2577
|
+
viewportRef.current = enablePanAndZoom(
|
|
2578
|
+
canvas,
|
|
2579
|
+
typeof opts?.panAndZoom === "object" ? opts.panAndZoom : void 0
|
|
2580
|
+
);
|
|
2581
|
+
}
|
|
2582
|
+
const alignmentEnabled = resolveAlignmentEnabled(
|
|
2583
|
+
opts?.enableAlignment,
|
|
2584
|
+
opts?.alignment
|
|
2547
2585
|
);
|
|
2586
|
+
if (alignmentEnabled) {
|
|
2587
|
+
alignmentCleanupRef.current = enableObjectAlignment(
|
|
2588
|
+
canvas,
|
|
2589
|
+
typeof opts?.alignment === "object" ? opts.alignment : void 0
|
|
2590
|
+
);
|
|
2591
|
+
}
|
|
2592
|
+
if (opts?.rotationSnap !== false) {
|
|
2593
|
+
rotationSnapCleanupRef.current = enableRotationSnap(
|
|
2594
|
+
canvas,
|
|
2595
|
+
typeof opts?.rotationSnap === "object" ? opts.rotationSnap : void 0
|
|
2596
|
+
);
|
|
2597
|
+
}
|
|
2598
|
+
if (opts?.history) {
|
|
2599
|
+
const historyOpts = typeof opts.history === "object" ? opts.history : void 0;
|
|
2600
|
+
historyRef.current = createHistoryTracker(canvas, historyOpts);
|
|
2601
|
+
}
|
|
2548
2602
|
}
|
|
2549
|
-
|
|
2550
|
-
setZoom(canvas.getZoom());
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
}
|
|
2593
|
-
});
|
|
2594
|
-
}
|
|
2595
|
-
if (opts?.history) {
|
|
2596
|
-
const historyOpts = typeof opts.history === "object" ? opts.history : void 0;
|
|
2597
|
-
historyRef.current = createHistoryTracker(canvas, historyOpts);
|
|
2603
|
+
function setupEventListeners() {
|
|
2604
|
+
canvas.on("mouse:wheel", () => setZoom(canvas.getZoom()));
|
|
2605
|
+
canvas.on("selection:created", (e) => setSelected(e.selected ?? []));
|
|
2606
|
+
canvas.on("selection:updated", (e) => setSelected(e.selected ?? []));
|
|
2607
|
+
canvas.on("selection:cleared", () => setSelected([]));
|
|
2608
|
+
if (opts?.trackChanges) {
|
|
2609
|
+
canvas.on("object:added", () => setIsDirty(true));
|
|
2610
|
+
canvas.on("object:removed", () => setIsDirty(true));
|
|
2611
|
+
canvas.on("object:modified", () => setIsDirty(true));
|
|
2612
|
+
}
|
|
2613
|
+
if (opts?.history) {
|
|
2614
|
+
const syncHistoryState = () => {
|
|
2615
|
+
const h = historyRef.current;
|
|
2616
|
+
if (!h) return;
|
|
2617
|
+
setTimeout(() => {
|
|
2618
|
+
setCanUndo(h.canUndo());
|
|
2619
|
+
setCanRedo(h.canRedo());
|
|
2620
|
+
}, 350);
|
|
2621
|
+
};
|
|
2622
|
+
canvas.on("object:added", syncHistoryState);
|
|
2623
|
+
canvas.on("object:removed", syncHistoryState);
|
|
2624
|
+
canvas.on("object:modified", syncHistoryState);
|
|
2625
|
+
}
|
|
2626
|
+
if (opts?.vertexEdit !== false) {
|
|
2627
|
+
const vertexOpts = typeof opts?.vertexEdit === "object" ? opts.vertexEdit : void 0;
|
|
2628
|
+
canvas.on("mouse:dblclick", (e) => {
|
|
2629
|
+
if (e.target && e.target instanceof import_fabric17.Polygon) {
|
|
2630
|
+
vertexEditCleanupRef.current?.();
|
|
2631
|
+
vertexEditCleanupRef.current = enableVertexEdit(
|
|
2632
|
+
canvas,
|
|
2633
|
+
e.target,
|
|
2634
|
+
{
|
|
2635
|
+
...vertexOpts,
|
|
2636
|
+
onExit: () => {
|
|
2637
|
+
vertexEditCleanupRef.current = null;
|
|
2638
|
+
setIsEditingVertices(false);
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
);
|
|
2642
|
+
setIsEditingVertices(true);
|
|
2643
|
+
}
|
|
2644
|
+
});
|
|
2645
|
+
}
|
|
2598
2646
|
}
|
|
2599
|
-
|
|
2600
|
-
|
|
2647
|
+
function invokeOnReady() {
|
|
2648
|
+
const onReadyResult = opts?.onReady?.(canvas);
|
|
2601
2649
|
Promise.resolve(onReadyResult).then(() => {
|
|
2602
|
-
if (canvas.backgroundImage) {
|
|
2650
|
+
if (opts?.autoFitToBackground !== false && canvas.backgroundImage) {
|
|
2603
2651
|
fitViewportToBackground(canvas);
|
|
2604
2652
|
syncZoom(canvasRef, setZoom);
|
|
2605
2653
|
}
|
|
2606
2654
|
historyRef.current?.pushSnapshot();
|
|
2607
2655
|
});
|
|
2608
|
-
} else {
|
|
2609
|
-
Promise.resolve(onReadyResult).then(() => {
|
|
2610
|
-
historyRef.current?.pushSnapshot();
|
|
2611
|
-
});
|
|
2612
2656
|
}
|
|
2613
2657
|
},
|
|
2614
|
-
//
|
|
2658
|
+
// Dependency array intentionally empty — we only initialize once on mount
|
|
2615
2659
|
[]
|
|
2616
2660
|
);
|
|
2617
2661
|
(0, import_react3.useEffect)(() => {
|
|
@@ -3001,18 +3045,24 @@ function useCanvasClick(canvasRef, onClick, options) {
|
|
|
3001
3045
|
}, [canvasRef]);
|
|
3002
3046
|
}
|
|
3003
3047
|
|
|
3004
|
-
// src/
|
|
3048
|
+
// src/overlay/ObjectOverlay.tsx
|
|
3005
3049
|
var import_react8 = require("react");
|
|
3050
|
+
var import_material = require("@mui/material");
|
|
3006
3051
|
var import_fabric18 = require("fabric");
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3052
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
3053
|
+
function ObjectOverlay({
|
|
3054
|
+
canvasRef,
|
|
3055
|
+
object,
|
|
3056
|
+
sx,
|
|
3057
|
+
children,
|
|
3058
|
+
...rest
|
|
3059
|
+
}) {
|
|
3060
|
+
const stackRef = (0, import_react8.useRef)(null);
|
|
3011
3061
|
(0, import_react8.useEffect)(() => {
|
|
3012
3062
|
const canvas = canvasRef.current;
|
|
3013
3063
|
if (!canvas || !object) return;
|
|
3014
3064
|
function update() {
|
|
3015
|
-
const el =
|
|
3065
|
+
const el = stackRef.current;
|
|
3016
3066
|
if (!el || !canvas || !object) return;
|
|
3017
3067
|
const zoom = canvas.getZoom();
|
|
3018
3068
|
const vt = canvas.viewportTransform;
|
|
@@ -3024,32 +3074,11 @@ function useObjectOverlay(canvasRef, object, options) {
|
|
|
3024
3074
|
const screenWidth = actualWidth * zoom;
|
|
3025
3075
|
const screenHeight = actualHeight * zoom;
|
|
3026
3076
|
const angle = object.angle ?? 0;
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
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;
|
|
3039
|
-
const textEls = el.querySelectorAll(opts.textSelector);
|
|
3040
|
-
const display = zoom < textMinScale ? "none" : "";
|
|
3041
|
-
textEls.forEach((t) => {
|
|
3042
|
-
t.style.display = display;
|
|
3043
|
-
});
|
|
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` : "";
|
|
3052
|
-
}
|
|
3077
|
+
el.style.left = `${screenCoords.x - screenWidth / 2}px`;
|
|
3078
|
+
el.style.top = `${screenCoords.y - screenHeight / 2}px`;
|
|
3079
|
+
el.style.width = `${screenWidth}px`;
|
|
3080
|
+
el.style.height = `${screenHeight}px`;
|
|
3081
|
+
el.style.transform = angle !== 0 ? `rotate(${angle}deg)` : "";
|
|
3053
3082
|
}
|
|
3054
3083
|
update();
|
|
3055
3084
|
canvas.on("after:render", update);
|
|
@@ -3063,7 +3092,168 @@ function useObjectOverlay(canvasRef, object, options) {
|
|
|
3063
3092
|
object.off("rotating", update);
|
|
3064
3093
|
};
|
|
3065
3094
|
}, [canvasRef, object]);
|
|
3066
|
-
return
|
|
3095
|
+
if (!object) return null;
|
|
3096
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
3097
|
+
import_material.Stack,
|
|
3098
|
+
{
|
|
3099
|
+
ref: stackRef,
|
|
3100
|
+
sx: {
|
|
3101
|
+
position: "absolute",
|
|
3102
|
+
pointerEvents: "none",
|
|
3103
|
+
alignItems: "center",
|
|
3104
|
+
justifyContent: "center",
|
|
3105
|
+
zIndex: 1,
|
|
3106
|
+
overflow: "hidden",
|
|
3107
|
+
...sx
|
|
3108
|
+
},
|
|
3109
|
+
...rest,
|
|
3110
|
+
children
|
|
3111
|
+
}
|
|
3112
|
+
);
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3115
|
+
// src/overlay/OverlayContent.tsx
|
|
3116
|
+
var import_material2 = require("@mui/material");
|
|
3117
|
+
var import_react9 = require("react");
|
|
3118
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
3119
|
+
function OverlayContent({
|
|
3120
|
+
children,
|
|
3121
|
+
padding = 4,
|
|
3122
|
+
maxScale = 2,
|
|
3123
|
+
sx,
|
|
3124
|
+
...rest
|
|
3125
|
+
}) {
|
|
3126
|
+
const outerRef = (0, import_react9.useRef)(null);
|
|
3127
|
+
const innerRef = (0, import_react9.useRef)(null);
|
|
3128
|
+
(0, import_react9.useEffect)(() => {
|
|
3129
|
+
const outer = outerRef.current;
|
|
3130
|
+
const inner = innerRef.current;
|
|
3131
|
+
if (!outer || !inner) return;
|
|
3132
|
+
function fit() {
|
|
3133
|
+
if (!outer || !inner) return;
|
|
3134
|
+
const containerW = outer.clientWidth;
|
|
3135
|
+
const containerH = outer.clientHeight;
|
|
3136
|
+
const natW = inner.scrollWidth;
|
|
3137
|
+
const natH = inner.scrollHeight;
|
|
3138
|
+
if (natW === 0 || natH === 0 || containerW <= 0 || containerH <= 0) {
|
|
3139
|
+
inner.style.transform = "";
|
|
3140
|
+
inner.style.removeProperty("--overlay-scale");
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
3143
|
+
const scale = Math.min(
|
|
3144
|
+
containerW / (natW + padding * 2),
|
|
3145
|
+
containerH / (natH + padding * 2),
|
|
3146
|
+
maxScale
|
|
3147
|
+
);
|
|
3148
|
+
inner.style.transform = `scale(${scale})`;
|
|
3149
|
+
inner.style.setProperty("--overlay-scale", String(scale));
|
|
3150
|
+
}
|
|
3151
|
+
const observer = new ResizeObserver(fit);
|
|
3152
|
+
observer.observe(outer);
|
|
3153
|
+
observer.observe(inner);
|
|
3154
|
+
fit();
|
|
3155
|
+
return () => observer.disconnect();
|
|
3156
|
+
}, [padding, maxScale]);
|
|
3157
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3158
|
+
import_material2.Stack,
|
|
3159
|
+
{
|
|
3160
|
+
ref: outerRef,
|
|
3161
|
+
sx: {
|
|
3162
|
+
width: "100%",
|
|
3163
|
+
height: "100%",
|
|
3164
|
+
alignItems: "center",
|
|
3165
|
+
justifyContent: "center",
|
|
3166
|
+
overflow: "hidden",
|
|
3167
|
+
...sx
|
|
3168
|
+
},
|
|
3169
|
+
...rest,
|
|
3170
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3171
|
+
import_material2.Stack,
|
|
3172
|
+
{
|
|
3173
|
+
ref: innerRef,
|
|
3174
|
+
sx: {
|
|
3175
|
+
transformOrigin: "center center",
|
|
3176
|
+
flexShrink: 0,
|
|
3177
|
+
width: "max-content"
|
|
3178
|
+
},
|
|
3179
|
+
children
|
|
3180
|
+
}
|
|
3181
|
+
)
|
|
3182
|
+
}
|
|
3183
|
+
);
|
|
3184
|
+
}
|
|
3185
|
+
|
|
3186
|
+
// src/overlay/FixedSizeContent.tsx
|
|
3187
|
+
var import_material3 = require("@mui/material");
|
|
3188
|
+
var import_react10 = require("react");
|
|
3189
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
3190
|
+
function FixedSizeContent({
|
|
3191
|
+
children,
|
|
3192
|
+
hideOnOverflow = true,
|
|
3193
|
+
truncationPadding = 4,
|
|
3194
|
+
sx,
|
|
3195
|
+
...rest
|
|
3196
|
+
}) {
|
|
3197
|
+
const ref = (0, import_react10.useRef)(null);
|
|
3198
|
+
const totalContentHeightRef = (0, import_react10.useRef)(0);
|
|
3199
|
+
(0, import_react10.useEffect)(() => {
|
|
3200
|
+
const el = ref.current;
|
|
3201
|
+
if (!el) return;
|
|
3202
|
+
let clipAncestor = el.parentElement;
|
|
3203
|
+
while (clipAncestor) {
|
|
3204
|
+
if (getComputedStyle(clipAncestor).overflow === "hidden") break;
|
|
3205
|
+
clipAncestor = clipAncestor.parentElement;
|
|
3206
|
+
}
|
|
3207
|
+
if (!clipAncestor) return;
|
|
3208
|
+
const ancestor = clipAncestor;
|
|
3209
|
+
totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
|
|
3210
|
+
function check() {
|
|
3211
|
+
requestAnimationFrame(() => {
|
|
3212
|
+
if (!el) return;
|
|
3213
|
+
const containerRect = ancestor.getBoundingClientRect();
|
|
3214
|
+
el.style.maxWidth = `${Math.max(0, containerRect.width - truncationPadding * 2)}px`;
|
|
3215
|
+
if (!hideOnOverflow) return;
|
|
3216
|
+
const fits = containerRect.height >= totalContentHeightRef.current;
|
|
3217
|
+
if (fits && el.style.display === "none") {
|
|
3218
|
+
el.style.display = "";
|
|
3219
|
+
totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
|
|
3220
|
+
if (containerRect.height < totalContentHeightRef.current) {
|
|
3221
|
+
el.style.display = "none";
|
|
3222
|
+
}
|
|
3223
|
+
} else if (!fits && el.style.display !== "none") {
|
|
3224
|
+
el.style.display = "none";
|
|
3225
|
+
}
|
|
3226
|
+
if (el.style.display !== "none") {
|
|
3227
|
+
totalContentHeightRef.current = el.parentElement?.scrollHeight ?? 0;
|
|
3228
|
+
}
|
|
3229
|
+
});
|
|
3230
|
+
}
|
|
3231
|
+
const observer = new ResizeObserver(check);
|
|
3232
|
+
observer.observe(ancestor);
|
|
3233
|
+
check();
|
|
3234
|
+
return () => observer.disconnect();
|
|
3235
|
+
}, [hideOnOverflow, truncationPadding]);
|
|
3236
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3237
|
+
import_material3.Stack,
|
|
3238
|
+
{
|
|
3239
|
+
ref,
|
|
3240
|
+
sx: {
|
|
3241
|
+
transform: "scale(calc(1 / var(--overlay-scale, 1)))",
|
|
3242
|
+
transformOrigin: "center center",
|
|
3243
|
+
flexShrink: 0,
|
|
3244
|
+
width: "max-content",
|
|
3245
|
+
overflow: "hidden",
|
|
3246
|
+
"& > *": {
|
|
3247
|
+
maxWidth: "100%",
|
|
3248
|
+
overflow: "hidden",
|
|
3249
|
+
textOverflow: "ellipsis"
|
|
3250
|
+
},
|
|
3251
|
+
...sx
|
|
3252
|
+
},
|
|
3253
|
+
...rest,
|
|
3254
|
+
children
|
|
3255
|
+
}
|
|
3256
|
+
);
|
|
3067
3257
|
}
|
|
3068
3258
|
|
|
3069
3259
|
// src/index.ts
|
|
@@ -3080,6 +3270,9 @@ var import_fabric19 = require("fabric");
|
|
|
3080
3270
|
FabricCanvas,
|
|
3081
3271
|
FabricImage,
|
|
3082
3272
|
FabricObject,
|
|
3273
|
+
FixedSizeContent,
|
|
3274
|
+
ObjectOverlay,
|
|
3275
|
+
OverlayContent,
|
|
3083
3276
|
Point,
|
|
3084
3277
|
Polygon,
|
|
3085
3278
|
Rect,
|
|
@@ -3125,7 +3318,6 @@ var import_fabric19 = require("fabric");
|
|
|
3125
3318
|
useCanvasEvents,
|
|
3126
3319
|
useCanvasTooltip,
|
|
3127
3320
|
useEditCanvas,
|
|
3128
|
-
useObjectOverlay,
|
|
3129
3321
|
useViewCanvas,
|
|
3130
3322
|
util
|
|
3131
3323
|
});
|