feat(tableaux): barre de défilement horizontale miroir synchronisée #82
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -379,6 +379,24 @@ table.cell-table th {
|
|||||||
background-color: rgb(255 240 240 / 40%);
|
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 Visibility Menu */
|
||||||
.column-actions {
|
.column-actions {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user