From a422c7d713b7c7b639ccc28a5212e957add30f3b Mon Sep 17 00:00:00 2001 From: Spark Date: Wed, 21 Oct 2026 12:48:11 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20barre=20commune=20=E2=80=94=20JS=20livr?= =?UTF-8?q?=C3=A9=20au=20bon=20chemin=20vendor/,=20v0.1.4=20palier=20900?= =?UTF-8?q?=20px=20(7.74.3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 13 ++++ VERSION | 2 +- app/main.py | 2 +- app/templates/_draco_bar.html | 2 +- static/css/app.css | 2 +- static/css/draco-barre.css | 11 ++- static/js/draco-barre.js | 128 -------------------------------- static/js/vendor/draco-barre.js | 86 +++++++++++++++++++-- 8 files changed, 104 insertions(+), 142 deletions(-) delete mode 100644 static/js/draco-barre.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 3851fe6..239440a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog - FlowDeck +## v7.74.3 (2026-10-21) — Barre commune : le menu du ✦ arrive enfin + palier 900 px + +### Corrigé + +- **Le JavaScript de la barre était copié au mauvais endroit** : + la page charge `/static/js/vendor/draco-barre.js`, la v0.1.3 + avait été posée dans `/static/js/` — le menu des applications + ne pouvait pas s'ouvrir (retour de Bruno). Fichier livré au bon + chemin, copie égarée supprimée. +- **Barre commune v0.1.4** : palier mobile élargi de 700 à + 900 px (zone morte fenêtres réduites / tablettes / webviews), + décalage de `.app-layout` aligné. + ## v7.74.2 (2026-10-21) — Barre commune v0.1.3 : menu des applications par le ✦ ### Ajouté diff --git a/VERSION b/VERSION index c75d362..8bd97d9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.74.2 +7.74.3 diff --git a/app/main.py b/app/main.py index be65956..72a1998 100644 --- a/app/main.py +++ b/app/main.py @@ -192,7 +192,7 @@ async def lifespan(_app: FastAPI): app = FastAPI( title="FlowDeck", - version="7.74.2", + version="7.74.3", docs_url="/docs", redoc_url="/redoc", lifespan=lifespan, diff --git a/app/templates/_draco_bar.html b/app/templates/_draco_bar.html index f3082c4..13f7c85 100644 --- a/app/templates/_draco_bar.html +++ b/app/templates/_draco_bar.html @@ -1,5 +1,5 @@ {# Barre d'apps commune dracodev — Fusion ACC-1.1 (issue #17). - Snippet FIGÉ vendangé depuis Projets/dracodev-commons v0.1.3 + Snippet FIGÉ vendangé depuis Projets/dracodev-commons v0.1.4 (ui/barre-apps/) : ne pas modifier la structure ici ; toute évolution passe par une nouvelle version de commons. CSS/JS : /static/css/draco-barre.css, /static/js/vendor/draco-barre.js. #} diff --git a/static/css/app.css b/static/css/app.css index 1cbc4dd..cd559fb 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -5313,7 +5313,7 @@ body.page-locked .get-started-toolbar { display: none !important; } /* ── Barre d'apps commune dracodev + section « Liens » des pages (Fusion ACC-1.1/1.3, snippet figé dracodev-commons v0.1.0) ── */ body.has-draco-bar .app-layout { height: calc(100vh - 60px); } -@media (max-width: 700px) { +@media (max-width: 900px) { body.has-draco-bar .app-layout { height: calc(100vh - 52px); } } .draco-liens { diff --git a/static/css/draco-barre.css b/static/css/draco-barre.css index bec8c50..2aaa71f 100644 --- a/static/css/draco-barre.css +++ b/static/css/draco-barre.css @@ -1,7 +1,8 @@ /* Barre d'apps commune dracodev — v0.1.0 (bloc 0, ACC-0.2) ; v0.1.2 : palier mobile resserré selon la spec (voir @media 700px) ; v0.1.3 : la marque ✦ (plus grande) ouvre le menu des applications — - c'est LE changement d'app en mobile (choix de Bruno, 2026-10-21). + c'est LE changement d'app en mobile (choix de Bruno, 2026-10-21) ; + v0.1.4 : palier mobile élargi de 700 à 900 px (zone morte comblée). Jetons repris du prototype validé Console v7.2. Thème : clair par défaut, sombre via [data-theme="dark"] sur ou prefers-color-scheme si l'app intégratrice ne force rien. */ @@ -143,14 +144,18 @@ .draco-bar { grid-template-columns: auto auto 1fr; } .draco-bar-droite { justify-content: flex-end; } } -@media (max-width: 700px) { +@media (max-width: 900px) { /* Mobile (spec des barres, § Mobile) : la barre se resserre sur l'essentiel — marque, onglet de l'app courante, alertes, Spark, avatar. Le rail, la recherche, les domaines et le menu « ••• » disparaissent : chaque app garde ses contrôles natifs (barre basse, en-tête) — un seul hamburger visible, celui de l'app. + Le changement d'app passe par le menu du ✦ (v0.1.3). (v0.1.2 — avant, les onglets entiers et les pastilles domaines - restaient affichés et la barre débordait sur téléphone.) */ + restaient affichés et la barre débordait sur téléphone. + v0.1.4 — palier élargi de 700 à 900 px : entre les deux, la + barre complète ne tenait pas (fenêtres réduites, tablettes, + webviews) alors que les apps sont déjà en mise en page mobile.) */ .draco-bar { height: 52px; grid-template-columns: auto 1fr auto; } .draco-bar-centre, .draco-domaines, .draco-bar .draco-icon-btn { display: none; } .draco-apptab { display: none; } diff --git a/static/js/draco-barre.js b/static/js/draco-barre.js deleted file mode 100644 index b5e454b..0000000 --- a/static/js/draco-barre.js +++ /dev/null @@ -1,128 +0,0 @@ -/* Barre d'apps commune dracodev — comportement (v0.1.0, ACC-0.2 ; - * v0.1.3 : menu des applications ouvert par la marque ✦). - * - * Aucun gestionnaire inline : un seul écouteur délégué sur le document - * (patron validé par le prototype — les onclick du HTML généré sont - * proscrits dans tout le programme). - * - * Configuration attendue avant le chargement : - * window.DRACO_BAR_CONFIG = { - * current: "tiktak", // app courante - * domain: "pro", // domaine actif - * counts: { tiktak: 3, kanbante: 3, alertes: 3, spark: 0 }, - * onAction: function (act, el) { … } // l'app relaie les actions - * } // qu'elle ne connaît pas - * Actions émises (data-act) : rail, palette, domaine (data-dom), - * alertes, spark, menu, rappel-entete. L'app intégratrice décide de - * chacune (ouvrir la palette, le panneau Spark, changer de domaine…). - * Action INTERNE (jamais relayée) : apps — la marque ✦ ouvre le menu - * des applications, construit depuis les onglets ; c'est le moyen de - * changer d'app en mobile, où les onglets sont repliés (v0.1.2). - */ -(function () { - "use strict"; - var cfg = window.DRACO_BAR_CONFIG || {}; - - function setCounts(counts) { - Object.keys(counts || {}).forEach(function (k) { - /* Toutes les occurrences : l'onglet ET sa ligne dans le menu. */ - document.querySelectorAll('.draco-badge[data-badge="' + k + '"]').forEach(function (b) { - b.textContent = counts[k]; - b.hidden = !(counts[k] > 0); - }); - }); - } - function setCurrent(app) { - document.querySelectorAll(".draco-apptab, .draco-apps-item").forEach(function (t) { - if (t.dataset.app) t.dataset.active = t.dataset.app === app ? "true" : "false"; - }); - } - function setDomain(dom) { - document.querySelectorAll('.draco-domaines [data-dom]').forEach(function (b) { - b.dataset.on = b.dataset.dom === dom ? "true" : "false"; - }); - } - - /* ── Menu des applications (v0.1.3) ────────────────────────────── - * Construit depuis les onglets présents dans la barre : mêmes - * liens, mêmes icônes, mêmes badges. Ouvert par la marque ✦ - * (data-act="apps"), fermé par un clic extérieur, Échap, ou le - * choix d'une application (la navigation suit son cours). */ - var bar = document.querySelector(".draco-bar"); - var brand = bar ? bar.querySelector(".draco-brand") : null; - var menu = null; - - function buildMenu() { - if (!bar || !brand || menu) return; - menu = document.createElement("div"); - menu.className = "draco-apps-menu"; - menu.hidden = true; - menu.setAttribute("role", "menu"); - menu.setAttribute("aria-label", "Applications"); - document.querySelectorAll(".draco-apptab").forEach(function (t) { - var a = document.createElement("a"); - a.className = "draco-apps-item"; - a.href = t.getAttribute("href"); - a.dataset.app = t.dataset.app; - a.setAttribute("role", "menuitem"); - var ico = t.querySelector(".draco-apptab-ico"); - if (ico) a.appendChild(ico.cloneNode(true)); - var txt = document.createElement("span"); - txt.className = "draco-apps-item-txt"; - var fort = t.querySelector(".draco-apptab-txt strong"); - var petit = t.querySelector(".draco-apptab-txt small"); - var nom = document.createElement("span"); - nom.className = "draco-apps-item-nom"; - nom.textContent = fort ? fort.textContent : t.dataset.app; - txt.appendChild(nom); - if (petit) { - var role = document.createElement("span"); - role.className = "draco-apps-item-role"; - role.textContent = petit.textContent; - txt.appendChild(role); - } - a.appendChild(txt); - var badge = t.querySelector(".draco-badge"); - if (badge) a.appendChild(badge.cloneNode(true)); - menu.appendChild(a); - }); - bar.appendChild(menu); - } - - function toggleMenu(force) { - buildMenu(); - if (!menu) return; - var open = typeof force === "boolean" ? force : menu.hidden; - menu.hidden = !open; - if (brand) brand.setAttribute("aria-expanded", open ? "true" : "false"); - } - - document.addEventListener("click", function (ev) { - var el = ev.target.closest("[data-act]"); - if (el && el.dataset.act === "apps") { - ev.preventDefault(); - toggleMenu(); - return; - } - if (menu && !menu.hidden && !ev.target.closest(".draco-apps-menu") && !ev.target.closest(".draco-brand")) { - toggleMenu(false); - } - if (!el) return; - var act = el.dataset.act; - if (act === "domaine") setDomain(el.dataset.dom); - if (typeof cfg.onAction === "function") cfg.onAction(act, el, ev); - else document.dispatchEvent(new CustomEvent("draco:bar", { detail: { act: act, el: el } })); - }); - - document.addEventListener("keydown", function (ev) { - if (ev.key === "Escape") toggleMenu(false); - }); - - /* API publique : l'app met les compteurs à jour sans recharger. */ - window.DRACO_BAR = { setCounts: setCounts, setCurrent: setCurrent, setDomain: setDomain }; - - buildMenu(); - if (cfg.current) setCurrent(cfg.current); - if (cfg.domain) setDomain(cfg.domain); - if (cfg.counts) setCounts(cfg.counts); -})(); diff --git a/static/js/vendor/draco-barre.js b/static/js/vendor/draco-barre.js index b39ba50..b5e454b 100644 --- a/static/js/vendor/draco-barre.js +++ b/static/js/vendor/draco-barre.js @@ -1,4 +1,5 @@ -/* Barre d'apps commune dracodev — comportement (v0.1.0, ACC-0.2) +/* Barre d'apps commune dracodev — comportement (v0.1.0, ACC-0.2 ; + * v0.1.3 : menu des applications ouvert par la marque ✦). * * Aucun gestionnaire inline : un seul écouteur délégué sur le document * (patron validé par le prototype — les onclick du HTML généré sont @@ -14,6 +15,9 @@ * Actions émises (data-act) : rail, palette, domaine (data-dom), * alertes, spark, menu, rappel-entete. L'app intégratrice décide de * chacune (ouvrir la palette, le panneau Spark, changer de domaine…). + * Action INTERNE (jamais relayée) : apps — la marque ✦ ouvre le menu + * des applications, construit depuis les onglets ; c'est le moyen de + * changer d'app en mobile, où les onglets sont repliés (v0.1.2). */ (function () { "use strict"; @@ -21,15 +25,16 @@ function setCounts(counts) { Object.keys(counts || {}).forEach(function (k) { - var b = document.querySelector('.draco-badge[data-badge="' + k + '"]'); - if (!b) return; - b.textContent = counts[k]; - b.hidden = !(counts[k] > 0); + /* Toutes les occurrences : l'onglet ET sa ligne dans le menu. */ + document.querySelectorAll('.draco-badge[data-badge="' + k + '"]').forEach(function (b) { + b.textContent = counts[k]; + b.hidden = !(counts[k] > 0); + }); }); } function setCurrent(app) { - document.querySelectorAll(".draco-apptab").forEach(function (t) { - t.dataset.active = t.dataset.app === app ? "true" : "false"; + document.querySelectorAll(".draco-apptab, .draco-apps-item").forEach(function (t) { + if (t.dataset.app) t.dataset.active = t.dataset.app === app ? "true" : "false"; }); } function setDomain(dom) { @@ -38,8 +43,70 @@ }); } + /* ── Menu des applications (v0.1.3) ────────────────────────────── + * Construit depuis les onglets présents dans la barre : mêmes + * liens, mêmes icônes, mêmes badges. Ouvert par la marque ✦ + * (data-act="apps"), fermé par un clic extérieur, Échap, ou le + * choix d'une application (la navigation suit son cours). */ + var bar = document.querySelector(".draco-bar"); + var brand = bar ? bar.querySelector(".draco-brand") : null; + var menu = null; + + function buildMenu() { + if (!bar || !brand || menu) return; + menu = document.createElement("div"); + menu.className = "draco-apps-menu"; + menu.hidden = true; + menu.setAttribute("role", "menu"); + menu.setAttribute("aria-label", "Applications"); + document.querySelectorAll(".draco-apptab").forEach(function (t) { + var a = document.createElement("a"); + a.className = "draco-apps-item"; + a.href = t.getAttribute("href"); + a.dataset.app = t.dataset.app; + a.setAttribute("role", "menuitem"); + var ico = t.querySelector(".draco-apptab-ico"); + if (ico) a.appendChild(ico.cloneNode(true)); + var txt = document.createElement("span"); + txt.className = "draco-apps-item-txt"; + var fort = t.querySelector(".draco-apptab-txt strong"); + var petit = t.querySelector(".draco-apptab-txt small"); + var nom = document.createElement("span"); + nom.className = "draco-apps-item-nom"; + nom.textContent = fort ? fort.textContent : t.dataset.app; + txt.appendChild(nom); + if (petit) { + var role = document.createElement("span"); + role.className = "draco-apps-item-role"; + role.textContent = petit.textContent; + txt.appendChild(role); + } + a.appendChild(txt); + var badge = t.querySelector(".draco-badge"); + if (badge) a.appendChild(badge.cloneNode(true)); + menu.appendChild(a); + }); + bar.appendChild(menu); + } + + function toggleMenu(force) { + buildMenu(); + if (!menu) return; + var open = typeof force === "boolean" ? force : menu.hidden; + menu.hidden = !open; + if (brand) brand.setAttribute("aria-expanded", open ? "true" : "false"); + } + document.addEventListener("click", function (ev) { var el = ev.target.closest("[data-act]"); + if (el && el.dataset.act === "apps") { + ev.preventDefault(); + toggleMenu(); + return; + } + if (menu && !menu.hidden && !ev.target.closest(".draco-apps-menu") && !ev.target.closest(".draco-brand")) { + toggleMenu(false); + } if (!el) return; var act = el.dataset.act; if (act === "domaine") setDomain(el.dataset.dom); @@ -47,9 +114,14 @@ else document.dispatchEvent(new CustomEvent("draco:bar", { detail: { act: act, el: el } })); }); + document.addEventListener("keydown", function (ev) { + if (ev.key === "Escape") toggleMenu(false); + }); + /* API publique : l'app met les compteurs à jour sans recharger. */ window.DRACO_BAR = { setCounts: setCounts, setCurrent: setCurrent, setDomain: setDomain }; + buildMenu(); if (cfg.current) setCurrent(cfg.current); if (cfg.domain) setDomain(cfg.domain); if (cfg.counts) setCounts(cfg.counts);