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.
6.0 KiB
6.0 KiB
#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 · Changelog · #154 · #155 · #179 · #156
1. Constat
Retours utilisateur après #179, quatre défauts d'ergonomie et un défaut de thème :
- 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]imposecursor: cellet les en-têtes reçoiventcursor: pointeren ligne (paintHeaders()). Les deux beat la règletable.style.cursorposée par lemousemove, y compris pendant le glissement. L'utilisateur n'a aucun retour sur la zone cliquable. - La grille ne suit pas le thème.
--grid-*n'est déclaré que pour le thème par défaut (dark + light) dansstyle.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.jsapplique pourtant bien les variables viasetProperty, sans les jetons de grille. - Les sorties de fichier sont dispersées. Télécharger, CSV et Exporter occupent trois commandes du ruban, à gauche du bouton Enregistrer.
- 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). - 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 lecursor: pointerinline — l'ancienne valeur est mémorisée puis restaurée quand le pointeur quitte le bord (mouseleave) ; - l'état est remis à zéro au
mouseupglobal : 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-borderet--grid-zebrades surfaces du thème actif. Le zébré passe parcolor-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 sansapplyTheme(), 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 fragmentfileHtmlsupplé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-btnconservé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-toolbardevientposition: sticky; top: 0avec 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-resizesur 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 defileHtml.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.jsreste le propriétaire du câblage ;command-bar.jsreste un constructeur de chaînes pur, donc testable sans DOM.