@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.
@@ -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
- object.updateDimensions();
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
- this.pinToBottom.execute(this.canvas);
443
- // this.stackObjects.execute(this.canvas);
444
- this.dimensionChange();
445
- this.repositionCutoffs.execute(this.canvas);
446
- this.repositionBorders.execute(this.canvas);
447
- this.repositionBackgrounds.execute(this.canvas);
448
- this.redrawAll.execute(this.canvas);
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
- this.stackObjects.execute(this.canvas);
529
- this.repositionCutoffs.execute(this.canvas);
530
- this.repositionBorders.execute(this.canvas);
531
- this.repositionBackgrounds.execute(this.canvas);
532
- this.updateObjectDimensions.execute(this.canvas);
533
- this.redrawAll.execute(this.canvas);
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.getCanvasOverflow();
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({ strokeWidth: 0 });
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
- let fontSize = 0;
2158
- each(this.fabricObject._textLines, (textLine, lineIndex) => {
2159
- fontSize = Math.max(fontSize, this.fabricObject.getValueOfPropertyAt(lineIndex, 0, "fontSize"));
2160
- });
2161
- return fontSize * 0.6;
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
- let maxValue = max((orderedListMarkerEntities !== null && orderedListMarkerEntities !== void 0 ? orderedListMarkerEntities : [])
2172
- .filter((x) => !!x)
2173
- .map((x) => x.getBoundingRect().width));
2174
- return maxValue ? (maxValue * 1.2) : 0;
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
- if (this.getBoundingBox().width > this.canvas.getDimensions().widthPx) {
2545
- this.fabricObject.set({ left: 0, width: this.canvas.getDimensions().widthPx });
2546
- this.redraw();
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
- this.notifyCanvasOfTextChanges();
2672
+ if (notifyCanvas) {
2673
+ this.notifyCanvasOfTextChanges();
2674
+ }
2563
2675
  }
2564
2676
  notifyCanvasOfTextChanges() {
2565
- this.canvas.notifyTextChanged();
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 = true;
2753
- this.fabricCanvas.targetFindTolerance = 4;
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: 'move',
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
- editable: false, // Only enable on double-click
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.fabricCanvas.requestRenderAll();
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
- if (!target.isEditing) {
4087
+ const targetIText = target;
4088
+ if (!targetIText.isEditing) {
3985
4089
  target.set({ editable: true, hoverCursor: 'text' });
3986
- // target.enterEditing();
3987
- // target.hiddenTextarea?.focus();
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
- object.updateDimensions();
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
- this.pinToBottom.execute(this.canvas);
464
- // this.stackObjects.execute(this.canvas);
465
- this.dimensionChange();
466
- this.repositionCutoffs.execute(this.canvas);
467
- this.repositionBorders.execute(this.canvas);
468
- this.repositionBackgrounds.execute(this.canvas);
469
- this.redrawAll.execute(this.canvas);
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
- 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);
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.getCanvasOverflow();
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({ strokeWidth: 0 });
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
- let fontSize = 0;
2179
- _.each(this.fabricObject._textLines, (textLine, lineIndex) => {
2180
- fontSize = Math.max(fontSize, this.fabricObject.getValueOfPropertyAt(lineIndex, 0, "fontSize"));
2181
- });
2182
- return fontSize * 0.6;
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
- let maxValue = _.max((orderedListMarkerEntities !== null && orderedListMarkerEntities !== void 0 ? orderedListMarkerEntities : [])
2193
- .filter((x) => !!x)
2194
- .map((x) => x.getBoundingRect().width));
2195
- return maxValue ? (maxValue * 1.2) : 0;
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
- if (this.getBoundingBox().width > this.canvas.getDimensions().widthPx) {
2566
- this.fabricObject.set({ left: 0, width: this.canvas.getDimensions().widthPx });
2567
- this.redraw();
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
- this.notifyCanvasOfTextChanges();
2693
+ if (notifyCanvas) {
2694
+ this.notifyCanvasOfTextChanges();
2695
+ }
2584
2696
  }
2585
2697
  notifyCanvasOfTextChanges() {
2586
- this.canvas.notifyTextChanged();
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 = true;
2774
- this.fabricCanvas.targetFindTolerance = 4;
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: 'move',
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
- editable: false, // Only enable on double-click
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.fabricCanvas.requestRenderAll();
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
- if (!target.isEditing) {
4108
+ const targetIText = target;
4109
+ if (!targetIText.isEditing) {
4006
4110
  target.set({ editable: true, hoverCursor: 'text' });
4007
- // target.enterEditing();
4008
- // target.hiddenTextarea?.focus();
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;
@@ -36,5 +36,6 @@ export declare class StandardLayoutManager extends AbstractLayoutManager {
36
36
  * @override
37
37
  */
38
38
  onMarginChange(): void;
39
+ private layoutBatchScheduled;
39
40
  private stackAndRedraw;
40
41
  }
@@ -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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leapfrog/interactive-canvas",
3
- "version": "2.0.25-beta-14",
3
+ "version": "2.0.25-beta-16",
4
4
  "description": "Leapfrog interactive canvas",
5
5
  "files": [
6
6
  "dist"