fix: barre commune — JS livré au bon chemin vendor/, v0.1.4 palier 900 px (7.74.3)
This commit is contained in:
@@ -1,5 +1,18 @@
|
|||||||
# Changelog - FlowDeck
|
# 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 ✦
|
## v7.74.2 (2026-10-21) — Barre commune v0.1.3 : menu des applications par le ✦
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
|
|||||||
+1
-1
@@ -192,7 +192,7 @@ async def lifespan(_app: FastAPI):
|
|||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="FlowDeck",
|
title="FlowDeck",
|
||||||
version="7.74.2",
|
version="7.74.3",
|
||||||
docs_url="/docs",
|
docs_url="/docs",
|
||||||
redoc_url="/redoc",
|
redoc_url="/redoc",
|
||||||
lifespan=lifespan,
|
lifespan=lifespan,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{# Barre d'apps commune dracodev — Fusion ACC-1.1 (issue #17).
|
{# 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
|
(ui/barre-apps/) : ne pas modifier la structure ici ; toute évolution
|
||||||
passe par une nouvelle version de commons.
|
passe par une nouvelle version de commons.
|
||||||
CSS/JS : /static/css/draco-barre.css, /static/js/vendor/draco-barre.js. #}
|
CSS/JS : /static/css/draco-barre.css, /static/js/vendor/draco-barre.js. #}
|
||||||
|
|||||||
+1
-1
@@ -5313,7 +5313,7 @@ body.page-locked .get-started-toolbar { display: none !important; }
|
|||||||
/* ── Barre d'apps commune dracodev + section « Liens » des pages
|
/* ── Barre d'apps commune dracodev + section « Liens » des pages
|
||||||
(Fusion ACC-1.1/1.3, snippet figé dracodev-commons v0.1.0) ── */
|
(Fusion ACC-1.1/1.3, snippet figé dracodev-commons v0.1.0) ── */
|
||||||
body.has-draco-bar .app-layout { height: calc(100vh - 60px); }
|
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); }
|
body.has-draco-bar .app-layout { height: calc(100vh - 52px); }
|
||||||
}
|
}
|
||||||
.draco-liens {
|
.draco-liens {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
/* Barre d'apps commune dracodev — v0.1.0 (bloc 0, ACC-0.2) ;
|
/* 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.2 : palier mobile resserré selon la spec (voir @media 700px) ;
|
||||||
v0.1.3 : la marque ✦ (plus grande) ouvre le menu des applications —
|
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.
|
Jetons repris du prototype validé Console v7.2.
|
||||||
Thème : clair par défaut, sombre via [data-theme="dark"] sur <html>
|
Thème : clair par défaut, sombre via [data-theme="dark"] sur <html>
|
||||||
ou prefers-color-scheme si l'app intégratrice ne force rien. */
|
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 { grid-template-columns: auto auto 1fr; }
|
||||||
.draco-bar-droite { justify-content: flex-end; }
|
.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
|
/* Mobile (spec des barres, § Mobile) : la barre se resserre sur
|
||||||
l'essentiel — marque, onglet de l'app courante, alertes, Spark,
|
l'essentiel — marque, onglet de l'app courante, alertes, Spark,
|
||||||
avatar. Le rail, la recherche, les domaines et le menu « ••• »
|
avatar. Le rail, la recherche, les domaines et le menu « ••• »
|
||||||
disparaissent : chaque app garde ses contrôles natifs (barre
|
disparaissent : chaque app garde ses contrôles natifs (barre
|
||||||
basse, en-tête) — un seul hamburger visible, celui de l'app.
|
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
|
(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 { height: 52px; grid-template-columns: auto 1fr auto; }
|
||||||
.draco-bar-centre, .draco-domaines, .draco-bar .draco-icon-btn { display: none; }
|
.draco-bar-centre, .draco-domaines, .draco-bar .draco-icon-btn { display: none; }
|
||||||
.draco-apptab { display: none; }
|
.draco-apptab { display: none; }
|
||||||
|
|||||||
@@ -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);
|
|
||||||
})();
|
|
||||||
Vendored
+79
-7
@@ -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
|
* 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
|
* (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),
|
* Actions émises (data-act) : rail, palette, domaine (data-dom),
|
||||||
* alertes, spark, menu, rappel-entete. L'app intégratrice décide de
|
* alertes, spark, menu, rappel-entete. L'app intégratrice décide de
|
||||||
* chacune (ouvrir la palette, le panneau Spark, changer de domaine…).
|
* 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 () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
@@ -21,15 +25,16 @@
|
|||||||
|
|
||||||
function setCounts(counts) {
|
function setCounts(counts) {
|
||||||
Object.keys(counts || {}).forEach(function (k) {
|
Object.keys(counts || {}).forEach(function (k) {
|
||||||
var b = document.querySelector('.draco-badge[data-badge="' + k + '"]');
|
/* Toutes les occurrences : l'onglet ET sa ligne dans le menu. */
|
||||||
if (!b) return;
|
document.querySelectorAll('.draco-badge[data-badge="' + k + '"]').forEach(function (b) {
|
||||||
b.textContent = counts[k];
|
b.textContent = counts[k];
|
||||||
b.hidden = !(counts[k] > 0);
|
b.hidden = !(counts[k] > 0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function setCurrent(app) {
|
function setCurrent(app) {
|
||||||
document.querySelectorAll(".draco-apptab").forEach(function (t) {
|
document.querySelectorAll(".draco-apptab, .draco-apps-item").forEach(function (t) {
|
||||||
t.dataset.active = t.dataset.app === app ? "true" : "false";
|
if (t.dataset.app) t.dataset.active = t.dataset.app === app ? "true" : "false";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function setDomain(dom) {
|
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) {
|
document.addEventListener("click", function (ev) {
|
||||||
var el = ev.target.closest("[data-act]");
|
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;
|
if (!el) return;
|
||||||
var act = el.dataset.act;
|
var act = el.dataset.act;
|
||||||
if (act === "domaine") setDomain(el.dataset.dom);
|
if (act === "domaine") setDomain(el.dataset.dom);
|
||||||
@@ -47,9 +114,14 @@
|
|||||||
else document.dispatchEvent(new CustomEvent("draco:bar", { detail: { act: act, el: el } }));
|
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. */
|
/* API publique : l'app met les compteurs à jour sans recharger. */
|
||||||
window.DRACO_BAR = { setCounts: setCounts, setCurrent: setCurrent, setDomain: setDomain };
|
window.DRACO_BAR = { setCounts: setCounts, setCurrent: setCurrent, setDomain: setDomain };
|
||||||
|
|
||||||
|
buildMenu();
|
||||||
if (cfg.current) setCurrent(cfg.current);
|
if (cfg.current) setCurrent(cfg.current);
|
||||||
if (cfg.domain) setDomain(cfg.domain);
|
if (cfg.domain) setDomain(cfg.domain);
|
||||||
if (cfg.counts) setCounts(cfg.counts);
|
if (cfg.counts) setCounts(cfg.counts);
|
||||||
|
|||||||
Reference in New Issue
Block a user