feat: replace calc topbar controls with popup menus

This commit is contained in:
2026-04-25 02:14:40 +02:00
parent 784c470b67
commit 3d58309e0d
3 changed files with 105 additions and 22 deletions
+36 -14
View File
@@ -167,8 +167,11 @@ body {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
} }
.topbar-label, .menu-cell {
.mode-select > span { position: relative;
}
.topbar-label {
display: block; display: block;
margin-bottom: 6px; margin-bottom: 6px;
font-size: 11px; font-size: 11px;
@@ -193,21 +196,41 @@ body {
background: rgba(255, 255, 255, 0.06); background: rgba(255, 255, 255, 0.06);
} }
.mode-select { .menu-trigger {
display: grid;
gap: 6px;
min-width: 0;
font-size: 12px;
}
.mode-cell select {
width: 100%; width: 100%;
min-height: 38px;
} }
.const-buttons { .popup-menu {
position: absolute;
top: calc(100% + 8px);
left: 10px;
right: 10px;
z-index: 20;
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px; gap: 8px;
padding: 10px;
border-radius: 14px;
background: rgba(18, 24, 34, 0.96);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 16px 30px rgba(0, 0, 0, 0.35);
}
.popup-menu[hidden] {
display: none;
}
.popup-menu-compact {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.hidden-select {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
opacity: 0;
pointer-events: none;
} }
.action-cell { .action-cell {
@@ -215,8 +238,7 @@ body {
align-items: stretch; align-items: stretch;
} }
.action-cell > button, .action-cell > button {
.const-buttons > button {
width: 100%; width: 100%;
} }
+9 -6
View File
@@ -26,18 +26,21 @@
<input id="input" class="hidden-input" type="text" autocomplete="off" aria-hidden="true" tabindex="-1"> <input id="input" class="hidden-input" type="text" autocomplete="off" aria-hidden="true" tabindex="-1">
<div class="topbar"> <div class="topbar">
<label class="topbar-cell mode-select mode-cell"> <div class="topbar-cell menu-cell" id="modeMenuWrap">
<span>mode</span> <div class="topbar-label">mode</div>
<select id="angleMode"> <button type="button" class="menu-trigger key-function" id="modeMenuButton" aria-haspopup="true" aria-expanded="false">deg</button>
<div class="popup-menu" id="modeMenu" hidden></div>
<select id="angleMode" class="hidden-select" aria-hidden="true" tabindex="-1">
<option value="deg">Degrees</option> <option value="deg">Degrees</option>
<option value="rad">Radians</option> <option value="rad">Radians</option>
<option value="grad">Grads</option> <option value="grad">Grads</option>
</select> </select>
</label> </div>
<div class="topbar-cell consts-cell"> <div class="topbar-cell menu-cell consts-cell" id="constsMenuWrap">
<div class="topbar-label">consts</div> <div class="topbar-label">consts</div>
<div class="const-buttons" id="constButtons"></div> <button type="button" class="menu-trigger key-function" id="constsMenuButton" aria-haspopup="true" aria-expanded="false">consts</button>
<div class="popup-menu popup-menu-compact" id="constsMenu" hidden></div>
</div> </div>
<div class="topbar-status status"> <div class="topbar-status status">
+60 -2
View File
@@ -9,6 +9,10 @@ const inputValueLabel = document.getElementById('inputValueLabel');
const editingLabel = document.getElementById('editingLabel'); const editingLabel = document.getElementById('editingLabel');
const modeLabel = document.getElementById('modeLabel'); const modeLabel = document.getElementById('modeLabel');
const angleMode = document.getElementById('angleMode'); const angleMode = document.getElementById('angleMode');
const modeMenuButton = document.getElementById('modeMenuButton');
const modeMenu = document.getElementById('modeMenu');
const constsMenuButton = document.getElementById('constsMenuButton');
const constsMenu = document.getElementById('constsMenu');
const keyLayouts = { const keyLayouts = {
functions: [ functions: [
@@ -137,6 +141,28 @@ function handleBackspaceAction() {
execute('drop'); execute('drop');
} }
function closePopupMenus() {
modeMenu.hidden = true;
constsMenu.hidden = true;
modeMenuButton.setAttribute('aria-expanded', 'false');
constsMenuButton.setAttribute('aria-expanded', 'false');
}
function togglePopupMenu(menuName) {
const isModeMenu = menuName === 'mode';
const targetMenu = isModeMenu ? modeMenu : constsMenu;
const targetButton = isModeMenu ? modeMenuButton : constsMenuButton;
const otherMenu = isModeMenu ? constsMenu : modeMenu;
const otherButton = isModeMenu ? constsMenuButton : modeMenuButton;
const willOpen = targetMenu.hidden;
otherMenu.hidden = true;
otherButton.setAttribute('aria-expanded', 'false');
targetMenu.hidden = !willOpen;
targetButton.setAttribute('aria-expanded', String(willOpen));
}
function createButton(cell) { function createButton(cell) {
if (!cell) { if (!cell) {
const spacer = document.createElement('div'); const spacer = document.createElement('div');
@@ -151,6 +177,7 @@ function createButton(cell) {
button.className = cell.className; button.className = cell.className;
button.addEventListener('click', () => { button.addEventListener('click', () => {
focusScreen(); focusScreen();
closePopupMenus();
if (cell.type === 'input') { if (cell.type === 'input') {
pressKey(cell.value); pressKey(cell.value);
return; return;
@@ -164,6 +191,11 @@ function createButton(cell) {
handleBackspaceAction(); handleBackspaceAction();
return; return;
} }
if (cell.value === 'setModeDeg' || cell.value === 'setModeRad' || cell.value === 'setModeGrad') {
angleMode.value = cell.value === 'setModeDeg' ? 'deg' : (cell.value === 'setModeRad' ? 'rad' : 'grad');
angleMode.dispatchEvent(new Event('change'));
return;
}
} }
execute(cell.value); execute(cell.value);
}); });
@@ -348,6 +380,7 @@ function render() {
inputValueLabel.textContent = calc.inputValue || '∅'; inputValueLabel.textContent = calc.inputValue || '∅';
editingLabel.textContent = String(calc.isEditing); editingLabel.textContent = String(calc.isEditing);
modeLabel.textContent = calc.angleMode; modeLabel.textContent = calc.angleMode;
modeMenuButton.textContent = calc.angleMode;
angleMode.value = calc.angleMode; angleMode.value = calc.angleMode;
errorEl.textContent = ''; errorEl.textContent = '';
} }
@@ -593,18 +626,43 @@ function handleKeydown(event) {
window.addEventListener('keydown', handleKeydown); window.addEventListener('keydown', handleKeydown);
screen.addEventListener('click', focusScreen); screen.addEventListener('click', () => {
closePopupMenus();
focusScreen();
});
window.addEventListener('load', focusScreen); window.addEventListener('load', focusScreen);
document.addEventListener('click', (event) => {
if (!event.target.closest('#modeMenuWrap') && !event.target.closest('#constsMenuWrap')) {
closePopupMenus();
}
});
modeMenuButton.addEventListener('click', (event) => {
event.stopPropagation();
togglePopupMenu('mode');
});
constsMenuButton.addEventListener('click', (event) => {
event.stopPropagation();
togglePopupMenu('consts');
});
angleMode.addEventListener('change', (event) => { angleMode.addEventListener('change', (event) => {
calc.angleMode = event.target.value; calc.angleMode = event.target.value;
closePopupMenus();
render(); render();
}); });
renderKeyLayout(document.getElementById('functionsButtons'), keyLayouts.functions); renderKeyLayout(document.getElementById('functionsButtons'), keyLayouts.functions);
renderKeyLayout(document.getElementById('numbersButtons'), keyLayouts.numbers); renderKeyLayout(document.getElementById('numbersButtons'), keyLayouts.numbers);
renderKeyLayout(document.getElementById('operatorsButtons'), keyLayouts.operators); renderKeyLayout(document.getElementById('operatorsButtons'), keyLayouts.operators);
renderKeyLayout(document.getElementById('constButtons'), [topButtons.consts]); renderKeyLayout(modeMenu, [[
{ type: 'action', value: 'setModeDeg', label: 'Degrees', className: 'key-function' },
{ type: 'action', value: 'setModeRad', label: 'Radians', className: 'key-function' },
{ type: 'action', value: 'setModeGrad', label: 'Grads', className: 'key-function' },
]]);
renderKeyLayout(constsMenu, [topButtons.consts]);
document.getElementById('deleteButton').appendChild(createButton(topButtons.del)); document.getElementById('deleteButton').appendChild(createButton(topButtons.del));
document.getElementById('backspaceButton').appendChild(createButton(topButtons.backspace)); document.getElementById('backspaceButton').appendChild(createButton(topButtons.backspace));
document.getElementById('escapeButton').appendChild(createButton(topButtons.escape)); document.getElementById('escapeButton').appendChild(createButton(topButtons.escape));