@leapfrog/interactive-canvas 2.0.25-beta-8 → 2.0.25-beta-10
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 +2 -0
- package/dist/interactive-canvas.d.ts +0 -1
- package/dist/interactive-canvas.es.js +125 -81
- package/dist/interactive-canvas.js +125 -81
- package/dist/layout/advanced-layout-manager.d.ts +1 -0
- package/dist/layout/standard-layout-manager.d.ts +1 -0
- package/dist/object/impl/text.d.ts +2 -0
- package/package.json +1 -1
|
@@ -340,8 +340,10 @@ export declare abstract class AbstractInteractiveCanvas implements IInteractiveC
|
|
|
340
340
|
* @inheritDoc
|
|
341
341
|
*/
|
|
342
342
|
cloneObject(sourceObject: FabricObject, objectData: CanvasObjectData): Promise<void>;
|
|
343
|
+
private redrawScheduled;
|
|
343
344
|
/**
|
|
344
345
|
* Redraw the canvas.
|
|
346
|
+
* Uses requestAnimationFrame to batch multiple redraw calls into a single render.
|
|
345
347
|
*/
|
|
346
348
|
redraw(): void;
|
|
347
349
|
syncObjectList(): void;
|
|
@@ -232,9 +232,15 @@ 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
|
+
canvas.getObjectsInternal().forEach((object) => {
|
|
236
|
+
var _a;
|
|
237
|
+
const canvasObj = canvas.getObjectsByTypeInternal(object.type).find(o => o.getId() === object.getId());
|
|
238
|
+
if (canvasObj && (((_a = canvasObj.isPinToBottom) === null || _a === void 0 ? void 0 : _a.call(canvasObj)) || object.top > currentDimensions.heightPx * 0.5)) {
|
|
239
|
+
object.setCoords();
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
canvas.redraw();
|
|
235
243
|
}
|
|
236
|
-
canvas.getObjectsInternal().forEach((object) => object.setCoords());
|
|
237
|
-
canvas.redraw();
|
|
238
244
|
}
|
|
239
245
|
}
|
|
240
246
|
|
|
@@ -262,13 +268,21 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
262
268
|
this.repositionCutoffs = new RepositionCutoffsLayoutFunction();
|
|
263
269
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
264
270
|
this.resizeCanvas = new ResizeCanvasLayoutFunction();
|
|
271
|
+
this.dimensionsLayoutScheduled = false;
|
|
265
272
|
}
|
|
266
273
|
onCanvasDimensionsChange() {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
this.
|
|
271
|
-
|
|
274
|
+
if (this.dimensionsLayoutScheduled) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
this.dimensionsLayoutScheduled = true;
|
|
278
|
+
Promise.resolve().then(() => {
|
|
279
|
+
this.pinToBottom.execute(this.canvas);
|
|
280
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
281
|
+
this.repositionBorders.execute(this.canvas);
|
|
282
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
283
|
+
this.redrawAll.execute(this.canvas);
|
|
284
|
+
this.dimensionsLayoutScheduled = false;
|
|
285
|
+
});
|
|
272
286
|
}
|
|
273
287
|
onTextChange() {
|
|
274
288
|
this.resizeCanvas.execute(this.canvas);
|
|
@@ -296,7 +310,6 @@ class StackObjectsLayoutFunction {
|
|
|
296
310
|
const footerObjects = objects[0];
|
|
297
311
|
const bodyObjects = objects[1];
|
|
298
312
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
299
|
-
canvas.redraw();
|
|
300
313
|
//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.
|
|
301
314
|
this.applyWidths(allObjects, bodyWidth);
|
|
302
315
|
const bodyRows = this.getRows(bodyObjects);
|
|
@@ -383,7 +396,11 @@ class StackObjectsLayoutFunction {
|
|
|
383
396
|
object.setPosition({
|
|
384
397
|
width: actualWidth
|
|
385
398
|
});
|
|
386
|
-
|
|
399
|
+
});
|
|
400
|
+
_(objects).forEach((object) => {
|
|
401
|
+
if (object.getType() !== ObjectType.VERTICAL_RULE) {
|
|
402
|
+
object.updateDimensions();
|
|
403
|
+
}
|
|
387
404
|
});
|
|
388
405
|
}
|
|
389
406
|
adjustCanvasSize(canvas, requiredHeight) {
|
|
@@ -456,6 +473,7 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
456
473
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
457
474
|
this.stackObjects = new StackObjectsLayoutFunction();
|
|
458
475
|
this.updateObjectDimensions = new UpdateObjectDimensionsLayoutFunction();
|
|
476
|
+
this.layoutBatchScheduled = false;
|
|
459
477
|
}
|
|
460
478
|
/**
|
|
461
479
|
* @override
|
|
@@ -497,12 +515,19 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
497
515
|
this.stackAndRedraw();
|
|
498
516
|
}
|
|
499
517
|
stackAndRedraw() {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
this.
|
|
504
|
-
|
|
505
|
-
|
|
518
|
+
if (this.layoutBatchScheduled) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
this.layoutBatchScheduled = true;
|
|
522
|
+
Promise.resolve().then(() => {
|
|
523
|
+
this.stackObjects.execute(this.canvas);
|
|
524
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
525
|
+
this.repositionBorders.execute(this.canvas);
|
|
526
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
527
|
+
this.updateObjectDimensions.execute(this.canvas);
|
|
528
|
+
this.redrawAll.execute(this.canvas);
|
|
529
|
+
this.layoutBatchScheduled = false;
|
|
530
|
+
});
|
|
506
531
|
}
|
|
507
532
|
}
|
|
508
533
|
|
|
@@ -1042,7 +1067,8 @@ class AbstractCanvasObject {
|
|
|
1042
1067
|
mt: options.allowScale,
|
|
1043
1068
|
mr: options.allowScale,
|
|
1044
1069
|
mb: options.allowScale,
|
|
1045
|
-
mtr: options.allowRotate
|
|
1070
|
+
mtr: options.allowRotate,
|
|
1071
|
+
// dragControl: options.allowMove // Show drag control when movement is allowed
|
|
1046
1072
|
});
|
|
1047
1073
|
}
|
|
1048
1074
|
}
|
|
@@ -1780,15 +1806,42 @@ var ObjectType;
|
|
|
1780
1806
|
class Text extends AbstractCanvasObject {
|
|
1781
1807
|
constructor(canvas, fabricObject, persistedField) {
|
|
1782
1808
|
super(ObjectType.TEXT, canvas, fabricObject, persistedField);
|
|
1809
|
+
this.notifyCanvasTimeout = null;
|
|
1810
|
+
this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
|
|
1783
1811
|
this.canvas = canvas;
|
|
1784
1812
|
this.fontLibrary = canvas.getFontLibrary();
|
|
1785
1813
|
this.baseComponentHeight = this.fabricObject.height;
|
|
1786
1814
|
this.text$ = new BehaviorSubject(this.fabricObject.text);
|
|
1787
1815
|
this.data.userText = this.fabricObject.text;
|
|
1788
1816
|
this.textSelection$ = new BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
1789
|
-
this.fabricObject.set({
|
|
1817
|
+
this.fabricObject.set({
|
|
1818
|
+
strokeWidth: 0,
|
|
1819
|
+
perPixelTargetFind: false // Disable per-pixel detection for this object
|
|
1820
|
+
});
|
|
1790
1821
|
this.fabricObject.on("changed", () => this.onTextChange());
|
|
1791
1822
|
this.fabricObject.on("selection:changed", () => this.onSelectionChange());
|
|
1823
|
+
this.fabricObject.containsPoint.bind(this.fabricObject);
|
|
1824
|
+
this.fabricObject.containsPoint = function (point) {
|
|
1825
|
+
const coords = this.getCoords();
|
|
1826
|
+
if (coords.length !== 4)
|
|
1827
|
+
return false;
|
|
1828
|
+
let inside = false;
|
|
1829
|
+
for (let i = 0, j = coords.length - 1; i < coords.length; j = i++) {
|
|
1830
|
+
const xi = coords[i].x, yi = coords[i].y;
|
|
1831
|
+
const xj = coords[j].x, yj = coords[j].y;
|
|
1832
|
+
const intersect = ((yi > point.y) !== (yj > point.y))
|
|
1833
|
+
&& (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
|
|
1834
|
+
if (intersect)
|
|
1835
|
+
inside = !inside;
|
|
1836
|
+
}
|
|
1837
|
+
return inside;
|
|
1838
|
+
};
|
|
1839
|
+
this.fabricObject.on("mousedown", (e) => {
|
|
1840
|
+
var _a;
|
|
1841
|
+
if (!this.fabricObject.isEditing) {
|
|
1842
|
+
(_a = e.e) === null || _a === void 0 ? void 0 : _a.preventDefault();
|
|
1843
|
+
}
|
|
1844
|
+
});
|
|
1792
1845
|
this.applyBulletListMonkeyPatches();
|
|
1793
1846
|
}
|
|
1794
1847
|
/**
|
|
@@ -2398,6 +2451,7 @@ class Text extends AbstractCanvasObject {
|
|
|
2398
2451
|
lockMovementY: true,
|
|
2399
2452
|
hasControls: false,
|
|
2400
2453
|
selectable: false,
|
|
2454
|
+
// evented: false, // Don't capture mouse events - allows clicks to pass through to textbox
|
|
2401
2455
|
originX: "left",
|
|
2402
2456
|
originY: "top"
|
|
2403
2457
|
});
|
|
@@ -2504,13 +2558,24 @@ class Text extends AbstractCanvasObject {
|
|
|
2504
2558
|
* @inheritDoc
|
|
2505
2559
|
*/
|
|
2506
2560
|
updateFabricObjectBehavior() {
|
|
2561
|
+
// DON'T call super.updateFabricObjectBehavior() for textboxes
|
|
2562
|
+
// because it locks movement which interferes with text editing in Fabric v7
|
|
2507
2563
|
super.updateFabricObjectBehavior();
|
|
2508
2564
|
const profile = this.canvas.getProfile();
|
|
2509
2565
|
const allowManualPositioning = !this.canvas.getLayoutManager().isEnforcedPositioning;
|
|
2566
|
+
// For textboxes, we keep them moveable but hide all controls except drag control
|
|
2567
|
+
// This allows text editing to work properly while still supporting drag via the icon
|
|
2510
2568
|
this.fabricObject.set({
|
|
2569
|
+
lockMovementX: false, // Allow dragging
|
|
2570
|
+
lockMovementY: false, // Allow dragging
|
|
2511
2571
|
lockScalingY: true,
|
|
2512
2572
|
lockScalingX: this.isPinToBottom(),
|
|
2513
|
-
lockScalingFlip: true
|
|
2573
|
+
lockScalingFlip: true,
|
|
2574
|
+
lockRotation: true,
|
|
2575
|
+
selectable: true,
|
|
2576
|
+
editable: false, // Only enable on double-click
|
|
2577
|
+
hoverCursor: "move", // Show move cursor by default
|
|
2578
|
+
moveCursor: "move",
|
|
2514
2579
|
});
|
|
2515
2580
|
this.fabricObject.setControlsVisibility({
|
|
2516
2581
|
tl: false,
|
|
@@ -2520,35 +2585,50 @@ class Text extends AbstractCanvasObject {
|
|
|
2520
2585
|
ml: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
|
|
2521
2586
|
mt: false,
|
|
2522
2587
|
mr: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
|
|
2523
|
-
mb: false
|
|
2588
|
+
mb: false,
|
|
2589
|
+
mtr: false,
|
|
2524
2590
|
});
|
|
2525
2591
|
this.redraw();
|
|
2526
2592
|
}
|
|
2527
2593
|
onTextChange() {
|
|
2528
2594
|
this.data.userText = this.fabricObject.text;
|
|
2529
2595
|
this.text$.next(this.fabricObject.text);
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2596
|
+
// Only check width bounds if text is close to canvas edge (performance optimization)
|
|
2597
|
+
const currentLeft = this.fabricObject.left || 0;
|
|
2598
|
+
const canvasWidth = this.canvas.getDimensions().widthPx;
|
|
2599
|
+
// Skip expensive getBoundingBox if object is clearly within bounds
|
|
2600
|
+
if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
|
|
2601
|
+
if (this.getBoundingBox().width > canvasWidth) {
|
|
2602
|
+
this.fabricObject.set({ left: 0, width: canvasWidth });
|
|
2603
|
+
this.redraw();
|
|
2604
|
+
}
|
|
2533
2605
|
}
|
|
2534
2606
|
}
|
|
2535
2607
|
onDelete() {
|
|
2536
2608
|
this.removeBullets();
|
|
2537
2609
|
}
|
|
2538
2610
|
onSelectionChange() {
|
|
2539
|
-
this.updateTextSelection();
|
|
2611
|
+
this.updateTextSelection(false);
|
|
2540
2612
|
}
|
|
2541
2613
|
hasTextSelection() {
|
|
2542
2614
|
return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
|
|
2543
2615
|
}
|
|
2544
|
-
updateTextSelection() {
|
|
2616
|
+
updateTextSelection(notifyCanvas = true) {
|
|
2545
2617
|
if (this.canvas.headless)
|
|
2546
2618
|
return;
|
|
2547
2619
|
this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
2548
|
-
|
|
2620
|
+
if (notifyCanvas) {
|
|
2621
|
+
this.notifyCanvasOfTextChanges();
|
|
2622
|
+
}
|
|
2549
2623
|
}
|
|
2550
2624
|
notifyCanvasOfTextChanges() {
|
|
2551
|
-
this.
|
|
2625
|
+
if (this.notifyCanvasTimeout) {
|
|
2626
|
+
clearTimeout(this.notifyCanvasTimeout);
|
|
2627
|
+
}
|
|
2628
|
+
this.notifyCanvasTimeout = setTimeout(() => {
|
|
2629
|
+
this.canvas.notifyTextChanged();
|
|
2630
|
+
this.notifyCanvasTimeout = null;
|
|
2631
|
+
}, this.NOTIFY_DELAY_MS);
|
|
2552
2632
|
}
|
|
2553
2633
|
}
|
|
2554
2634
|
Text.NEW_LINE = "\n";
|
|
@@ -2714,6 +2794,7 @@ class AbstractInteractiveCanvas {
|
|
|
2714
2794
|
this.devicePixelRatio = devicePixelRatio;
|
|
2715
2795
|
this.fontLibrary = new FontLibrary();
|
|
2716
2796
|
this.dimensionChangeRejections$ = new Subject();
|
|
2797
|
+
this.redrawScheduled = false;
|
|
2717
2798
|
this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
|
|
2718
2799
|
/**
|
|
2719
2800
|
* Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
|
|
@@ -2736,7 +2817,7 @@ class AbstractInteractiveCanvas {
|
|
|
2736
2817
|
this.fabricCanvas.stateful = true;
|
|
2737
2818
|
this.fabricCanvas.preserveObjectStacking = true;
|
|
2738
2819
|
this.fabricCanvas.perPixelTargetFind = true;
|
|
2739
|
-
this.fabricCanvas.targetFindTolerance = 4;
|
|
2820
|
+
this.fabricCanvas.targetFindTolerance = 4; // Reduced tolerance for more precise text selection
|
|
2740
2821
|
this.setBackgroundColor("#FFFFFF");
|
|
2741
2822
|
}
|
|
2742
2823
|
/**
|
|
@@ -3040,7 +3121,6 @@ class AbstractInteractiveCanvas {
|
|
|
3040
3121
|
*/
|
|
3041
3122
|
addImageObject(imageUrl) {
|
|
3042
3123
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3043
|
-
// Don't let the image be more than half the canvas in size.
|
|
3044
3124
|
const maxSizeScale = 2;
|
|
3045
3125
|
const maxWidth = this.dimensions$.value.widthPx / maxSizeScale;
|
|
3046
3126
|
const options = { crossOrigin: "anonymous" };
|
|
@@ -3052,7 +3132,6 @@ class AbstractInteractiveCanvas {
|
|
|
3052
3132
|
if (curWidth > maxWidth) {
|
|
3053
3133
|
newScale = maxWidth / curWidth;
|
|
3054
3134
|
}
|
|
3055
|
-
// Set image properties and add to the canvas
|
|
3056
3135
|
const fabricImage = new this.fabric.Image(image, {
|
|
3057
3136
|
id: id,
|
|
3058
3137
|
scaleX: newScale,
|
|
@@ -3072,7 +3151,6 @@ class AbstractInteractiveCanvas {
|
|
|
3072
3151
|
return object;
|
|
3073
3152
|
}
|
|
3074
3153
|
else {
|
|
3075
|
-
// Browser-side logic
|
|
3076
3154
|
return yield new Promise((resolve) => {
|
|
3077
3155
|
this.fabric.Image.fromURL(imageUrl, options).then((fabricImage) => {
|
|
3078
3156
|
const id = this.randomId();
|
|
@@ -3124,14 +3202,13 @@ class AbstractInteractiveCanvas {
|
|
|
3124
3202
|
fontFamily: profile.defaultFont,
|
|
3125
3203
|
textAlign: profile.defaultTextAlignment,
|
|
3126
3204
|
hoverCursor: "move",
|
|
3127
|
-
moveCursor:
|
|
3205
|
+
moveCursor: "move",
|
|
3206
|
+
editable: false,
|
|
3207
|
+
editingBorderColor: '#2196F3',
|
|
3128
3208
|
multiLine: true,
|
|
3129
3209
|
lineHeight: profile.defaultLineHeight,
|
|
3130
3210
|
charSpacing: profile.defaultCharacterSpacing,
|
|
3131
|
-
perPixelTargetFind: false,
|
|
3132
|
-
targetFindTolerance: 4,
|
|
3133
3211
|
splitByGrapheme: true,
|
|
3134
|
-
editable: false, // Only enable on double-click
|
|
3135
3212
|
padding: 0,
|
|
3136
3213
|
cornerSize: 8,
|
|
3137
3214
|
touchCornerSize: 12
|
|
@@ -3581,9 +3658,17 @@ class AbstractInteractiveCanvas {
|
|
|
3581
3658
|
}
|
|
3582
3659
|
/**
|
|
3583
3660
|
* Redraw the canvas.
|
|
3661
|
+
* Uses requestAnimationFrame to batch multiple redraw calls into a single render.
|
|
3584
3662
|
*/
|
|
3585
3663
|
redraw() {
|
|
3586
|
-
this.
|
|
3664
|
+
if (this.redrawScheduled) {
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
this.redrawScheduled = true;
|
|
3668
|
+
requestAnimationFrame(() => {
|
|
3669
|
+
this.fabricCanvas.requestRenderAll();
|
|
3670
|
+
this.redrawScheduled = false;
|
|
3671
|
+
});
|
|
3587
3672
|
}
|
|
3588
3673
|
syncObjectList() {
|
|
3589
3674
|
const newObjectList = _(this.fabricCanvas.getObjects())
|
|
@@ -3709,7 +3794,6 @@ class AbstractInteractiveCanvas {
|
|
|
3709
3794
|
* Update the actual dimensions of the canvas by multiplying the requested dimensions by the current zoom factor.
|
|
3710
3795
|
*/
|
|
3711
3796
|
updateCanvasDimensions(dims, zoom) {
|
|
3712
|
-
const oldWidth = this.fabricCanvas.getWidth();
|
|
3713
3797
|
const width = dims.widthPx * zoom;
|
|
3714
3798
|
const height = dims.heightPx * zoom;
|
|
3715
3799
|
this.fabricCanvas.setZoom(zoom);
|
|
@@ -3717,34 +3801,6 @@ class AbstractInteractiveCanvas {
|
|
|
3717
3801
|
width,
|
|
3718
3802
|
height
|
|
3719
3803
|
});
|
|
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();
|
|
3748
3804
|
}
|
|
3749
3805
|
onProfileChanged(profile) {
|
|
3750
3806
|
this.setMultiSelect(profile.allowMultiSelect);
|
|
@@ -3899,7 +3955,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3899
3955
|
this.fabricCanvas.on("selection:created", (event) => this.onFabricSelectionChanged(event));
|
|
3900
3956
|
this.fabricCanvas.on("selection:cleared", (event) => this.onFabricSelectionChanged(event));
|
|
3901
3957
|
this.fabricCanvas.on("text:editing:entered", () => this.onTextEditing());
|
|
3902
|
-
//this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
|
|
3903
3958
|
this.fabricCanvas.on("text:editing:exited", (event) => {
|
|
3904
3959
|
this.onTextEditing();
|
|
3905
3960
|
const target = event.target;
|
|
@@ -3908,8 +3963,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3908
3963
|
}
|
|
3909
3964
|
});
|
|
3910
3965
|
this.fabricCanvas.on("text:changed", () => this.onTextChange());
|
|
3911
|
-
// Handle double-click on textbox to enter editing mode
|
|
3912
|
-
// @ts-ignore
|
|
3913
3966
|
this.fabricCanvas.on("mouse:dblclick", (event) => this.onDoubleClick(event));
|
|
3914
3967
|
}
|
|
3915
3968
|
/**
|
|
@@ -3944,29 +3997,20 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3944
3997
|
selection.setEvent(event);
|
|
3945
3998
|
}
|
|
3946
3999
|
this.selection$.next(selection);
|
|
3947
|
-
this.enterTextSelectionModeIfRequired();
|
|
3948
|
-
}
|
|
3949
|
-
enterTextSelectionModeIfRequired() {
|
|
3950
|
-
var _a, _b;
|
|
3951
|
-
if (!this.profile$.value.enterTextEditImmediatelyOnSelect && !this.layoutManager$.value.enterTextEditImmediatelyOnSelect)
|
|
3952
|
-
return;
|
|
3953
|
-
if (((_b = (_a = this.selection$.value) === null || _a === void 0 ? void 0 : _a.objects[0]) === null || _b === void 0 ? void 0 : _b.getType()) !== ObjectType.TEXT)
|
|
3954
|
-
return;
|
|
3955
|
-
const text = this.selection$.value.objects[0];
|
|
3956
|
-
text.enterEditing();
|
|
3957
4000
|
}
|
|
3958
4001
|
onTextEditing() {
|
|
3959
4002
|
const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
|
|
3960
4003
|
this.selection$.next(new SelectionData(selected));
|
|
3961
4004
|
}
|
|
3962
4005
|
onDoubleClick(event) {
|
|
4006
|
+
var _a, _b;
|
|
3963
4007
|
const target = event.target;
|
|
3964
4008
|
if (target && (target.type === 'textbox' || target.type === 'i-text')) {
|
|
3965
|
-
|
|
4009
|
+
const targetIText = target;
|
|
4010
|
+
if (!targetIText.isEditing) {
|
|
3966
4011
|
target.set({ editable: true, hoverCursor: 'text' });
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
this.enterTextSelectionModeIfRequired();
|
|
4012
|
+
(_a = targetIText.enterEditing) === null || _a === void 0 ? void 0 : _a.call(targetIText);
|
|
4013
|
+
(_b = targetIText.hiddenTextarea) === null || _b === void 0 ? void 0 : _b.focus();
|
|
3970
4014
|
}
|
|
3971
4015
|
}
|
|
3972
4016
|
}
|
|
@@ -253,9 +253,15 @@ 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
|
+
canvas.getObjectsInternal().forEach((object) => {
|
|
257
|
+
var _a;
|
|
258
|
+
const canvasObj = canvas.getObjectsByTypeInternal(object.type).find(o => o.getId() === object.getId());
|
|
259
|
+
if (canvasObj && (((_a = canvasObj.isPinToBottom) === null || _a === void 0 ? void 0 : _a.call(canvasObj)) || object.top > currentDimensions.heightPx * 0.5)) {
|
|
260
|
+
object.setCoords();
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
canvas.redraw();
|
|
256
264
|
}
|
|
257
|
-
canvas.getObjectsInternal().forEach((object) => object.setCoords());
|
|
258
|
-
canvas.redraw();
|
|
259
265
|
}
|
|
260
266
|
}
|
|
261
267
|
|
|
@@ -283,13 +289,21 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
283
289
|
this.repositionCutoffs = new RepositionCutoffsLayoutFunction();
|
|
284
290
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
285
291
|
this.resizeCanvas = new ResizeCanvasLayoutFunction();
|
|
292
|
+
this.dimensionsLayoutScheduled = false;
|
|
286
293
|
}
|
|
287
294
|
onCanvasDimensionsChange() {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
this.
|
|
292
|
-
|
|
295
|
+
if (this.dimensionsLayoutScheduled) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
this.dimensionsLayoutScheduled = true;
|
|
299
|
+
Promise.resolve().then(() => {
|
|
300
|
+
this.pinToBottom.execute(this.canvas);
|
|
301
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
302
|
+
this.repositionBorders.execute(this.canvas);
|
|
303
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
304
|
+
this.redrawAll.execute(this.canvas);
|
|
305
|
+
this.dimensionsLayoutScheduled = false;
|
|
306
|
+
});
|
|
293
307
|
}
|
|
294
308
|
onTextChange() {
|
|
295
309
|
this.resizeCanvas.execute(this.canvas);
|
|
@@ -317,7 +331,6 @@ class StackObjectsLayoutFunction {
|
|
|
317
331
|
const footerObjects = objects[0];
|
|
318
332
|
const bodyObjects = objects[1];
|
|
319
333
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
320
|
-
canvas.redraw();
|
|
321
334
|
//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.
|
|
322
335
|
this.applyWidths(allObjects, bodyWidth);
|
|
323
336
|
const bodyRows = this.getRows(bodyObjects);
|
|
@@ -404,7 +417,11 @@ class StackObjectsLayoutFunction {
|
|
|
404
417
|
object.setPosition({
|
|
405
418
|
width: actualWidth
|
|
406
419
|
});
|
|
407
|
-
|
|
420
|
+
});
|
|
421
|
+
_(objects).forEach((object) => {
|
|
422
|
+
if (object.getType() !== exports.ObjectType.VERTICAL_RULE) {
|
|
423
|
+
object.updateDimensions();
|
|
424
|
+
}
|
|
408
425
|
});
|
|
409
426
|
}
|
|
410
427
|
adjustCanvasSize(canvas, requiredHeight) {
|
|
@@ -477,6 +494,7 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
477
494
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
478
495
|
this.stackObjects = new StackObjectsLayoutFunction();
|
|
479
496
|
this.updateObjectDimensions = new UpdateObjectDimensionsLayoutFunction();
|
|
497
|
+
this.layoutBatchScheduled = false;
|
|
480
498
|
}
|
|
481
499
|
/**
|
|
482
500
|
* @override
|
|
@@ -518,12 +536,19 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
518
536
|
this.stackAndRedraw();
|
|
519
537
|
}
|
|
520
538
|
stackAndRedraw() {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
this.
|
|
525
|
-
|
|
526
|
-
|
|
539
|
+
if (this.layoutBatchScheduled) {
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
this.layoutBatchScheduled = true;
|
|
543
|
+
Promise.resolve().then(() => {
|
|
544
|
+
this.stackObjects.execute(this.canvas);
|
|
545
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
546
|
+
this.repositionBorders.execute(this.canvas);
|
|
547
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
548
|
+
this.updateObjectDimensions.execute(this.canvas);
|
|
549
|
+
this.redrawAll.execute(this.canvas);
|
|
550
|
+
this.layoutBatchScheduled = false;
|
|
551
|
+
});
|
|
527
552
|
}
|
|
528
553
|
}
|
|
529
554
|
|
|
@@ -1063,7 +1088,8 @@ class AbstractCanvasObject {
|
|
|
1063
1088
|
mt: options.allowScale,
|
|
1064
1089
|
mr: options.allowScale,
|
|
1065
1090
|
mb: options.allowScale,
|
|
1066
|
-
mtr: options.allowRotate
|
|
1091
|
+
mtr: options.allowRotate,
|
|
1092
|
+
// dragControl: options.allowMove // Show drag control when movement is allowed
|
|
1067
1093
|
});
|
|
1068
1094
|
}
|
|
1069
1095
|
}
|
|
@@ -1801,15 +1827,42 @@ exports.ObjectType = void 0;
|
|
|
1801
1827
|
class Text extends AbstractCanvasObject {
|
|
1802
1828
|
constructor(canvas, fabricObject, persistedField) {
|
|
1803
1829
|
super(exports.ObjectType.TEXT, canvas, fabricObject, persistedField);
|
|
1830
|
+
this.notifyCanvasTimeout = null;
|
|
1831
|
+
this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
|
|
1804
1832
|
this.canvas = canvas;
|
|
1805
1833
|
this.fontLibrary = canvas.getFontLibrary();
|
|
1806
1834
|
this.baseComponentHeight = this.fabricObject.height;
|
|
1807
1835
|
this.text$ = new rxjs.BehaviorSubject(this.fabricObject.text);
|
|
1808
1836
|
this.data.userText = this.fabricObject.text;
|
|
1809
1837
|
this.textSelection$ = new rxjs.BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
1810
|
-
this.fabricObject.set({
|
|
1838
|
+
this.fabricObject.set({
|
|
1839
|
+
strokeWidth: 0,
|
|
1840
|
+
perPixelTargetFind: false // Disable per-pixel detection for this object
|
|
1841
|
+
});
|
|
1811
1842
|
this.fabricObject.on("changed", () => this.onTextChange());
|
|
1812
1843
|
this.fabricObject.on("selection:changed", () => this.onSelectionChange());
|
|
1844
|
+
this.fabricObject.containsPoint.bind(this.fabricObject);
|
|
1845
|
+
this.fabricObject.containsPoint = function (point) {
|
|
1846
|
+
const coords = this.getCoords();
|
|
1847
|
+
if (coords.length !== 4)
|
|
1848
|
+
return false;
|
|
1849
|
+
let inside = false;
|
|
1850
|
+
for (let i = 0, j = coords.length - 1; i < coords.length; j = i++) {
|
|
1851
|
+
const xi = coords[i].x, yi = coords[i].y;
|
|
1852
|
+
const xj = coords[j].x, yj = coords[j].y;
|
|
1853
|
+
const intersect = ((yi > point.y) !== (yj > point.y))
|
|
1854
|
+
&& (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
|
|
1855
|
+
if (intersect)
|
|
1856
|
+
inside = !inside;
|
|
1857
|
+
}
|
|
1858
|
+
return inside;
|
|
1859
|
+
};
|
|
1860
|
+
this.fabricObject.on("mousedown", (e) => {
|
|
1861
|
+
var _a;
|
|
1862
|
+
if (!this.fabricObject.isEditing) {
|
|
1863
|
+
(_a = e.e) === null || _a === void 0 ? void 0 : _a.preventDefault();
|
|
1864
|
+
}
|
|
1865
|
+
});
|
|
1813
1866
|
this.applyBulletListMonkeyPatches();
|
|
1814
1867
|
}
|
|
1815
1868
|
/**
|
|
@@ -2419,6 +2472,7 @@ class Text extends AbstractCanvasObject {
|
|
|
2419
2472
|
lockMovementY: true,
|
|
2420
2473
|
hasControls: false,
|
|
2421
2474
|
selectable: false,
|
|
2475
|
+
// evented: false, // Don't capture mouse events - allows clicks to pass through to textbox
|
|
2422
2476
|
originX: "left",
|
|
2423
2477
|
originY: "top"
|
|
2424
2478
|
});
|
|
@@ -2525,13 +2579,24 @@ class Text extends AbstractCanvasObject {
|
|
|
2525
2579
|
* @inheritDoc
|
|
2526
2580
|
*/
|
|
2527
2581
|
updateFabricObjectBehavior() {
|
|
2582
|
+
// DON'T call super.updateFabricObjectBehavior() for textboxes
|
|
2583
|
+
// because it locks movement which interferes with text editing in Fabric v7
|
|
2528
2584
|
super.updateFabricObjectBehavior();
|
|
2529
2585
|
const profile = this.canvas.getProfile();
|
|
2530
2586
|
const allowManualPositioning = !this.canvas.getLayoutManager().isEnforcedPositioning;
|
|
2587
|
+
// For textboxes, we keep them moveable but hide all controls except drag control
|
|
2588
|
+
// This allows text editing to work properly while still supporting drag via the icon
|
|
2531
2589
|
this.fabricObject.set({
|
|
2590
|
+
lockMovementX: false, // Allow dragging
|
|
2591
|
+
lockMovementY: false, // Allow dragging
|
|
2532
2592
|
lockScalingY: true,
|
|
2533
2593
|
lockScalingX: this.isPinToBottom(),
|
|
2534
|
-
lockScalingFlip: true
|
|
2594
|
+
lockScalingFlip: true,
|
|
2595
|
+
lockRotation: true,
|
|
2596
|
+
selectable: true,
|
|
2597
|
+
editable: false, // Only enable on double-click
|
|
2598
|
+
hoverCursor: "move", // Show move cursor by default
|
|
2599
|
+
moveCursor: "move",
|
|
2535
2600
|
});
|
|
2536
2601
|
this.fabricObject.setControlsVisibility({
|
|
2537
2602
|
tl: false,
|
|
@@ -2541,35 +2606,50 @@ class Text extends AbstractCanvasObject {
|
|
|
2541
2606
|
ml: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
|
|
2542
2607
|
mt: false,
|
|
2543
2608
|
mr: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
|
|
2544
|
-
mb: false
|
|
2609
|
+
mb: false,
|
|
2610
|
+
mtr: false,
|
|
2545
2611
|
});
|
|
2546
2612
|
this.redraw();
|
|
2547
2613
|
}
|
|
2548
2614
|
onTextChange() {
|
|
2549
2615
|
this.data.userText = this.fabricObject.text;
|
|
2550
2616
|
this.text$.next(this.fabricObject.text);
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2617
|
+
// Only check width bounds if text is close to canvas edge (performance optimization)
|
|
2618
|
+
const currentLeft = this.fabricObject.left || 0;
|
|
2619
|
+
const canvasWidth = this.canvas.getDimensions().widthPx;
|
|
2620
|
+
// Skip expensive getBoundingBox if object is clearly within bounds
|
|
2621
|
+
if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
|
|
2622
|
+
if (this.getBoundingBox().width > canvasWidth) {
|
|
2623
|
+
this.fabricObject.set({ left: 0, width: canvasWidth });
|
|
2624
|
+
this.redraw();
|
|
2625
|
+
}
|
|
2554
2626
|
}
|
|
2555
2627
|
}
|
|
2556
2628
|
onDelete() {
|
|
2557
2629
|
this.removeBullets();
|
|
2558
2630
|
}
|
|
2559
2631
|
onSelectionChange() {
|
|
2560
|
-
this.updateTextSelection();
|
|
2632
|
+
this.updateTextSelection(false);
|
|
2561
2633
|
}
|
|
2562
2634
|
hasTextSelection() {
|
|
2563
2635
|
return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
|
|
2564
2636
|
}
|
|
2565
|
-
updateTextSelection() {
|
|
2637
|
+
updateTextSelection(notifyCanvas = true) {
|
|
2566
2638
|
if (this.canvas.headless)
|
|
2567
2639
|
return;
|
|
2568
2640
|
this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
2569
|
-
|
|
2641
|
+
if (notifyCanvas) {
|
|
2642
|
+
this.notifyCanvasOfTextChanges();
|
|
2643
|
+
}
|
|
2570
2644
|
}
|
|
2571
2645
|
notifyCanvasOfTextChanges() {
|
|
2572
|
-
this.
|
|
2646
|
+
if (this.notifyCanvasTimeout) {
|
|
2647
|
+
clearTimeout(this.notifyCanvasTimeout);
|
|
2648
|
+
}
|
|
2649
|
+
this.notifyCanvasTimeout = setTimeout(() => {
|
|
2650
|
+
this.canvas.notifyTextChanged();
|
|
2651
|
+
this.notifyCanvasTimeout = null;
|
|
2652
|
+
}, this.NOTIFY_DELAY_MS);
|
|
2573
2653
|
}
|
|
2574
2654
|
}
|
|
2575
2655
|
Text.NEW_LINE = "\n";
|
|
@@ -2735,6 +2815,7 @@ class AbstractInteractiveCanvas {
|
|
|
2735
2815
|
this.devicePixelRatio = devicePixelRatio;
|
|
2736
2816
|
this.fontLibrary = new FontLibrary();
|
|
2737
2817
|
this.dimensionChangeRejections$ = new rxjs.Subject();
|
|
2818
|
+
this.redrawScheduled = false;
|
|
2738
2819
|
this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
|
|
2739
2820
|
/**
|
|
2740
2821
|
* Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
|
|
@@ -2757,7 +2838,7 @@ class AbstractInteractiveCanvas {
|
|
|
2757
2838
|
this.fabricCanvas.stateful = true;
|
|
2758
2839
|
this.fabricCanvas.preserveObjectStacking = true;
|
|
2759
2840
|
this.fabricCanvas.perPixelTargetFind = true;
|
|
2760
|
-
this.fabricCanvas.targetFindTolerance = 4;
|
|
2841
|
+
this.fabricCanvas.targetFindTolerance = 4; // Reduced tolerance for more precise text selection
|
|
2761
2842
|
this.setBackgroundColor("#FFFFFF");
|
|
2762
2843
|
}
|
|
2763
2844
|
/**
|
|
@@ -3061,7 +3142,6 @@ class AbstractInteractiveCanvas {
|
|
|
3061
3142
|
*/
|
|
3062
3143
|
addImageObject(imageUrl) {
|
|
3063
3144
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3064
|
-
// Don't let the image be more than half the canvas in size.
|
|
3065
3145
|
const maxSizeScale = 2;
|
|
3066
3146
|
const maxWidth = this.dimensions$.value.widthPx / maxSizeScale;
|
|
3067
3147
|
const options = { crossOrigin: "anonymous" };
|
|
@@ -3073,7 +3153,6 @@ class AbstractInteractiveCanvas {
|
|
|
3073
3153
|
if (curWidth > maxWidth) {
|
|
3074
3154
|
newScale = maxWidth / curWidth;
|
|
3075
3155
|
}
|
|
3076
|
-
// Set image properties and add to the canvas
|
|
3077
3156
|
const fabricImage = new this.fabric.Image(image, {
|
|
3078
3157
|
id: id,
|
|
3079
3158
|
scaleX: newScale,
|
|
@@ -3093,7 +3172,6 @@ class AbstractInteractiveCanvas {
|
|
|
3093
3172
|
return object;
|
|
3094
3173
|
}
|
|
3095
3174
|
else {
|
|
3096
|
-
// Browser-side logic
|
|
3097
3175
|
return yield new Promise((resolve) => {
|
|
3098
3176
|
this.fabric.Image.fromURL(imageUrl, options).then((fabricImage) => {
|
|
3099
3177
|
const id = this.randomId();
|
|
@@ -3145,14 +3223,13 @@ class AbstractInteractiveCanvas {
|
|
|
3145
3223
|
fontFamily: profile.defaultFont,
|
|
3146
3224
|
textAlign: profile.defaultTextAlignment,
|
|
3147
3225
|
hoverCursor: "move",
|
|
3148
|
-
moveCursor:
|
|
3226
|
+
moveCursor: "move",
|
|
3227
|
+
editable: false,
|
|
3228
|
+
editingBorderColor: '#2196F3',
|
|
3149
3229
|
multiLine: true,
|
|
3150
3230
|
lineHeight: profile.defaultLineHeight,
|
|
3151
3231
|
charSpacing: profile.defaultCharacterSpacing,
|
|
3152
|
-
perPixelTargetFind: false,
|
|
3153
|
-
targetFindTolerance: 4,
|
|
3154
3232
|
splitByGrapheme: true,
|
|
3155
|
-
editable: false, // Only enable on double-click
|
|
3156
3233
|
padding: 0,
|
|
3157
3234
|
cornerSize: 8,
|
|
3158
3235
|
touchCornerSize: 12
|
|
@@ -3602,9 +3679,17 @@ class AbstractInteractiveCanvas {
|
|
|
3602
3679
|
}
|
|
3603
3680
|
/**
|
|
3604
3681
|
* Redraw the canvas.
|
|
3682
|
+
* Uses requestAnimationFrame to batch multiple redraw calls into a single render.
|
|
3605
3683
|
*/
|
|
3606
3684
|
redraw() {
|
|
3607
|
-
this.
|
|
3685
|
+
if (this.redrawScheduled) {
|
|
3686
|
+
return;
|
|
3687
|
+
}
|
|
3688
|
+
this.redrawScheduled = true;
|
|
3689
|
+
requestAnimationFrame(() => {
|
|
3690
|
+
this.fabricCanvas.requestRenderAll();
|
|
3691
|
+
this.redrawScheduled = false;
|
|
3692
|
+
});
|
|
3608
3693
|
}
|
|
3609
3694
|
syncObjectList() {
|
|
3610
3695
|
const newObjectList = _(this.fabricCanvas.getObjects())
|
|
@@ -3730,7 +3815,6 @@ class AbstractInteractiveCanvas {
|
|
|
3730
3815
|
* Update the actual dimensions of the canvas by multiplying the requested dimensions by the current zoom factor.
|
|
3731
3816
|
*/
|
|
3732
3817
|
updateCanvasDimensions(dims, zoom) {
|
|
3733
|
-
const oldWidth = this.fabricCanvas.getWidth();
|
|
3734
3818
|
const width = dims.widthPx * zoom;
|
|
3735
3819
|
const height = dims.heightPx * zoom;
|
|
3736
3820
|
this.fabricCanvas.setZoom(zoom);
|
|
@@ -3738,34 +3822,6 @@ class AbstractInteractiveCanvas {
|
|
|
3738
3822
|
width,
|
|
3739
3823
|
height
|
|
3740
3824
|
});
|
|
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();
|
|
3769
3825
|
}
|
|
3770
3826
|
onProfileChanged(profile) {
|
|
3771
3827
|
this.setMultiSelect(profile.allowMultiSelect);
|
|
@@ -3920,7 +3976,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3920
3976
|
this.fabricCanvas.on("selection:created", (event) => this.onFabricSelectionChanged(event));
|
|
3921
3977
|
this.fabricCanvas.on("selection:cleared", (event) => this.onFabricSelectionChanged(event));
|
|
3922
3978
|
this.fabricCanvas.on("text:editing:entered", () => this.onTextEditing());
|
|
3923
|
-
//this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
|
|
3924
3979
|
this.fabricCanvas.on("text:editing:exited", (event) => {
|
|
3925
3980
|
this.onTextEditing();
|
|
3926
3981
|
const target = event.target;
|
|
@@ -3929,8 +3984,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3929
3984
|
}
|
|
3930
3985
|
});
|
|
3931
3986
|
this.fabricCanvas.on("text:changed", () => this.onTextChange());
|
|
3932
|
-
// Handle double-click on textbox to enter editing mode
|
|
3933
|
-
// @ts-ignore
|
|
3934
3987
|
this.fabricCanvas.on("mouse:dblclick", (event) => this.onDoubleClick(event));
|
|
3935
3988
|
}
|
|
3936
3989
|
/**
|
|
@@ -3965,29 +4018,20 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3965
4018
|
selection.setEvent(event);
|
|
3966
4019
|
}
|
|
3967
4020
|
this.selection$.next(selection);
|
|
3968
|
-
this.enterTextSelectionModeIfRequired();
|
|
3969
|
-
}
|
|
3970
|
-
enterTextSelectionModeIfRequired() {
|
|
3971
|
-
var _a, _b;
|
|
3972
|
-
if (!this.profile$.value.enterTextEditImmediatelyOnSelect && !this.layoutManager$.value.enterTextEditImmediatelyOnSelect)
|
|
3973
|
-
return;
|
|
3974
|
-
if (((_b = (_a = this.selection$.value) === null || _a === void 0 ? void 0 : _a.objects[0]) === null || _b === void 0 ? void 0 : _b.getType()) !== exports.ObjectType.TEXT)
|
|
3975
|
-
return;
|
|
3976
|
-
const text = this.selection$.value.objects[0];
|
|
3977
|
-
text.enterEditing();
|
|
3978
4021
|
}
|
|
3979
4022
|
onTextEditing() {
|
|
3980
4023
|
const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
|
|
3981
4024
|
this.selection$.next(new SelectionData(selected));
|
|
3982
4025
|
}
|
|
3983
4026
|
onDoubleClick(event) {
|
|
4027
|
+
var _a, _b;
|
|
3984
4028
|
const target = event.target;
|
|
3985
4029
|
if (target && (target.type === 'textbox' || target.type === 'i-text')) {
|
|
3986
|
-
|
|
4030
|
+
const targetIText = target;
|
|
4031
|
+
if (!targetIText.isEditing) {
|
|
3987
4032
|
target.set({ editable: true, hoverCursor: 'text' });
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
this.enterTextSelectionModeIfRequired();
|
|
4033
|
+
(_a = targetIText.enterEditing) === null || _a === void 0 ? void 0 : _a.call(targetIText);
|
|
4034
|
+
(_b = targetIText.hiddenTextarea) === null || _b === void 0 ? void 0 : _b.focus();
|
|
3991
4035
|
}
|
|
3992
4036
|
}
|
|
3993
4037
|
}
|
|
@@ -9,6 +9,7 @@ export declare class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
9
9
|
private repositionCutoffs;
|
|
10
10
|
private repositionBackgrounds;
|
|
11
11
|
private resizeCanvas;
|
|
12
|
+
private dimensionsLayoutScheduled;
|
|
12
13
|
constructor(canvas: IInteractiveCanvas);
|
|
13
14
|
onCanvasDimensionsChange(): void;
|
|
14
15
|
onTextChange(): void;
|
|
@@ -63,6 +63,8 @@ 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;
|
|
66
68
|
constructor(canvas: IInteractiveCanvas, fabricObject: fabric.Textbox, persistedField?: CanvasObjectData);
|
|
67
69
|
/**
|
|
68
70
|
* @override
|