reactjs-virtual-keyboard
Version:
A customizable virtual keyboard component for React applications with support for multiple layouts, multi-language support, hardware keyboard sync, and touch devices
889 lines (888 loc) • 26.1 kB
JavaScript
import { jsx, jsxs } from "react/jsx-runtime";
import { useRef, useCallback, useEffect, memo, useState } from "react";
const setInputValueAndDispatchEvents = (input, value, options = {}) => {
var _a;
const { skipValueAssignment = false } = options;
if (!skipValueAssignment) {
const proto = Object.getPrototypeOf(input);
const valueSetter = (_a = Object.getOwnPropertyDescriptor(proto, "value")) == null ? void 0 : _a.set;
if (valueSetter) {
valueSetter.call(input, value);
}
}
input.dispatchEvent(new InputEvent("input", { bubbles: true, cancelable: true }));
input.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
};
const validateValueUtil = (value, inputType) => {
switch (inputType) {
case "number":
return /^[0-9]*$/.test(value);
case "email":
return /^[a-zA-Z0-9@._+-]*$/.test(value);
case "tel":
return /^[0-9+\-() ]*$/.test(value);
case "url":
return /^[a-zA-Z0-9:/._-]*$/.test(value);
case "password":
return true;
default:
return true;
}
};
const getInitialLayout = (inputType, defaultLayout) => {
if (inputType === "number") {
return "numbers";
}
return defaultLayout;
};
const KEYBOARD_HEIGHT_VH = 38;
const INPUT_PADDING = 20;
const TRANSITION_CLASS = "vk-keyboard-shift-transition";
const TRANSITION_DURATION = 300;
let shiftedElements = [];
let styleInjected = false;
function injectStyles() {
if (styleInjected) return;
const style = document.createElement("style");
style.id = "vk-keyboard-shift-styles";
style.textContent = `
.${TRANSITION_CLASS} {
transition: transform ${TRANSITION_DURATION}ms ease-out;
will-change: transform;
}
`;
document.head.appendChild(style);
styleInjected = true;
}
function findContentElements(keyboardContainerRef) {
var _a;
if (!keyboardContainerRef) return [];
return Array.from(((_a = keyboardContainerRef.parentElement) == null ? void 0 : _a.children) ?? []).filter(
(child) => {
if (child === keyboardContainerRef) return false;
const { position } = window.getComputedStyle(child);
return position !== "fixed" && position !== "absolute";
}
);
}
function calculateShiftAmount(input) {
const inputBottom = input.getBoundingClientRect().bottom;
const visibleHeight = window.innerHeight * (1 - KEYBOARD_HEIGHT_VH / 100);
const overflow = inputBottom + INPUT_PADDING - visibleHeight;
return overflow > 0 ? overflow : 0;
}
function scrollInputIntoView(input, keyboardContainerRef) {
if (shiftedElements.length > 0) resetScrollPosition();
const shiftAmount = calculateShiftAmount(input);
if (shiftAmount === 0) return;
const contentElements = findContentElements(keyboardContainerRef);
if (contentElements.length === 0) return;
injectStyles();
shiftedElements = contentElements;
for (const element of contentElements) {
element.classList.add(TRANSITION_CLASS);
element.style.transform = `translateY(-${shiftAmount}px)`;
}
}
function resetScrollPosition() {
if (shiftedElements.length === 0) return;
for (const element of shiftedElements) {
if (document.body.contains(element)) {
element.style.transform = "translateY(0)";
}
}
const elementsToCleanup = [...shiftedElements];
setTimeout(() => {
for (const element of elementsToCleanup) {
if (document.body.contains(element)) {
element.classList.remove(TRANSITION_CLASS);
}
}
}, TRANSITION_DURATION);
shiftedElements = [];
}
const onEnterClickUtil = (focusedInputRef) => {
var _a;
if (focusedInputRef.current) {
const input = focusedInputRef.current;
input.blur();
(_a = input.form) == null ? void 0 : _a.submit();
}
};
const handleValueChangeUtil = (focusedInputRef, value) => {
const input = focusedInputRef.current;
if (!input) return;
setInputValueAndDispatchEvents(input, value);
};
const validateFocusInputs = (event) => {
const target = event.target;
const isInput = target.tagName === "INPUT";
const isTextarea = target.tagName === "TEXTAREA";
if (isTextarea) {
return target;
}
if (isInput) {
const input = target;
const excludedTypes = [
"checkbox",
"radio",
"range",
"date",
"time",
"color",
"month",
"week",
"file",
"hidden",
"submit",
"reset",
"button",
"image"
];
if (excludedTypes.includes(input.type)) return null;
return input;
}
return null;
};
function createCaretManager(getInputRef) {
const insertText = (text) => {
const input = getInputRef();
if (!input) return;
if (input.readOnly || input.disabled) return;
const startRaw = input.selectionStart;
const endRaw = input.selectionEnd;
const start = startRaw == null ? input.value.length : startRaw;
const end = endRaw == null ? input.value.length : endRaw;
const scrollTop = input.scrollTop ?? 0;
const scrollLeft = input.scrollLeft ?? 0;
const newValue = input.value.slice(0, start) + text + input.value.slice(end);
const caretAfter = start + text.length;
setInputValueAndDispatchEvents(input, newValue);
input.focus();
input.setSelectionRange(caretAfter, caretAfter);
if ("scrollTop" in input) {
input.scrollTop = scrollTop;
input.scrollLeft = scrollLeft;
}
};
const backspace = () => {
const input = getInputRef();
if (!input) return;
if (input.readOnly || input.disabled) return;
const startRaw = input.selectionStart;
const endRaw = input.selectionEnd;
const start = startRaw == null ? input.value.length : startRaw;
const end = endRaw == null ? input.value.length : endRaw;
const scrollTop = input.scrollTop ?? 0;
const scrollLeft = input.scrollLeft ?? 0;
if (start !== end) {
const newValue = input.value.slice(0, start) + input.value.slice(end);
setInputValueAndDispatchEvents(input, newValue);
input.focus();
input.setSelectionRange(start, start);
} else if (start > 0) {
const newValue = input.value.slice(0, start - 1) + input.value.slice(start);
const newCaret = start - 1;
setInputValueAndDispatchEvents(input, newValue);
input.focus();
input.setSelectionRange(newCaret, newCaret);
}
if ("scrollTop" in input) {
input.scrollTop = scrollTop;
input.scrollLeft = scrollLeft;
}
};
return {
insertText,
backspace
};
}
function setupHardwareKeyboard(handlers) {
const { onBackspace, onEnter, onSpace, onCapsToggle, onKeyClick } = handlers;
const handleKeyboardKeyDown = (event) => {
const key = event.key;
switch (key) {
case "Backspace":
event.preventDefault();
event.stopPropagation();
onBackspace();
return;
case "Enter":
event.preventDefault();
onEnter();
return;
case " ":
event.preventDefault();
onSpace();
return;
case "CapsLock":
event.preventDefault();
onCapsToggle();
return;
default:
if (key.length === 1) {
event.preventDefault();
onKeyClick(key);
}
}
};
document.addEventListener("keydown", handleKeyboardKeyDown);
return () => {
document.removeEventListener("keydown", handleKeyboardKeyDown);
};
}
const QWERTY_LAYOUT = [
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
["a", "s", "d", "f", "g", "h", "j", "k", "l"],
["z", "x", "c", "v", "b", "n", "m"]
];
const SYMBOLS_LAYOUT = [
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"],
["-", "_", "=", "+", "[", "]", "{", "}", "\\", "|"],
[";", ":", '"', "'", ",", ".", "<", ">", "/", "?"]
];
const NUMBERS_LAYOUT = [
["7", "8", "9", "#"],
["4", "5", "6", "-"],
["1", "2", "3"],
[",", "0", "."]
];
const DEFAULT_THEME = {
backgroundColor: "#1a1a1a",
keyColor: "#444444",
keyTextColor: "#ffffff",
keyActiveColor: "#666666",
keyHoverColor: "#555555",
activeStateColor: "#4a90e2",
keyBorderRadius: "0.5vw",
keyFontSize: "32px",
keyHeight: "100%"
};
const BackspaceIcon = (props) => /* @__PURE__ */ jsx(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 24 24",
fill: "currentColor",
width: "1em",
height: "1em",
...props,
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" })
}
);
const EnterIcon = (props) => /* @__PURE__ */ jsx(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 24 24",
fill: "currentColor",
width: "1em",
height: "1em",
...props,
children: /* @__PURE__ */ jsx("path", { d: "M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z" })
}
);
const SpacebarIcon = (props) => /* @__PURE__ */ jsx(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 24 24",
fill: "currentColor",
width: "1em",
height: "1em",
...props,
children: /* @__PURE__ */ jsx("path", { d: "M18 9v4H6V9H4v6h16V9z" })
}
);
const CapsLockIcon = (props) => /* @__PURE__ */ jsx(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 24 24",
fill: "currentColor",
width: "1em",
height: "1em",
style: { transform: "rotate(270deg)" },
...props,
children: /* @__PURE__ */ jsx("path", { d: "M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z" })
}
);
function useContinuousPress(onPress, {
initialDelay = 500,
interval = 50,
shouldPreventDefault = true
} = {}) {
const timeoutRef = useRef();
const intervalRef = useRef();
const clear = useCallback(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = void 0;
}
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = void 0;
}
}, []);
const start = useCallback(() => {
onPress();
timeoutRef.current = setTimeout(() => {
intervalRef.current = setInterval(onPress, interval);
}, initialDelay);
}, [onPress, initialDelay, interval]);
return {
onMouseDown: (e) => {
e.preventDefault();
start();
},
onTouchStart: (e) => {
if (shouldPreventDefault) e.preventDefault();
start();
},
onMouseUp: clear,
onMouseLeave: clear,
onTouchEnd: (e) => {
if (shouldPreventDefault) e.preventDefault();
clear();
}
};
}
function useKeyboardScroll(keyboardContainerRef) {
const handleScrollInput = useCallback((input) => {
setTimeout(() => {
if (!(keyboardContainerRef == null ? void 0 : keyboardContainerRef.current)) return;
scrollInputIntoView(input, keyboardContainerRef.current);
}, 0);
}, [keyboardContainerRef]);
const handleResetScroll = useCallback(() => {
resetScrollPosition();
}, []);
useEffect(() => {
return () => {
resetScrollPosition();
};
}, []);
return {
scrollInput: handleScrollInput,
resetScroll: handleResetScroll
};
}
const SpecialKey = memo((props) => {
const {
type,
icon,
onClick,
extraClass = "",
text,
capsLock = false,
enableContinuousPress = false
} = props;
const isCapsLockActive = type === "caps" && capsLock;
const keyClasses = [
"vk-key",
`vk-key--${extraClass}`,
isCapsLockActive ? "vk-key--caps-active" : ""
].filter(Boolean).join(" ");
const continuousPressHandlers = useContinuousPress(onClick, {
initialDelay: 500,
interval: 50
});
const buttonHandlers = enableContinuousPress ? continuousPressHandlers : { onClick };
return /* @__PURE__ */ jsxs(
"button",
{
type: "button",
className: keyClasses,
"data-testid": `${type}${isCapsLockActive ? "-active" : ""}`,
"data-key": isCapsLockActive ? `${type}-active` : type,
...buttonHandlers,
children: [
icon && icon,
text && /* @__PURE__ */ jsx("span", { className: "vk-key__text", children: text })
]
}
);
});
SpecialKey.displayName = "SpecialKey";
const VirtualKey = memo(({ keyValue, onClick, className = "" }) => {
const keyClasses = ["vk-key", className].filter(Boolean).join(" ");
return /* @__PURE__ */ jsx(
"button",
{
type: "button",
className: keyClasses,
onClick: () => onClick(keyValue),
"data-testid": keyValue,
children: keyValue
}
);
});
VirtualKey.displayName = "VirtualKey";
const KeyboardRow = ({ children, className = "" }) => {
const rowClasses = ["vk-row", className].filter(Boolean).join(" ");
return /* @__PURE__ */ jsx("div", { className: rowClasses, children });
};
const NumbersLayout = ({
currentLayoutData,
onBackspace,
onEnter,
onKeyClick,
capsLock
}) => {
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: [
row == null ? void 0 : row.map((key, keyIndex) => /* @__PURE__ */ jsx(
VirtualKey,
{
keyValue: key,
onClick: onKeyClick
},
`num-${rowIndex}-${keyIndex}-${key}`
)),
rowIndex === 3 && /* @__PURE__ */ jsx(
SpecialKey,
{
type: "enter",
icon: /* @__PURE__ */ jsx(EnterIcon, {}),
onClick: onEnter,
extraClass: "enter-num",
text: "Enter",
capsLock
},
"enter-num"
),
rowIndex === 2 && /* @__PURE__ */ jsx(
SpecialKey,
{
type: "backspace",
icon: /* @__PURE__ */ jsx(BackspaceIcon, {}),
onClick: onBackspace,
extraClass: "backspace-num",
text: "Backspace",
capsLock,
enableContinuousPress: true
},
"backspace-num"
)
] }, `num-row-${rowIndex}`)) });
};
const TextLayout = ({
inputType,
currentLayoutData,
onBackspace,
onEnter,
onSpace,
onCapsToggle,
onLayoutToggle,
onKeyClick,
capsLock,
currentLayout
}) => {
const renderSpecialKeysLeft = (rowIndex) => {
switch (rowIndex) {
case 3:
return currentLayout === "letters" && /* @__PURE__ */ jsx(
SpecialKey,
{
type: "caps",
icon: /* @__PURE__ */ jsx(CapsLockIcon, {}),
onClick: onCapsToggle,
extraClass: "capsLock",
text: "Caps Lock",
capsLock
},
"caps"
);
default:
return null;
}
};
const renderSpecialKeysRight = (rowIndex) => {
switch (rowIndex) {
case 3:
return /* @__PURE__ */ jsx(
SpecialKey,
{
type: "backspace",
icon: /* @__PURE__ */ jsx(BackspaceIcon, {}),
onClick: onBackspace,
extraClass: "backspace",
text: "Backspace",
enableContinuousPress: true
},
"backspace"
);
default:
return null;
}
};
return /* @__PURE__ */ jsxs("div", { className: "vk-layout vk-layout--text", "data-testid": "keyboard-layout", children: [
currentLayoutData == null ? void 0 : currentLayoutData.map((row, rowIndex) => /* @__PURE__ */ jsxs(KeyboardRow, { children: [
renderSpecialKeysLeft(rowIndex),
row.map((key, keyIndex) => {
const displayKey = capsLock ? key.toUpperCase() : key.toLowerCase();
return /* @__PURE__ */ jsx(
VirtualKey,
{
keyValue: displayKey,
onClick: onKeyClick
},
`${rowIndex}-${keyIndex}-${key}`
);
}),
renderSpecialKeysRight(rowIndex)
] }, `row-${rowIndex}`)),
/* @__PURE__ */ jsxs(KeyboardRow, { children: [
/* @__PURE__ */ jsx(
SpecialKey,
{
type: "layout",
icon: currentLayout === "letters" ? "&123" : "ABC",
onClick: onLayoutToggle,
extraClass: "layout",
text: ""
},
"layout"
),
inputType === "email" && /* @__PURE__ */ jsx(
SpecialKey,
{
type: "dot",
onClick: () => onKeyClick("."),
extraClass: "dot",
icon: "."
},
"dot"
),
/* @__PURE__ */ jsx(
SpecialKey,
{
type: "space",
icon: /* @__PURE__ */ jsx(SpacebarIcon, {}),
onClick: onSpace,
extraClass: "space",
text: "Space"
},
"space"
),
inputType === "email" && /* @__PURE__ */ jsx(
SpecialKey,
{
type: "at",
icon: "@",
onClick: () => onKeyClick("@"),
extraClass: "at",
text: ""
},
"at"
),
/* @__PURE__ */ jsx(
SpecialKey,
{
type: "enter",
icon: /* @__PURE__ */ jsx(EnterIcon, {}),
onClick: onEnter,
extraClass: "enter",
text: "Enter"
},
"enter"
)
] })
] });
};
const KeyboardLayout = ({
currentLayout,
capsLock,
onKeyClick,
onBackspace,
onEnter,
onSpace,
onCapsToggle,
onLayoutToggle,
inputType,
customLayouts
}) => {
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;
if (currentLayout === "numbers") {
return /* @__PURE__ */ jsx(
NumbersLayout,
{
currentLayoutData,
onBackspace,
onEnter,
onKeyClick,
capsLock,
currentLayout
}
);
}
return /* @__PURE__ */ jsx(
TextLayout,
{
inputType,
currentLayoutData,
onBackspace,
onEnter,
onSpace,
onCapsToggle,
onLayoutToggle,
onKeyClick,
capsLock,
currentLayout
}
);
};
const VirtualKeyboardContainer = ({
children,
className = ""
}) => {
const handleMouseDown = (e) => {
e.preventDefault();
};
const handleClick = (e) => {
e.preventDefault();
e.stopPropagation();
};
const containerClasses = ["vk-container", className].filter(Boolean).join(" ");
return /* @__PURE__ */ jsx(
"div",
{
className: containerClasses,
onMouseDown: handleMouseDown,
onClick: handleClick,
"data-testid": "keyboard-container",
children
}
);
};
const VirtualKeyboard = ({
focusedInputRef,
isInputFocused,
inputType = "text",
onEnterClick,
onChange,
className,
defaultLayout = "letters",
validate,
syncWithHardwareKeyboard = true,
customLayouts,
languages,
currentLanguage,
onLanguageChange,
showLanguageSwitcher = false
}) => {
const [capsLock, setCapsLock] = useState(false);
const [selectedLanguage, setSelectedLanguage] = useState(currentLanguage || Object.keys(languages || {})[0] || "en");
const { insertText, backspace } = createCaretManager(() => focusedInputRef.current);
const [currentLayout, setCurrentLayout] = useState(
() => getInitialLayout(inputType, defaultLayout)
);
const updateValue = useCallback(
(next) => {
var _a;
if (validate && !validate(next)) return;
if (!validateValueUtil(next, inputType)) return;
insertText(next);
onChange == null ? void 0 : onChange(((_a = focusedInputRef.current) == null ? void 0 : _a.value) ?? "");
},
[focusedInputRef, inputType, insertText, onChange, validate]
);
const handleKeyClick = useCallback(
(key) => {
updateValue(key);
},
[updateValue]
);
const handleBackspace = useCallback(() => {
var _a, _b;
if (((_a = focusedInputRef.current) == null ? void 0 : _a.value.length) === 0) return;
backspace();
onChange == null ? void 0 : onChange(((_b = focusedInputRef.current) == null ? void 0 : _b.value) ?? "");
}, [backspace, focusedInputRef, onChange]);
const handleEnter = useCallback(() => {
onEnterClick == null ? void 0 : onEnterClick();
}, [onEnterClick]);
const handleSpace = useCallback(() => {
var _a;
updateValue(" ");
insertText(" ");
onChange == null ? void 0 : onChange(((_a = focusedInputRef.current) == null ? void 0 : _a.value) || "");
}, [insertText, onChange, focusedInputRef]);
const handleCapsToggle = useCallback(() => {
setCapsLock((prev) => !prev);
}, []);
useEffect(() => {
setCurrentLayout(getInitialLayout(inputType, defaultLayout));
}, [inputType, defaultLayout]);
const keysHandlers = {
onBackspace: handleBackspace,
onEnter: handleEnter,
onSpace: handleSpace,
onCapsToggle: handleCapsToggle,
onKeyClick: handleKeyClick
};
useEffect(() => {
if (!isInputFocused || !syncWithHardwareKeyboard) return;
return setupHardwareKeyboard(keysHandlers);
}, [
isInputFocused,
syncWithHardwareKeyboard,
handleKeyClick,
handleBackspace,
handleEnter,
handleSpace,
handleCapsToggle
]);
const handleLanguageChange = (lang) => {
setSelectedLanguage(lang);
onLanguageChange == null ? void 0 : onLanguageChange(lang);
};
const activeLayouts = (languages == null ? void 0 : languages[selectedLanguage]) || customLayouts;
return /* @__PURE__ */ jsxs(VirtualKeyboardContainer, { className, children: [
showLanguageSwitcher && languages && Object.keys(languages).length > 1 && /* @__PURE__ */ jsx("div", { className: "vk-language-switcher", children: Object.entries(languages).map(([code, config]) => /* @__PURE__ */ jsx(
"button",
{
className: `vk-lang-btn ${selectedLanguage === code ? "active" : ""}`,
onClick: () => handleLanguageChange(code),
children: config.label || code.toUpperCase()
},
code
)) }),
/* @__PURE__ */ jsx(
KeyboardLayout,
{
capsLock,
currentLayout,
onKeyClick: handleKeyClick,
onBackspace: handleBackspace,
onEnter: handleEnter,
onSpace: handleSpace,
onCapsToggle: handleCapsToggle,
onLayoutToggle: () => setCurrentLayout((prev) => prev === "letters" ? "symbols" : "letters"),
inputType,
customLayouts: activeLayouts
}
)
] });
};
const GlobalVirtualKeyboard = ({
enabled = true,
className,
onVisibilityChange,
onEnterClick,
onChange
}) => {
const keyboardContainerRef = useRef(null);
const [isVisible, setIsVisible] = useState(false);
const [inputType, setInputType] = useState("text");
const focusedInputRef = useRef(null);
const originalInputTypeRef = useRef("text");
const { scrollInput, resetScroll } = useKeyboardScroll(keyboardContainerRef);
useEffect(() => {
if (!enabled) {
if (isVisible) {
setTimeout(() => {
setIsVisible(false);
}, 0);
onVisibilityChange == null ? void 0 : onVisibilityChange(false);
}
return;
}
const handleFocus = (event) => {
const input = validateFocusInputs(event);
if (!input) return;
const isTextarea = input.tagName === "TEXTAREA";
const detectedInputType = isTextarea ? "text" : input.type;
setInputType(detectedInputType);
setIsVisible(true);
onVisibilityChange == null ? void 0 : onVisibilityChange(true);
focusedInputRef.current = input;
if (!isTextarea) {
originalInputTypeRef.current = input.type;
if (input.type !== "text") {
const selectionStart = input.selectionStart;
const selectionEnd = input.selectionEnd;
input.type = "text";
const caretPos = selectionStart ?? input.value.length;
const caretEnd = selectionEnd ?? caretPos;
input.setSelectionRange(caretPos, caretEnd);
}
}
scrollInput(input);
};
const handleBlur = (event) => {
const input = validateFocusInputs(event);
if (!input) return;
const isTextarea = input.tagName === "TEXTAREA";
if (!isTextarea) {
input.type = originalInputTypeRef.current;
}
if (focusedInputRef.current === input) {
focusedInputRef.current = null;
setIsVisible(false);
onVisibilityChange == null ? void 0 : onVisibilityChange(false);
resetScroll();
}
};
document.addEventListener("focusin", handleFocus, true);
document.addEventListener("focusout", handleBlur, true);
return () => {
document.removeEventListener("focusin", handleFocus, true);
document.removeEventListener("focusout", handleBlur, true);
};
}, [enabled, scrollInput, resetScroll, onVisibilityChange]);
const handleEnterClick = () => {
setIsVisible(false);
onVisibilityChange == null ? void 0 : onVisibilityChange(false);
onEnterClickUtil(focusedInputRef);
onEnterClick == null ? void 0 : onEnterClick();
resetScroll();
};
if (!isVisible || !enabled) {
return null;
}
return /* @__PURE__ */ jsx("span", { ref: keyboardContainerRef, children: /* @__PURE__ */ jsx(
VirtualKeyboard,
{
focusedInputRef,
isInputFocused: isVisible,
inputType,
onEnterClick: handleEnterClick,
onChange,
className
}
) });
};
export {
BackspaceIcon,
CapsLockIcon,
DEFAULT_THEME,
EnterIcon,
GlobalVirtualKeyboard,
KeyboardLayout,
KeyboardRow,
NUMBERS_LAYOUT,
NumbersLayout,
QWERTY_LAYOUT,
SYMBOLS_LAYOUT,
SpacebarIcon,
SpecialKey,
TextLayout,
VirtualKey,
VirtualKeyboard,
VirtualKeyboardContainer,
createCaretManager,
getInitialLayout,
handleValueChangeUtil,
onEnterClickUtil,
resetScrollPosition,
scrollInputIntoView,
setInputValueAndDispatchEvents,
setupHardwareKeyboard,
useContinuousPress,
useKeyboardScroll,
validateFocusInputs,
validateValueUtil
};
//# sourceMappingURL=index.esm.js.map