@leapfrog/interactive-canvas 2.0.25-beta-14 → 2.0.25-beta-16
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 +1 -0
- package/dist/interactive-canvas.es.js +194 -91
- package/dist/interactive-canvas.js +194 -91
- package/dist/layout/advanced-layout-manager.d.ts +1 -0
- package/dist/layout/standard-layout-manager.d.ts +1 -0
- package/dist/object/abstract-canvas-object.d.ts +7 -0
- package/dist/object/impl/text.d.ts +14 -0
- package/package.json +1 -1
|
@@ -340,6 +340,7 @@ 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.
|
|
345
346
|
*/
|
|
@@ -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
|
|
|
@@ -264,7 +270,7 @@ class StackObjectsLayoutFunction {
|
|
|
264
270
|
const footerObjects = objects[0];
|
|
265
271
|
const bodyObjects = objects[1];
|
|
266
272
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
267
|
-
canvas.redraw();
|
|
273
|
+
// canvas.redraw();
|
|
268
274
|
//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.
|
|
269
275
|
this.applyWidths(allObjects, bodyWidth);
|
|
270
276
|
const bodyRows = this.getRows(bodyObjects);
|
|
@@ -351,7 +357,11 @@ class StackObjectsLayoutFunction {
|
|
|
351
357
|
object.setPosition({
|
|
352
358
|
width: actualWidth
|
|
353
359
|
});
|
|
354
|
-
|
|
360
|
+
_(objects).forEach((object) => {
|
|
361
|
+
if (object.getType() !== ObjectType.VERTICAL_RULE) {
|
|
362
|
+
object.updateDimensions();
|
|
363
|
+
}
|
|
364
|
+
});
|
|
355
365
|
});
|
|
356
366
|
}
|
|
357
367
|
adjustCanvasSize(canvas, requiredHeight) {
|
|
@@ -437,15 +447,22 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
437
447
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
438
448
|
this.resizeCanvas = new ResizeCanvasLayoutFunction();
|
|
439
449
|
this.stackObjects = new StackObjectsLayoutFunction();
|
|
450
|
+
this.dimensionsLayoutScheduled = false;
|
|
440
451
|
}
|
|
441
452
|
onCanvasDimensionsChange() {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
this.
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
453
|
+
if (this.dimensionsLayoutScheduled) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
this.dimensionsLayoutScheduled = true;
|
|
457
|
+
Promise.resolve().then(() => {
|
|
458
|
+
this.pinToBottom.execute(this.canvas);
|
|
459
|
+
this.dimensionChange();
|
|
460
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
461
|
+
this.repositionBorders.execute(this.canvas);
|
|
462
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
463
|
+
this.redrawAll.execute(this.canvas);
|
|
464
|
+
this.dimensionsLayoutScheduled = false;
|
|
465
|
+
});
|
|
449
466
|
}
|
|
450
467
|
onTextChange() {
|
|
451
468
|
this.resizeCanvas.execute(this.canvas);
|
|
@@ -456,9 +473,7 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
456
473
|
dimensionChange() {
|
|
457
474
|
const canvasMargin = this.canvas.getMargin();
|
|
458
475
|
const canvasDimensions = this.canvas.getDimensions();
|
|
459
|
-
const allObjects = this.canvas.getObjectsByTypesInternal(ObjectType.TEXT, ObjectType.RICH_TEXT,
|
|
460
|
-
//ObjectType.IMAGE,
|
|
461
|
-
ObjectType.RECTANGLE, ObjectType.VERTICAL_RULE, ObjectType.HORIZONTAL_RULE);
|
|
476
|
+
const allObjects = this.canvas.getObjectsByTypesInternal(ObjectType.TEXT, ObjectType.RICH_TEXT, ObjectType.RECTANGLE, ObjectType.VERTICAL_RULE, ObjectType.HORIZONTAL_RULE);
|
|
462
477
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
463
478
|
this.canvas.redraw();
|
|
464
479
|
//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.
|
|
@@ -484,6 +499,7 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
484
499
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
485
500
|
this.stackObjects = new StackObjectsLayoutFunction();
|
|
486
501
|
this.updateObjectDimensions = new UpdateObjectDimensionsLayoutFunction();
|
|
502
|
+
this.layoutBatchScheduled = false;
|
|
487
503
|
}
|
|
488
504
|
/**
|
|
489
505
|
* @override
|
|
@@ -525,12 +541,19 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
525
541
|
this.stackAndRedraw();
|
|
526
542
|
}
|
|
527
543
|
stackAndRedraw() {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
this.
|
|
532
|
-
|
|
533
|
-
|
|
544
|
+
if (this.layoutBatchScheduled) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
this.layoutBatchScheduled = true;
|
|
548
|
+
Promise.resolve().then(() => {
|
|
549
|
+
this.stackObjects.execute(this.canvas);
|
|
550
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
551
|
+
this.repositionBorders.execute(this.canvas);
|
|
552
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
553
|
+
this.updateObjectDimensions.execute(this.canvas);
|
|
554
|
+
this.redrawAll.execute(this.canvas);
|
|
555
|
+
this.layoutBatchScheduled = false;
|
|
556
|
+
});
|
|
534
557
|
}
|
|
535
558
|
}
|
|
536
559
|
|
|
@@ -997,7 +1020,7 @@ class AbstractCanvasObject {
|
|
|
997
1020
|
//no-op
|
|
998
1021
|
}
|
|
999
1022
|
preventLeavingCanvas() {
|
|
1000
|
-
const overflow = this.
|
|
1023
|
+
const overflow = this.getCanvasOverflowForBoundaryPrevention();
|
|
1001
1024
|
if (overflow.hasOverflow) {
|
|
1002
1025
|
const adjustX = overflow.left || -overflow.right;
|
|
1003
1026
|
const adjustY = overflow.top || -overflow.bottom;
|
|
@@ -1023,6 +1046,29 @@ class AbstractCanvasObject {
|
|
|
1023
1046
|
hasOverflow: top > 0 || right > 0 || bottom > 0 || left > 0
|
|
1024
1047
|
};
|
|
1025
1048
|
}
|
|
1049
|
+
getCanvasOverflowForBoundaryPrevention() {
|
|
1050
|
+
// Use a simpler bounding box calculation for boundary prevention
|
|
1051
|
+
// to avoid issues with decorative elements (like ordered list markers)
|
|
1052
|
+
// extending beyond the main object bounds
|
|
1053
|
+
const bound = this.getSimpleBoundingBox();
|
|
1054
|
+
const canvasDimensions = this.canvas.getDimensions();
|
|
1055
|
+
const top = Math.max(0, 0 - bound.top);
|
|
1056
|
+
const right = Math.max(0, bound.left + bound.width - canvasDimensions.widthPx);
|
|
1057
|
+
const bottom = Math.max(0, bound.top + bound.height - canvasDimensions.heightPx);
|
|
1058
|
+
const left = Math.max(0, 0 - bound.left);
|
|
1059
|
+
return {
|
|
1060
|
+
top: top,
|
|
1061
|
+
right: right,
|
|
1062
|
+
bottom: bottom,
|
|
1063
|
+
left: left,
|
|
1064
|
+
hasOverflow: top > 0 || right > 0 || bottom > 0 || left > 0
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
getSimpleBoundingBox() {
|
|
1068
|
+
// Default implementation: use normal bounding box
|
|
1069
|
+
// Subclasses can override this to provide simpler bounds for drag boundary checks
|
|
1070
|
+
return this.getBoundingBox();
|
|
1071
|
+
}
|
|
1026
1072
|
centerHorizontallyInternal() {
|
|
1027
1073
|
const canvasWidth = this.canvas.getDimensions().widthPx;
|
|
1028
1074
|
const objectWidth = this.getDimensions().width.px;
|
|
@@ -1794,15 +1840,27 @@ class RichText extends Image {
|
|
|
1794
1840
|
class Text extends AbstractCanvasObject {
|
|
1795
1841
|
constructor(canvas, fabricObject, persistedField) {
|
|
1796
1842
|
super(ObjectType.TEXT, canvas, fabricObject, persistedField);
|
|
1843
|
+
this.notifyCanvasTimeout = null;
|
|
1844
|
+
this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
|
|
1797
1845
|
this.canvas = canvas;
|
|
1798
1846
|
this.fontLibrary = canvas.getFontLibrary();
|
|
1799
1847
|
this.baseComponentHeight = this.fabricObject.height;
|
|
1800
1848
|
this.text$ = new BehaviorSubject(this.fabricObject.text);
|
|
1801
1849
|
this.data.userText = this.fabricObject.text;
|
|
1802
1850
|
this.textSelection$ = new BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
1803
|
-
this.fabricObject.set({
|
|
1851
|
+
this.fabricObject.set({
|
|
1852
|
+
strokeWidth: 0,
|
|
1853
|
+
perPixelTargetFind: false, // Disable per-pixel detection for this object
|
|
1854
|
+
padding: 0 // Add padding for larger hover area (Fabric v7 compatibility)
|
|
1855
|
+
});
|
|
1804
1856
|
this.fabricObject.on("changed", () => this.onTextChange());
|
|
1805
1857
|
this.fabricObject.on("selection:changed", () => this.onSelectionChange());
|
|
1858
|
+
this.fabricObject.on("mousedown", (e) => {
|
|
1859
|
+
var _a;
|
|
1860
|
+
if (!this.fabricObject.isEditing) {
|
|
1861
|
+
(_a = e.e) === null || _a === void 0 ? void 0 : _a.preventDefault();
|
|
1862
|
+
}
|
|
1863
|
+
});
|
|
1806
1864
|
this.applyBulletListMonkeyPatches();
|
|
1807
1865
|
}
|
|
1808
1866
|
/**
|
|
@@ -2154,11 +2212,25 @@ class Text extends AbstractCanvasObject {
|
|
|
2154
2212
|
getBulletMargin() {
|
|
2155
2213
|
if (!this.isBulleted())
|
|
2156
2214
|
return 0;
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2215
|
+
try {
|
|
2216
|
+
let fontSize = 0;
|
|
2217
|
+
each(this.fabricObject._textLines, (textLine, lineIndex) => {
|
|
2218
|
+
const size = this.fabricObject.getValueOfPropertyAt(lineIndex, 0, "fontSize");
|
|
2219
|
+
// Sanity check fontSize
|
|
2220
|
+
if (isFinite(size) && size > 0 && size < 1000) {
|
|
2221
|
+
fontSize = Math.max(fontSize, size);
|
|
2222
|
+
}
|
|
2223
|
+
});
|
|
2224
|
+
// Additional safety check
|
|
2225
|
+
if (!isFinite(fontSize) || fontSize < 0 || fontSize > 1000) {
|
|
2226
|
+
return 0;
|
|
2227
|
+
}
|
|
2228
|
+
return fontSize * 0.6;
|
|
2229
|
+
}
|
|
2230
|
+
catch (e) {
|
|
2231
|
+
console.warn('Error calculating bullet margin:', e);
|
|
2232
|
+
return 0;
|
|
2233
|
+
}
|
|
2162
2234
|
}
|
|
2163
2235
|
/**
|
|
2164
2236
|
* @override
|
|
@@ -2168,10 +2240,35 @@ class Text extends AbstractCanvasObject {
|
|
|
2168
2240
|
if (!this.isOrderedList())
|
|
2169
2241
|
return 0;
|
|
2170
2242
|
const orderedListMarkerEntities = this.fabricObject._orderedListMarkerEntities;
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2243
|
+
try {
|
|
2244
|
+
const widths = (orderedListMarkerEntities !== null && orderedListMarkerEntities !== void 0 ? orderedListMarkerEntities : [])
|
|
2245
|
+
.filter((x) => !!x)
|
|
2246
|
+
.map((x) => {
|
|
2247
|
+
var _a;
|
|
2248
|
+
try {
|
|
2249
|
+
const rect = x.getBoundingRect();
|
|
2250
|
+
const width = (_a = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _a !== void 0 ? _a : 0;
|
|
2251
|
+
// Sanity check: if width is invalid, return 0
|
|
2252
|
+
if (!isFinite(width) || width < 0 || width > 1000) {
|
|
2253
|
+
return 0;
|
|
2254
|
+
}
|
|
2255
|
+
return width;
|
|
2256
|
+
}
|
|
2257
|
+
catch (e) {
|
|
2258
|
+
return 0;
|
|
2259
|
+
}
|
|
2260
|
+
});
|
|
2261
|
+
const maxValue = max(widths);
|
|
2262
|
+
// Additional safety: ensure maxValue is a valid number
|
|
2263
|
+
if (!maxValue || !isFinite(maxValue) || maxValue < 0 || maxValue > 1000) {
|
|
2264
|
+
return 0;
|
|
2265
|
+
}
|
|
2266
|
+
return maxValue * 1.2;
|
|
2267
|
+
}
|
|
2268
|
+
catch (e) {
|
|
2269
|
+
console.warn('Error calculating ordered list margin:', e);
|
|
2270
|
+
return 0;
|
|
2271
|
+
}
|
|
2175
2272
|
}
|
|
2176
2273
|
/**
|
|
2177
2274
|
* @override
|
|
@@ -2506,7 +2603,7 @@ class Text extends AbstractCanvasObject {
|
|
|
2506
2603
|
text: `${lineNumber}.`
|
|
2507
2604
|
});
|
|
2508
2605
|
}
|
|
2509
|
-
if (method === "fillText" && this.fabricObject.left <= 3) {
|
|
2606
|
+
if (method === "fillText" && this.isOrderedList() && this.fabricObject.left <= 3) {
|
|
2510
2607
|
left = left + this.getOrderedListMargin();
|
|
2511
2608
|
}
|
|
2512
2609
|
this.fabricObject.dirty = true;
|
|
@@ -2522,9 +2619,16 @@ class Text extends AbstractCanvasObject {
|
|
|
2522
2619
|
const profile = this.canvas.getProfile();
|
|
2523
2620
|
const allowManualPositioning = !this.canvas.getLayoutManager().isEnforcedPositioning;
|
|
2524
2621
|
this.fabricObject.set({
|
|
2622
|
+
lockMovementX: false, // Allow dragging
|
|
2623
|
+
lockMovementY: false,
|
|
2525
2624
|
lockScalingY: true,
|
|
2526
2625
|
lockScalingX: this.isPinToBottom(),
|
|
2527
|
-
lockScalingFlip: true
|
|
2626
|
+
lockScalingFlip: true,
|
|
2627
|
+
lockRotation: true,
|
|
2628
|
+
selectable: true,
|
|
2629
|
+
editable: false, // Only enable on double-click
|
|
2630
|
+
hoverCursor: "move", // Show move cursor by default
|
|
2631
|
+
moveCursor: "move",
|
|
2528
2632
|
});
|
|
2529
2633
|
this.fabricObject.setControlsVisibility({
|
|
2530
2634
|
tl: false,
|
|
@@ -2534,35 +2638,63 @@ class Text extends AbstractCanvasObject {
|
|
|
2534
2638
|
ml: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
|
|
2535
2639
|
mt: false,
|
|
2536
2640
|
mr: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
|
|
2537
|
-
mb: false
|
|
2641
|
+
mb: false,
|
|
2642
|
+
mtr: false,
|
|
2538
2643
|
});
|
|
2539
2644
|
this.redraw();
|
|
2540
2645
|
}
|
|
2541
2646
|
onTextChange() {
|
|
2542
2647
|
this.data.userText = this.fabricObject.text;
|
|
2543
2648
|
this.text$.next(this.fabricObject.text);
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2649
|
+
const currentLeft = this.fabricObject.left || 0;
|
|
2650
|
+
const canvasWidth = this.canvas.getDimensions().widthPx;
|
|
2651
|
+
// Skip expensive getBoundingBox if object is clearly within bounds
|
|
2652
|
+
if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
|
|
2653
|
+
if (this.getBoundingBox().width > canvasWidth) {
|
|
2654
|
+
this.fabricObject.set({ left: 0, width: canvasWidth });
|
|
2655
|
+
this.redraw();
|
|
2656
|
+
}
|
|
2547
2657
|
}
|
|
2548
2658
|
}
|
|
2549
2659
|
onDelete() {
|
|
2550
2660
|
this.removeBullets();
|
|
2551
2661
|
}
|
|
2552
2662
|
onSelectionChange() {
|
|
2553
|
-
this.updateTextSelection();
|
|
2663
|
+
this.updateTextSelection(false);
|
|
2554
2664
|
}
|
|
2555
2665
|
hasTextSelection() {
|
|
2556
2666
|
return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
|
|
2557
2667
|
}
|
|
2558
|
-
updateTextSelection() {
|
|
2668
|
+
updateTextSelection(notifyCanvas = true) {
|
|
2559
2669
|
if (this.canvas.headless)
|
|
2560
2670
|
return;
|
|
2561
2671
|
this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
2562
|
-
|
|
2672
|
+
if (notifyCanvas) {
|
|
2673
|
+
this.notifyCanvasOfTextChanges();
|
|
2674
|
+
}
|
|
2563
2675
|
}
|
|
2564
2676
|
notifyCanvasOfTextChanges() {
|
|
2565
|
-
this.
|
|
2677
|
+
if (this.notifyCanvasTimeout) {
|
|
2678
|
+
clearTimeout(this.notifyCanvasTimeout);
|
|
2679
|
+
}
|
|
2680
|
+
this.notifyCanvasTimeout = setTimeout(() => {
|
|
2681
|
+
this.canvas.notifyTextChanged();
|
|
2682
|
+
this.notifyCanvasTimeout = null;
|
|
2683
|
+
}, this.NOTIFY_DELAY_MS);
|
|
2684
|
+
}
|
|
2685
|
+
/**
|
|
2686
|
+
* @override
|
|
2687
|
+
* Provide a simpler bounding box for drag boundary checks that uses the textbox
|
|
2688
|
+
* dimensions directly instead of getBoundingRect(), which includes ordered list
|
|
2689
|
+
* markers that may extend far beyond the text and cause drag shaking issues.
|
|
2690
|
+
*/
|
|
2691
|
+
getSimpleBoundingBox() {
|
|
2692
|
+
return {
|
|
2693
|
+
top: this.fabricObject.top || 0,
|
|
2694
|
+
left: this.fabricObject.left || 0,
|
|
2695
|
+
width: (this.fabricObject.width || 0) * (this.fabricObject.scaleX || 1),
|
|
2696
|
+
height: (this.fabricObject.height || 0) * (this.fabricObject.scaleY || 1)
|
|
2697
|
+
};
|
|
2566
2698
|
}
|
|
2567
2699
|
}
|
|
2568
2700
|
Text.NEW_LINE = "\n";
|
|
@@ -2728,6 +2860,7 @@ class AbstractInteractiveCanvas {
|
|
|
2728
2860
|
this.devicePixelRatio = devicePixelRatio;
|
|
2729
2861
|
this.fontLibrary = new FontLibrary();
|
|
2730
2862
|
this.dimensionChangeRejections$ = new Subject();
|
|
2863
|
+
this.redrawScheduled = false;
|
|
2731
2864
|
this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
|
|
2732
2865
|
/**
|
|
2733
2866
|
* Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
|
|
@@ -2749,8 +2882,8 @@ class AbstractInteractiveCanvas {
|
|
|
2749
2882
|
//Configure canvas behaviour
|
|
2750
2883
|
this.fabricCanvas.stateful = true;
|
|
2751
2884
|
this.fabricCanvas.preserveObjectStacking = true;
|
|
2752
|
-
this.fabricCanvas.perPixelTargetFind =
|
|
2753
|
-
this.fabricCanvas.targetFindTolerance =
|
|
2885
|
+
this.fabricCanvas.perPixelTargetFind = false;
|
|
2886
|
+
this.fabricCanvas.targetFindTolerance = 0;
|
|
2754
2887
|
this.setBackgroundColor("#FFFFFF");
|
|
2755
2888
|
}
|
|
2756
2889
|
/**
|
|
@@ -3138,15 +3271,14 @@ class AbstractInteractiveCanvas {
|
|
|
3138
3271
|
fontFamily: profile.defaultFont,
|
|
3139
3272
|
textAlign: profile.defaultTextAlignment,
|
|
3140
3273
|
hoverCursor: "move",
|
|
3141
|
-
moveCursor:
|
|
3274
|
+
moveCursor: "move",
|
|
3275
|
+
editable: false,
|
|
3276
|
+
editingBorderColor: '#2196F3',
|
|
3142
3277
|
multiLine: true,
|
|
3143
3278
|
lineHeight: profile.defaultLineHeight,
|
|
3144
3279
|
charSpacing: profile.defaultCharacterSpacing,
|
|
3145
|
-
perPixelTargetFind: false,
|
|
3146
|
-
targetFindTolerance: 4,
|
|
3147
3280
|
splitByGrapheme: true,
|
|
3148
|
-
|
|
3149
|
-
padding: 0,
|
|
3281
|
+
padding: 0, // Increased from 0 for better hover detection in Fabric v7
|
|
3150
3282
|
cornerSize: 8,
|
|
3151
3283
|
touchCornerSize: 12
|
|
3152
3284
|
};
|
|
@@ -3597,7 +3729,14 @@ class AbstractInteractiveCanvas {
|
|
|
3597
3729
|
* Redraw the canvas.
|
|
3598
3730
|
*/
|
|
3599
3731
|
redraw() {
|
|
3600
|
-
this.
|
|
3732
|
+
if (this.redrawScheduled) {
|
|
3733
|
+
return;
|
|
3734
|
+
}
|
|
3735
|
+
this.redrawScheduled = true;
|
|
3736
|
+
requestAnimationFrame(() => {
|
|
3737
|
+
this.fabricCanvas.requestRenderAll();
|
|
3738
|
+
this.redrawScheduled = false;
|
|
3739
|
+
});
|
|
3601
3740
|
}
|
|
3602
3741
|
syncObjectList() {
|
|
3603
3742
|
const newObjectList = _(this.fabricCanvas.getObjects())
|
|
@@ -3723,7 +3862,6 @@ class AbstractInteractiveCanvas {
|
|
|
3723
3862
|
* Update the actual dimensions of the canvas by multiplying the requested dimensions by the current zoom factor.
|
|
3724
3863
|
*/
|
|
3725
3864
|
updateCanvasDimensions(dims, zoom) {
|
|
3726
|
-
//const oldWidth = this.fabricCanvas.getWidth();
|
|
3727
3865
|
const width = dims.widthPx * zoom;
|
|
3728
3866
|
const height = dims.heightPx * zoom;
|
|
3729
3867
|
this.fabricCanvas.setZoom(zoom);
|
|
@@ -3731,39 +3869,6 @@ class AbstractInteractiveCanvas {
|
|
|
3731
3869
|
width,
|
|
3732
3870
|
height
|
|
3733
3871
|
});
|
|
3734
|
-
/*if (this.layoutManager$.value instanceof AdvancedLayoutManager) {
|
|
3735
|
-
const scaleFactor = width / oldWidth;
|
|
3736
|
-
//this.fabricCanvas.calcOffset();
|
|
3737
|
-
const objects = this.fabricCanvas.getObjects();
|
|
3738
|
-
objects.forEach((obj: any) => {
|
|
3739
|
-
obj.set({ ₹
|
|
3740
|
-
left: obj.left * scaleFactor,
|
|
3741
|
-
});
|
|
3742
|
-
if (obj.type === "textbox" || obj.type === "text") {
|
|
3743
|
-
obj.set({
|
|
3744
|
-
scaleX: 1,
|
|
3745
|
-
scaleY: 1,
|
|
3746
|
-
// scaleX: obj.scaleX * scaleFactor,
|
|
3747
|
-
// scaleY: obj.scaleY * scaleFactor,
|
|
3748
|
-
width: obj.width * scaleFactor,
|
|
3749
|
-
});
|
|
3750
|
-
// obj.initDimensions();
|
|
3751
|
-
} else if (obj.type === "border" || obj.type === "rect") {
|
|
3752
|
-
obj.set({
|
|
3753
|
-
width: obj.width * scaleFactor,
|
|
3754
|
-
scaleX: 1,
|
|
3755
|
-
strokeUniform: true,
|
|
3756
|
-
// strokeAlign: "inner",
|
|
3757
|
-
});
|
|
3758
|
-
} else {
|
|
3759
|
-
obj.set({
|
|
3760
|
-
scaleX: obj.scaleX * scaleFactor,
|
|
3761
|
-
});
|
|
3762
|
-
}
|
|
3763
|
-
obj.setCoords();
|
|
3764
|
-
});
|
|
3765
|
-
this.fabricCanvas.requestRenderAll();
|
|
3766
|
-
}*/
|
|
3767
3872
|
}
|
|
3768
3873
|
onProfileChanged(profile) {
|
|
3769
3874
|
this.setMultiSelect(profile.allowMultiSelect);
|
|
@@ -3918,7 +4023,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3918
4023
|
this.fabricCanvas.on("selection:created", (event) => this.onFabricSelectionChanged(event));
|
|
3919
4024
|
this.fabricCanvas.on("selection:cleared", (event) => this.onFabricSelectionChanged(event));
|
|
3920
4025
|
this.fabricCanvas.on("text:editing:entered", () => this.onTextEditing());
|
|
3921
|
-
this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
|
|
3922
4026
|
this.fabricCanvas.on("text:editing:exited", (event) => {
|
|
3923
4027
|
this.onTextEditing();
|
|
3924
4028
|
const target = event.target;
|
|
@@ -3927,8 +4031,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3927
4031
|
}
|
|
3928
4032
|
});
|
|
3929
4033
|
this.fabricCanvas.on("text:changed", () => this.onTextChange());
|
|
3930
|
-
// Handle double-click on textbox to enter editing mode
|
|
3931
|
-
// @ts-ignore
|
|
3932
4034
|
this.fabricCanvas.on("mouse:dblclick", (event) => this.onDoubleClick(event));
|
|
3933
4035
|
}
|
|
3934
4036
|
/**
|
|
@@ -3963,7 +4065,7 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3963
4065
|
selection.setEvent(event);
|
|
3964
4066
|
}
|
|
3965
4067
|
this.selection$.next(selection);
|
|
3966
|
-
this.enterTextSelectionModeIfRequired();
|
|
4068
|
+
//this.enterTextSelectionModeIfRequired();
|
|
3967
4069
|
}
|
|
3968
4070
|
enterTextSelectionModeIfRequired() {
|
|
3969
4071
|
var _a, _b;
|
|
@@ -3979,13 +4081,14 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3979
4081
|
this.selection$.next(new SelectionData(selected));
|
|
3980
4082
|
}
|
|
3981
4083
|
onDoubleClick(event) {
|
|
4084
|
+
var _a, _b;
|
|
3982
4085
|
const target = event.target;
|
|
3983
4086
|
if (target && (target.type === 'textbox' || target.type === 'i-text')) {
|
|
3984
|
-
|
|
4087
|
+
const targetIText = target;
|
|
4088
|
+
if (!targetIText.isEditing) {
|
|
3985
4089
|
target.set({ editable: true, hoverCursor: 'text' });
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
this.enterTextSelectionModeIfRequired();
|
|
4090
|
+
(_a = targetIText.enterEditing) === null || _a === void 0 ? void 0 : _a.call(targetIText);
|
|
4091
|
+
(_b = targetIText.hiddenTextarea) === null || _b === void 0 ? void 0 : _b.focus();
|
|
3989
4092
|
}
|
|
3990
4093
|
}
|
|
3991
4094
|
}
|
|
@@ -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
|
|
|
@@ -285,7 +291,7 @@ class StackObjectsLayoutFunction {
|
|
|
285
291
|
const footerObjects = objects[0];
|
|
286
292
|
const bodyObjects = objects[1];
|
|
287
293
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
288
|
-
canvas.redraw();
|
|
294
|
+
// canvas.redraw();
|
|
289
295
|
//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.
|
|
290
296
|
this.applyWidths(allObjects, bodyWidth);
|
|
291
297
|
const bodyRows = this.getRows(bodyObjects);
|
|
@@ -372,7 +378,11 @@ class StackObjectsLayoutFunction {
|
|
|
372
378
|
object.setPosition({
|
|
373
379
|
width: actualWidth
|
|
374
380
|
});
|
|
375
|
-
|
|
381
|
+
_(objects).forEach((object) => {
|
|
382
|
+
if (object.getType() !== exports.ObjectType.VERTICAL_RULE) {
|
|
383
|
+
object.updateDimensions();
|
|
384
|
+
}
|
|
385
|
+
});
|
|
376
386
|
});
|
|
377
387
|
}
|
|
378
388
|
adjustCanvasSize(canvas, requiredHeight) {
|
|
@@ -458,15 +468,22 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
458
468
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
459
469
|
this.resizeCanvas = new ResizeCanvasLayoutFunction();
|
|
460
470
|
this.stackObjects = new StackObjectsLayoutFunction();
|
|
471
|
+
this.dimensionsLayoutScheduled = false;
|
|
461
472
|
}
|
|
462
473
|
onCanvasDimensionsChange() {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
this.
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
474
|
+
if (this.dimensionsLayoutScheduled) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
this.dimensionsLayoutScheduled = true;
|
|
478
|
+
Promise.resolve().then(() => {
|
|
479
|
+
this.pinToBottom.execute(this.canvas);
|
|
480
|
+
this.dimensionChange();
|
|
481
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
482
|
+
this.repositionBorders.execute(this.canvas);
|
|
483
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
484
|
+
this.redrawAll.execute(this.canvas);
|
|
485
|
+
this.dimensionsLayoutScheduled = false;
|
|
486
|
+
});
|
|
470
487
|
}
|
|
471
488
|
onTextChange() {
|
|
472
489
|
this.resizeCanvas.execute(this.canvas);
|
|
@@ -477,9 +494,7 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
477
494
|
dimensionChange() {
|
|
478
495
|
const canvasMargin = this.canvas.getMargin();
|
|
479
496
|
const canvasDimensions = this.canvas.getDimensions();
|
|
480
|
-
const allObjects = this.canvas.getObjectsByTypesInternal(exports.ObjectType.TEXT, exports.ObjectType.RICH_TEXT,
|
|
481
|
-
//ObjectType.IMAGE,
|
|
482
|
-
exports.ObjectType.RECTANGLE, exports.ObjectType.VERTICAL_RULE, exports.ObjectType.HORIZONTAL_RULE);
|
|
497
|
+
const allObjects = this.canvas.getObjectsByTypesInternal(exports.ObjectType.TEXT, exports.ObjectType.RICH_TEXT, exports.ObjectType.RECTANGLE, exports.ObjectType.VERTICAL_RULE, exports.ObjectType.HORIZONTAL_RULE);
|
|
483
498
|
const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
|
|
484
499
|
this.canvas.redraw();
|
|
485
500
|
//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.
|
|
@@ -505,6 +520,7 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
505
520
|
this.repositionBackgrounds = new RepositionBackgroundsLayoutFunction();
|
|
506
521
|
this.stackObjects = new StackObjectsLayoutFunction();
|
|
507
522
|
this.updateObjectDimensions = new UpdateObjectDimensionsLayoutFunction();
|
|
523
|
+
this.layoutBatchScheduled = false;
|
|
508
524
|
}
|
|
509
525
|
/**
|
|
510
526
|
* @override
|
|
@@ -546,12 +562,19 @@ class StandardLayoutManager extends AbstractLayoutManager {
|
|
|
546
562
|
this.stackAndRedraw();
|
|
547
563
|
}
|
|
548
564
|
stackAndRedraw() {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
this.
|
|
553
|
-
|
|
554
|
-
|
|
565
|
+
if (this.layoutBatchScheduled) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
this.layoutBatchScheduled = true;
|
|
569
|
+
Promise.resolve().then(() => {
|
|
570
|
+
this.stackObjects.execute(this.canvas);
|
|
571
|
+
this.repositionCutoffs.execute(this.canvas);
|
|
572
|
+
this.repositionBorders.execute(this.canvas);
|
|
573
|
+
this.repositionBackgrounds.execute(this.canvas);
|
|
574
|
+
this.updateObjectDimensions.execute(this.canvas);
|
|
575
|
+
this.redrawAll.execute(this.canvas);
|
|
576
|
+
this.layoutBatchScheduled = false;
|
|
577
|
+
});
|
|
555
578
|
}
|
|
556
579
|
}
|
|
557
580
|
|
|
@@ -1018,7 +1041,7 @@ class AbstractCanvasObject {
|
|
|
1018
1041
|
//no-op
|
|
1019
1042
|
}
|
|
1020
1043
|
preventLeavingCanvas() {
|
|
1021
|
-
const overflow = this.
|
|
1044
|
+
const overflow = this.getCanvasOverflowForBoundaryPrevention();
|
|
1022
1045
|
if (overflow.hasOverflow) {
|
|
1023
1046
|
const adjustX = overflow.left || -overflow.right;
|
|
1024
1047
|
const adjustY = overflow.top || -overflow.bottom;
|
|
@@ -1044,6 +1067,29 @@ class AbstractCanvasObject {
|
|
|
1044
1067
|
hasOverflow: top > 0 || right > 0 || bottom > 0 || left > 0
|
|
1045
1068
|
};
|
|
1046
1069
|
}
|
|
1070
|
+
getCanvasOverflowForBoundaryPrevention() {
|
|
1071
|
+
// Use a simpler bounding box calculation for boundary prevention
|
|
1072
|
+
// to avoid issues with decorative elements (like ordered list markers)
|
|
1073
|
+
// extending beyond the main object bounds
|
|
1074
|
+
const bound = this.getSimpleBoundingBox();
|
|
1075
|
+
const canvasDimensions = this.canvas.getDimensions();
|
|
1076
|
+
const top = Math.max(0, 0 - bound.top);
|
|
1077
|
+
const right = Math.max(0, bound.left + bound.width - canvasDimensions.widthPx);
|
|
1078
|
+
const bottom = Math.max(0, bound.top + bound.height - canvasDimensions.heightPx);
|
|
1079
|
+
const left = Math.max(0, 0 - bound.left);
|
|
1080
|
+
return {
|
|
1081
|
+
top: top,
|
|
1082
|
+
right: right,
|
|
1083
|
+
bottom: bottom,
|
|
1084
|
+
left: left,
|
|
1085
|
+
hasOverflow: top > 0 || right > 0 || bottom > 0 || left > 0
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
getSimpleBoundingBox() {
|
|
1089
|
+
// Default implementation: use normal bounding box
|
|
1090
|
+
// Subclasses can override this to provide simpler bounds for drag boundary checks
|
|
1091
|
+
return this.getBoundingBox();
|
|
1092
|
+
}
|
|
1047
1093
|
centerHorizontallyInternal() {
|
|
1048
1094
|
const canvasWidth = this.canvas.getDimensions().widthPx;
|
|
1049
1095
|
const objectWidth = this.getDimensions().width.px;
|
|
@@ -1815,15 +1861,27 @@ class RichText extends Image {
|
|
|
1815
1861
|
class Text extends AbstractCanvasObject {
|
|
1816
1862
|
constructor(canvas, fabricObject, persistedField) {
|
|
1817
1863
|
super(exports.ObjectType.TEXT, canvas, fabricObject, persistedField);
|
|
1864
|
+
this.notifyCanvasTimeout = null;
|
|
1865
|
+
this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
|
|
1818
1866
|
this.canvas = canvas;
|
|
1819
1867
|
this.fontLibrary = canvas.getFontLibrary();
|
|
1820
1868
|
this.baseComponentHeight = this.fabricObject.height;
|
|
1821
1869
|
this.text$ = new rxjs.BehaviorSubject(this.fabricObject.text);
|
|
1822
1870
|
this.data.userText = this.fabricObject.text;
|
|
1823
1871
|
this.textSelection$ = new rxjs.BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
1824
|
-
this.fabricObject.set({
|
|
1872
|
+
this.fabricObject.set({
|
|
1873
|
+
strokeWidth: 0,
|
|
1874
|
+
perPixelTargetFind: false, // Disable per-pixel detection for this object
|
|
1875
|
+
padding: 0 // Add padding for larger hover area (Fabric v7 compatibility)
|
|
1876
|
+
});
|
|
1825
1877
|
this.fabricObject.on("changed", () => this.onTextChange());
|
|
1826
1878
|
this.fabricObject.on("selection:changed", () => this.onSelectionChange());
|
|
1879
|
+
this.fabricObject.on("mousedown", (e) => {
|
|
1880
|
+
var _a;
|
|
1881
|
+
if (!this.fabricObject.isEditing) {
|
|
1882
|
+
(_a = e.e) === null || _a === void 0 ? void 0 : _a.preventDefault();
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1827
1885
|
this.applyBulletListMonkeyPatches();
|
|
1828
1886
|
}
|
|
1829
1887
|
/**
|
|
@@ -2175,11 +2233,25 @@ class Text extends AbstractCanvasObject {
|
|
|
2175
2233
|
getBulletMargin() {
|
|
2176
2234
|
if (!this.isBulleted())
|
|
2177
2235
|
return 0;
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2236
|
+
try {
|
|
2237
|
+
let fontSize = 0;
|
|
2238
|
+
_.each(this.fabricObject._textLines, (textLine, lineIndex) => {
|
|
2239
|
+
const size = this.fabricObject.getValueOfPropertyAt(lineIndex, 0, "fontSize");
|
|
2240
|
+
// Sanity check fontSize
|
|
2241
|
+
if (isFinite(size) && size > 0 && size < 1000) {
|
|
2242
|
+
fontSize = Math.max(fontSize, size);
|
|
2243
|
+
}
|
|
2244
|
+
});
|
|
2245
|
+
// Additional safety check
|
|
2246
|
+
if (!isFinite(fontSize) || fontSize < 0 || fontSize > 1000) {
|
|
2247
|
+
return 0;
|
|
2248
|
+
}
|
|
2249
|
+
return fontSize * 0.6;
|
|
2250
|
+
}
|
|
2251
|
+
catch (e) {
|
|
2252
|
+
console.warn('Error calculating bullet margin:', e);
|
|
2253
|
+
return 0;
|
|
2254
|
+
}
|
|
2183
2255
|
}
|
|
2184
2256
|
/**
|
|
2185
2257
|
* @override
|
|
@@ -2189,10 +2261,35 @@ class Text extends AbstractCanvasObject {
|
|
|
2189
2261
|
if (!this.isOrderedList())
|
|
2190
2262
|
return 0;
|
|
2191
2263
|
const orderedListMarkerEntities = this.fabricObject._orderedListMarkerEntities;
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2264
|
+
try {
|
|
2265
|
+
const widths = (orderedListMarkerEntities !== null && orderedListMarkerEntities !== void 0 ? orderedListMarkerEntities : [])
|
|
2266
|
+
.filter((x) => !!x)
|
|
2267
|
+
.map((x) => {
|
|
2268
|
+
var _a;
|
|
2269
|
+
try {
|
|
2270
|
+
const rect = x.getBoundingRect();
|
|
2271
|
+
const width = (_a = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _a !== void 0 ? _a : 0;
|
|
2272
|
+
// Sanity check: if width is invalid, return 0
|
|
2273
|
+
if (!isFinite(width) || width < 0 || width > 1000) {
|
|
2274
|
+
return 0;
|
|
2275
|
+
}
|
|
2276
|
+
return width;
|
|
2277
|
+
}
|
|
2278
|
+
catch (e) {
|
|
2279
|
+
return 0;
|
|
2280
|
+
}
|
|
2281
|
+
});
|
|
2282
|
+
const maxValue = _.max(widths);
|
|
2283
|
+
// Additional safety: ensure maxValue is a valid number
|
|
2284
|
+
if (!maxValue || !isFinite(maxValue) || maxValue < 0 || maxValue > 1000) {
|
|
2285
|
+
return 0;
|
|
2286
|
+
}
|
|
2287
|
+
return maxValue * 1.2;
|
|
2288
|
+
}
|
|
2289
|
+
catch (e) {
|
|
2290
|
+
console.warn('Error calculating ordered list margin:', e);
|
|
2291
|
+
return 0;
|
|
2292
|
+
}
|
|
2196
2293
|
}
|
|
2197
2294
|
/**
|
|
2198
2295
|
* @override
|
|
@@ -2527,7 +2624,7 @@ class Text extends AbstractCanvasObject {
|
|
|
2527
2624
|
text: `${lineNumber}.`
|
|
2528
2625
|
});
|
|
2529
2626
|
}
|
|
2530
|
-
if (method === "fillText" && this.fabricObject.left <= 3) {
|
|
2627
|
+
if (method === "fillText" && this.isOrderedList() && this.fabricObject.left <= 3) {
|
|
2531
2628
|
left = left + this.getOrderedListMargin();
|
|
2532
2629
|
}
|
|
2533
2630
|
this.fabricObject.dirty = true;
|
|
@@ -2543,9 +2640,16 @@ class Text extends AbstractCanvasObject {
|
|
|
2543
2640
|
const profile = this.canvas.getProfile();
|
|
2544
2641
|
const allowManualPositioning = !this.canvas.getLayoutManager().isEnforcedPositioning;
|
|
2545
2642
|
this.fabricObject.set({
|
|
2643
|
+
lockMovementX: false, // Allow dragging
|
|
2644
|
+
lockMovementY: false,
|
|
2546
2645
|
lockScalingY: true,
|
|
2547
2646
|
lockScalingX: this.isPinToBottom(),
|
|
2548
|
-
lockScalingFlip: true
|
|
2647
|
+
lockScalingFlip: true,
|
|
2648
|
+
lockRotation: true,
|
|
2649
|
+
selectable: true,
|
|
2650
|
+
editable: false, // Only enable on double-click
|
|
2651
|
+
hoverCursor: "move", // Show move cursor by default
|
|
2652
|
+
moveCursor: "move",
|
|
2549
2653
|
});
|
|
2550
2654
|
this.fabricObject.setControlsVisibility({
|
|
2551
2655
|
tl: false,
|
|
@@ -2555,35 +2659,63 @@ class Text extends AbstractCanvasObject {
|
|
|
2555
2659
|
ml: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
|
|
2556
2660
|
mt: false,
|
|
2557
2661
|
mr: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
|
|
2558
|
-
mb: false
|
|
2662
|
+
mb: false,
|
|
2663
|
+
mtr: false,
|
|
2559
2664
|
});
|
|
2560
2665
|
this.redraw();
|
|
2561
2666
|
}
|
|
2562
2667
|
onTextChange() {
|
|
2563
2668
|
this.data.userText = this.fabricObject.text;
|
|
2564
2669
|
this.text$.next(this.fabricObject.text);
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2670
|
+
const currentLeft = this.fabricObject.left || 0;
|
|
2671
|
+
const canvasWidth = this.canvas.getDimensions().widthPx;
|
|
2672
|
+
// Skip expensive getBoundingBox if object is clearly within bounds
|
|
2673
|
+
if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
|
|
2674
|
+
if (this.getBoundingBox().width > canvasWidth) {
|
|
2675
|
+
this.fabricObject.set({ left: 0, width: canvasWidth });
|
|
2676
|
+
this.redraw();
|
|
2677
|
+
}
|
|
2568
2678
|
}
|
|
2569
2679
|
}
|
|
2570
2680
|
onDelete() {
|
|
2571
2681
|
this.removeBullets();
|
|
2572
2682
|
}
|
|
2573
2683
|
onSelectionChange() {
|
|
2574
|
-
this.updateTextSelection();
|
|
2684
|
+
this.updateTextSelection(false);
|
|
2575
2685
|
}
|
|
2576
2686
|
hasTextSelection() {
|
|
2577
2687
|
return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
|
|
2578
2688
|
}
|
|
2579
|
-
updateTextSelection() {
|
|
2689
|
+
updateTextSelection(notifyCanvas = true) {
|
|
2580
2690
|
if (this.canvas.headless)
|
|
2581
2691
|
return;
|
|
2582
2692
|
this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
|
|
2583
|
-
|
|
2693
|
+
if (notifyCanvas) {
|
|
2694
|
+
this.notifyCanvasOfTextChanges();
|
|
2695
|
+
}
|
|
2584
2696
|
}
|
|
2585
2697
|
notifyCanvasOfTextChanges() {
|
|
2586
|
-
this.
|
|
2698
|
+
if (this.notifyCanvasTimeout) {
|
|
2699
|
+
clearTimeout(this.notifyCanvasTimeout);
|
|
2700
|
+
}
|
|
2701
|
+
this.notifyCanvasTimeout = setTimeout(() => {
|
|
2702
|
+
this.canvas.notifyTextChanged();
|
|
2703
|
+
this.notifyCanvasTimeout = null;
|
|
2704
|
+
}, this.NOTIFY_DELAY_MS);
|
|
2705
|
+
}
|
|
2706
|
+
/**
|
|
2707
|
+
* @override
|
|
2708
|
+
* Provide a simpler bounding box for drag boundary checks that uses the textbox
|
|
2709
|
+
* dimensions directly instead of getBoundingRect(), which includes ordered list
|
|
2710
|
+
* markers that may extend far beyond the text and cause drag shaking issues.
|
|
2711
|
+
*/
|
|
2712
|
+
getSimpleBoundingBox() {
|
|
2713
|
+
return {
|
|
2714
|
+
top: this.fabricObject.top || 0,
|
|
2715
|
+
left: this.fabricObject.left || 0,
|
|
2716
|
+
width: (this.fabricObject.width || 0) * (this.fabricObject.scaleX || 1),
|
|
2717
|
+
height: (this.fabricObject.height || 0) * (this.fabricObject.scaleY || 1)
|
|
2718
|
+
};
|
|
2587
2719
|
}
|
|
2588
2720
|
}
|
|
2589
2721
|
Text.NEW_LINE = "\n";
|
|
@@ -2749,6 +2881,7 @@ class AbstractInteractiveCanvas {
|
|
|
2749
2881
|
this.devicePixelRatio = devicePixelRatio;
|
|
2750
2882
|
this.fontLibrary = new FontLibrary();
|
|
2751
2883
|
this.dimensionChangeRejections$ = new rxjs.Subject();
|
|
2884
|
+
this.redrawScheduled = false;
|
|
2752
2885
|
this.IMAGE_CAPTURE_COEFFICIENT = this.calculateImageCaptureCoefficient();
|
|
2753
2886
|
/**
|
|
2754
2887
|
* Fabric defaults to only 2 decimal places when saving, which causes havoc on the scaling of large images.
|
|
@@ -2770,8 +2903,8 @@ class AbstractInteractiveCanvas {
|
|
|
2770
2903
|
//Configure canvas behaviour
|
|
2771
2904
|
this.fabricCanvas.stateful = true;
|
|
2772
2905
|
this.fabricCanvas.preserveObjectStacking = true;
|
|
2773
|
-
this.fabricCanvas.perPixelTargetFind =
|
|
2774
|
-
this.fabricCanvas.targetFindTolerance =
|
|
2906
|
+
this.fabricCanvas.perPixelTargetFind = false;
|
|
2907
|
+
this.fabricCanvas.targetFindTolerance = 0;
|
|
2775
2908
|
this.setBackgroundColor("#FFFFFF");
|
|
2776
2909
|
}
|
|
2777
2910
|
/**
|
|
@@ -3159,15 +3292,14 @@ class AbstractInteractiveCanvas {
|
|
|
3159
3292
|
fontFamily: profile.defaultFont,
|
|
3160
3293
|
textAlign: profile.defaultTextAlignment,
|
|
3161
3294
|
hoverCursor: "move",
|
|
3162
|
-
moveCursor:
|
|
3295
|
+
moveCursor: "move",
|
|
3296
|
+
editable: false,
|
|
3297
|
+
editingBorderColor: '#2196F3',
|
|
3163
3298
|
multiLine: true,
|
|
3164
3299
|
lineHeight: profile.defaultLineHeight,
|
|
3165
3300
|
charSpacing: profile.defaultCharacterSpacing,
|
|
3166
|
-
perPixelTargetFind: false,
|
|
3167
|
-
targetFindTolerance: 4,
|
|
3168
3301
|
splitByGrapheme: true,
|
|
3169
|
-
|
|
3170
|
-
padding: 0,
|
|
3302
|
+
padding: 0, // Increased from 0 for better hover detection in Fabric v7
|
|
3171
3303
|
cornerSize: 8,
|
|
3172
3304
|
touchCornerSize: 12
|
|
3173
3305
|
};
|
|
@@ -3618,7 +3750,14 @@ class AbstractInteractiveCanvas {
|
|
|
3618
3750
|
* Redraw the canvas.
|
|
3619
3751
|
*/
|
|
3620
3752
|
redraw() {
|
|
3621
|
-
this.
|
|
3753
|
+
if (this.redrawScheduled) {
|
|
3754
|
+
return;
|
|
3755
|
+
}
|
|
3756
|
+
this.redrawScheduled = true;
|
|
3757
|
+
requestAnimationFrame(() => {
|
|
3758
|
+
this.fabricCanvas.requestRenderAll();
|
|
3759
|
+
this.redrawScheduled = false;
|
|
3760
|
+
});
|
|
3622
3761
|
}
|
|
3623
3762
|
syncObjectList() {
|
|
3624
3763
|
const newObjectList = _(this.fabricCanvas.getObjects())
|
|
@@ -3744,7 +3883,6 @@ class AbstractInteractiveCanvas {
|
|
|
3744
3883
|
* Update the actual dimensions of the canvas by multiplying the requested dimensions by the current zoom factor.
|
|
3745
3884
|
*/
|
|
3746
3885
|
updateCanvasDimensions(dims, zoom) {
|
|
3747
|
-
//const oldWidth = this.fabricCanvas.getWidth();
|
|
3748
3886
|
const width = dims.widthPx * zoom;
|
|
3749
3887
|
const height = dims.heightPx * zoom;
|
|
3750
3888
|
this.fabricCanvas.setZoom(zoom);
|
|
@@ -3752,39 +3890,6 @@ class AbstractInteractiveCanvas {
|
|
|
3752
3890
|
width,
|
|
3753
3891
|
height
|
|
3754
3892
|
});
|
|
3755
|
-
/*if (this.layoutManager$.value instanceof AdvancedLayoutManager) {
|
|
3756
|
-
const scaleFactor = width / oldWidth;
|
|
3757
|
-
//this.fabricCanvas.calcOffset();
|
|
3758
|
-
const objects = this.fabricCanvas.getObjects();
|
|
3759
|
-
objects.forEach((obj: any) => {
|
|
3760
|
-
obj.set({ ₹
|
|
3761
|
-
left: obj.left * scaleFactor,
|
|
3762
|
-
});
|
|
3763
|
-
if (obj.type === "textbox" || obj.type === "text") {
|
|
3764
|
-
obj.set({
|
|
3765
|
-
scaleX: 1,
|
|
3766
|
-
scaleY: 1,
|
|
3767
|
-
// scaleX: obj.scaleX * scaleFactor,
|
|
3768
|
-
// scaleY: obj.scaleY * scaleFactor,
|
|
3769
|
-
width: obj.width * scaleFactor,
|
|
3770
|
-
});
|
|
3771
|
-
// obj.initDimensions();
|
|
3772
|
-
} else if (obj.type === "border" || obj.type === "rect") {
|
|
3773
|
-
obj.set({
|
|
3774
|
-
width: obj.width * scaleFactor,
|
|
3775
|
-
scaleX: 1,
|
|
3776
|
-
strokeUniform: true,
|
|
3777
|
-
// strokeAlign: "inner",
|
|
3778
|
-
});
|
|
3779
|
-
} else {
|
|
3780
|
-
obj.set({
|
|
3781
|
-
scaleX: obj.scaleX * scaleFactor,
|
|
3782
|
-
});
|
|
3783
|
-
}
|
|
3784
|
-
obj.setCoords();
|
|
3785
|
-
});
|
|
3786
|
-
this.fabricCanvas.requestRenderAll();
|
|
3787
|
-
}*/
|
|
3788
3893
|
}
|
|
3789
3894
|
onProfileChanged(profile) {
|
|
3790
3895
|
this.setMultiSelect(profile.allowMultiSelect);
|
|
@@ -3939,7 +4044,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3939
4044
|
this.fabricCanvas.on("selection:created", (event) => this.onFabricSelectionChanged(event));
|
|
3940
4045
|
this.fabricCanvas.on("selection:cleared", (event) => this.onFabricSelectionChanged(event));
|
|
3941
4046
|
this.fabricCanvas.on("text:editing:entered", () => this.onTextEditing());
|
|
3942
|
-
this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
|
|
3943
4047
|
this.fabricCanvas.on("text:editing:exited", (event) => {
|
|
3944
4048
|
this.onTextEditing();
|
|
3945
4049
|
const target = event.target;
|
|
@@ -3948,8 +4052,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3948
4052
|
}
|
|
3949
4053
|
});
|
|
3950
4054
|
this.fabricCanvas.on("text:changed", () => this.onTextChange());
|
|
3951
|
-
// Handle double-click on textbox to enter editing mode
|
|
3952
|
-
// @ts-ignore
|
|
3953
4055
|
this.fabricCanvas.on("mouse:dblclick", (event) => this.onDoubleClick(event));
|
|
3954
4056
|
}
|
|
3955
4057
|
/**
|
|
@@ -3984,7 +4086,7 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
3984
4086
|
selection.setEvent(event);
|
|
3985
4087
|
}
|
|
3986
4088
|
this.selection$.next(selection);
|
|
3987
|
-
this.enterTextSelectionModeIfRequired();
|
|
4089
|
+
//this.enterTextSelectionModeIfRequired();
|
|
3988
4090
|
}
|
|
3989
4091
|
enterTextSelectionModeIfRequired() {
|
|
3990
4092
|
var _a, _b;
|
|
@@ -4000,13 +4102,14 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
|
|
|
4000
4102
|
this.selection$.next(new SelectionData(selected));
|
|
4001
4103
|
}
|
|
4002
4104
|
onDoubleClick(event) {
|
|
4105
|
+
var _a, _b;
|
|
4003
4106
|
const target = event.target;
|
|
4004
4107
|
if (target && (target.type === 'textbox' || target.type === 'i-text')) {
|
|
4005
|
-
|
|
4108
|
+
const targetIText = target;
|
|
4109
|
+
if (!targetIText.isEditing) {
|
|
4006
4110
|
target.set({ editable: true, hoverCursor: 'text' });
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
this.enterTextSelectionModeIfRequired();
|
|
4111
|
+
(_a = targetIText.enterEditing) === null || _a === void 0 ? void 0 : _a.call(targetIText);
|
|
4112
|
+
(_b = targetIText.hiddenTextarea) === null || _b === void 0 ? void 0 : _b.focus();
|
|
4010
4113
|
}
|
|
4011
4114
|
}
|
|
4012
4115
|
}
|
|
@@ -10,6 +10,7 @@ export declare class AdvancedLayoutManager extends AbstractLayoutManager {
|
|
|
10
10
|
private repositionBackgrounds;
|
|
11
11
|
private resizeCanvas;
|
|
12
12
|
private stackObjects;
|
|
13
|
+
private dimensionsLayoutScheduled;
|
|
13
14
|
constructor(canvas: IInteractiveCanvas);
|
|
14
15
|
onCanvasDimensionsChange(): void;
|
|
15
16
|
onTextChange(): void;
|
|
@@ -205,6 +205,13 @@ export declare abstract class AbstractCanvasObject<T extends FabricObject> imple
|
|
|
205
205
|
protected onDelete(): void;
|
|
206
206
|
protected preventLeavingCanvas(): void;
|
|
207
207
|
protected getCanvasOverflow(): IOverflow;
|
|
208
|
+
protected getCanvasOverflowForBoundaryPrevention(): IOverflow;
|
|
209
|
+
protected getSimpleBoundingBox(): {
|
|
210
|
+
top: number;
|
|
211
|
+
left: number;
|
|
212
|
+
width: number;
|
|
213
|
+
height: number;
|
|
214
|
+
};
|
|
208
215
|
protected centerHorizontallyInternal(): void;
|
|
209
216
|
protected centerVerticallyInternal(): void;
|
|
210
217
|
protected nudgeInternal(x?: number, y?: number): 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
|
|
@@ -321,4 +323,16 @@ export declare class Text extends AbstractCanvasObject<ExtendedFabricTextbox> im
|
|
|
321
323
|
private hasTextSelection;
|
|
322
324
|
private updateTextSelection;
|
|
323
325
|
private notifyCanvasOfTextChanges;
|
|
326
|
+
/**
|
|
327
|
+
* @override
|
|
328
|
+
* Provide a simpler bounding box for drag boundary checks that uses the textbox
|
|
329
|
+
* dimensions directly instead of getBoundingRect(), which includes ordered list
|
|
330
|
+
* markers that may extend far beyond the text and cause drag shaking issues.
|
|
331
|
+
*/
|
|
332
|
+
protected getSimpleBoundingBox(): {
|
|
333
|
+
top: number;
|
|
334
|
+
left: number;
|
|
335
|
+
width: number;
|
|
336
|
+
height: number;
|
|
337
|
+
};
|
|
324
338
|
}
|