Revoir l'ergonomie des panneaux et de la colonne des projets
Les panneaux d'édition étaient modaux et la colonne de gauche tronquait tout ce qu'elle portait. Sept corrections, mesurées sur data/exemple.json. Panneaux - Le voile qui recouvrait la page disparaît : la frise reste manipulable pendant l'édition, et cliquer une autre barre y bascule la sélection. Il rendait aussi le raccourci flèches inatteignable, le focus partant dans le champ « Nom » sans qu'on puisse cliquer ailleurs pour en sortir. - Un clic dans une zone inerte referme le panneau : ce que rendait le voile, sans le calque qui figeait la frise. - Les paramètres d'un projet quittent le <dialog> modal pour le même panneau qu'une phase, avec la même édition au fil de la saisie. « Nouveau projet » crée donc le projet aussitôt, comme le fait déjà l'ajout d'une phase : plus aucune création ne passe par un formulaire. Colonne des projets - Le nom ne disposait que de 56 px sur 250, et « client » s'affichait « c. ». Les commandes passent en surimpression au survol, les tags cèdent en bloc derrière un compteur, et le nom cède en dernier. - La colonne se redimensionne, largeur conservée dans localStorage. - L'œil disparaît : il faisait doublon avec l'entrée « Masquer » du menu. Un « + » le remplace, l'ajout d'une phase étant l'action la plus fréquente, et le nom du projet devient cliquable — il portait déjà un curseur « main » sans aucune action derrière. - Plus de commande « Ajouter un jalon » : un jalon est une phase dont on coche la case, pas un autre objet. Frise - Le nom d'une phase reste accroché au bord visible quand sa barre s'enfonce sous la colonne. - Cliquer un nom de phase hors écran ramène la frise sur elle. - La barre cumulative d'un projet s'affiche aussi quand il est déplié, amincie pour ne pas se faire passer pour une barre de phase. Arbitrages consignés dans docs/decisions.md, sections 8 et 17 à 21. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -92,15 +92,20 @@ Gérer les jours ouvrés impose de faire passer chaque calcul de date par un hel
|
||||
aussitôt la question des congés et des jours fériés — donc un calendrier à tenir à jour. Sur des
|
||||
phases qui se comptent en semaines et en mois, l'écart est dans le bruit de l'estimation.
|
||||
|
||||
## 8. Le mode plié est en lecture seule
|
||||
## 8. La barre cumulative est en lecture seule
|
||||
|
||||
Un couloir plié affiche une barre unique segmentée par phase. On aurait pu rendre cette barre
|
||||
glissable pour décaler tout le projet en bloc.
|
||||
La ligne d'un projet porte une barre unique segmentée par phase, qui donne sa forme d'ensemble. On
|
||||
aurait pu la rendre glissable pour décaler tout le projet en bloc.
|
||||
|
||||
Écarté pour une raison de lisibilité du geste : la même barre représenterait tantôt une phase, tantôt
|
||||
un projet entier, et un glissement de quelques pixels déplacerait alors plusieurs mois de travail
|
||||
d'un coup, sans que l'utilisateur voie ce qui bouge. Le mode plié sert à regarder ; pour modifier, on
|
||||
déplie.
|
||||
d'un coup, sans que l'utilisateur voie ce qui bouge. Elle sert à regarder ; pour modifier, on agit
|
||||
sur les phases.
|
||||
|
||||
**Elle est affichée que le projet soit plié ou déplié.** Elle ne l'était d'abord qu'en mode plié, ce
|
||||
qui obligeait à replier pour savoir où en était le projet entier — donc à perdre la vue qu'on était
|
||||
en train d'éditer. Dépliée, elle voisine avec les barres de phases : elle s'amincit alors pour qu'on
|
||||
ne la prenne pas pour l'une d'elles, et le fantôme de référence s'amincit avec elle.
|
||||
|
||||
## 9. Accroche à la semaine
|
||||
|
||||
@@ -239,15 +244,17 @@ liste des tags actifs pour savoir dans quel sens la prochaine case va jouer.
|
||||
|
||||
**Le filtre n'est pas enregistré.** C'est un état de vue, comme la fenêtre temporelle
|
||||
(décision 15) : un filtre qui survivrait au rechargement rouvrirait le planning amputé sans
|
||||
qu'on se souvienne pourquoi. Le masquage par l'œil (`hidden`), lui, est bien enregistré —
|
||||
qu'on se souvienne pourquoi. Le masquage d'un projet (`hidden`), lui, est bien enregistré —
|
||||
et les deux ne font pas la même chose :
|
||||
|
||||
| | Filtre par tags | Œil (`hidden`) |
|
||||
| | Filtre par tags | Masquage (`hidden`) |
|
||||
|---|---|---|
|
||||
| Portée | La vue entière, libellé compris | Les barres seules, le libellé reste grisé |
|
||||
| Durée de vie | La session | Enregistré dans le fichier |
|
||||
| Question posée | « Je ne regarde que les projets clients » | « Ce projet existe, mais il encombre ma frise » |
|
||||
|
||||
Le masquage n'a plus de bouton dédié dans la colonne : voir décision 20.
|
||||
|
||||
**La couleur d'un tag est calculée, pas choisie** : une teinte HSL dérivée du nom par
|
||||
hachage. Rien à stocker, rien à saisir, et le même tag garde sa couleur partout. Des
|
||||
collisions de teinte sont possibles et sans gravité — le nom est toujours écrit à côté.
|
||||
@@ -261,3 +268,168 @@ appliquée en normalisant, ce qui la rend vraie par construction — aussi bien
|
||||
Créer un projet alors qu'un filtre est actif **préremplit ses tags** avec ceux du filtre :
|
||||
sans cela, le nouveau venu naîtrait invisible, hors de la vue depuis laquelle on vient de le
|
||||
demander.
|
||||
|
||||
## 17. Le panneau de détail n'est pas modal
|
||||
|
||||
Le panneau s'ouvrait derrière un voile couvrant toute la page. L'intention était
|
||||
raisonnable — concentrer l'attention sur la phase en cours d'édition —, mais elle
|
||||
allait contre ce que fait un outil de frise : on y compare, on passe d'une phase à
|
||||
la suivante, on ajuste en regardant le voisinage.
|
||||
|
||||
Trois conséquences, dont deux invisibles à la lecture du code :
|
||||
|
||||
- passer d'une phase à l'autre imposait fermer, cliquer, rouvrir, alors que c'est
|
||||
le geste le plus fréquent de l'outil ;
|
||||
- le raccourci `←` `→` était **inatteignable**. Le panneau plaçait le focus dans
|
||||
le champ « Nom », et `app.js` ignore les flèches quand la frappe part dans un
|
||||
`input` — pour en sortir il aurait fallu cliquer ailleurs, ce que le voile
|
||||
interdisait précisément ;
|
||||
- le rafraîchissement du panneau depuis la frise, prévu pour qu'un glisser se
|
||||
reflète dans les champs, ne pouvait jamais se produire à la souris.
|
||||
|
||||
Le voile a donc disparu. Le panneau reste posé sur le bord droit, la frise
|
||||
continue de vivre derrière lui, et cliquer une autre barre y bascule la
|
||||
sélection. Le focus se pose sur le panneau lui-même — assez pour que le clavier
|
||||
le suive, pas assez pour capturer les flèches. Le champ « Nom » n'est
|
||||
présélectionné que pour une phase fraîchement créée, dont le nom provisoire est
|
||||
justement là pour être écrasé.
|
||||
|
||||
**Cliquer à côté ferme quand même.** C'était le seul service que rendait le
|
||||
voile, et il ne valait pas la frise inerte qu'il coûtait. Un écouteur sur
|
||||
`document` le rend sans calque : il ferme les panneaux au `pointerdown`, sauf
|
||||
sur deux familles de cibles.
|
||||
|
||||
- Les **surfaces protégées** — un panneau, le menu, un dialogue, la poignée de
|
||||
colonne — où l'on est manifestement au travail.
|
||||
- Les **cibles ouvrantes** : une barre, une poignée de barre, un libellé de
|
||||
phase, tout élément portant `data-action`. Leur propre gestionnaire ouvre déjà
|
||||
le bon panneau, et les faire passer par la fermeture générale serait pire
|
||||
qu'inutile : celle-ci redessine la frise, ce qui détacherait du DOM l'élément
|
||||
visé avant que son gestionnaire — ou le glisser qui commence peut-être — n'ait
|
||||
fini. C'est aussi pourquoi `fermer()` sort tout de suite quand le panneau est
|
||||
déjà fermé : sans cette garde, chaque clic dans le vide relancerait un rendu
|
||||
complet.
|
||||
|
||||
Échap et la croix restent les deux autres façons de fermer.
|
||||
|
||||
## 18. La colonne des libellés est redimensionnable
|
||||
|
||||
La colonne portait, sur 250 pixels fixes, le triangle de pli, le nom, les tags,
|
||||
l'étiquette de dérive, l'œil et le menu. Sur le jeu d'exemple, il ne restait que
|
||||
**56 pixels au nom** : « Refonte du site web » s'affichait « Refon… », pendant que
|
||||
« client » se réduisait à « c. ». Le pire des deux mondes — des pastilles qui
|
||||
occupaient la place d'un tag sans en livrer le nom.
|
||||
|
||||
Trois corrections, dans l'ordre de leur rendement :
|
||||
|
||||
- **Les commandes passent en surimpression**, révélées au survol de la ligne.
|
||||
Ce sont des actions occasionnelles ; elles coûtaient soixante pixels en
|
||||
permanence à une information lue en continu. Elles sont posées en `absolute`,
|
||||
et non retirées du flux au repos, pour que rien ne se déplace à l'approche du
|
||||
curseur. Elles restent visibles sans survol sur un projet masqué, seul chemin
|
||||
pour le rétablir.
|
||||
- **Les tags cèdent en bloc plutôt que de rétrécir.** Deux pastilles au plus,
|
||||
puis un compteur `+N` dont l'infobulle donne les manquants. Un tag présent dans
|
||||
le filtre courant passe devant : c'est lui qui explique la présence du projet à
|
||||
l'écran, il ne doit pas tomber dans le compteur.
|
||||
- **Le nom cède en dernier**, et pas en deçà de sept caractères.
|
||||
|
||||
Restait qu'aucune largeur ne convient à la fois à « Site web » et à « Refonte du
|
||||
portail fournisseurs — lot 2 ». D'où une poignée dans le coin de la grille —
|
||||
seul point de la colonne qui reste visible quel que soit le défilement —, avec
|
||||
double-clic pour revenir au défaut et flèches au clavier.
|
||||
|
||||
**Cette largeur est conservée, dans `localStorage`.** C'est la première entorse à
|
||||
la règle des décisions 15 et 16, où les états de vue meurent avec la session, et
|
||||
elle est délibérée : la fenêtre temporelle et le filtre décrivent *ce qu'on
|
||||
regarde*, une largeur de colonne décrit *l'écran sur lequel on regarde*. La
|
||||
placer dans le planning imposerait à un portable la largeur choisie sur un
|
||||
27 pouces ; ne pas la garder du tout obligerait à réajuster la poignée à chaque
|
||||
ouverture, ce qui la rendrait inutile. `localStorage` est exactement le bon
|
||||
rangement : local à la machine, hors des données.
|
||||
|
||||
## 19. Le nom d'une phase reste accroché au bord visible
|
||||
|
||||
Le libellé d'une barre était écrit à son bord gauche. Une phase de plusieurs mois
|
||||
dont le début sortait de l'écran occupait donc toute la largeur de la vue sans
|
||||
qu'on puisse lire son nom, parti sous la colonne des libellés.
|
||||
|
||||
Le nom vit désormais dans son propre élément, en `position: sticky`, calé sur la
|
||||
largeur de la colonne — c'est-à-dire sur le premier pixel réellement visible de
|
||||
la frise. Trois détails le rendent possible, et aucun n'est cosmétique :
|
||||
|
||||
- un `overflow: hidden` sur la barre en aurait fait le conteneur défilant de
|
||||
référence, et le décalage n'aurait plus eu lieu. Le rognage du texte trop long
|
||||
est donc porté par le nom lui-même, pas par la barre ;
|
||||
- le décalage est plafonné par la boîte du parent, ce qui garantit que le nom ne
|
||||
sort jamais de sa barre — d'où `inline-block`, qui laisse au texte la marge de
|
||||
manœuvre qu'un bloc pleine largeur n'aurait pas ;
|
||||
- quand le nom occupe presque toute sa barre, la marge s'épuise et il finit par
|
||||
s'enfoncer sous la colonne. On lit alors sa fin plutôt que rien, ce qui reste
|
||||
un progrès, et la colonne des libellés donne de toute façon le nom entier.
|
||||
|
||||
## 20. Ce que porte la ligne d'un projet
|
||||
|
||||
Trois commandes s'y étaient accumulées sans qu'on se demande si chacune méritait
|
||||
sa place. Le tri s'est fait sur une seule question : cette action est-elle assez
|
||||
fréquente pour valoir un bouton permanent ?
|
||||
|
||||
**L'œil disparaît.** Il faisait doublon avec l'entrée « Masquer de la frise » du
|
||||
menu, qui reste. Le masquage est une décision durable sur un projet — on le pose
|
||||
une fois et on l'oublie — pas un geste qu'on refait dix fois par séance. Depuis
|
||||
que les tags existent (décision 16), c'est d'ailleurs le filtre qui répond au
|
||||
besoin courant de dégager la vue. La fonction demeure, elle ne coûte simplement
|
||||
plus de pixels à tout le monde en permanence. Le format de données est inchangé :
|
||||
`hidden` reste un champ du projet.
|
||||
|
||||
**Le `+` apparaît.** Ajouter une phase est l'action la plus fréquente de l'outil,
|
||||
et elle était enterrée dans un menu.
|
||||
|
||||
**Le nom du projet devient cliquable.** Il portait déjà `cursor: pointer` sans
|
||||
qu'aucune action n'y réponde — un curseur qui promet et ne tient pas. Il ouvre
|
||||
maintenant le dialogue des paramètres, ce que la main annonçait.
|
||||
|
||||
**Il n'y a plus de commande « Ajouter un jalon ».** Un jalon n'est pas un autre
|
||||
objet : c'est une phase dont la fin rejoint le début, soit une case à cocher du
|
||||
panneau au même titre que le statut. Deux entrées de menu obligeaient à trancher
|
||||
avant d'avoir rien saisi, alors que la bascule reste possible ensuite dans les
|
||||
deux sens. Une seule commande crée une phase ; on coche « Jalon » si c'en est un.
|
||||
|
||||
## 21. Un projet s'édite comme une phase
|
||||
|
||||
Les paramètres d'un projet — nom, couleur, tags — passaient par un `<dialog>`
|
||||
modal à valider, quand ceux d'une phase s'éditaient dans un panneau latéral au
|
||||
fil de la saisie. Deux formes pour la même opération à un niveau de granularité
|
||||
près, avec deux modèles mentaux à tenir : ici on tape et ça s'applique, là on
|
||||
tape et il faut confirmer.
|
||||
|
||||
Les projets ont rejoint le panneau (`js/projet.js`, jumeau de `js/detail.js`).
|
||||
Même en-tête, même édition en direct, même bouton de suppression au bas du
|
||||
formulaire. Les deux panneaux occupent le même bord de l'écran : ouvrir l'un
|
||||
ferme l'autre.
|
||||
|
||||
**« Nouveau projet » ne demande donc plus rien.** Le projet est créé aussitôt
|
||||
avec un nom provisoire, et le panneau s'ouvre avec ce nom présélectionné — on
|
||||
tape par-dessus. C'est exactement le parti pris déjà retenu pour l'ajout d'une
|
||||
phase (décision 13), qui cesse ainsi d'être une exception : plus aucune création
|
||||
ne passe par un formulaire à valider.
|
||||
|
||||
Conséquences en cascade, toutes bienvenues :
|
||||
|
||||
- la couleur s'applique au clic sur sa pastille, donc la frise change sous les
|
||||
yeux — le seul moyen de juger si la teinte se distingue de celle des couloirs
|
||||
voisins, ce qu'un aperçu dans un dialogue ne montrait pas ;
|
||||
- il n'y a plus de bouton « Annuler », donc plus le cas où fermer la fenêtre
|
||||
perdait la saisie ;
|
||||
- `creerDialogueProjet` disparaît de `menu.js`, qui ne garde que le menu
|
||||
contextuel et la confirmation.
|
||||
|
||||
Le `<dialog>` natif reste pour cette dernière : sur une action irréversible,
|
||||
interrompre franchement *est* le but, et le voile, le piège à focus et la
|
||||
fermeture par Échap y sont gratuits.
|
||||
|
||||
Une contrepartie assumée : un projet créé par mégarde existe dans le fichier
|
||||
même si l'on ferme le panneau sans rien saisir. Il porte alors le nom
|
||||
« Nouveau projet », se voit sur la frise, et se supprime d'un clic depuis son
|
||||
propre panneau — ce qui reste moins coûteux que d'imposer un dialogue à tout le
|
||||
monde à chaque création.
|
||||
|
||||
Reference in New Issue
Block a user