From 3d58309e0d839bc367b8a34ec616a3914fafb0de Mon Sep 17 00:00:00 2001 From: MatMoul Date: Sat, 25 Apr 2026 02:14:40 +0200 Subject: [PATCH] feat: replace calc topbar controls with popup menus --- samples/calc-01/index.css | 50 +++++++++++++++++++++--------- samples/calc-01/index.html | 15 +++++---- samples/calc-01/index.js | 62 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 105 insertions(+), 22 deletions(-) diff --git a/samples/calc-01/index.css b/samples/calc-01/index.css index 94fe2e2..6ae0d54 100644 --- a/samples/calc-01/index.css +++ b/samples/calc-01/index.css @@ -167,8 +167,11 @@ body { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05); } -.topbar-label, -.mode-select > span { +.menu-cell { + position: relative; +} + +.topbar-label { display: block; margin-bottom: 6px; font-size: 11px; @@ -193,21 +196,41 @@ body { background: rgba(255, 255, 255, 0.06); } -.mode-select { - display: grid; - gap: 6px; - min-width: 0; - font-size: 12px; -} - -.mode-cell select { +.menu-trigger { width: 100%; + min-height: 38px; } -.const-buttons { +.popup-menu { + position: absolute; + top: calc(100% + 8px); + left: 10px; + right: 10px; + z-index: 20; display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); 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 { @@ -215,8 +238,7 @@ body { align-items: stretch; } -.action-cell > button, -.const-buttons > button { +.action-cell > button { width: 100%; } diff --git a/samples/calc-01/index.html b/samples/calc-01/index.html index 242e047..9d97d91 100644 --- a/samples/calc-01/index.html +++ b/samples/calc-01/index.html @@ -26,18 +26,21 @@
- +
-
+
diff --git a/samples/calc-01/index.js b/samples/calc-01/index.js index 6b71b9e..2ae15ed 100644 --- a/samples/calc-01/index.js +++ b/samples/calc-01/index.js @@ -9,6 +9,10 @@ const inputValueLabel = document.getElementById('inputValueLabel'); const editingLabel = document.getElementById('editingLabel'); const modeLabel = document.getElementById('modeLabel'); 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 = { functions: [ @@ -137,6 +141,28 @@ function handleBackspaceAction() { 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) { if (!cell) { const spacer = document.createElement('div'); @@ -151,6 +177,7 @@ function createButton(cell) { button.className = cell.className; button.addEventListener('click', () => { focusScreen(); + closePopupMenus(); if (cell.type === 'input') { pressKey(cell.value); return; @@ -164,6 +191,11 @@ function createButton(cell) { handleBackspaceAction(); 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); }); @@ -348,6 +380,7 @@ function render() { inputValueLabel.textContent = calc.inputValue || '∅'; editingLabel.textContent = String(calc.isEditing); modeLabel.textContent = calc.angleMode; + modeMenuButton.textContent = calc.angleMode; angleMode.value = calc.angleMode; errorEl.textContent = ''; } @@ -593,18 +626,43 @@ function handleKeydown(event) { window.addEventListener('keydown', handleKeydown); -screen.addEventListener('click', focusScreen); +screen.addEventListener('click', () => { + closePopupMenus(); + 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) => { calc.angleMode = event.target.value; + closePopupMenus(); render(); }); renderKeyLayout(document.getElementById('functionsButtons'), keyLayouts.functions); renderKeyLayout(document.getElementById('numbersButtons'), keyLayouts.numbers); 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('backspaceButton').appendChild(createButton(topButtons.backspace)); document.getElementById('escapeButton').appendChild(createButton(topButtons.escape));