Skip to content

Commit cd9563c

Browse files
committed
Use the target range to insert the replacement text
When the browser auto-corrects a misspelled word, it dispatches a `beforeinput` event with the `insertReplacementText` input type and a `dataTransfer` object containing the replacement text. The event also contains a `getTargetRanges` method that returns an array of `DOMRange` objects representing the range of text that will be replaced. When the `Level2InputController` was originally implemented, not all browsers supported the `getTargetRanges` method, so it was not used. Now that all supported browsers do support `getTargetRanges`, we can use it to insert the replacement text at the correct location. This ensures editor state is updated correctly the cursor is positioned correctly. Ref. - https://w3c.github.io/input-events/#overview (search for "insertReplacementText") - https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/getTargetRanges
1 parent 10573a8 commit cd9563c

File tree

3 files changed

+16
-4
lines changed

3 files changed

+16
-4
lines changed

src/test/system/level_2_input_test.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,22 @@ testGroup("Level 2 Input", testOptions, () => {
139139
})
140140

141141
// https://input-inspector.now.sh/profiles/hVXS1cHYFvc2EfdRyTWQ
142-
test("correcting a misspelled word in Chrome", async () => {
142+
test("correcting a misspelled word", async () => {
143143
insertString("onr")
144144
getComposition().setSelectedRange([ 0, 3 ])
145145
await nextFrame()
146146

147147
const inputType = "insertReplacementText"
148148
const dataTransfer = createDataTransfer({ "text/plain": "one" })
149-
const event = createEvent("beforeinput", { inputType, dataTransfer })
149+
150+
const targetRange = document.createRange()
151+
const textNode = getEditorElement().firstElementChild.lastChild
152+
targetRange.setStart(textNode, 0)
153+
targetRange.setEnd(textNode, 3)
154+
155+
const event = createEvent("beforeinput", { inputType, dataTransfer, getTargetRanges: () => [ targetRange ] })
150156
document.activeElement.dispatchEvent(event)
157+
151158
await nextFrame()
152159
expectDocument("one\n")
153160
})

src/trix/controllers/level_2_input_controller.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,12 @@ export default class Level2InputController extends InputController {
443443
},
444444

445445
insertReplacementText() {
446-
this.insertString(this.event.dataTransfer.getData("text/plain"), { updatePosition: false })
447-
this.requestRender()
446+
const replacement = this.event.dataTransfer.getData("text/plain")
447+
const domRange = this.event.getTargetRanges()[0]
448+
449+
this.withTargetDOMRange(domRange, () => {
450+
this.insertString(replacement, { updatePosition: false })
451+
})
448452
},
449453

450454
insertText() {

src/trix/models/selection_manager.js

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export default class SelectionManager extends BasicObject {
2929
this.lockCount = 0
3030
handleEvent("mousedown", { onElement: this.element, withCallback: this.didMouseDown })
3131
}
32+
3233
getLocationRange(options = {}) {
3334
if (options.strict === false) {
3435
return this.createLocationRangeFromDOMRange(getDOMRange())

0 commit comments

Comments
 (0)