Attachment
Des cartes de fichiers et d’images pour les uploads, avec progression, états, actions, un déclencheur sur toute la carte et des noms qui conservent leur extension.
pnpm dlx shadcn@latest add https://hextaui.com/r/attachment.jsonAjoute le composant, les tokens de thème HextaUI et les composants HextaUI dont il dépend.
Ajoutez les tokens de thème à votre CSS global, si ce n’est pas déjà fait.
Installez les dépendances.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnCopiez et collez le code suivant dans votre projet.
components/ui/attachment.tsx components/ui/aspect-ratio.tsx components/ui/skeleton.tsx components/ui/button.tsx components/ui/progress.tsx components/ui/scroll-area.tsx lib/motion.ts Mettez à jour les chemins d’import selon la configuration de votre projet.
Image
variant="image" sur <AttachmentMedia /> affiche une miniature carrée qui scintille pendant son chargement. Définissez orientation="vertical" pour une tuile avec l’aperçu au-dessus.
États
state couvre tout le cycle d’envoi. Les fichiers inactifs ont une bordure en pointillés, les titres en cours d’envoi et de traitement scintillent, et les erreurs colorent le média et la description en rouge.
Flux d’envoi
Passez progress pendant state="uploading" pour dessiner une fine barre de progression le long du bord inférieur. Les aperçus d’image restent atténués jusqu’à la fin de l’envoi.
Tailles
size met à l’échelle ensemble le remplissage, le média et le texte, et garde les coins intérieurs concentriques à la carte.
Noms longs
Les noms de fichier longs sont tronqués avant l’extension, pour que l’on distingue toujours un .pdf d’un .zip. Survolez le nom pour le lire en entier.
Groupe
<AttachmentGroup /> dispose les pièces jointes sur une ligne défilant horizontalement, avec des bords qui s’estompent et des éléments qui s’alignent par accroche.
Déclencheur
<AttachmentTrigger /> rend toute la carte cliquable, par exemple pour ouvrir un aperçu ou un lien, tandis que ses actions restent indépendantes. Donnez-lui un aria-label.
Champ de saisie
Les fichiers ajoutés après le chargement de la page apparaissent avec un effet pop, et le reste de la ligne glisse en douceur lorsqu’un fichier est retiré.
Image cassée
Si l’aperçu d’une image ne se charge pas, il se rabat sur une icône de fichier.
Stress
Noms hostiles dans une colonne étroite, un état aléatoire toutes les 60 ms et 200 éléments dans un même groupe. Rien ne déborde et la mise en page tient.
De droite à gauche
Le média, le contenu et les actions s’inversent, et la barre de progression se remplit depuis le bord de départ.
| Touche | Action |
|---|---|
| Tab | Déplace le focus vers chaque action, puis vers le déclencheur. Les actions masquées sur les tuiles verticales apparaissent lorsque le focus entre dans la carte. |
| EnterSpace | Active l’action ou le déclencheur ayant le focus. |
- Pendant l’envoi, la barre de progression a
role="progressbar"et est étiquetée par le titre. - Les titres de type chaîne reçoivent un attribut
titleavec le nom complet, pour que les noms tronqués restent lisibles. - Les actions réduites à une icône et le déclencheur n’ont pas de texte : donnez-leur toujours un
aria-labelqui nomme le fichier. - Avec la réduction des animations, les pièces jointes apparaissent sans effet pop, la barre de progression saute au lieu de s’adoucir et les groupes se réagencent instantanément.
Chaque partie, sauf le groupe, l’action et le déclencheur, rend un élément simple et accepte ses attributs. Les styles de la carte sont exportés sous attachmentVariants.
| Prop | Type | Par défaut |
|---|---|---|
state | "idle" | "uploading" | "processing" | "error" | "done" | "done" |
progressDe 0 à 100. Affiché uniquement pendant l’envoi, et borné à cette plage. | number | – |
size | "default" | "sm" | "xs" | "default" |
orientation | "horizontal" | "vertical" | "horizontal" |
| Attribut | Description |
|---|---|
data-slot="attachment" | Ciblez la carte en CSS. |
data-state | L’état actuel. |
data-size | La taille actuelle. |
data-orientation | L’orientation actuelle. |
data-slot="attachment-progress" | La barre de progression, présente pendant l’envoi. |
--attachment-radius | Le rayon des coins de la carte. Les coins intérieurs en sont dérivés. |
| Prop | Type | Par défaut |
|---|---|---|
variantimage enveloppe son enfant dans un AspectRatio carré avec une icône de fichier en repli. | "icon" | "image" | "icon" |
| Attribut | Description |
|---|---|
data-slot="attachment-media" | Ciblez le média en CSS. |
data-variant | La variante actuelle. |
Un <div> qui contient le titre et la description et occupe la largeur restante.
| Attribut | Description |
|---|---|
data-slot="attachment-content" | Ciblez le contenu en CSS. |
| Prop | Type | Par défaut |
|---|---|---|
childrenUne chaîne est découpée pour que son extension ne soit jamais tronquée. | ReactNode | – |
titleAffiché au survol. Par défaut, le nom complet. | string | children |
idGénéré si omis. La barre de progression en tire son étiquette. | string | – |
| Attribut | Description |
|---|---|
data-slot="attachment-title" | Ciblez le titre en CSS. |
Un <span> sur une seule ligne pour la taille, le type ou le statut. Il est tronqué lorsque la place manque.
| Attribut | Description |
|---|---|
data-slot="attachment-description" | Ciblez la description en CSS. |
Un <div> pour les boutons d’action. Sur les tuiles verticales, il flotte au-dessus de l’aperçu et, sur les appareils avec souris, apparaît au survol ou au focus.
| Attribut | Description |
|---|---|
data-slot="attachment-actions" | Ciblez les actions en CSS. |
Un <Button /> placé au-dessus du déclencheur. Il accepte toutes les props de Button.
| Prop | Type | Par défaut |
|---|---|---|
variant | ButtonVariant | "ghost" |
size | ButtonSize | "icon-xs" |
| Attribut | Description |
|---|---|
data-slot="attachment-action" | Ciblez les actions en CSS. |
Une couche invisible qui recouvre toute la carte. Composez-la avec un déclencheur de boîte de dialogue, un lien ou n’importe quel bouton.
| Prop | Type | Par défaut |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <button type="button"> |
| Attribut | Description |
|---|---|
data-slot="attachment-trigger" | Ciblez le déclencheur en CSS. |
Un <ScrollArea /> horizontal qui s’accroche à chaque pièce jointe et anime la ligne lorsque des éléments sont ajoutés ou retirés. Il accepte toutes les props de ScrollArea sauf scrollbars.
| Attribut | Description |
|---|---|
data-slot="attachment-group" | Ciblez le groupe en CSS. |
data-slot="attachment-group-list" | La ligne qui contient les pièces jointes. |
- Aspect ratioUne boîte qui garde sa forme avant le chargement du média, scintille pendant le chargement, fait apparaître le média en fondu et bascule sur un repli en cas d’échec.
- ButtonDes boutons dans toutes les variantes et tailles, avec un flux de chargement, de succès et d’erreur intégré qui évite le spinner pour les requêtes rapides.
- MotionLes courbes d’easing, les durées et la vérification de réduction des animations utilisées par chaque composant, ainsi que des hooks pour les morphs de taille et les surlignages glissants.
- ProgressUne barre ou un anneau qui montre l’avancement d’une tâche, s’adoucit entre les mises à jour et glisse tant que le total est inconnu.
- Scroll areaUn défilement natif avec une barre de défilement minimale, des bords qui s’estompent seulement là où il y a plus à voir, et un aperçu facultatif qui coupe le dernier élément en deux.
- BubbleDes bulles de messages de chat avec variantes, coins groupés, réactions et place pour du contenu interactif.
Utilisé dans les blocks
Des blocks qui s’appuient sur Attachment.
- Prompt InputUn champ de discussion qui démarre sur une seule ligne sobre, grandit en carte à mesure que vous écrivez et descend une fois la conversation lancée. Entrée envoie, sans risque avec la saisie japonaise et chinoise. Collez, déposez ou choisissez des fichiers avec aperçus, progression et nouvel essai. @ ajoute des fichiers et / lance des commandes depuis un menu au niveau du curseur. Un sélecteur de modèle avec touches numériques, un curseur d’effort qui s’anime à Max, un anneau de contexte, la dictée avec forme d’onde en direct, des chips d’outils, une file pour les messages saisis pendant qu’une réponse arrive en streaming, et des brouillons qui survivent à un rechargement.
- Chat ThreadToute la conversation autour du champ de saisie. Votre question reste épinglée en haut pendant que la réponse arrive en streaming, des points de repère sur le côté permettent de sauter d’un message à l’autre, et chaque réponse peut être copiée, modifiée, relancée, notée et basculée entre versions. Les réponses affichent du Markdown avec blocs de code, tableaux et citations, et la réflexion, les appels d’outils et Prompt Input s’y insèrent directement.