diff --git a/src/assets/css/style.css b/src/assets/css/style.css index c8a188c..a98f098 100644 --- a/src/assets/css/style.css +++ b/src/assets/css/style.css @@ -397,6 +397,14 @@ table.cell-table th { display: none; } +/* Conteneur scrollable horizontal — overflow-y:clip ne crée pas de scroll container vertical */ +/* donc position:sticky sur th.dash-header reste calé sur la page */ +.marches_table .dt-scroll-wrapper { + overflow-x: auto; + overflow-y: clip; + width: 100%; +} + /* Column Visibility Menu */ .column-actions { margin-right: 8px; diff --git a/src/assets/table_hscroll.js b/src/assets/table_hscroll.js index 8cb8085..829db34 100644 --- a/src/assets/table_hscroll.js +++ b/src/assets/table_hscroll.js @@ -1,22 +1,26 @@ -// Barre de défilement horizontale miroir pour les tableaux (.marches_table) — #82 +// Barre de défilement horizontale pour les tableaux (.marches_table) — #82 (function () { "use strict"; - // Renvoie le conteneur réellement scrollable horizontalement du tableau. - function getScrollEl(wrapper) { - return wrapper.querySelector(".dash-spreadsheet-container") || wrapper; - } - function setup(wrapper) { if (wrapper.dataset.hscrollReady === "1") return; - const scrollEl = getScrollEl(wrapper); + const dashContainer = wrapper.querySelector(".dash-spreadsheet-container"); + if (!dashContainer) return; + + // Injecter le conteneur scrollable horizontal autour du conteneur Dash + const scrollWrapper = document.createElement("div"); + scrollWrapper.className = "dt-scroll-wrapper"; + dashContainer.parentNode.insertBefore(scrollWrapper, dashContainer); + scrollWrapper.appendChild(dashContainer); + + // Injecter la barre de défilement en haut, avant le wrapper scrollable const bar = document.createElement("div"); bar.className = "dt-hscroll is-hidden"; const inner = document.createElement("div"); inner.className = "dt-hscroll-inner"; bar.appendChild(inner); - wrapper.insertBefore(bar, wrapper.firstChild); + wrapper.insertBefore(bar, scrollWrapper); // syncing évite les boucles dans la même pile d'exécution ; l'affectation de scrollLeft // déclenche l'événement scroll de façon synchrone dans Chromium. @@ -24,30 +28,34 @@ const onBar = () => { if (syncing) return; syncing = true; - scrollEl.scrollLeft = bar.scrollLeft; + scrollWrapper.scrollLeft = bar.scrollLeft; syncing = false; }; const onTable = () => { if (syncing) return; syncing = true; - bar.scrollLeft = scrollEl.scrollLeft; + bar.scrollLeft = scrollWrapper.scrollLeft; syncing = false; }; bar.addEventListener("scroll", onBar); - scrollEl.addEventListener("scroll", onTable); + scrollWrapper.addEventListener("scroll", onTable); const refresh = () => { - const total = scrollEl.scrollWidth; - const visible = scrollEl.clientWidth; + const total = scrollWrapper.scrollWidth; + const visible = scrollWrapper.clientWidth; inner.style.width = total + "px"; // +1 absorbe les erreurs d'arrondi sous-pixel bar.classList.toggle("is-hidden", total <= visible + 1); - bar.scrollLeft = scrollEl.scrollLeft; + bar.scrollLeft = scrollWrapper.scrollLeft; }; // Recalcule quand le tableau change (pagination, tri, filtre, données). const obs = new MutationObserver(() => refresh()); - obs.observe(scrollEl, { childList: true, subtree: true, attributes: true }); + obs.observe(dashContainer, { + childList: true, + subtree: true, + attributes: true, + }); // SPA : ce listener est intentionnellement conservé pour toute la durée de vie de la page. window.addEventListener("resize", refresh);