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>
209 lines
7.5 KiB
HTML
209 lines
7.5 KiB
HTML
<!doctype html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Frise multi-projets</title>
|
||
<link rel="stylesheet" href="css/style.css" />
|
||
<link
|
||
rel="icon"
|
||
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect width='16' height='16' rx='3' fill='%233b82f6'/><rect x='3' y='4' width='9' height='2' rx='1' fill='white'/><rect x='5' y='7' width='8' height='2' rx='1' fill='white'/><rect x='3' y='10' width='6' height='2' rx='1' fill='white'/></svg>"
|
||
/>
|
||
</head>
|
||
<body>
|
||
<header class="outils">
|
||
<h1>Frise multi-projets</h1>
|
||
|
||
<div class="outils__groupe">
|
||
<button type="button" id="aller-aujourdhui">Aujourd'hui</button>
|
||
<button type="button" id="nouveau-projet" class="bouton--principal">
|
||
Nouveau projet
|
||
</button>
|
||
</div>
|
||
|
||
<p class="outils__etat" id="etat-sauvegarde" role="status" aria-live="polite"></p>
|
||
</header>
|
||
|
||
<!--
|
||
Barre de filtres, masquée tant qu'aucun projet ne porte de tag : elle
|
||
n'apparaît que le jour où elle sert à quelque chose.
|
||
-->
|
||
<div class="filtres" id="filtres" hidden role="group" aria-label="Filtrer par tag">
|
||
<span class="filtres__titre">Filtrer</span>
|
||
<div class="tags tags--filtres" id="filtres-liste"></div>
|
||
<button type="button" id="effacer-filtres" hidden>Tout afficher</button>
|
||
</div>
|
||
|
||
<!--
|
||
Un seul conteneur défilant, découpé en grille 2×2 : l'en-tête de dates est
|
||
collé en haut (`sticky top`), la colonne des libellés collée à gauche
|
||
(`sticky left`), et le coin aux deux. Le navigateur gère ainsi le
|
||
défilement des deux axes sans aucune synchronisation en JavaScript.
|
||
-->
|
||
<main class="frise" id="frise">
|
||
<div class="grille">
|
||
<div class="grille__coin">
|
||
<!--
|
||
Poignée de largeur de la colonne. Placée dans le coin, seul endroit
|
||
de la colonne qui reste visible quel que soit le défilement.
|
||
-->
|
||
<div
|
||
class="poignee-colonne"
|
||
id="poignee-colonne"
|
||
role="separator"
|
||
aria-orientation="vertical"
|
||
aria-label="Largeur de la colonne des projets"
|
||
tabindex="0"
|
||
title="Glisser pour élargir la colonne — double-clic pour la réinitialiser"
|
||
></div>
|
||
</div>
|
||
<div class="grille__entete" id="entete"></div>
|
||
<div class="grille__libelles" id="libelles"></div>
|
||
<div class="grille__couloirs" id="couloirs"></div>
|
||
</div>
|
||
</main>
|
||
|
||
<p class="vide" id="message-vide" hidden>
|
||
Aucun projet pour l'instant.<br />
|
||
<button type="button" id="premier-projet" class="bouton--principal">
|
||
Créer un premier projet
|
||
</button>
|
||
</p>
|
||
|
||
<p class="vide" id="message-filtre" hidden>
|
||
Aucun projet ne porte tous les tags sélectionnés.<br />
|
||
<button type="button" id="effacer-filtres-vide">Tout afficher</button>
|
||
</p>
|
||
|
||
<!--
|
||
Panneau de détail, ouvert au clic sur une phase. Volontairement **non
|
||
modal** : la frise reste vivante derrière lui, on passe d'une phase à
|
||
l'autre en cliquant sa barre. `tabindex="-1"` permet d'y poser le focus à
|
||
l'ouverture sans le placer dans un champ de saisie, ce qui laisse les
|
||
flèches piloter la phase sélectionnée.
|
||
-->
|
||
<aside class="panneau" id="panneau" hidden tabindex="-1" aria-label="Détail de la phase">
|
||
<div class="panneau__entete">
|
||
<h2 id="panneau-titre">Phase</h2>
|
||
<button type="button" id="fermer-panneau" aria-label="Fermer">×</button>
|
||
</div>
|
||
<form class="panneau__corps" id="formulaire-phase">
|
||
<label>
|
||
Nom
|
||
<input type="text" name="name" required autocomplete="off" />
|
||
</label>
|
||
|
||
<div class="champ-double">
|
||
<label>
|
||
Début
|
||
<input type="date" name="start" required />
|
||
</label>
|
||
<label id="champ-fin">
|
||
Fin
|
||
<input type="date" name="end" required />
|
||
</label>
|
||
</div>
|
||
|
||
<label>
|
||
Statut
|
||
<select name="status">
|
||
<option value="todo">À venir</option>
|
||
<option value="doing">En cours</option>
|
||
<option value="done">Terminé</option>
|
||
<option value="blocked">Bloqué</option>
|
||
</select>
|
||
</label>
|
||
|
||
<label class="case">
|
||
<input type="checkbox" name="milestone" />
|
||
Jalon (une seule date, affichée en losange)
|
||
</label>
|
||
|
||
<label>
|
||
Notes
|
||
<textarea name="notes" rows="4"></textarea>
|
||
</label>
|
||
|
||
<p class="panneau__erreur" id="erreur-phase" role="alert" hidden></p>
|
||
|
||
<div class="panneau__actions">
|
||
<button type="button" id="supprimer-phase" class="bouton--danger">Supprimer</button>
|
||
</div>
|
||
</form>
|
||
</aside>
|
||
|
||
<!-- Menu contextuel, positionné à l'ouverture près de son point d'ancrage. -->
|
||
<div class="menu" id="menu" hidden role="menu"></div>
|
||
|
||
<!--
|
||
Paramètres d'un projet. Même panneau, mêmes gestes et même édition en
|
||
direct que le détail d'une phase : renommer un projet et renommer une
|
||
phase sont la même opération à un niveau près, elles n'ont pas à se
|
||
présenter différemment.
|
||
-->
|
||
<aside
|
||
class="panneau"
|
||
id="panneau-projet"
|
||
hidden
|
||
tabindex="-1"
|
||
aria-label="Paramètres du projet"
|
||
>
|
||
<div class="panneau__entete">
|
||
<h2 id="panneau-projet-titre">Projet</h2>
|
||
<button type="button" id="fermer-panneau-projet" aria-label="Fermer">×</button>
|
||
</div>
|
||
<form class="panneau__corps" id="formulaire-projet">
|
||
<label>
|
||
Nom
|
||
<input type="text" name="name" required autocomplete="off" />
|
||
</label>
|
||
|
||
<fieldset class="couleurs">
|
||
<legend>Couleur du couloir</legend>
|
||
<div id="panneau-projet-couleurs"></div>
|
||
</fieldset>
|
||
|
||
<label>
|
||
Tags, séparés par des virgules
|
||
<input type="text" name="tags" autocomplete="off" placeholder="client, refonte" />
|
||
</label>
|
||
|
||
<!-- Masqué tant qu'aucun tag n'existe ailleurs dans le planning. -->
|
||
<fieldset class="couleurs" id="panneau-projet-suggestions-bloc" hidden>
|
||
<legend>Tags déjà utilisés</legend>
|
||
<div class="tags tags--suggestions" id="panneau-projet-suggestions"></div>
|
||
</fieldset>
|
||
|
||
<p class="panneau__erreur" id="erreur-projet" role="alert" hidden></p>
|
||
|
||
<div class="panneau__actions">
|
||
<button type="button" id="supprimer-projet" class="bouton--danger">
|
||
Supprimer le projet
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</aside>
|
||
|
||
<!-- Confirmation d'une action irréversible. -->
|
||
<dialog class="dialogue" id="dialogue-confirmation">
|
||
<form method="dialog">
|
||
<h2 id="dialogue-confirmation-titre">Confirmer</h2>
|
||
<p id="dialogue-confirmation-message"></p>
|
||
<menu class="dialogue__actions">
|
||
<button type="submit" value="annuler">Annuler</button>
|
||
<button type="submit" value="ok" class="bouton--principal" id="dialogue-confirmation-ok">
|
||
Confirmer
|
||
</button>
|
||
</menu>
|
||
</form>
|
||
</dialog>
|
||
|
||
<!-- Bandeau d'erreur bloquante : fichier corrompu, serveur injoignable. -->
|
||
<div class="alerte" id="alerte" hidden role="alert">
|
||
<p id="alerte-message"></p>
|
||
</div>
|
||
|
||
<script type="module" src="js/app.js"></script>
|
||
</body>
|
||
</html>
|