feat: support global keyboard input in the dev sample

This commit is contained in:
2026-04-24 21:35:10 +02:00
parent 30714a6c4e
commit 67bac6f486
+80 -33
View File
@@ -213,7 +213,7 @@
<input id="input" class="hidden-input" type="text" autocomplete="off" aria-hidden="true" tabindex="-1">
<div class="input-row">
<div class="hint">Keyboard input is captured directly by the screen</div>
<div class="hint">Keyboard works globally: digits, numpad, Enter, Backspace, +, -, *, /, %, ^</div>
<select id="angleMode">
<option value="deg">Degrees</option>
<option value="rad">Radians</option>
@@ -338,10 +338,6 @@
if (calc.stack.length >= 1) calc.remove(0);
} else if (name === 'clear') {
calc.clear();
} else if (name === 'enter') {
if (calc.isEditing) {
pushEditingValueIfNeeded();
}
} else {
pushEditingValueIfNeeded();
calc.command(name);
@@ -354,7 +350,63 @@
}
function isInputChar(key) {
return /^[0-9a-fA-F.+\-]$/.test(key);
return /^[0-9a-fA-F.]$/.test(key);
}
function shouldIgnoreKeyboardEvent(event) {
const target = event.target;
if (!target) return false;
const tagName = target.tagName;
return (
tagName === 'INPUT' ||
tagName === 'TEXTAREA' ||
tagName === 'SELECT' ||
target.isContentEditable
);
}
function getKeyboardAction(event) {
const numpadMap = {
Numpad0: { type: 'input', value: '0' },
Numpad1: { type: 'input', value: '1' },
Numpad2: { type: 'input', value: '2' },
Numpad3: { type: 'input', value: '3' },
Numpad4: { type: 'input', value: '4' },
Numpad5: { type: 'input', value: '5' },
Numpad6: { type: 'input', value: '6' },
Numpad7: { type: 'input', value: '7' },
Numpad8: { type: 'input', value: '8' },
Numpad9: { type: 'input', value: '9' },
NumpadDecimal: { type: 'input', value: '.' },
NumpadAdd: { type: 'command', value: 'add' },
NumpadSubtract: { type: 'command', value: 'sub' },
NumpadMultiply: { type: 'command', value: 'mul' },
NumpadDivide: { type: 'command', value: 'div' },
NumpadEnter: { type: 'command', value: 'enter' },
};
if (numpadMap[event.code]) {
return numpadMap[event.code];
}
if (isInputChar(event.key)) {
return { type: 'input', value: event.key };
}
const keyMap = {
Enter: { type: 'command', value: 'enter' },
Backspace: { type: 'edit', value: 'Backspace' },
Escape: { type: 'command', value: 'clear' },
'+': { type: 'command', value: 'add' },
'-': { type: 'command', value: 'sub' },
'*': { type: 'command', value: 'mul' },
'/': { type: 'command', value: 'div' },
'%': { type: 'command', value: 'mod' },
'^': { type: 'command', value: 'pow' },
};
return keyMap[event.key] || null;
}
function focusScreen() {
@@ -382,47 +434,42 @@
syncInputFromState();
}
screen.addEventListener('keydown', (event) => {
function handleKeydown(event) {
if (shouldIgnoreKeyboardEvent(event)) {
return;
}
const action = getKeyboardAction(event);
if (!action) {
return;
}
try {
if (event.key === 'Enter') {
event.preventDefault();
execute('enter');
event.preventDefault();
if (action.type === 'input') {
editXWithKey(action.value);
render();
return;
}
if (event.key === 'Backspace') {
event.preventDefault();
if (action.type === 'edit') {
if (calc.isEditing) {
editXWithKey('Backspace');
editXWithKey(action.value);
render();
}
return;
}
if (isInputChar(event.key)) {
event.preventDefault();
editXWithKey(event.key);
render();
return;
}
const keyMap = {
'+': 'add',
'-': 'sub',
'*': 'mul',
'/': 'div',
'%': 'mod',
'^': 'pow',
};
if (keyMap[event.key]) {
event.preventDefault();
execute(keyMap[event.key]);
if (action.type === 'command') {
execute(action.value);
}
} catch (error) {
errorEl.textContent = error.message;
}
});
}
window.addEventListener('keydown', handleKeydown);
screen.addEventListener('click', focusScreen);
window.addEventListener('load', focusScreen);