From 3f4a170968b8c4a71e454c9bb9502cab2be88e9a Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Wed, 3 Apr 2024 16:41:56 +0100 Subject: [PATCH] Fix blur bug in editable text (#3343) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR fixes a bug that was introduced by #3223. There was a code path that normally used to never run (a blur event running when the shape was no longer editing) but which was being run now that shapes aren't immediately removed on pointer down. ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `bugfix` — Bug fix ### Test Plan 1. Create a sticky note 2. Begin editing the note 3. click on the canvas 4. You should be in pointing_canvas --- packages/editor/src/lib/editor/Editor.ts | 11 +++-- packages/state/api/api.json | 4 +- .../src/lib/shapes/shared/useEditableText.ts | 41 ++++++++----------- packages/tldraw/src/test/SelectTool.test.ts | 11 +++++ 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/packages/editor/src/lib/editor/Editor.ts b/packages/editor/src/lib/editor/Editor.ts index 6e3fdbea2..d44f2c75e 100644 --- a/packages/editor/src/lib/editor/Editor.ts +++ b/packages/editor/src/lib/editor/Editor.ts @@ -8229,7 +8229,6 @@ export class Editor extends EventEmitter { */ cancel(): this { this.dispatch({ type: 'misc', name: 'cancel' }) - this._tickManager.tick() return this } @@ -8245,7 +8244,6 @@ export class Editor extends EventEmitter { */ interrupt(): this { this.dispatch({ type: 'misc', name: 'interrupt' }) - this._tickManager.tick() return this } @@ -8367,6 +8365,9 @@ export class Editor extends EventEmitter { */ dispatch = (info: TLEventInfo): this => { this._pendingEventsForNextTick.push(info) + if (!(info.type === 'pointer' || info.type === 'wheel' || info.type === 'pinch')) { + this._flushEventsForTick(0) + } return this } @@ -8381,8 +8382,10 @@ export class Editor extends EventEmitter { this._flushEventForTick(info) } } - this.root.handleEvent({ type: 'misc', name: 'tick', elapsed }) - this.scribbles.tick(elapsed) + if (elapsed > 0) { + this.root.handleEvent({ type: 'misc', name: 'tick', elapsed }) + this.scribbles.tick(elapsed) + } }) } diff --git a/packages/state/api/api.json b/packages/state/api/api.json index 6b0f0291a..fc6d8c913 100644 --- a/packages/state/api/api.json +++ b/packages/state/api/api.json @@ -2387,7 +2387,7 @@ }, { "kind": "Content", - "text": "(() => Value) | Value" + "text": "Value | (() => Value)" }, { "kind": "Content", @@ -2813,7 +2813,7 @@ }, { "kind": "Content", - "text": "any[] | undefined" + "text": "undefined | any[]" }, { "kind": "Content", diff --git a/packages/tldraw/src/lib/shapes/shared/useEditableText.ts b/packages/tldraw/src/lib/shapes/shared/useEditableText.ts index 4c9851c15..e2cadd472 100644 --- a/packages/tldraw/src/lib/shapes/shared/useEditableText.ts +++ b/packages/tldraw/src/lib/shapes/shared/useEditableText.ts @@ -63,32 +63,27 @@ export function useEditableText(id: TLShapeId, type: string, text: string) { const elm = rInput.current const editingShapeId = editor.getEditingShapeId() // Did we move to a different shape? - if (elm && editingShapeId) { - // important! these ^v are two different things - // is that shape OUR shape? - if (editingShapeId === id) { - if (ranges) { - if (!ranges.length) { - // If we don't have any ranges, restore selection - // and select all of the text - elm.focus() - } else { - // Otherwise, skip the select-all-on-focus behavior - // and restore the selection - rSkipSelectOnFocus.current = true - elm.focus() - const selection = window.getSelection() - if (selection) { - ranges.forEach((range) => selection.addRange(range)) - } - } - } else { + // important! these ^v are two different things + // is that shape OUR shape? + if (elm && editingShapeId === id) { + if (ranges) { + if (!ranges.length) { + // If we don't have any ranges, restore selection + // and select all of the text elm.focus() + } else { + // Otherwise, skip the select-all-on-focus behavior + // and restore the selection + rSkipSelectOnFocus.current = true + elm.focus() + const selection = window.getSelection() + if (selection) { + ranges.forEach((range) => selection.addRange(range)) + } } + } else { + elm.focus() } - } else { - window.getSelection()?.removeAllRanges() - editor.complete() } }) }, [editor, id]) diff --git a/packages/tldraw/src/test/SelectTool.test.ts b/packages/tldraw/src/test/SelectTool.test.ts index a975eb910..9ff55b55a 100644 --- a/packages/tldraw/src/test/SelectTool.test.ts +++ b/packages/tldraw/src/test/SelectTool.test.ts @@ -494,3 +494,14 @@ describe('When in readonly mode', () => { expect(editor.getEditingShapeId()).toBe(ids.embed1) }) }) + +// This should be end to end, the problem is the blur handler of the react component +it('goes into pointing canvas', () => { + editor + .createShape({ type: 'note' }) + .pointerMove(50, 50) + .doubleClick() + .expectToBeIn('select.editing_shape') + .pointerDown(300, 300) + .expectToBeIn('select.pointing_canvas') +})