Afficher une liste de prospects (contacts froids)Puisque nous avons déjà commencé, je peux enfin parler d'un projet secret sur lequel je travaille depuis deux ans. L'une des fonctionnalités intéressantes de
Teamwork CRM est une vue de liste.
Il s'agit d'un composant puissant qui se produit sept fois dans l'application. En fait, un tableau sur les stéroïdes. Je pourrais en dire beaucoup, mais je ne veux pas vous ennuyer. Je vais me concentrer sur la façon dont nous avons mis en œuvre cette flexibilité avec seulement quelques lignes de CSS (Grid). À savoir, comment nous mettons en place des tableaux de données lourds, comment nous prenons en charge le redimensionnement des colonnes, et bien plus encore.
Tout d'abord, le contexte doit être expliqué, en commençant par le but et les tâches de conception de ces tableaux. Si cela ne vous intéresse pas, n'hésitez pas à procéder immédiatement à la
mise en œuvre technique .
Tout d'abord, notre solution permet aux clients de visualiser rapidement des listes, par exemple, des pistes ou des contacts, et de noter des détails importants. Cela ne ressemble pas à une feuille de calcul Excel - nous ferions mieux de traiter la disposition des données s'il y en a beaucoup.
Absolument tout fonctionne dans une conception flexible adaptative. Nous commençons par l'option la plus étroite et personnalisons la mise en page en fonction du contenu, de la conception et des cas d'utilisation (nous n'avons pas de points d'arrêt orientés appareil).
À la largeur d'écran minimale, les colonnes sont empilées verticalement, occupant toute la largeur de l'écran.
A quoi ressemble la liste sur un écran étroitLes tables flexibles ne sont pas faciles à faire. Vous pouvez choisir parmi
plusieurs modèles existants . Pensez aux informations que les utilisateurs recherchent et choisissez judicieusement.
Une fois que nous avons suffisamment de pixels sur l'écran, nous passons à une disposition plus typique, de sorte que les colonnes ... enfin, deviennent des colonnes. Ensuite, il n'y a pas de changements majeurs dans la mise en page, mais nous voulons toujours afficher les colonnes aussi pratiques que possible pour le client.
Supposons que nous ayons de nombreuses colonnes (nous verrons plus tard comment l'utilisateur peut les personnaliser). Tout d'abord, le tableau doit au moins remplir la largeur de l'écran. Deuxièmement, la largeur des colonnes doit être déterminée par leur contenu et le type de valeurs. Par exemple, texte court / long, date, numéro, URL, etc. Les colonnes avec une date doivent occuper moins d'espace que les colonnes de texte long.
Les colonnes doivent avoir une largeur minimale. Le résultat est souvent un tableau qui défile à la fois verticalement et horizontalement.
La façon dont la disposition dépend de la largeur de la fenêtre. Désolé pour le GIF de contraction, je vous donnerai quelques exemples interactifs plus tard.Pour commencer, nous utilisons au maximum le CSS régulier de l'ancienne école pour la table. Ensuite, nous l'améliorons avec CSS Grid. Ensuite, je montrerai comment les utilisateurs peuvent redimensionner les colonnes à l'aide des outils de grille, ce qui était beaucoup plus gênant avec le CSS standard.
Bien montrer déjà la grille CSS
Je ne suis pas un expert CSS Grid, mais j'aime ça. Il s'agit d'un outil extrêmement puissant et simple qui implémente des dispositions complexes avec un minimum de code. Ici, je vais sauter l'introduction à la technologie. Vous pouvez lire
The New CSS Layouts de Rachel Andrew ou
The Complete Grid Guide . Lorsque vous aurez fini de penser à l'endroit où cet outil a été toute votre vie, revenez à moi.
Tout d'abord, appliquez
display:grid
à
<table>
pour transformer la table en grille. Cela ne cassera rien: si le navigateur ne prend pas en charge Grid, il utilisera
display:table
. Les éléments enfants
<thead>
et
<tbody>
deviennent des éléments maillés. Nous n'avons pas besoin de penser à
<thead>
,
<tbody>
ou même
<tr>
. Nous voulons disposer nos
<th>
et
<td>
sur cette grille en appliquant
display:grid
à chacun d'eux (c'est-à-dire, des grilles à l'intérieur des grilles), mais ce n'est pas idéal. Chaque grille
<tr>
sera indépendante des autres, et ce n'est pas bon (vous verrez plus tard que le même problème avec Flexbox).
La solution consiste à utiliser
display:contents
sur
<thead>
,
<tbody>
et
<tr>
. Cela les supprime essentiellement de la disposition de la grille, poussant vers l'avant les éléments enfants (
<th>
et
<td>
).
Ensuite, nous utilisons la règle magique
grid-template-columns
pour contrôler les éléments de la grille. Oui, une seule ligne de CSS. Par exemple, si nous avons une colonne de date et une colonne d'URL, cela ressemblera à ceci:
grid-template-columns: minmax(150px, 1.33fr) minmax(150px, 2.33fr);
Nous utilisons la même taille minimale pour toutes les colonnes, mais la valeur maximale (
fr
) est déterminée par le type de données de la colonne. J'ai essayé
auto
et
max-content
, mais nous avons trouvé une meilleure option. Voici un exemple simplifié:
une table interactive avec du code . Essayez de redimensionner la fenêtre.
Modification de la largeur des colonnes avec Grid
De plus, dans nos tableaux, vous pouvez échanger, modifier la largeur et masquer les colonnes. Ce dernier est important car de nombreuses colonnes avec différents types de données sont prises en charge: ce sont les propriétés de l'élément lui-même (par exemple, les prospects), les propriétés des éléments associés (par exemple, une société associée à un prospect) et des champs personnalisés.
Par exemple, un utilisateur peut créer un champ personnalisé (date) pour les contacts appelé "Date de naissance", qui sera suivi dans le système pour chaque contact.
Puisque lors de la création d'un champ personnalisé, le type "Date" est sélectionné, le système traitera ce champ en tenant compte de ce type. Tout d'abord, je vais expliquer comment la largeur change.
- Lorsque l'utilisateur déplace le curseur sur l'en-tête de colonne, un marqueur de redimensionnement s'affiche à droite. Nous écoutons l'événement
mousedown
sur le curseur de redimensionnement.
- Lorsque l'utilisateur clique sur le curseur, nous
mousemove
quelques méthodes supplémentaires pour écouter les événements mousemove
et mousedown
(dans la window
). A ce stade, nous ajoutons également quelques cours de décoration.
- Lorsque l'utilisateur déplace la souris, nous calculons la nouvelle largeur de colonne en tenant compte de la position du curseur, de la position de défilement de la table et du minimum défini. Ensuite, nous avons réinitialisé la règle de
grid-template-columns
pour <table>
(via l'attribut style
), cette fois en remplaçant la valeur maximale ( fr
) par une pixel. Par exemple, grid-template-columns: minmax(150px, 1.33fr) 296px;
. Cela se fait en utilisant requestAnimationFrame
pour fournir une animation aussi fluide que possible.
- Lorsque
mouseup
arrive, nous mouseup
les écouteurs d'événements et supprimons les classes.
Essayez
cet exemple simplifié .
Remarquablement, il suffit de mettre à jour un seul élément dans le DOM, et pas chaque cellule.
Nous développons toujours une interface utilisateur basée sur la saisie tactile, mais dans ce cas, il est tout à fait normal de ne pas la prendre en charge. C'est une action trop précise. Même si je voulais redimensionner la colonne sur l'écran tactile, je m'attendrais probablement à une autre interaction, par exemple, à travers un geste multi-touch.
Colonnes à largeur fixe
Vous avez peut-être remarqué que vous étiez silencieux sur quelque chose. En fait, la largeur non seulement d'une colonne, mais de toutes les colonnes change. Peut-être que vous ne l'avez même pas remarqué, car c'est ainsi que cela devrait fonctionner.
Au départ, je pensais que les utilisateurs aimeraient: lorsqu'ils étirent ou pressent des colonnes, d'autres s'ajustent également. Si les colonnes remplissent bien la largeur de l'écran et que vous en réduisez une, les autres peuvent se développer s'il y a du contenu qui ne tient pas sur une seule ligne. Essayez
cet exemple .
Mais après un petit test utilisateur, il s'est avéré que pour les gens, c'est un comportement inattendu. L'utilisateur ressent une certaine perte de contrôle lorsque ses actions provoquent des effets secondaires imprévisibles.
Nous ne devons pas faire d'hypothèses en fonction des colonnes qui interagissent et de celles qui ne le sont pas. Lors du redimensionnement d'une colonne, l'utilisateur pouvait déjà prendre implicitement la décision que la largeur du reste était parfaite.
Par conséquent, si vous ouvrez l'application pour la première fois, les colonnes sont disposées de manière optimale. Si vous modifiez la taille de l'écran, les colonnes changeront également de la même manière. Dès que vous touchez le curseur pour redimensionner une colonne, la largeur de toutes les colonnes visibles est fixe.
Avant, pendant et après avoir modifié la largeur de colonne. Encore une fois, je suis désolé que le GIF tressaille un peuChaque fois qu'une colonne est redimensionnée ou corrigée, nous créons un enregistrement localStorage indépendant qui correspond à l'identificateur de colonne avec une valeur de pixel pour préserver les préférences de l'utilisateur.
Je ne me souviens pas exactement pourquoi nous avons décidé de définir une valeur fixe en pixels, pas une option adaptative. Peut-être par simplicité. Ou parce qu'en l'absence de prise en charge de Grid and
display:contents
il y a un retour à une approche plus archaïque pour ajuster la largeur des colonnes.
Probablement, l'option adaptative ne correspondra en aucun cas aux intentions de l'utilisateur. Nous ne pouvons pas supposer que la chose la plus importante pour lui est de rendre toutes les colonnes plus petites afin qu'elles restent toutes à l'écran. Si une personne a modifié la largeur de la colonne, elle souhaite voir une certaine quantité de contenu dans cette colonne. Si nous avons un bloc adaptatif, puis qu'il se rétrécit dans une fenêtre plus petite, alors nous ignorons le choix de la personne. Il devra à nouveau modifier la largeur de la colonne pour voir le même contenu. Il est peu probable que l'utilisateur pense: "Hmm, je veux que cette colonne occupe 20% de la fenêtre, même si je la change." Cependant, je plonge trop profondément dans la situation limite: en fait, les utilisateurs redimensionnent rarement les fenêtres.
Déplacement et suppression de colonnes
Interface de personnalisation des colonnes affichéesImaginez qu'un utilisateur a modifié un ensemble de colonnes via cette interface. Si aucune des colonnes sélectionnées n'a été modifiée précédemment, elles seront affichées en utilisant les valeurs par défaut
grid-template-column
, selon le type de données. Par exemple,
minmax(150px, 3.33fr)
.
Si la largeur d'une colonne est fixée dans localStorage, nous fixons la largeur de
toutes les colonnes sélectionnées et stockons également ces valeurs dans localStorage.
Au fil du temps, de plus en plus de colonnes conservent une largeur fixe. Pour les utilisateurs, la seule façon de revenir à une conception réactive est de réinitialiser les colonnes.
Nous stockons également un tableau d'identificateurs de colonne dans localStorage, séparé des entrées de largeur.
"Pourquoi n'avez-vous pas simplement utilisé {{libraryName}}?"
Avec la bibliothèque JavaScript, la solution sera lourde, tordue, ne fournira pas d'interactivité et peut même ne pas prendre en charge <table> du tout. Je ne voulais pas non plus écrire quelque chose comme ça. J'ai pensé: "Il doit y avoir une meilleure façon."
"Pourquoi n'avez-vous pas utilisé Flexbox?"
Chaque ligne sera évaluée / affichée indépendamment les unes des autres. La colonne peut ne pas être alignée avec la colonne ci-dessus en raison du volume de contenu différent.
Je pourrais passer à <div> pour les colonnes avec des cellules groupées verticalement à l'intérieur. Mais je ne voulais pas faire ça. Je voulais utiliser <table>. De plus, nous pourrions facilement rencontrer d'autres problèmes: par exemple, l'inadéquation des cellules en hauteur entre les colonnes.
"Pourquoi n'avez-vous pas simplement utilisé <colgroup>
?"
En effet,
<colgroup> est un ancien élément pratique. Après avoir défini les colonnes à l'aide de <col>, les styles appliqués à l'une seront effectivement appliqués à toutes les cellules de cette colonne.
Mais cela s'est avéré être une solution trop limitée. Nous avons essayé, mais très vite, nous l'avons refusé. Si vite que je ne me souviens pas exactement quels étaient les problèmes. Je suis presque sûr qu'il était impossible d'atteindre le niveau d'adaptabilité souhaité et cela n'a pas fonctionné avec Flexbox et Grid.
"Pourquoi n'avez-vous pas simplement utilisé la disposition des tableaux: fixe?"
Je pourrais appliquer la règle de
table-layout: fixed
sur la
<table>
et définir la largeur de la colonne en pourcentage. Mais en regardant les exemples et en jouant avec cette règle, j'ai eu l'impression qu'elle ne fonctionne que sur des tables 100% larges. De plus, le redimensionnement d'une colonne modifie la taille des autres colonnes pour atteindre une largeur totale de 100%.
"Mais vous pourriez vous en tirer avec de simples tables!"
Oui, les tableaux prêts à l'emploi sont capables de nombreuses choses intelligentes, mais ils ne peuvent pas prendre en charge efficacement tout ce que je voulais implémenter. Vous n'êtes pas d'accord? Très bien sorcier, apprends-moi.
N'allez pas trop loin avec l'affichage: contenu
La valeur
display: contents
permis de sauvegarder la disposition du tableau. Utilisez-le uniquement lorsque vous en avez vraiment besoin. Certains navigateurs ont, ou du moins ont eu des problèmes d'accessibilité et de lecteurs d'écran.
Nous avons trouvé un
étrange display: contents
bogue de display: contents
avec glisser-déposer natif dans Firefox.
Heureusement, une fonction de sous-grille sera bientôt publiée qui permettra aux éléments enfants de s'intégrer correctement dans les grilles. Dans notre application, nous voulons seulement simplifier le balisage, mais les sous-réseaux ouvriront les portes à des orgies de grille multidimensionnelles sauvages. Voir
«Pourquoi afficher: le contenu n'est pas une disposition de grille CSS» .
Je suppose que j'ai oublié quelque chose
Il semble qu'il y ait toujours eu un problème de débordement de texte lors du redimensionnement des colonnes, mais je ne me souviens pas exactement.
Pour enregistrer les en-têtes de tableau lors du défilement vers le bas, nous utilisons
position: sticky
. C'est une grande amélioration et elle se dégrade très bien dans les navigateurs plus anciens. Cependant, pour les utilisateurs d'IE11, nous avons une sauvegarde JavaScript. En fait, je ne recommanderais pas la
position: sticky
raison des difficultés de défilement horizontal.
Je n'ai même pas mentionné certaines des fonctions de nos vues de liste. Par exemple, les utilisateurs peuvent appliquer, enregistrer et échanger des filtres personnalisés (par exemple, afficher des prospects supérieurs à 500 $ avec des clients potentiels en Europe). Dans ces filtres, vous pouvez mémoriser un ensemble de colonnes pour toujours afficher des colonnes spécifiques pour un flux de travail particulier.
Bientôt, nous allons implémenter la modification en bloc dans la vue de liste, ainsi que l'exportation de vues personnalisées vers CSV.
Quoi qu'il en soit, merci d'avoir lu.