From f8f6011507557a691233746f50c104f93e9602ad Mon Sep 17 00:00:00 2001 From: Juan Ezquerro LLanes Date: Fri, 7 Aug 2026 17:11:12 +0200 Subject: [PATCH 1/2] feat(ui): implement fully responsive mobile and desktop layout redesign --- package.json | 2 +- src-tauri/tauri.conf.json | 2 +- src/styles.css | 319 +++++++++++++++++++++++++++++++- src/ui/controls.ts | 41 +++- src/ui/molecule-view.ts | 20 ++ src/ui/nav.ts | 40 +++- src/ui/orbital-physics-panel.ts | 10 +- src/ui/periodic-table.ts | 3 + 8 files changed, 420 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index bbf27ce..c9571ca 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atomic-explorer", "private": true, - "version": "0.1.0", + "version": "0.2.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index cf9666a..d6f47df 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Atomic Explorer", - "version": "0.1.0", + "version": "0.2.0", "identifier": "com.arrase.atomic-explorer", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/styles.css b/src/styles.css index d31111c..c32a75c 100644 --- a/src/styles.css +++ b/src/styles.css @@ -73,6 +73,16 @@ body { pointer-events: auto; } +/* Top Navigation Bar */ +.top-nav-container { + position: absolute; + top: 16px; + left: 16px; + right: 16px; + z-index: 50; + pointer-events: auto; +} + .top-nav { display: flex; align-items: center; @@ -84,6 +94,7 @@ body { border: 1px solid var(--panel-border); border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + position: relative; } .nav-brand { @@ -105,6 +116,30 @@ body { letter-spacing: 0.5px; } +.nav-actions { + display: flex; + align-items: center; + gap: 12px; +} + +.mobile-menu-toggle { + display: none; + background: rgba(255, 255, 255, 0.08); + border: 1px solid var(--panel-border); + color: #ffffff; + padding: 8px 12px; + border-radius: 10px; + font-size: 1.2rem; + cursor: pointer; + transition: all 0.2s ease; +} + +.mobile-menu-toggle:hover, +.mobile-menu-toggle.active { + background: rgba(255, 255, 255, 0.16); + border-color: var(--accent); +} + .nav-tabs { display: flex; gap: 8px; @@ -167,6 +202,38 @@ body { color: #ffffff; } +/* Mobile Floating Actions */ +.mobile-floating-actions { + display: none; +} + +.panel-header-actions, +.physics-header-top { + display: flex; + align-items: center; + gap: 8px; +} + +.panel-close-btn { + display: none; + background: rgba(255, 255, 255, 0.08); + border: 1px solid var(--panel-border); + color: #a0a0c0; + width: 32px; + height: 32px; + border-radius: 50%; + align-items: center; + justify-content: center; + font-size: 1rem; + cursor: pointer; + transition: all 0.2s ease; +} + +.panel-close-btn:hover { + background: rgba(255, 255, 255, 0.2); + color: #ffffff; +} + /* Views Layer Container */ .view-layer { @@ -1521,12 +1588,254 @@ select:focus { word-break: break-word; } -@media (max-width: 800px) { - .physics-panel-container { - position: static; +/* Comprehensive Responsive Media Queries */ + +@media (max-width: 1024px) { + .periodic-table-wrapper { + grid-template-columns: 1fr; + grid-template-rows: auto auto auto; + overflow-y: auto; + max-height: 100%; + } + + .element-inspector-panel { + width: 100%; + margin-top: 10px; + } +} + +@media (max-width: 768px) { + /* Top Nav Mobile Drawer */ + .top-nav-container { + top: 10px; + left: 10px; + right: 10px; + } + + .top-nav { + padding: 8px 14px; + border-radius: 14px; + } + + .nav-title { + font-size: 1.05rem; + } + + .mobile-menu-toggle { + display: flex; + } + + .panel-close-btn { + display: flex; + } + + .nav-tabs { + display: none; + flex-direction: column; + position: absolute; + top: calc(100% + 8px); + left: 0; + right: 0; + background: rgba(14, 14, 28, 0.96); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border: 1px solid var(--panel-border); + border-radius: 16px; + padding: 12px; + gap: 6px; + box-shadow: 0 16px 40px rgba(0, 0, 0, 0.7); + z-index: 60; + animation: slideDown 0.2s cubic-bezier(0.16, 1, 0.3, 1); + } + + .nav-tabs.mobile-open { + display: flex; + } + + .nav-tab { + width: 100%; + justify-content: flex-start; + padding: 12px 16px; + font-size: 0.92rem; + } + + /* View Layers Padding */ + .view-layer { + top: 70px; + left: 10px; + right: 10px; + bottom: 10px; + } + + /* Mobile Floating Action Buttons */ + .mobile-floating-actions { + display: flex; + position: fixed; + bottom: 24px; + left: 50%; + transform: translateX(-50%); + z-index: 35; + gap: 10px; + pointer-events: auto; + width: max-content; + max-width: 90vw; + } + + .mobile-float-btn { + display: flex; + align-items: center; + gap: 6px; + background: rgba(18, 18, 36, 0.88); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid var(--accent); + color: #ffffff; + padding: 10px 18px; + border-radius: 30px; + font-size: 0.85rem; + font-weight: 600; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6), 0 0 16px var(--accent-glow); + cursor: pointer; + transition: transform 0.2s ease, background-color 0.2s ease; + } + + .mobile-float-btn:active { + transform: scale(0.96); + } + + /* Control Panels Bottom Sheet Drawers */ + .control-panel, + .physics-panel-container, + .molecule-overlay-panel { + position: fixed !important; + top: auto !important; + bottom: 0 !important; + left: 0 !important; + right: 0 !important; + width: 100% !important; + max-width: 100% !important; + height: 72vh !important; + max-height: 80vh !important; + transform: translateY(100%); + transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1); + z-index: 45; + border-radius: 24px 24px 0 0 !important; + border-bottom: none !important; + box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.7); + overflow-y: auto; + pointer-events: auto; + padding: 20px; + background: rgba(14, 14, 28, 0.96); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + } + + .control-panel.mobile-open, + .physics-panel-container.mobile-open, + .molecule-overlay-panel.mobile-open { + transform: translateY(0); + } + + .orbital-physics-panel { + padding: 0; + border: none; + background: transparent; + box-shadow: none; + } + + /* Periodic Table Mobile Adjustments */ + .periodic-table-wrapper { + padding: 10px; + gap: 12px; + border-radius: 14px; + } + + .table-toolbar { + flex-direction: column; + align-items: stretch; + gap: 10px; + } + + .toolbar-group { width: 100%; - max-width: 100%; - margin-top: 16px; + justify-content: space-between; + } + + .toolbar-group.search-group { + width: 100%; + } + + .search-group input, + .search-group button { + width: 100% !important; + } + + .periodic-grid-container { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + border-radius: 10px; + padding-bottom: 6px; + } + + .periodic-grid { + grid-template-columns: repeat(18, minmax(36px, 1fr)); + grid-template-rows: repeat(10, minmax(40px, 1fr)); + gap: 4px; + padding: 6px; + } + + .element-cell { + padding: 2px; + border-radius: 4px; + } + + .el-z { + font-size: 0.58rem; + } + + .el-symbol { + font-size: 0.78rem; + } + + .el-name { + font-size: 0.48rem; + } + + /* FPS Counter Mobile Repositioning */ + #fps-counter { + bottom: 80px; + right: 12px; + font-size: 0.75rem; + } +} + +@media (max-width: 480px) { + .nav-title { + font-size: 0.95rem; + } + + .mobile-float-btn { + padding: 8px 14px; + font-size: 0.8rem; + } + + .nodes-grid { + grid-template-columns: 1fr; + } + + .detail-row { + font-size: 0.8rem; + } +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); } } diff --git a/src/ui/controls.ts b/src/ui/controls.ts index 9238ae1..2b1ef99 100644 --- a/src/ui/controls.ts +++ b/src/ui/controls.ts @@ -53,12 +53,26 @@ export class ControlPanel { const isCustom = this.currentParams.quality === 'custom'; this.container.innerHTML = ` +
+ + +
+

