feat(hp48): color-code keypad and inline error display
This commit is contained in:
+104
-50
@@ -16,6 +16,20 @@
|
||||
--key-text: #f2f2f2;
|
||||
--accent: #8cff6d;
|
||||
--border: #111;
|
||||
--key-number: #5a5a5a;
|
||||
--key-number-2: #444;
|
||||
--key-arithmetic: #7f5c24;
|
||||
--key-arithmetic-2: #5f4216;
|
||||
--key-trig: #2d6d73;
|
||||
--key-trig-2: #1f4f54;
|
||||
--key-stack: #6b4a8f;
|
||||
--key-stack-2: #4f356b;
|
||||
--key-special: #3d5d8a;
|
||||
--key-special-2: #2c4464;
|
||||
--key-danger: #8a3f3f;
|
||||
--key-danger-2: #632d2d;
|
||||
--key-enter: #5d8a3d;
|
||||
--key-enter-2: #45662e;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
@@ -124,34 +138,6 @@
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
margin: 0 0 10px;
|
||||
font-size: 14px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.keypad-labels {
|
||||
display: grid;
|
||||
grid-template-columns: 4fr 3fr 2fr;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.keypad-label {
|
||||
color: #d8d8d8;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
padding: 6px 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.keypad-wrap {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 4fr) minmax(0, 3fr) minmax(0, 2fr);
|
||||
@@ -185,11 +171,41 @@
|
||||
min-height: 46px;
|
||||
}
|
||||
|
||||
.keypad button.active-toggle {
|
||||
.keypad button.active-toggle,
|
||||
.active-toggle {
|
||||
background: linear-gradient(180deg, #6a8c52, #49693a);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.key-number {
|
||||
background: linear-gradient(180deg, var(--key-number), var(--key-number-2));
|
||||
}
|
||||
|
||||
.key-arithmetic {
|
||||
background: linear-gradient(180deg, var(--key-arithmetic), var(--key-arithmetic-2));
|
||||
}
|
||||
|
||||
.key-trigonometry {
|
||||
background: linear-gradient(180deg, var(--key-trig), var(--key-trig-2));
|
||||
}
|
||||
|
||||
.key-stack {
|
||||
background: linear-gradient(180deg, var(--key-stack), var(--key-stack-2));
|
||||
}
|
||||
|
||||
.key-special {
|
||||
background: linear-gradient(180deg, var(--key-special), var(--key-special-2));
|
||||
}
|
||||
|
||||
.key-danger {
|
||||
background: linear-gradient(180deg, var(--key-danger), var(--key-danger-2));
|
||||
}
|
||||
|
||||
.key-enter {
|
||||
background: linear-gradient(180deg, var(--key-enter), var(--key-enter-2));
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.keypad-group button.enter-tall {
|
||||
grid-row: span 2;
|
||||
}
|
||||
@@ -230,12 +246,9 @@
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.error {
|
||||
margin-top: 10px;
|
||||
min-height: 20px;
|
||||
color: #ff8a8a;
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 13px;
|
||||
.display-error {
|
||||
color: #8a2a1c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
@@ -244,7 +257,6 @@
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.keypad-labels,
|
||||
.keypad-wrap {
|
||||
gap: 8px;
|
||||
}
|
||||
@@ -265,7 +277,6 @@
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.keypad-labels,
|
||||
.keypad-wrap {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@@ -297,12 +308,6 @@
|
||||
<input id="input" class="hidden-input" type="text" autocomplete="off" aria-hidden="true" tabindex="-1">
|
||||
|
||||
<div class="panel" id="keypadPanel">
|
||||
<div class="title">Calculator Keys</div>
|
||||
<div class="keypad-labels" aria-hidden="true">
|
||||
<div class="keypad-label">Functions</div>
|
||||
<div class="keypad-label">Numbers</div>
|
||||
<div class="keypad-label">Operators</div>
|
||||
</div>
|
||||
<div class="keypad-wrap">
|
||||
<div class="keypad-group functions" id="functionsKeypad"></div>
|
||||
<div class="keypad-group numbers" id="numbersKeypad"></div>
|
||||
@@ -319,7 +324,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="error" class="error"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -331,7 +335,6 @@
|
||||
const stackEl = document.getElementById('stack');
|
||||
|
||||
const displayEl = document.getElementById('display');
|
||||
const errorEl = document.getElementById('error');
|
||||
const modeLabel = document.getElementById('modeLabel');
|
||||
const functionsKeypadEl = document.getElementById('functionsKeypad');
|
||||
const numbersKeypadEl = document.getElementById('numbersKeypad');
|
||||
@@ -344,6 +347,7 @@
|
||||
let openMenuName = null;
|
||||
let isMovingStackItem = false;
|
||||
let stackSnapshotBeforeMove = null;
|
||||
let currentErrorMessage = '';
|
||||
|
||||
function getKeypadLayout() {
|
||||
return {
|
||||
@@ -427,6 +431,42 @@
|
||||
};
|
||||
}
|
||||
|
||||
function getKeyClassName(key) {
|
||||
if (key.type === 'input') {
|
||||
return 'key-number';
|
||||
}
|
||||
|
||||
if (key.value === 'clear') {
|
||||
return 'key-danger';
|
||||
}
|
||||
|
||||
if (key.value === 'enter') {
|
||||
return 'key-enter';
|
||||
}
|
||||
|
||||
if (key.type === 'special') {
|
||||
return 'key-special';
|
||||
}
|
||||
|
||||
const stackCommands = new Set(['neg']);
|
||||
const arithmeticCommands = new Set(['add', 'sub', 'mul', 'div', 'mod', 'pow', 'sqr', 'sqrt', 'recip', 'log', 'ln']);
|
||||
const trigonometryCommands = new Set(['sin', 'cos', 'tan', 'asin', 'acos', 'atan']);
|
||||
|
||||
if (stackCommands.has(key.value)) {
|
||||
return 'key-stack';
|
||||
}
|
||||
|
||||
if (arithmeticCommands.has(key.value)) {
|
||||
return 'key-arithmetic';
|
||||
}
|
||||
|
||||
if (trigonometryCommands.has(key.value)) {
|
||||
return 'key-trigonometry';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function renderKeypadGroup(groupEl, layout) {
|
||||
groupEl.innerHTML = '';
|
||||
const occupied = new Set();
|
||||
@@ -449,6 +489,10 @@
|
||||
|
||||
const button = document.createElement('button');
|
||||
button.textContent = key.label;
|
||||
const keyClassName = getKeyClassName(key);
|
||||
if (keyClassName) {
|
||||
button.classList.add(keyClassName);
|
||||
}
|
||||
button.style.gridColumn = String(colIndex + 1);
|
||||
button.style.gridRow = `${rowIndex + 1} / span ${key.rowspan || 1}`;
|
||||
if (key.rowspan && key.rowspan > 1) {
|
||||
@@ -591,7 +635,8 @@
|
||||
execute(key.value);
|
||||
}
|
||||
} catch (error) {
|
||||
errorEl.textContent = error.message;
|
||||
currentErrorMessage = error.message;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,17 +764,23 @@
|
||||
}
|
||||
|
||||
stackEl.innerHTML = lines.join('');
|
||||
if (calc.isEditing) {
|
||||
if (currentErrorMessage) {
|
||||
displayEl.textContent = `ERROR: ${currentErrorMessage}`;
|
||||
displayEl.classList.add('display-error');
|
||||
} else if (calc.isEditing) {
|
||||
displayEl.textContent = `ENTERING: ${calc.inputValue}`;
|
||||
displayEl.classList.remove('display-error');
|
||||
} else if (isMovingStackItem && hasStackSelection()) {
|
||||
displayEl.textContent = `MOVING: ${['X', 'Y', 'Z', 'T'][stackCursor] || '?'}`;
|
||||
displayEl.classList.remove('display-error');
|
||||
} else if (hasStackSelection()) {
|
||||
displayEl.textContent = `SELECTED: ${['X', 'Y', 'Z', 'T'][stackCursor] || '?'}`;
|
||||
displayEl.classList.remove('display-error');
|
||||
} else {
|
||||
displayEl.textContent = 'READY';
|
||||
displayEl.classList.remove('display-error');
|
||||
}
|
||||
modeLabel.textContent = calc.angleMode;
|
||||
errorEl.textContent = '';
|
||||
renderKeypad();
|
||||
}
|
||||
|
||||
@@ -773,10 +824,12 @@
|
||||
clearStackSelection();
|
||||
calc.command(name);
|
||||
}
|
||||
currentErrorMessage = '';
|
||||
syncInputFromState();
|
||||
render();
|
||||
} catch (error) {
|
||||
errorEl.textContent = error.message;
|
||||
currentErrorMessage = error.message;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -960,7 +1013,8 @@
|
||||
execute(action.value);
|
||||
}
|
||||
} catch (error) {
|
||||
errorEl.textContent = error.message;
|
||||
currentErrorMessage = error.message;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user