v2026.5.21 — Polish positionnement popups
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Planification",
|
"name": "Planification",
|
||||||
"version": "2026.5.20",
|
"version": "2026.5.21",
|
||||||
"description": "Vue claire et rapide du planning des techniciens EasyVista. Regroupe interventions et réservations par tech, affiche horaires, contact, lieu, catégorie et statut en un coup d'œil.",
|
"description": "Vue claire et rapide du planning des techniciens EasyVista. Regroupe interventions et réservations par tech, affiche horaires, contact, lieu, catégorie et statut en un coup d'œil.",
|
||||||
"permissions": ["activeTab", "scripting", "storage", "tabs", "alarms"],
|
"permissions": ["activeTab", "scripting", "storage", "tabs", "alarms"],
|
||||||
"host_permissions": [
|
"host_permissions": [
|
||||||
|
|||||||
+22
-8
@@ -2591,15 +2591,16 @@ header.topbar::before {
|
|||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
v2026.5.17 : mode Minimisé (popup flottant compact, juste la ref)
|
v2026.5.17 : mode Minimisé (popup flottant compact, juste la ref)
|
||||||
v2026.5.19 : refonte — élément .pinned-popup-minref créé à la volée
|
v2026.5.19 : refonte — élément .pinned-popup-minref créé à la volée
|
||||||
|
v2026.5.21 : agrandi pour que la ref tienne sans déborder
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
.pinned-popup.pinned-popup-minimized {
|
.pinned-popup.pinned-popup-minimized {
|
||||||
min-width: 180px !important;
|
min-width: 240px !important;
|
||||||
max-width: 260px !important;
|
max-width: 320px !important;
|
||||||
width: auto !important;
|
width: 260px !important;
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
min-height: 60px !important;
|
min-height: 70px !important;
|
||||||
padding: 28px 10px 10px 10px !important;
|
padding: 36px 14px 14px 14px !important;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
background: var(--bg-elevated) !important;
|
background: var(--bg-elevated) !important;
|
||||||
border: 1px solid var(--border) !important;
|
border: 1px solid var(--border) !important;
|
||||||
}
|
}
|
||||||
@@ -2611,15 +2612,18 @@ header.topbar::before {
|
|||||||
.pinned-popup-minref {
|
.pinned-popup-minref {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 6px 8px;
|
padding: 8px 10px;
|
||||||
font-family: var(--mono, monospace);
|
font-family: var(--mono, monospace);
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: background 0.12s;
|
transition: background 0.12s;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
.pinned-popup-minref:hover {
|
.pinned-popup-minref:hover {
|
||||||
background: var(--bg-muted);
|
background: var(--bg-muted);
|
||||||
@@ -2891,3 +2895,13 @@ body.popup-dragging .pinned-popup {
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
v2026.5.21 : icône 📍 "active" dans le tooltip hover = déjà épinglée
|
||||||
|
========================================================================== */
|
||||||
|
.tooltip-pinbtn.tooltip-pinbtn-active {
|
||||||
|
opacity: 1 !important;
|
||||||
|
filter: none !important;
|
||||||
|
background: rgba(239, 68, 68, 0.15);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6554,6 +6554,34 @@ function pinTooltip() {
|
|||||||
if (!srcEl) return;
|
if (!srcEl) return;
|
||||||
const iv = state.currentTooltipIv;
|
const iv = state.currentTooltipIv;
|
||||||
|
|
||||||
|
// v2026.5.21 : unicité actionId + date. Si un popup pour la même ref
|
||||||
|
// ET la même date est déjà épinglé, on le supprime et on re-crée un nouveau
|
||||||
|
// (user a choisi ce comportement : "tu supprime le popup actuellement
|
||||||
|
// épinglé et tu répingle la nouvelle fenêtre").
|
||||||
|
const currentDate = state.currentDate || "";
|
||||||
|
const existingKey = (iv.actionId || "") + "|" + currentDate;
|
||||||
|
for (let i = pinnedPopups.length - 1; i >= 0; i--) {
|
||||||
|
const p = pinnedPopups[i];
|
||||||
|
if (!p || !p.el) continue;
|
||||||
|
const aid = p.el.dataset.actionId || "";
|
||||||
|
const d = p.el.dataset.originDate || "";
|
||||||
|
if (aid + "|" + d === existingKey) {
|
||||||
|
// Retirer l'ancien (popup + pastille dock éventuelle)
|
||||||
|
if (p.el._linkedPill) {
|
||||||
|
try { p.el._linkedPill.remove(); } catch (e) {}
|
||||||
|
}
|
||||||
|
try { p.el.remove(); } catch (e) {}
|
||||||
|
pinnedPopups.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Nettoyer un éventuel dock devenu vide
|
||||||
|
const dockEl = document.getElementById("pinned-popups-dock");
|
||||||
|
if (dockEl && dockEl.querySelectorAll(".pinned-popup-dock-pill").length === 0) {
|
||||||
|
dockEl.classList.remove("visible");
|
||||||
|
const closeAllBtn = document.getElementById("pinned-popups-close-all");
|
||||||
|
if (closeAllBtn) closeAllBtn.remove();
|
||||||
|
}
|
||||||
|
|
||||||
// Chercher la ligne source (row iv-v2)
|
// Chercher la ligne source (row iv-v2)
|
||||||
let rowEl = null;
|
let rowEl = null;
|
||||||
if (iv.actionId) {
|
if (iv.actionId) {
|
||||||
@@ -7592,10 +7620,10 @@ function bindTooltipInteractions() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Double-Ctrl : v4.3.0
|
// Double-Ctrl : v2026.5.21 — toggle sur l'intervention survolée
|
||||||
// - Si 0 popup épinglé ET un tooltip live visible : épingler
|
// - Si tooltip visible et iv pas encore épinglée pour cette date : épingle
|
||||||
// - Si EXACTEMENT 1 popup épinglé ET souris pas dessus : le fermer
|
// - Si tooltip visible et iv déjà épinglée pour cette date : désépingle
|
||||||
// - Si 2+ popups épinglés : ne fait rien (ambigu, user doit utiliser Échap)
|
// - Sinon (pas de tooltip visible) : rien
|
||||||
// On détecte 2 keydown Control dans une fenêtre de 400 ms.
|
// On détecte 2 keydown Control dans une fenêtre de 400 ms.
|
||||||
let lastCtrlTs = 0;
|
let lastCtrlTs = 0;
|
||||||
document.addEventListener("keydown", (e) => {
|
document.addEventListener("keydown", (e) => {
|
||||||
@@ -7604,17 +7632,36 @@ function bindTooltipInteractions() {
|
|||||||
const now = performance.now();
|
const now = performance.now();
|
||||||
if (now - lastCtrlTs < 400) {
|
if (now - lastCtrlTs < 400) {
|
||||||
lastCtrlTs = 0;
|
lastCtrlTs = 0;
|
||||||
if (pinnedPopups.length === 0) {
|
const iv = state.currentTooltipIv;
|
||||||
// Aucun popup épinglé : épingler le tooltip live s'il y en a un
|
if (!iv) return;
|
||||||
if (state.currentTooltipIv) pinTooltip();
|
const pinState = _getPinStateForIv(iv);
|
||||||
} else if (pinnedPopups.length === 1) {
|
if (pinState.isPinned && pinState.popup) {
|
||||||
// 1 popup épinglé : le fermer si la souris n'est pas dessus
|
// Déjà épinglée : désépingle (ferme le popup correspondant)
|
||||||
const p = pinnedPopups[0];
|
const idx = pinnedPopups.findIndex(p => p.el === pinState.popup);
|
||||||
if (!p.el.matches(":hover")) {
|
if (idx >= 0) pinnedPopups.splice(idx, 1);
|
||||||
_closePinnedPopup(p.el);
|
// Fermer aussi la pastille dock si elle existe
|
||||||
|
if (pinState.popup._linkedPill) {
|
||||||
|
try { pinState.popup._linkedPill.remove(); } catch (e) {}
|
||||||
}
|
}
|
||||||
|
try { pinState.popup.remove(); } catch (e) {}
|
||||||
|
// Nettoyer dock si vide
|
||||||
|
const dock = document.getElementById("pinned-popups-dock");
|
||||||
|
if (dock && dock.querySelectorAll(".pinned-popup-dock-pill").length === 0) {
|
||||||
|
dock.classList.remove("visible");
|
||||||
|
const closeAllBtn = document.getElementById("pinned-popups-close-all");
|
||||||
|
if (closeAllBtn) closeAllBtn.remove();
|
||||||
|
} else {
|
||||||
|
_ensureDockCloseAllBtn();
|
||||||
|
}
|
||||||
|
// Mettre à jour le tooltip hover (📍 → 📌)
|
||||||
|
const tip = tooltipEl();
|
||||||
|
if (tip && tip.classList.contains("visible")) {
|
||||||
|
tip.innerHTML = buildTooltipHTML(iv);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Pas encore épinglée : épingle
|
||||||
|
pinTooltip();
|
||||||
}
|
}
|
||||||
// 2+ popups : rien faire (Échap pour tout fermer)
|
|
||||||
} else {
|
} else {
|
||||||
lastCtrlTs = now;
|
lastCtrlTs = now;
|
||||||
}
|
}
|
||||||
@@ -7628,11 +7675,35 @@ function bindTooltipInteractions() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const action = btn.dataset.action;
|
const action = btn.dataset.action;
|
||||||
if (action === "pin") {
|
if (action === "pin") {
|
||||||
// v4.3.0 : toujours épingler (le tooltip live clone son contenu en popup
|
// v2026.5.21 : toggle — si déjà épinglée, désépingle ; sinon épingle
|
||||||
// détaché). Pour désépingler, l'user utilise × sur le popup, ou Échap.
|
|
||||||
if (state.currentTooltipIv) {
|
if (state.currentTooltipIv) {
|
||||||
|
const iv = state.currentTooltipIv;
|
||||||
|
const pinState = _getPinStateForIv(iv);
|
||||||
|
if (pinState.isPinned && pinState.popup) {
|
||||||
|
// Désépingle
|
||||||
|
const idx = pinnedPopups.findIndex(p => p.el === pinState.popup);
|
||||||
|
if (idx >= 0) pinnedPopups.splice(idx, 1);
|
||||||
|
if (pinState.popup._linkedPill) {
|
||||||
|
try { pinState.popup._linkedPill.remove(); } catch (err) {}
|
||||||
|
}
|
||||||
|
try { pinState.popup.remove(); } catch (err) {}
|
||||||
|
const dock = document.getElementById("pinned-popups-dock");
|
||||||
|
if (dock && dock.querySelectorAll(".pinned-popup-dock-pill").length === 0) {
|
||||||
|
dock.classList.remove("visible");
|
||||||
|
const closeAllBtn = document.getElementById("pinned-popups-close-all");
|
||||||
|
if (closeAllBtn) closeAllBtn.remove();
|
||||||
|
} else {
|
||||||
|
_ensureDockCloseAllBtn();
|
||||||
|
}
|
||||||
|
// Mettre à jour le tooltip (📍 → 📌)
|
||||||
|
const tip = tooltipEl();
|
||||||
|
if (tip && tip.classList.contains("visible")) {
|
||||||
|
tip.innerHTML = buildTooltipHTML(iv);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
pinTooltip();
|
pinTooltip();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (action === "reload") {
|
} else if (action === "reload") {
|
||||||
// v4.1.14 : recharger uniquement l'intervention actuellement affichée
|
// v4.1.14 : recharger uniquement l'intervention actuellement affichée
|
||||||
if (state.currentTooltipIv) {
|
if (state.currentTooltipIv) {
|
||||||
@@ -7772,12 +7843,25 @@ function buildTooltipHTML(iv) {
|
|||||||
rows.push(`<dt></dt><dd style="color:var(--text-faint);font-size:11px">Cliquer pour ouvrir la fiche</dd>`);
|
rows.push(`<dt></dt><dd style="color:var(--text-faint);font-size:11px">Cliquer pour ouvrir la fiche</dd>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// v2026.5.21 : icône épingle adaptative
|
||||||
|
// 📌 (épingle couchée) = pas encore épinglée, clic pour épingler
|
||||||
|
// 📍 (épingle plantée, rouge) = déjà épinglée pour cette (ref + date)
|
||||||
|
// — clic pour désépingler. NB : le popup a sa propre topbar avec un
|
||||||
|
// bouton 📍 explicite ; celui-ci dans le tooltip hover sert surtout
|
||||||
|
// à montrer visuellement l'état à l'user au survol.
|
||||||
|
const _pinState = _getPinStateForIv(iv);
|
||||||
|
const _pinIcon = _pinState.isPinned ? "📍" : "📌";
|
||||||
|
const _pinTitle = _pinState.isPinned
|
||||||
|
? "Cette intervention est déjà épinglée pour ce jour. Cliquer pour désépingler."
|
||||||
|
: "Épingler la bulle (ou double-Ctrl). Cliquer à nouveau pour libérer.";
|
||||||
|
const _pinClass = _pinState.isPinned ? " tooltip-pinbtn-active" : "";
|
||||||
|
|
||||||
if (rows.length === 0) {
|
if (rows.length === 0) {
|
||||||
return `<div class="tooltip-actions">
|
return `<div class="tooltip-actions">
|
||||||
<div class="tooltip-actionbtn" data-action="reload" title="Recharger uniquement cette intervention">
|
<div class="tooltip-actionbtn" data-action="reload" title="Recharger uniquement cette intervention">
|
||||||
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M2 8a6 6 0 1 0 1.76-4.24M2 3v3h3" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M2 8a6 6 0 1 0 1.76-4.24M2 3v3h3" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="tooltip-actionbtn tooltip-pinbtn" data-action="pin" title="Épingler la bulle (ou double-Ctrl). Cliquer à nouveau pour libérer.">📌</div>
|
<div class="tooltip-actionbtn tooltip-pinbtn${_pinClass}" data-action="pin" title="${_pinTitle}">${_pinIcon}</div>
|
||||||
</div><dl><dt>Info</dt><dd>Aucun détail disponible</dd></dl>`;
|
</div><dl><dt>Info</dt><dd>Aucun détail disponible</dd></dl>`;
|
||||||
}
|
}
|
||||||
// v4.1.13/14 : boutons d'action en haut à droite (recharger + épingler)
|
// v4.1.13/14 : boutons d'action en haut à droite (recharger + épingler)
|
||||||
@@ -7785,10 +7869,30 @@ function buildTooltipHTML(iv) {
|
|||||||
<div class="tooltip-actionbtn" data-action="reload" title="Recharger uniquement cette intervention">
|
<div class="tooltip-actionbtn" data-action="reload" title="Recharger uniquement cette intervention">
|
||||||
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M2 8a6 6 0 1 0 1.76-4.24M2 3v3h3" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M2 8a6 6 0 1 0 1.76-4.24M2 3v3h3" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="tooltip-actionbtn tooltip-pinbtn" data-action="pin" title="Épingler la bulle (ou double-Ctrl). Cliquer à nouveau pour libérer.">📌</div>
|
<div class="tooltip-actionbtn tooltip-pinbtn${_pinClass}" data-action="pin" title="${_pinTitle}">${_pinIcon}</div>
|
||||||
</div><dl>${rows.join("")}</dl>`;
|
</div><dl>${rows.join("")}</dl>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* v2026.5.21 : retourne l'état d'épinglage pour une intervention donnée
|
||||||
|
* (pour la date actuellement affichée). Utilisé pour afficher 📌 vs 📍
|
||||||
|
* dans le tooltip hover.
|
||||||
|
*/
|
||||||
|
function _getPinStateForIv(iv) {
|
||||||
|
if (!iv || !iv.actionId) return { isPinned: false };
|
||||||
|
const date = state.currentDate || "";
|
||||||
|
const key = iv.actionId + "|" + date;
|
||||||
|
for (const p of pinnedPopups) {
|
||||||
|
if (!p || !p.el) continue;
|
||||||
|
const aid = p.el.dataset.actionId || "";
|
||||||
|
const d = p.el.dataset.originDate || "";
|
||||||
|
if (aid + "|" + d === key) {
|
||||||
|
return { isPinned: true, popup: p.el };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { isPinned: false };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Met en forme un texte d'action EasyVista en ajoutant des retours à la ligne
|
* Met en forme un texte d'action EasyVista en ajoutant des retours à la ligne
|
||||||
* avant chaque étiquette connue ("Date :", "Lieu :", "Contact :", etc.).
|
* avant chaque étiquette connue ("Date :", "Lieu :", "Contact :", etc.).
|
||||||
|
|||||||
Reference in New Issue
Block a user