feat(ui) : required cohérent + astérisque label + sanitisation email (MUI-41) (#60)
## Résumé (MUI-41) Harmonise l'état « obligatoire » des composants de formulaire et normalise le champ email. ### `required` + astérisque - Nouveau composant partagé `MalioRequiredMark` : astérisque rouge (`text-m-danger`, **16px**), `aria-hidden`. - Prop `required` désormais cohérente sur toute la famille formulaire ; quand vraie, l'astérisque s'affiche **dans le label**. - Prop ajoutée à `Select`, `SelectCheckbox`, `InputUpload`, `InputRichText` (les autres l'avaient déjà). - Accessibilité : `required` natif là où l'élément le supporte, sinon `aria-required` (Select/SelectCheckbox sur le `<button>`, RichText sur le wrapper éditeur, Upload sur le champ visible). - `MalioSiteSelector` **exclu** volontairement (segmented control, pas de label de champ). ### Sanitisation email (`MalioInputEmail`) - Suppression de **tous les espaces** à la saisie (pas de masque). - Nouvelle prop opt-in `lowercase` (défaut `false`) : normalise en minuscules à la frappe (cohérent RG-1.21 Starseed). - Garde défensive curseur : l'API de sélection est interdite sur `type="email"` → repositionnement best-effort sans jamais lever. - La validation de format reste à la couche `error`. ### Docs & playground - `COMPONENTS.md` (doc `required` cohérente + note famille + `lowercase`) et `CHANGELOG.md` mis à jour. - Exemples playground `required` et email `lowercase` ajoutés. ## Test plan - [x] Suite complète : 42 fichiers / 771 tests verts - [x] Lint : 0 erreur - [x] Tests `aria-required` sur Select/SelectCheckbox/RichText - [ ] Vérif visuelle playground : astérisque 16px dans le label, email qui retire les espaces / minuscule Spec & plan : `docs/superpowers/specs/` et `docs/superpowers/plans/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #60 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #60.
This commit is contained in:
@@ -36,6 +36,36 @@
|
||||
<template #details><p class="p-4">Détails avancés</p></template>
|
||||
</MalioTabList>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4">
|
||||
<h2 class="mb-1 text-xl font-bold">Beaucoup d'onglets (fenêtré)</h2>
|
||||
<p class="mb-4 text-sm text-m-muted">
|
||||
7 onglets avec <code>:max-visible-tabs="5"</code> — flèches gauche/droite pour faire défiler
|
||||
(1 par 1). L'onglet actif reste sélectionné même hors fenêtre.
|
||||
</p>
|
||||
<MalioTabList v-model="manyValue" :tabs="manyTabs" :max-visible-tabs="5">
|
||||
<template #infos><p class="p-4">Contenu Informations</p></template>
|
||||
<template #adresses><p class="p-4">Contenu Adresses</p></template>
|
||||
<template #contacts><p class="p-4">Contenu Contacts</p></template>
|
||||
<template #compta><p class="p-4">Contenu Comptabilité</p></template>
|
||||
<template #documents><p class="p-4">Contenu Documents</p></template>
|
||||
<template #historique><p class="p-4">Contenu Historique</p></template>
|
||||
<template #parametres><p class="p-4">Contenu Paramètres</p></template>
|
||||
</MalioTabList>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4">
|
||||
<h2 class="mb-1 text-xl font-bold">Peu d'onglets avec maxVisibleTabs</h2>
|
||||
<p class="mb-4 text-sm text-m-muted">
|
||||
3 onglets avec <code>:max-visible-tabs="5"</code> — le fenêtrage ne s'active pas
|
||||
(onglets ≤ max), donc pas de flèches, affichage normal centré.
|
||||
</p>
|
||||
<MalioTabList v-model="fewValue" :tabs="fewTabs" :max-visible-tabs="5">
|
||||
<template #general><p class="p-4">Contenu Général</p></template>
|
||||
<template #adresses><p class="p-4">Contenu Adresses</p></template>
|
||||
<template #contacts><p class="p-4">Contenu Contacts</p></template>
|
||||
</MalioTabList>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -60,7 +90,25 @@ const tabsTwo = [
|
||||
{ key: 'details', label: 'Détails', icon: 'mdi:cog-outline' },
|
||||
]
|
||||
|
||||
const manyTabs = [
|
||||
{ key: 'infos', label: 'Informations', icon: 'mdi:information-outline' },
|
||||
{ key: 'adresses', label: 'Adresses', icon: 'mdi:map-marker-outline' },
|
||||
{ key: 'contacts', label: 'Contacts', icon: 'mdi:account-box-outline' },
|
||||
{ key: 'compta', label: 'Comptabilité', icon: 'mdi:web' },
|
||||
{ key: 'documents', label: 'Documents', icon: 'mdi:file-document-outline' },
|
||||
{ key: 'historique', label: 'Historique', icon: 'mdi:history' },
|
||||
{ key: 'parametres', label: 'Paramètres', icon: 'mdi:cog-outline' },
|
||||
]
|
||||
|
||||
const simpleValue = ref('qualimat')
|
||||
const noIconValue = ref('tab1')
|
||||
const twoTabValue = ref('general')
|
||||
const manyValue = ref('infos')
|
||||
|
||||
const fewTabs = [
|
||||
{ key: 'general', label: 'Général', icon: 'mdi:information-outline' },
|
||||
{ key: 'adresses', label: 'Adresses', icon: 'mdi:map-marker-outline' },
|
||||
{ key: 'contacts', label: 'Contacts', icon: 'mdi:account-box-outline' },
|
||||
]
|
||||
const fewValue = ref('general')
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user