@nasser-sw/fabric 7.0.1-beta10 → 7.0.1-beta12

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.
Files changed (47) hide show
  1. package/dist/index.js +177 -117
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.min.js +1 -1
  4. package/dist/index.min.js.map +1 -1
  5. package/dist/index.min.mjs +1 -1
  6. package/dist/index.min.mjs.map +1 -1
  7. package/dist/index.mjs +177 -117
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/index.node.cjs +177 -117
  10. package/dist/index.node.cjs.map +1 -1
  11. package/dist/index.node.mjs +177 -117
  12. package/dist/index.node.mjs.map +1 -1
  13. package/dist/package.json.min.mjs +1 -1
  14. package/dist/package.json.mjs +1 -1
  15. package/dist/src/shapes/IText/ITextBehavior.d.ts.map +1 -1
  16. package/dist/src/shapes/IText/ITextBehavior.min.mjs +1 -1
  17. package/dist/src/shapes/IText/ITextBehavior.min.mjs.map +1 -1
  18. package/dist/src/shapes/IText/ITextBehavior.mjs +10 -0
  19. package/dist/src/shapes/IText/ITextBehavior.mjs.map +1 -1
  20. package/dist/src/shapes/IText/ITextKeyBehavior.d.ts.map +1 -1
  21. package/dist/src/shapes/IText/ITextKeyBehavior.min.mjs +1 -1
  22. package/dist/src/shapes/IText/ITextKeyBehavior.min.mjs.map +1 -1
  23. package/dist/src/shapes/IText/ITextKeyBehavior.mjs +162 -116
  24. package/dist/src/shapes/IText/ITextKeyBehavior.mjs.map +1 -1
  25. package/dist/src/shapes/Text/Text.d.ts.map +1 -1
  26. package/dist/src/shapes/Text/Text.min.mjs +1 -1
  27. package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
  28. package/dist/src/shapes/Text/Text.mjs +4 -0
  29. package/dist/src/shapes/Text/Text.mjs.map +1 -1
  30. package/dist/src/shapes/Text/constants.d.ts +1 -1
  31. package/dist/src/shapes/Text/constants.d.ts.map +1 -1
  32. package/dist/src/shapes/Text/constants.min.mjs +1 -1
  33. package/dist/src/shapes/Text/constants.min.mjs.map +1 -1
  34. package/dist/src/shapes/Text/constants.mjs +1 -1
  35. package/dist/src/shapes/Text/constants.mjs.map +1 -1
  36. package/dist-extensions/src/shapes/IText/ITextBehavior.d.ts.map +1 -1
  37. package/dist-extensions/src/shapes/IText/ITextKeyBehavior.d.ts.map +1 -1
  38. package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
  39. package/dist-extensions/src/shapes/Text/constants.d.ts +1 -1
  40. package/dist-extensions/src/shapes/Text/constants.d.ts.map +1 -1
  41. package/fabric-test-editor.html +1 -1
  42. package/package.json +1 -1
  43. package/rtl-debug.html +745 -0
  44. package/src/shapes/IText/ITextBehavior.ts +11 -0
  45. package/src/shapes/IText/ITextKeyBehavior.ts +732 -691
  46. package/src/shapes/Text/Text.ts +4 -0
  47. package/src/shapes/Text/constants.ts +1 -0
@@ -3,39 +3,39 @@ import { getFabricDocument, getEnv } from '../../env/index.mjs';
3
3
  import { capValue } from '../../util/misc/capValue.mjs';
4
4
  import { ITextBehavior } from './ITextBehavior.mjs';
5
5
  import { getDocumentFromElement } from '../../util/dom_misc.mjs';
6
- import { RIGHT, LEFT, CHANGED } from '../../constants.mjs';
6
+ import { CHANGED, RIGHT, LEFT } from '../../constants.mjs';
7
7
 
