{"_id":"@buillaume.biondo/fab-ui","_rev":"3-23e4ca160434cf610ec941f293936746","name":"@buillaume.biondo/fab-ui","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@buillaume.biondo/fab-ui","version":"0.1.0","_id":"@buillaume.biondo/fab-ui@0.1.0","maintainers":[{"name":"buillaume.biondo","email":"guillaume.biondo@gmail.com"}],"dist":{"shasum":"da55dd13a53e3ba3b03cb48981f40c4fbaf74d72","tarball":"https://registry.npmjs.org/@buillaume.biondo/fab-ui/-/fab-ui-0.1.0.tgz","fileCount":217,"integrity":"sha512-2wIqKzeOC55vNoZBPqqduGGpyMUFo409nz+ktTz2LyVxg4cuy/Uctnwa6X1K5FDysWwHeVVatVIF3xc3fNQprw==","signatures":[{"sig":"MEUCIQDn9FzL4ZmTtlpLXhleL1cwP1MCeNBvF2cKQ/mfvAXVgwIgc6vguc3gFD9V03Er0Yk6t+KXOLJiUQj6nDMjAkIYwFg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4209491},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./style":"./dist/style.css"},"gitHead":"3865486a30c201d61b61ebdecd77317cd71f6dd4","scripts":{"dev":"vite build --watch","build":"vite build","prepare":"npm run build"},"_npmUser":{"name":"buillaume.biondo","email":"guillaume.biondo@gmail.com"},"_npmVersion":"11.6.2","description":"> Bibliothèque de composants Vue 3 construite sur **Reka UI**, **Tailwind CSS v4** et **class-variance-authority**. Chaque composant respecte un système de design cohérent basé sur des CSS custom properties (`--color-*`, `--radius-*`, `--shadow-*`).","directories":{},"_nodeVersion":"22.17.0","dependencies":{"clsx":"^2.1.1","highlight.js":"^11.0.0","tailwind-merge":"^3.2.0"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.13","vite":"^7.0.0","typescript":"^5.2.0","vue-input-otp":"^0.3.2","vite-plugin-dts":"^4.0.0","@vitejs/plugin-vue":"^6.0.0"},"peerDependencies":{"vue":"^3.5.0","reka-ui":"^2.0.0","tailwindcss":"^4.0.0","@vueuse/core":"^12.0.0","vue-input-otp":"^0.3.0","lucide-vue-next":">=0.400.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"vue-input-otp":{"optional":true},"@inertiajs/vue3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/fab-ui_0.1.0_1772382662320_0.16884801139577088","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@buillaume.biondo/fab-ui","version":"0.1.1","_id":"@buillaume.biondo/fab-ui@0.1.1","maintainers":[{"name":"buillaume.biondo","email":"guillaume.biondo@gmail.com"}],"dist":{"shasum":"b2a35a6611eeef54f3ee32385cace91b0262b0d5","tarball":"https://registry.npmjs.org/@buillaume.biondo/fab-ui/-/fab-ui-0.1.1.tgz","fileCount":227,"integrity":"sha512-yu+hd7jUDiLNL1kxmCNhse3hbAptZiEUpu1944NWKI/Ohi1klXJ3MSyOpXJP77PWEdN3IQvAI4zzsFKZgMRGcQ==","signatures":[{"sig":"MEYCIQCgIv0l65sW3tsy1PwyZCp0XmhWEZ7Zv+BvNkkTeoRBswIhAJjyzcxuRL4dUS3DzIyjMEm2ZoIt1tkMytDCo2bBROG/","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4291644},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./style":"./dist/style.css"},"gitHead":"58cca40003e4e693db114d650c6dfddf9d07040a","scripts":{"dev":"vite build --watch","build":"vite build","prepare":"npm run build"},"_npmUser":{"name":"buillaume.biondo","email":"guillaume.biondo@gmail.com"},"_npmVersion":"11.6.2","description":"> Bibliothèque de composants Vue 3 construite sur **Reka UI**, **Tailwind CSS v4** et **class-variance-authority**. Chaque composant respecte un système de design cohérent basé sur des CSS custom properties (`--color-*`, `--radius-*`, `--shadow-*`).","directories":{},"_nodeVersion":"22.17.0","dependencies":{"clsx":"^2.1.1","highlight.js":"^11.0.0","tailwind-merge":"^3.2.0"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.13","vite":"^7.0.0","typescript":"^5.2.0","vue-input-otp":"^0.3.2","vite-plugin-dts":"^4.0.0","@vitejs/plugin-vue":"^6.0.0"},"peerDependencies":{"vue":"^3.5.0","reka-ui":"^2.0.0","tailwindcss":"^4.0.0","@vueuse/core":"^12.0.0","vue-input-otp":"^0.3.0","lucide-vue-next":">=0.400.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"vue-input-otp":{"optional":true},"@inertiajs/vue3":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/fab-ui_0.1.1_1772397846427_0.029120532214631067","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@buillaume.biondo/fab-ui","version":"0.1.2","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","license":"MIT","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./style":"./dist/style.css"},"scripts":{"build":"vite build","dev":"vite build --watch","prepare":"npm run build"},"peerDependencies":{"@vueuse/core":"^12.0.0","class-variance-authority":"^0.7.0","lucide-vue-next":">=0.400.0","reka-ui":"^2.0.0","tailwindcss":"^4.0.0","vue":"^3.5.0","vue-input-otp":"^0.3.0"},"peerDependenciesMeta":{"@inertiajs/vue3":{"optional":true},"vue-input-otp":{"optional":true}},"dependencies":{"clsx":"^2.1.1","highlight.js":"^11.0.0","tailwind-merge":"^3.2.0"},"devDependencies":{"@vitejs/plugin-vue":"^6.0.0","typescript":"^5.2.0","vite":"^7.0.0","vite-plugin-dts":"^4.0.0","vue":"^3.5.13","vue-input-otp":"^0.3.2"},"gitHead":"09ed114f8ad5eda387f7992f5c9705fd33f32028","_id":"@buillaume.biondo/fab-ui@0.1.2","description":"> Bibliothèque de composants Vue 3 construite sur **Reka UI**, **Tailwind CSS v4** et **class-variance-authority**. Chaque composant respecte un système de design cohérent basé sur des CSS custom properties (`--color-*`, `--radius-*`, `--shadow-*`).","_nodeVersion":"22.17.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-Ab0zTBEOdzP0oKHGkmUhFKrRYgyHsHBvUJG2SVgZfQ0EZjOgeYoz24n+9nXc2lE3IjjFR70EdzDv5BTa/2njvg==","shasum":"c34c02bd02103b395a4885fc51838aa0821246ba","tarball":"https://registry.npmjs.org/@buillaume.biondo/fab-ui/-/fab-ui-0.1.2.tgz","fileCount":203,"unpackedSize":805947,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDQd8LHYPnfEuQ4j+Ct5mBICqfMMuwmIp/unbw4HiDXigIgEhBiNuelGNWJgRKXa1id6vk2zIirWPCYNJ4+ra7owtg="}]},"_npmUser":{"name":"buillaume.biondo","email":"guillaume.biondo@gmail.com"},"directories":{},"maintainers":[{"name":"buillaume.biondo","email":"guillaume.biondo@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/fab-ui_0.1.2_1772486617560_0.9774369786375832"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-01T16:31:02.163Z","modified":"2026-03-02T21:23:37.847Z","0.1.0":"2026-03-01T16:31:02.570Z","0.1.1":"2026-03-01T20:44:06.651Z","0.1.2":"2026-03-02T21:23:37.706Z"},"description":"> Bibliothèque de composants Vue 3 construite sur **Reka UI**, **Tailwind CSS v4** et **class-variance-authority**. Chaque composant respecte un système de design cohérent basé sur des CSS custom properties (`--color-*`, `--radius-*`, `--shadow-*`).","maintainers":[{"name":"buillaume.biondo","email":"guillaume.biondo@gmail.com"}],"readme":"# FAB-UI\n\n> Bibliothèque de composants Vue 3 construite sur **Reka UI**, **Tailwind CSS v4** et **class-variance-authority**. Chaque composant respecte un système de design cohérent basé sur des CSS custom properties (`--color-*`, `--radius-*`, `--shadow-*`).\n\n---\nnpm require @buillaume.biondo/fab-ui@0.1.0\n---\n\n## Sommaire\n\n1. [Types globaux](#1-types-globaux)\n2. [Inputs](#2-inputs)\n   - [Input](#input)\n   - [InputPassword](#inputpassword)\n   - [InputNumber](#inputnumber)\n   - [InputArea](#inputarea)\n   - [Checkbox](#checkbox)\n3. [Boutons & Actions](#3-boutons--actions)\n   - [Button](#button)\n   - [SplitButton](#splitbutton)\n   - [Toggle](#toggle)\n   - [ToggleSwitch](#toggleswitch)\n4. [Badge & Avatar](#4-badge--avatar)\n   - [Badge](#badge)\n   - [OverlayBadge](#overlaybadge)\n   - [Avatar](#avatar)\n5. [Feedback](#5-feedback)\n   - [Alert](#alert)\n   - [Toast — useToast()](#toast--usetoast)\n   - [ProgressBar](#progressbar)\n6. [Contenu](#6-contenu)\n   - [Card + CardHeader](#card--cardheader)\n   - [Code](#code)\n   - [Image](#image)\n   - [Carousel + CarouselSlide](#carousel--carouselslide)\n7. [Formulaires étendus](#7-formulaires-étendus)\n   - [Slider](#slider)\n8. [Overlays & Navigation](#8-overlays--navigation)\n   - [ContextMenu](#contextmenu)\n   - [CollapsibleContent](#collapsiblecontent)\n   - [SpeedDial](#speeddial)\n9. [Données](#9-données)\n   - [DataTable](#datatable)\n\n---\n\n## 1. Types globaux\n\nCes types TypeScript sont définis dans `/resources/js/types/ui-kit.ts` et utilisés par la quasi-totalité des composants.\n\n### Severity\n\nCouleur sémantique commune à Button, Badge, Alert, Card, ProgressBar, Toggle, Slider…\n\n```ts\ntype Severity =\n  | 'primary'    // couleur d'action principale du design system\n  | 'secondary'  // ton neutre / gris\n  | 'success'    // vert\n  | 'danger'     // rouge\n  | 'info'       // bleu\n  | 'warning'    // orange\n  | 'help'       // violet\n  | 'contrast'   // contraste fort (noir/blanc selon le thème)\n```\n\n### Size\n\nTaille standardisée commune à Button, Badge, Avatar, Input…\n\n```ts\ntype Size = 'tiny' | 'small' | 'default' | 'large' | 'extra'\n```\n\n### ButtonShape\n\nForme visuelle du composant Button (et SplitButton).\n\n```ts\ntype ButtonShape = 'plain' | 'outline' | 'ghost' | 'link'\n```\n\n### BadgeShape\n\nForme visuelle du composant Badge.\n\n```ts\ntype BadgeShape = 'filled' | 'outline' | 'ghost'\n```\n\n---\n\n## 2. Inputs\n\n### Input\n\nChamp de texte de base. Supporte `v-model`, validation (`error`), désactivation et l'attribut HTML `type`.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| `modelValue` | `string \\| number` | — | Valeur liée via `v-model` |\n| `defaultValue` | `string \\| number` | — | Valeur initiale non contrôlée |\n| `type` | `string` | `'text'` | Type HTML de l'input (`text`, `email`, `search`, `file`…) |\n| `placeholder` | `string` | — | Texte indicatif |\n| `disabled` | `boolean` | `false` | Désactive le champ |\n| `required` | `boolean` | `false` | Marque le champ comme requis |\n| **`error`** | `string` | — | Si défini, applique le style d'erreur (`aria-invalid`) |\n| `class` | `string` | — | Classes Tailwind additionnelles |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| `update:modelValue` | `string \\| number` | Émis à chaque frappe |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { Input } from 'fab-ui'\n\nconst email = ref('')\n</script>\n\n<template>\n  <Input v-model=\"email\" type=\"email\" placeholder=\"votre@email.com\" />\n</template>\n```\n\n```vue\n<!-- Avec erreur de validation -->\n<Input\n  v-model=\"email\"\n  type=\"email\"\n  placeholder=\"votre@email.com\"\n  :error=\"errors.email\"\n/>\n<p v-if=\"errors.email\" class=\"text-sm text-red-500 mt-1\">{{ errors.email }}</p>\n```\n\n---\n\n### InputPassword\n\nChamp mot de passe avec bouton bascule pour afficher/masquer la valeur. Mêmes styles et comportements que `Input`.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| `modelValue` | `string` | — | Valeur liée via `v-model` |\n| `defaultValue` | `string` | — | Valeur initiale non contrôlée |\n| `placeholder` | `string` | — | Texte indicatif |\n| `disabled` | `boolean` | `false` | Désactive le champ |\n| `required` | `boolean` | `false` | Marque le champ comme requis |\n| **`error`** | `string` | — | Active le style d'erreur |\n| `class` | `string` | — | Classes additionnelles sur le wrapper |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| `update:modelValue` | `string` | Émis à chaque frappe |\n\n#### Exemple\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { InputPassword } from 'fab-ui'\n\nconst password = ref('')\n</script>\n\n<template>\n  <InputPassword v-model=\"password\" placeholder=\"Mot de passe\" />\n</template>\n```\n\n---\n\n### InputNumber\n\nChamp numérique avec boutons `−` et `+` intégrés. Gère `min`, `max` et `step`. Les boutons se désactivent automatiquement aux bornes.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| `modelValue` | `number` | — | Valeur liée via `v-model` |\n| `defaultValue` | `number` | — | Valeur initiale non contrôlée |\n| `placeholder` | `string` | — | Texte indicatif |\n| `disabled` | `boolean` | `false` | Désactive le champ et les boutons |\n| `required` | `boolean` | `false` | Marque le champ comme requis |\n| **`error`** | `string` | — | Active le style d'erreur |\n| **`min`** | `number` | — | Valeur minimale |\n| **`max`** | `number` | — | Valeur maximale |\n| **`step`** | `number` | `1` | Incrément des boutons +/− |\n| **`controls`** | `boolean` | `true` | Affiche ou masque les boutons +/− |\n| `class` | `string` | — | Classes additionnelles sur le wrapper |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| `update:modelValue` | `number` | Émis à chaque changement |\n\n#### Exemple\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { InputNumber } from 'fab-ui'\n\nconst quantity = ref(1)\n</script>\n\n<template>\n  <!-- Quantité entre 1 et 99 -->\n  <InputNumber v-model=\"quantity\" :min=\"1\" :max=\"99\" />\n\n  <!-- Sans boutons (champ seul) -->\n  <InputNumber v-model=\"quantity\" :controls=\"false\" />\n</template>\n```\n\n---\n\n### InputArea\n\nZone de texte multiligne (`textarea`) avec compteur de caractères optionnel.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| `modelValue` | `string` | — | Valeur liée via `v-model` |\n| `defaultValue` | `string` | — | Valeur initiale non contrôlée |\n| `placeholder` | `string` | — | Texte indicatif |\n| `disabled` | `boolean` | `false` | Désactive le champ |\n| `required` | `boolean` | `false` | Marque le champ comme requis |\n| **`error`** | `string` | — | Active le style d'erreur |\n| `rows` | `number` | `3` | Nombre de lignes visibles |\n| `cols` | `number` | — | Largeur fixe en colonnes (mode `inline`) |\n| **`maxlength`** | `number` | — | Limite de caractères — affiche un compteur |\n| **`resize`** | `'none' \\| 'vertical' \\| 'horizontal' \\| 'both'` | `'vertical'` | Comportement du redimensionnement |\n| `class` | `string` | — | Classes additionnelles sur le wrapper |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| `update:modelValue` | `string` | Émis à chaque frappe |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { InputArea } from 'fab-ui'\n\nconst bio = ref('')\n</script>\n\n<template>\n  <InputArea v-model=\"bio\" placeholder=\"Votre biographie…\" :rows=\"5\" />\n</template>\n```\n\n```vue\n<!-- Avec limite de caractères et resize désactivé -->\n<InputArea\n  v-model=\"bio\"\n  :maxlength=\"280\"\n  resize=\"none\"\n  placeholder=\"Bio (280 car. max)\"\n/>\n```\n\n---\n\n### Checkbox\n\nCase à cocher accessible, construite sur `CheckboxRoot` de Reka UI. Compatible avec `v-model` (prop `checked` / événement `update:checked`).\n\n#### Props\n\nHérite de `CheckboxRootProps` (Reka UI) :\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`checked`** / `v-model` | `boolean \\| 'indeterminate'` | — | État de la case |\n| `defaultChecked` | `boolean` | — | État initial non contrôlé |\n| `disabled` | `boolean` | `false` | Désactive la case |\n| `required` | `boolean` | `false` | Marque comme requis dans un formulaire |\n| `name` | `string` | — | Nom pour la soumission de formulaire |\n| `value` | `string` | — | Valeur pour la soumission de formulaire |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | Icône custom à la place de la coche par défaut |\n\n#### Exemple\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { Checkbox } from 'fab-ui'\n\nconst accepted = ref(false)\n</script>\n\n<template>\n  <label class=\"flex items-center gap-2 cursor-pointer\">\n    <Checkbox v-model:checked=\"accepted\" />\n    <span>J'accepte les conditions d'utilisation</span>\n  </label>\n</template>\n```\n\n---\n\n## 3. Boutons & Actions\n\n### Button\n\nBouton polyvalent avec 8 variantes de couleur, 4 formes, 5 tailles et des options d'icônes, de badge et de chargement. Basé sur `Primitive` de Reka UI pour pouvoir être rendu en `<a>`, `<router-link>`, etc.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`variant`** | `Severity` | `'primary'` | Couleur sémantique |\n| **`shape`** | `ButtonShape` | `'plain'` | Forme visuelle (`plain`, `outline`, `ghost`, `link`) |\n| **`size`** | `Size` | `'default'` | Taille (`tiny`, `small`, `default`, `large`, `extra`) |\n| `fluid` | `boolean` | `false` | Prend toute la largeur disponible (`w-full`) |\n| `rounded` | `boolean` | `false` | Coins complètement arrondis (`rounded-full`) |\n| **`icon`** | `boolean` | `false` | Mode icône seule — bouton carré (ou cercle si `rounded`) |\n| **`loading`** | `boolean` | `false` | Affiche un spinner et désactive le bouton |\n| `disabled` | `boolean` | `false` | Désactive le bouton |\n| **`badge`** | `number \\| null` | `null` | Badge numérique en overlay coin supérieur droit (max `99+`) |\n| **`iconLeft`** | `Component` | — | Composant icône affiché à gauche du texte |\n| **`iconRight`** | `Component` | — | Composant icône affiché à droite du texte |\n| `as` | `string \\| Component` | `'button'` | Élément ou composant HTML rendu |\n| `asChild` | `boolean` | `false` | Passe les props à l'enfant direct (pattern Reka UI) |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | Contenu du bouton (texte, icône principale) |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { Button } from 'fab-ui'\nimport { SaveIcon, TrashIcon } from 'lucide-vue-next'\n</script>\n\n<template>\n  <!-- Variantes de forme -->\n  <Button variant=\"primary\" shape=\"plain\">Enregistrer</Button>\n  <Button variant=\"danger\" shape=\"outline\">Supprimer</Button>\n  <Button variant=\"secondary\" shape=\"ghost\">Annuler</Button>\n  <Button variant=\"primary\" shape=\"link\">En savoir plus</Button>\n</template>\n```\n\n```vue\n<!-- Tailles, icônes, loading, badge -->\n<Button size=\"small\" :icon-left=\"SaveIcon\">Sauvegarder</Button>\n<Button size=\"large\" variant=\"danger\" :icon-left=\"TrashIcon\" shape=\"outline\">Supprimer</Button>\n<Button :loading=\"isSaving\" variant=\"primary\">Traitement…</Button>\n<Button :badge=\"5\" variant=\"primary\">Notifications</Button>\n\n<!-- Mode icône seule -->\n<Button :icon=\"true\" variant=\"ghost\" rounded :aria-label=\"'Supprimer'\">\n  <TrashIcon />\n</Button>\n\n<!-- Rendu en lien -->\n<Button as=\"a\" href=\"/dashboard\" variant=\"secondary\" shape=\"ghost\">\n  Tableau de bord\n</Button>\n```\n\n---\n\n### SplitButton\n\nBouton divisé : partie gauche déclenchable et partie droite ouvrant un menu déroulant. Partage les mêmes variantes que `Button`.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`variant`** | `Severity` | `'primary'` | Couleur sémantique |\n| **`shape`** | `'plain' \\| 'outline' \\| 'ghost'` | `'plain'` | Forme visuelle |\n| **`size`** | `Size` | `'default'` | Taille |\n| `fluid` | `boolean` | `false` | Prend toute la largeur disponible |\n| `disabled` | `boolean` | `false` | Désactive les deux parties |\n| `loading` | `boolean` | `false` | Affiche un spinner dans la partie principale |\n| **`iconLeft`** | `Component` | — | Icône à gauche du label dans la partie principale |\n| `class` | `string` | — | Classes additionnelles sur le wrapper |\n\n#### Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | Contenu de la partie principale (texte) |\n| **`dropdown`** | Items du menu déroulant (utiliser `DropdownMenuItem`) |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| `click` | `MouseEvent` | Clic sur la partie principale du bouton |\n\n#### Exemple\n\n```vue\n<script setup lang=\"ts\">\nimport { SplitButton } from 'fab-ui'\nimport { DropdownMenuItem } from 'fab-ui'\nimport { SaveIcon } from 'lucide-vue-next'\n\nfunction save() { /* ... */ }\nfunction saveAsDraft() { /* ... */ }\nfunction saveAndPublish() { /* ... */ }\n</script>\n\n<template>\n  <SplitButton variant=\"primary\" :icon-left=\"SaveIcon\" @click=\"save\">\n    Enregistrer\n    <template #dropdown>\n      <DropdownMenuItem @click=\"saveAsDraft\">Sauvegarder comme brouillon</DropdownMenuItem>\n      <DropdownMenuItem @click=\"saveAndPublish\">Sauvegarder et publier</DropdownMenuItem>\n    </template>\n  </SplitButton>\n</template>\n```\n\n---\n\n### Toggle\n\nBouton à bascule (actif/inactif) basé sur `Toggle` de Reka UI. Peut afficher un texte différent selon l'état via `onValue` / `offValue`.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`variant`** | `Severity` | `'primary'` | Couleur quand l'état est `on` |\n| **`shape`** | `'ghost' \\| 'outline' \\| 'plain'` | `'ghost'` | Forme visuelle |\n| **`size`** | `Size` | `'default'` | Taille |\n| `pressed` | `boolean` | — | État contrôlé |\n| `defaultPressed` | `boolean` | — | État initial non contrôlé |\n| `disabled` | `boolean` | `false` | Désactive le toggle |\n| **`onValue`** | `string` | — | Texte affiché quand `pressed = true` |\n| **`offValue`** | `string` | — | Texte affiché quand `pressed = false` |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | Contenu du toggle (icône, texte). Ignoré si `onValue`/`offValue` sont définis |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| `update:pressed` | `boolean` | Changement d'état |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { Toggle } from 'fab-ui'\nimport { BoldIcon } from 'lucide-vue-next'\n\nconst bold = ref(false)\n</script>\n\n<template>\n  <!-- Toggle icône (éditeur de texte) -->\n  <Toggle v-model:pressed=\"bold\" variant=\"primary\" shape=\"outline\" aria-label=\"Gras\">\n    <BoldIcon />\n  </Toggle>\n</template>\n```\n\n```vue\n<!-- Toggle texte contextuel -->\n<Toggle\n  v-model:pressed=\"isPublic\"\n  variant=\"success\"\n  on-value=\"Public\"\n  off-value=\"Privé\"\n  shape=\"plain\"\n/>\n```\n\n---\n\n### ToggleSwitch\n\nInterrupteur de type switch (on/off) avec label intégré. Tailles limitées à `small`, `default`, `large`.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`variant`** | `Severity` | `'primary'` | Couleur quand le switch est actif |\n| **`size`** | `'small' \\| 'default' \\| 'large'` | `'default'` | Taille |\n| **`label`** | `string` | — | Libellé affiché à droite du switch |\n| `checked` | `boolean` | — | État contrôlé |\n| `defaultChecked` | `boolean` | — | État initial non contrôlé |\n| `disabled` | `boolean` | `false` | Désactive le switch |\n| `required` | `boolean` | `false` | Marque comme requis |\n| `name` | `string` | — | Nom pour la soumission de formulaire |\n| `class` | `string` | — | Classes additionnelles sur le wrapper |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| `update:checked` | `boolean` | Changement d'état |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { ToggleSwitch } from 'fab-ui'\n\nconst notifications = ref(true)\n</script>\n\n<template>\n  <ToggleSwitch v-model:checked=\"notifications\" label=\"Notifications push\" />\n</template>\n```\n\n```vue\n<!-- Variantes de couleur et taille -->\n<ToggleSwitch v-model:checked=\"darkMode\" variant=\"contrast\" label=\"Mode sombre\" size=\"large\" />\n<ToggleSwitch v-model:checked=\"analytics\" variant=\"info\" label=\"Analytics\" size=\"small\" />\n```\n\n---\n\n## 4. Badge & Avatar\n\n### Badge\n\nPastille d'étiquette inline avec 8 couleurs, 3 formes et 5 tailles. Supporte un point coloré décoratif via `dot`.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`variant`** | `Severity` | `'primary'` | Couleur sémantique |\n| **`shape`** | `BadgeShape` | `'filled'` | Forme (`filled`, `outline`, `ghost`) |\n| **`size`** | `Size` | `'default'` | Taille |\n| `fluid` | `boolean` | `false` | Prend toute la largeur disponible |\n| **`dot`** | `boolean` | `false` | Affiche un petit point coloré à gauche du texte |\n| `as` | `string \\| Component` | `'span'` | Élément HTML rendu |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | Contenu du badge (texte) |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { Badge } from 'fab-ui'\n</script>\n\n<template>\n  <!-- Formes -->\n  <Badge variant=\"success\" shape=\"filled\">Actif</Badge>\n  <Badge variant=\"warning\" shape=\"outline\">En attente</Badge>\n  <Badge variant=\"danger\" shape=\"ghost\">Erreur</Badge>\n\n  <!-- Avec point -->\n  <Badge variant=\"success\" dot>En ligne</Badge>\n\n  <!-- Tailles -->\n  <Badge size=\"tiny\" variant=\"info\">Tiny</Badge>\n  <Badge size=\"extra\" variant=\"primary\">Extra</Badge>\n</template>\n```\n\n---\n\n### OverlayBadge\n\nEnveloppe positionnant un badge numérique (ou un point) en overlay par rapport à son enfant. Typiquement utilisé sur une icône ou un avatar.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`value`** | `number \\| null` | — | Nombre affiché. `0`, `null` ou absent = point. Max affiché : `99+` |\n| **`variant`** | `Severity` | `'danger'` | Couleur du badge |\n| **`position`** | `'top-right' \\| 'top-left' \\| 'bottom-right' \\| 'bottom-left'` | `'top-right'` | Position de l'overlay |\n| `class` | `string` | — | Classes additionnelles sur le wrapper |\n\n#### Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | Élément cible (icône, avatar…) |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { OverlayBadge } from 'fab-ui'\nimport { BellIcon } from 'lucide-vue-next'\n</script>\n\n<template>\n  <!-- Badge numérique sur une icône -->\n  <OverlayBadge :value=\"12\" variant=\"danger\">\n    <BellIcon class=\"size-6\" />\n  </OverlayBadge>\n\n  <!-- Point indicateur (pas de valeur) -->\n  <OverlayBadge variant=\"success\">\n    <img src=\"/avatar.jpg\" class=\"size-8 rounded-full\" alt=\"Avatar\" />\n  </OverlayBadge>\n\n  <!-- Position personnalisée -->\n  <OverlayBadge :value=\"3\" position=\"bottom-right\" variant=\"primary\">\n    <BellIcon class=\"size-6\" />\n  </OverlayBadge>\n</template>\n```\n\n---\n\n### Avatar\n\nConteneur circulaire pour une image ou des initiales, basé sur `AvatarRoot` de Reka UI. Utiliser les sous-composants Reka UI `AvatarImage` et `AvatarFallback` à l'intérieur.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`size`** | `Size` | `'default'` | Taille (`tiny`=20px, `small`=28px, `default`=36px, `large`=48px, `extra`=64px) |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | `AvatarImage` + `AvatarFallback` de Reka UI |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { Avatar } from 'fab-ui'\nimport { AvatarImage, AvatarFallback } from 'reka-ui'\n</script>\n\n<template>\n  <!-- Avatar avec image et fallback initiales -->\n  <Avatar size=\"large\">\n    <AvatarImage src=\"/users/alice.jpg\" alt=\"Alice\" />\n    <AvatarFallback class=\"bg-blue-100 text-blue-700 font-semibold text-sm\">\n      AL\n    </AvatarFallback>\n  </Avatar>\n\n  <!-- Différentes tailles -->\n  <Avatar size=\"tiny\"><AvatarFallback>A</AvatarFallback></Avatar>\n  <Avatar size=\"small\"><AvatarFallback>AB</AvatarFallback></Avatar>\n  <Avatar size=\"extra\"><AvatarFallback>AB</AvatarFallback></Avatar>\n</template>\n```\n\n---\n\n## 5. Feedback\n\n### Alert\n\nBannière d'alerte avec icône, titre, fermeture et disparition automatique. Se monte/démonte avec une animation de transition.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`variant`** | `Severity` | `'info'` | Couleur sémantique |\n| `title` | `string` | — | Titre en gras affiché avant le corps |\n| **`dismissible`** | `boolean` | `false` | Affiche un bouton de fermeture × |\n| **`icon`** | `boolean` | `true` | Affiche l'icône par défaut selon la variante |\n| **`iconComponent`** | `Component` | — | Remplace l'icône par défaut par un composant Vue |\n| **`delay`** | `number` | `0` | Fermeture automatique après N millisecondes (`0` = désactivé) |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | Corps du message |\n| `icon` | Icône personnalisée (remplace `iconComponent`) |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| `close` | — | Émis quand l'alerte se ferme (bouton ou `delay`) |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { Alert } from 'fab-ui'\n</script>\n\n<template>\n  <Alert variant=\"success\" title=\"Enregistrement réussi\">\n    Vos modifications ont bien été sauvegardées.\n  </Alert>\n\n  <Alert variant=\"danger\" title=\"Erreur\" :dismissible=\"true\">\n    Une erreur est survenue lors de la connexion au serveur.\n  </Alert>\n</template>\n```\n\n```vue\n<!-- Auto-fermeture après 5 secondes -->\n<Alert variant=\"info\" :delay=\"5000\" @close=\"handleClose\">\n  Votre session expirera dans 5 minutes.\n</Alert>\n\n<!-- Icône personnalisée -->\n<Alert variant=\"warning\" :icon-component=\"ShieldIcon\" title=\"Sécurité\">\n  Activez l'authentification à deux facteurs.\n</Alert>\n```\n\n---\n\n### Toast — useToast()\n\nSystème de notifications toast global. Les toasts s'affichent via le composable `useToast()`. Maximum 3 toasts simultanés — le plus ancien est automatiquement retiré.\n\n#### Durées par défaut\n\n| Variante | Durée | Persistant |\n|----------|-------|-----------|\n| `info` | 4 000 ms | non |\n| `success` | 4 000 ms | non |\n| `warning` | 6 000 ms | non |\n| `error` | — | **oui** (doit être fermé manuellement) |\n\n#### API du composable\n\n```ts\nconst { toasts, success, warning, error, info, dismiss } = useToast()\n```\n\n| Méthode | Signature | Description |\n|---------|-----------|-------------|\n| **`success`** | `(message: string, options?: ToastOptions) => string` | Toast de succès |\n| **`warning`** | `(message: string, options?: ToastOptions) => string` | Toast d'avertissement |\n| **`error`** | `(message: string, options?: ToastOptions) => string` | Toast d'erreur persistant |\n| **`info`** | `(message: string, options?: ToastOptions) => string` | Toast informatif |\n| **`dismiss`** | `(id: string) => void` | Ferme un toast par son identifiant |\n| `toasts` | `Toast[]` réactif | Liste des toasts visibles |\n\n#### Type ToastOptions\n\n```ts\ntype ToastOptions = {\n  duration?:   number;   // Durée d'affichage en ms (écrase la valeur par défaut)\n  persistent?: boolean;  // true = ne se ferme pas automatiquement\n}\n```\n\n#### Exemple\n\n```vue\n<script setup lang=\"ts\">\nimport { useToast } from '@/composables/useToast'\n\nconst toast = useToast()\n\nasync function saveData() {\n  try {\n    await api.save()\n    toast.success('Données enregistrées avec succès.')\n  } catch (err) {\n    toast.error('Impossible d'enregistrer. Vérifiez votre connexion.')\n  }\n}\n\nfunction notify() {\n  const id = toast.info('Traitement en cours…', { persistent: true })\n  setTimeout(() => toast.dismiss(id), 3000)\n}\n</script>\n\n<template>\n  <button @click=\"saveData\">Enregistrer</button>\n  <button @click=\"toast.warning('Stock faible !', { duration: 8000 })\">Avertir</button>\n</template>\n```\n\n---\n\n### ProgressBar\n\nBarre de progression déterminée ou indéterminée (animation) avec label de pourcentage optionnel.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`value`** | `number` | — | Valeur de `0` à `100`. Si absent : mode **indéterminé** (animation de défilement) |\n| **`variant`** | `Severity` | `'primary'` | Couleur de la barre |\n| **`size`** | `'tiny' \\| 'small' \\| 'default' \\| 'large'` | `'default'` | Épaisseur de la barre |\n| **`label`** | `boolean` | `false` | Affiche le pourcentage en texte à droite |\n| `class` | `string` | — | Classes additionnelles |\n\n> **Note :** `size` n'inclut pas `'extra'` pour ce composant.\n\n#### Exemple\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { ProgressBar } from 'fab-ui'\n\nconst progress = ref(65)\n</script>\n\n<template>\n  <!-- Barre déterminée avec label -->\n  <ProgressBar :value=\"progress\" variant=\"primary\" :label=\"true\" />\n\n  <!-- Barre indéterminée (chargement) -->\n  <ProgressBar variant=\"info\" size=\"tiny\" />\n\n  <!-- Variantes de couleur -->\n  <ProgressBar :value=\"80\" variant=\"success\" size=\"small\" />\n  <ProgressBar :value=\"30\" variant=\"danger\" size=\"large\" />\n</template>\n```\n\n---\n\n## 6. Contenu\n\n### Card + CardHeader\n\nConteneur de contenu avec bordure, ombre et slots structurels. `CardHeader` est un sous-composant optionnel qui organise le titre et les actions en grille.\n\n#### Card — Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`variant`** | `'default' \\| Severity` | `'default'` | Couleur de la bordure |\n| `padded` | `boolean` | `true` | Ajoute un padding interne et un gap entre les slots |\n| `bordered` | `boolean` | `true` | Affiche la bordure |\n| **`hoverable`** | `boolean` | `false` | Effet de survol (ombre + curseur pointer) |\n| **`raised`** | `boolean` | `false` | Ombre plus prononcée |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Card — Slots\n\n| Slot | Description |\n|------|-------------|\n| `header` | Zone d'en-tête (typiquement `CardHeader`) |\n| `default` | Corps principal |\n| `footer` | Pied de carte (actions, boutons…) |\n\n#### CardHeader — Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`flush`** | `boolean` | `false` | Supprime le padding — utile pour une image collée aux bords |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { Card, CardHeader } from 'fab-ui'\nimport { Button } from 'fab-ui'\n</script>\n\n<template>\n  <Card>\n    <template #header>\n      <CardHeader>\n        <h3 class=\"font-semibold text-lg\">Titre de la carte</h3>\n        <p class=\"text-sm text-muted-foreground\">Description optionnelle</p>\n      </CardHeader>\n    </template>\n\n    <p>Contenu principal de la carte.</p>\n\n    <template #footer>\n      <div class=\"flex justify-end gap-2 px-6\">\n        <Button variant=\"secondary\" shape=\"ghost\">Annuler</Button>\n        <Button variant=\"primary\">Confirmer</Button>\n      </div>\n    </template>\n  </Card>\n</template>\n```\n\n```vue\n<!-- Carte cliquable avec variante de couleur -->\n<Card variant=\"success\" :hoverable=\"true\" :raised=\"true\" @click=\"handleClick\">\n  <template #header>\n    <CardHeader>\n      <h3 class=\"font-semibold\">Paiement réussi</h3>\n    </CardHeader>\n  </template>\n  <p class=\"px-6\">Votre commande a été confirmée.</p>\n</Card>\n```\n\n---\n\n### Code\n\nBloc de code avec coloration syntaxique (highlight.js, thème Atom One Dark), bouton de copie, numéros de ligne et hauteur maximale configurable.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`code`** | `string` | — | Code à afficher (alternatif : slot par défaut) |\n| **`language`** | `string` | — | Langage pour highlight.js (`javascript`, `php`, `json`, `bash`, `yaml`…) |\n| `filename` | `string` | — | Nom de fichier affiché dans l'en-tête |\n| **`copyable`** | `boolean` | `true` | Affiche le bouton \"Copier\" |\n| **`lineNumbers`** | `boolean` | `false` | Affiche les numéros de ligne |\n| **`maxHeight`** | `string` | — | CSS `max-height` du corps scrollable (ex : `'400px'`) |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | Alternative à `code` : texte brut du code (le composant extrait le texte) |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { Code } from 'fab-ui'\n\nconst snippet = `const greet = (name: string) => \\`Hello, \\${name}!\\``\n</script>\n\n<template>\n  <!-- Via prop code -->\n  <Code :code=\"snippet\" language=\"typescript\" filename=\"greet.ts\" />\n\n  <!-- Via slot -->\n  <Code language=\"json\" :line-numbers=\"true\">{{ '{\"key\": \"value\"}' }}</Code>\n\n  <!-- Sans bouton de copie, avec hauteur max -->\n  <Code :code=\"longCode\" language=\"php\" :copyable=\"false\" max-height=\"300px\" />\n</template>\n```\n\n---\n\n### Image\n\nComposant image avec skeleton de chargement, fallback en cas d'erreur, ratio d'aspect et `object-fit` configurables.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`src`** | `string` | — | **Requis.** URL de l'image |\n| `alt` | `string` | `''` | Texte alternatif |\n| **`ratio`** | `'1/1' \\| '4/3' \\| '16/9' \\| '3/2' \\| '2/3' \\| '9/16'` | — | Ratio d'aspect fixe |\n| **`fit`** | `'cover' \\| 'contain' \\| 'fill' \\| 'none'` | `'cover'` | CSS `object-fit` |\n| **`skeleton`** | `boolean` | `true` | Affiche un skeleton pendant le chargement |\n| **`fallback`** | `string` | — | URL de remplacement en cas d'erreur |\n| **`rounded`** | `boolean \\| 'full'` | `false` | `true` = coins arrondis, `'full'` = cercle |\n| `class` | `string` | — | Classes additionnelles sur le wrapper |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| `load` | `Event` | Image chargée avec succès |\n| `error` | `Event` | Erreur de chargement |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { Image } from 'fab-ui'\n</script>\n\n<template>\n  <!-- Image 16/9 avec skeleton -->\n  <Image src=\"/banner.jpg\" ratio=\"16/9\" alt=\"Bannière\" />\n\n  <!-- Avatar circulaire -->\n  <Image src=\"/avatar.jpg\" ratio=\"1/1\" rounded=\"full\" class=\"w-16\" />\n\n  <!-- Avec fallback -->\n  <Image\n    src=\"/product.jpg\"\n    fallback=\"/placeholder.jpg\"\n    ratio=\"4/3\"\n    fit=\"contain\"\n    @error=\"onImageError\"\n  />\n</template>\n```\n\n---\n\n### Carousel + CarouselSlide\n\nCarrousel tactile avec navigation, indicateurs et lecture automatique.\n\n#### Carousel — Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| `arrows` | `boolean` | `true` | Affiche les flèches de navigation |\n| `dots` | `boolean` | `true` | Affiche les points indicateurs |\n| **`autoplay`** | `number` | `0` | Intervalle en ms entre les slides (`0` = désactivé) |\n| **`loop`** | `boolean` | `false` | Retourne au début après la dernière slide |\n| **`perView`** | `number` | `1` | Nombre de slides visibles simultanément |\n| **`gap`** | `string` | `'gap-4'` | Classe Tailwind de gap entre les slides |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Carousel — Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | Les `CarouselSlide` à afficher |\n\n#### CarouselSlide — Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`perView`** | `number` | `1` | Doit correspondre au `:perView` du `Carousel` parent pour le calcul de largeur |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Exemple\n\n```vue\n<script setup lang=\"ts\">\nimport { Carousel, CarouselSlide } from 'fab-ui'\n\nconst slides = [\n  { title: 'Slide 1', img: '/img/1.jpg' },\n  { title: 'Slide 2', img: '/img/2.jpg' },\n  { title: 'Slide 3', img: '/img/3.jpg' },\n]\n</script>\n\n<template>\n  <!-- Carrousel simple -->\n  <Carousel :autoplay=\"4000\" loop>\n    <CarouselSlide v-for=\"s in slides\" :key=\"s.title\">\n      <img :src=\"s.img\" :alt=\"s.title\" class=\"w-full rounded-lg\" />\n    </CarouselSlide>\n  </Carousel>\n\n  <!-- Multi-slides (2 visibles) -->\n  <Carousel :per-view=\"2\" gap=\"gap-6\" :dots=\"false\">\n    <CarouselSlide v-for=\"s in slides\" :key=\"s.title\" :per-view=\"2\">\n      <div class=\"rounded-lg bg-gray-100 p-6\">{{ s.title }}</div>\n    </CarouselSlide>\n  </Carousel>\n</template>\n```\n\n---\n\n## 7. Formulaires étendus\n\n### Slider\n\nCurseur de sélection de valeur numérique, basé sur `SliderRoot` de Reka UI. Supporte les plages (range) via un tableau de valeurs.\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`variant`** | `Severity` | `'primary'` | Couleur de la range et du thumb |\n| **`size`** | `'small' \\| 'default' \\| 'large'` | `'default'` | Épaisseur de la piste et taille du thumb |\n| `modelValue` / `v-model` | `number[]` | — | Valeur(s) contrôlée(s) |\n| `defaultValue` | `number[]` | — | Valeur(s) initiale(s) non contrôlée(s) |\n| `min` | `number` | `0` | Valeur minimale |\n| `max` | `number` | `100` | Valeur maximale |\n| `step` | `number` | `1` | Incrément |\n| `disabled` | `boolean` | `false` | Désactive le slider |\n| `orientation` | `'horizontal' \\| 'vertical'` | `'horizontal'` | Orientation |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| `update:modelValue` | `number[]` | Changement de valeur pendant le glissement |\n| `valueCommit` | `number[]` | Valeur finalisée au relâchement |\n\n#### Exemples\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { Slider } from 'fab-ui'\n\nconst volume = ref([50])\nconst priceRange = ref([20, 80])\n</script>\n\n<template>\n  <!-- Curseur simple -->\n  <Slider v-model=\"volume\" :min=\"0\" :max=\"100\" variant=\"primary\" />\n  <p>Volume : {{ volume[0] }}%</p>\n\n  <!-- Plage de prix -->\n  <Slider v-model=\"priceRange\" :min=\"0\" :max=\"200\" :step=\"5\" variant=\"success\" size=\"large\" />\n  <p>Prix : {{ priceRange[0] }}€ – {{ priceRange[1] }}€</p>\n</template>\n```\n\n---\n\n## 8. Overlays & Navigation\n\n### ContextMenu\n\nMenu contextuel (clic droit) composé de plusieurs sous-composants. Basé sur Reka UI.\n\n#### Composants exportés\n\n| Composant | Rôle |\n|-----------|------|\n| `ContextMenu` | Racine — gère l'état ouvert/fermé |\n| `ContextMenuTrigger` | Zone déclenchant l'ouverture (clic droit) |\n| `ContextMenuContent` | Conteneur du menu (panneau flottant) |\n| `ContextMenuItem` | Élément cliquable |\n| `ContextMenuLabel` | Libellé non cliquable (groupe) |\n| `ContextMenuSeparator` | Séparateur horizontal |\n| `ContextMenuShortcut` | Raccourci clavier aligné à droite |\n| `ContextMenuGroup` | Groupe logique d'items |\n| `ContextMenuSub` | Racine du sous-menu |\n| `ContextMenuSubTrigger` | Déclencheur du sous-menu |\n| `ContextMenuSubContent` | Contenu du sous-menu |\n| `ContextMenuCheckboxItem` | Item avec case à cocher |\n| `ContextMenuRadioGroup` | Groupe radio |\n| `ContextMenuRadioItem` | Item radio |\n\n#### ContextMenuContent — Props\n\nHérite de `ContextMenuContentProps` de Reka UI.\n\n| Prop | Type | Description |\n|------|------|-------------|\n| `class` | `string` | Classes additionnelles |\n\n#### ContextMenuItem — Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| `inset` | `boolean` | `false` | Décale le contenu vers la droite (alignement avec icône) |\n| **`variant`** | `'default' \\| 'danger'` | `'default'` | `'danger'` colore l'item en rouge |\n| `disabled` | `boolean` | `false` | Désactive l'item |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Exemple complet\n\n```vue\n<script setup lang=\"ts\">\nimport {\n  ContextMenu,\n  ContextMenuTrigger,\n  ContextMenuContent,\n  ContextMenuItem,\n  ContextMenuLabel,\n  ContextMenuSeparator,\n  ContextMenuShortcut,\n  ContextMenuSub,\n  ContextMenuSubTrigger,\n  ContextMenuSubContent,\n  ContextMenuCheckboxItem,\n} from 'fab-ui'\nimport { ref } from 'vue'\n\nconst showPreview = ref(true)\n\nfunction openFile()   { /* ... */ }\nfunction deleteFile() { /* ... */ }\n</script>\n\n<template>\n  <ContextMenu>\n    <ContextMenuTrigger>\n      <div class=\"w-64 h-32 rounded-lg border border-dashed flex items-center justify-center cursor-default select-none\">\n        Clic droit ici\n      </div>\n    </ContextMenuTrigger>\n\n    <ContextMenuContent>\n      <ContextMenuLabel>Actions</ContextMenuLabel>\n      <ContextMenuSeparator />\n\n      <ContextMenuItem @click=\"openFile\">\n        Ouvrir\n        <ContextMenuShortcut>⌘O</ContextMenuShortcut>\n      </ContextMenuItem>\n\n      <ContextMenuSub>\n        <ContextMenuSubTrigger>Partager</ContextMenuSubTrigger>\n        <ContextMenuSubContent>\n          <ContextMenuItem>Par e-mail</ContextMenuItem>\n          <ContextMenuItem>Lien public</ContextMenuItem>\n        </ContextMenuSubContent>\n      </ContextMenuSub>\n\n      <ContextMenuCheckboxItem v-model:checked=\"showPreview\">\n        Aperçu\n      </ContextMenuCheckboxItem>\n\n      <ContextMenuSeparator />\n\n      <ContextMenuItem variant=\"danger\" @click=\"deleteFile\">\n        Supprimer\n        <ContextMenuShortcut>⌫</ContextMenuShortcut>\n      </ContextMenuItem>\n    </ContextMenuContent>\n  </ContextMenu>\n</template>\n```\n\n---\n\n### CollapsibleContent\n\nExtension du `CollapsibleContent` de Reka UI avec un mode **overlay** : le panneau se déploie par-dessus les éléments suivants sans perturber le flux du document (idéal pour des menus déroulants custom).\n\n#### Props\n\nHérite de `CollapsibleContentProps` de Reka UI.\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`overlay`** | `boolean` | `false` | Positionne le contenu en `absolute` par-dessus le reste |\n| `class` | `string` | — | Classes additionnelles |\n\n> **Note :** En mode `overlay`, le parent doit avoir `position: relative`.\n\n#### Exemple\n\n```vue\n<script setup lang=\"ts\">\nimport { CollapsibleRoot, CollapsibleTrigger } from 'reka-ui'\nimport { CollapsibleContent } from 'fab-ui'\nimport { Button } from 'fab-ui'\n</script>\n\n<template>\n  <!-- Collapsible normal (dans le flux) -->\n  <CollapsibleRoot>\n    <CollapsibleTrigger as-child>\n      <Button variant=\"secondary\" shape=\"outline\">Afficher les détails</Button>\n    </CollapsibleTrigger>\n    <CollapsibleContent class=\"mt-2 p-4 border rounded-lg\">\n      <p>Contenu révélé dans le flux normal.</p>\n    </CollapsibleContent>\n  </CollapsibleRoot>\n\n  <!-- Mode overlay (dropdown custom) -->\n  <div class=\"relative\">\n    <CollapsibleRoot>\n      <CollapsibleTrigger as-child>\n        <Button variant=\"secondary\" shape=\"outline\">Options</Button>\n      </CollapsibleTrigger>\n      <CollapsibleContent :overlay=\"true\" class=\"p-2\">\n        <button class=\"block w-full text-left px-3 py-2 hover:bg-gray-100 rounded\">Option 1</button>\n        <button class=\"block w-full text-left px-3 py-2 hover:bg-gray-100 rounded\">Option 2</button>\n      </CollapsibleContent>\n    </CollapsibleRoot>\n  </div>\n</template>\n```\n\n---\n\n### SpeedDial\n\nBouton d'action flottant (FAB) qui révèle un ensemble d'actions secondaires dans une direction configurable. Chaque action dispose d'un tooltip au survol.\n\n#### Type SpeedDialItem\n\n```ts\ninterface SpeedDialItem {\n  label:     string;     // Texte du tooltip\n  icon:      Component;  // Icône Lucide (ou tout composant Vue)\n  onClick:   () => void; // Callback au clic\n  disabled?: boolean;    // Désactive l'action\n  variant?:  Severity;   // Couleur du bouton action (défaut : 'secondary')\n}\n```\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`items`** | `SpeedDialItem[]` | — | **Requis.** Liste des actions secondaires |\n| **`direction`** | `'up' \\| 'down' \\| 'left' \\| 'right'` | `'up'` | Direction d'ouverture |\n| **`variant`** | `Severity` | `'primary'` | Couleur du bouton principal |\n| **`size`** | `'small' \\| 'default' \\| 'large'` | `'default'` | Taille du bouton principal |\n| **`icon`** | `Component` | — | Icône custom du bouton principal (sinon `+` / `×`) |\n| `disabled` | `boolean` | `false` | Désactive tout le composant |\n| `class` | `string` | — | Classes additionnelles |\n\n#### Exemple\n\n```vue\n<script setup lang=\"ts\">\nimport { SpeedDial, type SpeedDialItem } from 'fab-ui'\nimport { EditIcon, TrashIcon, ShareIcon, DownloadIcon } from 'lucide-vue-next'\n\nconst actions: SpeedDialItem[] = [\n  { label: 'Modifier',     icon: EditIcon,     onClick: () => console.log('edit'),     variant: 'primary' },\n  { label: 'Partager',     icon: ShareIcon,    onClick: () => console.log('share'),    variant: 'info' },\n  { label: 'Télécharger',  icon: DownloadIcon, onClick: () => console.log('download'), variant: 'success' },\n  { label: 'Supprimer',    icon: TrashIcon,    onClick: () => console.log('delete'),   variant: 'danger' },\n]\n</script>\n\n<template>\n  <!-- Fixé en bas à droite -->\n  <div class=\"fixed bottom-6 right-6 z-50\">\n    <SpeedDial :items=\"actions\" direction=\"up\" variant=\"primary\" size=\"default\" />\n  </div>\n\n  <!-- Direction horizontale -->\n  <SpeedDial :items=\"actions\" direction=\"right\" variant=\"secondary\" size=\"small\" />\n</template>\n```\n\n---\n\n## 9. Données\n\n### DataTable\n\nTableau de données complet avec tri multi-colonnes, recherche globale (debounced), sélection de lignes, pagination et slots custom par colonne.\n\n#### Type DataTableColumn\n\n```ts\ninterface DataTableColumn<Row = Record<string, unknown>> {\n  key:       string;                                // Clé dans l'objet de données\n  label:     string;                                // En-tête de la colonne\n  sortable?: boolean;                               // Active le tri sur cette colonne\n  width?:    string;                                // Largeur CSS ('120px', '20%'…)\n  align?:    'left' | 'center' | 'right';           // Alignement du contenu (défaut : 'left')\n  format?:   (value: unknown, row: Row) => string;  // Formateur de valeur (fallback si pas de slot)\n}\n```\n\n#### Props\n\n| Prop | Type | Défaut | Description |\n|------|------|--------|-------------|\n| **`columns`** | `DataTableColumn[]` | — | **Requis.** Définition des colonnes |\n| **`data`** | `T[]` | — | **Requis.** Données à afficher |\n| **`searchable`** | `boolean` | `false` | Affiche un champ de recherche globale (debounce 250 ms) |\n| **`selectable`** | `boolean` | `false` | Affiche une colonne de cases à cocher |\n| **`pageSize`** | `number` | `15` | Nombre de lignes par page (`0` = pas de pagination) |\n| **`pageSizes`** | `number[]` | `[10,25,50,100]` | Options du sélecteur de taille de page |\n| **`loading`** | `boolean` | `false` | Affiche le skeleton de chargement |\n| `emptyText` | `string` | `'Aucun résultat.'` | Texte affiché si aucune donnée |\n| **`striped`** | `boolean` | `false` | Lignes alternées (zèbre) |\n| `hoverable` | `boolean` | `true` | Mise en évidence au survol |\n| `class` | `string` | — | Classes additionnelles |\n\n#### v-model\n\n| Binding | Type | Description |\n|---------|------|-------------|\n| **`v-model:selected`** | `T[]` | Tableau des lignes sélectionnées (active implicitement la colonne checkbox) |\n\n#### Événements\n\n| Événement | Payload | Description |\n|-----------|---------|-------------|\n| **`@row-click`** | `row: T` | Clic sur une ligne entière |\n| **`@cell-click`** | `key: string, row: T` | Clic sur une cellule précise |\n\n#### Slots\n\n| Slot | Props du slot | Description |\n|------|---------------|-------------|\n| **`#column-{key}`** | `{ row: T, value: unknown }` | Rendu custom pour la colonne `key`. Remplace le formateur `format` |\n| `#empty` | — | Contenu de l'état vide (remplace `emptyText`) |\n\n#### Exemple complet\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { DataTable, type DataTableColumn } from 'fab-ui'\nimport { Badge } from 'fab-ui'\nimport { Button } from 'fab-ui'\n\ninterface User {\n  id:     number\n  name:   string\n  email:  string\n  role:   string\n  status: 'active' | 'inactive' | 'pending'\n  joined: string\n}\n\nconst columns: DataTableColumn<User>[] = [\n  { key: 'name',   label: 'Nom',     sortable: true,  width: '200px' },\n  { key: 'email',  label: 'E-mail',  sortable: true },\n  { key: 'role',   label: 'Rôle',   align: 'center', width: '120px' },\n  { key: 'status', label: 'Statut', align: 'center', width: '120px' },\n  { key: 'joined', label: 'Inscrit',\n    sortable: true,\n    format: (v) => new Date(v as string).toLocaleDateString('fr-FR'),\n  },\n  { key: 'actions', label: '', width: '80px', align: 'right' },\n]\n\nconst users = ref<User[]>([\n  { id: 1, name: 'Alice Martin',   email: 'alice@example.com', role: 'Admin',    status: 'active',   joined: '2024-01-15' },\n  { id: 2, name: 'Bob Dupont',     email: 'bob@example.com',   role: 'Éditeur',  status: 'inactive', joined: '2024-03-22' },\n  { id: 3, name: 'Claire Bernard', email: 'claire@example.com',role: 'Lecteur',  status: 'pending',  joined: '2024-06-01' },\n])\n\nconst selected = ref<User[]>([])\nconst loading  = ref(false)\n\nfunction handleRowClick(user: User) {\n  console.log('Ligne cliquée :', user.name)\n}\n\nfunction editUser(user: User) {\n  console.log('Modifier :', user.id)\n}\n</script>\n\n<template>\n  <DataTable\n    :columns=\"columns\"\n    :data=\"users\"\n    v-model:selected=\"selected\"\n    :searchable=\"true\"\n    :striped=\"true\"\n    :loading=\"loading\"\n    :page-size=\"10\"\n    empty-text=\"Aucun utilisateur trouvé.\"\n    @row-click=\"handleRowClick\"\n    @cell-click=\"(key, row) => console.log(key, row)\"\n  >\n    <!-- Slot custom pour la colonne status -->\n    <template #column-status=\"{ value }\">\n      <Badge\n        :variant=\"value === 'active' ? 'success' : value === 'pending' ? 'warning' : 'secondary'\"\n        shape=\"filled\"\n        size=\"small\"\n      >\n        {{ value === 'active' ? 'Actif' : value === 'pending' ? 'En attente' : 'Inactif' }}\n      </Badge>\n    </template>\n\n    <!-- Slot custom pour les actions -->\n    <template #column-actions=\"{ row }\">\n      <Button\n        size=\"small\"\n        variant=\"secondary\"\n        shape=\"ghost\"\n        @click.stop=\"editUser(row)\"\n      >\n        Modifier\n      </Button>\n    </template>\n\n    <!-- État vide personnalisé -->\n    <template #empty>\n      <div class=\"py-8 text-center text-gray-400\">\n        <p class=\"font-medium\">Aucun résultat</p>\n        <p class=\"text-sm mt-1\">Modifiez vos critères de recherche.</p>\n      </div>\n    </template>\n  </DataTable>\n\n  <!-- Affichage des lignes sélectionnées -->\n  <p v-if=\"selected.length\" class=\"mt-4 text-sm text-gray-600\">\n    {{ selected.length }} utilisateur(s) sélectionné(s) :\n    {{ selected.map(u => u.name).join(', ') }}\n  </p>\n</template>\n```\n","readmeFilename":"readme.md","license":"MIT"}