336cb9e315
Cette PR regroupe **trois évolutions** de la librairie (retours ERP). --- ## 1. MalioDate — saisie manuelle au clavier Ajoute la **saisie manuelle au clavier** `JJ/MM/AAAA` sur `MalioDate` (opt-in via la prop `editable`), en plus de la sélection au calendrier. - `CalendarField` (interne) gagne un mode `editable` : input non `readonly`, masque maska `##/##/####`, buffer local synchronisé sur la valeur, event `commit` au blur / à Entrée. - `MalioDate` parse le texte (`parseDisplayToIso`), valide les bornes (`isDateInRange`) et gère un état d'erreur interne fusionné avec la prop `error` du consommateur. - Le focus ouvre le popover ; la saisie invalide/hors bornes conserve le texte et affiche un message (`invalidMessage`, défaut `Date invalide`) ; la sélection au calendrier ou un changement externe de `modelValue` efface l'erreur. - **Aucune régression** : `editable` défaut `false` ; le reste de la famille Date (DateRange/DateTime/DateWeek) est inchangé. Nouvelles props `MalioDate` : `editable` (boolean, défaut false), `invalidMessage` (string, défaut Date invalide). --- ## 2. MalioInputEmail — bouton « + » d'ajout Ajoute à `MalioInputEmail` le même bouton « + » que `MalioInputPhone` : un bouton optionnel qui émet un event `add` (ex. pour ajouter dynamiquement un autre champ email). - Props `addable` (défaut `false`), `addIconName` (défaut `mdi:plus`), `addButtonLabel` (défaut `Ajouter une adresse email`) ; nouvel event `add()`. - L'icône email étant à droite par défaut, une computed `effectiveIconPosition` la **déplace automatiquement à gauche** quand `addable` est actif, libérant la droite pour le bouton. - Le bouton respecte `disabled`/`readonly` (pas d'émission). - **Aucune régression** : `addable` défaut `false` ; la logique de sanitisation email (espaces, `lowercase`, caret) est intacte. --- ## 3. MalioInputAmount — séparateurs de milliers Affiche les montants groupés à la française (`1 234 567,89` : espace pour les milliers, virgule décimale), **en temps réel** pendant la saisie, tout en gardant une valeur émise propre. - La valeur émise (`modelValue`) reste une **chaîne numérique propre** : point décimal, sans espaces (`'1234567.89'`). Contrat consommateur inchangé. - Fonctions pures extraites dans `composables/amountFormat.ts` (`normalizeAmount`, `formatGroupedAmount`, helpers curseur) — testées en isolation. - À la frappe : parse → émission du modèle propre → reformatage groupé → repositionnement du curseur (comptage des caractères significatifs hors espaces). - `maxLength` borne désormais la **longueur du modèle** (le `maxlength` natif, qui compterait les espaces, est retiré). - **Activé par défaut** sur tous les `MalioInputAmount` ; format FR figé. --- Spec et plan des trois features : `docs/superpowers/specs/` et `docs/superpowers/plans/`. ## Plan de test - [x] `npm run test -- Date.test.ts` → 40 tests OK - [x] `npm run test -- InputEmail.test.ts` → 52 tests OK - [x] `npm run test -- amountFormat.test.ts InputAmount.test.ts` → 50 tests OK - [x] `npm run lint` → 0 erreur - [ ] Vérif manuelle playground `composant/date` : saisie valide → ISO ; `32/13/2026` → texte conservé + rouge ; sélection calendrier efface l'erreur - [ ] Vérif manuelle playground `composant/input/inputEmail` : carte « Ajout dynamique » → le « + » ajoute un champ ; icône à gauche + bouton à droite - [ ] Vérif manuelle playground `composant/input/inputAmount` : carte « Grand montant » → `1234567` s'affiche `1 234 567` en live, `modelValue` émis `1234567` ; curseur cohérent 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #68 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
278 lines
6.8 KiB
Vue
278 lines
6.8 KiB
Vue
<template>
|
|
<Story title="Input/Email">
|
|
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Simple</h2>
|
|
<MalioInputEmail
|
|
v-model="simpleValue"
|
|
label="Adresse email"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Icône à gauche</h2>
|
|
<MalioInputEmail
|
|
v-model="leftIconValue"
|
|
label="Adresse email"
|
|
icon-position="left"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Avec bouton « ajouter »</h2>
|
|
<MalioInputEmail
|
|
v-model="addableValue"
|
|
label="Adresse email"
|
|
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">Sans icône</h2>
|
|
<MalioInputEmail
|
|
v-model="noIconValue"
|
|
label="Adresse email"
|
|
:icon-name="''"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Avec hint</h2>
|
|
<MalioInputEmail
|
|
v-model="hintValue"
|
|
label="Adresse email"
|
|
hint="ex: prenom.nom@malio.fr"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Désactivé</h2>
|
|
<MalioInputEmail
|
|
v-model="disabledValue"
|
|
label="Adresse email"
|
|
disabled
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Readonly</h2>
|
|
<MalioInputEmail
|
|
v-model="readonlyValue"
|
|
label="Adresse email"
|
|
readonly
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Erreur</h2>
|
|
<MalioInputEmail
|
|
v-model="errorValue"
|
|
label="Adresse email"
|
|
error="Adresse email invalide"
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-lg border p-4">
|
|
<h2 class="mb-4 text-xl font-bold">Succès</h2>
|
|
<MalioInputEmail
|
|
v-model="successValue"
|
|
label="Adresse email"
|
|
success="Adresse email valide"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Story>
|
|
</template>
|
|
|
|
<docs lang="md">
|
|
# MalioInputEmail
|
|
|
|
Champ email avec label flottant, icône email par défaut, états visuels
|
|
(erreur / succès) et accessibilité. Basé sur InputText mais ciblé sur la
|
|
saisie d'une adresse email (`type="email"` + `inputmode="email"`).
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## Props détaillées
|
|
|
|
### id
|
|
|
|
- Type: string
|
|
- Description: Identifiant HTML de l'input.
|
|
- Comportement: Si non fourni, un id unique est généré automatiquement
|
|
(préfixe `malio-input-email-`).
|
|
|
|
### label
|
|
|
|
- Type: string
|
|
- Description: Texte affiché comme label flottant.
|
|
- Comportement: Si absent, aucun label n'est rendu.
|
|
|
|
### name
|
|
|
|
- Type: string
|
|
- Description: Attribut name de l'input (utile pour les formulaires).
|
|
|
|
### autocomplete
|
|
|
|
- Type: string
|
|
- Défaut: `off`
|
|
- Description: Active ou configure l'autocomplétion navigateur. La
|
|
valeur par défaut est `off` pour les formulaires de création d'ERP.
|
|
Passer `email` pour permettre au navigateur de suggérer l'adresse
|
|
de l'utilisateur (formulaires de connexion / inscription).
|
|
|
|
### modelValue
|
|
|
|
- Type: string | null | undefined
|
|
- Description: Valeur contrôlée du composant.
|
|
- Comportement:
|
|
- Si défini → composant contrôlé (v-model).
|
|
- Sinon → gestion interne de l'état.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## Apparence & Style
|
|
|
|
### inputClass
|
|
|
|
- Type: string
|
|
- Description: Classes CSS appliquées à l'input.
|
|
|
|
### labelClass
|
|
|
|
- Type: string
|
|
- Description: Classes CSS appliquées au label.
|
|
|
|
### groupClass
|
|
|
|
- Type: string
|
|
- Description: Classes CSS appliquées au conteneur.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## Validation & Contraintes
|
|
|
|
### required
|
|
|
|
- Type: boolean
|
|
- Description: Ajoute l'attribut HTML required.
|
|
|
|
### disabled
|
|
|
|
- Type: boolean
|
|
- Description: Désactive complètement le champ.
|
|
|
|
### readonly
|
|
|
|
- Type: boolean
|
|
- Description: Rend le champ non modifiable mais focusable.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## États & Messages
|
|
|
|
### hint
|
|
|
|
- Type: string
|
|
- Description: Message d'aide affiché sous le champ.
|
|
|
|
### error
|
|
|
|
- Type: string
|
|
- Description: Message d'erreur.
|
|
- Effet:
|
|
- Active l'état visuel erreur.
|
|
- aria-invalid=true
|
|
- Prioritaire sur success et hint.
|
|
|
|
### success
|
|
|
|
- Type: string
|
|
- Description: Message de succès.
|
|
- Effet:
|
|
- Actif uniquement si error est absent.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## Icône
|
|
|
|
### iconName
|
|
|
|
- Type: string
|
|
- Défaut: `mdi:email-outline`
|
|
- Description: Nom Iconify de l'icône affichée. Passer une chaîne
|
|
vide pour ne pas afficher d'icône.
|
|
|
|
### iconPosition
|
|
|
|
- Type: `'left' | 'right'`
|
|
- Défaut: `right`
|
|
|
|
### iconSize
|
|
|
|
- Type: string | number
|
|
- Défaut: `24`
|
|
|
|
### iconColor
|
|
|
|
- Type: string
|
|
- Défaut: `text-m-muted`
|
|
- Description: Classe Tailwind de couleur. Surchargée automatiquement
|
|
par les états erreur / succès.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## Comportement
|
|
|
|
- Aucune validation interne — le composant ne vérifie pas le format
|
|
de l'email. Utiliser la validation HTML native (`type="email"`) ou
|
|
piloter `error` / `success` depuis le parent.
|
|
- `inputmode="email"` est appliqué pour adapter le clavier mobile.
|
|
|
|
## Priorité visuelle
|
|
|
|
1. error
|
|
2. success
|
|
3. neutre
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## Accessibilité
|
|
|
|
- aria-invalid est activé si error existe.
|
|
- aria-describedby référence dynamiquement le message affiché.
|
|
- Fonctionne avec ou sans v-model.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
## Events
|
|
|
|
### update:modelValue
|
|
|
|
- Émis à chaque modification de l'input.
|
|
- Permet l'utilisation avec v-model.
|
|
|
|
</docs>
|
|
|
|
<script setup lang="ts">
|
|
import {ref} from 'vue'
|
|
import MalioInputEmail from '../../components/malio/input/InputEmail.vue'
|
|
|
|
const simpleValue = ref('')
|
|
const addableValue = ref('')
|
|
const addClicks = ref(0)
|
|
const onAdd = () => { addClicks.value += 1 }
|
|
const leftIconValue = ref('')
|
|
const noIconValue = ref('')
|
|
const hintValue = ref('')
|
|
const disabledValue = ref('contact@malio.fr')
|
|
const readonlyValue = ref('readonly@malio.fr')
|
|
const errorValue = ref('pas-un-email')
|
|
const successValue = ref('contact@malio.fr')
|
|
</script>
|