8
8
  class ITextKeyBehavior extends ITextBehavior {
9
- /**
10
- * For functionalities on keyDown
11
- * Map a special key to a function of the instance/prototype
12
- * If you need different behavior for ESC or TAB or arrows, you have to change
13
- * this map setting the name of a function that you build on the IText or
14
- * your prototype.
15
- * the map change will affect all Instances unless you need for only some text Instances
16
- * in that case you have to clone this object and assign your Instance.
17
- * this.keysMap = Object.assign({}, this.keysMap);
18
- * The function must be in IText.prototype.myFunction And will receive event as args[0]
9
+ /**
10
+ * For functionalities on keyDown
11
+ * Map a special key to a function of the instance/prototype
12
+ * If you need different behavior for ESC or TAB or arrows, you have to change
13
+ * this map setting the name of a function that you build on the IText or
14
+ * your prototype.
15
+ * the map change will affect all Instances unless you need for only some text Instances
16
+ * in that case you have to clone this object and assign your Instance.
17
+ * this.keysMap = Object.assign({}, this.keysMap);
18
+ * The function must be in IText.prototype.myFunction And will receive event as args[0]
19
19
  */
20
20
 
21
- /**
22
- * For functionalities on keyUp + ctrl || cmd
21
+ /**
22
+ * For functionalities on keyUp + ctrl || cmd
23
23
  */
24
24
 
25
- /**
26
- * For functionalities on keyDown + ctrl || cmd
25
+ /**
26
+ * For functionalities on keyDown + ctrl || cmd
27
27
  */
28
28
 
29
- /**
30
- * DOM container to append the hiddenTextarea.
31
- * An alternative to attaching to the document.body.
32
- * Useful to reduce laggish redraw of the full document.body tree and
33
- * also with modals event capturing that won't let the textarea take focus.
34
- * @type HTMLElement
29
+ /**
30
+ * DOM container to append the hiddenTextarea.
31
+ * An alternative to attaching to the document.body.
32
+ * Useful to reduce laggish redraw of the full document.body tree and
33
+ * also with modals event capturing that won't let the textarea take focus.
34
+ * @type HTMLElement
35
35
  */
36
36
 
37
- /**
38
- * Initializes hidden textarea (needed to bring up keyboard in iOS)
37
+ /**
38
+ * Initializes hidden textarea (needed to bring up keyboard in iOS)
39
39
  */
40
40
  initHiddenTextarea() {
41
41
  const doc = this.canvas && getDocumentFromElement(this.canvas.getElement()) || getFabricDocument();
@@ -78,17 +78,17 @@ class ITextKeyBehavior extends ITextBehavior {
78
78
  this.hiddenTextarea = textarea;
79
79
  }
80
80
 
81
- /**
82
- * Override this method to customize cursor behavior on textbox blur
81
+ /**
82
+ * Override this method to customize cursor behavior on textbox blur
83
83
  */
84
84
  blur() {
85
85
  this.abortCursorAnimation();
86
86
  }
87
87
 
88
- /**
89
- * Handles keydown event
90
- * only used for arrows and combination of modifier keys.
91
- * @param {KeyboardEvent} e Event object
88
+ /**
89
+ * Handles keydown event
90
+ * only used for arrows and combination of modifier keys.
91
+ * @param {KeyboardEvent} e Event object
92
92
  */
93
93
  onKeyDown(e) {
94
94
  if (!this.isEditing) {
@@ -114,11 +114,11 @@ class ITextKeyBehavior extends ITextBehavior {
114
114
  }
115
115
  }
116
116
 
117
- /**
118
- * Handles keyup event
119
- * We handle KeyUp because ie11 and edge have difficulties copy/pasting
120
- * if a copy/cut event fired, keyup is dismissed
121
- * @param {KeyboardEvent} e Event object
117
+ /**
118
+ * Handles keyup event
119
+ * We handle KeyUp because ie11 and edge have difficulties copy/pasting
120
+ * if a copy/cut event fired, keyup is dismissed
121
+ * @param {KeyboardEvent} e Event object
122
122
  */
123
123
  onKeyUp(e) {
124
124
  if (!this.isEditing || this._copyDone || this.inCompositionMode) {
@@ -135,9 +135,9 @@ class ITextKeyBehavior extends ITextBehavior {
135
135
  this.canvas && this.canvas.requestRenderAll();
136
136
  }
137
137
 
138
- /**
139
- * Handles onInput event
140
- * @param {Event} e Event object
138
+ /**
139
+ * Handles onInput event
140
+ * @param {Event} e Event object
141
141
  */
142
142
  onInput(e) {
143
143
  const fromPaste = this.fromPaste;
@@ -151,6 +151,52 @@ class ITextKeyBehavior extends ITextBehavior {
151
151
  if (!this.isEditing) {
152
152
  return;
153
153
  }
154
+
155
+ // Debug log to track the double keypress issue
156
+ console.log('🔤 onInput debug:', {
157
+ fabricText: this.text,
158
+ textareaValue: value,
159
+ fabricSelection: {
160
+ start: this.selectionStart,
161
+ end: this.selectionEnd
162
+ },
163
+ textareaSelection: {
164
+ start: selectionStart,
165
+ end: selectionEnd
166
+ },
167
+ fromPaste,
168
+ inComposition: this.inCompositionMode
169
+ });
170
+
171
+ // Immediate sync for simple character replacement - fix for double keypress issue
172
+ if (this.text !== value && !this.inCompositionMode) {
173
+ console.log('🔤 Immediate sync: fabric text differs from textarea, syncing immediately');
174
+ console.log('🔤 Before sync - fabric text:', this.text);
175
+ console.log('🔤 Before sync - textarea value:', value);
176
+ console.log('🔤 fromPaste:', fromPaste);
177
+
178
+ // Clear all relevant caches that might prevent visual updates
179
+ this.cursorOffsetCache = {};
180
+ this._browserWrapCache = null;
181
+ this._lastDimensionState = null;
182
+ this._forceClearCache = true;
183
+ console.log('🔤 Cleared all caches');
184
+
185
+ // Use the same logic as updateAndFire but immediately
186
+ this.updateFromTextArea();
187
+ this.fire(CHANGED);
188
+ if (this.canvas) {
189
+ this.canvas.fire('text:changed', {
190
+ target: this
191
+ });
192
+ // ONLY use synchronous rendering to avoid race conditions
193
+ // Remove requestRenderAll() which queues for next animation frame
194
+ this.canvas.renderAll();
195
+ }
196
+ console.log('🔤 After updateFromTextArea - fabric text:', this.text);
197
+ console.log('🔤 Sync complete, caches cleared, synchronous render only');
198
+ return;
199
+ }
154
200
  const updateAndFire = () => {
155
201
  this.updateFromTextArea();
156
202
  this.fire(CHANGED);
@@ -228,15 +274,15 @@ class ITextKeyBehavior extends ITextBehavior {
228
274
  updateAndFire();
229
275
  }
230
276
 
231
- /**
232
- * Composition start
277
+ /**
278
+ * Composition start
233
279
  */
234
280
  onCompositionStart() {
235
281
  this.inCompositionMode = true;
236
282
  }
237
283
 
238
- /**
239
- * Composition end
284
+ /**
285
+ * Composition end
240
286
  */
241
287
  onCompositionEnd() {
242
288
  this.inCompositionMode = false;
@@ -254,8 +300,8 @@ class ITextKeyBehavior extends ITextBehavior {
254
300
  this.updateTextareaPosition();
255
301
  }
256
302
 
257
- /**
258
- * Copies selected text
303
+ /**
304
+ * Copies selected text
259
305
  */
260
306
  copy() {
261
307
  if (this.selectionStart === this.selectionEnd) {
@@ -274,19 +320,19 @@ class ITextKeyBehavior extends ITextBehavior {
274
320
  this._copyDone = true;
275
321
  }
276
322
 
277
- /**
278
- * Pastes text
323
+ /**
324
+ * Pastes text
279
325
  */
280
326
  paste() {
281
327
  this.fromPaste = true;
282
328
  }
283
329
 
284
- /**
285
- * Finds the width in pixels before the cursor on the same line
286
- * @private
287
- * @param {Number} lineIndex
288
- * @param {Number} charIndex
289
- * @return {Number} widthBeforeCursor width before cursor
330
+ /**
331
+ * Finds the width in pixels before the cursor on the same line
332
+ * @private
333
+ * @param {Number} lineIndex
334
+ * @param {Number} charIndex
335
+ * @return {Number} widthBeforeCursor width before cursor
290
336
  */
291
337
  _getWidthBeforeCursor(lineIndex, charIndex) {
292
338
  let widthBeforeCursor = this._getLineLeftOffset(lineIndex),
@@ -298,11 +344,11 @@ class ITextKeyBehavior extends ITextBehavior {
298
344
  return widthBeforeCursor;
299
345
  }
300
346
 
301
- /**
302
- * Gets start offset of a selection
303
- * @param {KeyboardEvent} e Event object
304
- * @param {Boolean} isRight
305
- * @return {Number}
347
+ /**
348
+ * Gets start offset of a selection
349
+ * @param {KeyboardEvent} e Event object
350
+ * @param {Boolean} isRight
351
+ * @return {Number}
306
352
  */
307
353
  getDownCursorOffset(e, isRight) {
308
354
  const selectionProp = this._getSelectionForOffset(e, isRight),
@@ -320,12 +366,12 @@ class ITextKeyBehavior extends ITextBehavior {
320
366
  return textAfterCursor.length + indexOnOtherLine + 1 + this.missingNewlineOffset(lineIndex);
321
367
  }
322
368
 
323
- /**
324
- * private
325
- * Helps finding if the offset should be counted from Start or End
326
- * @param {KeyboardEvent} e Event object
327
- * @param {Boolean} isRight
328
- * @return {Number}
369
+ /**
370
+ * private
371
+ * Helps finding if the offset should be counted from Start or End
372
+ * @param {KeyboardEvent} e Event object
373
+ * @param {Boolean} isRight
374
+ * @return {Number}
329
375
  */
330
376
  _getSelectionForOffset(e, isRight) {
331
377
  if (e.shiftKey && this.selectionStart !== this.selectionEnd && isRight) {
@@ -335,10 +381,10 @@ class ITextKeyBehavior extends ITextBehavior {
335
381
  }
336
382
  }
337
383
 
338
- /**
339
- * @param {KeyboardEvent} e Event object
340
- * @param {Boolean} isRight
341
- * @return {Number}
384
+ /**
385
+ * @param {KeyboardEvent} e Event object
386
+ * @param {Boolean} isRight
387
+ * @return {Number}
342
388
  */
343
389
  getUpCursorOffset(e, isRight) {
344
390
  const selectionProp = this._getSelectionForOffset(e, isRight),
@@ -357,9 +403,9 @@ class ITextKeyBehavior extends ITextBehavior {
357
403
  return -this._textLines[lineIndex - 1].length + indexOnOtherLine - textBeforeCursor.length + (1 - missingNewlineOffset);
358
404
  }
359
405
 
360
- /**
361
- * for a given width it founds the matching character.
362
- * @private
406
+ /**
407
+ * for a given width it founds the matching character.
408
+ * @private
363
409
  */
364
410
  _getIndexOnLine(lineIndex, width) {
365
411
  const line = this._textLines[lineIndex],
@@ -389,9 +435,9 @@ class ITextKeyBehavior extends ITextBehavior {
389
435
  return indexOnLine;
390
436
  }
391
437
 
392
- /**
393
- * Moves cursor down
394
- * @param {KeyboardEvent} e Event object
438
+ /**
439
+ * Moves cursor down
440
+ * @param {KeyboardEvent} e Event object
395
441
  */
396
442
  moveCursorDown(e) {
397
443
  if (this.selectionStart >= this._text.length && this.selectionEnd >= this._text.length) {
@@ -400,9 +446,9 @@ class ITextKeyBehavior extends ITextBehavior {
400
446
  this._moveCursorUpOrDown('Down', e);
401
447
  }
402
448
 
403
- /**
404
- * Moves cursor up
405
- * @param {KeyboardEvent} e Event object
449
+ /**
450
+ * Moves cursor up
451
+ * @param {KeyboardEvent} e Event object
406
452
  */
407
453
  moveCursorUp(e) {
408
454
  if (this.selectionStart === 0 && this.selectionEnd === 0) {
@@ -411,10 +457,10 @@ class ITextKeyBehavior extends ITextBehavior {
411
457
  this._moveCursorUpOrDown('Up', e);
412
458
  }
413
459
 
414
- /**
415
- * Moves cursor up or down, fires the events
416
- * @param {String} direction 'Up' or 'Down'
417
- * @param {KeyboardEvent} e Event object
460
+ /**
461
+ * Moves cursor up or down, fires the events
462
+ * @param {String} direction 'Up' or 'Down'
463
+ * @param {KeyboardEvent} e Event object
418
464
  */
419
465
  _moveCursorUpOrDown(direction, e) {
420
466
  const offset = this[`get${direction}CursorOffset`](e, this._selectionDirection === RIGHT);
@@ -436,9 +482,9 @@ class ITextKeyBehavior extends ITextBehavior {
436
482
  }
437
483
  }
438
484
 
439
- /**
440
- * Moves cursor with shift
441
- * @param {Number} offset
485
+ /**
486
+ * Moves cursor with shift
487
+ * @param {Number} offset
442
488
  */
443
489
  moveCursorWithShift(offset) {
444
490
  const newSelection = this._selectionDirection === LEFT ? this.selectionStart + offset : this.selectionEnd + offset;
@@ -446,9 +492,9 @@ class ITextKeyBehavior extends ITextBehavior {
446
492
  return offset !== 0;
447
493
  }
448
494
 
449
- /**
450
- * Moves cursor up without shift
451
- * @param {Number} offset
495
+ /**
496
+ * Moves cursor up without shift
497
+ * @param {Number} offset
452
498
  */
453
499
  moveCursorWithoutShift(offset) {
454
500
  if (offset < 0) {
@@ -461,9 +507,9 @@ class ITextKeyBehavior extends ITextBehavior {
461
507
  return offset !== 0;
462
508
  }
463
509
 
464
- /**
465
- * Moves cursor left
466
- * @param {KeyboardEvent} e Event object
510
+ /**
511
+ * Moves cursor left
512
+ * @param {KeyboardEvent} e Event object
467
513
  */
468
514
  moveCursorLeft(e) {
469
515
  if (this.selectionStart === 0 && this.selectionEnd === 0) {
@@ -472,11 +518,11 @@ class ITextKeyBehavior extends ITextBehavior {
472
518
  this._moveCursorLeftOrRight('Left', e);
473
519
  }
474
520
 
475
- /**
476
- * @private
477
- * @return {Boolean} true if a change happened
478
- *
479
- * @todo refactor not to use method name composition
521
+ /**
522
+ * @private
523
+ * @return {Boolean} true if a change happened
524
+ *
525
+ * @todo refactor not to use method name composition
480
526
  */
481
527
  _move(e, prop, direction) {
482
528
  let newValue;
@@ -495,23 +541,23 @@ class ITextKeyBehavior extends ITextBehavior {
495
541
  return false;
496
542
  }
497
543
 
498
- /**
499
- * @private
544
+ /**
545
+ * @private
500
546
  */
501
547
  _moveLeft(e, prop) {
502
548
  return this._move(e, prop, 'Left');
503
549
  }
504
550
 
505
- /**
506
- * @private
551
+ /**
552
+ * @private
507
553
  */
508
554
  _moveRight(e, prop) {
509
555
  return this._move(e, prop, 'Right');
510
556
  }
511
557
 
512
- /**
513
- * Moves cursor left without keeping selection
514
- * @param {KeyboardEvent} e
558
+ /**
559
+ * Moves cursor left without keeping selection
560
+ * @param {KeyboardEvent} e
515
561
  */
516
562
  moveCursorLeftWithoutShift(e) {
517
563
  let change = true;
@@ -526,9 +572,9 @@ class ITextKeyBehavior extends ITextBehavior {
526
572
  return change;
527
573
  }
528
574
 
529
- /**
530
- * Moves cursor left while keeping selection
531
- * @param {KeyboardEvent} e
575
+ /**
576
+ * Moves cursor left while keeping selection
577
+ * @param {KeyboardEvent} e
532
578
  */
533
579
  moveCursorLeftWithShift(e) {
534
580
  if (this._selectionDirection === RIGHT && this.selectionStart !== this.selectionEnd) {
@@ -539,9 +585,9 @@ class ITextKeyBehavior extends ITextBehavior {
539
585
  }
540
586
  }
541
587
 
542
- /**
543
- * Moves cursor right
544
- * @param {KeyboardEvent} e Event object
588
+ /**
589
+ * Moves cursor right
590
+ * @param {KeyboardEvent} e Event object
545
591
  */
546
592
  moveCursorRight(e) {
547
593
  if (this.selectionStart >= this._text.length && this.selectionEnd >= this._text.length) {
@@ -550,10 +596,10 @@ class ITextKeyBehavior extends ITextBehavior {
550
596
  this._moveCursorLeftOrRight('Right', e);
551
597
  }
552
598
 
553
- /**
554
- * Moves cursor right or Left, fires event
555
- * @param {String} direction 'Left', 'Right'
556
- * @param {KeyboardEvent} e Event object
599
+ /**
600
+ * Moves cursor right or Left, fires event
601
+ * @param {String} direction 'Left', 'Right'
602
+ * @param {KeyboardEvent} e Event object
557
603
  */
558
604
  _moveCursorLeftOrRight(direction, e) {
559
605
  const actionName = `moveCursor${direction}${e.shiftKey ? 'WithShift' : 'WithoutShift'}`;
@@ -568,9 +614,9 @@ class ITextKeyBehavior extends ITextBehavior {
568
614
  }
569
615
  }
570
616
 
571
- /**
572
- * Moves cursor right while keeping selection
573
- * @param {KeyboardEvent} e
617
+ /**
618
+ * Moves cursor right while keeping selection
619
+ * @param {KeyboardEvent} e
574
620
  */
575
621
  moveCursorRightWithShift(e) {
576
622
  if (this._selectionDirection === LEFT && this.selectionStart !== this.selectionEnd) {
@@ -581,9 +627,9 @@ class ITextKeyBehavior extends ITextBehavior {
581
627
  }
582
628
  }
583
629
 
584
- /**
585
- * Moves cursor right without keeping selection
586
- * @param {KeyboardEvent} e Event object
630
+ /**
631
+ * Moves cursor right without keeping selection
632
+ * @param {KeyboardEvent} e Event object
587
633
  */
588
634
  moveCursorRightWithoutShift(e) {
589
635
  let changed = true;