From 36b4962efb3692b9e017f8c7afa10fda6232f753 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Thu, 8 Oct 2026 15:45:37 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20tableur=20mobile=20=E2=80=94=20barres?= =?UTF-8?q?=20repli=C3=A9es=20par=20d=C3=A9faut,=20bascule=20unique=20#189?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sous 768 px, la barre de menus, le ruban et le groupe Recherche sont repliés par défaut : la barre de formule (fx), les onglets et Enregistrer restent visibles et la grille récupère la hauteur d'écran. Un bouton unique (#xlsx-tools-toggle) dans la barre des feuilles déplie/replie l'ensemble (aria-expanded, état actif) ; desktop inchangé. Ergonomie tactile : cibles ≥ 44 px, onglets en défilement horizontal, champs de la barre de formule en 16 px (anti-zoom iOS), barre de formule en 2 lignes quand les outils sont ouverts. Tests : JSDOM xlsx-viewer.test.mjs (+2, 165/165), E2E mobile (repli par défaut, dépliage, no-overflow ruban, cibles 44 px) ; openXlsx() déplie automatiquement ≤ 768 px pour les tests desktop. --- CHANGELOG.md | 26 +++++++- README.fr.md | 8 +-- README.md | 8 +-- VERSION | 2 +- desktop/Cargo.lock | 2 +- desktop/Cargo.toml | 2 +- desktop/tauri.conf.json | 2 +- docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md | 8 +++ docs/ROADMAP.md | 31 +++++++++- docs/features/xlsx-mobile-layout-189.md | 81 +++++++++++++++++++++++++ frontend/js/viewer.js | 19 ++++++ frontend/locales/en.json | 1 + frontend/locales/fr.json | 1 + frontend/style.css | 79 ++++++++++++++++++++++++ package.json | 2 +- tests/e2e/xlsx-viewer.spec.js | 38 +++++++++++- tests/frontend/xlsx-viewer.test.mjs | 48 +++++++++++++++ 17 files changed, 341 insertions(+), 17 deletions(-) create mode 100644 docs/features/xlsx-mobile-layout-189.md diff --git a/CHANGELOG.md b/CHANGELOG.md index f7e2723..a8dac55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/), et [Semantic Versioning](https://semver.org/spec/v2.0.0.html). > **En cours de développement** : les changements à venir sont listés dans la section -> [Unreleased](#unreleased). La dernière version livrée est **2.55.0**. +> [Unreleased](#unreleased). La dernière version livrée est **2.56.0**. --- @@ -14,6 +14,30 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html). --- +## [2.56.0] — 2026-10-08 + +### Ajouté + +- **#189 - Tableur : mise en page mobile (barres repliées, grille prioritaire)** + - Sur mobile (≤ 768 px), la **barre de menus** (Fichier, Modifier…), le + **ruban** de formatage et le **groupe Recherche** sont **repliés par + défaut** : la barre des feuilles et la **barre de formule (fx)** restent + toujours visibles, et la grille récupère la hauteur d'écran perdue + (l'ensemble dépassait 50 % de l'écran). + - Un **bouton unique** (`#xlsx-tools-toggle`, icône ☰) dans la barre des + feuilles déplie/replie le tout (`aria-expanded`, état actif) ; sur + desktop il reste masqué et rien ne bouge. + - **Ergonomie tactile** : cibles ≥ 44 px (boutons, menus, onglets), groupes + du ruban qui s'enroulent au lieu de déborder à cette taille, onglets de + feuilles sur une seule ligne qui **défile horizontalement**, champs de la + barre de formule en 16 px (pas de zoom iOS au focus), barre de formule qui + passe en 2 lignes quand les outils sont ouverts. + - Tests : JSDOM `xlsx-viewer.test.mjs` (bascule + règles CSS, 165/165) et + E2E mobile (repli par défaut, dépliage, ruban sans débordement, cibles + 44 px). + +--- + ## [2.55.0] — 2026-10-08 ### Ajouté diff --git a/README.fr.md b/README.fr.md index d709c80..a92751c 100644 --- a/README.fr.md +++ b/README.fr.md @@ -4,7 +4,7 @@ **Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive. -[![Version](https://img.shields.io/badge/Version-2.55.0-blue.svg)]() +[![Version](https://img.shields.io/badge/Version-2.56.0-blue.svg)]() [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/) [![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](https://www.python.org/) @@ -85,7 +85,7 @@ Les **guides d'utilisation** pas à pas se trouvent dans [`docs/GUIDES/`](docs/G - **🖼️ Images Obsidian** : Support complet des syntaxes d'images Obsidian avec résolution intelligente - **🎬 Audio & vidéo** : Lecteurs HTML5 intégrés (`.mp3 .wav .flac .mp4 .webm`…) avec streaming HTTP Range (lecture, déplacement, plein écran) et **lecture persistante** (mini-lecteur flottant / mini-fenêtre vidéo, retour au média ou arrêt à tout moment, contrôles écran verrouillé via Media Session), repli téléchargement si le format n'est pas lisible par le navigateur - **🎨 Diagrammes Excalidraw** : Visualiseur/éditeur natif des fichiers `.excalidraw` et `.excalidraw.md` (iframe sandboxée, auto-save, thème clair/sombre, texte des diagrammes indexé pour la recherche) -- **📊 Tableurs Excel** : les fichiers `.xlsx` et `.xlsm` s'ouvrent dans un visualiseur dédié — un tableau par feuille avec onglets, en-têtes A1 et édition directe des cellules (`PUT /api/file/{vault}/xlsx/save`, backup automatique, écriture atomique), plus le téléchargement du fichier d'origine. Le visualiseur rend polices, couleurs, cellules fusionnées et volets figés, et offre navigation et raccourcis clavier (`Ctrl+S`, `Suppr`, `F2`, `Ctrl+Origine/Fin`, `PgPréc/PgSuiv`, `Ctrl+flèches`), barre de formule avec noms de fonctions, zone Nom éditable (« Atteindre » `A1:B3`), presse-papiers de plage (copier/couper/coller un bloc, depuis ou vers Excel), un menu **Mise en forme** (gras/italique/souligné, alignements, couleurs, formats de nombre, fusions, volets figés, largeur/hauteur — `PUT /api/file/{vault}/xlsx/style`), tri/filtre/recherche sur toutes les feuilles, export CSV/Markdown/HTML et impression (sélection ou feuille), édition de la structure (feuilles, lignes, colonnes) et un tableau de bord du classeur (plages nommées, détection graphiques/TCD, stats par feuille) ; un `.csv` s'édite dans la même grille (RFC 4180) tandis que `.xls` et `.ods` s'ouvrent en lecture seule. Les classeurs contenant des éléments qu'ObsiGate ne peut pas conserver (valeurs calculées, segments, contrôles de formulaire, signature…) affichent un **avertissement** et demandent confirmation avant l'enregistrement ; une saisie commençant par `=` ou `@` est stockée comme texte sauf activation du bouton `f(x)`, et les écritures concurrentes d'un autre poste sont détectées (`If-Match` → « Réessayer »). L'assistant IA peut lister les feuilles, injecter un tableau borné dans son contexte, rechercher dans le classeur, analyser une plage, modifier des cellules et ajouter des lignes — sur `.xlsx`, `.xlsm` et `.csv` +- **📊 Tableurs Excel** : les fichiers `.xlsx` et `.xlsm` s'ouvrent dans un visualiseur dédié — un tableau par feuille avec onglets, en-têtes A1 et édition directe des cellules (`PUT /api/file/{vault}/xlsx/save`, backup automatique, écriture atomique), plus le téléchargement du fichier d'origine. Le visualiseur rend polices, couleurs, cellules fusionnées et volets figés, et offre navigation et raccourcis clavier (`Ctrl+S`, `Suppr`, `F2`, `Ctrl+Origine/Fin`, `PgPréc/PgSuiv`, `Ctrl+flèches`), barre de formule avec noms de fonctions, zone Nom éditable (« Atteindre » `A1:B3`), presse-papiers de plage (copier/couper/coller un bloc, depuis ou vers Excel), un menu **Mise en forme** (gras/italique/souligné, alignements, couleurs, formats de nombre, fusions, volets figés, largeur/hauteur — `PUT /api/file/{vault}/xlsx/style`), tri/filtre/recherche sur toutes les feuilles, export CSV/Markdown/HTML et impression (sélection ou feuille), édition de la structure (feuilles, lignes, colonnes) et un tableau de bord du classeur (plages nommées, détection graphiques/TCD, stats par feuille) ; un `.csv` s'édite dans la même grille (RFC 4180) tandis que `.xls` et `.ods` s'ouvrent en lecture seule. Les classeurs contenant des éléments qu'ObsiGate ne peut pas conserver (valeurs calculées, segments, contrôles de formulaire, signature…) affichent un **avertissement** et demandent confirmation avant l'enregistrement ; une saisie commençant par `=` ou `@` est stockée comme texte sauf activation du bouton `f(x)`, et les écritures concurrentes d'un autre poste sont détectées (`If-Match` → « Réessayer »). L'assistant IA peut lister les feuilles, injecter un tableau borné dans son contexte, rechercher dans le classeur, analyser une plage, modifier des cellules et ajouter des lignes — sur `.xlsx`, `.xlsm` et `.csv`. Sur mobile (≤ 768 px), la barre de menus et le ruban sont **repliés par défaut** — un bouton ☰ les déplie — pour que la grille occupe toute la hauteur d'écran - **🎨 Syntax highlight** : Coloration syntaxique des blocs de code - **🌓 Thème clair/sombre** : Toggle persisté en localStorage - **📡 Synchronisation temps réel** : Surveillance automatique des fichiers via watchdog avec mise à jour incrémentale de l'index @@ -976,8 +976,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l ## 📝 Changelog -Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.55.0). +Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.56.0). --- -*Projet : ObsiGate | Version : 2.55.0 | Dernière mise à jour : Septembre 2026* +*Projet : ObsiGate | Version : 2.56.0 | Dernière mise à jour : Septembre 2026* diff --git a/README.md b/README.md index 1bb829e..1ef3250 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface. -[![Version](https://img.shields.io/badge/Version-2.55.0-blue.svg)]() +[![Version](https://img.shields.io/badge/Version-2.56.0-blue.svg)]() [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/) [![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](https://www.python.org/) @@ -84,7 +84,7 @@ Step-by-step **user guides** live in [`docs/GUIDES/`](docs/GUIDES/): - **🖼️ Obsidian Images** : Full support for all Obsidian image syntaxes with intelligent resolution - **🎬 Audio & video** : Built-in HTML5 players (`.mp3 .wav .flac .mp4 .webm`…) with HTTP Range streaming (play, seek, fullscreen) and **persistent playback** (floating mini-player / mini video window, return to media or stop anytime, lock-screen controls via Media Session), falling back to download when the format is not playable in the browser - **🎨 Excalidraw Diagrams** : Native viewer/editor for `.excalidraw` and `.excalidraw.md` files (sandboxed iframe, autosave, dark/light theme, diagram text indexed for search) -- **📊 Excel Spreadsheets** : `.xlsx` and `.xlsm` files open in a dedicated viewer — one table per sheet with tabs, A1 headers and inline cell editing (`PUT /api/file/{vault}/xlsx/save`, automatic backup, atomic write), plus download of the original file. The viewer renders fonts, colors, merged cells and frozen panes, offers keyboard navigation and shortcuts (`Ctrl+S`, `Delete`, `F2`, `Ctrl+Home/End`, `PgUp/PgDn`, `Ctrl+arrows`), a formula bar with function suggestions, an editable Name Box ("go to" `A1:B3`), a range clipboard (copy/cut/paste a block, from or to Excel), a **Format** menu (bold/italic/underline, alignments, font & fill colours, number formats, merges, frozen panes, column width/row height — `PUT /api/file/{vault}/xlsx/style`), sort/filter/find across every sheet, CSV/Markdown/HTML export and printing (selection or sheet), sheet & row/column structure editing and a workbook dashboard (named ranges, charts/pivot detection, per-sheet stats); `.csv` is edited in the same grid (RFC 4180) while `.xls` and `.ods` open read-only. Workbooks holding elements ObsiGate cannot preserve (cached values, slicers, form controls, signature…) show a **warning** and ask for confirmation before saving; a value starting with `=` or `@` is stored as text unless the `f(x)` toggle is enabled, and concurrent writes from another process are detected (`If-Match` → "Retry"). The AI assistant can list sheets, dump a bounded table to its context, search the workbook, analyze a range, update cells and append rows — on `.xlsx`, `.xlsm` and `.csv` +- **📊 Excel Spreadsheets** : `.xlsx` and `.xlsm` files open in a dedicated viewer — one table per sheet with tabs, A1 headers and inline cell editing (`PUT /api/file/{vault}/xlsx/save`, automatic backup, atomic write), plus download of the original file. The viewer renders fonts, colors, merged cells and frozen panes, offers keyboard navigation and shortcuts (`Ctrl+S`, `Delete`, `F2`, `Ctrl+Home/End`, `PgUp/PgDn`, `Ctrl+arrows`), a formula bar with function suggestions, an editable Name Box ("go to" `A1:B3`), a range clipboard (copy/cut/paste a block, from or to Excel), a **Format** menu (bold/italic/underline, alignments, font & fill colours, number formats, merges, frozen panes, column width/row height — `PUT /api/file/{vault}/xlsx/style`), sort/filter/find across every sheet, CSV/Markdown/HTML export and printing (selection or sheet), sheet & row/column structure editing and a workbook dashboard (named ranges, charts/pivot detection, per-sheet stats); `.csv` is edited in the same grid (RFC 4180) while `.xls` and `.ods` open read-only. Workbooks holding elements ObsiGate cannot preserve (cached values, slicers, form controls, signature…) show a **warning** and ask for confirmation before saving; a value starting with `=` or `@` is stored as text unless the `f(x)` toggle is enabled, and concurrent writes from another process are detected (`If-Match` → "Retry"). The AI assistant can list sheets, dump a bounded table to its context, search the workbook, analyze a range, update cells and append rows — on `.xlsx`, `.xlsm` and `.csv`. On mobile (≤ 768 px), the menu bar and ribbon are **collapsed by default** — a single ☰ button expands them — so the grid gets the full screen height - **🎨 Syntax Highlight** : Syntax highlighting for code blocks - **🌓 Light/Dark Theme** : Toggle persisted in localStorage - **📡 Real-time Sync** : Automatic file monitoring via watchdog with incremental index updates @@ -1151,8 +1151,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) ## 📝 Changelog -See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.55.0). +See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.56.0). --- -*Project: ObsiGate | Version: 2.55.0 | Last updated: September 2026* +*Project: ObsiGate | Version: 2.56.0 | Last updated: September 2026* diff --git a/VERSION b/VERSION index c2576f1..5f46e11 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.55.0 +2.56.0 diff --git a/desktop/Cargo.lock b/desktop/Cargo.lock index e3410c6..8d76b74 100644 --- a/desktop/Cargo.lock +++ b/desktop/Cargo.lock @@ -2626,7 +2626,7 @@ dependencies = [ [[package]] name = "obsigate-desktop" -version = "2.55.0" +version = "2.56.0" dependencies = [ "chrono", "env_logger", diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index 85c446a..b4ef971 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "obsigate-desktop" -version = "2.55.0" +version = "2.56.0" description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian" authors = ["Bruno Charest"] edition = "2021" diff --git a/desktop/tauri.conf.json b/desktop/tauri.conf.json index 2009101..eebee4b 100644 --- a/desktop/tauri.conf.json +++ b/desktop/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json", "productName": "ObsiGate", - "version": "2.55.0", + "version": "2.56.0", "identifier": "com.obsigate.desktop", "build": { "frontendDist": "../frontend", diff --git a/docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md b/docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md index 995b6e8..d94eef7 100644 --- a/docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md +++ b/docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md @@ -155,6 +155,14 @@ regroupe figeage, tri, filtre, dimensions, lien, commentaire ; les menus haut** de la page et l'éditeur occupe toute la largeur du contenu, pour que les commandes restent atteignables pendant le défilement d'un grand tableau. +**Mise en page mobile** — sur un écran étroit (≤ 768 px), la barre de menus et +le ruban de formatage sont **repliés par défaut** afin que la grille occupe +toute la hauteur disponible : seuls les onglets de feuilles, **Enregistrer** et +la **barre de formule (fx)** restent affichés. Le bouton **☰** (à gauche des +onglets) déplie/replie l'ensemble ; les commandes tactiles font au moins +44 px, les onglets défilent horizontalement et les champs de saisie restent en +16 px pour éviter le zoom automatique d'iOS. + Les largeurs de colonnes et hauteurs de lignes s'ajustent en **glissant le bord des en-têtes** (souris et tactile) : en plaçant le pointeur sur le bord d'un en-tête de colonne (`A`, `B`, `C`…) ou de ligne (`1`, `2`, `3`…), le diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index f66b349..fd80b4b 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,6 +1,6 @@ # ObsiGate — Roadmap -> **Version :** 2.55.0 | **Dernière mise à jour :** 2026-10-08 +> **Version :** 2.56.0 | **Dernière mise à jour :** 2026-10-08 > **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact > vers les fonctionnalités livrées. > - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)** @@ -528,6 +528,34 @@ --- +## ✅ Terminé — Mise en page mobile du tableur + +### 189. Tableur — mise en page mobile (barres repliées, grille prioritaire) + +- **Effort :** 0,5-1 jour | **Impact :** 🟡 | **Framework :** CSS vanilla + `frontend/js/viewer.js` +- **Statut :** ✅ **livré le 2026-10-08** — ouvert le 2026-10-08 +- **Fiche :** [features/xlsx-mobile-layout-189.md](./features/xlsx-mobile-layout-189.md) +- **Description :** sur mobile, la barre de menus + le ruban + la barre de + formule s'empilaient et occupaient plus de 50 % de la hauteur d'écran. + Sous 768 px, `.xlsx-menubar`, `.xlsx-cmdbar` et le groupe Recherche sont + **repliés par défaut** (la barre de formule fx et les onglets restent + visibles) et un **bouton unique** `#xlsx-tools-toggle` dans la barre des + feuilles déplie/replie l'ensemble (`aria-expanded`, état actif) — desktop + inchangé (bouton masqué). +- **Sous-tâches :** + - [x] **A1** Repli CSS par défaut ≤ 768 px + révélation via + `.xlsx-toolbar.xlsx-tools-open` (menubar, ruban, Recherche) + - [x] **A2** Bascule `#xlsx-tools-toggle` dans `.xlsx-sheetbar` + (`aria-expanded` + classe `active` + i18n `xlsx.tools_toggle` FR/EN) + - [x] **A3** Tactile : cibles ≥ 44 px, onglets en défilement horizontal, + champs de la barre de formule en 16 px (anti-zoom iOS), barre de + formule en 2 lignes quand les outils sont ouverts + - [x] **A4** Tests : JSDOM `xlsx-viewer.test.mjs` (+2, 165/165) et E2E + mobile (repli par défaut, dépliage, no-overflow ruban, 44 px) ; + `openXlsx()` déplie automatiquement ≤ 768 px pour les tests desktop + +--- + ## ⚪ Backlog — Priorité 4 (P4) ### 73. Synchronisation multi-appareils — Obsidian Sync compatible @@ -721,6 +749,7 @@ | 185 | Tableur — barre de menus Google Sheets (10 menus), ruban Sheets, grille unie | 2.54.0 | [features/xlsx-menus-185.md](./features/xlsx-menus-185.md) | | 186 | Création d'un fichier Excel (`.xlsx`) depuis la modale « Créer un fichier » | 2.54.0 | [features/create-file-xlsx-186.md](./features/create-file-xlsx-186.md) | | 188 | Secrets — détection universelle des clés API & mots de passe, masque cliquable (clic = copie) | Unreleased | [features/secret-mask-188.md](./features/secret-mask-188.md) | +| 189 | Tableur — mise en page mobile : barres repliées par défaut, bascule unique, cibles tactiles 44 px | Unreleased | [features/xlsx-mobile-layout-189.md](./features/xlsx-mobile-layout-189.md) | | 159 | Desktop — gestion des vaults & dossiers : retrait par menu contextuel + section Configuration (ajout vault/dossier racine) | 2.50.0 | [features/desktop-tauri.md](./features/desktop-tauri.md) | | 160 | Desktop — premier lancement professionnel (répertoire `%USERPROFILE%\ObsiGate` + `Prise en main.md`) et section Configuration harmonisée | 2.51.0 | [features/desktop-tauri.md](./features/desktop-tauri.md) | | BUG-047 | Versionnage — source unique `VERSION` + bump SemVer automatique au commit (hooks + tag) | 2.3.0 | [DEVELOPMENT_AND_RELEASES.md](./DEVELOPMENT_AND_RELEASES.md) | diff --git a/docs/features/xlsx-mobile-layout-189.md b/docs/features/xlsx-mobile-layout-189.md new file mode 100644 index 0000000..5eb2e52 --- /dev/null +++ b/docs/features/xlsx-mobile-layout-189.md @@ -0,0 +1,81 @@ +# #189 — Tableur : mise en page mobile (barres repliées, grille prioritaire) + +> **Statut :** ✅ livré (2026-10-08) | **Effort :** 0,5-1 jour | **Impact :** 🟡 +> **Références :** [Roadmap](../ROADMAP.md) · [Changelog](../../CHANGELOG.md) · +> [#180](./xlsx-sheetbar-180.md) · [#185](./xlsx-menus-185.md) + +## 1. Constat + +Sur mobile, l'empilement `.xlsx-menubar` (10 menus) + `.xlsx-cmdbar` +(~40 boutons qui s'enroulent sur 4-5 rangées) + `.xlsx-sheetbar` + +`.xlsx-formula-bar` occupait **plus de 50 % de la hauteur d'écran** : la +grille, objet premier de l'éditeur, passait après la chrome. + +## 2. Livré + +### A1 — Repli par défaut (CSS, ≤ 768 px) + +Bloc `#189` dans `frontend/style.css` (après les règles `pointer: coarse`) : + +- `.xlsx-menubar`, `.xlsx-cmdbar`, `.xlsx-find-group` → `display: none` par + défaut sous 768 px ; +- `.xlsx-toolbar.xlsx-tools-open …` les réaffiche (état de la classe portée + par la **barre entière**, donc menubar + ruban + Recherche restent cohérents) ; +- `.xlsx-formula-bar` reste **toujours visible** (zone Nom + `fx` + champ) et + passe en `flex-wrap` quand les outils sont ouverts (le groupe Recherche + réapparaît sur une 2ᵉ ligne) ; +- `.xlsx-tools-toggle { display: none }` sur desktop : **zéro changement + visuel hors mobile**. + +### A2 — Bascule unique (JS) + +`frontend/js/viewer.js` (`renderXlsxViewer`) : + +- bouton `#xlsx-tools-toggle` (icône Lucide `menu`, `aria-expanded`, + `aria-label` i18n `xlsx.tools_toggle`) inséré **en tête de + `.xlsx-sheetbar`** — donc toujours visible, entre les onglets et Enregistrer ; +- listener : bascule `.xlsx-tools-open` sur `.xlsx-toolbar`, met à jour + `aria-expanded` + classe `active`, `safeCreateIcons()` pour l'icône. + +### A3 — Ergonomie tactile (CSS) + +- cibles ≥ **44 px** : `.xlsx-toolbar .btn-action`, `.xlsx-menubtn`, + `.xlsx-tab` / `.xlsx-tab-add` ; à cette taille un groupe du ruban + (police, nombre…) déborde d'un téléphone : `.xlsx-cmd-group` passe en + `flex: 0 1 auto; min-width: 0; flex-wrap: wrap` pour s'enrouler au lieu + de sortir de l'écran (contre-preuve E2E : `scrollWidth - clientWidth ≤ 1`) ; +- onglets de feuilles : une seule ligne, **défilement horizontal** + (`overflow-x: auto`, scrollbars masquées) au lieu d'enrouler ; +- champs de la barre de formule en **16 px** : en dessous, iOS zoome + automatiquement la page au focus (la « classique » debout de l'UI) ; +- groupe Recherche masqué avec le ruban pour ne pas écraser le champ de saisie. + +### A4 — i18n + +Clé `xlsx.tools_toggle` : FR « Afficher / masquer les barres d'outils » / +EN « Show / hide toolbars » (parité vérifiée par `tests/test_i18n_parity.py`). + +## 3. Tests + +- `tests/frontend/xlsx-viewer.test.mjs` (+2, **165/165**) : + 1. la bascule vit dans la barre des feuilles et flippe correctement + classe / `aria-expanded` / `active` (aller-retour) ; + 2. `style.css` masque menubar/ruban/Recherche par défaut sous 768 px et les + réaffiche via `.xlsx-tools-open` (44 px, onglets scrollables, inputs + 16 px présents dans le bloc). +- `tests/e2e/xlsx-viewer.spec.js` — test mobile réécrit : repli par défaut + (menubar/ruban cachés, formule + onglets visibles) → dépliage → ruban sans + débordement horizontal → cibles ≥ 44 px → repli ; `openXlsx()` déplie + automatiquement sous 768 px pour que les tests « desktop » restent valables + sur les deux viewports. + +## 4. Limites assumées + +- Le repli est piloté par la **largeur** (`max-width: 768px`), pas par + `pointer: coarse` : une tablette large en mode tactile garde la chrome + complète (elle a la hauteur pour). +- Le groupe Recherche n'est accessible sur mobile qu'avec les outils ouverts + ; le reset de tri reste atteignable par la mini-barre de sélection et le + menu Données (vérifié). +- Pas de drawer latéral ni de FAB : un seul bouton rétractable suffit à + l'usage — ajouter un panneau glissant si le retour utilisateur le réclame. diff --git a/frontend/js/viewer.js b/frontend/js/viewer.js index ca8001e..bc40b83 100644 --- a/frontend/js/viewer.js +++ b/frontend/js/viewer.js @@ -1473,6 +1473,11 @@ const GRID_CHUNK_ROWS = 100; // rows materialised per extension step ${actionsHtml}
+ ${tabs} ${fileHtml}
@@ -1526,6 +1531,20 @@ const GRID_CHUNK_ROWS = 100; // rows materialised per extension step // A re-render invalidates any open context menu (it targets the old DOM). closeContextMenu(); + // #189 — mobile : la barre de menus + le ruban sont repliés par défaut + // (cf. style.css ≤768px). Ce bouton, seul dans la barre des feuilles, + // déplie/replie le tout sans jamais masquer la barre de formule. + const toolsToggle = area.querySelector("#xlsx-tools-toggle"); + const toolsBar = area.querySelector(".xlsx-toolbar"); + if (toolsToggle && toolsBar) { + toolsToggle.addEventListener("click", () => { + const open = toolsBar.classList.toggle("xlsx-tools-open"); + toolsToggle.setAttribute("aria-expanded", String(open)); + toolsToggle.classList.toggle("active", open); + safeCreateIcons(); + }); + } + // ── #153 A17 — dashboard panel: named ranges, objects, per-sheet KPI ── // Fetched lazily on first open of the dashboard tab; a workbook without // anything notable hides the button entirely (no empty panel). diff --git a/frontend/locales/en.json b/frontend/locales/en.json index 3ffbe11..68e8329 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -2050,6 +2050,7 @@ "xlsx.mbar_label": "Spreadsheet menus", "xlsx.cmdbar_label": "Spreadsheet command bar", + "xlsx.tools_toggle": "Show / hide toolbars", "xlsx.mbar_file": "File", "xlsx.mbar_edit": "Edit", "xlsx.mbar_view": "View", diff --git a/frontend/locales/fr.json b/frontend/locales/fr.json index c94aa3a..37adf68 100644 --- a/frontend/locales/fr.json +++ b/frontend/locales/fr.json @@ -2050,6 +2050,7 @@ "xlsx.mbar_label": "Menus du tableur", "xlsx.cmdbar_label": "Barre de commandes du tableur", + "xlsx.tools_toggle": "Afficher / masquer les barres d'outils", "xlsx.mbar_file": "Fichier", "xlsx.mbar_edit": "Modifier", "xlsx.mbar_view": "Affichage", diff --git a/frontend/style.css b/frontend/style.css index 2fe7c8c..155703c 100644 --- a/frontend/style.css +++ b/frontend/style.css @@ -12172,6 +12172,85 @@ body.desktop-mode .editor-container { max-width: 92vw; } } +/* #189 — mise en page mobile : par défaut seuls restent la barre des + feuilles (avec le basculeur) et la barre de formule. La barre de menus et + le ruban sont repliés derrière #xlsx-tools-toggle ; le groupe Recherche, + logé dans la barre de formule, suit le même état pour ne pas écraser le + champ de saisie. */ +.xlsx-tools-toggle { + display: none; +} +@media (max-width: 768px) { + .xlsx-tools-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 44px; + height: 44px; + flex: 0 0 auto; + } + .xlsx-menubar, + .xlsx-cmdbar, + .xlsx-find-group { + display: none; + } + .xlsx-toolbar.xlsx-tools-open .xlsx-menubar { + display: flex; + } + .xlsx-toolbar.xlsx-tools-open .xlsx-cmdbar { + display: flex; + } + .xlsx-toolbar.xlsx-tools-open .xlsx-find-group { + display: flex; + } + /* La barre de formule ne déborde jamais : elle passe en 2 lignes quand le + groupe Recherche réapparaît avec les outils ouverts. */ + .xlsx-toolbar.xlsx-tools-open .xlsx-formula-bar { + flex-wrap: wrap; + } + /* Une seule ligne de feuilles : les onglets défilent horizontalement au + lieu d'enrouler et de grignoter la hauteur de la grille. */ + .xlsx-sheetbar { + flex-wrap: nowrap; + } + .xlsx-sheetbar .xlsx-tabs { + flex-wrap: nowrap; + overflow-x: auto; + scrollbar-width: none; + } + .xlsx-sheetbar .xlsx-tabs::-webkit-scrollbar { + display: none; + } + /* Cibles tactiles : le ruban replié n'existe que derrière le basculeur, + mais une fois ouvert chaque commande reste atteignable au doigt. */ + .xlsx-toolbar .btn-action { + min-width: 44px; + height: 44px; + } + /* À 44px un groupe (police, nombre…) dépasse la largeur d'un téléphone : + il doit pouvoir se réduire ET s'enrouler plutôt que déborder. */ + .xlsx-cmdbar .xlsx-cmd-group { + flex: 0 1 auto; + min-width: 0; + flex-wrap: wrap; + } + .xlsx-menubtn { + padding: 12px 14px; + min-height: 44px; + } + .xlsx-tab, + .xlsx-tab-add { + padding: 10px 14px; + min-height: 44px; + } + /* 16px minimum : en dessous iOS zoome automatiquement la page dès que le + champ reçoit le focus, ce qui fait « sauter » l'interface. */ + .xlsx-active-cell, + .xlsx-formula-input, + .xlsx-find-input { + font-size: 16px; + } +} .xlsx-context-item { text-align: left; border: none; diff --git a/package.json b/package.json index 0634c5a..131a20f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsigate", - "version": "2.55.0", + "version": "2.56.0", "description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.", "main": "patch.js", "directories": { diff --git a/tests/e2e/xlsx-viewer.spec.js b/tests/e2e/xlsx-viewer.spec.js index a1b8aa8..ff9976a 100644 --- a/tests/e2e/xlsx-viewer.spec.js +++ b/tests/e2e/xlsx-viewer.spec.js @@ -72,6 +72,14 @@ async function openXlsx(page, file) { } await treeItem.dblclick({ timeout: 5000 }); await expect(page.locator('#content-area .xlsx-table')).toBeVisible({ timeout: 15000 }); + // #189 — sous 768px la barre de menus et le ruban sont repliés par défaut. + // Les tests « desktop » qui les manipulent les déplient d'abord ; le test + // mobile dédié ouvre lui-même la feuille pour vérifier l'état replié. + const toggle = page.locator('#xlsx-tools-toggle'); + if (await toggle.isVisible().catch(() => false)) { + await toggle.click(); + await expect(page.locator('#content-area .xlsx-menubar')).toBeVisible(); + } } test.describe('Excel viewer — garde-fous d\'écriture et valeurs calculées (#153)', () => { @@ -513,12 +521,27 @@ test.describe('Excel viewer — menus à icônes et fermeture au focus (#179)', await expect(page.locator('#xlsx-minibar')).toBeVisible(); }); - test('mobile : ruban sans débordement et cibles tactiles 44px', async ({ page, viewport }) => { + test('mobile : barres repliées par défaut, ruban sans débordement et cibles 44px (#189)', async ({ page, viewport }) => { test.skip((viewport?.width ?? 9999) > 768, 'Mobile viewport required'); await login(page); - await page.evaluate(() => window.TabManager.open('TestVault', FIXTURE)); + await page.evaluate((f) => window.TabManager.open('TestVault', f), FIXTURE); await expect(page.locator('#content-area .xlsx-table')).toBeVisible({ timeout: 15000 }); + // #189 — par défaut la barre de menus et le ruban sont repliés : la grille + // garde la hauteur d'écran. La barre de formule, elle, reste visible. + await expect(page.locator('#content-area .xlsx-menubar')).toBeHidden(); + await expect(page.locator('#content-area .xlsx-cmdbar')).toBeHidden(); + await expect(page.locator('#content-area .xlsx-formula-bar')).toBeVisible(); + await expect(page.locator('#content-area .xlsx-sheetbar')).toBeVisible(); + const toggle = page.locator('#xlsx-tools-toggle'); + await expect(toggle).toBeVisible(); + + // Un clic déplie menus + ruban (état annoncé par aria-expanded). + await toggle.click(); + await expect(page.locator('#content-area .xlsx-menubar')).toBeVisible(); + await expect(page.locator('#content-area .xlsx-cmdbar')).toBeVisible(); + await expect(toggle).toHaveAttribute('aria-expanded', 'true'); + // Le ruban s'enroule au lieu de déborder horizontalement. const overflow = await page.evaluate(() => { const bar = document.querySelector('#content-area .xlsx-cmdbar'); @@ -526,6 +549,17 @@ test.describe('Excel viewer — menus à icônes et fermeture au focus (#179)', }); expect(overflow).toBeLessThanOrEqual(1); + // Cibles tactiles : le basculeur et chaque commande du ruban font 44px. + const toggleBox = await toggle.boundingBox(); + expect(toggleBox.height).toBeGreaterThanOrEqual(44); + const ribbonBtnBox = await page.locator('#xs-b').boundingBox(); + expect(ribbonBtnBox.height).toBeGreaterThanOrEqual(44); + + // Repli : la grille retrouve toute sa hauteur. + await toggle.click(); + await expect(page.locator('#content-area .xlsx-menubar')).toBeHidden(); + await expect(toggle).toHaveAttribute('aria-expanded', 'false'); + // Menu contextuel (clic droit émulé) : tient dans le viewport, entrées ≥ 44px. await page.locator('#content-area td[data-cell="A1"]').click({ button: 'right' }); const menu = page.locator('.xlsx-context-menu'); diff --git a/tests/frontend/xlsx-viewer.test.mjs b/tests/frontend/xlsx-viewer.test.mjs index 15e29ae..9838ccc 100644 --- a/tests/frontend/xlsx-viewer.test.mjs +++ b/tests/frontend/xlsx-viewer.test.mjs @@ -2775,6 +2775,54 @@ await test("a read-only workbook keeps the view menus but drops the editing ones assert.ok(area.querySelector('[data-xlsx-action="toggle-grid"]'), "Gridlines stay"); }); +// ── #189 — mise en page mobile : barres repliées derrière un basculeur ────── + +await test("the sheet strip carries the tools toggle that flips the toolbar (#189)", () => { + const area = mountGrid(); + const strip = area.querySelector(".xlsx-sheetbar"); + const toggle = area.querySelector("#xlsx-tools-toggle"); + assert.ok(toggle, "toggle rendered"); + assert.equal(toggle.closest(".xlsx-sheetbar"), strip, "it lives in the sheet strip"); + assert.equal(toggle.getAttribute("aria-expanded"), "false", "starts collapsed"); + const toolbar = area.querySelector(".xlsx-toolbar"); + assert.ok(!toolbar.classList.contains("xlsx-tools-open"), "toolbar starts closed"); + toggle.click(); + assert.ok(toolbar.classList.contains("xlsx-tools-open"), "one click opens the tools"); + assert.equal(toggle.getAttribute("aria-expanded"), "true"); + assert.ok(toggle.classList.contains("active"), "the toggle shows its own state"); + toggle.click(); + assert.ok(!toolbar.classList.contains("xlsx-tools-open"), "a second click closes them"); + assert.equal(toggle.getAttribute("aria-expanded"), "false"); +}); + +await test("style.css hides menus/ribbon/find by default under 768px and reveals them on .xlsx-tools-open (#189)", () => { + const css = readFileSync(path.join(REPO_ROOT, "frontend", "style.css"), "utf8"); + // The desktop default: the toggle itself is out of the way. + assert.match(css, /\.xlsx-tools-toggle\s*\{\s*display:\s*none/, "toggle hidden on desktop"); + // Mobile block: collapsed by default… (CRLF-safe slice, from the #189 + // comment up to the next top-level rule). + const from = css.indexOf("/* #189"); + assert.ok(from > 0, "the #189 mobile block exists after the coarse-pointer rules"); + const to = css.indexOf(".xlsx-context-item", from); + assert.ok(to > from, "the #189 block is followed by the next rule"); + const block = css.slice(from, to); + for (const cls of [".xlsx-menubar", ".xlsx-cmdbar", ".xlsx-find-group"]) { + const hidden = new RegExp(cls.replace(".", "\\.") + "[^}]*display:\\s*none").test(block) + || new RegExp(cls.replace(".", "\\.") + ",[\\s\\S]{0,120}?display:\\s*none").test(block); + assert.ok(hidden, `${cls} hidden by default on mobile`); + assert.ok( + block.includes(`.xlsx-toolbar.xlsx-tools-open ${cls}`), + `${cls} revealed by .xlsx-tools-open`, + ); + } + // The formula bar always stays, and the open state can wrap it (find group). + assert.ok(block.includes(".xlsx-toolbar.xlsx-tools-open .xlsx-formula-bar"), "formula bar wraps when open"); + // Touch: 44px targets, tabs on one scrollable row, 16px inputs (no iOS zoom). + assert.match(block, /min-width:\s*44px/, "44px touch targets"); + assert.match(block, /\.xlsx-sheetbar \.xlsx-tabs\s*\{[^}]*overflow-x:\s*auto/, "tabs scroll horizontally"); + assert.match(block, /font-size:\s*16px/, "inputs at 16px avoid the iOS focus zoom"); +}); + // ── Report ────────────────────────────────────────────────────────────────── console.log(`\n${passCount}/${testCount} tests passed\n`); process.exit(passCount === testCount ? 0 : 1);