Anne  de Morel

Anne de Morel

1656666000

Créer Une Palette De Commandes Avec Tailwind CSS Et Headless UI

En tant que développeurs, nous nous efforçons souvent d'optimiser au maximum nos flux de travail, en gagnant du temps en tirant parti d'outils comme le terminal. Une palette de commandes est l'un de ces outils qui affiche l'activité récente dans une application Web ou de bureau, permettant une navigation rapide, un accès facile aux commandes et aux raccourcis, entre autres.

Pour élever votre niveau de productivité, une palette de commandes est essentiellement un composant d'interface utilisateur qui prend la forme d'un modal. Une palette de commandes est particulièrement utile dans les grandes applications complexes avec de nombreuses pièces mobiles, par exemple, où il peut vous falloir plusieurs clics ou parcourir plusieurs listes déroulantes pour accéder à une ressource.

Dans ce didacticiel, nous allons explorer comment créer une palette de commandes entièrement fonctionnelle à partir de zéro à l'aide du composant Headless UI Combobox et de Tailwind CSS.

Cas d'utilisation réels pour une palette de commandes

En tant que développeur, il y a de fortes chances que vous ayez déjà utilisé une palette de commandes. La plus populaire est la palette de commandes VS Code , mais il existe de nombreux autres exemples, notamment la palette de commandes GitHub, Linear, Figma, Slack, monkeytype , etc.

L'application GitHub

GitHub a récemment publié une fonctionnalité de palette de commandes qui est toujours en version bêta publique au moment de la rédaction. Il vous permet d'accéder rapidement à différentes pages, de rechercher des commandes et d'obtenir des suggestions en fonction de votre contexte actuel. Vous pouvez également restreindre l'étendue des ressources que vous recherchez en cliquant sur l'une des options ou en utilisant un caractère spécial :

Palette de commandes Github

L'application linéaire

Si vous n'êtes pas familier avec Linear , c'est un outil de gestion de projet similaire à Jira et Asana qui offre une très bonne expérience utilisateur. Linear a une palette de commandes très intuitive qui vous permet d'accéder à l'ensemble des fonctionnalités de l'application grâce à sa conception axée sur le clavier. Dans ce didacticiel, nous allons créer une palette de commandes similaire à Linear :

Palette de commandes d'application linéaire

Fonctionnalités essentielles d'une palette de commandes

Plusieurs applications modernes implémentent des palettes de commandes en tant que fonctionnalité, mais qu'est-ce qui fait un bon composant de palette de commandes ? Voici une liste concise des éléments à surveiller :

  • Un simple raccourci pour ouvrir la palette, c'est-à-dire,ctrl + k
  • Il peut être accessible de n'importe où dans l'application
  • Il dispose de fonctionnalités de recherche étendues, telles que la recherche floue
  • Les commandes communiquent l'intention et sont faciles à comprendre
  • Il permet d'accéder à toutes les parties de l'application à partir d'un seul endroit

Dans la section suivante, nous allons créer notre propre composant qui comprend toutes les fonctionnalités répertoriées ci-dessus. Allons-y !

 

Construire le composant

La palette de commandes n'est pas aussi complexe qu'il n'y paraît, et n'importe qui peut en créer une rapidement. J'ai préparé un projet de démarrage pour ce tutoriel afin que vous puissiez facilement suivre. Le projet de démarrage est un SPA React et Vite qui reproduit la page des problèmes linéaires.

Mise en place du projet

Pour commencer, clonez le référentiel dans votre répertoire local, installez les dépendances nécessaires et démarrez le serveur de développement. Le projet utilise Yarn, mais si vous êtes plus à l'aise avec npm ou pnPm, vous pouvez supprimer le yarn.lockfichier avant de lancer npm installou pnpm install:

// clone repository
$ git clone https://github.com/Mayowa-Ojo/command-palette
// switch to the 'starter-project' branch
$ git checkout starter-project
// install dependencies
$ yarn
// start dev server
$ yarn dev

Si vous visitez localhost:3000, vous verrez la page suivante :

Clone du référentiel Github

Le CommandPalettecomposant

Ensuite, nous allons construire le composant. Nous utiliserons l'interface utilisateur comboboxet dialogles composants Headless. comboboxsera le composant de base de notre palette de commandes. Il possède des fonctionnalités intégrées telles que la gestion de la mise au point et l'interaction au clavier. Nous utiliserons le dialogcomposant pour rendre notre palette de commandes dans un modal.

Pour styliser les composants, nous utiliserons Tailwind CSS. Tailwind est une bibliothèque d'utilitaires CSS qui vous permet d'ajouter facilement des styles en ligne dans vos fichiers HTML ou JSX. Le projet de démarrage inclut déjà la configuration pour Tailwind.

Installez les dépendances nécessaires comme suit :

$ yarn add @headlessui/react @heroicons/react

Dans le componentsdossier, créez un CommandPalette.jsxfichier et ajoutez le bloc de code suivant :

import { Dialog, Combobox } from "@headlessui/react";

export const CommandPalette = ({ commands }) => {
  const [isOpen, setIsOpen] = useState(true);

  return (
    <Dialog
      open={isOpen}
      onClose={setIsOpen}
      className="fixed inset-0 p-4 pt-[15vh] overflow-y-auto"
    >
      <Dialog.Overlay className="fixed inset-0 backdrop-blur-[1px]" />
      <Combobox
         as="div"
         className="bg-accent-dark max-w-2xl mx-auto rounded-lg shadow-2xl relative flex flex-col"
         onChange={(command) => {
            // we have access to the selected command
            // a redirect can happen here or any action can be executed
            setIsOpen(false);
         }}
      >
         <div className="mx-4 mt-4 px-2 h-[25px] text-xs text-slate-100 bg-primary/30 rounded self-start flex items-center flex-shrink-0">
            Issue
         </div>
         <div className="flex items-center text-lg font-medium border-b border-slate-500">
            <Combobox.Input
               className="p-5 text-white placeholder-gray-200 w-full bg-transparent border-0 outline-none"
               placeholder="Type a command or search..."
            />
         </div>
         <Combobox.Options
            className="max-h-72 overflow-y-auto flex flex-col"
            static
         ></Combobox.Options>
      </Combobox>
   </Dialog>
  );
};

