From a5fddba9a06a825daf62b593e91eea3875b4f5c2 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 8 Oct 2026 09:06:15 +0200 Subject: [PATCH] =?UTF-8?q?Explorateur=20=C3=A9pinglable=20:=20panneau=20f?= =?UTF-8?q?ixe=20ou=20flottant=20au-dessus=20du=20sch=C3=A9ma?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bouton punaise (barre de recherche de l'explorateur) : épinglé = colonne fixe redimensionnable (comportement actuel) ; désépinglé = panneau masqué qui s'ouvre par-dessus le canevas. - Désépinglé : ouverture par le bouton ☰ ou en survolant le bord gauche ; fermeture à l'ouverture d'un schéma, au clic hors du panneau, par Échap, ou en quittant le panneau s'il a été ouvert par survol. Il revient seul quand le schéma est fermé ou qu'aucun n'est ouvert. - À la bascule, le défilement du canevas est compensé : le schéma ne bouge pas à l'écran (zoom conservé, pas de recadrage). - Choix mémorisé par navigateur (localStorage drawerPinned). Sur mobile, l'explorateur reste un tiroir et le bouton est masqué. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01XR9aSZwC3Ssajfy1R7SLPo --- README.md | 1 + frontend/src/Explorer.tsx | 7 +++-- frontend/src/icons.tsx | 3 ++ frontend/src/styles.css | 59 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 62bde56..cc9c1b9 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ Sans `id_token_hint`, Keycloak demande de confirmer la déconnexion ; `OIDC_LOGO - **Lien direct** : l'URL `http://hôte:8080/#/dossier/schema.excalidraw` ouvre le schéma. - **Bibliothèque** Excalidraw : persistée côté serveur, partagée entre tous les schémas. - **Largeur de l'explorateur** (bureau) : glisser la poignée entre l'explorateur et le canevas ; flèches ←/→ au clavier quand elle a le focus (Maj : pas plus grand), double-clic pour revenir à 330 px. Mémorisée par navigateur. +- **Explorateur épinglé ou flottant** (bureau) : la punaise à droite de la recherche bascule entre panneau fixe à côté du schéma (par défaut) et panneau masqué, qui s'ouvre par-dessus le schéma avec le bouton ☰ ou en survolant le bord gauche de la fenêtre. Il se referme à l'ouverture d'un schéma, au clic à côté, avec `Échap`, ou dès que le pointeur le quitte s'il a été ouvert par survol. À la bascule, le schéma reste en place à l'écran. Choix mémorisé par navigateur ; sur mobile, l'explorateur reste un tiroir. - **Style** : l'explorateur et la barre du haut reprennent les couleurs, la police, les rayons et les tailles de boutons d'Excalidraw (jetons `--ex-*` en tête de `styles.css`), en clair comme en sombre. - **Mise à jour d'Excalidraw** : quand la version publiée sur le canal choisi (voir *Paramètres*) diffère de celle embarquée, une icône apparaît à droite de la barre du haut ; un clic ouvre les paramètres sur la commande de reconstruction (voir *Mettre à jour Excalidraw*). - **Mode clair/sombre** : bouton ☀/☾ à droite de la barre du haut ; s'applique à l'interface et au canevas Excalidraw, reste synchronisé avec le choix du menu d'Excalidraw. Mémorisé par navigateur. diff --git a/frontend/src/Explorer.tsx b/frontend/src/Explorer.tsx index b1d9a92..a31eb27 100644 --- a/frontend/src/Explorer.tsx +++ b/frontend/src/Explorer.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import type { DragEvent, KeyboardEvent, MouseEvent } from "react"; +import type { DragEvent, KeyboardEvent, MouseEvent, ReactNode } from "react"; import { api, ApiError, @@ -62,6 +62,8 @@ interface Props { onHistory: (path: string) => void; /** Affiche les liens de partage d'un schéma. */ onShare: (path: string) => void; + /** Contenu ajouté au bout de la barre de recherche (bouton d'épinglage du panneau). */ + toolbarEnd?: ReactNode; } type View = "list" | "grid"; @@ -88,7 +90,7 @@ const initialView = (): View => { const canMoveInto = (path: string, targetDir: string) => dirname(path) !== targetDir && targetDir !== path && !targetDir.startsWith(path + "/"); -export function Explorer({ current, refreshKey, onOpen, onMoved, onDeleted, onHistory, onShare }: Props) { +export function Explorer({ current, refreshKey, onOpen, onMoved, onDeleted, onHistory, onShare, toolbarEnd }: Props) { const [dir, setDir] = useState(() => (current ? dirname(current) : "")); const [entries, setEntries] = useState([]); const [query, setQuery] = useState(""); @@ -642,6 +644,7 @@ export function Explorer({ current, refreshKey, onOpen, onMoved, onDeleted, onHi + {toolbarEnd}
diff --git a/frontend/src/icons.tsx b/frontend/src/icons.tsx index 5438f5d..6dbfec0 100644 --- a/frontend/src/icons.tsx +++ b/frontend/src/icons.tsx @@ -200,6 +200,9 @@ export const EyeIcon = icon( , ); +/** Punaise : panneau épinglé ou flottant (comme le bouton d'ancrage des panneaux d'Excalidraw). */ +export const PinIcon = icon(); + export const LogoutIcon = icon( <> diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 928bc88..49deee5 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -245,6 +245,10 @@ button.danger:hover:not(:disabled) { cursor: col-resize; user-select: none; } +.app.instant, +.app.instant .drawer { + transition: none; +} /* Poignée de redimensionnement, à cheval sur la bordure de l'explorateur. */ .splitter { @@ -258,7 +262,7 @@ button.danger:hover:not(:disabled) { cursor: col-resize; touch-action: none; } -.drawer-open .splitter { +.pinned.drawer-open .splitter { display: block; } .splitter::after { @@ -546,6 +550,56 @@ li.active > .row { height: var(--ex-btn); } +/* --- Explorateur non épinglé (bureau) : panneau flottant sur le canevas ------ */ + +.peek-edge { + display: none; +} +@media (min-width: 769px) { + .app.unpinned, + .app.unpinned.drawer-open { + grid-template-columns: 0 1fr; + } + .unpinned .drawer { + position: fixed; + z-index: 20; + top: var(--topbar-h); + bottom: 0; + left: 0; + width: var(--drawer-w); + transform: translateX(-100%); + visibility: hidden; + transition: + transform 0.15s ease, + visibility 0.15s; + box-shadow: var(--ex-shadow-island); + } + .unpinned.drawer-open .drawer { + transform: none; + visibility: visible; + } + /* Clic hors du panneau : le referme sans agir sur le schéma. */ + .unpinned.drawer-open .backdrop { + display: block; + position: fixed; + z-index: 15; + inset: var(--topbar-h) 0 0 0; + } + /* Bord gauche sensible au survol pour faire apparaître le panneau. */ + .unpinned .peek-edge { + display: block; + position: fixed; + z-index: 16; + top: var(--topbar-h); + bottom: 0; + left: 0; + width: 6px; + } + .unpinned .peek-edge:hover { + background: linear-gradient(to right, var(--accent-soft), transparent); + } +} + /* --- Mobile : l'explorateur devient un tiroir superposé -------------------- */ @media (max-width: 768px) { @@ -585,7 +639,8 @@ li.active > .row { display: none; } .splitter, - .drawer-open .splitter { + .pinned.drawer-open .splitter, + .pin-btn { display: none; } .row .name {