@leapfrog/interactive-canvas 2.0.25-beta-6 → 2.0.25-beta-8
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/abstract-interactive-canvas.d.ts +0 -1
- package/dist/interactive-canvas.d.ts +1 -0
- package/dist/interactive-canvas.es.js +80 -87
- package/dist/interactive-canvas.js +80 -87
- package/dist/layout/advanced-layout-manager.d.ts +0 -1
- package/dist/layout/standard-layout-manager.d.ts +0 -1
- package/dist/object/impl/text.d.ts +0 -2
- package/package.json +1 -1
|
@@ -340,7 +340,6 @@ export declare abstract class AbstractInteractiveCanvas implements IInteractiveC
|
|
|
340
340
|
* @inheritDoc
|
|
341
341
|
*/
|
|
342
342
|
cloneObject(sourceObject: FabricObject, objectData: CanvasObjectData): Promise<void>;
|
|
343
|
-
private redrawScheduled;
|
|
344
343
|
/**
|
|
345
344
|
* Redraw the canvas.
|
|
346
345
|
*/
|
|
@@ -232,18 +232,9 @@ class ResizeCanvasLayoutFunction {
|
|
|
232
232
|
const targetHeight = increase.withSize(increase.width, baseDimensions.height + increase.height);
|
|
233
233
|
if (currentDimensions.height !== targetHeight.height) {
|
|
234
234
|
canvas.setDimensions(targetHeight);
|
|
235
|
-
// Only update coords for objects affected by height change (pinned to bottom, etc.)
|
|
236
|
-
// This is much faster than updating all objects on slow machines
|
|
237
|
-
canvas.getObjectsInternal().forEach((object) => {
|
|
238
|
-
var _a;
|
|
239
|
-
const canvasObj = canvas.getObjectsByTypeInternal(object.type).find(o => o.getId() === object.getId());
|
|
240
|
-
// Only update coords for objects that are positioned relative to canvas bottom or that might be affected
|
|
241
|
-
if (canvasObj && (((_a = canvasObj.isPinToBottom) === null || _a === void 0 ? void 0 : _a.call(canvasObj)) || object.top > currentDimensions.heightPx * 0.5)) {
|
|
242
|
-
object.setCoords();
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
canvas.redraw();
|
|
246
235
|
}
|
|
236
|
+
canvas.getObjectsInternal().forEach((object) => object.setCoords());
|
|
237
|
+
canvas.redraw();
|
|
247
238
|
}
|
|
248
239
|
}
|
|
249
240
|
|
|
@@ -271,22 +262,13 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
271
262
|
this.repositionCutoffs = new RepositionCutoffsLayoutFunction();
|
|
272
263
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
273
264
|
this.resizeCanvas = new ResizeCanvasLayoutFunction();
|
|
274
|
-
this.dimensionsLayoutScheduled = false;
|
|
275
265
|
}
|
|
276
266
|
onCanvasDimensionsChange() {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
this.
|
|
282
|
-
Promise.resolve().then(() => {
|
|
283
|
-
this.pinToBottom.execute(this.canvas);
|
|
284
|
-
this.repositionCutoffs.execute(this.canvas);
|
|
285
|
-
this.repositionBorders.execute(this.canvas);
|
|
286
|
-
this.repositionBackgrounds.execute(this.canvas);
|
|
287
|
-
this.redrawAll.execute(this.canvas);
|
|
288
|
-
this.dimensionsLayoutScheduled = false;
|
|
289
|
-
});
|
|
267
|
+
this.pinToBottom.execute(this.canvas);
|
|
268
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
269
|
+
this.repositionBorders.execute(this.canvas);
|
|
270
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
271
|
+
this.redrawAll.execute(this.canvas);
|
|
290
272
|
}
|
|
291
273
|
onTextChange() {
|
|
292
274
|
this.resizeCanvas.execute(this.canvas);
|
|
@@ -314,9 +296,7 @@ class StackObjectsLayoutFunction {
|
|
|
314
296
|
const footerObjects = objects[0];
|
|
315
297
|
const bodyObjects = objects[1];
|
|
316
298
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
317
|
-
|
|
318
|
-
// No need to redraw here - it will be called by the layout manager after all layout functions complete
|
|
319
|
-
// This reduces redundant render passes significantly
|
|
299
|
+
canvas.redraw();
|
|
320
300
|
//Make one pass and set the widths of all elements. This will force objects with dynamic heights (like text) to take on their necessary height before trying to lay them out.
|
|
321
301
|
this.applyWidths(allObjects, bodyWidth);
|
|
322
302
|
const bodyRows = this.getRows(bodyObjects);
|
|
@@ -403,11 +383,7 @@ class StackObjectsLayoutFunction {
|
|
|
403
383
|
object.setPosition({
|
|
404
384
|
width: actualWidth
|
|
405
385
|
});
|
|
406
|
-
|
|
407
|
-
if (object.getType() !== ObjectType.VERTICAL_RULE) {
|
|
408
|
-
object.updateDimensions();
|
|
409
|
-
}
|
|
410
|
-
});
|
|
386
|
+
object.updateDimensions();
|
|
411
387
|
});
|
|
412
388
|
}
|
|
413
389
|
adjustCanvasSize(canvas, requiredHeight) {
|
|
@@ -480,7 +456,6 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
480
456
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
481
457
|
this.stackObjects = new StackObjectsLayoutFunction();
|
|
482
458
|
this.updateObjectDimensions = new UpdateObjectDimensionsLayoutFunction();
|
|
483
|
-
this.layoutBatchScheduled = false;
|
|
484
459
|
}
|
|
485
460
|
/**
|
|
486
461
|
* @override
|
|
@@ -522,21 +497,12 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
522
497
|
this.stackAndRedraw();
|
|
523
498
|
}
|
|
524
499
|
stackAndRedraw() {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
this.
|
|
530
|
-
|
|
531
|
-
Promise.resolve().then(() => {
|
|
532
|
-
this.stackObjects.execute(this.canvas);
|
|
533
|
-
this.repositionCutoffs.execute(this.canvas);
|
|
534
|
-
this.repositionBorders.execute(this.canvas);
|
|
535
|
-
this.repositionBackgrounds.execute(this.canvas);
|
|
536
|
-
this.updateObjectDimensions.execute(this.canvas);
|
|
537
|
-
this.redrawAll.execute(this.canvas);
|
|
538
|
-
this.layoutBatchScheduled = false;
|
|
539
|
-
});
|
|
500
|
+
this.stackObjects.execute(this.canvas);
|
|
501
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
502
|
+
this.repositionBorders.execute(this.canvas);
|
|
503
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
504
|
+
this.updateObjectDimensions.execute(this.canvas);
|
|
505
|
+
this.redrawAll.execute(this.canvas);
|
|
540
506
|
}
|
|
541
507
|
}
|
|
542
508
|
|
|
@@ -1814,9 +1780,6 @@ var ObjectType;
|
|
|
1814
1780
|
class Text extends AbstractCanvasObject {
|
|
1815
1781
|
constructor(canvas, fabricObject, persistedField) {
|
|
1816
1782
|
super(ObjectType.TEXT, canvas, fabricObject, persistedField);
|
|
1817
|
-
//private notifyCanvasTimeout: NodeJS.Timeout | null = null;
|
|
1818
|
-
this.notifyCanvasTimeout = null;
|
|
1819
|
-
this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
|
|
1820
1783
|
this.canvas = canvas;
|
|
1821
1784
|
this.fontLibrary = canvas.getFontLibrary();
|
|
1822
1785
|
this.baseComponentHeight = this.fabricObject.height;
|
|
@@ -2564,42 +2527,28 @@ class Text extends AbstractCanvasObject {
|
|
|
2564
2527
|
onTextChange() {
|
|
2565
2528
|
this.data.userText = this.fabricObject.text;
|
|
2566
2529
|
this.text$.next(this.fabricObject.text);
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
// Skip expensive getBoundingBox if object is clearly within bounds
|
|
2571
|
-
if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
|
|
2572
|
-
if (this.getBoundingBox().width > canvasWidth) {
|
|
2573
|
-
this.fabricObject.set({ left: 0, width: canvasWidth });
|
|
2574
|
-
this.redraw();
|
|
2575
|
-
}
|
|
2530
|
+
if (this.getBoundingBox().width > this.canvas.getDimensions().widthPx) {
|
|
2531
|
+
this.fabricObject.set({ left: 0, width: this.canvas.getDimensions().widthPx });
|
|
2532
|
+
this.redraw();
|
|
2576
2533
|
}
|
|
2577
2534
|
}
|
|
2578
2535
|
onDelete() {
|
|
2579
2536
|
this.removeBullets();
|
|
2580
2537
|
}
|
|
2581
2538
|
onSelectionChange() {
|
|
2582
|
-
this.updateTextSelection(
|
|
2539
|
+
this.updateTextSelection();
|
|
2583
2540
|
}
|
|
2584
2541
|
hasTextSelection() {
|
|
2585
2542
|
return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
|
|
2586
2543
|
}
|
|
2587
|
-
updateTextSelection(
|
|
2544
|
+
updateTextSelection() {
|
|
2588
2545
|
if (this.canvas.headless)
|
|
2589
2546
|
return;
|
|
2590
2547
|
this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
2591
|
-
|
|
2592
|
-
this.notifyCanvasOfTextChanges();
|
|
2593
|
-
}
|
|
2548
|
+
this.notifyCanvasOfTextChanges();
|
|
2594
2549
|
}
|
|
2595
2550
|
notifyCanvasOfTextChanges() {
|
|
2596
|
-
|
|
2597
|
-
clearTimeout(this.notifyCanvasTimeout);
|
|
2598
|
-
}
|
|
2599
|
-
this.notifyCanvasTimeout = setTimeout(() => {
|
|
2600
|
-
this.canvas.notifyTextChanged();
|
|
2601
|
-
this.notifyCanvasTimeout = null;
|
|
2602
|
-
}, this.NOTIFY_DELAY_MS);
|
|
2551
|
+
this.canvas.notifyTextChanged();
|
|
2603
2552
|
}
|
|
2604
2553
|
}
|
|
2605
2554
|
Text.NEW_LINE = "\n";
|
|
@@ -2765,7 +2714,6 @@ class AbstractInteractiveCanvas {
|
|
|
2765
2714
|
this.devicePixelRatio = devicePixelRatio;
|
|
2766
2715
|
this.fontLibrary = new FontLibrary();
|
|
2767
2716
|
this.dimensionChangeRejections$ = new Subject();
|
|
2768
|
-
this.redrawScheduled = false;
|
|
2769
2717
|
this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
|
|
2770
2718
|
/**
|
|
2771
2719
|
* Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
|
|
@@ -2788,7 +2736,7 @@ class AbstractInteractiveCanvas {
|
|
|
2788
2736
|
this.fabricCanvas.stateful = true;
|
|
2789
2737
|
this.fabricCanvas.preserveObjectStacking = true;
|
|
2790
2738
|
this.fabricCanvas.perPixelTargetFind = true;
|
|
2791
|
-
this.fabricCanvas.targetFindTolerance =
|
|
2739
|
+
this.fabricCanvas.targetFindTolerance = 4;
|
|
2792
2740
|
this.setBackgroundColor("#FFFFFF");
|
|
2793
2741
|
}
|
|
2794
2742
|
/**
|
|
@@ -3182,7 +3130,11 @@ class AbstractInteractiveCanvas {
|
|
|
3182
3130
|
charSpacing: profile.defaultCharacterSpacing,
|
|
3183
3131
|
perPixelTargetFind: false,
|
|
3184
3132
|
targetFindTolerance: 4,
|
|
3185
|
-
splitByGrapheme: true
|
|
3133
|
+
splitByGrapheme: true,
|
|
3134
|
+
editable: false, // Only enable on double-click
|
|
3135
|
+
padding: 0,
|
|
3136
|
+
cornerSize: 8,
|
|
3137
|
+
touchCornerSize: 12
|
|
3186
3138
|
};
|
|
3187
3139
|
const fabricTextbox = new this.fabric.Textbox("Text goes here", textOptions);
|
|
3188
3140
|
this.fabricCanvas.add(fabricTextbox);
|
|
@@ -3631,16 +3583,7 @@ class AbstractInteractiveCanvas {
|
|
|
3631
3583
|
* Redraw the canvas.
|
|
3632
3584
|
*/
|
|
3633
3585
|
redraw() {
|
|
3634
|
-
|
|
3635
|
-
if (this.redrawScheduled) {
|
|
3636
|
-
return; // Already scheduled, don't schedule again
|
|
3637
|
-
}
|
|
3638
|
-
this.redrawScheduled = true;
|
|
3639
|
-
// Use requestAnimationFrame for smoother rendering and automatic batching
|
|
3640
|
-
requestAnimationFrame(() => {
|
|
3641
|
-
this.fabricCanvas.requestRenderAll();
|
|
3642
|
-
this.redrawScheduled = false;
|
|
3643
|
-
});
|
|
3586
|
+
this.fabricCanvas.requestRenderAll();
|
|
3644
3587
|
}
|
|
3645
3588
|
syncObjectList() {
|
|
3646
3589
|
const newObjectList = _(this.fabricCanvas.getObjects())
|
|
@@ -3766,6 +3709,7 @@ class AbstractInteractiveCanvas {
|
|
|
3766
3709
|
* Update the actual dimensions of the canvas by multiplying the requested dimensions by the current zoom factor.
|
|
3767
3710
|
*/
|
|
3768
3711
|
updateCanvasDimensions(dims, zoom) {
|
|
3712
|
+
const oldWidth = this.fabricCanvas.getWidth();
|
|
3769
3713
|
const width = dims.widthPx * zoom;
|
|
3770
3714
|
const height = dims.heightPx * zoom;
|
|
3771
3715
|
this.fabricCanvas.setZoom(zoom);
|
|
@@ -3773,6 +3717,34 @@ class AbstractInteractiveCanvas {
|
|
|
3773
3717
|
width,
|
|
3774
3718
|
height
|
|
3775
3719
|
});
|
|
3720
|
+
const scaleFactor = width / oldWidth;
|
|
3721
|
+
const objects = this.fabricCanvas.getObjects();
|
|
3722
|
+
objects.forEach((obj) => {
|
|
3723
|
+
obj.set({
|
|
3724
|
+
left: obj.left * scaleFactor,
|
|
3725
|
+
});
|
|
3726
|
+
if (obj.type === "textbox" || obj.type === "text") {
|
|
3727
|
+
obj.set({
|
|
3728
|
+
scaleX: 1,
|
|
3729
|
+
scaleY: 1,
|
|
3730
|
+
width: obj.width * scaleFactor,
|
|
3731
|
+
});
|
|
3732
|
+
}
|
|
3733
|
+
else if (obj.type === "border" || obj.type === "rect") {
|
|
3734
|
+
obj.set({
|
|
3735
|
+
width: obj.width * scaleFactor,
|
|
3736
|
+
scaleX: 1,
|
|
3737
|
+
strokeUniform: true,
|
|
3738
|
+
});
|
|
3739
|
+
}
|
|
3740
|
+
else {
|
|
3741
|
+
obj.set({
|
|
3742
|
+
scaleX: obj.scaleX * scaleFactor,
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
obj.setCoords();
|
|
3746
|
+
});
|
|
3747
|
+
this.fabricCanvas.requestRenderAll();
|
|
3776
3748
|
}
|
|
3777
3749
|
onProfileChanged(profile) {
|
|
3778
3750
|
this.setMultiSelect(profile.allowMultiSelect);
|
|
@@ -3927,8 +3899,18 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3927
3899
|
this.fabricCanvas.on("selection:created", (event) => this.onFabricSelectionChanged(event));
|
|
3928
3900
|
this.fabricCanvas.on("selection:cleared", (event) => this.onFabricSelectionChanged(event));
|
|
3929
3901
|
this.fabricCanvas.on("text:editing:entered", () => this.onTextEditing());
|
|
3930
|
-
this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
|
|
3902
|
+
//this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
|
|
3903
|
+
this.fabricCanvas.on("text:editing:exited", (event) => {
|
|
3904
|
+
this.onTextEditing();
|
|
3905
|
+
const target = event.target;
|
|
3906
|
+
if (target && (target.type === 'textbox' || target.type === 'i-text')) {
|
|
3907
|
+
target.set({ editable: false, hoverCursor: 'move' });
|
|
3908
|
+
}
|
|
3909
|
+
});
|
|
3931
3910
|
this.fabricCanvas.on("text:changed", () => this.onTextChange());
|
|
3911
|
+
// Handle double-click on textbox to enter editing mode
|
|
3912
|
+
// @ts-ignore
|
|
3913
|
+
this.fabricCanvas.on("mouse:dblclick", (event) => this.onDoubleClick(event));
|
|
3932
3914
|
}
|
|
3933
3915
|
/**
|
|
3934
3916
|
* @override
|
|
@@ -3977,6 +3959,17 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3977
3959
|
const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
|
|
3978
3960
|
this.selection$.next(new SelectionData(selected));
|
|
3979
3961
|
}
|
|
3962
|
+
onDoubleClick(event) {
|
|
3963
|
+
const target = event.target;
|
|
3964
|
+
if (target && (target.type === 'textbox' || target.type === 'i-text')) {
|
|
3965
|
+
if (!target.isEditing) {
|
|
3966
|
+
target.set({ editable: true, hoverCursor: 'text' });
|
|
3967
|
+
// target.enterEditing();
|
|
3968
|
+
// target.hiddenTextarea?.focus();
|
|
3969
|
+
this.enterTextSelectionModeIfRequired();
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3980
3973
|
}
|
|
3981
3974
|
|
|
3982
3975
|
export { AbstractCanvasObject, AbstractInteractiveCanvas, AbstractLayoutManager, AdvancedLayoutManager, BackgroundImage, Border, CanvasObjectData, Cutoff, DefaultCanvasProfile, Font, FontLibrary, HeadlessInteractiveCanvas, HorizontalRule, Image, ImageType, InteractiveCanvas, LayoutManager, MillimeterDimensions, ObjectType, PixelCanvasDimensions, Print6ColCanvasDimensions, Print8ColCanvasDimensions, Rectangle, RichText, SelectionData, StandardLayoutManager, Text, UNKNOWN_FONT, UnknownFont, VerticalRule, isMutableBackgroundColor, isMutableColor, isMutableImage, isMutablePosition, isMutableStroke, isMutableText, isMutableTextStyle };
|
|
@@ -253,18 +253,9 @@ class ResizeCanvasLayoutFunction {
|
|
|
253
253
|
const targetHeight = increase.withSize(increase.width, baseDimensions.height + increase.height);
|
|
254
254
|
if (currentDimensions.height !== targetHeight.height) {
|
|
255
255
|
canvas.setDimensions(targetHeight);
|
|
256
|
-
// Only update coords for objects affected by height change (pinned to bottom, etc.)
|
|
257
|
-
// This is much faster than updating all objects on slow machines
|
|
258
|
-
canvas.getObjectsInternal().forEach((object) => {
|
|
259
|
-
var _a;
|
|
260
|
-
const canvasObj = canvas.getObjectsByTypeInternal(object.type).find(o => o.getId() === object.getId());
|
|
261
|
-
// Only update coords for objects that are positioned relative to canvas bottom or that might be affected
|
|
262
|
-
if (canvasObj && (((_a = canvasObj.isPinToBottom) === null || _a === void 0 ? void 0 : _a.call(canvasObj)) || object.top > currentDimensions.heightPx * 0.5)) {
|
|
263
|
-
object.setCoords();
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
canvas.redraw();
|
|
267
256
|
}
|
|
257
|
+
canvas.getObjectsInternal().forEach((object) => object.setCoords());
|
|
258
|
+
canvas.redraw();
|
|
268
259
|
}
|
|
269
260
|
}
|
|
270
261
|
|
|
@@ -292,22 +283,13 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
292
283
|
this.repositionCutoffs = new RepositionCutoffsLayoutFunction();
|
|
293
284
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
294
285
|
this.resizeCanvas = new ResizeCanvasLayoutFunction();
|
|
295
|
-
this.dimensionsLayoutScheduled = false;
|
|
296
286
|
}
|
|
297
287
|
onCanvasDimensionsChange() {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
this.
|
|
303
|
-
Promise.resolve().then(() => {
|
|
304
|
-
this.pinToBottom.execute(this.canvas);
|
|
305
|
-
this.repositionCutoffs.execute(this.canvas);
|
|
306
|
-
this.repositionBorders.execute(this.canvas);
|
|
307
|
-
this.repositionBackgrounds.execute(this.canvas);
|
|
308
|
-
this.redrawAll.execute(this.canvas);
|
|
309
|
-
this.dimensionsLayoutScheduled = false;
|
|
310
|
-
});
|
|
288
|
+
this.pinToBottom.execute(this.canvas);
|
|
289
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
290
|
+
this.repositionBorders.execute(this.canvas);
|
|
291
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
292
|
+
this.redrawAll.execute(this.canvas);
|
|
311
293
|
}
|
|
312
294
|
onTextChange() {
|
|
313
295
|
this.resizeCanvas.execute(this.canvas);
|
|
@@ -335,9 +317,7 @@ class StackObjectsLayoutFunction {
|
|
|
335
317
|
const footerObjects = objects[0];
|
|
336
318
|
const bodyObjects = objects[1];
|
|
337
319
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
338
|
-
|
|
339
|
-
// No need to redraw here - it will be called by the layout manager after all layout functions complete
|
|
340
|
-
// This reduces redundant render passes significantly
|
|
320
|
+
canvas.redraw();
|
|
341
321
|
//Make one pass and set the widths of all elements. This will force objects with dynamic heights (like text) to take on their necessary height before trying to lay them out.
|
|
342
322
|
this.applyWidths(allObjects, bodyWidth);
|
|
343
323
|
const bodyRows = this.getRows(bodyObjects);
|
|
@@ -424,11 +404,7 @@ class StackObjectsLayoutFunction {
|
|
|
424
404
|
object.setPosition({
|
|
425
405
|
width: actualWidth
|
|
426
406
|
});
|
|
427
|
-
|
|
428
|
-
if (object.getType() !== exports.ObjectType.VERTICAL_RULE) {
|
|
429
|
-
object.updateDimensions();
|
|
430
|
-
}
|
|
431
|
-
});
|
|
407
|
+
object.updateDimensions();
|
|
432
408
|
});
|
|
433
409
|
}
|
|
434
410
|
adjustCanvasSize(canvas, requiredHeight) {
|
|
@@ -501,7 +477,6 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
501
477
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
502
478
|
this.stackObjects = new StackObjectsLayoutFunction();
|
|
503
479
|
this.updateObjectDimensions = new UpdateObjectDimensionsLayoutFunction();
|
|
504
|
-
this.layoutBatchScheduled = false;
|
|
505
480
|
}
|
|
506
481
|
/**
|
|
507
482
|
* @override
|
|
@@ -543,21 +518,12 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
543
518
|
this.stackAndRedraw();
|
|
544
519
|
}
|
|
545
520
|
stackAndRedraw() {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
this.
|
|
551
|
-
|
|
552
|
-
Promise.resolve().then(() => {
|
|
553
|
-
this.stackObjects.execute(this.canvas);
|
|
554
|
-
this.repositionCutoffs.execute(this.canvas);
|
|
555
|
-
this.repositionBorders.execute(this.canvas);
|
|
556
|
-
this.repositionBackgrounds.execute(this.canvas);
|
|
557
|
-
this.updateObjectDimensions.execute(this.canvas);
|
|
558
|
-
this.redrawAll.execute(this.canvas);
|
|
559
|
-
this.layoutBatchScheduled = false;
|
|
560
|
-
});
|
|
521
|
+
this.stackObjects.execute(this.canvas);
|
|
522
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
523
|
+
this.repositionBorders.execute(this.canvas);
|
|
524
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
525
|
+
this.updateObjectDimensions.execute(this.canvas);
|
|
526
|
+
this.redrawAll.execute(this.canvas);
|
|
561
527
|
}
|
|
562
528
|
}
|
|
563
529
|
|
|
@@ -1835,9 +1801,6 @@ exports.ObjectType = void 0;
|
|
|
1835
1801
|
class Text extends AbstractCanvasObject {
|
|
1836
1802
|
constructor(canvas, fabricObject, persistedField) {
|
|
1837
1803
|
super(exports.ObjectType.TEXT, canvas, fabricObject, persistedField);
|
|
1838
|
-
//private notifyCanvasTimeout: NodeJS.Timeout | null = null;
|
|
1839
|
-
this.notifyCanvasTimeout = null;
|
|
1840
|
-
this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
|
|
1841
1804
|
this.canvas = canvas;
|
|
1842
1805
|
this.fontLibrary = canvas.getFontLibrary();
|
|
1843
1806
|
this.baseComponentHeight = this.fabricObject.height;
|
|
@@ -2585,42 +2548,28 @@ class Text extends AbstractCanvasObject {
|
|
|
2585
2548
|
onTextChange() {
|
|
2586
2549
|
this.data.userText = this.fabricObject.text;
|
|
2587
2550
|
this.text$.next(this.fabricObject.text);
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
// Skip expensive getBoundingBox if object is clearly within bounds
|
|
2592
|
-
if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
|
|
2593
|
-
if (this.getBoundingBox().width > canvasWidth) {
|
|
2594
|
-
this.fabricObject.set({ left: 0, width: canvasWidth });
|
|
2595
|
-
this.redraw();
|
|
2596
|
-
}
|
|
2551
|
+
if (this.getBoundingBox().width > this.canvas.getDimensions().widthPx) {
|
|
2552
|
+
this.fabricObject.set({ left: 0, width: this.canvas.getDimensions().widthPx });
|
|
2553
|
+
this.redraw();
|
|
2597
2554
|
}
|
|
2598
2555
|
}
|
|
2599
2556
|
onDelete() {
|
|
2600
2557
|
this.removeBullets();
|
|
2601
2558
|
}
|
|
2602
2559
|
onSelectionChange() {
|
|
2603
|
-
this.updateTextSelection(
|
|
2560
|
+
this.updateTextSelection();
|
|
2604
2561
|
}
|
|
2605
2562
|
hasTextSelection() {
|
|
2606
2563
|
return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
|
|
2607
2564
|
}
|
|
2608
|
-
updateTextSelection(
|
|
2565
|
+
updateTextSelection() {
|
|
2609
2566
|
if (this.canvas.headless)
|
|
2610
2567
|
return;
|
|
2611
2568
|
this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
2612
|
-
|
|
2613
|
-
this.notifyCanvasOfTextChanges();
|
|
2614
|
-
}
|
|
2569
|
+
this.notifyCanvasOfTextChanges();
|
|
2615
2570
|
}
|
|
2616
2571
|
notifyCanvasOfTextChanges() {
|
|
2617
|
-
|
|
2618
|
-
clearTimeout(this.notifyCanvasTimeout);
|
|
2619
|
-
}
|
|
2620
|
-
this.notifyCanvasTimeout = setTimeout(() => {
|
|
2621
|
-
this.canvas.notifyTextChanged();
|
|
2622
|
-
this.notifyCanvasTimeout = null;
|
|
2623
|
-
}, this.NOTIFY_DELAY_MS);
|
|
2572
|
+
this.canvas.notifyTextChanged();
|
|
2624
2573
|
}
|
|
2625
2574
|
}
|
|
2626
2575
|
Text.NEW_LINE = "\n";
|
|
@@ -2786,7 +2735,6 @@ class AbstractInteractiveCanvas {
|
|
|
2786
2735
|
this.devicePixelRatio = devicePixelRatio;
|
|
2787
2736
|
this.fontLibrary = new FontLibrary();
|
|
2788
2737
|
this.dimensionChangeRejections$ = new rxjs.Subject();
|
|
2789
|
-
this.redrawScheduled = false;
|
|
2790
2738
|
this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
|
|
2791
2739
|
/**
|
|
2792
2740
|
* Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
|
|
@@ -2809,7 +2757,7 @@ class AbstractInteractiveCanvas {
|
|
|
2809
2757
|
this.fabricCanvas.stateful = true;
|
|
2810
2758
|
this.fabricCanvas.preserveObjectStacking = true;
|
|
2811
2759
|
this.fabricCanvas.perPixelTargetFind = true;
|
|
2812
|
-
this.fabricCanvas.targetFindTolerance =
|
|
2760
|
+
this.fabricCanvas.targetFindTolerance = 4;
|
|
2813
2761
|
this.setBackgroundColor("#FFFFFF");
|
|
2814
2762
|
}
|
|
2815
2763
|
/**
|
|
@@ -3203,7 +3151,11 @@ class AbstractInteractiveCanvas {
|
|
|
3203
3151
|
charSpacing: profile.defaultCharacterSpacing,
|
|
3204
3152
|
perPixelTargetFind: false,
|
|
3205
3153
|
targetFindTolerance: 4,
|
|
3206
|
-
splitByGrapheme: true
|
|
3154
|
+
splitByGrapheme: true,
|
|
3155
|
+
editable: false, // Only enable on double-click
|
|
3156
|
+
padding: 0,
|
|
3157
|
+
cornerSize: 8,
|
|
3158
|
+
touchCornerSize: 12
|
|
3207
3159
|
};
|
|
3208
3160
|
const fabricTextbox = new this.fabric.Textbox("Text goes here", textOptions);
|
|
3209
3161
|
this.fabricCanvas.add(fabricTextbox);
|
|
@@ -3652,16 +3604,7 @@ class AbstractInteractiveCanvas {
|
|
|
3652
3604
|
* Redraw the canvas.
|
|
3653
3605
|
*/
|
|
3654
3606
|
redraw() {
|
|
3655
|
-
|
|
3656
|
-
if (this.redrawScheduled) {
|
|
3657
|
-
return; // Already scheduled, don't schedule again
|
|
3658
|
-
}
|
|
3659
|
-
this.redrawScheduled = true;
|
|
3660
|
-
// Use requestAnimationFrame for smoother rendering and automatic batching
|
|
3661
|
-
requestAnimationFrame(() => {
|
|
3662
|
-
this.fabricCanvas.requestRenderAll();
|
|
3663
|
-
this.redrawScheduled = false;
|
|
3664
|
-
});
|
|
3607
|
+
this.fabricCanvas.requestRenderAll();
|
|
3665
3608
|
}
|
|
3666
3609
|
syncObjectList() {
|
|
3667
3610
|
const newObjectList = _(this.fabricCanvas.getObjects())
|
|
@@ -3787,6 +3730,7 @@ class AbstractInteractiveCanvas {
|
|
|
3787
3730
|
* Update the actual dimensions of the canvas by multiplying the requested dimensions by the current zoom factor.
|
|
3788
3731
|
*/
|
|
3789
3732
|
updateCanvasDimensions(dims, zoom) {
|
|
3733
|
+
const oldWidth = this.fabricCanvas.getWidth();
|
|
3790
3734
|
const width = dims.widthPx * zoom;
|
|
3791
3735
|
const height = dims.heightPx * zoom;
|
|
3792
3736
|
this.fabricCanvas.setZoom(zoom);
|
|
@@ -3794,6 +3738,34 @@ class AbstractInteractiveCanvas {
|
|
|
3794
3738
|
width,
|
|
3795
3739
|
height
|
|
3796
3740
|
});
|
|
3741
|
+
const scaleFactor = width / oldWidth;
|
|
3742
|
+
const objects = this.fabricCanvas.getObjects();
|
|
3743
|
+
objects.forEach((obj) => {
|
|
3744
|
+
obj.set({
|
|
3745
|
+
left: obj.left * scaleFactor,
|
|
3746
|
+
});
|
|
3747
|
+
if (obj.type === "textbox" || obj.type === "text") {
|
|
3748
|
+
obj.set({
|
|
3749
|
+
scaleX: 1,
|
|
3750
|
+
scaleY: 1,
|
|
3751
|
+
width: obj.width * scaleFactor,
|
|
3752
|
+
});
|
|
3753
|
+
}
|
|
3754
|
+
else if (obj.type === "border" || obj.type === "rect") {
|
|
3755
|
+
obj.set({
|
|
3756
|
+
width: obj.width * scaleFactor,
|
|
3757
|
+
scaleX: 1,
|
|
3758
|
+
strokeUniform: true,
|
|
3759
|
+
});
|
|
3760
|
+
}
|
|
3761
|
+
else {
|
|
3762
|
+
obj.set({
|
|
3763
|
+
scaleX: obj.scaleX * scaleFactor,
|
|
3764
|
+
});
|
|
3765
|
+
}
|
|
3766
|
+
obj.setCoords();
|
|
3767
|
+
});
|
|
3768
|
+
this.fabricCanvas.requestRenderAll();
|
|
3797
3769
|
}
|
|
3798
3770
|
onProfileChanged(profile) {
|
|
3799
3771
|
this.setMultiSelect(profile.allowMultiSelect);
|
|
@@ -3948,8 +3920,18 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3948
3920
|
this.fabricCanvas.on("selection:created", (event) => this.onFabricSelectionChanged(event));
|
|
3949
3921
|
this.fabricCanvas.on("selection:cleared", (event) => this.onFabricSelectionChanged(event));
|
|
3950
3922
|
this.fabricCanvas.on("text:editing:entered", () => this.onTextEditing());
|
|
3951
|
-
this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
|
|
3923
|
+
//this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
|
|
3924
|
+
this.fabricCanvas.on("text:editing:exited", (event) => {
|
|
3925
|
+
this.onTextEditing();
|
|
3926
|
+
const target = event.target;
|
|
3927
|
+
if (target && (target.type === 'textbox' || target.type === 'i-text')) {
|
|
3928
|
+
target.set({ editable: false, hoverCursor: 'move' });
|
|
3929
|
+
}
|
|
3930
|
+
});
|
|
3952
3931
|
this.fabricCanvas.on("text:changed", () => this.onTextChange());
|
|
3932
|
+
// Handle double-click on textbox to enter editing mode
|
|
3933
|
+
// @ts-ignore
|
|
3934
|
+
this.fabricCanvas.on("mouse:dblclick", (event) => this.onDoubleClick(event));
|
|
3953
3935
|
}
|
|
3954
3936
|
/**
|
|
3955
3937
|
* @override
|
|
@@ -3998,6 +3980,17 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3998
3980
|
const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
|
|
3999
3981
|
this.selection$.next(new SelectionData(selected));
|
|
4000
3982
|
}
|
|
3983
|
+
onDoubleClick(event) {
|
|
3984
|
+
const target = event.target;
|
|
3985
|
+
if (target && (target.type === 'textbox' || target.type === 'i-text')) {
|
|
3986
|
+
if (!target.isEditing) {
|
|
3987
|
+
target.set({ editable: true, hoverCursor: 'text' });
|
|
3988
|
+
// target.enterEditing();
|
|
3989
|
+
// target.hiddenTextarea?.focus();
|
|
3990
|
+
this.enterTextSelectionModeIfRequired();
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
4001
3994
|
}
|
|
4002
3995
|
|
|
4003
3996
|
exports.AbstractCanvasObject = AbstractCanvasObject;
|
|
@@ -9,7 +9,6 @@ export declare class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
9
9
|
private repositionCutoffs;
|
|
10
10
|
private repositionBackgrounds;
|
|
11
11
|
private resizeCanvas;
|
|
12
|
-
private dimensionsLayoutScheduled;
|
|
13
12
|
constructor(canvas: IInteractiveCanvas);
|
|
14
13
|
onCanvasDimensionsChange(): void;
|
|
15
14
|
onTextChange(): void;
|
|
@@ -63,8 +63,6 @@ export declare class Text extends AbstractCanvasObject<ExtendedFabricTextbox> im
|
|
|
63
63
|
private readonly text$;
|
|
64
64
|
private readonly textSelection$;
|
|
65
65
|
static readonly NEW_LINE: string;
|
|
66
|
-
private notifyCanvasTimeout;
|
|
67
|
-
private readonly NOTIFY_DELAY_MS;
|
|
68
66
|
constructor(canvas: IInteractiveCanvas, fabricObject: fabric.Textbox, persistedField?: CanvasObjectData);
|
|
69
67
|
/**
|
|
70
68
|
* @override
|