@leapfrog/interactive-canvas 2.0.25-beta-7 → 2.0.25-beta-9

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.
@@ -22,4 +22,5 @@ export declare class InteractiveCanvas extends AbstractInteractiveCanvas {
22
22
  private onFabricSelectionChanged;
23
23
  private enterTextSelectionModeIfRequired;
24
24
  private onTextEditing;
25
+ private onDoubleClick;
25
26
  }
@@ -232,12 +232,9 @@ class ResizeCanvasLayoutFunction {
232
232
  const targetHeight = increase.withSize(increase.width, baseDimensions.height + increase.height);
233
233
  if (currentDimensions.height !== targetHeight.height) {
234
234
  canvas.setDimensions(targetHeight);
235
- // Only update coords for objects affected by height change (pinned to bottom, etc.)
236
- // This is much faster than updating all objects on slow machines
237
235
  canvas.getObjectsInternal().forEach((object) => {
238
236
  var _a;
239
237
  const canvasObj = canvas.getObjectsByTypeInternal(object.type).find(o => o.getId() === object.getId());
240
- // Only update coords for objects that are positioned relative to canvas bottom or that might be affected
241
238
  if (canvasObj && (((_a = canvasObj.isPinToBottom) === null || _a === void 0 ? void 0 : _a.call(canvasObj)) || object.top > currentDimensions.heightPx * 0.5)) {
242
239
  object.setCoords();
243
240
  }
@@ -274,7 +271,6 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
274
271
  this.dimensionsLayoutScheduled = false;
275
272
  }
276
273
  onCanvasDimensionsChange() {
277
- // Batch dimension changes to avoid redundant layout passes
278
274
  if (this.dimensionsLayoutScheduled) {
279
275
  return;
280
276
  }
@@ -314,9 +310,7 @@ class StackObjectsLayoutFunction {
314
310
  const footerObjects = objects[0];
315
311
  const bodyObjects = objects[1];
316
312
  const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
317
- //canvas.redraw();
318
- // No need to redraw here - it will be called by the layout manager after all layout functions complete
319
- // This reduces redundant render passes significantly
313
+ // canvas.redraw();
320
314
  //Make one pass and set the widths of all elements. This will force objects with dynamic heights (like text) to take on their necessary height before trying to lay them out.
321
315
  this.applyWidths(allObjects, bodyWidth);
322
316
  const bodyRows = this.getRows(bodyObjects);
@@ -403,6 +397,7 @@ class StackObjectsLayoutFunction {
403
397
  object.setPosition({
404
398
  width: actualWidth
405
399
  });
400
+ //object.updateDimensions();
406
401
  _(objects).forEach((object) => {
407
402
  if (object.getType() !== ObjectType.VERTICAL_RULE) {
408
403
  object.updateDimensions();
@@ -522,12 +517,10 @@ class StandardLayoutManager extends AbstractLayoutManager {
522
517
  this.stackAndRedraw();
523
518
  }
524
519
  stackAndRedraw() {
525
- // Batch multiple layout requests into a single pass
526
520
  if (this.layoutBatchScheduled) {
527
521
  return;
528
522
  }
529
523
  this.layoutBatchScheduled = true;
530
- // Use microtask to batch synchronous calls, but still execute before next frame
531
524
  Promise.resolve().then(() => {
532
525
  this.stackObjects.execute(this.canvas);
533
526
  this.repositionCutoffs.execute(this.canvas);
@@ -1814,19 +1807,43 @@ var ObjectType;
1814
1807
  class Text extends AbstractCanvasObject {
1815
1808
  constructor(canvas, fabricObject, persistedField) {
1816
1809
  super(ObjectType.TEXT, canvas, fabricObject, persistedField);
1817
- //private notifyCanvasTimeout: NodeJS.Timeout | null = null;
1818
1810
  this.notifyCanvasTimeout = null;
1819
- this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
1811
+ this.NOTIFY_DELAY_MS = 150;
1820
1812
  this.canvas = canvas;
1821
1813
  this.fontLibrary = canvas.getFontLibrary();
1822
1814
  this.baseComponentHeight = this.fabricObject.height;
1823
1815
  this.text$ = new BehaviorSubject(this.fabricObject.text);
1824
1816
  this.data.userText = this.fabricObject.text;
1825
1817
  this.textSelection$ = new BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
1826
- console.log(`inside constructor in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
1827
- this.fabricObject.set({ strokeWidth: 0 });
1818
+ //this.fabricObject.set({strokeWidth: 0});
1819
+ this.fabricObject.set({
1820
+ strokeWidth: 0,
1821
+ perPixelTargetFind: false // Disable per-pixel detection for this object
1822
+ });
1828
1823
  this.fabricObject.on("changed", () => this.onTextChange());
1829
1824
  this.fabricObject.on("selection:changed", () => this.onSelectionChange());
1825
+ this.fabricObject.containsPoint.bind(this.fabricObject);
1826
+ this.fabricObject.containsPoint = function (point) {
1827
+ const coords = this.getCoords();
1828
+ if (coords.length !== 4)
1829
+ return false;
1830
+ let inside = false;
1831
+ for (let i = 0, j = coords.length - 1; i < coords.length; j = i++) {
1832
+ const xi = coords[i].x, yi = coords[i].y;
1833
+ const xj = coords[j].x, yj = coords[j].y;
1834
+ const intersect = ((yi > point.y) !== (yj > point.y))
1835
+ && (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
1836
+ if (intersect)
1837
+ inside = !inside;
1838
+ }
1839
+ return inside;
1840
+ };
1841
+ this.fabricObject.on("mousedown", (e) => {
1842
+ var _a;
1843
+ if (!this.fabricObject.isEditing) {
1844
+ (_a = e.e) === null || _a === void 0 ? void 0 : _a.preventDefault();
1845
+ }
1846
+ });
1830
1847
  this.applyBulletListMonkeyPatches();
1831
1848
  }
1832
1849
  /**
@@ -2546,9 +2563,16 @@ class Text extends AbstractCanvasObject {
2546
2563
  const profile = this.canvas.getProfile();
2547
2564
  const allowManualPositioning = !this.canvas.getLayoutManager().isEnforcedPositioning;
2548
2565
  this.fabricObject.set({
2566
+ lockMovementX: false,
2567
+ lockMovementY: false,
2549
2568
  lockScalingY: true,
2550
2569
  lockScalingX: this.isPinToBottom(),
2551
- lockScalingFlip: true
2570
+ lockScalingFlip: true,
2571
+ lockRotation: true,
2572
+ selectable: true,
2573
+ editable: false, // Only enable on double-click
2574
+ hoverCursor: "move", // Show move cursor by default
2575
+ moveCursor: "move",
2552
2576
  });
2553
2577
  this.fabricObject.setControlsVisibility({
2554
2578
  tl: false,
@@ -2558,59 +2582,49 @@ class Text extends AbstractCanvasObject {
2558
2582
  ml: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
2559
2583
  mt: false,
2560
2584
  mr: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
2561
- mb: false
2585
+ mb: false,
2586
+ mtr: false
2562
2587
  });
2563
2588
  this.redraw();
2564
2589
  }
2565
2590
  onTextChange() {
2566
- console.log(`inside onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2567
2591
  this.data.userText = this.fabricObject.text;
2568
2592
  this.text$.next(this.fabricObject.text);
2569
- // Only check width bounds if text is close to canvas edge (performance optimization)
2570
2593
  const currentLeft = this.fabricObject.left || 0;
2571
2594
  const canvasWidth = this.canvas.getDimensions().widthPx;
2572
2595
  // Skip expensive getBoundingBox if object is clearly within bounds
2573
2596
  if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
2574
- console.log(`inside if onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2575
2597
  if (this.getBoundingBox().width > canvasWidth) {
2576
2598
  this.fabricObject.set({ left: 0, width: canvasWidth });
2577
2599
  this.redraw();
2578
2600
  }
2579
2601
  }
2580
- console.log(`end of onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2581
2602
  }
2582
2603
  onDelete() {
2583
2604
  this.removeBullets();
2584
2605
  }
2585
2606
  onSelectionChange() {
2586
- console.log(`start of onSelectionChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2587
2607
  this.updateTextSelection(false);
2588
2608
  }
2589
2609
  hasTextSelection() {
2590
2610
  return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
2591
2611
  }
2592
2612
  updateTextSelection(notifyCanvas = true) {
2593
- console.log(`start of updateTextSelection in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2594
2613
  if (this.canvas.headless)
2595
2614
  return;
2596
2615
  this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
2597
2616
  if (notifyCanvas) {
2598
- console.log(`inside if in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2599
2617
  this.notifyCanvasOfTextChanges();
2600
2618
  }
2601
2619
  }
2602
2620
  notifyCanvasOfTextChanges() {
2603
- console.log(`start of notifyCanvasOfTextChanges in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2604
2621
  if (this.notifyCanvasTimeout) {
2605
- console.log(`inside timeout if in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2606
2622
  clearTimeout(this.notifyCanvasTimeout);
2607
2623
  }
2608
2624
  this.notifyCanvasTimeout = setTimeout(() => {
2609
- console.log(`start of setTimeout in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2610
2625
  this.canvas.notifyTextChanged();
2611
2626
  this.notifyCanvasTimeout = null;
2612
2627
  }, this.NOTIFY_DELAY_MS);
2613
- console.log(`end of notifyCanvasOfTextChanges in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2614
2628
  }
2615
2629
  }
2616
2630
  Text.NEW_LINE = "\n";
@@ -2618,7 +2632,6 @@ class TextSelection {
2618
2632
  constructor(fabricObject, fontLibrary) {
2619
2633
  this.fabricObject = fabricObject;
2620
2634
  this.fontLibrary = fontLibrary;
2621
- console.log(`constructor of textselection in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2622
2635
  this.start = this.fabricObject.selectionStart;
2623
2636
  this.end = this.fabricObject.selectionEnd;
2624
2637
  this.length = this.end - this.start;
@@ -2800,7 +2813,7 @@ class AbstractInteractiveCanvas {
2800
2813
  this.fabricCanvas.stateful = true;
2801
2814
  this.fabricCanvas.preserveObjectStacking = true;
2802
2815
  this.fabricCanvas.perPixelTargetFind = true;
2803
- this.fabricCanvas.targetFindTolerance = 10;
2816
+ this.fabricCanvas.targetFindTolerance = 4;
2804
2817
  this.setBackgroundColor("#FFFFFF");
2805
2818
  }
2806
2819
  /**
@@ -3194,7 +3207,12 @@ class AbstractInteractiveCanvas {
3194
3207
  charSpacing: profile.defaultCharacterSpacing,
3195
3208
  perPixelTargetFind: false,
3196
3209
  targetFindTolerance: 4,
3197
- splitByGrapheme: true
3210
+ splitByGrapheme: true,
3211
+ editable: false, // Only enable on double-click
3212
+ editingBorderColor: '#2196F3',
3213
+ padding: 0,
3214
+ cornerSize: 8,
3215
+ touchCornerSize: 12
3198
3216
  };
3199
3217
  const fabricTextbox = new this.fabric.Textbox("Text goes here", textOptions);
3200
3218
  this.fabricCanvas.add(fabricTextbox);
@@ -3643,12 +3661,10 @@ class AbstractInteractiveCanvas {
3643
3661
  * Redraw the canvas.
3644
3662
  */
3645
3663
  redraw() {
3646
- //this.fabricCanvas.requestRenderAll();
3647
3664
  if (this.redrawScheduled) {
3648
- return; // Already scheduled, don't schedule again
3665
+ return;
3649
3666
  }
3650
3667
  this.redrawScheduled = true;
3651
- // Use requestAnimationFrame for smoother rendering and automatic batching
3652
3668
  requestAnimationFrame(() => {
3653
3669
  this.fabricCanvas.requestRenderAll();
3654
3670
  this.redrawScheduled = false;
@@ -3778,6 +3794,7 @@ class AbstractInteractiveCanvas {
3778
3794
  * Update the actual dimensions of the canvas by multiplying the requested dimensions by the current zoom factor.
3779
3795
  */
3780
3796
  updateCanvasDimensions(dims, zoom) {
3797
+ const oldWidth = this.fabricCanvas.getWidth();
3781
3798
  const width = dims.widthPx * zoom;
3782
3799
  const height = dims.heightPx * zoom;
3783
3800
  this.fabricCanvas.setZoom(zoom);
@@ -3785,6 +3802,34 @@ class AbstractInteractiveCanvas {
3785
3802
  width,
3786
3803
  height
3787
3804
  });
3805
+ const scaleFactor = width / oldWidth;
3806
+ const objects = this.fabricCanvas.getObjects();
3807
+ objects.forEach((obj) => {
3808
+ obj.set({
3809
+ left: obj.left * scaleFactor,
3810
+ });
3811
+ if (obj.type === "textbox" || obj.type === "text") {
3812
+ obj.set({
3813
+ scaleX: 1,
3814
+ scaleY: 1,
3815
+ width: obj.width * scaleFactor,
3816
+ });
3817
+ }
3818
+ else if (obj.type === "border" || obj.type === "rect") {
3819
+ obj.set({
3820
+ width: obj.width * scaleFactor,
3821
+ scaleX: 1,
3822
+ strokeUniform: true,
3823
+ });
3824
+ }
3825
+ else {
3826
+ obj.set({
3827
+ scaleX: obj.scaleX * scaleFactor,
3828
+ });
3829
+ }
3830
+ obj.setCoords();
3831
+ });
3832
+ this.fabricCanvas.requestRenderAll();
3788
3833
  }
3789
3834
  onProfileChanged(profile) {
3790
3835
  this.setMultiSelect(profile.allowMultiSelect);
@@ -3939,8 +3984,18 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3939
3984
  this.fabricCanvas.on("selection:created", (event) => this.onFabricSelectionChanged(event));
3940
3985
  this.fabricCanvas.on("selection:cleared", (event) => this.onFabricSelectionChanged(event));
3941
3986
  this.fabricCanvas.on("text:editing:entered", () => this.onTextEditing());
3942
- this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
3987
+ //this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
3988
+ this.fabricCanvas.on("text:editing:exited", (event) => {
3989
+ this.onTextEditing();
3990
+ const target = event.target;
3991
+ if (target && (target.type === 'textbox' || target.type === 'i-text')) {
3992
+ target.set({ editable: false, hoverCursor: 'move' });
3993
+ }
3994
+ });
3943
3995
  this.fabricCanvas.on("text:changed", () => this.onTextChange());
3996
+ // Handle double-click on textbox to enter editing mode
3997
+ // @ts-ignore
3998
+ this.fabricCanvas.on("mouse:dblclick", (event) => this.onDoubleClick(event));
3944
3999
  }
3945
4000
  /**
3946
4001
  * @override
@@ -3968,7 +4023,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3968
4023
  });
3969
4024
  }
3970
4025
  onFabricSelectionChanged(event) {
3971
- console.log(`start of onfabricselectionchange in ic.ts - ${MouseEvent}- ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
3972
4026
  const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
3973
4027
  const selection = new SelectionData(selected);
3974
4028
  if (event) {
@@ -3979,7 +4033,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3979
4033
  }
3980
4034
  enterTextSelectionModeIfRequired() {
3981
4035
  var _a, _b;
3982
- console.log(`start of enterTextSelectionModeIfRequired in IC.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
3983
4036
  if (!this.profile$.value.enterTextEditImmediatelyOnSelect && !this.layoutManager$.value.enterTextEditImmediatelyOnSelect)
3984
4037
  return;
3985
4038
  if (((_b = (_a = this.selection$.value) === null || _a === void 0 ? void 0 : _a.objects[0]) === null || _b === void 0 ? void 0 : _b.getType()) !== ObjectType.TEXT)
@@ -3988,10 +4041,20 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3988
4041
  text.enterEditing();
3989
4042
  }
3990
4043
  onTextEditing() {
3991
- console.log(`start of onTextEditing in IC.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
3992
4044
  const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
3993
4045
  this.selection$.next(new SelectionData(selected));
3994
4046
  }
4047
+ onDoubleClick(event) {
4048
+ const target = event.target;
4049
+ if (target && (target.type === 'textbox' || target.type === 'i-text')) {
4050
+ if (!target.isEditing) {
4051
+ target.set({ editable: true, hoverCursor: 'text' });
4052
+ // target.enterEditing();
4053
+ // target.hiddenTextarea?.focus();
4054
+ this.enterTextSelectionModeIfRequired();
4055
+ }
4056
+ }
4057
+ }
3995
4058
  }
3996
4059
 
3997
4060
  export { AbstractCanvasObject, AbstractInteractiveCanvas, AbstractLayoutManager, AdvancedLayoutManager, BackgroundImage, Border, CanvasObjectData, Cutoff, DefaultCanvasProfile, Font, FontLibrary, HeadlessInteractiveCanvas, HorizontalRule, Image, ImageType, InteractiveCanvas, LayoutManager, MillimeterDimensions, ObjectType, PixelCanvasDimensions, Print6ColCanvasDimensions, Print8ColCanvasDimensions, Rectangle, RichText, SelectionData, StandardLayoutManager, Text, UNKNOWN_FONT, UnknownFont, VerticalRule, isMutableBackgroundColor, isMutableColor, isMutableImage, isMutablePosition, isMutableStroke, isMutableText, isMutableTextStyle };
@@ -253,12 +253,9 @@ class ResizeCanvasLayoutFunction {
253
253
  const targetHeight = increase.withSize(increase.width, baseDimensions.height + increase.height);
254
254
  if (currentDimensions.height !== targetHeight.height) {
255
255
  canvas.setDimensions(targetHeight);
256
- // Only update coords for objects affected by height change (pinned to bottom, etc.)
257
- // This is much faster than updating all objects on slow machines
258
256
  canvas.getObjectsInternal().forEach((object) => {
259
257
  var _a;
260
258
  const canvasObj = canvas.getObjectsByTypeInternal(object.type).find(o => o.getId() === object.getId());
261
- // Only update coords for objects that are positioned relative to canvas bottom or that might be affected
262
259
  if (canvasObj && (((_a = canvasObj.isPinToBottom) === null || _a === void 0 ? void 0 : _a.call(canvasObj)) || object.top > currentDimensions.heightPx * 0.5)) {
263
260
  object.setCoords();
264
261
  }
@@ -295,7 +292,6 @@ class AdvancedLayoutManager extends AbstractLayoutManager {
295
292
  this.dimensionsLayoutScheduled = false;
296
293
  }
297
294
  onCanvasDimensionsChange() {
298
- // Batch dimension changes to avoid redundant layout passes
299
295
  if (this.dimensionsLayoutScheduled) {
300
296
  return;
301
297
  }
@@ -335,9 +331,7 @@ class StackObjectsLayoutFunction {
335
331
  const footerObjects = objects[0];
336
332
  const bodyObjects = objects[1];
337
333
  const bodyWidth = canvasDimensions.widthPx - canvasMargin.right - canvasMargin.left;
338
- //canvas.redraw();
339
- // No need to redraw here - it will be called by the layout manager after all layout functions complete
340
- // This reduces redundant render passes significantly
334
+ // canvas.redraw();
341
335
  //Make one pass and set the widths of all elements. This will force objects with dynamic heights (like text) to take on their necessary height before trying to lay them out.
342
336
  this.applyWidths(allObjects, bodyWidth);
343
337
  const bodyRows = this.getRows(bodyObjects);
@@ -424,6 +418,7 @@ class StackObjectsLayoutFunction {
424
418
  object.setPosition({
425
419
  width: actualWidth
426
420
  });
421
+ //object.updateDimensions();
427
422
  _(objects).forEach((object) => {
428
423
  if (object.getType() !== exports.ObjectType.VERTICAL_RULE) {
429
424
  object.updateDimensions();
@@ -543,12 +538,10 @@ class StandardLayoutManager extends AbstractLayoutManager {
543
538
  this.stackAndRedraw();
544
539
  }
545
540
  stackAndRedraw() {
546
- // Batch multiple layout requests into a single pass
547
541
  if (this.layoutBatchScheduled) {
548
542
  return;
549
543
  }
550
544
  this.layoutBatchScheduled = true;
551
- // Use microtask to batch synchronous calls, but still execute before next frame
552
545
  Promise.resolve().then(() => {
553
546
  this.stackObjects.execute(this.canvas);
554
547
  this.repositionCutoffs.execute(this.canvas);
@@ -1835,19 +1828,43 @@ exports.ObjectType = void 0;
1835
1828
  class Text extends AbstractCanvasObject {
1836
1829
  constructor(canvas, fabricObject, persistedField) {
1837
1830
  super(exports.ObjectType.TEXT, canvas, fabricObject, persistedField);
1838
- //private notifyCanvasTimeout: NodeJS.Timeout | null = null;
1839
1831
  this.notifyCanvasTimeout = null;
1840
- this.NOTIFY_DELAY_MS = 150; // Debounce canvas notifications
1832
+ this.NOTIFY_DELAY_MS = 150;
1841
1833
  this.canvas = canvas;
1842
1834
  this.fontLibrary = canvas.getFontLibrary();
1843
1835
  this.baseComponentHeight = this.fabricObject.height;
1844
1836
  this.text$ = new rxjs.BehaviorSubject(this.fabricObject.text);
1845
1837
  this.data.userText = this.fabricObject.text;
1846
1838
  this.textSelection$ = new rxjs.BehaviorSubject(new TextSelection(this.fabricObject, this.fontLibrary));
1847
- console.log(`inside constructor in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
1848
- this.fabricObject.set({ strokeWidth: 0 });
1839
+ //this.fabricObject.set({strokeWidth: 0});
1840
+ this.fabricObject.set({
1841
+ strokeWidth: 0,
1842
+ perPixelTargetFind: false // Disable per-pixel detection for this object
1843
+ });
1849
1844
  this.fabricObject.on("changed", () => this.onTextChange());
1850
1845
  this.fabricObject.on("selection:changed", () => this.onSelectionChange());
1846
+ this.fabricObject.containsPoint.bind(this.fabricObject);
1847
+ this.fabricObject.containsPoint = function (point) {
1848
+ const coords = this.getCoords();
1849
+ if (coords.length !== 4)
1850
+ return false;
1851
+ let inside = false;
1852
+ for (let i = 0, j = coords.length - 1; i < coords.length; j = i++) {
1853
+ const xi = coords[i].x, yi = coords[i].y;
1854
+ const xj = coords[j].x, yj = coords[j].y;
1855
+ const intersect = ((yi > point.y) !== (yj > point.y))
1856
+ && (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
1857
+ if (intersect)
1858
+ inside = !inside;
1859
+ }
1860
+ return inside;
1861
+ };
1862
+ this.fabricObject.on("mousedown", (e) => {
1863
+ var _a;
1864
+ if (!this.fabricObject.isEditing) {
1865
+ (_a = e.e) === null || _a === void 0 ? void 0 : _a.preventDefault();
1866
+ }
1867
+ });
1851
1868
  this.applyBulletListMonkeyPatches();
1852
1869
  }
1853
1870
  /**
@@ -2567,9 +2584,16 @@ class Text extends AbstractCanvasObject {
2567
2584
  const profile = this.canvas.getProfile();
2568
2585
  const allowManualPositioning = !this.canvas.getLayoutManager().isEnforcedPositioning;
2569
2586
  this.fabricObject.set({
2587
+ lockMovementX: false,
2588
+ lockMovementY: false,
2570
2589
  lockScalingY: true,
2571
2590
  lockScalingX: this.isPinToBottom(),
2572
- lockScalingFlip: true
2591
+ lockScalingFlip: true,
2592
+ lockRotation: true,
2593
+ selectable: true,
2594
+ editable: false, // Only enable on double-click
2595
+ hoverCursor: "move", // Show move cursor by default
2596
+ moveCursor: "move",
2573
2597
  });
2574
2598
  this.fabricObject.setControlsVisibility({
2575
2599
  tl: false,
@@ -2579,59 +2603,49 @@ class Text extends AbstractCanvasObject {
2579
2603
  ml: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
2580
2604
  mt: false,
2581
2605
  mr: allowManualPositioning && profile.allowScale && !this.isPinToBottom(),
2582
- mb: false
2606
+ mb: false,
2607
+ mtr: false
2583
2608
  });
2584
2609
  this.redraw();
2585
2610
  }
2586
2611
  onTextChange() {
2587
- console.log(`inside onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2588
2612
  this.data.userText = this.fabricObject.text;
2589
2613
  this.text$.next(this.fabricObject.text);
2590
- // Only check width bounds if text is close to canvas edge (performance optimization)
2591
2614
  const currentLeft = this.fabricObject.left || 0;
2592
2615
  const canvasWidth = this.canvas.getDimensions().widthPx;
2593
2616
  // Skip expensive getBoundingBox if object is clearly within bounds
2594
2617
  if (currentLeft + (this.fabricObject.width || 0) > canvasWidth * 0.9) {
2595
- console.log(`inside if onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2596
2618
  if (this.getBoundingBox().width > canvasWidth) {
2597
2619
  this.fabricObject.set({ left: 0, width: canvasWidth });
2598
2620
  this.redraw();
2599
2621
  }
2600
2622
  }
2601
- console.log(`end of onTextChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2602
2623
  }
2603
2624
  onDelete() {
2604
2625
  this.removeBullets();
2605
2626
  }
2606
2627
  onSelectionChange() {
2607
- console.log(`start of onSelectionChange in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2608
2628
  this.updateTextSelection(false);
2609
2629
  }
2610
2630
  hasTextSelection() {
2611
2631
  return this.canvas.getSelection().isText && this.textSelection$.value.length > 0;
2612
2632
  }
2613
2633
  updateTextSelection(notifyCanvas = true) {
2614
- console.log(`start of updateTextSelection in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2615
2634
  if (this.canvas.headless)
2616
2635
  return;
2617
2636
  this.textSelection$.next(new TextSelection(this.fabricObject, this.fontLibrary));
2618
2637
  if (notifyCanvas) {
2619
- console.log(`inside if in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2620
2638
  this.notifyCanvasOfTextChanges();
2621
2639
  }
2622
2640
  }
2623
2641
  notifyCanvasOfTextChanges() {
2624
- console.log(`start of notifyCanvasOfTextChanges in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2625
2642
  if (this.notifyCanvasTimeout) {
2626
- console.log(`inside timeout if in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2627
2643
  clearTimeout(this.notifyCanvasTimeout);
2628
2644
  }
2629
2645
  this.notifyCanvasTimeout = setTimeout(() => {
2630
- console.log(`start of setTimeout in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2631
2646
  this.canvas.notifyTextChanged();
2632
2647
  this.notifyCanvasTimeout = null;
2633
2648
  }, this.NOTIFY_DELAY_MS);
2634
- console.log(`end of notifyCanvasOfTextChanges in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2635
2649
  }
2636
2650
  }
2637
2651
  Text.NEW_LINE = "\n";
@@ -2639,7 +2653,6 @@ class TextSelection {
2639
2653
  constructor(fabricObject, fontLibrary) {
2640
2654
  this.fabricObject = fabricObject;
2641
2655
  this.fontLibrary = fontLibrary;
2642
- console.log(`constructor of textselection in text.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
2643
2656
  this.start = this.fabricObject.selectionStart;
2644
2657
  this.end = this.fabricObject.selectionEnd;
2645
2658
  this.length = this.end - this.start;
@@ -2821,7 +2834,7 @@ class AbstractInteractiveCanvas {
2821
2834
  this.fabricCanvas.stateful = true;
2822
2835
  this.fabricCanvas.preserveObjectStacking = true;
2823
2836
  this.fabricCanvas.perPixelTargetFind = true;
2824
- this.fabricCanvas.targetFindTolerance = 10;
2837
+ this.fabricCanvas.targetFindTolerance = 4;
2825
2838
  this.setBackgroundColor("#FFFFFF");
2826
2839
  }
2827
2840
  /**
@@ -3215,7 +3228,12 @@ class AbstractInteractiveCanvas {
3215
3228
  charSpacing: profile.defaultCharacterSpacing,
3216
3229
  perPixelTargetFind: false,
3217
3230
  targetFindTolerance: 4,
3218
- splitByGrapheme: true
3231
+ splitByGrapheme: true,
3232
+ editable: false, // Only enable on double-click
3233
+ editingBorderColor: '#2196F3',
3234
+ padding: 0,
3235
+ cornerSize: 8,
3236
+ touchCornerSize: 12
3219
3237
  };
3220
3238
  const fabricTextbox = new this.fabric.Textbox("Text goes here", textOptions);
3221
3239
  this.fabricCanvas.add(fabricTextbox);
@@ -3664,12 +3682,10 @@ class AbstractInteractiveCanvas {
3664
3682
  * Redraw the canvas.
3665
3683
  */
3666
3684
  redraw() {
3667
- //this.fabricCanvas.requestRenderAll();
3668
3685
  if (this.redrawScheduled) {
3669
- return; // Already scheduled, don't schedule again
3686
+ return;
3670
3687
  }
3671
3688
  this.redrawScheduled = true;
3672
- // Use requestAnimationFrame for smoother rendering and automatic batching
3673
3689
  requestAnimationFrame(() => {
3674
3690
  this.fabricCanvas.requestRenderAll();
3675
3691
  this.redrawScheduled = false;
@@ -3799,6 +3815,7 @@ class AbstractInteractiveCanvas {
3799
3815
  * Update the actual dimensions of the canvas by multiplying the requested dimensions by the current zoom factor.
3800
3816
  */
3801
3817
  updateCanvasDimensions(dims, zoom) {
3818
+ const oldWidth = this.fabricCanvas.getWidth();
3802
3819
  const width = dims.widthPx * zoom;
3803
3820
  const height = dims.heightPx * zoom;
3804
3821
  this.fabricCanvas.setZoom(zoom);
@@ -3806,6 +3823,34 @@ class AbstractInteractiveCanvas {
3806
3823
  width,
3807
3824
  height
3808
3825
  });
3826
+ const scaleFactor = width / oldWidth;
3827
+ const objects = this.fabricCanvas.getObjects();
3828
+ objects.forEach((obj) => {
3829
+ obj.set({
3830
+ left: obj.left * scaleFactor,
3831
+ });
3832
+ if (obj.type === "textbox" || obj.type === "text") {
3833
+ obj.set({
3834
+ scaleX: 1,
3835
+ scaleY: 1,
3836
+ width: obj.width * scaleFactor,
3837
+ });
3838
+ }
3839
+ else if (obj.type === "border" || obj.type === "rect") {
3840
+ obj.set({
3841
+ width: obj.width * scaleFactor,
3842
+ scaleX: 1,
3843
+ strokeUniform: true,
3844
+ });
3845
+ }
3846
+ else {
3847
+ obj.set({
3848
+ scaleX: obj.scaleX * scaleFactor,
3849
+ });
3850
+ }
3851
+ obj.setCoords();
3852
+ });
3853
+ this.fabricCanvas.requestRenderAll();
3809
3854
  }
3810
3855
  onProfileChanged(profile) {
3811
3856
  this.setMultiSelect(profile.allowMultiSelect);
@@ -3960,8 +4005,18 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3960
4005
  this.fabricCanvas.on("selection:created", (event) => this.onFabricSelectionChanged(event));
3961
4006
  this.fabricCanvas.on("selection:cleared", (event) => this.onFabricSelectionChanged(event));
3962
4007
  this.fabricCanvas.on("text:editing:entered", () => this.onTextEditing());
3963
- this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
4008
+ //this.fabricCanvas.on("text:editing:exited", () => this.onTextEditing());
4009
+ this.fabricCanvas.on("text:editing:exited", (event) => {
4010
+ this.onTextEditing();
4011
+ const target = event.target;
4012
+ if (target && (target.type === 'textbox' || target.type === 'i-text')) {
4013
+ target.set({ editable: false, hoverCursor: 'move' });
4014
+ }
4015
+ });
3964
4016
  this.fabricCanvas.on("text:changed", () => this.onTextChange());
4017
+ // Handle double-click on textbox to enter editing mode
4018
+ // @ts-ignore
4019
+ this.fabricCanvas.on("mouse:dblclick", (event) => this.onDoubleClick(event));
3965
4020
  }
3966
4021
  /**
3967
4022
  * @override
@@ -3989,7 +4044,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
3989
4044
  });
3990
4045
  }
3991
4046
  onFabricSelectionChanged(event) {
3992
- console.log(`start of onfabricselectionchange in ic.ts - ${MouseEvent}- ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
3993
4047
  const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
3994
4048
  const selection = new SelectionData(selected);
3995
4049
  if (event) {
@@ -4000,7 +4054,6 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
4000
4054
  }
4001
4055
  enterTextSelectionModeIfRequired() {
4002
4056
  var _a, _b;
4003
- console.log(`start of enterTextSelectionModeIfRequired in IC.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
4004
4057
  if (!this.profile$.value.enterTextEditImmediatelyOnSelect && !this.layoutManager$.value.enterTextEditImmediatelyOnSelect)
4005
4058
  return;
4006
4059
  if (((_b = (_a = this.selection$.value) === null || _a === void 0 ? void 0 : _a.objects[0]) === null || _b === void 0 ? void 0 : _b.getType()) !== exports.ObjectType.TEXT)
@@ -4009,10 +4062,20 @@ class InteractiveCanvas extends AbstractInteractiveCanvas {
4009
4062
  text.enterEditing();
4010
4063
  }
4011
4064
  onTextEditing() {
4012
- console.log(`start of onTextEditing in IC.ts - ${new Date().toLocaleString()}-${new Date().getMilliseconds()}`);
4013
4065
  const selected = this.getFieldsByCanvasObject(this.fabricCanvas.getActiveObjects());
4014
4066
  this.selection$.next(new SelectionData(selected));
4015
4067
  }
4068
+ onDoubleClick(event) {
4069
+ const target = event.target;
4070
+ if (target && (target.type === 'textbox' || target.type === 'i-text')) {
4071
+ if (!target.isEditing) {
4072
+ target.set({ editable: true, hoverCursor: 'text' });
4073
+ // target.enterEditing();
4074
+ // target.hiddenTextarea?.focus();
4075
+ this.enterTextSelectionModeIfRequired();
4076
+ }
4077
+ }
4078
+ }
4016
4079
  }
4017
4080
 
4018
4081
  exports.AbstractCanvasObject = AbstractCanvasObject;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leapfrog/interactive-canvas",
3
- "version": "2.0.25-beta-7",
3
+ "version": "2.0.25-beta-9",
4
4
  "description": "Leapfrog interactive canvas",
5
5
  "files": [
6
6
  "dist"