| Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Co-authored-by: matthieu <matthieu@yuno.malio.fr> Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Reviewed-on: #52 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
34 KiB
@malio/layer-ui — Composants
Tous les composants sont auto-importés avec le préfixe Malio. Utiliser v-model pour le binding bidirectionnel sur les composants de formulaire.
MalioInputText
Champ texte avec label, icône optionnelle et support de masque de saisie.
| Prop | Type | Défaut | Description |
|---|---|---|---|
id |
string |
auto | Identifiant HTML |
label |
string |
'' |
Label du champ |
modelValue |
string | null |
undefined |
Valeur (v-model) |
disabled |
boolean |
false |
Désactive le champ |
readonly |
boolean |
false |
Lecture seule |
required |
boolean |
false |
Champ requis |
hint |
string |
'' |
Message d'aide |
error |
string |
'' |
Message d'erreur |
success |
string |
'' |
Message de succès |
iconName |
string |
'' |
Icône Iconify (ex: mdi:magnify) |
iconPosition |
'left' | 'right' |
'right' |
Position de l'icône |
iconSize |
string | number |
24 |
Taille icône |
iconColor |
string |
'text-m-muted' |
Classe couleur icône |
mask |
string | MaskInputOptions |
— | Masque de saisie (maska) |
maxLength |
number | string |
— | Longueur max |
minLength |
number | string |
— | Longueur min |
inputClass |
string |
'' |
Classes CSS input |
labelClass |
string |
'' |
Classes CSS label |
groupClass |
string |
'' |
Classes CSS conteneur |
Events : update:modelValue(value: string)
<MalioInputText v-model="nom" label="Nom" />
<MalioInputText v-model="search" label="Recherche" icon-name="mdi:magnify" />
<MalioInputText v-model="tel" label="Téléphone" mask="## ## ## ## ##" />
<MalioInputText v-model="email" label="Email" error="Email invalide" />
<MalioInputText v-model="info" label="Info" disabled hint="Champ désactivé" />
MalioInputPassword
Champ mot de passe avec toggle visibilité.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | null |
undefined |
Valeur (v-model) |
label |
string |
'' |
Label |
displayIcon |
boolean |
true |
Afficher l'icône toggle |
disabled |
boolean |
false |
Désactivé |
readonly |
boolean |
false |
Lecture seule |
hint |
string |
'' |
Message d'aide |
error |
string |
'' |
Message d'erreur |
success |
string |
'' |
Message de succès |
Events : update:modelValue(value: string)
<MalioInputPassword v-model="password" label="Mot de passe" />
<MalioInputPassword v-model="password" label="Sans icône" :display-icon="false" />
MalioInputEmail
Champ email (type="email" + inputmode="email") avec icône mdi:email-outline à droite par défaut.
| Prop | Type | Défaut | Description |
|---|---|---|---|
id |
string |
auto | Identifiant HTML |
label |
string |
'' |
Label du champ |
modelValue |
string | null |
undefined |
Valeur (v-model) |
name |
string |
'' |
Attribut name |
autocomplete |
string |
'off' |
Autocomplétion (passer 'email' pour suggérer l'email utilisateur) |
disabled |
boolean |
false |
Désactive le champ |
readonly |
boolean |
false |
Lecture seule |
required |
boolean |
false |
Champ requis |
hint |
string |
'' |
Message d'aide |
error |
string |
'' |
Message d'erreur |
success |
string |
'' |
Message de succès |
iconName |
string |
'mdi:email-outline' |
Icône Iconify (chaîne vide pour masquer) |
iconPosition |
'left' | 'right' |
'right' |
Position de l'icône |
iconSize |
string | number |
24 |
Taille icône |
iconColor |
string |
'text-m-muted' |
Classe couleur icône |
inputClass |
string |
'' |
Classes CSS input |
labelClass |
string |
'' |
Classes CSS label |
groupClass |
string |
'' |
Classes CSS conteneur |
Events : update:modelValue(value: string)
<MalioInputEmail v-model="email" label="Adresse email" />
<MalioInputEmail v-model="email" label="Email" autocomplete="email" />
<MalioInputEmail v-model="email" label="Email" :icon-name="''" />
<MalioInputEmail v-model="email" label="Email" error="Adresse email invalide" />
MalioInputPhone
Champ téléphone (type="tel" + inputmode="tel") avec icône mdi:phone-outline à gauche par défaut et bouton + optionnel à droite pour gérer une liste de numéros côté parent.
| Prop | Type | Défaut | Description |
|---|---|---|---|
id |
string |
auto | Identifiant HTML |
label |
string |
'' |
Label du champ |
modelValue |
string | null |
undefined |
Valeur (v-model) |
name |
string |
'' |
Attribut name |
autocomplete |
string |
'off' |
Autocomplétion (passer 'tel' pour suggérer un numéro enregistré) |
disabled |
boolean |
false |
Désactive le champ et le bouton + |
readonly |
boolean |
false |
Lecture seule (désactive aussi le bouton +) |
required |
boolean |
false |
Champ requis |
hint |
string |
'' |
Message d'aide |
error |
string |
'' |
Message d'erreur |
success |
string |
'' |
Message de succès |
iconName |
string |
'mdi:phone-outline' |
Icône Iconify (chaîne vide pour masquer) |
iconPosition |
'left' | 'right' |
'left' |
Position de l'icône |
iconSize |
string | number |
24 |
Taille icône |
iconColor |
string |
'text-m-muted' |
Classe couleur icône |
mask |
string | MaskInputOptions |
undefined |
Masque maska (aucun par défaut, utile pour mono-pays) |
addable |
boolean |
false |
Affiche un bouton à droite qui émet l'event add |
addIconName |
string |
'mdi:plus' |
Icône Iconify du bouton d'ajout |
addButtonLabel |
string |
'Ajouter un numéro' |
aria-label du bouton d'ajout |
inputClass |
string |
'' |
Classes CSS input |
labelClass |
string |
'' |
Classes CSS label |
groupClass |
string |
'' |
Classes CSS conteneur |
Events :
update:modelValue(value: string)add()— émis au clic du bouton+(uniquement siaddable, nondisabled, nonreadonly)
<MalioInputPhone v-model="phone" label="Téléphone" />
<MalioInputPhone v-model="phone" label="Téléphone (FR)" mask="+33 # ## ## ## ##" />
<MalioInputPhone v-model="phone" label="Téléphone" addable @add="addPhoneField" />
<MalioInputPhone v-model="phone" label="Téléphone" error="Numéro invalide" />
MalioInputAutocomplete
Champ de saisie assistée (typeahead / combobox) : l'utilisateur tape pour filtrer une liste d'options, ou pour déclencher une recherche côté parent (API). Le parent alimente options et loading en réponse à l'event search — c'est lui qui gère l'appel API, l'auth, la transformation et le cache.
| Prop | Type | Défaut | Description |
|---|---|---|---|
id |
string |
auto | Identifiant HTML |
label |
string |
'' |
Label flottant |
modelValue |
string | number | null |
undefined |
Valeur sélectionnée (v-model) |
name |
string |
'' |
Attribut name |
options |
{label: string; value: string|number}[] |
[] |
Liste affichée dans le dropdown |
loading |
boolean |
false |
Affiche un spinner + un message de chargement |
debounce |
number |
300 |
Délai (ms) avant émission de search |
minSearchLength |
number |
0 |
Caractères mini avant d'émettre search |
allowCreate |
boolean |
false |
Autorise la saisie libre validée par Entrée (émet create) |
iconName |
string |
'' |
Icône Iconify décorative |
iconPosition |
'left' | 'right' |
'left' |
Position de l'icône décorative |
iconSize |
string | number |
24 |
Taille de l'icône |
iconColor |
string |
'text-m-muted' |
Classe couleur de l'icône |
noResultsText |
string |
'Aucun résultat' |
Texte affiché quand options est vide |
loadingText |
string |
'Chargement…' |
Texte affiché pendant le chargement |
minSearchText |
string |
'Tapez pour rechercher' |
Texte affiché tant que minSearchLength n'est pas atteint |
disabled |
boolean |
false |
Désactive le champ et empêche l'ouverture |
readonly |
boolean |
false |
Lecture seule (n'ouvre pas le dropdown) |
required |
boolean |
false |
Champ requis |
hint |
string |
'' |
Message d'aide |
error |
string |
'' |
Message d'erreur (prioritaire) |
success |
string |
'' |
Message de succès |
inputClass |
string |
'' |
Classes CSS input |
labelClass |
string |
'' |
Classes CSS label |
groupClass |
string |
'' |
Classes CSS conteneur |
Events :
update:modelValue(value: string \| number \| null)— valeur sélectionnée (v-model)search(query: string)— émis (après debounce + minSearchLength) avec le texte tapé ; le parent l'écoute pour lancer son fetch APIselect(option: Option \| null)— émis avec l'objetOptioncomplet (utile pour récupérer aussi lelabel)create(value: string)— émis quandallowCreate=trueet que l'utilisateur valide une valeur libre
Clavier : ↓ / ↑ navigation, Entrée sélection (ou création), Échap ferme le dropdown.
<!-- Usage statique -->
<MalioInputAutocomplete v-model="country" label="Pays" :options="countries" />
<!-- Usage API (parent gère le fetch) -->
<MalioInputAutocomplete
v-model="clientId"
label="Client"
:options="clientOptions"
:loading="isFetching"
:min-search-length="2"
@search="onSearchClients"
@select="onSelectClient"
/>
<!-- Avec création libre -->
<MalioInputAutocomplete
v-model="category"
label="Catégorie"
:options="categories"
allow-create
@create="onCreateCategory"
/>
async function onSearchClients(query: string) {
isFetching.value = true
const res = await $fetch('/api/clients', {params: {q: query}})
clientOptions.value = res.map(c => ({label: c.name, value: c.id}))
isFetching.value = false
}
MalioInputAmount
Champ montant avec icône devise (euro par défaut).
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | null |
undefined |
Valeur (v-model) |
label |
string |
'' |
Label |
iconName |
string |
'mdi:currency-eur' |
Icône devise |
disabled |
boolean |
false |
Désactivé |
error |
string |
'' |
Message d'erreur |
Events : update:modelValue(value: string)
<MalioInputAmount v-model="montant" label="Montant TTC" />
<MalioInputAmount v-model="prix" label="Prix" error="Montant invalide" />
MalioInputNumber
Champ numérique avec boutons +/-.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | null |
undefined |
Valeur (v-model) |
label |
string |
'' |
Label |
min |
number | string |
— | Valeur minimum |
max |
number | string |
— | Valeur maximum |
disabled |
boolean |
false |
Désactivé |
error |
string |
'' |
Message d'erreur |
Events : update:modelValue(value: string)
<MalioInputNumber v-model="quantite" label="Quantité" min="0" max="100" />
MalioInputTextArea
Zone de texte multiligne avec compteur et redimensionnement.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | null |
undefined |
Valeur (v-model) |
label |
string |
'' |
Label |
size |
number | string |
2 |
Nombre de lignes |
resize |
'none' | 'both' | 'horizontal' | 'vertical' |
'both' |
Mode redimensionnement |
maxLength |
number |
800 |
Longueur max |
showCounter |
boolean |
false |
Afficher le compteur |
disabled |
boolean |
false |
Désactivé |
error |
string |
'' |
Message d'erreur |
groupClass |
string |
'' |
Classes CSS sur la div conteneur (utile pour row-span-*, col-span-*, etc.) |
Events : update:modelValue(value: string)
<MalioInputTextArea v-model="commentaire" label="Commentaire" :show-counter="true" />
<MalioInputTextArea v-model="note" label="Note" resize="vertical" :size="4" />
MalioInputRichText
Éditeur de texte riche basé sur TipTap v3 + StarterKit + tiptap-markdown + TextStyle/Color/Highlight. Toolbar avec gras, italique, barré, titres H2/H3, listes, citation, code, code-block, lien, couleur du texte, surlignage, undo/redo. Sortie en HTML (par défaut) ou markdown.
Couleurs et surlignages ne sont pas persistés en markdown. Pour les conserver au save/reload, utiliser
output-format="html".
| Prop | Type | Défaut | Description |
|---|---|---|---|
id |
string |
auto | Identifiant HTML |
label |
string |
'' |
Label affiché au-dessus de l'éditeur |
modelValue |
string | null |
undefined |
Contenu (v-model) |
placeholder |
string |
'' |
Texte affiché quand vide |
minHeight |
string |
'160px' |
Hauteur min de la zone d'édition |
editable |
boolean |
true |
false → mode affichage seul (toolbar masquée) |
disabled |
boolean |
false |
Désactive l'édition et la toolbar |
readonly |
boolean |
false |
Lecture seule (toolbar visible mais désactivée) |
hint |
string |
'' |
Message d'aide |
error |
string |
'' |
Message d'erreur |
success |
string |
'' |
Message de succès |
outputFormat |
'markdown' | 'html' |
'html' |
Format émis dans update:modelValue |
groupClass |
string |
'' |
Classes CSS conteneur (twMerge) |
labelClass |
string |
'' |
Classes CSS label (twMerge) |
editorClass |
string |
'' |
Classes CSS wrapper éditeur (twMerge) |
Events : update:modelValue(value: string)
<MalioInputRichText v-model="note" label="Note" placeholder="Écrire ici…" />
<MalioInputRichText v-model="cr" label="Compte-rendu" error="Trop court" />
<MalioInputRichText v-model="article" label="Article" min-height="240px" />
<MalioInputRichText :model-value="content" :editable="false" />
MalioInputUpload
Champ d'upload de fichier.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | null |
undefined |
Nom du fichier (v-model) |
label |
string |
'' |
Label |
accept |
string |
'' |
Types de fichiers acceptés |
displayIcon |
boolean |
true |
Afficher l'icône |
disabled |
boolean |
false |
Désactivé |
error |
string |
'' |
Message d'erreur |
Events : update:modelValue(value: string), file-selected(file: File)
<MalioInputUpload v-model="fileName" label="Document" accept=".pdf,.doc" @file-selected="onFile" />
MalioSelect
Liste déroulante.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | number | null |
requis | Valeur sélectionnée (v-model) |
options |
{ value: string | number, text: string }[] |
[] |
Options disponibles |
emptyOptionLabel |
string |
'' |
Placeholder option vide |
label |
string |
'' |
Label |
hint |
string |
'' |
Message d'aide |
error |
string |
'' |
Message d'erreur |
success |
string |
'' |
Message de succès |
disabled |
boolean |
false |
Désactivé |
groupClass |
string |
'' |
Classes CSS conteneur (twMerge) |
rounded |
string |
'rounded-md' |
Classe border-radius |
textField |
string |
'text-lg' |
Classe taille texte bouton |
textValue |
string |
'text-lg' |
Classe taille texte valeur |
textLabel |
string |
'text-sm' |
Classe taille texte label |
noOptionsText |
string |
'Aucune option disponible' |
Message affiché dans la dropdown quand options est vide |
Events : update:modelValue(value: string | number | null)
Slots : icon (icône dropdown custom)
<MalioSelect v-model="pays" label="Pays" :options="[{ value: 'FR', text: 'France' }, { value: 'BE', text: 'Belgique' }]" />
<MalioSelect v-model="ville" label="Ville" :options="villes" empty-option-label="Choisir..." />
<MalioSelect v-model="civilite" label="Civilité" :options="civilites" group-class="mt-0" />
MalioSelectCheckbox
Liste déroulante multi-sélection avec checkboxes.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
(string | number)[] |
requis | Valeurs sélectionnées (v-model) |
options |
{ value: string | number, text: string }[] |
[] |
Options |
displayTag |
boolean |
false |
Afficher les tags sélectionnés |
displaySelectAll |
boolean |
false |
Afficher "Tout sélectionner" |
selectAllLabel |
string |
'Tout sélectionner' |
Texte du sélecteur global |
label |
string |
'' |
Label |
disabled |
boolean |
false |
Désactivé |
noOptionsText |
string |
'Aucune option disponible' |
Message affiché dans la dropdown quand options est vide |
Events : update:modelValue(value: (string | number)[])
<MalioSelectCheckbox v-model="competences" label="Compétences" :options="skills" :display-tag="true" />
<MalioSelectCheckbox v-model="sites" label="Sites" :options="sitesList" :display-select-all="true" />
MalioCheckbox
Case à cocher.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
boolean | null |
undefined |
Valeur (v-model) |
label |
string |
'' |
Label |
disabled |
boolean |
false |
Désactivé |
readonly |
boolean |
false |
Lecture seule |
error |
string |
'' |
Message d'erreur |
Events : update:modelValue(value: boolean)
<MalioCheckbox v-model="accepte" label="J'accepte les conditions" />
<MalioCheckbox v-model="newsletter" label="Newsletter" disabled />
MalioRadioButton
Bouton radio (à utiliser en groupe avec le même name).
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | number | boolean | null |
undefined |
Valeur du groupe (v-model) |
value |
string | number | boolean | null |
undefined |
Valeur de cette option |
label |
string |
'' |
Label |
name |
string |
'' |
Nom du groupe radio |
disabled |
boolean |
false |
Désactivé |
readonly |
boolean |
false |
Lecture seule |
Events : update:modelValue(value: string | number | boolean | null)
<MalioRadioButton v-model="civilite" name="civilite" value="M" label="Monsieur" />
<MalioRadioButton v-model="civilite" name="civilite" value="Mme" label="Madame" />
MalioDate
Sélecteur de date unique avec popover (grille de calendrier + vue mois/année).
La valeur est une chaîne ISO "YYYY-MM-DD". Cliquer un jour émet la date et ferme le popover.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | null |
undefined |
Date ISO "YYYY-MM-DD" (v-model) |
id |
string |
'' |
Id du champ |
name |
string |
'' |
Attribut name |
label |
string |
'' |
Label flottant |
placeholder |
string |
'JJ/MM/AAAA' |
Placeholder |
required |
boolean |
false |
Requis |
disabled |
boolean |
false |
Désactivé |
readonly |
boolean |
false |
Lecture seule |
hint |
string |
'' |
Texte d'aide |
error |
string |
'' |
Message d'erreur |
success |
string |
'' |
Message de succès |
min |
string |
undefined |
Date min "YYYY-MM-DD" (jours antérieurs désactivés) |
max |
string |
undefined |
Date max "YYYY-MM-DD" (jours postérieurs désactivés) |
clearable |
boolean |
true |
Affiche la croix d'effacement |
inputClass / labelClass / groupClass |
string |
'' |
Override des classes |
Events : update:modelValue(value: string | null)
<MalioDate v-model="date" label="Date de naissance" />
<!-- date === "2026-05-20" -->
<MalioDate v-model="rdv" label="Rendez-vous" :min="todayIso" :max="maxIso" />
MalioDateRange
Sélecteur de plage de dates (date de début → date de fin) dans un seul champ. Cliquer un premier jour démarre la plage, le second la termine ; un survol prévisualise la plage.
La valeur est un objet { start: string; end: string } (dates ISO "YYYY-MM-DD"), ou null.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
{ start: string; end: string } | null |
undefined |
Plage de dates ISO (v-model) |
id |
string |
'' |
Id du champ |
name |
string |
'' |
Attribut name |
label |
string |
'' |
Label flottant |
placeholder |
string |
'JJ/MM/AAAA' |
Placeholder |
required |
boolean |
false |
Requis |
disabled |
boolean |
false |
Désactivé |
readonly |
boolean |
false |
Lecture seule |
hint |
string |
'' |
Texte d'aide |
error |
string |
'' |
Message d'erreur |
success |
string |
'' |
Message de succès |
min |
string |
undefined |
Date min "YYYY-MM-DD" |
max |
string |
undefined |
Date max "YYYY-MM-DD" |
clearable |
boolean |
true |
Affiche la croix d'effacement |
inputClass / labelClass / groupClass |
string |
'' |
Override des classes |
Events : update:modelValue(value: { start: string; end: string } | null)
<MalioDateRange v-model="periode" label="Période de séjour" />
<!-- periode === { start: "2026-05-20", end: "2026-05-27" } -->
MalioDateWeek
Sélecteur de semaine ISO : cliquer un jour (ou un numéro de semaine) sélectionne la semaine entière.
La valeur est une chaîne au format semaine ISO native "YYYY-Www" (ex. "2026-W21"), ou null. Le champ affiche Semaine W (JJ/MM → JJ/MM/AAAA).
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | null |
undefined |
Semaine ISO "YYYY-Www" (v-model) |
id |
string |
'' |
Id du champ |
name |
string |
'' |
Attribut name |
label |
string |
'' |
Label flottant |
placeholder |
string |
'JJ/MM/AAAA' |
Placeholder |
required |
boolean |
false |
Requis |
disabled |
boolean |
false |
Désactivé |
readonly |
boolean |
false |
Lecture seule |
hint |
string |
'' |
Texte d'aide |
error |
string |
'' |
Message d'erreur |
success |
string |
'' |
Message de succès |
min |
string |
undefined |
Date min "YYYY-MM-DD" |
max |
string |
undefined |
Date max "YYYY-MM-DD" |
clearable |
boolean |
true |
Affiche la croix d'effacement |
inputClass / labelClass / groupClass |
string |
'' |
Override des classes |
Events : update:modelValue(value: string | null)
<MalioDateWeek v-model="semaine" label="Semaine de livraison" />
<!-- semaine === "2026-W21" -->
MalioTime
Sélecteur d'heure.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | null |
undefined |
Heure au format HH:mm (v-model) |
label |
string |
'' |
Label |
disabled |
boolean |
false |
Désactivé |
readonly |
boolean |
false |
Lecture seule |
error |
string |
'' |
Message d'erreur |
Events : update:modelValue(value: string)
<MalioTime v-model="heure" label="Heure de début" />
<MalioTime v-model="fin" label="Heure de fin" readonly />
MalioDateTime
Champ unique combinant date et heure dans un popover (grille de calendrier + sélecteur d'heure sous la grille).
⚠️ Version intérimaire : le sélecteur d'heure est un
<input type="time">natif, en attendant la maquette d'un sélecteur d'heure dédié. Le bloc heure est isolé pour être remplacé sans impact sur le reste.
La valeur est une chaîne ISO naïve sans fuseau au format "YYYY-MM-DDTHH:MM:00" (heure murale locale). Symfony (DateTimeNormalizer) parse ce format et applique son fuseau configuré côté back — pas de gestion de fuseau côté front.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string | null |
undefined |
Date + heure ISO naïve "YYYY-MM-DDTHH:MM:00" (v-model) |
id |
string |
'' |
Id du champ |
name |
string |
'' |
Attribut name |
label |
string |
'' |
Label flottant |
placeholder |
string |
'JJ/MM/AAAA HH:MM' |
Placeholder |
required |
boolean |
false |
Requis |
disabled |
boolean |
false |
Désactivé |
readonly |
boolean |
false |
Lecture seule |
hint |
string |
'' |
Texte d'aide |
error |
string |
'' |
Message d'erreur |
success |
string |
'' |
Message de succès |
min |
string |
undefined |
Borne min (datetime ou date ; borne la grille sur la partie date) |
max |
string |
undefined |
Borne max (idem) |
clearable |
boolean |
true |
Affiche la croix d'effacement |
inputClass / labelClass / groupClass |
string |
'' |
Override des classes |
Events : update:modelValue(value: string | null)
Flux : cliquer un jour fixe la date (heure par défaut 00:00), régler l'heure met à jour l'heure ; le popover se ferme au clic extérieur. La valeur est émise en direct à chaque interaction.
<MalioDateTime v-model="rdv" label="Date et heure du rendez-vous" />
<!-- rdv === "2026-05-20T14:30:00" -->
MalioButton
Bouton d'action avec 4 variantes visuelles et icône optionnelle.
| Prop | Type | Défaut | Description |
|---|---|---|---|
label |
string |
'' |
Texte du bouton (ou slot par défaut) |
variant |
'primary' | 'secondary' | 'tertiary' | 'danger' |
'primary' |
Variante visuelle |
disabled |
boolean |
false |
Désactivé |
buttonClass |
string |
'' |
Classes CSS additionnelles (twMerge) |
iconName |
string |
'' |
Icône Iconify |
iconPosition |
'left' | 'right' |
'right' |
Position icône |
iconSize |
string | number |
16 |
Taille icône |
Events : click(e: MouseEvent)
Slots : default (contenu du bouton, remplace label)
<MalioButton label="Valider" />
<MalioButton label="Modifier" variant="secondary" icon-name="mdi:pencil" icon-position="left" />
<MalioButton label="Voir plus" variant="tertiary" />
<MalioButton label="Supprimer" variant="danger" icon-name="mdi:trash" icon-position="left" />
<MalioButton label="Pleine largeur" button-class="w-full" />
MalioButtonIcon
Bouton icône seul (sans texte).
| Prop | Type | Défaut | Description |
|---|---|---|---|
icon |
string |
requis | Icône Iconify |
ariaLabel |
string |
requis | Label accessible |
variant |
'filled' | 'ghost' |
'filled' |
Variante visuelle |
disabled |
boolean |
false |
Désactivé |
buttonClass |
string |
'' |
Classes CSS additionnelles |
iconSize |
string | number |
24 |
Taille icône |
Events : click(e: MouseEvent)
<MalioButtonIcon icon="mdi:pencil" aria-label="Modifier" />
<MalioButtonIcon icon="mdi:trash" aria-label="Supprimer" variant="ghost" />
MalioTabList
Navigation par onglets avec contenu dynamique.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
string |
undefined |
Onglet actif (v-model) |
tabs |
Tab[] |
requis | Liste des onglets (voir type ci-dessous) |
Type Tab :
| Propriété | Type | Défaut | Description |
|---|---|---|---|
key |
string |
— | Identifiant unique (utilisé pour le slot et le v-model) |
label |
string |
— | Texte de l'onglet |
icon |
string |
— | Nom Iconify (optionnel) |
iconSize |
string |
24 |
Taille de l'icône |
disabled |
boolean |
false |
Onglet désactivé : grisé et non cliquable. Le parent calcule cet état selon sa logique de validation |
Events : update:modelValue(value: string) — émis uniquement quand l'onglet cible n'est pas disabled
Slots : Un slot nommé par tab.key pour le contenu de chaque onglet
<MalioTabList v-model="activeTab" :tabs="tabs">
<template #infos>Contenu infos</template>
<template #docs>Contenu docs</template>
</MalioTabList>
Pattern de gating progressif (déverrouille les onglets quand les précédents sont valides) :
const informationValid = computed(() => name.value && email.value)
const adressesValid = computed(() => /^\d{5}$/.test(codePostal.value))
const tabs = computed(() => [
{ key: 'information', label: 'Information' },
{ key: 'contacts', label: 'Contacts', disabled: !informationValid.value },
{ key: 'adresses', label: 'Adresses', disabled: !informationValid.value },
{ key: 'transport', label: 'Transport', disabled: !informationValid.value || !adressesValid.value },
])
MalioSidebar
Barre latérale de navigation rétractable.
| Prop | Type | Défaut | Description |
|---|---|---|---|
modelValue |
boolean |
undefined |
État ouvert/fermé (v-model) |
sections |
SidebarSection[] |
requis | Sections de navigation |
sidebarClass |
string |
'' |
Classes CSS sidebar |
toggleClass |
string |
'' |
Classes CSS bouton toggle |
Type SidebarSection : { title?: string, items: { label: string, icon?: string, to?: string, href?: string, active?: boolean }[] }
Events : update:modelValue(value: boolean)
Slots : logo (sidebar ouverte), logo-collapsed (sidebar fermée)
<MalioSidebar v-model="isOpen" :sections="menuSections">
<template #logo><img src="/logo.png" /></template>
<template #logo-collapsed><img src="/logo-small.png" /></template>
</MalioSidebar>
MalioDrawer
Panneau latéral (drawer) qui s'ouvre depuis la droite ou la gauche avec backdrop semi-transparent. Gère l'accessibilité (focus-trap, restitution du focus, Échap), le verrouillage du scroll de la page et un empilement correct de plusieurs drawers.
| Prop | Type | Défaut | Description |
|---|---|---|---|
id |
string |
auto | Identifiant HTML |
modelValue |
boolean |
undefined |
État ouvert/fermé (v-model) |
side |
'right' | 'left' |
'right' |
Côté d'apparition |
showClose |
boolean |
true |
Afficher le bouton de fermeture (croix) |
dismissable |
boolean |
true |
Fermer au clic sur le backdrop |
closeOnEscape |
boolean |
true |
Fermer avec la touche Échap |
ariaLabel |
string |
'' |
Nom accessible de secours quand le slot #header est absent |
drawerClass |
string |
'' |
Classes CSS panneau, ex. largeur max-w-2xl (twMerge) |
overlayClass |
string |
'' |
Classes CSS backdrop (twMerge) |
headerClass |
string |
'' |
Classes CSS barre header (twMerge) |
bodyClass |
string |
'' |
Classes CSS zone scrollable (twMerge) |
footerClass |
string |
'' |
Classes CSS wrapper du footer (aucune position imposée) |
Events : update:modelValue(value: boolean), close()
Slots :
header— en-tête (titre, etc.). S'il est absent et queshowCloseesttrue, seule la croix est affichée.default— contenu (zone scrollable).footer— rendu dans la zone scrollable, sans positionnement imposé : le consommateur choisit (sticky bottom-0,fixed, ou rien).
<MalioDrawer v-model="isOpen">
<template #header>
<h2 class="text-[24px] font-bold">Détails</h2>
</template>
<p>Contenu du drawer</p>
</MalioDrawer>
<!-- Côté gauche, largeur custom -->
<MalioDrawer v-model="isOpen" side="left" drawer-class="max-w-2xl">
<template #header><h2>Navigation</h2></template>
<p>Drawer large depuis la gauche</p>
</MalioDrawer>
<!-- Footer collé en bas (le consommateur applique le positionnement) -->
<MalioDrawer v-model="isOpen">
<template #header><h2>Formulaire</h2></template>
<MalioInputText label="Nom" />
<template #footer>
<div class="sticky bottom-0 bg-white py-4">
<MalioButton label="Enregistrer" button-class="w-full" @click="isOpen = false" />
</div>
</template>
</MalioDrawer>
<!-- Non fermable au backdrop / Échap (croix uniquement) -->
<MalioDrawer v-model="isOpen" :dismissable="false" :close-on-escape="false">
<template #header><h2>Action requise</h2></template>
<p>Fermeture via la croix uniquement</p>
</MalioDrawer>
MalioDataTable
Tableau de données presentational avec pagination, filtres par slots et lignes cliquables.
| Prop | Type | Défaut | Description |
|---|---|---|---|
id |
string |
auto | Identifiant HTML |
columns |
{ key: string, label: string }[] |
requis | Définition des colonnes |
items |
Record<string, unknown>[] |
requis | Données à afficher |
totalItems |
number |
requis | Total pour la pagination |
page |
number |
1 |
Page courante (v-model) |
perPage |
number |
10 |
Lignes par page (v-model) |
perPageOptions |
number[] |
[10, 25, 50] |
Options du sélecteur de lignes |
rowClickable |
boolean |
true |
Lignes cliquables (cursor pointer + hover) |
tableClass |
string |
'' |
Classes CSS sur <table> (twMerge) |
emptyMessage |
string |
'Aucune donnée' |
Message si items vide |
Events : update:page(value: number), update:per-page(value: number), row-click(item: Record<string, unknown>)
Slots : #header-{key} (filtre dans le <th>, placeholder = label), #cell-{key} (contenu du <td>), #empty (état vide)
<!-- Avec filtres et pagination -->
<MalioDataTable
:columns="[{ key: 'nom', label: 'Nom' }, { key: 'ville', label: 'Ville' }]"
:items="data"
:total-items="total"
v-model:page="page"
v-model:per-page="perPage"
@row-click="router.push(`/contact/${$event.id}`)"
>
<template #header-nom>
<input v-model="filtreNom" placeholder="Nom" class="w-full border-0 border-b border-black bg-transparent px-0 py-1 text-sm outline-none">
</template>
<template #header-ville>
<select v-model="filtreVille" class="w-full appearance-none border-0 border-b border-black bg-transparent px-0 py-1 text-sm outline-none">
<option value="">Ville</option>
<option v-for="v in villes" :key="v" :value="v">{{ v }}</option>
</select>
</template>
<template #cell-nom="{ item }">
<strong>{{ item.nom }}</strong>
</template>
</MalioDataTable>
<!-- Simple sans filtres -->
<MalioDataTable
:columns="columns"
:items="data"
:total-items="total"
v-model:page="page"
v-model:per-page="perPage"
/>