fix: side peek des bases repasse en vanilla JS + largeur 1100px standard (v7.49.0)
FlowDeck CI / test (push) Failing after 3h13m58s
FlowDeck CI / lint (push) Successful in 2m12s
FlowDeck CI / docker (push) Skipped

- Panneau peek: les bindings Alpine (x-data absent du conteneur) rendaient
  loovverture et le redimensionnement inoperants -> cblage direct sur le document.
- Helper unique window.fdWirePeekResize (app.js): pointer capture, 300px-90vw,
  clic=fermer, largeur persiste fd_peek_width partagee entre les 4 peeks.
- database-table-container margin:0 (tableau colle a gauche, marge Library).
- .lib-container remonte dans app.css (trash etait pleine largeur), .db-index 1100px.
- ObsiGate verifie sans code: creation .xlsx OK (openpyxl, #186).
This commit is contained in:
2026-10-05 22:47:36 -04:00
parent 894004a1f5
commit 1d1cdbd618
63 changed files with 9473 additions and 393 deletions
+71 -3
View File
@@ -252,6 +252,12 @@
// managed by Alpine or HTMX keep their default behaviour.
function shouldIntercept(a, evt) {
if (!a || !a.getAttribute || !isPlainLeftClick(evt)) return false;
// Un handler plus profond a déjà traité le clic et appelé preventDefault
// (onglets de vue, lien interne…). Sans ce test, le navigateur AND ce
// navigateur SPA traitent le même clic : le composant rend son état, puis
// `fdLoad` remplace la zone `.main-wrapper` par la réponse serveur — le
// composant perd son DOM et la page finit vide.
if (evt.defaultPrevented) return false;
if (a.hasAttribute('data-no-nav')) return false;
var target = (a.getAttribute('target') || '').toLowerCase();
if (target && target !== '_self') return false;
@@ -278,14 +284,30 @@
}
var loading = false;
var LOAD_WATCHDOG_MS = 15000;
function fdLoad(url, push) {
if (!window.htmx) { window.location.href = url; return; }
if (loading) return;
// Une navigation est déjà en vol : on l'ABANDONNE au profit d'un chargement
// complet. Avant, ce clic etait simplement ignore (aucune URL, aucun
// changement) — et si la promesse htmx ne se resolvait jamais, `loading`
// restait a `true` indefiniment : plus aucune navigation ne repartait, la
// page restait figee (ou a moitie peinte) jusqu'au hard refresh.
if (loading) { window.location.href = url; return; }
loading = true;
var settled = false;
function done() {
if (settled) return;
settled = true;
loading = false;
clearTimeout(watchdog);
}
// Filet de securite : `loading` ne doit jamais pouvoir rester bloque.
var watchdog = setTimeout(function () {
if (settled) return;
done();
window.location.href = url;
}, LOAD_WATCHDOG_MS);
var p;
try {
p = window.htmx.ajax('GET', url, {
@@ -361,7 +383,8 @@
mw.style.opacity = '0';
mw.style.pointerEvents = 'none';
mw.setAttribute('x-ignore', '');
var pending = mw.querySelectorAll('script[src]').length;
var scripts = Array.prototype.slice.call(mw.querySelectorAll('script[src]'));
var pending = scripts.length;
var reveal = function () {
if (!mw.isConnected) return;
mw.style.opacity = '';
@@ -380,7 +403,14 @@
reveal();
};
var onScript = function (e) {
if (!e.target || e.target.tagName !== 'SCRIPT') return;
var node = e.target;
// `load`/`error` en capture sur `document` voit TOUS les <script> de la
// page, pas seulement ceux de la zone swappée : compter les uns et les
// autres décomptait trop vite et montait l'arbre Alpine avant que les
// scripts de la page n'aient tourné (page à moitié montée, composants
// affiché à l'état brut). On ne compte donc que les noeuds attendus.
if (!node || node.tagName !== 'SCRIPT') return;
if (scripts.indexOf(node) === -1) return;
if (--pending <= 0) done();
};
if (!pending) {
@@ -481,3 +511,41 @@ function openCardDetail(id) {
document.getElementById('card-modal-title').textContent = 'Issue #' + id;
}
// ── Side peek : poignée de redimensionnement partagée (v7.49.1) ──
// Une seule implémentation pour les peeks de Library, du workspace local,
// de My Tasks et des bases de données (le code copié trois fois dérivait :
// celui de My Tasks ne capturait pas le pointeur et ne persistait rien).
// La largeur choisie est commune à tous les peeks (localStorage fd_peek_width),
// comme l'utilisateur ne voit qu'un seul panneau à la fois.
window.fdWirePeekResize = function(panel, handle, onClose) {
if (!panel || !handle) return;
var startX, startWidth, dragging = false;
handle.addEventListener('pointerdown', function(e) {
e.preventDefault();
try { handle.setPointerCapture(e.pointerId); } catch { /* pointeur fantôme */ }
startX = e.clientX;
startWidth = panel.offsetWidth;
dragging = true;
document.body.style.userSelect = 'none';
});
handle.addEventListener('pointermove', function(e) {
if (!dragging) return;
var w = Math.max(300, Math.min(window.innerWidth * 0.9, startWidth + (startX - e.clientX)));
panel.style.width = w + 'px';
});
function stop(e) {
if (!dragging) return;
dragging = false;
document.body.style.userSelect = '';
try { handle.releasePointerCapture(e.pointerId); } catch { /* déjà relâché */ }
if (e && Math.abs(e.clientX - startX) < 3 && typeof onClose === 'function') onClose();
try { localStorage.setItem('fd_peek_width', panel.style.width); } catch { /* navigation privée */ }
}
handle.addEventListener('pointerup', stop);
handle.addEventListener('pointercancel', function() { dragging = false; document.body.style.userSelect = ''; });
try {
var saved = localStorage.getItem('fd_peek_width');
if (saved) panel.style.width = saved;
} catch { /* navigation privée */ }
};