Explorateur épinglable : panneau fixe ou flottant au-dessus du schéma

- 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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XR9aSZwC3Ssajfy1R7SLPo
This commit is contained in:
claude BotandClaude Opus 5.5 committed 2026-10-08 09:06:15 +02:00
1 parent ffb5ff1311
commit a5fddba9a0
4 files changed
+66 -4

No files matched your search

+5 -2
View File
@@ -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<Entry[]>([]);
const [query, setQuery] = useState("");
@@ -642,6 +644,7 @@ export function Explorer({ current, refreshKey, onOpen, onMoved, onDeleted, onHi
<button className="icon-btn" onClick={refresh} title="Rafraîchir" aria-label="Rafraîchir">
<RefreshIcon />
</button>
{toolbarEnd}
</div>
<div className="filters" role="group" aria-label="Filtres">
+3
View File
@@ -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(<path d="M15 4.5l-4 4-4 1.5-1.5 1.5 7 7 1.5-1.5 1.5-4 4-4M9 15l-4.5 4.5M14.5 4 20 9.5" />);
export const LogoutIcon = icon(
<>
<path d="M14 8V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2v-2" />
+57 -2
View File
@@ -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 {