From cb1685d0552a29a7203f6c76980e4f92e01762d8 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Tue, 23 Jun 2026 16:00:45 +0200 Subject: [PATCH] =?UTF-8?q?feat(tableaux):=20barre=20de=20d=C3=A9filement?= =?UTF-8?q?=20horizontale=20miroir=20synchronis=C3=A9e=20#82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- src/assets/css/style.css | 18 +++++++++++ src/assets/table_hscroll.js | 63 +++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/assets/table_hscroll.js diff --git a/src/assets/css/style.css b/src/assets/css/style.css index 87cd9df..c8a188c 100644 --- a/src/assets/css/style.css +++ b/src/assets/css/style.css @@ -379,6 +379,24 @@ table.cell-table th { background-color: rgb(255 240 240 / 40%); } +/* Barre de défilement horizontale miroir, collée en haut */ +.marches_table .dt-hscroll { + position: sticky; + top: 0; + z-index: 11; /* au-dessus des en-têtes sticky */ + overflow-x: auto; + overflow-y: hidden; + height: 14px; +} + +.marches_table .dt-hscroll-inner { + height: 1px; +} + +.marches_table .dt-hscroll.is-hidden { + display: none; +} + /* Column Visibility Menu */ .column-actions { margin-right: 8px; diff --git a/src/assets/table_hscroll.js b/src/assets/table_hscroll.js new file mode 100644 index 0000000..626ae27 --- /dev/null +++ b/src/assets/table_hscroll.js @@ -0,0 +1,63 @@ +// Barre de défilement horizontale miroir 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); + if (!scrollEl) return; + + 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); + + let syncing = false; + const onBar = () => { + if (syncing) return; + syncing = true; + scrollEl.scrollLeft = bar.scrollLeft; + syncing = false; + }; + const onTable = () => { + if (syncing) return; + syncing = true; + bar.scrollLeft = scrollEl.scrollLeft; + syncing = false; + }; + bar.addEventListener("scroll", onBar); + scrollEl.addEventListener("scroll", onTable); + + const refresh = () => { + const total = scrollEl.scrollWidth; + const visible = scrollEl.clientWidth; + inner.style.width = total + "px"; + bar.classList.toggle("is-hidden", total <= visible + 1); + bar.scrollLeft = scrollEl.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 }); + window.addEventListener("resize", refresh); + + wrapper.dataset.hscrollReady = "1"; + refresh(); + } + + function scan() { + document.querySelectorAll(".marches_table").forEach(setup); + } + + // Les tableaux apparaissent après le rendu Dash : observer le body. + const rootObs = new MutationObserver(() => scan()); + rootObs.observe(document.body, { childList: true, subtree: true }); + scan(); +})();