fix(batch): move the selection bar into the title row

Selecting an entry inserted the batch bar as a block above the grid, pushing
every row down ~60px — the list jumped under the cursor mid-click. The bar
now renders inside .content-header, to the right of "All items", and the
header reserves its height (min-height) so appearing and disappearing shifts
nothing at all.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-23 15:59:53 +01:00
parent 9ea26be422
commit 6f4363bde6
3 changed files with 16 additions and 14 deletions
+6 -6
View File
@@ -25,27 +25,27 @@ Convention : **PRÉ** = état de départ, **ACTION**, **ATTENDU**. Un test qui
Préparer : 3 entrées `A`, `B`, `C`, dont `A` dans le dossier `Work` avec le tag Préparer : 3 entrées `A`, `B`, `C`, dont `A` dans le dossier `Work` avec le tag
`old`. `old`.
### A1 · batch Add tag sur l'entrée éditée ### A1 · batch Add tag sur l'entrée éditée
- **PRÉ** : `A` ouverte dans l'éditeur, taper du texte dans NOTE/PASSWORD (ne pas sauver) - **PRÉ** : `A` ouverte dans l'éditeur, taper du texte dans NOTE/PASSWORD (ne pas sauver)
- **ACTION** : cocher `A` + `B` → barre de sélection → *Add tag* → `shared` → puis **Save** dans l'éditeur - **ACTION** : cocher `A` + `B` → barre de sélection → *Add tag* → `shared` → puis **Save** dans l'éditeur
- **ATTENDU** : le chip `shared` apparaît **immédiatement** dans l'éditeur ; après Save, `A` porte `old, shared` **et** la modif tapée. Le bouton Save reste visible (l'entrée était déjà dirty) mais le tag seul ne rend pas dirty. - **ATTENDU** : le chip `shared` apparaît **immédiatement** dans l'éditeur ; après Save, `A` porte `old, shared` **et** la modif tapée. Le bouton Save reste visible (l'entrée était déjà dirty) mais le tag seul ne rend pas dirty.✅
### A2 · batch Add tag sur une **autre** entrée ### A2 · batch Add tag sur une **autre** entrée
- **PRÉ** : `A` ouverte + dirty - **PRÉ** : `A` ouverte + dirty
- **ACTION** : cocher `B` + `C` seulement → *Add tag* → `x` → Save dans l'éditeur - **ACTION** : cocher `B` + `C` seulement → *Add tag* → `x` → Save dans l'éditeur
- **ATTENDU** : l'éditeur ne bouge pas (aucun chip ajouté), `A` conserve ses tags, `B`/`C` taguées. - **ATTENDU** : l'éditeur ne bouge pas (aucun chip ajouté), `A` conserve ses tags, `B`/`C` taguées.✅
### A3 · batch Move to folder sur l'entrée éditée ### A3 · batch Move to folder sur l'entrée éditée
- **ACTION** : `A` ouverte + dirty → cocher `A` → *Move to folder…* → `Perso` → Save - **ACTION** : `A` ouverte + dirty → cocher `A` → *Move to folder…* → `Perso` → Save
- **ATTENDU** : le select Folder de l'éditeur bascule sur `Perso` tout seul ; après Save l'entrée est bien dans `Perso`. - **ATTENDU** : le select Folder de l'éditeur bascule sur `Perso` tout seul ; après Save l'entrée est bien dans `Perso`.✅
### A4 · dossier choisi dans l'éditeur **puis** move extérieur ### A4 · dossier choisi dans l'éditeur **puis** move extérieur
- **ACTION** : `A` ouverte, changer Folder → `Archive` (ne pas sauver) → cocher `A` → *Move to folder…* → `Perso` → Save - **ACTION** : `A` ouverte, changer Folder → `Archive` (ne pas sauver) → cocher `A` → *Move to folder…* → `Perso` → Save
- **ATTENDU** : `Perso` gagne (action extérieure = intention la plus récente). Comportement documenté, pas un bug. - **ATTENDU** : `Perso` gagne (action extérieure = intention la plus récente). Comportement documenté, pas un bug. ✅
### A5 · glisser une entrée sur un dossier (`moveEntryToFolder`) ### A5 · glisser une entrée sur un dossier (`moveEntryToFolder`)
- **ACTION** : `A` ouverte + dirty → glisser la ligne `A` sur `Perso` dans la sidebar → Save - **ACTION** : `A` ouverte + dirty → glisser la ligne `A` sur `Perso` dans la sidebar → Save
- **ATTENDU** : identique à A3. - **ATTENDU** : identique à A3.✅
### A6 · glisser une **multi-sélection** sur un dossier (`moveEntriesToFolder`) ⚠️ ### A6 · glisser une **multi-sélection** sur un dossier (`moveEntriesToFolder`) ⚠️
- **ACTION** : cocher `A` + `B` → glisser sur `Perso` → recharger (F5 impossible : relancer l'app) - **ACTION** : cocher `A` + `B` → glisser sur `Perso` → recharger (F5 impossible : relancer l'app)
+9 -6
View File
@@ -807,6 +807,8 @@ input[type="range"]::-webkit-slider-thumb {
table header) offsets by --hdr-h. */ table header) offsets by --hdr-h. */
.content-header { .content-header {
display: flex; align-items: baseline; gap: 12px; margin-bottom: 20px; display: flex; align-items: baseline; gap: 12px; margin-bottom: 20px;
/* Height of the batch bar — reserved even when nothing is selected. */
min-height: 38px;
position: sticky; top: 0; z-index: 12; position: sticky; top: 0; z-index: 12;
background: var(--bg); background: var(--bg);
padding-top: 24px; padding-top: 24px;
@@ -1008,14 +1010,15 @@ input[type="range"]::-webkit-slider-thumb {
font-size: 11px; font-size: 11px;
} }
/* Batch action bar (appears when selection is non-empty) */ /* Batch action bar (appears when selection is non-empty). It lives INSIDE
.content-header, to the right of the title: as a block of its own it pushed
the whole list down on every selection. The header reserves its height (see
.content-header min-height) so appearing/disappearing shifts nothing. */
.batch-bar { .batch-bar {
position: sticky; flex: 1;
top: var(--hdr-h, 0px); align-self: center;
z-index: 10;
display: flex; align-items: center; gap: 8px; display: flex; align-items: center; gap: 8px;
padding: 10px 14px; padding: 5px 10px;
margin-bottom: 16px;
background: var(--bg-elev); background: var(--bg-elev);
border: 1px solid var(--accent); border: 1px solid var(--accent);
border-radius: var(--radius); border-radius: var(--radius);
+1 -2
View File
@@ -3708,8 +3708,7 @@ function renderBatchBar() {
on: { click: clearChecked }, on: { click: clearChecked },
}, withIcon('i-x', 'Clear'))); }, withIcon('i-x', 'Clear')));
const content = $('.content'); $('.content-header').appendChild(bar);
content.insertBefore(bar, $('#entryGrid'));
} }
// ============================================================ // ============================================================