Files
malio-layer-ui/COMPONENTS.md
tristan f3a18ace1d
All checks were successful
Release / release (push) Successful in 1m12s
feat: composant saisie assistée, composant téléphone et composant mail (#47)
| 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: #47
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-05-13 07:01:30 +00:00

26 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 si addable, non disabled, non readonly)
<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 API
  • select(option: Option \| null) — émis avec l'objet Option complet (utile pour récupérer aussi le label)
  • create(value: string) — émis quand allowCreate=true et 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" />

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 />

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 avec backdrop semi-transparent.

Prop Type Défaut Description
id string auto Identifiant HTML
modelValue boolean undefined État ouvert/fermé (v-model)
title string '' Titre affiché dans le header
showClose boolean true Afficher le bouton de fermeture (croix)
drawerClass string '' Classes CSS panneau (twMerge)

Events : update:modelValue(value: boolean) Slots : default (contenu du drawer)

<MalioDrawer v-model="isOpen" title="Détails">
  <p>Contenu du drawer</p>
</MalioDrawer>
<MalioDrawer v-model="isOpen" title="Sans croix" :show-close="false">
  <p>Fermeture uniquement via backdrop</p>
</MalioDrawer>
<MalioDrawer v-model="isOpen" title="Large" drawer-class="max-w-2xl">
  <p>Drawer plus large</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"
/>