reactjs-virtual-keyboard 1.0.2 → 2.0.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/README.md +293 -191
- package/dist/index.d.ts +108 -27
- package/dist/index.esm.js +695 -485
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -7
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +4 -3
package/dist/index.esm.js
CHANGED
|
@@ -1,190 +1,129 @@
|
|
|
1
|
-
import { jsx
|
|
2
|
-
import { useRef
|
|
3
|
-
const
|
|
4
|
-
var
|
|
5
|
-
const { skipValueAssignment
|
|
6
|
-
if (!
|
|
7
|
-
const
|
|
8
|
-
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useCallback, useEffect, memo, useState } from "react";
|
|
3
|
+
const setInputValueAndDispatchEvents = (input, value, options = {}) => {
|
|
4
|
+
var _a;
|
|
5
|
+
const { skipValueAssignment = false } = options;
|
|
6
|
+
if (!skipValueAssignment) {
|
|
7
|
+
const proto = Object.getPrototypeOf(input);
|
|
8
|
+
const valueSetter = (_a = Object.getOwnPropertyDescriptor(proto, "value")) == null ? void 0 : _a.set;
|
|
9
|
+
if (valueSetter) {
|
|
10
|
+
valueSetter.call(input, value);
|
|
11
|
+
}
|
|
9
12
|
}
|
|
10
|
-
|
|
13
|
+
input.dispatchEvent(new InputEvent("input", { bubbles: true, cancelable: true }));
|
|
14
|
+
input.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
|
|
11
15
|
};
|
|
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
|
-
return {
|
|
48
|
-
onMouseDown: (u) => {
|
|
49
|
-
u.preventDefault(), o();
|
|
50
|
-
},
|
|
51
|
-
onTouchStart: (u) => {
|
|
52
|
-
t && u.preventDefault(), o();
|
|
53
|
-
},
|
|
54
|
-
onMouseUp: c,
|
|
55
|
-
onMouseLeave: c,
|
|
56
|
-
onTouchEnd: (u) => {
|
|
57
|
-
t && u.preventDefault(), c();
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function G({
|
|
62
|
-
isInputFocused: e,
|
|
63
|
-
onBackspace: r,
|
|
64
|
-
onEnter: n,
|
|
65
|
-
onSpace: t,
|
|
66
|
-
onCapsToggle: s,
|
|
67
|
-
onKeyClick: l
|
|
68
|
-
}) {
|
|
69
|
-
$(() => {
|
|
70
|
-
if (!e) return;
|
|
71
|
-
const c = (o) => {
|
|
72
|
-
const u = o.key;
|
|
73
|
-
switch (u) {
|
|
74
|
-
case "Backspace":
|
|
75
|
-
o.preventDefault(), o.stopPropagation(), r();
|
|
76
|
-
return;
|
|
77
|
-
case "Enter":
|
|
78
|
-
o.preventDefault(), n();
|
|
79
|
-
return;
|
|
80
|
-
case " ":
|
|
81
|
-
o.preventDefault(), t();
|
|
82
|
-
return;
|
|
83
|
-
case "CapsLock":
|
|
84
|
-
o.preventDefault(), s();
|
|
85
|
-
return;
|
|
86
|
-
default:
|
|
87
|
-
u.length === 1 && (o.preventDefault(), l(u));
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
return document.addEventListener("keydown", c), () => {
|
|
91
|
-
document.removeEventListener("keydown", c);
|
|
92
|
-
};
|
|
93
|
-
}, [e, r, n, t, s, l]);
|
|
94
|
-
}
|
|
95
|
-
const X = 38, Z = 20, _ = "vk-keyboard-shift-transition", R = 300;
|
|
96
|
-
let g = [], D = !1;
|
|
97
|
-
function q() {
|
|
98
|
-
if (D) return;
|
|
99
|
-
const e = document.createElement("style");
|
|
100
|
-
e.id = "vk-keyboard-shift-styles", e.textContent = `
|
|
101
|
-
.${_} {
|
|
102
|
-
transition: transform ${R}ms ease-out;
|
|
16
|
+
const validateValueUtil = (value, inputType) => {
|
|
17
|
+
switch (inputType) {
|
|
18
|
+
case "number":
|
|
19
|
+
return /^[0-9]*$/.test(value);
|
|
20
|
+
case "email":
|
|
21
|
+
return /^[a-zA-Z0-9@._+-]*$/.test(value);
|
|
22
|
+
case "tel":
|
|
23
|
+
return /^[0-9+\-() ]*$/.test(value);
|
|
24
|
+
case "url":
|
|
25
|
+
return /^[a-zA-Z0-9:/._-]*$/.test(value);
|
|
26
|
+
case "password":
|
|
27
|
+
return true;
|
|
28
|
+
default:
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const getInitialLayout = (inputType, defaultLayout) => {
|
|
33
|
+
if (inputType === "number") {
|
|
34
|
+
return "numbers";
|
|
35
|
+
}
|
|
36
|
+
return defaultLayout;
|
|
37
|
+
};
|
|
38
|
+
const KEYBOARD_HEIGHT_VH = 38;
|
|
39
|
+
const INPUT_PADDING = 20;
|
|
40
|
+
const TRANSITION_CLASS = "vk-keyboard-shift-transition";
|
|
41
|
+
const TRANSITION_DURATION = 300;
|
|
42
|
+
let shiftedElements = [];
|
|
43
|
+
let styleInjected = false;
|
|
44
|
+
function injectStyles() {
|
|
45
|
+
if (styleInjected) return;
|
|
46
|
+
const style = document.createElement("style");
|
|
47
|
+
style.id = "vk-keyboard-shift-styles";
|
|
48
|
+
style.textContent = `
|
|
49
|
+
.${TRANSITION_CLASS} {
|
|
50
|
+
transition: transform ${TRANSITION_DURATION}ms ease-out;
|
|
103
51
|
will-change: transform;
|
|
104
52
|
}
|
|
105
|
-
|
|
53
|
+
`;
|
|
54
|
+
document.head.appendChild(style);
|
|
55
|
+
styleInjected = true;
|
|
106
56
|
}
|
|
107
|
-
function
|
|
108
|
-
var
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
57
|
+
function findContentElements(keyboardContainerRef) {
|
|
58
|
+
var _a;
|
|
59
|
+
if (!keyboardContainerRef) return [];
|
|
60
|
+
return Array.from(((_a = keyboardContainerRef.parentElement) == null ? void 0 : _a.children) ?? []).filter(
|
|
61
|
+
(child) => {
|
|
62
|
+
if (child === keyboardContainerRef) return false;
|
|
63
|
+
const { position } = window.getComputedStyle(child);
|
|
64
|
+
return position !== "fixed" && position !== "absolute";
|
|
114
65
|
}
|
|
115
|
-
)
|
|
66
|
+
);
|
|
116
67
|
}
|
|
117
|
-
function
|
|
118
|
-
const
|
|
119
|
-
|
|
68
|
+
function calculateShiftAmount(input) {
|
|
69
|
+
const inputBottom = input.getBoundingClientRect().bottom;
|
|
70
|
+
const visibleHeight = window.innerHeight * (1 - KEYBOARD_HEIGHT_VH / 100);
|
|
71
|
+
const overflow = inputBottom + INPUT_PADDING - visibleHeight;
|
|
72
|
+
return overflow > 0 ? overflow : 0;
|
|
120
73
|
}
|
|
121
|
-
function
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
125
|
-
const
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
74
|
+
function scrollInputIntoView(input, keyboardContainerRef) {
|
|
75
|
+
if (shiftedElements.length > 0) resetScrollPosition();
|
|
76
|
+
const shiftAmount = calculateShiftAmount(input);
|
|
77
|
+
if (shiftAmount === 0) return;
|
|
78
|
+
const contentElements = findContentElements(keyboardContainerRef);
|
|
79
|
+
if (contentElements.length === 0) return;
|
|
80
|
+
injectStyles();
|
|
81
|
+
shiftedElements = contentElements;
|
|
82
|
+
for (const element of contentElements) {
|
|
83
|
+
element.classList.add(TRANSITION_CLASS);
|
|
84
|
+
element.style.transform = `translateY(-${shiftAmount}px)`;
|
|
130
85
|
}
|
|
131
86
|
}
|
|
132
|
-
function
|
|
133
|
-
if (
|
|
134
|
-
for (const
|
|
135
|
-
document.body.contains(
|
|
136
|
-
|
|
87
|
+
function resetScrollPosition() {
|
|
88
|
+
if (shiftedElements.length === 0) return;
|
|
89
|
+
for (const element of shiftedElements) {
|
|
90
|
+
if (document.body.contains(element)) {
|
|
91
|
+
element.style.transform = "translateY(0)";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const elementsToCleanup = [...shiftedElements];
|
|
137
95
|
setTimeout(() => {
|
|
138
|
-
for (const
|
|
139
|
-
document.body.contains(
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
e != null && e.current && J(t, e.current);
|
|
146
|
-
}, 0);
|
|
147
|
-
}, [e]), n = k(() => {
|
|
148
|
-
C();
|
|
149
|
-
}, []);
|
|
150
|
-
return $(() => () => {
|
|
151
|
-
C();
|
|
152
|
-
}, []), {
|
|
153
|
-
scrollInput: r,
|
|
154
|
-
resetScroll: n
|
|
155
|
-
};
|
|
96
|
+
for (const element of elementsToCleanup) {
|
|
97
|
+
if (document.body.contains(element)) {
|
|
98
|
+
element.classList.remove(TRANSITION_CLASS);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}, TRANSITION_DURATION);
|
|
102
|
+
shiftedElements = [];
|
|
156
103
|
}
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
case "tel":
|
|
164
|
-
return /^[0-9+\-() ]*$/.test(e);
|
|
165
|
-
case "url":
|
|
166
|
-
return /^[a-zA-Z0-9:/._-]*$/.test(e);
|
|
167
|
-
case "password":
|
|
168
|
-
return !0;
|
|
169
|
-
default:
|
|
170
|
-
return !0;
|
|
104
|
+
const onEnterClickUtil = (focusedInputRef) => {
|
|
105
|
+
var _a;
|
|
106
|
+
if (focusedInputRef.current) {
|
|
107
|
+
const input = focusedInputRef.current;
|
|
108
|
+
input.blur();
|
|
109
|
+
(_a = input.form) == null ? void 0 : _a.submit();
|
|
171
110
|
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
111
|
+
};
|
|
112
|
+
const handleValueChangeUtil = (focusedInputRef, value) => {
|
|
113
|
+
const input = focusedInputRef.current;
|
|
114
|
+
if (!input) return;
|
|
115
|
+
setInputValueAndDispatchEvents(input, value);
|
|
116
|
+
};
|
|
117
|
+
const validateFocusInputs = (event) => {
|
|
118
|
+
const target = event.target;
|
|
119
|
+
const isInput = target.tagName === "INPUT";
|
|
120
|
+
const isTextarea = target.tagName === "TEXTAREA";
|
|
121
|
+
if (isTextarea) {
|
|
122
|
+
return target;
|
|
177
123
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}, M = (e) => {
|
|
182
|
-
const r = e.target, n = r.tagName === "INPUT";
|
|
183
|
-
if (r.tagName === "TEXTAREA")
|
|
184
|
-
return r;
|
|
185
|
-
if (n) {
|
|
186
|
-
const s = r;
|
|
187
|
-
return [
|
|
124
|
+
if (isInput) {
|
|
125
|
+
const input = target;
|
|
126
|
+
const excludedTypes = [
|
|
188
127
|
"checkbox",
|
|
189
128
|
"radio",
|
|
190
129
|
"range",
|
|
@@ -199,25 +138,118 @@ const ee = (e, r) => {
|
|
|
199
138
|
"reset",
|
|
200
139
|
"button",
|
|
201
140
|
"image"
|
|
202
|
-
]
|
|
141
|
+
];
|
|
142
|
+
if (excludedTypes.includes(input.type)) return null;
|
|
143
|
+
return input;
|
|
203
144
|
}
|
|
204
145
|
return null;
|
|
205
|
-
}
|
|
146
|
+
};
|
|
147
|
+
function createCaretManager(getInputRef) {
|
|
148
|
+
const insertText = (text) => {
|
|
149
|
+
const input = getInputRef();
|
|
150
|
+
if (!input) return;
|
|
151
|
+
if (input.readOnly || input.disabled) return;
|
|
152
|
+
const startRaw = input.selectionStart;
|
|
153
|
+
const endRaw = input.selectionEnd;
|
|
154
|
+
const start = startRaw == null ? input.value.length : startRaw;
|
|
155
|
+
const end = endRaw == null ? input.value.length : endRaw;
|
|
156
|
+
const scrollTop = input.scrollTop ?? 0;
|
|
157
|
+
const scrollLeft = input.scrollLeft ?? 0;
|
|
158
|
+
const newValue = input.value.slice(0, start) + text + input.value.slice(end);
|
|
159
|
+
const caretAfter = start + text.length;
|
|
160
|
+
setInputValueAndDispatchEvents(input, newValue);
|
|
161
|
+
input.focus();
|
|
162
|
+
input.setSelectionRange(caretAfter, caretAfter);
|
|
163
|
+
if ("scrollTop" in input) {
|
|
164
|
+
input.scrollTop = scrollTop;
|
|
165
|
+
input.scrollLeft = scrollLeft;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const backspace = () => {
|
|
169
|
+
const input = getInputRef();
|
|
170
|
+
if (!input) return;
|
|
171
|
+
if (input.readOnly || input.disabled) return;
|
|
172
|
+
const startRaw = input.selectionStart;
|
|
173
|
+
const endRaw = input.selectionEnd;
|
|
174
|
+
const start = startRaw == null ? input.value.length : startRaw;
|
|
175
|
+
const end = endRaw == null ? input.value.length : endRaw;
|
|
176
|
+
const scrollTop = input.scrollTop ?? 0;
|
|
177
|
+
const scrollLeft = input.scrollLeft ?? 0;
|
|
178
|
+
if (start !== end) {
|
|
179
|
+
const newValue = input.value.slice(0, start) + input.value.slice(end);
|
|
180
|
+
setInputValueAndDispatchEvents(input, newValue);
|
|
181
|
+
input.focus();
|
|
182
|
+
input.setSelectionRange(start, start);
|
|
183
|
+
} else if (start > 0) {
|
|
184
|
+
const newValue = input.value.slice(0, start - 1) + input.value.slice(start);
|
|
185
|
+
const newCaret = start - 1;
|
|
186
|
+
setInputValueAndDispatchEvents(input, newValue);
|
|
187
|
+
input.focus();
|
|
188
|
+
input.setSelectionRange(newCaret, newCaret);
|
|
189
|
+
}
|
|
190
|
+
if ("scrollTop" in input) {
|
|
191
|
+
input.scrollTop = scrollTop;
|
|
192
|
+
input.scrollLeft = scrollLeft;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
return {
|
|
196
|
+
insertText,
|
|
197
|
+
backspace
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function setupHardwareKeyboard(handlers) {
|
|
201
|
+
const { onBackspace, onEnter, onSpace, onCapsToggle, onKeyClick } = handlers;
|
|
202
|
+
const handleKeyboardKeyDown = (event) => {
|
|
203
|
+
const key = event.key;
|
|
204
|
+
switch (key) {
|
|
205
|
+
case "Backspace":
|
|
206
|
+
event.preventDefault();
|
|
207
|
+
event.stopPropagation();
|
|
208
|
+
onBackspace();
|
|
209
|
+
return;
|
|
210
|
+
case "Enter":
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
onEnter();
|
|
213
|
+
return;
|
|
214
|
+
case " ":
|
|
215
|
+
event.preventDefault();
|
|
216
|
+
onSpace();
|
|
217
|
+
return;
|
|
218
|
+
case "CapsLock":
|
|
219
|
+
event.preventDefault();
|
|
220
|
+
onCapsToggle();
|
|
221
|
+
return;
|
|
222
|
+
default:
|
|
223
|
+
if (key.length === 1) {
|
|
224
|
+
event.preventDefault();
|
|
225
|
+
onKeyClick(key);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
document.addEventListener("keydown", handleKeyboardKeyDown);
|
|
230
|
+
return () => {
|
|
231
|
+
document.removeEventListener("keydown", handleKeyboardKeyDown);
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const QWERTY_LAYOUT = [
|
|
206
235
|
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
|
|
207
236
|
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
|
|
208
237
|
["a", "s", "d", "f", "g", "h", "j", "k", "l"],
|
|
209
238
|
["z", "x", "c", "v", "b", "n", "m"]
|
|
210
|
-
]
|
|
239
|
+
];
|
|
240
|
+
const SYMBOLS_LAYOUT = [
|
|
211
241
|
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
|
|
212
242
|
["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"],
|
|
213
243
|
["-", "_", "=", "+", "[", "]", "{", "}", "\\", "|"],
|
|
214
244
|
[";", ":", '"', "'", ",", ".", "<", ">", "/", "?"]
|
|
215
|
-
]
|
|
245
|
+
];
|
|
246
|
+
const NUMBERS_LAYOUT = [
|
|
216
247
|
["7", "8", "9", "#"],
|
|
217
248
|
["4", "5", "6", "-"],
|
|
218
249
|
["1", "2", "3"],
|
|
219
250
|
[",", "0", "."]
|
|
220
|
-
]
|
|
251
|
+
];
|
|
252
|
+
const DEFAULT_THEME = {
|
|
221
253
|
backgroundColor: "#1a1a1a",
|
|
222
254
|
keyColor: "#444444",
|
|
223
255
|
keyTextColor: "#ffffff",
|
|
@@ -227,7 +259,8 @@ const ee = (e, r) => {
|
|
|
227
259
|
keyBorderRadius: "0.5vw",
|
|
228
260
|
keyFontSize: "32px",
|
|
229
261
|
keyHeight: "100%"
|
|
230
|
-
}
|
|
262
|
+
};
|
|
263
|
+
const BackspaceIcon = (props) => /* @__PURE__ */ jsx(
|
|
231
264
|
"svg",
|
|
232
265
|
{
|
|
233
266
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -235,10 +268,11 @@ const ee = (e, r) => {
|
|
|
235
268
|
fill: "currentColor",
|
|
236
269
|
width: "1em",
|
|
237
270
|
height: "1em",
|
|
238
|
-
...
|
|
239
|
-
children: /* @__PURE__ */
|
|
271
|
+
...props,
|
|
272
|
+
children: /* @__PURE__ */ jsx("path", { d: "M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.11c.36.53.9.89 1.59.89h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H7.07L2.4 12l4.66-7H22v14zm-11.59-2L14 13.41 17.59 17 19 15.59 15.41 12 19 8.41 17.59 7 14 10.59 10.41 7 9 8.41 12.59 12 9 15.59z" })
|
|
240
273
|
}
|
|
241
|
-
)
|
|
274
|
+
);
|
|
275
|
+
const EnterIcon = (props) => /* @__PURE__ */ jsx(
|
|
242
276
|
"svg",
|
|
243
277
|
{
|
|
244
278
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -246,10 +280,11 @@ const ee = (e, r) => {
|
|
|
246
280
|
fill: "currentColor",
|
|
247
281
|
width: "1em",
|
|
248
282
|
height: "1em",
|
|
249
|
-
...
|
|
250
|
-
children: /* @__PURE__ */
|
|
283
|
+
...props,
|
|
284
|
+
children: /* @__PURE__ */ jsx("path", { d: "M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z" })
|
|
251
285
|
}
|
|
252
|
-
)
|
|
286
|
+
);
|
|
287
|
+
const SpacebarIcon = (props) => /* @__PURE__ */ jsx(
|
|
253
288
|
"svg",
|
|
254
289
|
{
|
|
255
290
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -257,10 +292,11 @@ const ee = (e, r) => {
|
|
|
257
292
|
fill: "currentColor",
|
|
258
293
|
width: "1em",
|
|
259
294
|
height: "1em",
|
|
260
|
-
...
|
|
261
|
-
children: /* @__PURE__ */
|
|
295
|
+
...props,
|
|
296
|
+
children: /* @__PURE__ */ jsx("path", { d: "M18 9v4H6V9H4v6h16V9z" })
|
|
262
297
|
}
|
|
263
|
-
)
|
|
298
|
+
);
|
|
299
|
+
const CapsLockIcon = (props) => /* @__PURE__ */ jsx(
|
|
264
300
|
"svg",
|
|
265
301
|
{
|
|
266
302
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -269,137 +305,211 @@ const ee = (e, r) => {
|
|
|
269
305
|
width: "1em",
|
|
270
306
|
height: "1em",
|
|
271
307
|
style: { transform: "rotate(270deg)" },
|
|
272
|
-
...
|
|
273
|
-
children: /* @__PURE__ */
|
|
308
|
+
...props,
|
|
309
|
+
children: /* @__PURE__ */ jsx("path", { d: "M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z" })
|
|
274
310
|
}
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
311
|
+
);
|
|
312
|
+
function useContinuousPress(onPress, {
|
|
313
|
+
initialDelay = 500,
|
|
314
|
+
interval = 50,
|
|
315
|
+
shouldPreventDefault = true
|
|
316
|
+
} = {}) {
|
|
317
|
+
const timeoutRef = useRef();
|
|
318
|
+
const intervalRef = useRef();
|
|
319
|
+
const clear = useCallback(() => {
|
|
320
|
+
if (timeoutRef.current) {
|
|
321
|
+
clearTimeout(timeoutRef.current);
|
|
322
|
+
timeoutRef.current = void 0;
|
|
323
|
+
}
|
|
324
|
+
if (intervalRef.current) {
|
|
325
|
+
clearInterval(intervalRef.current);
|
|
326
|
+
intervalRef.current = void 0;
|
|
327
|
+
}
|
|
328
|
+
}, []);
|
|
329
|
+
const start = useCallback(() => {
|
|
330
|
+
onPress();
|
|
331
|
+
timeoutRef.current = setTimeout(() => {
|
|
332
|
+
intervalRef.current = setInterval(onPress, interval);
|
|
333
|
+
}, initialDelay);
|
|
334
|
+
}, [onPress, initialDelay, interval]);
|
|
335
|
+
return {
|
|
336
|
+
onMouseDown: (e) => {
|
|
337
|
+
e.preventDefault();
|
|
338
|
+
start();
|
|
339
|
+
},
|
|
340
|
+
onTouchStart: (e) => {
|
|
341
|
+
if (shouldPreventDefault) e.preventDefault();
|
|
342
|
+
start();
|
|
343
|
+
},
|
|
344
|
+
onMouseUp: clear,
|
|
345
|
+
onMouseLeave: clear,
|
|
346
|
+
onTouchEnd: (e) => {
|
|
347
|
+
if (shouldPreventDefault) e.preventDefault();
|
|
348
|
+
clear();
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function useKeyboardScroll(keyboardContainerRef) {
|
|
353
|
+
const handleScrollInput = useCallback((input) => {
|
|
354
|
+
setTimeout(() => {
|
|
355
|
+
if (!(keyboardContainerRef == null ? void 0 : keyboardContainerRef.current)) return;
|
|
356
|
+
scrollInputIntoView(input, keyboardContainerRef.current);
|
|
357
|
+
}, 0);
|
|
358
|
+
}, [keyboardContainerRef]);
|
|
359
|
+
const handleResetScroll = useCallback(() => {
|
|
360
|
+
resetScrollPosition();
|
|
361
|
+
}, []);
|
|
362
|
+
useEffect(() => {
|
|
363
|
+
return () => {
|
|
364
|
+
resetScrollPosition();
|
|
365
|
+
};
|
|
366
|
+
}, []);
|
|
367
|
+
return {
|
|
368
|
+
scrollInput: handleScrollInput,
|
|
369
|
+
resetScroll: handleResetScroll
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
const SpecialKey = memo((props) => {
|
|
373
|
+
const {
|
|
374
|
+
type,
|
|
375
|
+
icon,
|
|
376
|
+
onClick,
|
|
377
|
+
extraClass = "",
|
|
378
|
+
text,
|
|
379
|
+
capsLock = false,
|
|
380
|
+
enableContinuousPress = false
|
|
381
|
+
} = props;
|
|
382
|
+
const isCapsLockActive = type === "caps" && capsLock;
|
|
383
|
+
const keyClasses = [
|
|
285
384
|
"vk-key",
|
|
286
|
-
`vk-key--${
|
|
287
|
-
|
|
288
|
-
].filter(Boolean).join(" ")
|
|
385
|
+
`vk-key--${extraClass}`,
|
|
386
|
+
isCapsLockActive ? "vk-key--caps-active" : ""
|
|
387
|
+
].filter(Boolean).join(" ");
|
|
388
|
+
const continuousPressHandlers = useContinuousPress(onClick, {
|
|
289
389
|
initialDelay: 500,
|
|
290
390
|
interval: 50
|
|
291
|
-
})
|
|
292
|
-
|
|
391
|
+
});
|
|
392
|
+
const buttonHandlers = enableContinuousPress ? continuousPressHandlers : { onClick };
|
|
393
|
+
return /* @__PURE__ */ jsxs(
|
|
293
394
|
"button",
|
|
294
395
|
{
|
|
295
396
|
type: "button",
|
|
296
|
-
className:
|
|
297
|
-
"data-testid": `${
|
|
298
|
-
"data-key":
|
|
299
|
-
...
|
|
397
|
+
className: keyClasses,
|
|
398
|
+
"data-testid": `${type}${isCapsLockActive ? "-active" : ""}`,
|
|
399
|
+
"data-key": isCapsLockActive ? `${type}-active` : type,
|
|
400
|
+
...buttonHandlers,
|
|
300
401
|
children: [
|
|
301
|
-
|
|
302
|
-
|
|
402
|
+
icon && icon,
|
|
403
|
+
text && /* @__PURE__ */ jsx("span", { className: "vk-key__text", children: text })
|
|
303
404
|
]
|
|
304
405
|
}
|
|
305
406
|
);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
|
|
407
|
+
});
|
|
408
|
+
SpecialKey.displayName = "SpecialKey";
|
|
409
|
+
const VirtualKey = memo(({ keyValue, onClick, className = "" }) => {
|
|
410
|
+
const keyClasses = ["vk-key", className].filter(Boolean).join(" ");
|
|
411
|
+
return /* @__PURE__ */ jsx(
|
|
309
412
|
"button",
|
|
310
413
|
{
|
|
311
414
|
type: "button",
|
|
312
|
-
className:
|
|
313
|
-
onClick: () =>
|
|
314
|
-
"data-testid":
|
|
315
|
-
children:
|
|
415
|
+
className: keyClasses,
|
|
416
|
+
onClick: () => onClick(keyValue),
|
|
417
|
+
"data-testid": keyValue,
|
|
418
|
+
children: keyValue
|
|
316
419
|
}
|
|
317
420
|
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
{
|
|
331
|
-
keyValue: o,
|
|
332
|
-
onClick: t
|
|
333
|
-
},
|
|
334
|
-
`num-${c}-${u}-${o}`
|
|
335
|
-
)),
|
|
336
|
-
c === 3 && /* @__PURE__ */ a(
|
|
337
|
-
T,
|
|
338
|
-
{
|
|
339
|
-
type: "enter",
|
|
340
|
-
icon: /* @__PURE__ */ a(j, {}),
|
|
341
|
-
onClick: n,
|
|
342
|
-
extraClass: "enter-num",
|
|
343
|
-
text: "Enter",
|
|
344
|
-
capsLock: s
|
|
345
|
-
},
|
|
346
|
-
"enter-num"
|
|
347
|
-
),
|
|
348
|
-
c === 2 && /* @__PURE__ */ a(
|
|
349
|
-
T,
|
|
350
|
-
{
|
|
351
|
-
type: "backspace",
|
|
352
|
-
icon: /* @__PURE__ */ a(U, {}),
|
|
353
|
-
onClick: r,
|
|
354
|
-
extraClass: "backspace-num",
|
|
355
|
-
text: "Backspace",
|
|
356
|
-
capsLock: s,
|
|
357
|
-
enableContinuousPress: !0
|
|
358
|
-
},
|
|
359
|
-
"backspace-num"
|
|
360
|
-
)
|
|
361
|
-
] }, `num-row-${c}`)) }), ae = ({
|
|
362
|
-
inputType: e,
|
|
363
|
-
currentLayoutData: r,
|
|
364
|
-
onBackspace: n,
|
|
365
|
-
onEnter: t,
|
|
366
|
-
onSpace: s,
|
|
367
|
-
onCapsToggle: l,
|
|
368
|
-
onLayoutToggle: c,
|
|
369
|
-
onKeyClick: o,
|
|
370
|
-
capsLock: u,
|
|
371
|
-
currentLayout: f
|
|
421
|
+
});
|
|
422
|
+
VirtualKey.displayName = "VirtualKey";
|
|
423
|
+
const KeyboardRow = ({ children, className = "" }) => {
|
|
424
|
+
const rowClasses = ["vk-row", className].filter(Boolean).join(" ");
|
|
425
|
+
return /* @__PURE__ */ jsx("div", { className: rowClasses, children });
|
|
426
|
+
};
|
|
427
|
+
const NumbersLayout = ({
|
|
428
|
+
currentLayoutData,
|
|
429
|
+
onBackspace,
|
|
430
|
+
onEnter,
|
|
431
|
+
onKeyClick,
|
|
432
|
+
capsLock
|
|
372
433
|
}) => {
|
|
373
|
-
|
|
374
|
-
|
|
434
|
+
return /* @__PURE__ */ jsx("div", { className: "vk-layout vk-layout--numbers", "data-testid": "keyboard-layout", children: currentLayoutData == null ? void 0 : currentLayoutData.map((row, rowIndex) => /* @__PURE__ */ jsxs(KeyboardRow, { children: [
|
|
435
|
+
row == null ? void 0 : row.map((key, keyIndex) => /* @__PURE__ */ jsx(
|
|
436
|
+
VirtualKey,
|
|
437
|
+
{
|
|
438
|
+
keyValue: key,
|
|
439
|
+
onClick: onKeyClick
|
|
440
|
+
},
|
|
441
|
+
`num-${rowIndex}-${keyIndex}-${key}`
|
|
442
|
+
)),
|
|
443
|
+
rowIndex === 3 && /* @__PURE__ */ jsx(
|
|
444
|
+
SpecialKey,
|
|
445
|
+
{
|
|
446
|
+
type: "enter",
|
|
447
|
+
icon: /* @__PURE__ */ jsx(EnterIcon, {}),
|
|
448
|
+
onClick: onEnter,
|
|
449
|
+
extraClass: "enter-num",
|
|
450
|
+
text: "Enter",
|
|
451
|
+
capsLock
|
|
452
|
+
},
|
|
453
|
+
"enter-num"
|
|
454
|
+
),
|
|
455
|
+
rowIndex === 2 && /* @__PURE__ */ jsx(
|
|
456
|
+
SpecialKey,
|
|
457
|
+
{
|
|
458
|
+
type: "backspace",
|
|
459
|
+
icon: /* @__PURE__ */ jsx(BackspaceIcon, {}),
|
|
460
|
+
onClick: onBackspace,
|
|
461
|
+
extraClass: "backspace-num",
|
|
462
|
+
text: "Backspace",
|
|
463
|
+
capsLock,
|
|
464
|
+
enableContinuousPress: true
|
|
465
|
+
},
|
|
466
|
+
"backspace-num"
|
|
467
|
+
)
|
|
468
|
+
] }, `num-row-${rowIndex}`)) });
|
|
469
|
+
};
|
|
470
|
+
const TextLayout = ({
|
|
471
|
+
inputType,
|
|
472
|
+
currentLayoutData,
|
|
473
|
+
onBackspace,
|
|
474
|
+
onEnter,
|
|
475
|
+
onSpace,
|
|
476
|
+
onCapsToggle,
|
|
477
|
+
onLayoutToggle,
|
|
478
|
+
onKeyClick,
|
|
479
|
+
capsLock,
|
|
480
|
+
currentLayout
|
|
481
|
+
}) => {
|
|
482
|
+
const renderSpecialKeysLeft = (rowIndex) => {
|
|
483
|
+
switch (rowIndex) {
|
|
375
484
|
case 3:
|
|
376
|
-
return
|
|
377
|
-
|
|
485
|
+
return currentLayout === "letters" && /* @__PURE__ */ jsx(
|
|
486
|
+
SpecialKey,
|
|
378
487
|
{
|
|
379
488
|
type: "caps",
|
|
380
|
-
icon: /* @__PURE__ */
|
|
381
|
-
onClick:
|
|
489
|
+
icon: /* @__PURE__ */ jsx(CapsLockIcon, {}),
|
|
490
|
+
onClick: onCapsToggle,
|
|
382
491
|
extraClass: "capsLock",
|
|
383
492
|
text: "Caps Lock",
|
|
384
|
-
capsLock
|
|
493
|
+
capsLock
|
|
385
494
|
},
|
|
386
495
|
"caps"
|
|
387
496
|
);
|
|
388
497
|
default:
|
|
389
498
|
return null;
|
|
390
499
|
}
|
|
391
|
-
}
|
|
392
|
-
|
|
500
|
+
};
|
|
501
|
+
const renderSpecialKeysRight = (rowIndex) => {
|
|
502
|
+
switch (rowIndex) {
|
|
393
503
|
case 3:
|
|
394
|
-
return /* @__PURE__ */
|
|
395
|
-
|
|
504
|
+
return /* @__PURE__ */ jsx(
|
|
505
|
+
SpecialKey,
|
|
396
506
|
{
|
|
397
507
|
type: "backspace",
|
|
398
|
-
icon: /* @__PURE__ */
|
|
399
|
-
onClick:
|
|
508
|
+
icon: /* @__PURE__ */ jsx(BackspaceIcon, {}),
|
|
509
|
+
onClick: onBackspace,
|
|
400
510
|
extraClass: "backspace",
|
|
401
511
|
text: "Backspace",
|
|
402
|
-
enableContinuousPress:
|
|
512
|
+
enableContinuousPress: true
|
|
403
513
|
},
|
|
404
514
|
"backspace"
|
|
405
515
|
);
|
|
@@ -407,72 +517,72 @@ const ee = (e, r) => {
|
|
|
407
517
|
return null;
|
|
408
518
|
}
|
|
409
519
|
};
|
|
410
|
-
return /* @__PURE__ */
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const
|
|
415
|
-
return /* @__PURE__ */
|
|
416
|
-
|
|
520
|
+
return /* @__PURE__ */ jsxs("div", { className: "vk-layout vk-layout--text", "data-testid": "keyboard-layout", children: [
|
|
521
|
+
currentLayoutData == null ? void 0 : currentLayoutData.map((row, rowIndex) => /* @__PURE__ */ jsxs(KeyboardRow, { children: [
|
|
522
|
+
renderSpecialKeysLeft(rowIndex),
|
|
523
|
+
row.map((key, keyIndex) => {
|
|
524
|
+
const displayKey = capsLock ? key.toUpperCase() : key.toLowerCase();
|
|
525
|
+
return /* @__PURE__ */ jsx(
|
|
526
|
+
VirtualKey,
|
|
417
527
|
{
|
|
418
|
-
keyValue:
|
|
419
|
-
onClick:
|
|
528
|
+
keyValue: displayKey,
|
|
529
|
+
onClick: onKeyClick
|
|
420
530
|
},
|
|
421
|
-
`${
|
|
531
|
+
`${rowIndex}-${keyIndex}-${key}`
|
|
422
532
|
);
|
|
423
533
|
}),
|
|
424
|
-
|
|
425
|
-
] }, `row-${
|
|
426
|
-
/* @__PURE__ */
|
|
427
|
-
/* @__PURE__ */
|
|
428
|
-
|
|
534
|
+
renderSpecialKeysRight(rowIndex)
|
|
535
|
+
] }, `row-${rowIndex}`)),
|
|
536
|
+
/* @__PURE__ */ jsxs(KeyboardRow, { children: [
|
|
537
|
+
/* @__PURE__ */ jsx(
|
|
538
|
+
SpecialKey,
|
|
429
539
|
{
|
|
430
540
|
type: "layout",
|
|
431
|
-
icon:
|
|
432
|
-
onClick:
|
|
541
|
+
icon: currentLayout === "letters" ? "&123" : "ABC",
|
|
542
|
+
onClick: onLayoutToggle,
|
|
433
543
|
extraClass: "layout",
|
|
434
544
|
text: ""
|
|
435
545
|
},
|
|
436
546
|
"layout"
|
|
437
547
|
),
|
|
438
|
-
|
|
439
|
-
|
|
548
|
+
inputType === "email" && /* @__PURE__ */ jsx(
|
|
549
|
+
SpecialKey,
|
|
440
550
|
{
|
|
441
551
|
type: "dot",
|
|
442
|
-
onClick: () =>
|
|
552
|
+
onClick: () => onKeyClick("."),
|
|
443
553
|
extraClass: "dot",
|
|
444
554
|
icon: "."
|
|
445
555
|
},
|
|
446
556
|
"dot"
|
|
447
557
|
),
|
|
448
|
-
/* @__PURE__ */
|
|
449
|
-
|
|
558
|
+
/* @__PURE__ */ jsx(
|
|
559
|
+
SpecialKey,
|
|
450
560
|
{
|
|
451
561
|
type: "space",
|
|
452
|
-
icon: /* @__PURE__ */
|
|
453
|
-
onClick:
|
|
562
|
+
icon: /* @__PURE__ */ jsx(SpacebarIcon, {}),
|
|
563
|
+
onClick: onSpace,
|
|
454
564
|
extraClass: "space",
|
|
455
565
|
text: "Space"
|
|
456
566
|
},
|
|
457
567
|
"space"
|
|
458
568
|
),
|
|
459
|
-
|
|
460
|
-
|
|
569
|
+
inputType === "email" && /* @__PURE__ */ jsx(
|
|
570
|
+
SpecialKey,
|
|
461
571
|
{
|
|
462
572
|
type: "at",
|
|
463
573
|
icon: "@",
|
|
464
|
-
onClick: () =>
|
|
574
|
+
onClick: () => onKeyClick("@"),
|
|
465
575
|
extraClass: "at",
|
|
466
576
|
text: ""
|
|
467
577
|
},
|
|
468
578
|
"at"
|
|
469
579
|
),
|
|
470
|
-
/* @__PURE__ */
|
|
471
|
-
|
|
580
|
+
/* @__PURE__ */ jsx(
|
|
581
|
+
SpecialKey,
|
|
472
582
|
{
|
|
473
583
|
type: "enter",
|
|
474
|
-
icon: /* @__PURE__ */
|
|
475
|
-
onClick:
|
|
584
|
+
icon: /* @__PURE__ */ jsx(EnterIcon, {}),
|
|
585
|
+
onClick: onEnter,
|
|
476
586
|
extraClass: "enter",
|
|
477
587
|
text: "Enter"
|
|
478
588
|
},
|
|
@@ -480,199 +590,299 @@ const ee = (e, r) => {
|
|
|
480
590
|
)
|
|
481
591
|
] })
|
|
482
592
|
] });
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
593
|
+
};
|
|
594
|
+
const KeyboardLayout = ({
|
|
595
|
+
currentLayout,
|
|
596
|
+
capsLock,
|
|
597
|
+
onKeyClick,
|
|
598
|
+
onBackspace,
|
|
599
|
+
onEnter,
|
|
600
|
+
onSpace,
|
|
601
|
+
onCapsToggle,
|
|
602
|
+
onLayoutToggle,
|
|
603
|
+
inputType,
|
|
604
|
+
customLayouts
|
|
493
605
|
}) => {
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
606
|
+
const currentLayoutData = currentLayout === "letters" ? (customLayouts == null ? void 0 : customLayouts.letters) || QWERTY_LAYOUT : currentLayout === "symbols" ? (customLayouts == null ? void 0 : customLayouts.symbols) || SYMBOLS_LAYOUT : (customLayouts == null ? void 0 : customLayouts.numbers) || NUMBERS_LAYOUT;
|
|
607
|
+
if (currentLayout === "numbers") {
|
|
608
|
+
return /* @__PURE__ */ jsx(
|
|
609
|
+
NumbersLayout,
|
|
610
|
+
{
|
|
611
|
+
currentLayoutData,
|
|
612
|
+
onBackspace,
|
|
613
|
+
onEnter,
|
|
614
|
+
onKeyClick,
|
|
615
|
+
capsLock,
|
|
616
|
+
currentLayout
|
|
617
|
+
}
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
return /* @__PURE__ */ jsx(
|
|
621
|
+
TextLayout,
|
|
507
622
|
{
|
|
508
|
-
inputType
|
|
509
|
-
currentLayoutData
|
|
510
|
-
onBackspace
|
|
511
|
-
onEnter
|
|
512
|
-
onSpace
|
|
513
|
-
onCapsToggle
|
|
514
|
-
onLayoutToggle
|
|
515
|
-
onKeyClick
|
|
516
|
-
capsLock
|
|
517
|
-
currentLayout
|
|
623
|
+
inputType,
|
|
624
|
+
currentLayoutData,
|
|
625
|
+
onBackspace,
|
|
626
|
+
onEnter,
|
|
627
|
+
onSpace,
|
|
628
|
+
onCapsToggle,
|
|
629
|
+
onLayoutToggle,
|
|
630
|
+
onKeyClick,
|
|
631
|
+
capsLock,
|
|
632
|
+
currentLayout
|
|
518
633
|
}
|
|
519
634
|
);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
635
|
+
};
|
|
636
|
+
const VirtualKeyboardContainer = ({
|
|
637
|
+
children,
|
|
638
|
+
className = ""
|
|
523
639
|
}) => {
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
640
|
+
const handleMouseDown = (e) => {
|
|
641
|
+
e.preventDefault();
|
|
642
|
+
};
|
|
643
|
+
const handleClick = (e) => {
|
|
644
|
+
e.preventDefault();
|
|
645
|
+
e.stopPropagation();
|
|
646
|
+
};
|
|
647
|
+
const containerClasses = ["vk-container", className].filter(Boolean).join(" ");
|
|
648
|
+
return /* @__PURE__ */ jsx(
|
|
530
649
|
"div",
|
|
531
650
|
{
|
|
532
|
-
className:
|
|
533
|
-
onMouseDown:
|
|
534
|
-
onClick:
|
|
651
|
+
className: containerClasses,
|
|
652
|
+
onMouseDown: handleMouseDown,
|
|
653
|
+
onClick: handleClick,
|
|
535
654
|
"data-testid": "keyboard-container",
|
|
536
|
-
children
|
|
655
|
+
children
|
|
537
656
|
}
|
|
538
657
|
);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
658
|
+
};
|
|
659
|
+
const VirtualKeyboard = ({
|
|
660
|
+
focusedInputRef,
|
|
661
|
+
isInputFocused,
|
|
662
|
+
inputType = "text",
|
|
663
|
+
onEnterClick,
|
|
664
|
+
onChange,
|
|
665
|
+
className,
|
|
666
|
+
defaultLayout = "letters",
|
|
667
|
+
validate,
|
|
668
|
+
syncWithHardwareKeyboard = true,
|
|
669
|
+
customLayouts,
|
|
670
|
+
languages,
|
|
671
|
+
currentLanguage,
|
|
672
|
+
onLanguageChange,
|
|
673
|
+
showLanguageSwitcher = false
|
|
548
674
|
}) => {
|
|
549
|
-
const [
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
675
|
+
const [capsLock, setCapsLock] = useState(false);
|
|
676
|
+
const [selectedLanguage, setSelectedLanguage] = useState(currentLanguage || Object.keys(languages || {})[0] || "en");
|
|
677
|
+
const { insertText, backspace } = createCaretManager(() => focusedInputRef.current);
|
|
678
|
+
const [currentLayout, setCurrentLayout] = useState(
|
|
679
|
+
() => getInitialLayout(inputType, defaultLayout)
|
|
680
|
+
);
|
|
681
|
+
const updateValue = useCallback(
|
|
682
|
+
(next) => {
|
|
683
|
+
var _a;
|
|
684
|
+
if (validate && !validate(next)) return;
|
|
685
|
+
if (!validateValueUtil(next, inputType)) return;
|
|
686
|
+
insertText(next);
|
|
687
|
+
onChange == null ? void 0 : onChange(((_a = focusedInputRef.current) == null ? void 0 : _a.value) ?? "");
|
|
555
688
|
},
|
|
556
|
-
[
|
|
557
|
-
)
|
|
558
|
-
|
|
559
|
-
|
|
689
|
+
[focusedInputRef, inputType, insertText, onChange, validate]
|
|
690
|
+
);
|
|
691
|
+
const handleKeyClick = useCallback(
|
|
692
|
+
(key) => {
|
|
693
|
+
updateValue(key);
|
|
560
694
|
},
|
|
561
|
-
[
|
|
562
|
-
)
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
}, [])
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
695
|
+
[updateValue]
|
|
696
|
+
);
|
|
697
|
+
const handleBackspace = useCallback(() => {
|
|
698
|
+
var _a, _b;
|
|
699
|
+
if (((_a = focusedInputRef.current) == null ? void 0 : _a.value.length) === 0) return;
|
|
700
|
+
backspace();
|
|
701
|
+
onChange == null ? void 0 : onChange(((_b = focusedInputRef.current) == null ? void 0 : _b.value) ?? "");
|
|
702
|
+
}, [backspace, focusedInputRef, onChange]);
|
|
703
|
+
const handleEnter = useCallback(() => {
|
|
704
|
+
onEnterClick == null ? void 0 : onEnterClick();
|
|
705
|
+
}, [onEnterClick]);
|
|
706
|
+
const handleSpace = useCallback(() => {
|
|
707
|
+
var _a;
|
|
708
|
+
updateValue(" ");
|
|
709
|
+
insertText(" ");
|
|
710
|
+
onChange == null ? void 0 : onChange(((_a = focusedInputRef.current) == null ? void 0 : _a.value) || "");
|
|
711
|
+
}, [insertText, onChange, focusedInputRef]);
|
|
712
|
+
const handleCapsToggle = useCallback(() => {
|
|
713
|
+
setCapsLock((prev) => !prev);
|
|
714
|
+
}, []);
|
|
715
|
+
useEffect(() => {
|
|
716
|
+
setCurrentLayout(getInitialLayout(inputType, defaultLayout));
|
|
717
|
+
}, [inputType, defaultLayout]);
|
|
718
|
+
const keysHandlers = {
|
|
719
|
+
onBackspace: handleBackspace,
|
|
720
|
+
onEnter: handleEnter,
|
|
721
|
+
onSpace: handleSpace,
|
|
722
|
+
onCapsToggle: handleCapsToggle,
|
|
723
|
+
onKeyClick: handleKeyClick
|
|
583
724
|
};
|
|
584
|
-
|
|
585
|
-
isInputFocused
|
|
586
|
-
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
)
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
725
|
+
useEffect(() => {
|
|
726
|
+
if (!isInputFocused || !syncWithHardwareKeyboard) return;
|
|
727
|
+
return setupHardwareKeyboard(keysHandlers);
|
|
728
|
+
}, [
|
|
729
|
+
isInputFocused,
|
|
730
|
+
syncWithHardwareKeyboard,
|
|
731
|
+
handleKeyClick,
|
|
732
|
+
handleBackspace,
|
|
733
|
+
handleEnter,
|
|
734
|
+
handleSpace,
|
|
735
|
+
handleCapsToggle
|
|
736
|
+
]);
|
|
737
|
+
const handleLanguageChange = (lang) => {
|
|
738
|
+
setSelectedLanguage(lang);
|
|
739
|
+
onLanguageChange == null ? void 0 : onLanguageChange(lang);
|
|
740
|
+
};
|
|
741
|
+
const activeLayouts = (languages == null ? void 0 : languages[selectedLanguage]) || customLayouts;
|
|
742
|
+
return /* @__PURE__ */ jsxs(VirtualKeyboardContainer, { className, children: [
|
|
743
|
+
showLanguageSwitcher && languages && Object.keys(languages).length > 1 && /* @__PURE__ */ jsx("div", { className: "vk-language-switcher", children: Object.entries(languages).map(([code, config]) => /* @__PURE__ */ jsx(
|
|
744
|
+
"button",
|
|
745
|
+
{
|
|
746
|
+
className: `vk-lang-btn ${selectedLanguage === code ? "active" : ""}`,
|
|
747
|
+
onClick: () => handleLanguageChange(code),
|
|
748
|
+
children: config.label || code.toUpperCase()
|
|
749
|
+
},
|
|
750
|
+
code
|
|
751
|
+
)) }),
|
|
752
|
+
/* @__PURE__ */ jsx(
|
|
753
|
+
KeyboardLayout,
|
|
754
|
+
{
|
|
755
|
+
capsLock,
|
|
756
|
+
currentLayout,
|
|
757
|
+
onKeyClick: handleKeyClick,
|
|
758
|
+
onBackspace: handleBackspace,
|
|
759
|
+
onEnter: handleEnter,
|
|
760
|
+
onSpace: handleSpace,
|
|
761
|
+
onCapsToggle: handleCapsToggle,
|
|
762
|
+
onLayoutToggle: () => setCurrentLayout((prev) => prev === "letters" ? "symbols" : "letters"),
|
|
763
|
+
inputType,
|
|
764
|
+
customLayouts: activeLayouts
|
|
765
|
+
}
|
|
766
|
+
)
|
|
767
|
+
] });
|
|
768
|
+
};
|
|
769
|
+
const GlobalVirtualKeyboard = ({
|
|
770
|
+
enabled = true,
|
|
771
|
+
className,
|
|
772
|
+
onVisibilityChange,
|
|
773
|
+
onEnterClick,
|
|
774
|
+
onChange
|
|
603
775
|
}) => {
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
776
|
+
const keyboardContainerRef = useRef(null);
|
|
777
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
778
|
+
const [inputType, setInputType] = useState("text");
|
|
779
|
+
const focusedInputRef = useRef(null);
|
|
780
|
+
const originalInputTypeRef = useRef("text");
|
|
781
|
+
const { scrollInput, resetScroll } = useKeyboardScroll(keyboardContainerRef);
|
|
782
|
+
useEffect(() => {
|
|
783
|
+
if (!enabled) {
|
|
784
|
+
if (isVisible) {
|
|
785
|
+
setTimeout(() => {
|
|
786
|
+
setIsVisible(false);
|
|
787
|
+
}, 0);
|
|
788
|
+
onVisibilityChange == null ? void 0 : onVisibilityChange(false);
|
|
789
|
+
}
|
|
610
790
|
return;
|
|
611
791
|
}
|
|
612
|
-
const
|
|
613
|
-
const
|
|
614
|
-
if (!
|
|
615
|
-
const
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
792
|
+
const handleFocus = (event) => {
|
|
793
|
+
const input = validateFocusInputs(event);
|
|
794
|
+
if (!input) return;
|
|
795
|
+
const isTextarea = input.tagName === "TEXTAREA";
|
|
796
|
+
const detectedInputType = isTextarea ? "text" : input.type;
|
|
797
|
+
setInputType(detectedInputType);
|
|
798
|
+
setIsVisible(true);
|
|
799
|
+
onVisibilityChange == null ? void 0 : onVisibilityChange(true);
|
|
800
|
+
focusedInputRef.current = input;
|
|
801
|
+
if (!isTextarea) {
|
|
802
|
+
originalInputTypeRef.current = input.type;
|
|
803
|
+
if (input.type !== "text") {
|
|
804
|
+
const selectionStart = input.selectionStart;
|
|
805
|
+
const selectionEnd = input.selectionEnd;
|
|
806
|
+
input.type = "text";
|
|
807
|
+
const caretPos = selectionStart ?? input.value.length;
|
|
808
|
+
const caretEnd = selectionEnd ?? caretPos;
|
|
809
|
+
input.setSelectionRange(caretPos, caretEnd);
|
|
810
|
+
}
|
|
621
811
|
}
|
|
622
|
-
|
|
623
|
-
}, b = (S) => {
|
|
624
|
-
const i = M(S);
|
|
625
|
-
if (!i) return;
|
|
626
|
-
i.tagName === "TEXTAREA" || (i.type = v.current), d.current === i && (d.current = null, o(!1), n == null || n(!1), h());
|
|
812
|
+
scrollInput(input);
|
|
627
813
|
};
|
|
628
|
-
|
|
629
|
-
|
|
814
|
+
const handleBlur = (event) => {
|
|
815
|
+
const input = validateFocusInputs(event);
|
|
816
|
+
if (!input) return;
|
|
817
|
+
const isTextarea = input.tagName === "TEXTAREA";
|
|
818
|
+
if (!isTextarea) {
|
|
819
|
+
input.type = originalInputTypeRef.current;
|
|
820
|
+
}
|
|
821
|
+
if (focusedInputRef.current === input) {
|
|
822
|
+
focusedInputRef.current = null;
|
|
823
|
+
setIsVisible(false);
|
|
824
|
+
onVisibilityChange == null ? void 0 : onVisibilityChange(false);
|
|
825
|
+
resetScroll();
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
document.addEventListener("focusin", handleFocus, true);
|
|
829
|
+
document.addEventListener("focusout", handleBlur, true);
|
|
830
|
+
return () => {
|
|
831
|
+
document.removeEventListener("focusin", handleFocus, true);
|
|
832
|
+
document.removeEventListener("focusout", handleBlur, true);
|
|
630
833
|
};
|
|
631
|
-
}, [
|
|
632
|
-
const
|
|
633
|
-
|
|
834
|
+
}, [enabled, scrollInput, resetScroll, onVisibilityChange]);
|
|
835
|
+
const handleEnterClick = () => {
|
|
836
|
+
setIsVisible(false);
|
|
837
|
+
onVisibilityChange == null ? void 0 : onVisibilityChange(false);
|
|
838
|
+
onEnterClickUtil(focusedInputRef);
|
|
839
|
+
onEnterClick == null ? void 0 : onEnterClick();
|
|
840
|
+
resetScroll();
|
|
634
841
|
};
|
|
635
|
-
|
|
636
|
-
|
|
842
|
+
if (!isVisible || !enabled) {
|
|
843
|
+
return null;
|
|
844
|
+
}
|
|
845
|
+
return /* @__PURE__ */ jsx("span", { ref: keyboardContainerRef, children: /* @__PURE__ */ jsx(
|
|
846
|
+
VirtualKeyboard,
|
|
637
847
|
{
|
|
638
|
-
focusedInputRef
|
|
639
|
-
isInputFocused:
|
|
640
|
-
inputType
|
|
641
|
-
onEnterClick:
|
|
642
|
-
onChange
|
|
643
|
-
className
|
|
848
|
+
focusedInputRef,
|
|
849
|
+
isInputFocused: isVisible,
|
|
850
|
+
inputType,
|
|
851
|
+
onEnterClick: handleEnterClick,
|
|
852
|
+
onChange,
|
|
853
|
+
className
|
|
644
854
|
}
|
|
645
855
|
) });
|
|
646
856
|
};
|
|
647
857
|
export {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
858
|
+
BackspaceIcon,
|
|
859
|
+
CapsLockIcon,
|
|
860
|
+
DEFAULT_THEME,
|
|
861
|
+
EnterIcon,
|
|
862
|
+
GlobalVirtualKeyboard,
|
|
863
|
+
KeyboardLayout,
|
|
864
|
+
KeyboardRow,
|
|
865
|
+
NUMBERS_LAYOUT,
|
|
866
|
+
NumbersLayout,
|
|
867
|
+
QWERTY_LAYOUT,
|
|
868
|
+
SYMBOLS_LAYOUT,
|
|
869
|
+
SpacebarIcon,
|
|
870
|
+
SpecialKey,
|
|
871
|
+
TextLayout,
|
|
872
|
+
VirtualKey,
|
|
873
|
+
VirtualKeyboard,
|
|
874
|
+
VirtualKeyboardContainer,
|
|
875
|
+
createCaretManager,
|
|
876
|
+
getInitialLayout,
|
|
877
|
+
handleValueChangeUtil,
|
|
878
|
+
onEnterClickUtil,
|
|
879
|
+
resetScrollPosition,
|
|
880
|
+
scrollInputIntoView,
|
|
881
|
+
setInputValueAndDispatchEvents,
|
|
882
|
+
setupHardwareKeyboard,
|
|
883
|
+
useContinuousPress,
|
|
884
|
+
useKeyboardScroll,
|
|
885
|
+
validateFocusInputs,
|
|
886
|
+
validateValueUtil
|
|
677
887
|
};
|
|
678
888
|
//# sourceMappingURL=index.esm.js.map
|