Files
ObsiGate/docs/features/xlsx-menus-185.md
T
bruno 634ba8a272
CI / lint (push) Successful in 2m43s
CI / security (push) Successful in 1m33s
CI / test (push) Canceled after 0s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
feat: éditeur tableur — grille A → Z par blocs, barre de menus Sheets, panneau de couleurs, création .xlsx (#180 → #186)
Restaure et termine le lot tableur resté non committé. Il n'existait que dans un
`git stash` (24 fichiers suivis) et en fichiers non suivis (menus.js, formula.js,
color-picker.js, tests, fiches) : la 2.53.3 livrée ne le contenait donc pas. Le
stash, créé avant le commit BUG-108, n'a jamais été restauré.

#180 Redimensionnement au curseur (en-tête de bord miroité + classe de repaint),
grille thémée sur les 15 thèmes et les modes contraste élevé / sépia, menu
Fichier au niveau des onglets, barre épinglée pleine largeur.

#181 Modèle de saisie Google Sheets : sélection ≠ édition (caret masqué sans
quitter contenteditable, double-clic / F2 / première frappe qui remplace,
Entrée contextuelle, Échap qui restaure) + inventaire priorisé des écarts.

#182 Désélection fiable (clic simple sans dépendre du focus), contours de plage
non empilés (sélecteur de classes sans point), couleurs texte/fond sur une
plage, clic droit qui préserve la sélection multiple.

#183 Panneau de couleurs façon Google Sheets : palette 8 × 10, STANDARD,
PERSONNALISÉ, coche selon la luminance, sortie `#rrggbb` (une valeur HSL était
rejetée par normHex).

#184 Peinture de format complète (toutes propriétés, source sans format =
réinitialisation), sélection multi-lignes/colonnes depuis les marges, grille
étendue : colonnes A → Z d'emblée, lignes ajoutées PAR BLOCS DE 100 au
défilement jusqu'à 1000 — matérialiser 1000 lignes d'un coup = ~26 000 cellules
câblées par feuille, ce qui épuisait le tas de la suite JSDOM ; index de
cellules `ref → td`, court-circuits formule/styles, marqueur data-wired.

#185 Barre de menus : les 10 menus Google Sheets (161 entrées, 125 câblées, 36
annoncées indisponibles), ruban façon Sheets, grille unie 1 px dérivée du thème.

#186 « Créer un fichier » propose .xlsx et construit un vrai classeur OPC
(openpyxl) au lieu d'une charge utile texte illisible.

Corrections trouvées en restaurant et en exerçant le lot :
- fuite mémoire : écouteur `click` anonyme posé sur #content-area à chaque
  rendu, jamais retiré — sa fermeture retenait la grille précédente en entier ;
- sorties Markdown / HTML / Imprimer de la barre de menus inertes
  (`data-xlsx-export` jamais réparti, seul `data-xlsx-action` l'était) ;
- collision de classe `.xlsx-structure-menu` entre la barre de menus et le menu
  Structure de la barre d'outils (toute requête tombait sur un nœud masqué) ;
- curseur col/row-resize absent quand le pointeur est sur la table elle-même ;
- l'export emportait les lignes et colonnes vides du quadrillage (CSV, Markdown,
  HTML, impression) ;
- clic extérieur avalé par la grâce de 250 ms du menu contextuel (destinée au
  seul appui long tactile) ;
- une entrée indisponible laissait la barre de menus ouverte.

Tests : pytest 1595 passés / 2 ignorés ; ruff et mypy 0 erreur ; bandit 0 ;
33 suites frontend vertes (xlsx-viewer 163/163, xlsx-menus 19/19,
xlsx-formula 14/14) ; E2E complet 132 passés / 12 ignorés ; E2E xlsx-viewer
19/19.
2026-10-08 12:00:15 -04:00