Quelques choses se passent ici. Tout d'abord, nous importons les composants Dialoget Combobox. Dialogest rendu comme un wrapper autour du Combobox, et nous initialisons un état local appelé isOpenpour contrôler le modal.

Nous rendons un Dialog.Overlayà l'intérieur du Dialogcomposant pour servir de superposition pour le modal. Vous pouvez le styliser comme vous le souhaitez, mais ici, nous utilisons simplement backdrop-blur. Ensuite, nous rendons le Comboboxcomposant et passons une fonction de gestionnaire à la onChangeprop. Ce gestionnaire est appelé chaque fois qu'un élément est sélectionné dans le fichier Combobox. Vous voudriez généralement naviguer vers une page ou exécuter une action ici, mais pour l'instant, nous fermons simplement le fichier Dialog.

Combobox.Inputgérera la fonctionnalité de recherche, que nous ajouterons plus tard dans cette section. Combobox.Optionsrend un ulélément qui encapsule la liste des résultats que nous rendrons. Nous passons une staticprop qui indique que nous voulons ignorer l'état géré en interne du composant.

Ensuite, nous rendons notre CommandPalettedans le App.jsxfichier :

const App = () => {
   return (
      <div className="flex w-full bg-primary h-screen max-h-screen min-h-screen overflow-hidden">
         <Drawer teams={teams} />
         <AllIssues issues={issues} />
         <CommandPalette commands={commands}/>
      </div>
   );
};

Parlons du fonctionnement de notre palette de commandes. Nous avons une liste de commandes prédéfinies dans le data/seed.jsonfichier. Ces commandes seront affichées dans la palette lors de son ouverture et peuvent être filtrées en fonction de la requête de recherche. Assez simple, non ?

Le CommandGroupcomposant

CommandPalettereçoit un commandsaccessoire, qui est la liste des commandes que nous avons importées de seed.json. Maintenant, créez un CommandGroup.jsxfichier dans le componentsdossier et ajoutez le code suivant :

// CommandGroup.jsx
import React from "react";
import clsx from "clsx";
import { Combobox } from "@headlessui/react";
import { PlusIcon, ArrowSmRightIcon } from "@heroicons/react/solid";
import {
   CogIcon,
   UserCircleIcon,
   FastForwardIcon,
} from "@heroicons/react/outline";
import { ProjectIcon } from "../icons/ProjectIcon";
import { ViewsIcon } from "../icons/ViewsIcon";
import { TemplatesIcon } from "../icons/TemplatesIcon";
import { TeamIcon } from "../icons/TeamIcon";

export const CommandGroup = ({ commands, group }) => {
   return (
      <React.Fragment>
         {/* only show the header when there are commands belonging to this group */}
         {commands.filter((command) => command.group === group).length >= 1 && (
            <div className="flex items-center h-6 flex-shrink-0 bg-accent/50">
               <span className="text-xs text-slate-100 px-3.5">{group}</span>
            </div>
         )}
         {commands
            .filter((command) => command.group === group)
            .map((command, idx) => (
               <Combobox.Option key={idx} value={command}>
                  {({ active }) => (
                     <div
                        className={clsx(
                           "w-full h-[46px] text-white flex items-center hover:bg-primary/40 cursor-default transition-colors duration-100 ease-in",
                           active ? "bg-primary/40" : ""
                        )}
                     >
                        <div className="px-3.5 flex items-center w-full">
                           <div className="mr-3 flex items-center justify-center w-4">
                              {mapCommandGroupToIcon(
                                 command.group.toLowerCase()
                              )}
                           </div>
                           <span className="text-sm text-left flex flex-auto">
                              {command.name}
                           </span>
                           <span className="text-[10px]">{command.shortcut}</span>
                        </div>
                     </div>
                  )}
               </Combobox.Option>
            ))}
      </React.Fragment>
   );
};

Nous utilisons simplement le CommandGroupcomposant pour éviter un code répétitif. Si vous regardez la palette de commandes linéaires, vous verrez que les commandes sont regroupées en fonction du contexte. Pour implémenter cela, nous devons filtrer les commandes qui appartiennent au même groupe et répéter cette logique pour chaque groupe.

Le CommandGroupcomposant reçoit deux accessoires, commandset group. Nous allons filtrer les commandes en fonction du groupe actuel et les rendre à l'aide du Combobox.Optioncomposant. En utilisant les accessoires de rendu, nous pouvons obtenir l' activeélément et le styler en conséquence, ce qui nous permet de rendre le CommandGrouppour chaque groupe CommandPalettetout en gardant le code propre.

Notez que nous avons une mapCommandGroupToIconfonction quelque part dans le bloc de code ci-dessus. En effet, chaque groupe a une icône différente et la fonction n'est qu'une aide pour rendre l'icône correcte pour le groupe actuel. Maintenant, ajoutez la fonction juste en dessous du CommandGroupcomposant dans le même fichier :

