fix(tableaux): scroll horizontal via wrapper overflow-x auto #82
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -397,6 +397,14 @@ table.cell-table th {
|
|||||||
display: none;
|
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 Visibility Menu */
|
||||||
.column-actions {
|
.column-actions {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
|
|||||||
+23
-15
@@ -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 () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Renvoie le conteneur réellement scrollable horizontalement du tableau.
|
|
||||||
function getScrollEl(wrapper) {
|
|
||||||
return wrapper.querySelector(".dash-spreadsheet-container") || wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setup(wrapper) {
|
function setup(wrapper) {
|
||||||
if (wrapper.dataset.hscrollReady === "1") return;
|
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");
|
const bar = document.createElement("div");
|
||||||
bar.className = "dt-hscroll is-hidden";
|
bar.className = "dt-hscroll is-hidden";
|
||||||
const inner = document.createElement("div");
|
const inner = document.createElement("div");
|
||||||
inner.className = "dt-hscroll-inner";
|
inner.className = "dt-hscroll-inner";
|
||||||
bar.appendChild(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
|
// 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.
|
// déclenche l'événement scroll de façon synchrone dans Chromium.
|
||||||
@@ -24,30 +28,34 @@
|
|||||||
const onBar = () => {
|
const onBar = () => {
|
||||||
if (syncing) return;
|
if (syncing) return;
|
||||||
syncing = true;
|
syncing = true;
|
||||||
scrollEl.scrollLeft = bar.scrollLeft;
|
scrollWrapper.scrollLeft = bar.scrollLeft;
|
||||||
syncing = false;
|
syncing = false;
|
||||||
};
|
};
|
||||||
const onTable = () => {
|
const onTable = () => {
|
||||||
if (syncing) return;
|
if (syncing) return;
|
||||||
syncing = true;
|
syncing = true;
|
||||||
bar.scrollLeft = scrollEl.scrollLeft;
|
bar.scrollLeft = scrollWrapper.scrollLeft;
|
||||||
syncing = false;
|
syncing = false;
|
||||||
};
|
};
|
||||||
bar.addEventListener("scroll", onBar);
|
bar.addEventListener("scroll", onBar);
|
||||||
scrollEl.addEventListener("scroll", onTable);
|
scrollWrapper.addEventListener("scroll", onTable);
|
||||||
|
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
const total = scrollEl.scrollWidth;
|
const total = scrollWrapper.scrollWidth;
|
||||||
const visible = scrollEl.clientWidth;
|
const visible = scrollWrapper.clientWidth;
|
||||||
inner.style.width = total + "px";
|
inner.style.width = total + "px";
|
||||||
// +1 absorbe les erreurs d'arrondi sous-pixel
|
// +1 absorbe les erreurs d'arrondi sous-pixel
|
||||||
bar.classList.toggle("is-hidden", total <= visible + 1);
|
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).
|
// Recalcule quand le tableau change (pagination, tri, filtre, données).
|
||||||
const obs = new MutationObserver(() => refresh());
|
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.
|
// SPA : ce listener est intentionnellement conservé pour toute la durée de vie de la page.
|
||||||
window.addEventListener("resize", refresh);
|
window.addEventListener("resize", refresh);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user