Listes et contenu
Glisser-déposer
Quand l'utiliser
Pour changer l'ordre d'une liste courte quand cet ordre a un sens pour l'utilisateur : tâches d'une check-list, étapes d'une procédure, rayons affichés sur la tablette, colonnes d'un export. Pour classer selon une valeur (date, prix), un tri de tableau. Pour envoyer un fichier, la zone de dépôt.
États
Au repos, une poignée drag_indicator à gauche. Pendant le déplacement, la ligne tenue se soulève (ombre de niveau 3, légèrement agrandie) et suit le pointeur ; l'emplacement où elle arrivera est dessiné en pointillé, en couleur métier ; les autres lignes s'écartent en 180 ms. Ici, « Nettoyer la vitrine » est en train de descendre en bas de la liste.
Code
<div style="max-width:560px">
<div class="hb-list">
<div class="hb-list-row"><button class="hb-list-handle" aria-label="Déplacer « Relever les températures »"><i class="material-icons">drag_indicator</i></button><input type="checkbox" class="hb-check hb-list-check" aria-label="Faite"><div class="hb-list-body"><span class="hb-list-title">Relever les températures</span></div></div>
<div class="hb-list-row"><button class="hb-list-handle" aria-label="Déplacer « Vérifier les DLC »"><i class="material-icons">drag_indicator</i></button><input type="checkbox" class="hb-check hb-list-check" aria-label="Faite"><div class="hb-list-body"><span class="hb-list-title">Vérifier les DLC</span></div></div>
<div class="hb-list-row hb-list-row--ghost"><span class="hb-list-handle"></span><span></span><div class="hb-list-body"><span class="hb-list-title">Nettoyer la vitrine</span></div></div>
</div>
<div class="hb-list-row hb-drag-lifted" style="position:relative;margin:calc(-.6 * var(--row)) -12px 0 24px"><button class="hb-list-handle" aria-label="Déplacer « Nettoyer la vitrine »"><i class="material-icons">drag_indicator</i></button><input type="checkbox" class="hb-check hb-list-check" aria-label="Faite"><div class="hb-list-body"><span class="hb-list-title">Nettoyer la vitrine</span></div></div>
</div>| Classe | Rôle | Option Sortable.js |
|---|---|---|
hb-handle | poignée seule, à poser dans n'importe quelle ligne (liste simple, ligne de tableau, carte) | handle |
hb-list-handle | la poignée de la liste de tâches, révélée au survol | handle |
hb-drag-ghost (hb-list-row--ghost) | l'emplacement d'arrivée : pointillé et fond doux métier, contenu masqué | ghostClass |
hb-drag-lifted | la copie qui suit le pointeur : ombre de niveau 3, agrandie de 2 % | dragClass |
hb-drop-over | une autre liste ou un onglet survolé, qui recevra la ligne | ajoutée par l'application (@move) |
Vers une autre liste
Quand une ligne peut changer de groupe (une tâche vers une autre check-list, une commande vers un autre jour), la liste ou l'onglet qui la recevra prend hb-drop-over.
Aujourd'hui
Demain
Code
<div class="hb-row" style="align-items:flex-start;gap:var(--space-4)">
<div style="flex:1;min-width:220px"><p class="hb-label">Aujourd'hui</p><div class="hb-list"><div class="hb-list-row"><button class="hb-list-handle" aria-label="Déplacer « Commander le saumon »"><i class="material-icons">drag_indicator</i></button><input type="checkbox" class="hb-check hb-list-check" aria-label="Faite"><div class="hb-list-body"><span class="hb-list-title">Commander le saumon</span></div></div></div></div>
<div style="flex:1;min-width:220px"><p class="hb-label">Demain</p><div class="hb-list hb-drop-over"><div class="hb-list-row"><button class="hb-list-handle" aria-label="Déplacer « Inventaire du rayon »"><i class="material-icons">drag_indicator</i></button><input type="checkbox" class="hb-check hb-list-check" aria-label="Faite"><div class="hb-list-body"><span class="hb-list-title">Inventaire du rayon</span></div></div><div class="hb-list-row hb-list-row--ghost"><span class="hb-list-handle"></span><span></span><div class="hb-list-body"><span class="hb-list-title">Vérifier les DLC</span></div></div></div></div>
</div>Sans glisser
Glisser demande un geste précis : au doigt, avec des gants, au clavier ou avec un lecteur d'écran, il ne suffit pas. Toute liste qui se réordonne propose donc aussi :
- au clavier : la poignée est un
<button>; Flèche haut et Flèche bas déplacent la ligne d'un cran, le focus reste sur la poignée ; - au pointeur, sans glisser : « Monter », « Descendre », « Déplacer en haut », « Déplacer en bas » dans le menu ⋮ de la ligne (et « Déplacer vers… » quand elle peut changer de liste).
Code
<div class="hb-menu" style="width:220px">
<div class="hb-menu-item"><i class="material-icons" style="font-size:18px">arrow_upward</i>Monter</div>
<div class="hb-menu-item"><i class="material-icons" style="font-size:18px">arrow_downward</i>Descendre</div>
<div class="hb-menu-sep"></div>
<div class="hb-menu-item"><i class="material-icons" style="font-size:18px">vertical_align_top</i>Déplacer en haut</div>
<div class="hb-menu-item"><i class="material-icons" style="font-size:18px">vertical_align_bottom</i>Déplacer en bas</div>
</div>Après chaque déplacement, une phrase est lue aux lecteurs d'écran dans une zone role="status" masquée (hb-sr-only) : « Nettoyer la vitrine, position 2 sur 4 ».
Règles
- On saisit par la poignée, jamais par toute la ligne : la ligne garde le clic pour éditer, la case pour cocher, et le doigt pour faire défiler. La poignée est toujours à gauche.
- Au doigt, la poignée est toujours visible et sa zone de prise fait 44 px, sans changer la mise en page. Elle bloque le défilement (
touch-action: none) : on n'a pas besoin d'appui long. - La liste défile seule quand la ligne approche du bord de l'écran ou de la carte.
- Le lâcher enregistre tout de suite, sans bouton « Enregistrer ». Si l'enregistrement échoue, la ligne revient à sa place et un toast d'erreur le dit.
- Une liste triée par une valeur (date, nom) ne se réordonne pas à la main : on n'affiche pas de poignée.
- Avec
prefers-reduced-motion, les lignes changent de place sans glisser (animation: 0).
Dans Vuetify
<draggable v-model="tasks" item-key="_id" handle=".hb-list-handle"
ghost-class="hb-list-row--ghost" drag-class="hb-drag-lifted"
:force-fallback="true" :animation="calm ? 0 : Hublot.motion.state"
@end="saveOrder">
force-fallback remplace l'image fantôme du navigateur (différente sur chaque système, absente sur iOS) par une vraie copie de la ligne, habillée par hb-drag-lifted. calm vaut matchMedia('(prefers-reduced-motion: reduce)').matches.