119 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# #185 — Tableur : barre de menus Google Sheets, boutons façon Sheets, grille unie
> **Statut :** ✅ livré le 2026-10-08 (v2.54.0) — ouvert le 2026-10-05 | **Effort :** 1-2 jours | **Impact :** 🟡
> **Références :** [Roadmap](../ROADMAP.md) · [Changelog](../../CHANGELOG.md) ·
> [#154](./xlsx-ui-redesign.md) · [#179](./xlsx-menus-179.md) ·
> [#180](./xlsx-sheetbar-180.md) · [#183](./xlsx-sheet-input-181.md))
## 1. Constat
L'éditeur tableur avait un ruban de boutons et trois menus (Structure, Mise en
forme, Export) mais **aucune barre de menus** : ni Fichier, ni Modifier, ni
Affichage, ni Insertion, ni Format, ni Données, ni Outils, ni Gemini, ni
Extensions, ni Aide. Beaucoup de fonctions existentes n'étaient atteignables que
par un clic droit ou par un bouton à connaître. Le ruban lui-même gardait un
chromer bordersé qui ne ressemblait à rien, et la grille n'était bordée qu'en
bas avec une couleur (`--border`) quasi identique au fond des cellules : le
quadillage se lisait comme une tache, pas comme une grille.
## 2. Livré
### A1 — Barre de menus (les 10 menus de Google Sheets)
`frontend/js/xlsx/menus.js` (nouveau) décrit **les dix menus, dans l'ordre et avec
les libellés exacts de Google Sheets** (Fichier · Modifier · Affichage ·
Insertion · Format · Données · Outils · Gemini · Extensions · Aide), puis
`buildMenuBar()` produit la barre et ses dix menus déroulants. 161 entrées, dont
**125 wirables** (67 actions distinctes) et **36 honestement déclarées
indisponibles** (elles s'affichent mais répondent « *« X » n'est pas disponible
dans ObsiGate* »** — jamais un clic muet). Les sections imbriquées (Nombre,
Thème, Alignement, Zoom, Nettoyage…) sont des **accordéons** comme dans Sheets :
une seule ouverte à la fois par niveau.
Le menu **Fichier** a quitté la barre d'onglets pour la barre de menus, en
conservant ses identifiants (`#xlsx-file-btn`, `#xlsx-file-menu`,
`#xlsx-download-btn`, `#xlsx-csv-btn`, `[data-xlsx-export]`). Seul
**Enregistrer** reste épinglé à droite de la barre d'onglets.
### A2 — Fonctions réellement câblées
- **Fichier** : Partager, Télécharger (original · CSV · Markdown · HTML),
Imprimer, **Renommer** (`PATCH /api/file/{vault}`), **Corbeille**
(`DELETE /api/file/{vault}`), **Historique des versions** (visionneuse de
sauvegardes), Détails (vault, chemin, feuilles, cellules, révision, ·
modifications en attente via `/api/health` pour le numéro de version).
- **Modifier** : Annuler, Rétablir, Couper, Copier, Coller, **Collage spécial →
valeurs uniquement** (les formules sont évaluées puis collées comme texte),
**Supprimer → lignes / colonnes / contenu**, Tout sélectionner,
**Rechercher et remplacer** (toutes les feuilles).
- **Affichage** : Figer (ici / ligne / colonne / libérer), **Afficher → barre de
formule · grille · en-têtes · tableau de bord**, Grouper (déclaré), Feuilles
masquées (déclaré), **Zoom** (50 → 200 %, même état que le ruban), **Plein
écran**.
- **Insertion** : Lignes (au-dessus / en dessous), Colonnes (à gauche / à
droite), Cellules, Lien, Commentaire, **Fonction** (insère l'appel et place le
curseur entre les parenthèses).
- **Format** : Nombre (6 formats), **Thème** (liste des 15 thèmes construite à la
demande), Gras / Italique / Souligné / Barré, Taille de police, Alignement
(horizontal + vertical), Retour à la ligne, Rotation, **Couleurs alternées**
(une écriture par ligne, lots de 50 — plafond du backend), Effacer la mise en
forme.
- **Données** : Trier la plage (croissant / décroissant / réinitialiser),
**Créer un filtre** (sur la valeur de la cellule active), **Plages nommées**
(panneau du tableau de bord), **Nettoyage → supprimer les doublons ·trimmer**,
**Diviser le texte en colonnes**.
- **Outils** : **Saisie semi-automatique** (bascule la liste de fonctions de la
barre de formule), **Accessibilité** (cellules plus grandes, grille plus
contrastée).
- **Gemini** : les six entrées ouvrent le panneau de l'assistant IA.
- **Aide** : aide intégrée, **Mises à jour** (version), **Raccourcis clavier**
(liste), **Signaler un problème** (diagnostic copié dans le presse-papiers).
### A3 — Ruban façon Google Sheets
Boutons **plats** : 30 × 30, sans bordure ni fond au repos, survol discret, état
actif teinté, groupes séparés par des filets verticaux. Les sélecteurs (zoom,
format de nombre, taille de police) suivent le même traitement. **Enregistrer**
reste le seul pilier plein. Nouveau bouton **grille** (`#xs-grid`), piloté par le
menu Affichage et inversement.
### A4 — Grille unie et visible
- Chaque cellule est encadrée sur **les quatre côtés** par **la même** ligne
1 px (les en-têtes compris : ils se distinguent par leur fond, plus par une
bordure plus lourde).
- `--grid-border` n'est plus `--border` (1-2 % d'écart avec le fond sur la plupart
des thèmes) mais un `color-mix` **du texte vers la surface** calculé par
`themes.js` : une seule valeur pour toute la grille, lisible en sombre comme
en clair et dans les modes générés (contraste élevé, sépia).
- Les valeurs de repli des 4 blocs `:root` ont été remontées en contraste.
## 3. Tests
- `tests/frontend/xlsx-menus.test.mjs` : i18n amorcé, barre de menus (10 libellés,
un déroulant par bouton, accordéon), ** exhaustivité des actions déclarées**,
disparition des entrées d'édition en lecture seule, absence de HTML dans les
libellés, ids historiques du menu Fichier ; ruban (save, « + », grille).
- `tests/frontend/xlsx-viewer.test.mjs` : barre présente au-dessus du ruban,
entrée indisponible qui répond (et n'envoie rien), bascules Affichage, Zoom,
écriture de structure (Insertion), écriture de style (Format ▸ Nombre),
**Couleurs alternées** (3 lignes → 3 ops), Accessibilité, Raccourcis clavier,
classeur en lecture seule, jeton `--grid-border`.
- `tests/e2e/xlsx-viewer.spec.js` : les 10 menus dans le bon ordre et **au-dessus**
du ruban, accordéon Format ▸ Nombre, option indisponible, **grille 1 px sur
les 4 côtés** puis masquée à 0 px.
## 4. Limites assumées
- Les 36 options Google Sheets qu'ObsiGate n'implémente pas (macros, Apps
Script, TCD, graphiques, formulaires,/modules, AppSheet, Looker Studio,
training…) sont **listées pour rester fidèle** à la demande, et **annoncées**
comme indisponibles au clic. Elles ne sont pas masquées : la parité visuelle
prime, l'honnêteté prime sur le silence.
- **Déplacer** lignes/colonnes est déclaré indisponible : leivinguer
(supprimer + insérer + réécrire) sur un classeur avec fusions, styles et
formules n'est pas sûr en un seul geste.
- Le menu **Thème** est construit à la demande (import dynamique de
`themes.js`) : 15 entrées, pas de coût au rendu.
- Les couleurs de la grille sont dérivées via `color-mix` : un moteur qui ne le
supporte pas retombe sur `--grid-border` (feuille de style).