# #180 — Éditeur tableur : redimensionnement au curseur, grille thémée, menu Fichier & barre épinglée > **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) · [#155](./xlsx-context-menu.md) · > [#179](./xlsx-menus-179.md) · [#156](./xlsx-editor-completeness.md) ## 1. Constat Retours utilisateur après #179, quatre défauts d'ergonomie et un défaut de thème : 1. **Le curseur de redimensionnement n'apparaît pas.** Le redimensionnement par les marges existe depuis #156-A8 (`wireResize`), mais l'indicateur est invisible : `.xlsx-table td[contenteditable]` impose `cursor: cell` et les en-têtes reçoivent `cursor: pointer` **en ligne** (`paintHeaders()`). Les deux beat la règle `table.style.cursor` posée par le `mousemove`, y compris pendant le glissement. L'utilisateur n'a aucun retour sur la zone cliquable. 2. **La grille ne suit pas le thème.** `--grid-*` n'est déclaré que pour le thème par défaut (dark + light) dans `style.css`. Les 13 autres thèmes et les modes générés *contraste élevé* / *sépia* n'en fournissent pas : la grille y reste en couleurs GitHub. `themes.js` applique pourtant bien les variables via `setProperty`, sans les jetons de grille. 3. **Les sorties de fichier sont dispersées.** Télécharger, CSV et Exporter occupent trois commandes du ruban, à gauche du bouton Enregistrer. 4. **La barre n'est pas épinglée** et l'éditeur ne prend pas toute la largeur du contenu quand la sidebar est masquée (contrairement aux viewers PDF / images / Excalidraw, cf. la règle `:has(...)` de `.content-area`). 5. La pastille « Formules calculées localement » occupe une ligne entière pour annoncer un comportement normal, pas une limite. ## 2. Livré (A1-A4) ### A1 — Curseur de redimensionnement sur les marges `wireResize()` (viewer.js) remplace le `table.style.cursor = …` isolé par `paintResizeCursor(kind, th)` : - la table reçoit `.xlsx-resize-col` / `.xlsx-resize-row`, repint par le CSS sur la table **et** ses cellules (surplus des deux curseurs concurrents) ; - l'en-tête de bord est **miroité en ligne** (`th.style.cursor`), seul moyen de passer devant le `cursor: pointer` inline — l'ancienne valeur est mémorisée puis restaurée quand le pointeur quitte le bord (`mouseleave`) ; - l'état est remis à zéro au `mouseup` global : le curseur ne reste pas « coincé » après un glissement. ### A2 — Grille compatible tous les thèmes - `themes.js` : `applyTheme()` dérive les jetons `--grid-bg`, `--grid-header-bg`, `--grid-header-text`, `--grid-border` et `--grid-zebra` des surfaces du thème actif. Le zébré passe par `color-mix(… 4%, transparent)` pour rester subtil sur fond clair **et** sombre. Un thème importé sans ces jetons est donc couvert automatiquement. - `style.css` : jetons de repli (`var(--grid-bg, var(--surface))`, …) pour que la grille reste lisible même sans `applyTheme()`, plus les deux modes *contraste élevé* et *sépia* déclarés explicitement. - Le fond du menu Export passe de `var(--bg, #fff)` (repli clair cassé en thème sombre) à `var(--surface)`. ### A3 — Menu Fichier + Enregistrer au niveau des onglets - `buildCommandBar()` renvoie un fragment `fileHtml` supplémentaire : menu **Fichier** (`download`, `file-spreadsheet`, `file-text`, `code`, `printer`) + bouton **Enregistrer** primary. Les trois commandes de sortie sortent du ruban, qui ne garde que l'édition. - La coquille (`viewer.js`) rend une barre de feuilles `.xlsx-sheetbar` : onglets à gauche, `.xlsx-sheetbar-end` (menu + Enregistrer) à droite. - Câblage : `trackDismissable()` (même contrat que #179), navigation clavier, fermeture au clic sur Télécharger / CSV, `aria-haspopup` / `aria-expanded`. - **Identifiants stables** : `#xlsx-download-btn`, `#xlsx-csv-btn`, `#xlsx-save-btn` conservés — seuls le conteneur (`#xlsx-file-btn` / `#xlsx-file-menu`) et la classe (`.xlsx-file-menu`) sont nouveaux. ### A4 — Retrait de la pastille + barre épinglée pleine largeur - La pastille « Formules calculées localement » est retirée ; la barre de statut ne subsiste que pour la pastille *lecture seule* (format `.xls` / `.ods`), et disparaît entièrement quand elle n'a rien à afficher. - `.xlsx-toolbar` devient `position: sticky; top: 0` avec des marges négatives qui neutralizing le padding de `.content-area` : la barre se colle au bord haut de la page et l'éditeur occupe la largeur totale du contenu. - `.content-area:has(.xlsx-viewer)` rejoint les viewers plein cadre. ## 3. Tests - `tests/frontend/xlsx-viewer.test.mjs` — 136/136 : - barre de feuilles (onglets / menu / Enregistrer à droite, onglets sortis du ruban), contenu exact du menu Fichier (5 icônes dans l'ordre), toggle ; - curseur `col-resize` / `row-resize` sur les deux types de bord, classes de repaint, restauration du curseur d'en-tête à la sortie ; - jetons `--grid-*` pour 4 thèmes (Spotify dark, Notion light, GitHub contrasté élevé, GitHub sépia) + zébré `color-mix` ; - absence de pastille formules (xlsx **et** csv). - `tests/frontend/xlsx-menus.test.mjs` — 12/12 : icônes de `fileHtml`. - `tests/e2e/xlsx-viewer.spec.js` : le menu Fichier s'ouvre, expose les 5 entrées, se ferme au clic extérieur ; Enregistrer présent dans `.xlsx-sheetbar-end`. - 8 tests préexistants réalignés sur la sémantique « écriture verrouillée puis application locale » (le viewer n'est plus re-lu après une écriture structure / mise en forme) : le second appel PUT n'a plus lieu, les assertions portent sur la grille peinte en place. ## 4. Points d'attention - La copie des cellules vers le presse-papiers système et l'export sont inchangés : seul le **point d'entrée** des sorties de fichier a bougé. - `viewer.js` reste le propriétaire du câblage ; `command-bar.js` reste un constructeur de chaînes pur, donc testable sans DOM.