fix: keep virtual keyboard open when tapping special keys

- Use onPointerDown with preventDefault() instead of onClick
- Add onKeepFocus callback to SpecialKeysStrip and SpecialKeysPanel
- Expose focusInput via onTerminalReady in TerminalComponent
- MobileTerminalWrapper passes focus callback to keep keyboard open
This commit is contained in:
Fusion
2026-05-24 11:57:55 +02:00
parent aea1ff95f6
commit 7e0df57f8c
4 changed files with 42 additions and 15 deletions
@@ -29,6 +29,7 @@ export const MobileTerminalWrapper: React.FC<MobileTerminalWrapperProps> = ({
const [terminalRef, setTerminalRef] = useState<{
sendData: (data: string) => void;
connectionStatus: "connecting" | "connected" | "disconnected" | "error";
focusInput: () => void;
} | null>(null);
const headerAutoHide = useAutoHide({ timeout: 3000, enabled: isMobile });
@@ -40,8 +41,8 @@ export const MobileTerminalWrapper: React.FC<MobileTerminalWrapperProps> = ({
}, [headerAutoHide, keysAutoHide]);
const handleTerminalReady = useCallback(
(sendData: (data: string) => void, connectionStatus: "connecting" | "connected" | "disconnected" | "error") => {
setTerminalRef({ sendData, connectionStatus });
(sendData: (data: string) => void, connectionStatus: "connecting" | "connected" | "disconnected" | "error", focusInput: () => void) => {
setTerminalRef({ sendData, connectionStatus, focusInput });
},
[]
);
@@ -93,12 +94,14 @@ export const MobileTerminalWrapper: React.FC<MobileTerminalWrapperProps> = ({
onSend={handleSendKey}
isVisible={keysAutoHide.isVisible && !showPanel}
onMoreClick={() => setShowPanel(true)}
onKeepFocus={() => terminalRef?.focusInput()}
/>
<SpecialKeysPanel
onSend={handleSendKey}
isOpen={showPanel}
onClose={() => setShowPanel(false)}
onKeepFocus={() => terminalRef?.focusInput()}
/>
</div>
);
+11 -8
View File
@@ -5,6 +5,7 @@ interface SpecialKeysPanelProps {
onSend: (data: string) => void;
isOpen: boolean;
onClose: () => void;
onKeepFocus?: () => void;
}
const EXPANDED_KEYS: { key: SpecialKey; label: string }[] = [
@@ -36,11 +37,19 @@ export const SpecialKeysPanel: React.FC<SpecialKeysPanelProps> = ({
onSend,
isOpen,
onClose,
onKeepFocus,
}) => {
const { sendKey } = useSpecialKeys({ onSend });
if (!isOpen) return null;
const handlePointerDown = (e: React.PointerEvent, key: SpecialKey) => {
e.preventDefault();
sendKey(key);
onClose();
onKeepFocus?.();
};
return (
<div className="special-keys-panel-overlay" onClick={onClose}>
<div
@@ -52,10 +61,7 @@ export const SpecialKeysPanel: React.FC<SpecialKeysPanelProps> = ({
<button
key={key}
className="special-key-button"
onClick={() => {
sendKey(key);
onClose();
}}
onPointerDown={(e) => handlePointerDown(e, key)}
type="button"
>
{label}
@@ -68,10 +74,7 @@ export const SpecialKeysPanel: React.FC<SpecialKeysPanelProps> = ({
<button
key={key}
className="special-key-button"
onClick={() => {
sendKey(key);
onClose();
}}
onPointerDown={(e) => handlePointerDown(e, key)}
type="button"
>
{label}
+16 -2
View File
@@ -5,6 +5,7 @@ interface SpecialKeysStripProps {
onSend: (data: string) => void;
isVisible: boolean;
onMoreClick?: () => void;
onKeepFocus?: () => void;
}
const PRIMARY_KEYS: { key: SpecialKey; label: string }[] = [
@@ -22,16 +23,29 @@ export const SpecialKeysStrip: React.FC<SpecialKeysStripProps> = ({
onSend,
isVisible,
onMoreClick,
onKeepFocus,
}) => {
const { sendKey } = useSpecialKeys({ onSend });
const handlePointerDown = (e: React.PointerEvent, key: SpecialKey) => {
e.preventDefault();
sendKey(key);
onKeepFocus?.();
};
const handleMorePointerDown = (e: React.PointerEvent) => {
e.preventDefault();
onMoreClick?.();
onKeepFocus?.();
};
return (
<div className={`special-keys-strip ${isVisible ? "visible" : "hidden"}`}>
{PRIMARY_KEYS.map(({ key, label }) => (
<button
key={key}
className="special-key-button"
onClick={() => sendKey(key)}
onPointerDown={(e) => handlePointerDown(e, key)}
type="button"
aria-label={`Send ${label}`}
>
@@ -41,7 +55,7 @@ export const SpecialKeysStrip: React.FC<SpecialKeysStripProps> = ({
{onMoreClick && (
<button
className="special-key-button special-key-more"
onClick={onMoreClick}
onPointerDown={handleMorePointerDown}
type="button"
aria-label="More special keys"
>
+10 -3
View File
@@ -10,7 +10,8 @@ interface TerminalProps {
isMobile?: boolean;
onTerminalReady?: (
sendData: (data: string) => void,
connectionStatus: "connecting" | "connected" | "disconnected" | "error"
connectionStatus: "connecting" | "connected" | "disconnected" | "error",
focusInput: () => void
) => void;
}
@@ -196,7 +197,10 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
ws.send(data);
}
};
onTerminalReadyRef.current(sendData, status);
const focusInput = () => {
hiddenInputRef.current?.focus();
};
onTerminalReadyRef.current(sendData, status, focusInput);
}
// Visibility API for reconnection
@@ -225,7 +229,10 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
wsRef.current.send(data);
}
};
onTerminalReady(sendData, status);
const focusInput = () => {
hiddenInputRef.current?.focus();
};
onTerminalReady(sendData, status, focusInput);
}
}, [status, onTerminalReady]);