diff --git a/lidar_pipeline/tests/test_mapserve.py b/lidar_pipeline/tests/test_mapserve.py index c087208..fa0a08f 100644 --- a/lidar_pipeline/tests/test_mapserve.py +++ b/lidar_pipeline/tests/test_mapserve.py @@ -1403,3 +1403,17 @@ def test_ui_single_tabbed_panel(): assert "@media (max-width: 720px)" in _MAP_CSS and "#panel" in _MAP_CSS for state in ('data-sheet="closed"', 'data-sheet="half"', 'data-sheet="full"'): assert state.split('"')[1] in _MAP_CSS, state + + +def test_ui_panel_restores_tab_and_ignores_collapse_on_phone(): + from lidar_pipeline.mapui import _MAP_JS + render = _MAP_JS.split("function panelRender(")[1].split("\nfunction ")[0] + # Onglet choisi gardé à part : Génération rétablie une fois /api/status connu + assert "PANEL.want = PANEL.tab;" in _MAP_JS + assert "PANEL.tab = tabs.includes(PANEL.want) ? PANEL.want : 'view';" in render + assert "tab: PANEL.want" in _MAP_JS.split("function panelSave(")[1].split("\n")[0] + assert "PANEL.want = PANEL.tab = tab;" in _MAP_JS.split("function openTab(")[1].split("\nfunction ")[0] + # Repli en bande d'icônes : ordinateur seulement + assert "PANEL.collapsed && !isPhone()" in render + assert "toggle('collapsed', PANEL.collapsed)" not in render + assert "toggle('panel-collapsed', PANEL.collapsed)" not in render diff --git a/lidar_pipeline/web/map.js b/lidar_pipeline/web/map.js index 1886c0c..9809f04 100644 --- a/lidar_pipeline/web/map.js +++ b/lidar_pipeline/web/map.js @@ -1112,6 +1112,10 @@ document.addEventListener('panel:tab', (e) => { // --- panneau à onglets -------------------------------------------------------- const PANEL_LS = 'lidar-panel'; const PANEL = Object.assign({ tab: 'view', collapsed: false, sheet: 'half' }, lsGet(PANEL_LS, {})); +// want = onglet choisi (mémorisé) ; tab = onglet affiché. Génération encore +// inconnue au démarrage (api/status) : Affichage en attendant, puis l'onglet +// choisi revient dès qu'il est proposé. +PANEL.want = PANEL.tab; const phoneQuery = window.matchMedia('(max-width: 720px)'); const isPhone = () => phoneQuery.matches; @@ -1127,20 +1131,22 @@ function panelVisibleTab() { return PANEL.tab; } -function panelSave() { lsSet(PANEL_LS, { tab: PANEL.tab, collapsed: PANEL.collapsed, sheet: PANEL.sheet }); } +function panelSave() { lsSet(PANEL_LS, { tab: PANEL.want, collapsed: PANEL.collapsed, sheet: PANEL.sheet }); } function panelRender() { const tabs = panelTabs(); - if (!tabs.includes(PANEL.tab)) PANEL.tab = 'view'; + PANEL.tab = tabs.includes(PANEL.want) ? PANEL.want : 'view'; el('panelTabs').querySelectorAll('button[data-tab]').forEach(b => { b.hidden = !tabs.includes(b.dataset.tab); b.setAttribute('aria-selected', String(b.dataset.tab === PANEL.tab)); }); for (const t of ['view', 'print', 'gen', 'share']) el('tab-' + t).hidden = t !== PANEL.tab; const panel = el('panel'); - panel.classList.toggle('collapsed', PANEL.collapsed); + // Repli en bande d'icônes : ordinateur seulement (le téléphone a son volet). + const collapsed = PANEL.collapsed && !isPhone(); + panel.classList.toggle('collapsed', collapsed); panel.dataset.sheet = PANEL.sheet; - document.body.classList.toggle('panel-collapsed', PANEL.collapsed); + document.body.classList.toggle('panel-collapsed', collapsed); // Hauteur couverte par le volet, que l'échelle, l'attribution et les messages // dégagent ; volet plein : la carte est cachée, rien à dégager (sinon ils // remonteraient en haut de l'écran, sur les outils). @@ -1159,7 +1165,7 @@ function openTab(tab) { PANEL.sheet = 'half'; } const resized = isPhone() && PANEL.sheet !== before; - PANEL.tab = tab; + PANEL.want = PANEL.tab = tab; PANEL.collapsed = false; panelSave(); panelRender(); if (resized) setTimeout(panelRender, 220); // hauteur finale après la transition CSS