@leapfrog/interactive-canvas 2.0.25-beta-9 → 2.0.25-beta-11
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.
|
@@ -343,6 +343,7 @@ export declare abstract class AbstractInteractiveCanvas implements IInteractiveC
|
|
|
343
343
|
private redrawScheduled;
|
|
344
344
|
/**
|
|
345
345
|
* Redraw the canvas.
|
|
346
|
+
* Uses requestAnimationFrame to batch multiple redraw calls into a single render.
|
|
346
347
|
*/
|
|
347
348
|
redraw(): void;
|
|
348
349
|
syncObjectList(): void;
|
|
@@ -310,7 +310,6 @@ class StackObjectsLayoutFunction {
|
|
|
310
310
|
const footerObjects = objects[0];
|
|
311
311
|
const bodyObjects = objects[1];
|
|
312
312
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
313
|
-
// canvas.redraw();
|
|
314
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.
|
|
315
314
|
this.applyWidths(allObjects, bodyWidth);
|
|
316
315
|
const bodyRows = this.getRows(bodyObjects);
|
|
@@ -397,12 +396,11 @@ class StackObjectsLayoutFunction {
|
|
|
397
396
|
object.setPosition({
|
|
398
397
|
width: actualWidth
|
|
399
398
|
});
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
});
|
|
399
|
+
});
|
|
400
|
+
_(objects).forEach((object) => {
|
|
401
|
+
if (object.getType() !== ObjectType.VERTICAL_RULE) {
|
|
402
|
+
object.updateDimensions();
|
|
403
|
+
}
|
|
406
404
|
});
|
|
407
405
|
}
|
|
408
406
|
adjustCanvasSize(canvas, requiredHeight) {
|
|
@@ -1069,7 +1067,8 @@ class AbstractCanvasObject {
|
|
|
1069
1067
|
mt: options.allowScale,
|
|
1070
1068
|
mr: options.allowScale,
|
|
1071
1069
|
mb: options.allowScale,
|
|
1072
|
-
mtr: options.allowRotate
|
|
1070
|
+
mtr: options.allowRotate,
|
|
1071
|
+
// dragControl: options.allowMove // Show drag control when movement is allowed
|
|
1073
1072
|
});
|
|
1074
1073
|
}
|
|
1075
1074
|
}
|
|
@@ -1808,14 +1807,13 @@ class Text extends AbstractCanvasObject {
|
|
|
1808
1807
|
constructor(canvas, fabricObject, persistedField) {
|
|
1809
1808
|
super(ObjectType.TEXT, canvas, fabricObject, persistedField);
|
|
1810
1809
|
this.notifyCanvasTimeout = null;
|
|
1811
|
-
this.NOTIFY_DELAY_MS = 150;
|
|
1810
|
+
this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
|
|
1812
1811
|
this.canvas = canvas;
|
|
1813
1812
|
this.fontLibrary = canvas.getFontLibrary();
|
|
1814
1813
|
this.baseComponentHeight = this.fabricObject.height;
|
|
1815
1814
|
this.text$ = new BehaviorSubject(this.fabricObject.text);
|
|
1816
1815
|
this.data.userText = this.fabricObject.text;
|
|
1817
1816
|
this.textSelection$ = new BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
1818
|
-
//this.fabricObject.set({strokeWidth: 0});
|
|
1819
1817
|
this.fabricObject.set({
|
|
1820
1818
|
strokeWidth: 0,
|
|
1821
1819
|
perPixelTargetFind: false // Disable per-pixel detection for this object
|
|
@@ -2453,6 +2451,7 @@ class Text extends AbstractCanvasObject {
|
|
|
2453
2451
|
lockMovementY: true,
|
|
2454
2452
|
hasControls: false,
|
|
2455
2453
|
selectable: false,
|
|
2454
|
+
// evented: false, // Don't capture mouse events - allows clicks to pass through to textbox
|
|
2456
2455
|
originX: "left",
|
|
2457
2456
|
originY: "top"
|
|
2458
2457
|
});
|
|
@@ -2547,7 +2546,7 @@ class Text extends AbstractCanvasObject {
|
|
|
2547
2546
|
text: `${lineNumber}.`
|
|
2548
2547
|
});
|
|
2549
2548
|
}
|
|
2550
|
-
if (method === "fillText" && this.fabricObject.left <= 3) {
|
|
2549
|
+
if (method === "fillText" && this.isOrderedList() && this.fabricObject.left <= 3) {
|
|
2551
2550
|
left = left + this.getOrderedListMargin();
|
|
2552
2551
|
}
|
|
2553
2552
|
this.fabricObject.dirty = true;
|
|
@@ -2559,12 +2558,16 @@ class Text extends AbstractCanvasObject {
|
|
|
2559
2558
|
* @inheritDoc
|
|
2560
2559
|
*/
|
|
2561
2560
|
updateFabricObjectBehavior() {
|
|
2561
|
+
// DON'T call super.updateFabricObjectBehavior() for textboxes
|
|
2562
|
+
// because it locks movement which interferes with text editing in Fabric v7
|
|
2562
2563
|
super.updateFabricObjectBehavior();
|
|
2563
2564
|
const profile = this.canvas.getProfile();
|
|
2564
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
|
|
2565
2568
|
this.fabricObject.set({
|
|
2566
|
-
lockMovementX: false,
|
|
2567
|
-
lockMovementY: false,
|
|
2569
|
+
lockMovementX: false, // Allow dragging
|
|
2570
|
+
lockMovementY: false, // Allow dragging
|
|
2568
2571
|
lockScalingY: true,
|
|
2569
2572
|
lockScalingX: this.isPinToBottom(),
|
|
2570
2573
|
lockScalingFlip: true,
|
|
@@ -2583,13 +2586,14 @@ class Text extends AbstractCanvasObject {
|
|
|
2583
2586
|
mt: false,
|
|
2584
2587
|
mr: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
|
|
2585
2588
|
mb: false,
|
|
2586
|
-
mtr: false
|
|
2589
|
+
mtr: false,
|
|
2587
2590
|
});
|
|
2588
2591
|
this.redraw();
|
|
2589
2592
|
}
|
|
2590
2593
|
onTextChange() {
|
|
2591
2594
|
this.data.userText = this.fabricObject.text;
|
|
2592
2595
|
this.text$.next(this.fabricObject.text);
|
|
2596
|
+
// Only check width bounds if text is close to canvas edge (performance optimization)
|
|
2593
2597
|
const currentLeft = this.fabricObject.left || 0;
|
|
2594
2598
|
const canvasWidth = this.canvas.getDimensions().widthPx;
|
|
2595
2599
|
// Skip expensive getBoundingBox if object is clearly within bounds
|
|
@@ -2813,7 +2817,7 @@ class AbstractInteractiveCanvas {
|
|
|
2813
2817
|
this.fabricCanvas.stateful = true;
|
|
2814
2818
|
this.fabricCanvas.preserveObjectStacking = true;
|
|
2815
2819
|
this.fabricCanvas.perPixelTargetFind = true;
|
|
2816
|
-
this.fabricCanvas.targetFindTolerance = 4;
|
|
2820
|
+
this.fabricCanvas.targetFindTolerance = 4; // Reduced tolerance for more precise text selection
|
|
2817
2821
|
this.setBackgroundColor("#FFFFFF");
|
|
2818
2822
|
}
|
|
2819
2823
|
/**
|
|
@@ -3117,7 +3121,6 @@ class AbstractInteractiveCanvas {
|
|
|
3117
3121
|
*/
|
|
3118
3122
|
addImageObject(imageUrl) {
|
|
3119
3123
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3120
|
-
// Don't let the image be more than half the canvas in size.
|
|
3121
3124
|
const maxSizeScale = 2;
|
|
3122
3125
|
const maxWidth = this.dimensions$.value.widthPx / maxSizeScale;
|
|
3123
3126
|
const options = { crossOrigin: "anonymous" };
|
|
@@ -3129,7 +3132,6 @@ class AbstractInteractiveCanvas {
|
|
|
3129
3132
|
if (curWidth > maxWidth) {
|
|
3130
3133
|
newScale = maxWidth / curWidth;
|
|
3131
3134
|
}
|
|
3132
|
-
// Set image properties and add to the canvas
|
|
3133
3135
|
const fabricImage = new this.fabric.Image(image, {
|
|
3134
3136
|
id: id,
|
|
3135
3137
|
scaleX: newScale,
|
|
@@ -3149,7 +3151,6 @@ class AbstractInteractiveCanvas {
|
|
|
3149
3151
|
return object;
|
|
3150
3152
|
}
|
|
3151
3153
|
else {
|
|
3152
|
-
// Browser-side logic
|
|
3153
3154
|
return yield new Promise((resolve) => {
|
|
3154
3155
|
this.fabric.Image.fromURL(imageUrl, options).then((fabricImage) => {
|
|
3155
3156
|
const id = this.randomId();
|
|
@@ -3201,15 +3202,13 @@ class AbstractInteractiveCanvas {
|
|
|
3201
3202
|
fontFamily: profile.defaultFont,
|
|
3202
3203
|
textAlign: profile.defaultTextAlignment,
|
|
3203
3204
|
hoverCursor: "move",
|
|
3204
|
-
moveCursor:
|
|
3205
|
+
moveCursor: "move",
|
|
3206
|
+
editable: false,
|
|
3207
|
+
editingBorderColor: '#2196F3',
|
|
3205
3208
|
multiLine: true,
|
|
3206
3209
|
lineHeight: profile.defaultLineHeight,
|
|
3207
3210
|
charSpacing: profile.defaultCharacterSpacing,
|
|
3208
|
-
perPixelTargetFind: false,
|
|
3209
|
-
targetFindTolerance: 4,
|
|
3210
3211
|
splitByGrapheme: true,
|
|
3211
|
-
editable: false, // Only enable on double-click
|
|
3212
|
-
editingBorderColor: '#2196F3',
|
|
3213
3212
|
padding: 0,
|
|
3214
3213
|
cornerSize: 8,
|
|
3215
3214
|
touchCornerSize: 12
|
|
@@ -3659,6 +3658,7 @@ class AbstractInteractiveCanvas {
|
|
|
3659
3658
|
}
|
|
3660
3659
|
/**
|
|
3661
3660
|
* Redraw the canvas.
|
|
3661
|
+
* Uses requestAnimationFrame to batch multiple redraw calls into a single render.
|
|
3662
3662
|
*/
|
|
3663
3663
|
redraw() {
|
|
3664
3664
|
if (this.redrawScheduled) {
|
|
@@ -3794,7 +3794,6 @@ class AbstractInteractiveCanvas {
|
|
|
3794
3794
|
* Update the actual dimensions of the canvas by multiplying the requested dimensions by the current zoom factor.
|
|
3795
3795
|
*/
|
|
3796
3796
|
updateCanvasDimensions(dims, zoom) {
|
|
3797
|
-
const oldWidth = this.fabricCanvas.getWidth();
|
|
3798
3797
|
const width = dims.widthPx * zoom;
|
|
3799
3798
|
const height = dims.heightPx * zoom;
|
|
3800
3799
|
this.fabricCanvas.setZoom(zoom);
|
|
@@ -3802,34 +3801,6 @@ class AbstractInteractiveCanvas {
|
|
|
3802
3801
|
width,
|
|
3803
3802
|
height
|
|
3804
3803
|
});
|
|
3805
|
-
const scaleFactor = width / oldWidth;
|
|
3806
|
-
const objects = this.fabricCanvas.getObjects();
|
|
3807
|
-
objects.forEach((obj) => {
|
|
3808
|
-
obj.set({
|
|
3809
|
-
left: obj.left * scaleFactor,
|
|
3810
|
-
});
|
|
3811
|
-
if (obj.type === "textbox" || obj.type === "text") {
|
|
3812
|
-
obj.set({
|
|
3813
|
-
scaleX: 1,
|
|
3814
|
-
scaleY: 1,
|
|
3815
|
-
width: obj.width * scaleFactor,
|
|
3816
|
-
});
|
|
3817
|
-
}
|
|
3818
|
-
else if (obj.type === "border" || obj.type === "rect") {
|
|
3819
|
-
obj.set({
|
|
3820
|
-
width: obj.width * scaleFactor,
|
|
3821
|
-
scaleX: 1,
|
|
3822
|
-
strokeUniform: true,
|
|
3823
|
-
});
|
|
3824
|
-
}
|
|
3825
|
-
else {
|
|
3826
|
-
obj.set({
|
|
3827
|
-
scaleX: obj.scaleX * scaleFactor,
|
|
3828
|
-
});
|
|
3829
|
-
}
|
|
3830
|
-
obj.setCoords();
|
|
3831
|
-
});
|
|
3832
|
-
this.fabricCanvas.requestRenderAll();
|
|
3833
3804
|
}
|
|
3834
3805
|
onProfileChanged(profile) {
|
|
3835
3806
|
this.setMultiSelect(profile.allowMultiSelect);
|
|
@@ -3984,7 +3955,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3984
3955
|
this.fabricCanvas.on("selection:created", (event) => this.onFabricSelectionChanged(event));
|
|
3985
3956
|
this.fabricCanvas.on("selection:cleared", (event) => this.onFabricSelectionChanged(event));
|
|
3986
3957
|
this.fabricCanvas.on("text:editing:entered", () => this.onTextEditing());
|
|
3987
|
-
//this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
|
|
3988
3958
|
this.fabricCanvas.on("text:editing:exited", (event) => {
|
|
3989
3959
|
this.onTextEditing();
|
|
3990
3960
|
const target = event.target;
|
|
@@ -3993,8 +3963,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3993
3963
|
}
|
|
3994
3964
|
});
|
|
3995
3965
|
this.fabricCanvas.on("text:changed", () => this.onTextChange());
|
|
3996
|
-
// Handle double-click on textbox to enter editing mode
|
|
3997
|
-
// @ts-ignore
|
|
3998
3966
|
this.fabricCanvas.on("mouse:dblclick", (event) => this.onDoubleClick(event));
|
|
3999
3967
|
}
|
|
4000
3968
|
/**
|
|
@@ -4029,29 +3997,20 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
4029
3997
|
selection.setEvent(event);
|
|
4030
3998
|
}
|
|
4031
3999
|
this.selection$.next(selection);
|
|
4032
|
-
this.enterTextSelectionModeIfRequired();
|
|
4033
|
-
}
|
|
4034
|
-
enterTextSelectionModeIfRequired() {
|
|
4035
|
-
var _a, _b;
|
|
4036
|
-
if (!this.profile$.value.enterTextEditImmediatelyOnSelect && !this.layoutManager$.value.enterTextEditImmediatelyOnSelect)
|
|
4037
|
-
return;
|
|
4038
|
-
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)
|
|
4039
|
-
return;
|
|
4040
|
-
const text = this.selection$.value.objects[0];
|
|
4041
|
-
text.enterEditing();
|
|
4042
4000
|
}
|
|
4043
4001
|
onTextEditing() {
|
|
4044
4002
|
const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
|
|
4045
4003
|
this.selection$.next(new SelectionData(selected));
|
|
4046
4004
|
}
|
|
4047
4005
|
onDoubleClick(event) {
|
|
4006
|
+
var _a, _b;
|
|
4048
4007
|
const target = event.target;
|
|
4049
4008
|
if (target && (target.type === 'textbox' || target.type === 'i-text')) {
|
|
4050
|
-
|
|
4009
|
+
const targetIText = target;
|
|
4010
|
+
if (!targetIText.isEditing) {
|
|
4051
4011
|
target.set({ editable: true, hoverCursor: 'text' });
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
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();
|
|
4055
4014
|
}
|
|
4056
4015
|
}
|
|
4057
4016
|
}
|
|
@@ -331,7 +331,6 @@ class StackObjectsLayoutFunction {
|
|
|
331
331
|
const footerObjects = objects[0];
|
|
332
332
|
const bodyObjects = objects[1];
|
|
333
333
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
334
|
-
// canvas.redraw();
|
|
335
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.
|
|
336
335
|
this.applyWidths(allObjects, bodyWidth);
|
|
337
336
|
const bodyRows = this.getRows(bodyObjects);
|
|
@@ -418,12 +417,11 @@ class StackObjectsLayoutFunction {
|
|
|
418
417
|
object.setPosition({
|
|
419
418
|
width: actualWidth
|
|
420
419
|
});
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
});
|
|
420
|
+
});
|
|
421
|
+
_(objects).forEach((object) => {
|
|
422
|
+
if (object.getType() !== exports.ObjectType.VERTICAL_RULE) {
|
|
423
|
+
object.updateDimensions();
|
|
424
|
+
}
|
|
427
425
|
});
|
|
428
426
|
}
|
|
429
427
|
adjustCanvasSize(canvas, requiredHeight) {
|
|
@@ -1090,7 +1088,8 @@ class AbstractCanvasObject {
|
|
|
1090
1088
|
mt: options.allowScale,
|
|
1091
1089
|
mr: options.allowScale,
|
|
1092
1090
|
mb: options.allowScale,
|
|
1093
|
-
mtr: options.allowRotate
|
|
1091
|
+
mtr: options.allowRotate,
|
|
1092
|
+
// dragControl: options.allowMove // Show drag control when movement is allowed
|
|
1094
1093
|
});
|
|
1095
1094
|
}
|
|
1096
1095
|
}
|
|
@@ -1829,14 +1828,13 @@ class Text extends AbstractCanvasObject {
|
|
|
1829
1828
|
constructor(canvas, fabricObject, persistedField) {
|
|
1830
1829
|
super(exports.ObjectType.TEXT, canvas, fabricObject, persistedField);
|
|
1831
1830
|
this.notifyCanvasTimeout = null;
|
|
1832
|
-
this.NOTIFY_DELAY_MS = 150;
|
|
1831
|
+
this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
|
|
1833
1832
|
this.canvas = canvas;
|
|
1834
1833
|
this.fontLibrary = canvas.getFontLibrary();
|
|
1835
1834
|
this.baseComponentHeight = this.fabricObject.height;
|
|
1836
1835
|
this.text$ = new rxjs.BehaviorSubject(this.fabricObject.text);
|
|
1837
1836
|
this.data.userText = this.fabricObject.text;
|
|
1838
1837
|
this.textSelection$ = new rxjs.BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
1839
|
-
//this.fabricObject.set({strokeWidth: 0});
|
|
1840
1838
|
this.fabricObject.set({
|
|
1841
1839
|
strokeWidth: 0,
|
|
1842
1840
|
perPixelTargetFind: false // Disable per-pixel detection for this object
|
|
@@ -2474,6 +2472,7 @@ class Text extends AbstractCanvasObject {
|
|
|
2474
2472
|
lockMovementY: true,
|
|
2475
2473
|
hasControls: false,
|
|
2476
2474
|
selectable: false,
|
|
2475
|
+
// evented: false, // Don't capture mouse events - allows clicks to pass through to textbox
|
|
2477
2476
|
originX: "left",
|
|
2478
2477
|
originY: "top"
|
|
2479
2478
|
});
|
|
@@ -2568,7 +2567,7 @@ class Text extends AbstractCanvasObject {
|
|
|
2568
2567
|
text: `${lineNumber}.`
|
|
2569
2568
|
});
|
|
2570
2569
|
}
|
|
2571
|
-
if (method === "fillText" && this.fabricObject.left <= 3) {
|
|
2570
|
+
if (method === "fillText" && this.isOrderedList() && this.fabricObject.left <= 3) {
|
|
2572
2571
|
left = left + this.getOrderedListMargin();
|
|
2573
2572
|
}
|
|
2574
2573
|
this.fabricObject.dirty = true;
|
|
@@ -2580,12 +2579,16 @@ class Text extends AbstractCanvasObject {
|
|
|
2580
2579
|
* @inheritDoc
|
|
2581
2580
|
*/
|
|
2582
2581
|
updateFabricObjectBehavior() {
|
|
2582
|
+
// DON'T call super.updateFabricObjectBehavior() for textboxes
|
|
2583
|
+
// because it locks movement which interferes with text editing in Fabric v7
|
|
2583
2584
|
super.updateFabricObjectBehavior();
|
|
2584
2585
|
const profile = this.canvas.getProfile();
|
|
2585
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
|
|
2586
2589
|
this.fabricObject.set({
|
|
2587
|
-
lockMovementX: false,
|
|
2588
|
-
lockMovementY: false,
|
|
2590
|
+
lockMovementX: false, // Allow dragging
|
|
2591
|
+
lockMovementY: false, // Allow dragging
|
|
2589
2592
|
lockScalingY: true,
|
|
2590
2593
|
lockScalingX: this.isPinToBottom(),
|
|
2591
2594
|
lockScalingFlip: true,
|
|
@@ -2604,13 +2607,14 @@ class Text extends AbstractCanvasObject {
|
|
|
2604
2607
|
mt: false,
|
|
2605
2608
|
mr: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
|
|
2606
2609
|
mb: false,
|
|
2607
|
-
mtr: false
|
|
2610
|
+
mtr: false,
|
|
2608
2611
|
});
|
|
2609
2612
|
this.redraw();
|
|
2610
2613
|
}
|
|
2611
2614
|
onTextChange() {
|
|
2612
2615
|
this.data.userText = this.fabricObject.text;
|
|
2613
2616
|
this.text$.next(this.fabricObject.text);
|
|
2617
|
+
// Only check width bounds if text is close to canvas edge (performance optimization)
|
|
2614
2618
|
const currentLeft = this.fabricObject.left || 0;
|
|
2615
2619
|
const canvasWidth = this.canvas.getDimensions().widthPx;
|
|
2616
2620
|
// Skip expensive getBoundingBox if object is clearly within bounds
|
|
@@ -2834,7 +2838,7 @@ class AbstractInteractiveCanvas {
|
|
|
2834
2838
|
this.fabricCanvas.stateful = true;
|
|
2835
2839
|
this.fabricCanvas.preserveObjectStacking = true;
|
|
2836
2840
|
this.fabricCanvas.perPixelTargetFind = true;
|
|
2837
|
-
this.fabricCanvas.targetFindTolerance = 4;
|
|
2841
|
+
this.fabricCanvas.targetFindTolerance = 4; // Reduced tolerance for more precise text selection
|
|
2838
2842
|
this.setBackgroundColor("#FFFFFF");
|
|
2839
2843
|
}
|
|
2840
2844
|
/**
|
|
@@ -3138,7 +3142,6 @@ class AbstractInteractiveCanvas {
|
|
|
3138
3142
|
*/
|
|
3139
3143
|
addImageObject(imageUrl) {
|
|
3140
3144
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3141
|
-
// Don't let the image be more than half the canvas in size.
|
|
3142
3145
|
const maxSizeScale = 2;
|
|
3143
3146
|
const maxWidth = this.dimensions$.value.widthPx / maxSizeScale;
|
|
3144
3147
|
const options = { crossOrigin: "anonymous" };
|
|
@@ -3150,7 +3153,6 @@ class AbstractInteractiveCanvas {
|
|
|
3150
3153
|
if (curWidth > maxWidth) {
|
|
3151
3154
|
newScale = maxWidth / curWidth;
|
|
3152
3155
|
}
|
|
3153
|
-
// Set image properties and add to the canvas
|
|
3154
3156
|
const fabricImage = new this.fabric.Image(image, {
|
|
3155
3157
|
id: id,
|
|
3156
3158
|
scaleX: newScale,
|
|
@@ -3170,7 +3172,6 @@ class AbstractInteractiveCanvas {
|
|
|
3170
3172
|
return object;
|
|
3171
3173
|
}
|
|
3172
3174
|
else {
|
|
3173
|
-
// Browser-side logic
|
|
3174
3175
|
return yield new Promise((resolve) => {
|
|
3175
3176
|
this.fabric.Image.fromURL(imageUrl, options).then((fabricImage) => {
|
|
3176
3177
|
const id = this.randomId();
|
|
@@ -3222,15 +3223,13 @@ class AbstractInteractiveCanvas {
|
|
|
3222
3223
|
fontFamily: profile.defaultFont,
|
|
3223
3224
|
textAlign: profile.defaultTextAlignment,
|
|
3224
3225
|
hoverCursor: "move",
|
|
3225
|
-
moveCursor:
|
|
3226
|
+
moveCursor: "move",
|
|
3227
|
+
editable: false,
|
|
3228
|
+
editingBorderColor: '#2196F3',
|
|
3226
3229
|
multiLine: true,
|
|
3227
3230
|
lineHeight: profile.defaultLineHeight,
|
|
3228
3231
|
charSpacing: profile.defaultCharacterSpacing,
|
|
3229
|
-
perPixelTargetFind: false,
|
|
3230
|
-
targetFindTolerance: 4,
|
|
3231
3232
|
splitByGrapheme: true,
|
|
3232
|
-
editable: false, // Only enable on double-click
|
|
3233
|
-
editingBorderColor: '#2196F3',
|
|
3234
3233
|
padding: 0,
|
|
3235
3234
|
cornerSize: 8,
|
|
3236
3235
|
touchCornerSize: 12
|
|
@@ -3680,6 +3679,7 @@ class AbstractInteractiveCanvas {
|
|
|
3680
3679
|
}
|
|
3681
3680
|
/**
|
|
3682
3681
|
* Redraw the canvas.
|
|
3682
|
+
* Uses requestAnimationFrame to batch multiple redraw calls into a single render.
|
|
3683
3683
|
*/
|
|
3684
3684
|
redraw() {
|
|
3685
3685
|
if (this.redrawScheduled) {
|
|
@@ -3815,7 +3815,6 @@ class AbstractInteractiveCanvas {
|
|
|
3815
3815
|
* Update the actual dimensions of the canvas by multiplying the requested dimensions by the current zoom factor.
|
|
3816
3816
|
*/
|
|
3817
3817
|
updateCanvasDimensions(dims, zoom) {
|
|
3818
|
-
const oldWidth = this.fabricCanvas.getWidth();
|
|
3819
3818
|
const width = dims.widthPx * zoom;
|
|
3820
3819
|
const height = dims.heightPx * zoom;
|
|
3821
3820
|
this.fabricCanvas.setZoom(zoom);
|
|
@@ -3823,34 +3822,6 @@ class AbstractInteractiveCanvas {
|
|
|
3823
3822
|
width,
|
|
3824
3823
|
height
|
|
3825
3824
|
});
|
|
3826
|
-
const scaleFactor = width / oldWidth;
|
|
3827
|
-
const objects = this.fabricCanvas.getObjects();
|
|
3828
|
-
objects.forEach((obj) => {
|
|
3829
|
-
obj.set({
|
|
3830
|
-
left: obj.left * scaleFactor,
|
|
3831
|
-
});
|
|
3832
|
-
if (obj.type === "textbox" || obj.type === "text") {
|
|
3833
|
-
obj.set({
|
|
3834
|
-
scaleX: 1,
|
|
3835
|
-
scaleY: 1,
|
|
3836
|
-
width: obj.width * scaleFactor,
|
|
3837
|
-
});
|
|
3838
|
-
}
|
|
3839
|
-
else if (obj.type === "border" || obj.type === "rect") {
|
|
3840
|
-
obj.set({
|
|
3841
|
-
width: obj.width * scaleFactor,
|
|
3842
|
-
scaleX: 1,
|
|
3843
|
-
strokeUniform: true,
|
|
3844
|
-
});
|
|
3845
|
-
}
|
|
3846
|
-
else {
|
|
3847
|
-
obj.set({
|
|
3848
|
-
scaleX: obj.scaleX * scaleFactor,
|
|
3849
|
-
});
|
|
3850
|
-
}
|
|
3851
|
-
obj.setCoords();
|
|
3852
|
-
});
|
|
3853
|
-
this.fabricCanvas.requestRenderAll();
|
|
3854
3825
|
}
|
|
3855
3826
|
onProfileChanged(profile) {
|
|
3856
3827
|
this.setMultiSelect(profile.allowMultiSelect);
|
|
@@ -4005,7 +3976,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
4005
3976
|
this.fabricCanvas.on("selection:created", (event) => this.onFabricSelectionChanged(event));
|
|
4006
3977
|
this.fabricCanvas.on("selection:cleared", (event) => this.onFabricSelectionChanged(event));
|
|
4007
3978
|
this.fabricCanvas.on("text:editing:entered", () => this.onTextEditing());
|
|
4008
|
-
//this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
|
|
4009
3979
|
this.fabricCanvas.on("text:editing:exited", (event) => {
|
|
4010
3980
|
this.onTextEditing();
|
|
4011
3981
|
const target = event.target;
|
|
@@ -4014,8 +3984,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
4014
3984
|
}
|
|
4015
3985
|
});
|
|
4016
3986
|
this.fabricCanvas.on("text:changed", () => this.onTextChange());
|
|
4017
|
-
// Handle double-click on textbox to enter editing mode
|
|
4018
|
-
// @ts-ignore
|
|
4019
3987
|
this.fabricCanvas.on("mouse:dblclick", (event) => this.onDoubleClick(event));
|
|
4020
3988
|
}
|
|
4021
3989
|
/**
|
|
@@ -4050,29 +4018,20 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
4050
4018
|
selection.setEvent(event);
|
|
4051
4019
|
}
|
|
4052
4020
|
this.selection$.next(selection);
|
|
4053
|
-
this.enterTextSelectionModeIfRequired();
|
|
4054
|
-
}
|
|
4055
|
-
enterTextSelectionModeIfRequired() {
|
|
4056
|
-
var _a, _b;
|
|
4057
|
-
if (!this.profile$.value.enterTextEditImmediatelyOnSelect && !this.layoutManager$.value.enterTextEditImmediatelyOnSelect)
|
|
4058
|
-
return;
|
|
4059
|
-
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)
|
|
4060
|
-
return;
|
|
4061
|
-
const text = this.selection$.value.objects[0];
|
|
4062
|
-
text.enterEditing();
|
|
4063
4021
|
}
|
|
4064
4022
|
onTextEditing() {
|
|
4065
4023
|
const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
|
|
4066
4024
|
this.selection$.next(new SelectionData(selected));
|
|
4067
4025
|
}
|
|
4068
4026
|
onDoubleClick(event) {
|
|
4027
|
+
var _a, _b;
|
|
4069
4028
|
const target = event.target;
|
|
4070
4029
|
if (target && (target.type === 'textbox' || target.type === 'i-text')) {
|
|
4071
|
-
|
|
4030
|
+
const targetIText = target;
|
|
4031
|
+
if (!targetIText.isEditing) {
|
|
4072
4032
|
target.set({ editable: true, hoverCursor: 'text' });
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
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();
|
|
4076
4035
|
}
|
|
4077
4036
|
}
|
|
4078
4037
|
}
|