feat: support global keyboard input in the dev sample
This commit is contained in:
+79
-32
@@ -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');
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user