@flighthq/textinput 0.1.0
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.
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/selectableRichTextManager.d.ts +10 -0
- package/dist/selectableRichTextManager.d.ts.map +1 -0
- package/dist/selectableRichTextManager.js +87 -0
- package/dist/selectableRichTextManager.js.map +1 -0
- package/dist/textInput.d.ts +6 -0
- package/dist/textInput.d.ts.map +1 -0
- package/dist/textInput.js +73 -0
- package/dist/textInput.js.map +1 -0
- package/dist/textInputEditing.d.ts +36 -0
- package/dist/textInputEditing.d.ts.map +1 -0
- package/dist/textInputEditing.js +790 -0
- package/dist/textInputEditing.js.map +1 -0
- package/dist/textInputManager.d.ts +11 -0
- package/dist/textInputManager.d.ts.map +1 -0
- package/dist/textInputManager.js +96 -0
- package/dist/textInputManager.js.map +1 -0
- package/package.json +41 -0
- package/src/selectableRichTextManager.test.ts +189 -0
- package/src/textInput.test.ts +61 -0
- package/src/textInputEditing.test.ts +914 -0
- package/src/textInputManager.test.ts +175 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { createSignal, emitSignal } from '@flighthq/signals';
|
|
2
|
+
import { createRichText } from '@flighthq/text';
|
|
3
|
+
import type { RichText, RichTextData } from '@flighthq/types';
|
|
4
|
+
import { KeyCode } from '@flighthq/types';
|
|
5
|
+
|
|
6
|
+
import { enableTextInput, getTextInputState } from './textInput';
|
|
7
|
+
import {
|
|
8
|
+
getTextInputSelectionBeginIndex,
|
|
9
|
+
getTextInputSelectionEndIndex,
|
|
10
|
+
setTextInputSelection,
|
|
11
|
+
} from './textInputEditing';
|
|
12
|
+
import {
|
|
13
|
+
blurTextInput,
|
|
14
|
+
connectInputToTextInput,
|
|
15
|
+
createTextInputManager,
|
|
16
|
+
dispatchTextInput,
|
|
17
|
+
dispatchTextInputKeyDown,
|
|
18
|
+
dispatchTextInputPointerDown,
|
|
19
|
+
dispatchTextInputPointerMove,
|
|
20
|
+
dispatchTextInputWheel,
|
|
21
|
+
focusTextInput,
|
|
22
|
+
} from './textInputManager';
|
|
23
|
+
|
|
24
|
+
function createInput(data: Partial<RichTextData> = {}): RichText {
|
|
25
|
+
const text = createRichText({ data });
|
|
26
|
+
enableTextInput(text);
|
|
27
|
+
return text;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isFocused(node: RichText): boolean {
|
|
31
|
+
return getTextInputState(node)?.focused === true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const keyData = {
|
|
35
|
+
altKey: false,
|
|
36
|
+
capsLock: false,
|
|
37
|
+
code: '',
|
|
38
|
+
ctrlKey: false,
|
|
39
|
+
key: 'Backspace',
|
|
40
|
+
keyCode: KeyCode.BACKSPACE,
|
|
41
|
+
location: 0,
|
|
42
|
+
metaKey: false,
|
|
43
|
+
modifier: 0,
|
|
44
|
+
numLock: false,
|
|
45
|
+
repeat: false,
|
|
46
|
+
shiftKey: false,
|
|
47
|
+
timeStamp: 0,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
describe('blurTextInput', () => {
|
|
51
|
+
it('clears the focused target', () => {
|
|
52
|
+
const manager = createTextInputManager();
|
|
53
|
+
const input = createInput();
|
|
54
|
+
focusTextInput(manager, input);
|
|
55
|
+
expect(isFocused(input)).toBe(true);
|
|
56
|
+
blurTextInput(manager);
|
|
57
|
+
expect(manager.focused).toBeNull();
|
|
58
|
+
expect(isFocused(input)).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('connectInputToTextInput', () => {
|
|
63
|
+
it('routes normalized text input into the focused target', () => {
|
|
64
|
+
const input = { onKeyDown: createSignal(), onTextInput: createSignal() };
|
|
65
|
+
const manager = createTextInputManager();
|
|
66
|
+
const target = createInput();
|
|
67
|
+
focusTextInput(manager, target);
|
|
68
|
+
const disconnect = connectInputToTextInput(input, manager);
|
|
69
|
+
|
|
70
|
+
emitSignal(input.onTextInput, { length: 1, start: 0, text: 'A' });
|
|
71
|
+
|
|
72
|
+
expect(target.data.text).toBe('A');
|
|
73
|
+
disconnect();
|
|
74
|
+
emitSignal(input.onTextInput, { length: 1, start: 0, text: 'B' });
|
|
75
|
+
expect(target.data.text).toBe('A');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('createTextInputManager', () => {
|
|
80
|
+
it('creates an enabled manager without focus', () => {
|
|
81
|
+
const manager = createTextInputManager();
|
|
82
|
+
expect(manager.enabled).toBe(true);
|
|
83
|
+
expect(manager.focused).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('dispatchTextInput', () => {
|
|
88
|
+
it('inserts text into the focused target', () => {
|
|
89
|
+
const manager = createTextInputManager();
|
|
90
|
+
const target = createInput();
|
|
91
|
+
focusTextInput(manager, target);
|
|
92
|
+
expect(dispatchTextInput(manager, 'x')).toBe(true);
|
|
93
|
+
expect(target.data.text).toBe('x');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('returns false without focus', () => {
|
|
97
|
+
expect(dispatchTextInput(createTextInputManager(), 'x')).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('dispatchTextInputKeyDown', () => {
|
|
102
|
+
it('routes keyboard commands into the focused target', () => {
|
|
103
|
+
const manager = createTextInputManager();
|
|
104
|
+
const target = createInput({ text: 'abc' });
|
|
105
|
+
setTextInputSelection(target, 2, 2);
|
|
106
|
+
focusTextInput(manager, target);
|
|
107
|
+
expect(dispatchTextInputKeyDown(manager, keyData)).toBe(true);
|
|
108
|
+
expect(target.data.text).toBe('ac');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('invokes onCopy callback for copy command', () => {
|
|
112
|
+
const manager = createTextInputManager();
|
|
113
|
+
const target = createInput({ text: 'hello' });
|
|
114
|
+
setTextInputSelection(target, 0, 5);
|
|
115
|
+
focusTextInput(manager, target);
|
|
116
|
+
const copied: string[] = [];
|
|
117
|
+
dispatchTextInputKeyDown(manager, { ...keyData, ctrlKey: true, key: 'c', keyCode: KeyCode.C }, undefined, (text) =>
|
|
118
|
+
copied.push(text),
|
|
119
|
+
);
|
|
120
|
+
expect(copied).toEqual(['hello']);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('dispatchTextInputPointerDown', () => {
|
|
125
|
+
it('focuses the target', () => {
|
|
126
|
+
const manager = createTextInputManager();
|
|
127
|
+
const target = createInput({ text: 'hello' });
|
|
128
|
+
dispatchTextInputPointerDown(manager, target, 0, 0);
|
|
129
|
+
expect(manager.focused).toBe(target);
|
|
130
|
+
expect(isFocused(target)).toBe(true);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('dispatchTextInputPointerMove', () => {
|
|
135
|
+
it('does not throw when nothing is focused', () => {
|
|
136
|
+
expect(() => dispatchTextInputPointerMove(createTextInputManager(), 0, 0)).not.toThrow();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('dispatchTextInputWheel', () => {
|
|
141
|
+
it('advances scrollV by the given delta', () => {
|
|
142
|
+
const manager = createTextInputManager();
|
|
143
|
+
const target = createInput({ text: 'hello' });
|
|
144
|
+
focusTextInput(manager, target);
|
|
145
|
+
expect(target.data.scrollV).toBe(1);
|
|
146
|
+
dispatchTextInputWheel(manager, 2);
|
|
147
|
+
expect(target.data.scrollV).toBe(3);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('does not throw when nothing is focused', () => {
|
|
151
|
+
expect(() => dispatchTextInputWheel(createTextInputManager(), 1)).not.toThrow();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('focusTextInput', () => {
|
|
156
|
+
it('sets the focused target', () => {
|
|
157
|
+
const manager = createTextInputManager();
|
|
158
|
+
const target = createInput({ text: 'abc' });
|
|
159
|
+
focusTextInput(manager, target);
|
|
160
|
+
expect(manager.focused).toBe(target);
|
|
161
|
+
expect(isFocused(target)).toBe(true);
|
|
162
|
+
expect(getTextInputSelectionBeginIndex(target)).toBe(0);
|
|
163
|
+
expect(getTextInputSelectionEndIndex(target)).toBe(0);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('clears focus from the previous target', () => {
|
|
167
|
+
const manager = createTextInputManager();
|
|
168
|
+
const first = createInput();
|
|
169
|
+
const second = createInput();
|
|
170
|
+
focusTextInput(manager, first);
|
|
171
|
+
focusTextInput(manager, second);
|
|
172
|
+
expect(isFocused(first)).toBe(false);
|
|
173
|
+
expect(isFocused(second)).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
});
|