v2026.5.43 — Fix Firefox menu dock position + stabilité popup pin/unpin

Bug Firefox uniquement : positionnement du menu hover des pastilles du
dock (popup réduit) corrigé. La cause était que getBoundingClientRect()
était appelé immédiatement après appendChild sans que Firefox n'ait fini
de calculer la mise en page, combiné à un transform: translateY dans
l'animation d'apparition du menu. Fix : positionnement hors écran initial,
force-layout via offsetHeight, puis pose finale. Animation CSS simplifiée
en opacité-only.

Stabilité popup au pin/unpin (tous navigateurs) : la popup épinglée
bougeait de 16px et changeait légèrement de taille quand on la
dé-épinglait via le bouton 📌. Cause : .pinned-popup avait padding-top
28px + border 2px alors que .soft-unpinned avait padding-top 12px + border
1px. Fix : .soft-unpinned conserve désormais les mêmes dimensions, juste
la couleur de bordure change (--border-strong gris au lieu de --accent
bleu) pour signaler le mode détaché.
This commit is contained in:
Quentin Rouiller
2026-04-27 04:57:03 +02:00
parent 3c7e7c0c25
commit d4c1250be0
7 changed files with 76 additions and 21 deletions
+32 -5
View File
@@ -9,6 +9,33 @@
--- ---
## v2026.5.43 — Fix Firefox : positionnement menu dock + stabilité popup pin/unpin
### Menu hover sur pastille du dock (popup réduit)
- Bug Firefox uniquement : quand un popup épinglé était réduit dans la
taskbar du bas, le menu qui apparaît au survol de la pastille
(Agrandir / Fermer) se positionnait trop haut, pas juste au-dessus de
la pastille.
- Cause : `getBoundingClientRect()` était appelé immédiatement après
`appendChild`, avant que Firefox n'ait calculé la mise en page.
Combiné avec un `transform: translateY(4px)` dans l'animation
`pill-hover-menu-appear`, Firefox lisait des dimensions décalées.
- Fix : positionnement hors écran initial, force-layout via
`void offsetHeight`, mesure des dimensions, puis pose finale. CSS de
l'animation simplifiée en opacité-only (plus de transform).
### Stabilité popup au pin/unpin
- Bug : la popup épinglée bougeait visuellement et changeait légèrement
de taille quand on la dé-épinglait avec le bouton 📌 (puis l'inverse).
- Cause : `.pinned-popup` avait `padding-top: 28px` (place pour la
dragbar) et `border: 2px`, alors que `.soft-unpinned` avait
`padding-top: 12px` et `border: 1px`. Le contenu se décalait de 16px
vers le haut et la popup devenait 1px plus fine de chaque côté.
- Fix : `.soft-unpinned` conserve désormais `padding-top: 28px` et
`border: 2px` comme `.pinned-popup`. Bordure passe juste en
`--border-strong` (gris discret) plutôt que `--accent` (bleu) pour
signaler visuellement le mode "détaché". Position et taille stables.
## v2026.5.42 — Nettoyage de commentaires + exemples génériques ## v2026.5.42 — Nettoyage de commentaires + exemples génériques
- Passage en revue des commentaires de `src/viewer.js` : les exemples qui - Passage en revue des commentaires de `src/viewer.js` : les exemples qui
@@ -66,7 +93,7 @@
au viewer via `err.kind`. au viewer via `err.kind`.
- Toutes les anciennes constantes hardcodées (`EV_ORIGINS`, - Toutes les anciennes constantes hardcodées (`EV_ORIGINS`,
`DEFAULT_SUPPORT_IDS` interne à `detectTeamFromEV`, `DEFAULT_SUPPORT_IDS` interne à `detectTeamFromEV`,
`isPillonelAbsentFriday`) ont été remplacées ou retirées. `isXXXAbsentFriday`) ont été remplacées ou retirées.
### Conflits absence/réservation × intervention ### Conflits absence/réservation × intervention
- Nouveau code visuel : si une intervention est planifiée pendant - Nouveau code visuel : si une intervention est planifiée pendant
@@ -259,12 +286,12 @@
- Stats rapides .tech-row-stats ajoutés au header (nb interv, Xm · Ya) - Stats rapides .tech-row-stats ajoutés au header (nb interv, Xm · Ya)
## v2026.5.31 — Sarcelle pour absence récurrente (REJETÉ par utilisateur) ## v2026.5.31 — Sarcelle pour absence récurrente (REJETÉ par utilisateur)
- Couleur Pillonel vendredi : sarcelle foncée #0f766e / soft #ccfbf1 - Couleur absence récurrente (jour fixe) : sarcelle foncée #0f766e / soft #ccfbf1
- Variables --c-recurring, --c-recurring-soft - Variables --c-recurring, --c-recurring-soft
- Layout 4 colonnes forcées + scroll interne cartes (REJETÉ : "scroll en continu") - Layout 4 colonnes forcées + scroll interne cartes (REJETÉ : "scroll en continu")
## v2026.5.30 — Absence récurrente cyan + mode compact 24" ## v2026.5.30 — Absence récurrente cyan + mode compact 24"
- Absence récurrente Pillonel vendredi en cyan - Absences récurrentes (configurées par tech) en cyan
- Mode compact @media (max-width: 1920px) avec grid-template-columns: repeat(4, 1fr) - Mode compact @media (max-width: 1920px) avec grid-template-columns: repeat(4, 1fr)
## v2026.5.29 — Contraste++ + footer ## v2026.5.29 — Contraste++ + footer
@@ -348,8 +375,8 @@
## Notes techniques persistantes (toutes versions) ## Notes techniques persistantes (toutes versions)
- 8 techs hardcodés : "76272,83725,66635,92235,90070,40944,72485,86874" - 8 techs hardcodés à l'origine (depuis v2026.5.41 : retirés, alimentés par admin_config)
- Pillonel Olivier (ID 40944) absent tous les vendredis (hardcodé) - Absences récurrentes (un tech absent un jour fixe par semaine) hardcodées à l'origine, depuis v2026.5.41 configurables via Paramètres → Équipe
- Group ID EasyVista : 191 - Group ID EasyVista : 191
- Domaines cibles : itsma.etat-de-vaud.ch (interne), itsma.vd.ch (externe) - Domaines cibles : itsma.etat-de-vaud.ch (interne), itsma.vd.ch (externe)
- SSO : Canton ForgeRock OpenAM - SSO : Canton ForgeRock OpenAM
+11 -5
View File
@@ -7,7 +7,7 @@ Extension Chrome / Firefox pour visualiser de manière claire et rapide le plann
- **Auteur** : Quentin Rouiller (QRO) - **Auteur** : Quentin Rouiller (QRO)
- **Cible** : techniciens DGNSI (Canton de Vaud), EasyVista (`itsma.etat-de-vaud.ch` / `itsma.vd.ch`) - **Cible** : techniciens DGNSI (Canton de Vaud), EasyVista (`itsma.etat-de-vaud.ch` / `itsma.vd.ch`)
- **Démarrage projet** : jeudi 16 avril 2026 - **Démarrage projet** : jeudi 16 avril 2026
- **Version actuelle** : `v2026.5.42` - **Version actuelle** : `v2026.5.43`
- **Contact** : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact) - **Contact** : voir [page wiki Contact](https://gitea.netaplaid.ch/FroSteel/Planification/wiki/Contact)
- **Manifest** : V3 (Chrome/Edge/Firefox) - **Manifest** : V3 (Chrome/Edge/Firefox)
- **Format** : `.zip` (Chromium) + `.xpi` signé (Firefox) - **Format** : `.zip` (Chromium) + `.xpi` signé (Firefox)
@@ -40,7 +40,7 @@ Extension Chrome / Firefox pour visualiser de manière claire et rapide le plann
- **Congé / Congés** : cyan `#06b6d4` (suffixe `s` adaptatif) - **Congé / Congés** : cyan `#06b6d4` (suffixe `s` adaptatif)
- **Pompier** : rouge `#b03030` - **Pompier** : rouge `#b03030`
- Badge + barre gauche colorée + dégradé fond - Badge + barre gauche colorée + dégradé fond
- Absence récurrente Pillonel vendredi : cyan (depuis v2026.5.30) - Absences récurrentes (configurées par tech) : cyan (depuis v2026.5.30)
### User et session ### User et session
- Badge user avec photo/initiales en topbar - Badge user avec photo/initiales en topbar
@@ -87,7 +87,13 @@ Le numéro de **majeure** n'est **pas** un mois et **pas** un chiffre lié au ca
## Versions notables ## Versions notables
### `v2026.5.42` (latest, 27 avril 2026) — Nettoyage de commentaires + exemples génériques ### `v2026.5.43` (latest, 27 avril 2026) — Fix Firefox : menu dock + stabilité popup pin/unpin
- Firefox : le menu hover sur les pastilles du dock (popup réduit) se
positionne désormais correctement au-dessus de la pastille.
- Pin/unpin : la popup épinglée ne bouge plus et garde la même taille
quand on la dé-épingle / re-épingle.
### `v2026.5.42` — Nettoyage de commentaires + exemples génériques
- Uniformisation des exemples utilisés dans les commentaires de `viewer.js` - Uniformisation des exemples utilisés dans les commentaires de `viewer.js`
(parsing contacts/lieux/références/codes-barres) en placeholders abstraits. (parsing contacts/lieux/références/codes-barres) en placeholders abstraits.
Comportement runtime strictement inchangé. Comportement runtime strictement inchangé.
@@ -99,7 +105,7 @@ Le numéro de **majeure** n'est **pas** un mois et **pas** un chiffre lié au ca
- **Heures de la journée** : bouton ✓ Appliquer explicite (au lieu de save direct), toast de confirmation, refetch automatique du planning. Synchronisation effective avec les requêtes EV (`day_start_hour` / `day_end_hour` / `begin_hour` / `end_hour`) — avant, l'affichage changeait mais les requêtes restaient sur 8h-19h hardcodés. - **Heures de la journée** : bouton ✓ Appliquer explicite (au lieu de save direct), toast de confirmation, refetch automatique du planning. Synchronisation effective avec les requêtes EV (`day_start_hour` / `day_end_hour` / `begin_hour` / `end_hour`) — avant, l'affichage changeait mais les requêtes restaient sur 8h-19h hardcodés.
- **Thème unifié** : le toggle 🌙 de la topbar et le sélecteur Apparence du panel admin écrivent dans la même clé (`cfg.theme`). Le mode "Automatique" est résolu en JS via `prefers-color-scheme` (le CSS n'avait pas de bloc `@media`, ce qui faisait retomber sur le clair même quand l'OS était en sombre). Listener `matchMedia` pour bascule live en mode auto. - **Thème unifié** : le toggle 🌙 de la topbar et le sélecteur Apparence du panel admin écrivent dans la même clé (`cfg.theme`). Le mode "Automatique" est résolu en JS via `prefers-color-scheme` (le CSS n'avait pas de bloc `@media`, ce qui faisait retomber sur le clair même quand l'OS était en sombre). Listener `matchMedia` pour bascule live en mode auto.
- **Conflit absence/réservation × intervention** : si une intervention est planifiée pendant qu'un tech a une absence (toute la journée ou demi-journée) ou une réservation au même créneau, sa carte est peinte en **rouge plein** (intervention conflictuelle). Logique : full-day → toutes en rouge ; partiel → seules celles en chevauchement. - **Conflit absence/réservation × intervention** : si une intervention est planifiée pendant qu'un tech a une absence (toute la journée ou demi-journée) ou une réservation au même créneau, sa carte est peinte en **rouge plein** (intervention conflictuelle). Logique : full-day → toutes en rouge ; partiel → seules celles en chevauchement.
- **Pillonel & Cie** : suppression de la fonction hardcodée `isPillonelAbsentFriday()`. L'absence récurrente est désormais générique : `RECURRING_ABSENCES[tech.id]` lit `cfg.recurringAbsences` et le label "Absent le X" est calculé dynamiquement depuis le jour de la semaine. - **Absences récurrentes génériques** : suppression de la fonction hardcodée `isXXXAbsentFriday()`. L'absence récurrente est désormais générique : `RECURRING_ABSENCES[tech.id]` lit `cfg.recurringAbsences` et le label "Absent le X" est calculé dynamiquement depuis le jour de la semaine.
- **Notifications au-dessus du flou** : z-index `.toast-stack` relevé à 11000 (le panel admin est à 10000) pour que les toasts de feedback restent visibles quand l'admin est ouvert. - **Notifications au-dessus du flou** : z-index `.toast-stack` relevé à 11000 (le panel admin est à 10000) pour que les toasts de feedback restent visibles quand l'admin est ouvert.
- **Vue horizontale** : popups au survol/clic limités aux candidats `dessous`/`dessus` (la sidebar à gauche et la timeline pleine largeur rendent gauche/droite peu praticables). - **Vue horizontale** : popups au survol/clic limités aux candidats `dessous`/`dessus` (la sidebar à gauche et la timeline pleine largeur rendent gauche/droite peu praticables).
- **Tri équipe** : inclus d'abord, puis exclus, alphabétique dans chaque sous-groupe (ne saute plus quand on coche/décoche). - **Tri équipe** : inclus d'abord, puis exclus, alphabétique dans chaque sous-groupe (ne saute plus quand on coche/décoche).
@@ -211,7 +217,7 @@ Planning/
### Constantes / valeurs hardcodées (toutes versions) ### Constantes / valeurs hardcodées (toutes versions)
- Group ID EV par défaut : `191` (SI-CSS) — surchargeable via le sélecteur depuis v2026.5.40 - Group ID EV par défaut : `191` (SI-CSS) — surchargeable via le sélecteur depuis v2026.5.40
- Pillonel Olivier (ID 40944) : absent tous les vendredis (récurrent) - Absences récurrentes par tech : configurables via Paramètres → Équipe (depuis v2026.5.41)
- GUIDs forms EV : - GUIDs forms EV :
- Demande : `S={C99ECD05-3D48-4C62-ABF0-66292053AED6}` - Demande : `S={C99ECD05-3D48-4C62-ABF0-66292053AED6}`
- Incident : `I={07ED9C68-6172-48EA-8A58-90912B0A283E}` - Incident : `I={07ED9C68-6172-48EA-8A58-90912B0A283E}`
+5
View File
@@ -2,6 +2,11 @@
"addons": { "addons": {
"planification-dgnsi@netaplaid.ch": { "planification-dgnsi@netaplaid.ch": {
"updates": [ "updates": [
{
"version": "2026.5.43",
"update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.43/planification-v2026.5.43-firefox.xpi",
"update_hash": "sha256:9d1f0cb49b98cdb46a0e022dc53c77c98fde380590e72036e188c128d6b19965"
},
{ {
"version": "2026.5.42", "version": "2026.5.42",
"update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.42/planification-v2026.5.42-firefox.xpi", "update_link": "https://gitea.netaplaid.ch/FroSteel/Planification/releases/download/v2026.5.42/planification-v2026.5.42-firefox.xpi",
+1 -1
View File
@@ -1175,7 +1175,7 @@ async function detectTeamFromEV(origin, phpsessid, groupIdArg, supportIdsArg) {
} }
console.log("[bg] parsing pattern 1 (checkbox) :", results.length, "résultats"); console.log("[bg] parsing pattern 1 (checkbox) :", results.length, "résultats");
// Pattern 2 : fallback <option value="76272">Nom...</option> // Pattern 2 : fallback <option value="NNNNN">Nom...</option>
if (results.length === 0) { if (results.length === 0) {
const rxOption = /<option[^>]*value=["'](\d{4,7})["'][^>]*>([^<]+)<\/option>/gi; const rxOption = /<option[^>]*value=["'](\d{4,7})["'][^>]*>([^<]+)<\/option>/gi;
let mO; let mO;
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Planification", "name": "Planification",
"version": "2026.5.42", "version": "2026.5.43",
"description": "Vue claire et rapide du planning des techniciens EasyVista. Développé par Quentin Rouiller — DGNSI, Canton de Vaud.", "description": "Vue claire et rapide du planning des techniciens EasyVista. Développé par Quentin Rouiller — DGNSI, Canton de Vaud.",
"permissions": [ "permissions": [
"activeTab", "activeTab",
+14 -8
View File
@@ -1670,7 +1670,7 @@ html.view-horizontal .timeline-noon {
} }
/* ───────────────────────────────────────────────────────────────────────── /* ─────────────────────────────────────────────────────────────────────────
v4.1.20 : Message d'absence récurrente (Pillonel vendredi) v4.1.20 : Message d'absence récurrente (configurée par tech)
───────────────────────────────────────────────────────────────────────── */ ───────────────────────────────────────────────────────────────────────── */
.tech-absence-recurring { .tech-absence-recurring {
padding: 14px 12px; padding: 14px 12px;
@@ -2013,11 +2013,14 @@ body.modal-open {
z-index: 5 !important; z-index: 5 !important;
opacity: 1 !important; opacity: 1 !important;
pointer-events: auto !important; pointer-events: auto !important;
/* Pas de bordure bleue, pas de padding-top (plus de dragbar), juste les /* v2026.5.43 : on conserve les MÊMES dimensions que .pinned-popup
styles de base du tooltip (hérités de .tooltip). */ (padding-top 28px, border 2px) pour que la popup ne bouge ni ne change
border: 1px solid var(--border-strong) !important; de taille au softUnpin. La dragbar est juste retirée (l'espace
reste, c'est le tradeoff pour préserver position + taille).
Bordure plus discrète (variable --border-strong au lieu de --accent). */
border: 2px solid var(--border-strong) !important;
box-shadow: var(--shadow-hover) !important; box-shadow: var(--shadow-hover) !important;
padding-top: 12px !important; padding-top: 28px !important;
animation: none !important; animation: none !important;
} }
@@ -3045,9 +3048,12 @@ body.popup-dragging .pinned-popup {
min-width: 130px; min-width: 130px;
animation: pill-hover-menu-appear 0.12s ease-out; animation: pill-hover-menu-appear 0.12s ease-out;
} }
/* v2026.5.43 : pas de transform dans cette animation — Firefox inclut les
transforms dans getBoundingClientRect ce qui fausse le calcul de position
du menu juste après son insertion dans le DOM. Animation en opacité seule. */
@keyframes pill-hover-menu-appear { @keyframes pill-hover-menu-appear {
from { opacity: 0; transform: translateY(4px); } from { opacity: 0; }
to { opacity: 1; transform: translateY(0); } to { opacity: 1; }
} }
.pill-hover-menu-btn { .pill-hover-menu-btn {
display: flex; display: flex;
@@ -3419,7 +3425,7 @@ html.theme-dark .card.absence-cat-conge {
} }
/* ========================================================================== /* ==========================================================================
v2026.5.30 : absences récurrentes (Pillonel vendredi) en cyan v2026.5.30 : absences récurrentes (configurées par tech) en cyan
(même couleur que Congé mais texte distinct "Absent le vendredi") (même couleur que Congé mais texte distinct "Absent le vendredi")
========================================================================== */ ========================================================================== */
+12 -1
View File
@@ -9339,9 +9339,19 @@ function _showPillHoverMenu(pill, popup) {
}); });
menu.appendChild(closeBtn); menu.appendChild(closeBtn);
// v2026.5.43 (Firefox-fix) : positionner le menu HORS écran d'abord pour
// qu'il soit layouté sans flash, puis mesurer ses dimensions, puis poser
// la position finale. Sans ça, Firefox lit parfois des dimensions à 0
// (timing de l'animation `pill-hover-menu-appear` + transform initial),
// ce qui projette le menu n'importe où sur l'écran.
menu.style.left = "-9999px";
menu.style.top = "-9999px";
menu.style.visibility = "hidden";
document.body.appendChild(menu); document.body.appendChild(menu);
// Force le navigateur à calculer la mise en page maintenant (Firefox ne
// le fait pas toujours sur getBoundingClientRect immédiat après append).
void menu.offsetHeight;
// Positionner au-dessus de la pastille
const r = pill.getBoundingClientRect(); const r = pill.getBoundingClientRect();
const menuR = menu.getBoundingClientRect(); const menuR = menu.getBoundingClientRect();
let left = r.left + (r.width / 2) - (menuR.width / 2); let left = r.left + (r.width / 2) - (menuR.width / 2);
@@ -9349,6 +9359,7 @@ function _showPillHoverMenu(pill, popup) {
if (left + menuR.width > window.innerWidth - 4) left = window.innerWidth - menuR.width - 4; if (left + menuR.width > window.innerWidth - 4) left = window.innerWidth - menuR.width - 4;
menu.style.left = left + "px"; menu.style.left = left + "px";
menu.style.top = (r.top - menuR.height - 8) + "px"; menu.style.top = (r.top - menuR.height - 8) + "px";
menu.style.visibility = "";
// Garder ouvert si la souris entre dans le menu // Garder ouvert si la souris entre dans le menu
menu.addEventListener("mouseenter", () => { menu.addEventListener("mouseenter", () => {