const mapCommandGroupToIcon = (group) => {
   switch (group) {
      case "issue":
         return <PlusIcon className="w-4 h-4 text-white"/>;
      case "project":

Maintenant, nous devons rendre le CommandGroupcomposant dans CommandPalette.
Importez le composant comme suit :

import { CommandGroup } from "./CommandGroup";

Affichez-le à l'intérieur du Combobox.Optionspour chaque groupe :

<Combobox.Options
   className="max-h-72 overflow-y-auto flex flex-col"
   static
>
   <CommandGroup commands={commands} group="Issue"/>
   <CommandGroup commands={commands} group="Project"/>
   <CommandGroup commands={commands} group="Views"/>
   <CommandGroup commands={commands} group="Team"/>
   <CommandGroup commands={commands} group="Templates"/>
   <CommandGroup commands={commands} group="Navigation"/>
   <CommandGroup commands={commands} group="Settings"/>
   <CommandGroup commands={commands} group="Account"/>
</Combobox.Options>

Vous devriez voir la liste des commandes rendues maintenant. L'étape suivante consiste à câbler la fonctionnalité de recherche.

Implémentation de la fonctionnalité de recherche

Créez une variable d'état local dansCommandPalette.jsx :

// CommandPalette.jsx
const [query, setQuery] = useState("");

Transmettez le gestionnaire de mise à jour d'état à la onChangeprop dans Combobox.Input. Le querysera mis à jour avec chaque caractère que vous saisissez dans la zone de saisie :

<Combobox.Input
  className="p-5 text-white placeholder-gray-200 w-full bg-transparent border-0 outline-none"
  placeholder="Type a command or search..."
  onChange={(e) => setQuery(e.target.value)}
/>

L'une des principales propriétés d'une bonne palette de commandes est une fonctionnalité de recherche étendue. Nous pouvons simplement faire une simple comparaison de chaîne de la requête de recherche avec les commandes, mais cela ne tiendrait pas compte des fautes de frappe et du contexte. Une bien meilleure solution qui n'introduit pas trop de complexité est une recherche floue.

Nous utiliserons la bibliothèque Fuse.js pour cela. Fuse.js est une bibliothèque de recherche puissante, légère et floue sans aucune dépendance. Si vous n'êtes pas familier avec la recherche floue, il s'agit d'une technique de correspondance de chaînes qui privilégie la correspondance approximative à la correspondance exacte, ce qui implique que vous pouvez obtenir des suggestions correctes même si la requête contient des fautes de frappe ou des fautes d'orthographe.

Tout d'abord, installez la bibliothèque Fuse.js :

$ yarn add fuse.js

Dans CommandPalette.jsx, instanciez la Fuseclasse avec une liste de commandes :

// CommandPalette.jsx
const fuse = new Fuse(commands, { includeScore: true, keys: ["name"] });

La Fuseclasse accepte un tableau de commandes et d'options de configuration. Le keyschamp est l'endroit où nous enregistrons les champs de la liste des commandes à indexer par Fuse.js. Maintenant, créez une fonction qui gérera la recherche et renverra les résultats filtrés :

// CommandPalette.jsx
const filteredCommands =
  query === ""
     ? commands
     : fuse.search(query).map((res) => ({ ...res.item }));

Nous vérifions si le queryest vide, renvoyons toutes les commandes, et sinon, exécutons la fuse.searchméthode avec la requête. De plus, nous mappons les résultats pour créer un nouvel objet. Ceci afin de maintenir la cohérence car les résultats renvoyés par Fuse.js ont de nouveaux champs et ne correspondront pas à la structure que nous avons déjà.

Maintenant, passez le filteredCommandsà l' commandsaccessoire dans chaque CommandGroupcomposant. Cela devrait ressembler au code ci-dessous :

// CommandPalette.jsx
<CommandGroup commands={filteredCommands} group="Issue"/>
<CommandGroup commands={filteredCommands} group="Project"/>
<CommandGroup commands={filteredCommands} group="Views"/>
<CommandGroup commands={filteredCommands} group="Team"/>
<CommandGroup commands={filteredCommands} group="Templates"/>
<CommandGroup commands={filteredCommands} group="Navigation"/>
<CommandGroup commands={filteredCommands} group="Settings"/>
<CommandGroup commands={filteredCommands} group="Account"/>

Essayez de rechercher dans la palette de commandes et voyez si les résultats sont filtrés :

Filtre de recherche de la palette de commandes

Nous avons une palette de commandes entièrement fonctionnelle, mais vous remarquerez peut-être qu'elle est toujours ouverte. Nous devons pouvoir contrôler son état ouvert. Définissons un événement clavier qui écoutera une combinaison de touches et mettra à jour l'état ouvert. Ajoutez le code suivant à CommandPalette.jsx:

// CommandPalette.jsx
useEffect(() => {
  const onKeydown = (e) => {
     if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        setIsOpen(true);
     }
  };
  window.addEventListener("keydown", onKeydown);
  return () => {
     window.removeEventListener("keydown", onKeydown);
  };
}, []);

Nous utilisons un useEffectcrochet pour enregistrer un keydownévénement de clavier lorsque le composant est monté, et nous utilisons une fonction de nettoyage pour supprimer l'écouteur lorsque le composant se démonte.

Dans le Hook, nous vérifions si la combinaison de touches correspond à ctrl + k. Si c'est le cas, l'état ouvert est défini sur true. Vous pouvez également utiliser une combinaison de touches différente, mais il est important de ne pas utiliser de combinaisons qui entrent en conflit avec les raccourcis natifs du navigateur.

C'est ça! Vous pouvez trouver la version finale de ce projet sur la branche du projet fini .

react-command-palette : composant prédéfini

Nous avons exploré comment créer un composant de palette de commandes à partir de zéro. Cependant, vous préféreriez probablement ne pas créer la vôtre chaque fois que vous avez besoin d'une palette de commandes. C'est là qu'un composant prédéfini peut être utile. La plupart des bibliothèques de composants n'offrent pas de palette de commandes, mais react-command-palette est un composant bien écrit, accessible et compatible avec les navigateurs.

Pour utiliser ce composant, installez-le en tant que dépendance dans votre projet :

$ yarn add react-command-palette

Importez le composant et transmettez-lui votre liste de commandes comme suit :

import React from "react";
import CommandPalette from 'react-command-palette';

const commands = [{
  name: "Foo",
  command() {}
},{
  name: "Bar",
  command() {}
}]

export default function App() {
  return (
    <div>
      <CommandPalette commands={commands} />
    </div>
  );
}

Il existe de nombreuses options de configuration que vous pouvez utiliser pour personnaliser l'apparence et le comportement en fonction de vos besoins. Par exemple, la themeconfiguration vous permet de choisir parmi un certain nombre de thèmes intégrés ou de créer votre propre thème personnalisé.

Prochaines étapes

Dans cet article, vous avez découvert les palettes de commandes, leurs cas d'utilisation idéaux et les fonctionnalités qui constituent une bonne palette de commandes. Vous avez également exploré dans les étapes détaillées comment en créer un à l'aide du composant combobox de l'interface utilisateur sans tête et du CSS Tailwind.

Si vous souhaitez simplement intégrer rapidement cette fonctionnalité dans votre application, un composant prédéfini tel que react-command-palette est la solution. Merci d'avoir lu et n'oubliez pas de laisser un commentaire si vous avez des questions.

Source : https://blog.logrocket.com/react-command-palette-tailwind-css-headless-ui/

#tailwindcss #headless #react 

What is GEEK

Buddha Community

Créer Une Palette De Commandes Avec Tailwind CSS Et Headless UI
Anne  de Morel

Anne de Morel

1656666000

Créer Une Palette De Commandes Avec Tailwind CSS Et Headless UI

En tant que développeurs, nous nous efforçons souvent d'optimiser au maximum nos flux de travail, en gagnant du temps en tirant parti d'outils comme le terminal. Une palette de commandes est l'un de ces outils qui affiche l'activité récente dans une application Web ou de bureau, permettant une navigation rapide, un accès facile aux commandes et aux raccourcis, entre autres.

Pour élever votre niveau de productivité, une palette de commandes est essentiellement un composant d'interface utilisateur qui prend la forme d'un modal. Une palette de commandes est particulièrement utile dans les grandes applications complexes avec de nombreuses pièces mobiles, par exemple, où il peut vous falloir plusieurs clics ou parcourir plusieurs listes déroulantes pour accéder à une ressource.

Dans ce didacticiel, nous allons explorer comment créer une palette de commandes entièrement fonctionnelle à partir de zéro à l'aide du composant Headless UI Combobox et de Tailwind CSS.

Cas d'utilisation réels pour une palette de commandes

En tant que développeur, il y a de fortes chances que vous ayez déjà utilisé une palette de commandes. La plus populaire est la palette de commandes VS Code , mais il existe de nombreux autres exemples, notamment la palette de commandes GitHub, Linear, Figma, Slack, monkeytype , etc.

L'application GitHub

GitHub a récemment publié une fonctionnalité de palette de commandes qui est toujours en version bêta publique au moment de la rédaction. Il vous permet d'accéder rapidement à différentes pages, de rechercher des commandes et d'obtenir des suggestions en fonction de votre contexte actuel. Vous pouvez également restreindre l'étendue des ressources que vous recherchez en cliquant sur l'une des options ou en utilisant un caractère spécial :

Palette de commandes Github

L'application linéaire

Si vous n'êtes pas familier avec Linear , c'est un outil de gestion de projet similaire à Jira et Asana qui offre une très bonne expérience utilisateur. Linear a une palette de commandes très intuitive qui vous permet d'accéder à l'ensemble des fonctionnalités de l'application grâce à sa conception axée sur le clavier. Dans ce didacticiel, nous allons créer une palette de commandes similaire à Linear :

Palette de commandes d'application linéaire

Fonctionnalités essentielles d'une palette de commandes

Plusieurs applications modernes implémentent des palettes de commandes en tant que fonctionnalité, mais qu'est-ce qui fait un bon composant de palette de commandes ? Voici une liste concise des éléments à surveiller :

  • Un simple raccourci pour ouvrir la palette, c'est-à-dire,ctrl + k
  • Il peut être accessible de n'importe où dans l'application
  • Il dispose de fonctionnalités de recherche étendues, telles que la recherche floue
  • Les commandes communiquent l'intention et sont faciles à comprendre
  • Il permet d'accéder à toutes les parties de l'application à partir d'un seul endroit

Dans la section suivante, nous allons créer notre propre composant qui comprend toutes les fonctionnalités répertoriées ci-dessus. Allons-y !

 

Construire le composant

La palette de commandes n'est pas aussi complexe qu'il n'y paraît, et n'importe qui peut en créer une rapidement. J'ai préparé un projet de démarrage pour ce tutoriel afin que vous puissiez facilement suivre. Le projet de démarrage est un SPA React et Vite qui reproduit la page des problèmes linéaires.

Mise en place du projet

Pour commencer, clonez le référentiel dans votre répertoire local, installez les dépendances nécessaires et démarrez le serveur de développement. Le projet utilise Yarn, mais si vous êtes plus à l'aise avec npm ou pnPm, vous pouvez supprimer le yarn.lockfichier avant de lancer npm installou pnpm install:

// clone repository
$ git clone https://github.com/Mayowa-Ojo/command-palette
// switch to the 'starter-project' branch
$ git checkout starter-project
// install dependencies
$ yarn
// start dev server
$ yarn dev

Si vous visitez localhost:3000, vous verrez la page suivante :

Clone du référentiel Github

Le CommandPalettecomposant

Ensuite, nous allons construire le composant. Nous utiliserons l'interface utilisateur comboboxet dialogles composants Headless. comboboxsera le composant de base de notre palette de commandes. Il possède des fonctionnalités intégrées telles que la gestion de la mise au point et l'interaction au clavier. Nous utiliserons le dialogcomposant pour rendre notre palette de commandes dans un modal.

Pour styliser les composants, nous utiliserons Tailwind CSS. Tailwind est une bibliothèque d'utilitaires CSS qui vous permet d'ajouter facilement des styles en ligne dans vos fichiers HTML ou JSX. Le projet de démarrage inclut déjà la configuration pour Tailwind.

Installez les dépendances nécessaires comme suit :

$ yarn add @headlessui/react @heroicons/react

Dans le componentsdossier, créez un CommandPalette.jsxfichier et ajoutez le bloc de code suivant :

import { Dialog, Combobox } from "@headlessui/react";

export const CommandPalette = ({ commands }) => {
  const [isOpen, setIsOpen] = useState(true);

  return (
    <Dialog
      open={isOpen}
      onClose={setIsOpen}
      className="fixed inset-0 p-4 pt-[15vh] overflow-y-auto"
    >
      <Dialog.Overlay className="fixed inset-0 backdrop-blur-[1px]" />
      <Combobox
         as="div"
         className="bg-accent-dark max-w-2xl mx-auto rounded-lg shadow-2xl relative flex flex-col"
         onChange={(command) => {
            // we have access to the selected command
            // a redirect can happen here or any action can be executed
            setIsOpen(false);
         }}
      >
         <div className="mx-4 mt-4 px-2 h-[25px] text-xs text-slate-100 bg-primary/30 rounded self-start flex items-center flex-shrink-0">
            Issue
         </div>
         <div className="flex items-center text-lg font-medium border-b border-slate-500">
            <Combobox.Input
               className="p-5 text-white placeholder-gray-200 w-full bg-transparent border-0 outline-none"
               placeholder="Type a command or search..."
            />
         </div>
         <Combobox.Options
            className="max-h-72 overflow-y-auto flex flex-col"
            static
         ></Combobox.Options>
      </Combobox>
   </Dialog>
  );
};

Quelques choses se passent ici. Tout d'abord, nous importons les composants Dialoget Combobox. Dialogest rendu comme un wrapper autour du Combobox, et nous initialisons un état local appelé isOpenpour contrôler le modal.

Nous rendons un Dialog.Overlayà l'intérieur du Dialogcomposant pour servir de superposition pour le modal. Vous pouvez le styliser comme vous le souhaitez, mais ici, nous utilisons simplement backdrop-blur. Ensuite, nous rendons le Comboboxcomposant et passons une fonction de gestionnaire à la onChangeprop. Ce gestionnaire est appelé chaque fois qu'un élément est sélectionné dans le fichier Combobox. Vous voudriez généralement naviguer vers une page ou exécuter une action ici, mais pour l'instant, nous fermons simplement le fichier Dialog.

Combobox.Inputgérera la fonctionnalité de recherche, que nous ajouterons plus tard dans cette section. Combobox.Optionsrend un ulélément qui encapsule la liste des résultats que nous rendrons. Nous passons une staticprop qui indique que nous voulons ignorer l'état géré en interne du composant.

Ensuite, nous rendons notre CommandPalettedans le App.jsxfichier :

const App = () => {
   return (
      <div className="flex w-full bg-primary h-screen max-h-screen min-h-screen overflow-hidden">
         <Drawer teams={teams} />
         <AllIssues issues={issues} />
         <CommandPalette commands={commands}/>
      </div>
   );
};

Parlons du fonctionnement de notre palette de commandes. Nous avons une liste de commandes prédéfinies dans le data/seed.jsonfichier. Ces commandes seront affichées dans la palette lors de son ouverture et peuvent être filtrées en fonction de la requête de recherche. Assez simple, non ?

Le CommandGroupcomposant

CommandPalettereçoit un commandsaccessoire, qui est la liste des commandes que nous avons importées de seed.json. Maintenant, créez un CommandGroup.jsxfichier dans le componentsdossier et ajoutez le code suivant :

// CommandGroup.jsx
import React from "react";
import clsx from "clsx";
import { Combobox } from "@headlessui/react";
import { PlusIcon, ArrowSmRightIcon } from "@heroicons/react/solid";
import {
   CogIcon,
   UserCircleIcon,
   FastForwardIcon,
} from "@heroicons/react/outline";
import { ProjectIcon } from "../icons/ProjectIcon";
import { ViewsIcon } from "../icons/ViewsIcon";
import { TemplatesIcon } from "../icons/TemplatesIcon";
import { TeamIcon } from "../icons/TeamIcon";

export const CommandGroup = ({ commands, group }) => {
   return (
      <React.Fragment>
         {/* only show the header when there are commands belonging to this group */}
         {commands.filter((command) => command.group === group).length >= 1 && (
            <div className="flex items-center h-6 flex-shrink-0 bg-accent/50">
               <span className="text-xs text-slate-100 px-3.5">{group}</span>
            </div>
         )}
         {commands
            .filter((command) => command.group === group)
            .map((command, idx) => (
               <Combobox.Option key={idx} value={command}>
                  {({ active }) => (
                     <div
                        className={clsx(
                           "w-full h-[46px] text-white flex items-center hover:bg-primary/40 cursor-default transition-colors duration-100 ease-in",
                           active ? "bg-primary/40" : ""
                        )}
                     >
                        <div className="px-3.5 flex items-center w-full">
                           <div className="mr-3 flex items-center justify-center w-4">
                              {mapCommandGroupToIcon(
                                 command.group.toLowerCase()
                              )}
                           </div>
                           <span className="text-sm text-left flex flex-auto">
                              {command.name}
                           </span>
                           <span className="text-[10px]">{command.shortcut}</span>
                        </div>
                     </div>
                  )}
               </Combobox.Option>
            ))}
      </React.Fragment>
   );
};

Nous utilisons simplement le CommandGroupcomposant pour éviter un code répétitif. Si vous regardez la palette de commandes linéaires, vous verrez que les commandes sont regroupées en fonction du contexte. Pour implémenter cela, nous devons filtrer les commandes qui appartiennent au même groupe et répéter cette logique pour chaque groupe.

Le CommandGroupcomposant reçoit deux accessoires, commandset group. Nous allons filtrer les commandes en fonction du groupe actuel et les rendre à l'aide du Combobox.Optioncomposant. En utilisant les accessoires de rendu, nous pouvons obtenir l' activeélément et le styler en conséquence, ce qui nous permet de rendre le CommandGrouppour chaque groupe CommandPalettetout en gardant le code propre.

Notez que nous avons une mapCommandGroupToIconfonction quelque part dans le bloc de code ci-dessus. En effet, chaque groupe a une icône différente et la fonction n'est qu'une aide pour rendre l'icône correcte pour le groupe actuel. Maintenant, ajoutez la fonction juste en dessous du CommandGroupcomposant dans le même fichier :

const mapCommandGroupToIcon = (group) => {
   switch (group) {
      case "issue":
         return <PlusIcon className="w-4 h-4 text-white"/>;
      case "project":

Maintenant, nous devons rendre le CommandGroupcomposant dans CommandPalette.
Importez le composant comme suit :

import { CommandGroup } from "./CommandGroup";

Affichez-le à l'intérieur du Combobox.Optionspour chaque groupe :

<Combobox.Options
   className="max-h-72 overflow-y-auto flex flex-col"
   static
>
   <CommandGroup commands={commands} group="Issue"/>
   <CommandGroup commands={commands} group="Project"/>
   <CommandGroup commands={commands} group="Views"/>
   <CommandGroup commands={commands} group="Team"/>
   <CommandGroup commands={commands} group="Templates"/>
   <CommandGroup commands={commands} group="Navigation"/>
   <CommandGroup commands={commands} group="Settings"/>
   <CommandGroup commands={commands} group="Account"/>
</Combobox.Options>

Vous devriez voir la liste des commandes rendues maintenant. L'étape suivante consiste à câbler la fonctionnalité de recherche.

Implémentation de la fonctionnalité de recherche

Créez une variable d'état local dansCommandPalette.jsx :

// CommandPalette.jsx
const [query, setQuery] = useState("");

Transmettez le gestionnaire de mise à jour d'état à la onChangeprop dans Combobox.Input. Le querysera mis à jour avec chaque caractère que vous saisissez dans la zone de saisie :

<Combobox.Input
  className="p-5 text-white placeholder-gray-200 w-full bg-transparent border-0 outline-none"
  placeholder="Type a command or search..."
  onChange={(e) => setQuery(e.target.value)}
/>

L'une des principales propriétés d'une bonne palette de commandes est une fonctionnalité de recherche étendue. Nous pouvons simplement faire une simple comparaison de chaîne de la requête de recherche avec les commandes, mais cela ne tiendrait pas compte des fautes de frappe et du contexte. Une bien meilleure solution qui n'introduit pas trop de complexité est une recherche floue.

Nous utiliserons la bibliothèque Fuse.js pour cela. Fuse.js est une bibliothèque de recherche puissante, légère et floue sans aucune dépendance. Si vous n'êtes pas familier avec la recherche floue, il s'agit d'une technique de correspondance de chaînes qui privilégie la correspondance approximative à la correspondance exacte, ce qui implique que vous pouvez obtenir des suggestions correctes même si la requête contient des fautes de frappe ou des fautes d'orthographe.

Tout d'abord, installez la bibliothèque Fuse.js :

$ yarn add fuse.js

Dans CommandPalette.jsx, instanciez la Fuseclasse avec une liste de commandes :

// CommandPalette.jsx
const fuse = new Fuse(commands, { includeScore: true, keys: ["name"] });

La Fuseclasse accepte un tableau de commandes et d'options de configuration. Le keyschamp est l'endroit où nous enregistrons les champs de la liste des commandes à indexer par Fuse.js. Maintenant, créez une fonction qui gérera la recherche et renverra les résultats filtrés :

// CommandPalette.jsx
const filteredCommands =
  query === ""
     ? commands
     : fuse.search(query).map((res) => ({ ...res.item }));

Nous vérifions si le queryest vide, renvoyons toutes les commandes, et sinon, exécutons la fuse.searchméthode avec la requête. De plus, nous mappons les résultats pour créer un nouvel objet. Ceci afin de maintenir la cohérence car les résultats renvoyés par Fuse.js ont de nouveaux champs et ne correspondront pas à la structure que nous avons déjà.

Maintenant, passez le filteredCommandsà l' commandsaccessoire dans chaque CommandGroupcomposant. Cela devrait ressembler au code ci-dessous :

// CommandPalette.jsx
<CommandGroup commands={filteredCommands} group="Issue"/>
<CommandGroup commands={filteredCommands} group="Project"/>
<CommandGroup commands={filteredCommands} group="Views"/>
<CommandGroup commands={filteredCommands} group="Team"/>
<CommandGroup commands={filteredCommands} group="Templates"/>
<CommandGroup commands={filteredCommands} group="Navigation"/>
<CommandGroup commands={filteredCommands} group="Settings"/>
<CommandGroup commands={filteredCommands} group="Account"/>

Essayez de rechercher dans la palette de commandes et voyez si les résultats sont filtrés :

Filtre de recherche de la palette de commandes

Nous avons une palette de commandes entièrement fonctionnelle, mais vous remarquerez peut-être qu'elle est toujours ouverte. Nous devons pouvoir contrôler son état ouvert. Définissons un événement clavier qui écoutera une combinaison de touches et mettra à jour l'état ouvert. Ajoutez le code suivant à CommandPalette.jsx:

// CommandPalette.jsx
useEffect(() => {
  const onKeydown = (e) => {
     if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        setIsOpen(true);
     }
  };
  window.addEventListener("keydown", onKeydown);
  return () => {
     window.removeEventListener("keydown", onKeydown);
  };
}, []);

Nous utilisons un useEffectcrochet pour enregistrer un keydownévénement de clavier lorsque le composant est monté, et nous utilisons une fonction de nettoyage pour supprimer l'écouteur lorsque le composant se démonte.

Dans le Hook, nous vérifions si la combinaison de touches correspond à ctrl + k. Si c'est le cas, l'état ouvert est défini sur true. Vous pouvez également utiliser une combinaison de touches différente, mais il est important de ne pas utiliser de combinaisons qui entrent en conflit avec les raccourcis natifs du navigateur.

C'est ça! Vous pouvez trouver la version finale de ce projet sur la branche du projet fini .

react-command-palette : composant prédéfini

Nous avons exploré comment créer un composant de palette de commandes à partir de zéro. Cependant, vous préféreriez probablement ne pas créer la vôtre chaque fois que vous avez besoin d'une palette de commandes. C'est là qu'un composant prédéfini peut être utile. La plupart des bibliothèques de composants n'offrent pas de palette de commandes, mais react-command-palette est un composant bien écrit, accessible et compatible avec les navigateurs.

Pour utiliser ce composant, installez-le en tant que dépendance dans votre projet :

$ yarn add react-command-palette

Importez le composant et transmettez-lui votre liste de commandes comme suit :

import React from "react";
import CommandPalette from 'react-command-palette';

const commands = [{
  name: "Foo",
  command() {}
},{
  name: "Bar",
  command() {}
}]

export default function App() {
  return (
    <div>
      <CommandPalette commands={commands} />
    </div>
  );
}

Il existe de nombreuses options de configuration que vous pouvez utiliser pour personnaliser l'apparence et le comportement en fonction de vos besoins. Par exemple, la themeconfiguration vous permet de choisir parmi un certain nombre de thèmes intégrés ou de créer votre propre thème personnalisé.

Prochaines étapes

Dans cet article, vous avez découvert les palettes de commandes, leurs cas d'utilisation idéaux et les fonctionnalités qui constituent une bonne palette de commandes. Vous avez également exploré dans les étapes détaillées comment en créer un à l'aide du composant combobox de l'interface utilisateur sans tête et du CSS Tailwind.

Si vous souhaitez simplement intégrer rapidement cette fonctionnalité dans votre application, un composant prédéfini tel que react-command-palette est la solution. Merci d'avoir lu et n'oubliez pas de laisser un commentaire si vous avez des questions.

Source : https://blog.logrocket.com/react-command-palette-tailwind-css-headless-ui/

#tailwindcss #headless #react 

Reggie  Hudson

Reggie Hudson

1627084440

Learn Headless UI with React: Listbox Component (Tailwind CSS)

Using Headless UI in React, we easily create a react ready listbox component and then incorporate the Headless UI Transition component as well.

Headless UI Documentation: https://github.com/tailwindlabs/headlessui/blob/develop/packages/%40headlessui-react/README.md

📚 Library(s) needed:
npm install tailwindcss
npm install @headlessui/react

🖥️ Source code: https://devascend.com/github?link=https://github.com/DevAscend/YT-HeadlessUI-React-Tutorials

💡 Have a video request?
Suggest it in the Dev Ascend Discord community server or leave it in the comments below!

🕐 Timestamps:
00:00 Introduction
00:34 Creating the Listbox component
05:45 Incorporating the Transition component

#headless #ui #react

#headless #ui #react #tailwind css #css

Poppy Cooke

Poppy Cooke

1658207640

Comment créer une barre de navigation fixe avec Tailwind CSS

Dans ce didacticiel Tailwind CSS, vous apprendrez à créer une barre de navigation fixe avec Tailwind CSS et à la personnaliser.

Voulez-vous utiliser Tailwind CSS pour créer une barre de navigation fixe qui reste en haut de votre page lorsque vous faites défiler ? Dans cet article, nous allons créer cela et nous nous occuperons également de la réactivité pour mobile !

Au cours des dernières semaines, j'ai commencé à essayer Tailwind CSS pour la première fois, et je dois dire que je l'aime jusqu'à présent. Un article que j'ai créé il y a quelque temps était de savoir comment créer une barre de navigation collante et réactive avec un CSS de base (vous pouvez le trouver ici ). J'ai donc pensé qu'il serait bien de créer quelque chose de similaire avec le vent arrière et de voir les différences !

Commençons donc à créer la barre de navigation que vous pouvez voir dans le gif suivant :

Navbar fixe avec Tailwind CSS : Aperçu

Préparation du projet

Pour ce projet, nous utiliserons vite pour lancer notre serveur de développement et tailwind en tant que framework CSS. Pour commencer, nous allons initialiser un nouveau projet vite et installer les dépendances requises pour un environnement de développement fluide.

Tout d'abord, nous allons créer un nouveau projet vite en exécutant :

npm init vite navbar-tw

À l'étape suivante, nous sélectionnons 'vanilla' comme cadre et appuyons sur Entrée pour continuer. À partir de là, vous pouvez choisir d'utiliser du javascript vanille ou du tapuscrit. Nous utiliserons le javascript de base pour ce guide, alors sélectionnez à nouveau vanille.

Vous pouvez maintenant entrer dans le répertoire et installer les packages requis :

cd navbar-tw
npm install

Avec cela, nous avons installé vite et avons la possibilité de voir les modifications appliquées en temps réel après le démarrage du serveur de développement. Mais avant cela, installons tailwind. En plus de tailwind, nous installerons également postcss pour faciliter le développement :

npm install -D tailwindcss postcss autoprefixer

Et ensuite, nous créons des fichiers de configuration pour les différents composants :

npx tailwindcss init -p

Dans le fichier tailwind.config.js nouvellement créé, nous allons modifier la partie contenu comme suit :

module.exports = {
  content: [
    "./index.html"
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

La dernière étape de la configuration de tailwind consiste à mettre à jour le fichier style.css comme suit :

@tailwind base;
@tailwind components;
@tailwind utilities;

Maintenant que nous avons configuré vite et tailwind, nous pouvons démarrer notre serveur de développement avec :

npm run dev

Avant de continuer et de construire notre barre de navigation fixe, nettoyons d'abord certains fichiers créés par Vite. Supprimez le contenu de main.js et mettez à jour index.html comme suit :

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tailwind Navbar</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body class="bg-gradient-to-t from-blue-900 to-blue-500 h-[200vh]">

    <script src="/main.js"></script>
  </body>
</html>

Cela crée un dégradé bleu avec la hauteur de deux fois votre écran. Nous générons cet arrière-plan pour visualiser l'effet de défilement collant.

Créer une barre de navigation fixe pour mobile

L'un des principes de construction avec Tailwind CSS est qu'il utilise un système de point d'arrêt Mobile-First, ce qui signifie que les classes sans préfixe s'appliqueront à tous les écrans et que les classes préfixées s'appliqueront après le point d'arrêt défini. Par conséquent, nous allons d'abord créer la mise en page mobile, puis créer la version de bureau.

La première étape pour nous est de définir un header comprenant une image (donc créer une image appelée avatar.png) et un conteneur comprenant l'icône du menu et les éléments du menu (dans ce cas, un div) :

<header>
  <div id="img-container">
    <img src="avatar.png" alt="avatar">
  </div>

  <div id="nav-container">
    <div id="nav-icon">
      <svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 20 20" fill="currentColor">
        <path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd" />
      </svg>
    </div>

    <ul id="nav-menu">
      <li>Menu Item</li>
      <li>Menu Item</li>
      <li>Menu Item</li>
    </ul>
  </div>
</header>

Nous allons maintenant styliser tous ces éléments avec les classes CSS Tailwind. Le premier est l'en-tête. Nous voulons qu'il ait une hauteur spécifique, qu'il reste en haut et qu'il ait un fond blanc. Alors créons ça.

<header class="h-16 bg-white sticky top-0">
	<!-- ... -->
</header>

Nous avons maintenant une barre qui colle au sommet, mais les autres éléments sont trop grands ou au mauvais endroit, alors continuons avec l'image. Nous le voulons à l'intérieur de l'en-tête sur le côté gauche. Par conséquent, nous changeons les classes comme ceci :

<div id="img-container" class="absolute left-2 top-2 h-12 w-12">
    <img src="avatar.png" alt="avatar" class="h-full w-full">
</div>

Et puis on fait la même chose avec l'icône de la barre de navigation, juste à droite. En plus de cela, nous voulons également les éléments de menu sous l'icône. Pour cela, nous devons mettre à jour les classes comme ceci :

<div id="nav-container" class="bg-white p-2 flex flex-col items-end">
  <div id="nav-icon" class="h-12 w-12 p-2 group">
    <svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 20 20" fill="currentColor" class="h-full w-full group-hover:fill-blue-500">
      <path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd" />
    </svg>
  </div>

  <ul id="nav-menu" class="w-full space-y-2 pr-3 font-semibold text-xl text-right">
    <li>Menu Item</li>
    <li>Menu Item</li>
    <li>Menu Item</li>
  </ul>
</div>

Une chose à noter est la classe de groupe. Cela nous permet de colorer l'élément enfant (dans ce cas, le SVG) au survol de l'élément du groupe. C'est une méthode pratique ajoutée par tailwind !

Avec ces classes en place, nous avons la base de notre barre de navigation mobile :

Barre de navigation fixe avec Tailwind CSS : Foundation

Tout semble beau et attendu, mais nous ne pouvons pas encore ouvrir et fermer la navigation. Alors ajoutons cela à l'étape suivante. Par conséquent, nous ajoutons d'abord le groupe masqué à l'élément "nav-menu" (cela masquera les éléments du menu).

<ul id="nav-menu" class="hidden w-full space-y-2 pr-3 font-semibold text-xl text-right">
	...
</ul>

Pour le montrer à nouveau, nous allons créer une fonction qui s'exécute lorsque nous cliquons sur l'icône de navigation.

Pour cela, nous allons ajouter les lignes suivantes au main.js :

document.getElementById("nav-icon").addEventListener('click',
  () => document.getElementById("nav-menu").classList.toggle("hidden")
);

Avec cela en place, nous pouvons maintenant ouvrir et fermer le menu de navigation. En conséquence, nous avons une barre de navigation fonctionnelle pour mobile !

Création d'une barre de navigation fixe pour les ordinateurs de bureau

Pour rendre la barre de navigation réactive et avoir une belle apparence sur les ordinateurs de bureau, nous devons également modifier les styles de certains points d'arrêt. Dans les prochaines étapes, nous devons mettre à jour l'icône de navigation et le menu de navigation.

Commençons par masquer l'icône de navigation car nous n'en avons pas besoin sur les appareils de bureau :

<div id="nav-icon" class="h-12 w-12 p-2 group md:hidden">
  ...
</div>

Et puis, nous ajoutons les classes suivantes sur le bureau pour faire apparaître les éléments de menu dans une rangée et avec un certain espacement :

<ul id="nav-menu" class="hidden w-full space-y-2 pr-3 font-semibold text-xl text-right md:h-12 md:flex md:flex-row md:items-center md:justify-end md:space-x-5 md:space-y-0">
	...
</ul>

Cela se traduit par le résultat suivant :

Barre de navigation fixe avec Tailwind CSS : Bureau

Quelques personnalisations pour la barre de navigation fixe

Déplacer l'icône de navigation

Pour déplacer l'icône de navigation, vous devez modifier le conteneur de navigation. Dans le conteneur, vous devez modifier la classe items-[…] pour l'adapter au style souhaité. Par exemple, vous pouvez le déplacer vers le centre comme ceci :

<div id="nav-container" class="bg-white p-2 flex flex-col items-center md:flex-row">
	...
</div>

En résulte ceci :

Barre de navigation fixe avec CSS Tailwind : icône de navigation centrée

Déplacer les éléments du menu

Vous pouvez également déplacer les éléments de menu en mettant à jour la classe text-[…]. Par exemple, vous pouvez centrer les éléments de menu comme ceci :

<ul id="nav-menu" class="hidden w-full space-y-2 pr-3 font-semibold text-xl text-center md:h-12 md:flex md:flex-row md:items-center md:justify-end md:space-x-5 md:space-y-0">
	...
</ul>

En résulte ceci :

Barre de navigation fixe avec Tailwind CSS : éléments de menu déplacés

Créer un effet de survol pour les éléments de menu

Vous pouvez ajouter un effet de survol aux éléments de menu en ajoutant la classe suivante aux éléments de liste :

<li class="hover:text-blue-500">Menu Item</li>

Résultant en cet effet de survol :

Barre de navigation fixe avec Tailwind CSS : effet de survol de l'élément de menu

Conclusion

Dans cet article, nous avons appris à créer une barre de navigation fixe avec CSS tailwind. De plus, pour coller au top, il est également responsive et facilement modifiable selon vos préférences.

Source de l'article original sur https://www.programonaut.com

#tailwindcss #tailwind #webdev #css

What is Tailwind CSS useful for?

Let’s talk Tailwind.css. It’s new, it’s customizable, it’s responsive, and a utility-first CSS framework, let’s try it out!

The beauty of Tailwind.css is that to use it is pretty simple. In good old basic CSS, you will declare classes and specifically code the design elements that will come from the class you’ve created(i.e. Font-family, color). In Tailwind, you also use class names, but different from classic CSS, you pull from a pre-created library of class names that you will have access to once you download Tailwind into your app. It’s a matter of understanding what class names you’ll need, and that is going to take a little searching on the Tailwind Docs Website search bar. Nothing like a handy search!

#tailwind-ui #css #web-development #tailwind-css