forked from slab/quill
-
Notifications
You must be signed in to change notification settings - Fork 0
/
input.ts
103 lines (88 loc) · 2.85 KB
/
input.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import Delta from 'quill-delta';
import Module from '../core/module';
import Quill from '../core/quill';
import type { Range } from '../core/selection';
import { deleteRange } from './keyboard';
const INSERT_TYPES = ['insertText', 'insertReplacementText'];
class Input extends Module {
constructor(quill: Quill, options: Record<string, never>) {
super(quill, options);
quill.root.addEventListener('beforeinput', (event) => {
this.handleBeforeInput(event);
});
// Gboard with English input on Android triggers `compositionstart` sometimes even
// users are not going to type anything.
if (!/Android/i.test(navigator.userAgent)) {
quill.on(Quill.events.COMPOSITION_BEFORE_START, () => {
this.handleCompositionStart();
});
}
}
private deleteRange(range: Range) {
deleteRange({ range, quill: this.quill });
}
private replaceText(range: Range, text = '') {
if (range.length === 0) return false;
if (text) {
// Follow the native behavior that inherits the formats of the first character
const formats = this.quill.getFormat(range.index, 1);
this.deleteRange(range);
this.quill.updateContents(
new Delta().retain(range.index).insert(text, formats),
Quill.sources.USER,
);
} else {
this.deleteRange(range);
}
this.quill.setSelection(range.index + text.length, 0, Quill.sources.SILENT);
return true;
}
private handleBeforeInput(event: InputEvent) {
if (
this.quill.composition.isComposing ||
event.defaultPrevented ||
!INSERT_TYPES.includes(event.inputType)
) {
return;
}
const staticRange = event.getTargetRanges
? event.getTargetRanges()[0]
: null;
if (!staticRange || staticRange.collapsed === true) {
return;
}
const text = getPlainTextFromInputEvent(event);
if (text == null) {
return;
}
const normalized = this.quill.selection.normalizeNative(staticRange);
const range = normalized
? this.quill.selection.normalizedToRange(normalized)
: null;
if (range && this.replaceText(range, text)) {
event.preventDefault();
}
}
private handleCompositionStart() {
const range = this.quill.getSelection();
if (range) {
this.replaceText(range);
}
}
}
function getPlainTextFromInputEvent(event: InputEvent) {
// When `inputType` is "insertText":
// - `event.data` should be string (Safari uses `event.dataTransfer`).
// - `event.dataTransfer` should be null.
// When `inputType` is "insertReplacementText":
// - `event.data` should be null.
// - `event.dataTransfer` should contain "text/plain" data.
if (typeof event.data === 'string') {
return event.data;
}
if (event.dataTransfer?.types.includes('text/plain')) {
return event.dataTransfer.getData('text/plain');
}
return null;
}
export default Input;