${strings.orbitalControls}

- +
+ + +
@@ -202,6 +216,27 @@ export class ControlPanel { private attachEventListeners(): void { const strings = getStrings(); + const controlPanel = this.container.querySelector('.control-panel') as HTMLElement; + const physicsContainer = this.container.querySelector('.physics-panel-container') as HTMLElement; + + const btnShowControls = this.container.querySelector('#btn-show-controls'); + const btnShowPhysics = this.container.querySelector('#btn-show-physics'); + const btnCloseControls = this.container.querySelector('#btn-close-controls'); + + btnShowControls?.addEventListener('click', () => { + controlPanel?.classList.add('mobile-open'); + physicsContainer?.classList.remove('mobile-open'); + }); + + btnShowPhysics?.addEventListener('click', () => { + physicsContainer?.classList.add('mobile-open'); + controlPanel?.classList.remove('mobile-open'); + }); + + btnCloseControls?.addEventListener('click', () => { + controlPanel?.classList.remove('mobile-open'); + }); + const nInput = this.container.querySelector('#n-select') as HTMLInputElement; const lInput = this.container.querySelector('#l-select') as HTMLInputElement; const mInput = this.container.querySelector('#m-select') as HTMLInputElement; diff --git a/src/ui/molecule-view.ts b/src/ui/molecule-view.ts index 3bb6650..bb9ae4b 100644 --- a/src/ui/molecule-view.ts +++ b/src/ui/molecule-view.ts @@ -61,9 +61,17 @@ export class MoleculeView { this.renderer.loadMolecule(this.currentMolecule); this.container.innerHTML = ` +
+ +
+

${strings.moleculesVseprTitle}

+
@@ -135,6 +143,18 @@ export class MoleculeView { } private attachEventListeners(): void { + const molPanel = this.container.querySelector('.molecule-overlay-panel') as HTMLElement; + const btnShowMol = this.container.querySelector('#btn-show-molecule'); + const btnCloseMol = this.container.querySelector('#btn-close-molecule'); + + btnShowMol?.addEventListener('click', () => { + molPanel?.classList.add('mobile-open'); + }); + + btnCloseMol?.addEventListener('click', () => { + molPanel?.classList.remove('mobile-open'); + }); + const molSelect = this.container.querySelector('#molecule-select') as HTMLSelectElement; const btnToggle = this.container.querySelector('#btn-toggle-lobes') as HTMLButtonElement; diff --git a/src/ui/nav.ts b/src/ui/nav.ts index 7dca958..896ae24 100644 --- a/src/ui/nav.ts +++ b/src/ui/nav.ts @@ -34,7 +34,7 @@ export class NavigationBar { ${strings.title}
-