887ebdebd7
## 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>
160 lines
4.2 KiB
Vue
160 lines
4.2 KiB
Vue
<template>
|
|
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Simple</h2>
|
|
<MalioInputPhone />
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Avec label</h2>
|
|
<MalioInputPhone
|
|
v-model="phoneValue"
|
|
label="Téléphone"
|
|
name="phone"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Avec bouton « ajouter »</h2>
|
|
<MalioInputPhone
|
|
v-model="phoneAddable"
|
|
label="Téléphone"
|
|
addable
|
|
@add="onAdd"
|
|
/>
|
|
<p v-if="addClicks > 0" class="mt-2 text-sm text-m-muted">
|
|
Bouton cliqué {{ addClicks }} fois
|
|
</p>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Icône à droite (sans bouton +)</h2>
|
|
<MalioInputPhone
|
|
label="Téléphone"
|
|
icon-position="right"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Sans icône</h2>
|
|
<MalioInputPhone
|
|
label="Téléphone"
|
|
:icon-name="''"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Avec masque français</h2>
|
|
<MalioInputPhone
|
|
v-model="phoneFrench"
|
|
label="Téléphone (FR)"
|
|
mask="+33 # ## ## ## ##"
|
|
hint="Saisir uniquement les chiffres"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Désactivé (avec addable)</h2>
|
|
<MalioInputPhone
|
|
model-value="+33 6 12 34 56 78"
|
|
addable
|
|
disabled
|
|
label="Téléphone"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Readonly (avec addable)</h2>
|
|
<MalioInputPhone
|
|
model-value="+33 6 12 34 56 78"
|
|
addable
|
|
readonly
|
|
label="Téléphone"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Readonly (readonly vide)</h2>
|
|
<MalioInputPhone
|
|
label="Téléphone (readonly vide)"
|
|
:readonly="true"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Readonly (readonly rempli)</h2>
|
|
<MalioInputPhone
|
|
v-model="readonlyFilledPhone"
|
|
label="Téléphone (readonly rempli)"
|
|
:readonly="true"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Avec hint</h2>
|
|
<MalioInputPhone
|
|
label="Téléphone"
|
|
hint="Format international recommandé"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Erreur</h2>
|
|
<MalioInputPhone
|
|
model-value="abc"
|
|
label="Téléphone"
|
|
error="Numéro de téléphone invalide"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Succès</h2>
|
|
<MalioInputPhone
|
|
model-value="+33 6 12 34 56 78"
|
|
label="Téléphone"
|
|
success="Numéro valide"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4 md:col-span-2">
|
|
<h2 class="mb-4 text-xl font-bold">Cas ERP — liste de téléphones (max 2)</h2>
|
|
<p class="mb-3 text-sm text-m-muted">
|
|
Le bouton + s'affiche sur le dernier champ tant que la liste contient moins de {{ MAX_PHONES }} numéros.
|
|
</p>
|
|
<div class="flex flex-col gap-4">
|
|
<MalioInputPhone
|
|
v-for="(phone, index) in phones"
|
|
:key="index"
|
|
v-model="phones[index]"
|
|
:label="`Téléphone ${index + 1}`"
|
|
:addable="index === phones.length - 1 && phones.length < MAX_PHONES"
|
|
@add="addPhone"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
|
|
const readonlyFilledPhone = ref('+33 6 12 34 56 78')
|
|
const phoneValue = ref('')
|
|
const phoneAddable = ref('')
|
|
const phoneFrench = ref('')
|
|
const addClicks = ref(0)
|
|
|
|
const onAdd = () => {
|
|
addClicks.value++
|
|
}
|
|
|
|
const MAX_PHONES = 2
|
|
const phones = ref<string[]>([''])
|
|
|
|
const addPhone = () => {
|
|
if (phones.value.length < MAX_PHONES) {
|
|
phones.value.push('')
|
|
}
|
|
}
|
|
</script>
|