@cnamts/synapse 1.0.12 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DateFilter-DoCcOfDW.js → DateFilter-_EFzsvvM.js} +1 -1
- package/dist/{NumberFilter-9uR8uo6p.js → NumberFilter-CUxEbKJh.js} +1 -1
- package/dist/{PeriodFilter-CxN5ini7.js → PeriodFilter-D5ueqtKy.js} +1 -1
- package/dist/{SelectFilter-bfxipgvt.js → SelectFilter-BciBNydy.js} +1 -1
- package/dist/{TextFilter-yCnWcmW2.js → TextFilter-DMN_WAQB.js} +1 -1
- package/dist/components/Amelipro/AmeliproAccordion/AmeliproAccordion.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproAccordion/AmeliproAccordionTemplate/AmeliproAccordionTemplate.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResultTemplate/AmeliproAccordionResultTemplate.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +44 -62
- package/dist/components/Amelipro/AmeliproCard/AmeliproCard.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproIcon/AmeliproIcon.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproIconBtn/AmeliproIconBtn.d.ts +5 -5
- package/dist/components/Amelipro/AmeliproMultipleFoldingCard/AmeliproMultipleFoldingCard.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproNumberedCard/AmeliproNumberedCard.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproPostalAddressField/AmeliproPostalAddressCityRow/AmeliproPostalAddressCityRow.d.ts +24 -32
- package/dist/components/Amelipro/AmeliproPostalAddressField/AmeliproPostalAddressField.d.ts +36 -48
- package/dist/components/Amelipro/AmeliproSelect/AmeliproSelect.d.ts +44 -62
- package/dist/components/Amelipro/AmeliproTabs/AmeliproTabs.d.ts +44 -62
- package/dist/components/Amelipro/AmeliproTextArea/AmeliproTextArea.d.ts +0 -4
- package/dist/components/Amelipro/AmeliproTextField/AmeliproTextField.d.ts +12 -16
- package/dist/components/Captcha/Captcha.d.ts +68 -0
- package/dist/components/Captcha/CaptchaAlert.d.ts +13 -0
- package/dist/components/Captcha/CaptchaBase.d.ts +55 -0
- package/dist/components/Captcha/CaptchaBtn.d.ts +12 -0
- package/dist/components/Captcha/CaptchaForm.d.ts +16 -0
- package/dist/components/Captcha/CaptchaImg.d.ts +12 -0
- package/dist/components/Captcha/CaptchaInformation.d.ts +20 -0
- package/dist/components/Captcha/captchaApi.d.ts +41 -0
- package/dist/components/Captcha/icons/volumeUp.d.ts +2 -0
- package/dist/components/Captcha/locales.d.ts +35 -0
- package/dist/components/Captcha/types.d.ts +2 -0
- package/dist/components/ChipList/ChipList.d.ts +2 -2
- package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +2 -2
- package/dist/components/Customs/SyForm/SyForm.d.ts +6 -3
- package/dist/components/Customs/SyTextField/SyTextField.d.ts +13 -17
- package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +56 -64
- package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +47 -64
- package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +18 -17
- package/dist/components/DatePicker/tests/setup.d.ts +448 -512
- package/dist/components/HeaderToolbar/HeaderToolbar.d.ts +4 -4
- package/dist/components/NirField/NirField.d.ts +29 -34
- package/dist/components/NirField/locales.d.ts +1 -3
- package/dist/components/PasswordField/PasswordField.d.ts +2 -0
- package/dist/components/PeriodField/PeriodField.d.ts +112 -128
- package/dist/components/PhoneField/PhoneField.d.ts +13 -17
- package/dist/components/SearchListField/SearchListField.d.ts +2 -2
- package/dist/components/SyTextArea/SyTextArea.d.ts +0 -4
- package/dist/components/Tables/common/SyTablePagination.d.ts +2 -2
- package/dist/components/index.d.ts +1 -0
- package/dist/composables/validation/useFormValidation.d.ts +10 -0
- package/dist/composables/validation/useValidatable.d.ts +10 -2
- package/dist/design-system-v3.js +126 -125
- package/dist/design-system-v3.umd.cjs +155 -155
- package/dist/main-DISHlqcd.js +34217 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/Amelipro/AmeliproFooter/AmeliproFooter.vue +6 -7
- package/src/components/Amelipro/AmeliproFooter/__tests__/AmeliproFooter.spec.ts +787 -0
- package/src/components/Amelipro/AmeliproFooter/__tests__/__snapshots__/AmeliproFooter.spec.ts.snap +318 -0
- package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/AmeliproHeaderBrandSection/__tests__/AmeliproHeaderBrandSection.spec.ts +167 -0
- package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/AmeliproHeaderBrandSection/__tests__/__snapshots__/AmeliproHeaderBrandSection.spec.ts.snap +100 -0
- package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/__tests__/AmeliproHeaderBar.spec.ts +312 -0
- package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/__tests__/__snapshots__/AmeliproHeaderBar.spec.ts.snap +98 -0
- package/src/components/Amelipro/AmeliproHeader/__tests__/AmeliproHeader.spec.ts +361 -0
- package/src/components/Amelipro/AmeliproHeader/__tests__/__snapshots__/AmeliproHeader.spec.ts.snap +22 -0
- package/src/components/Amelipro/AmeliproMenu/__tests__/AmeliproMenu.spec.ts +168 -0
- package/src/components/Amelipro/AmeliproMenu/__tests__/__snapshots__/AmeliproMenu.spec.ts.snap +295 -0
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/AmeliproDropdownMenuBtn/__tests__/AmeliproDropdownMenuBtn.spec.ts +128 -0
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/AmeliproDropdownMenuBtn/__tests__/__snapshots__/AmeliproDropdownMenuBtn.spec.ts.snap +67 -0
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/__tests__/AmeliproDropdownMenu.spec.ts +266 -0
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/__tests__/__snapshots__/AmeliproDropdownMenu.spec.ts.snap +134 -0
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproMessagingMenuBtn/__tests__/AmeliproMessagingMenuBtn.spec.ts +72 -0
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproMessagingMenuBtn/__tests__/__snapshots__/AmeliproMessagingMenuBtn.spec.ts.snap +71 -0
- package/src/components/Amelipro/AmeliproPageLayout/tests/__snapshots__/AmeliproPageLayout.spec.ts.snap +12 -0
- package/src/components/Captcha/Captcha.mdx +72 -0
- package/src/components/Captcha/Captcha.stories.ts +276 -0
- package/src/components/Captcha/Captcha.vue +325 -0
- package/src/components/Captcha/CaptchaAlert.vue +60 -0
- package/src/components/Captcha/CaptchaBase.vue +219 -0
- package/src/components/Captcha/CaptchaBtn.vue +35 -0
- package/src/components/Captcha/CaptchaForm.vue +58 -0
- package/src/components/Captcha/CaptchaImg.vue +41 -0
- package/src/components/Captcha/CaptchaInformation.vue +64 -0
- package/src/components/Captcha/captchaApi.ts +111 -0
- package/src/components/Captcha/icons/volumeUp.vue +11 -0
- package/src/components/Captcha/locales.ts +35 -0
- package/src/components/Captcha/readme.md +5 -0
- package/src/components/Captcha/tests/Captcha.spec.ts +298 -0
- package/src/components/Captcha/tests/__snapshots__/Captcha.spec.ts.snap +716 -0
- package/src/components/Captcha/types.ts +2 -0
- package/src/components/Customs/Selects/SySelect/SySelect.vue +2 -2
- package/src/components/Customs/SyCheckbox/SyCheckbox.vue +4 -0
- package/src/components/Customs/SyForm/SyForm.stories.ts +133 -23
- package/src/components/Customs/SyForm/SyForm.vue +17 -1
- package/src/components/Customs/SyTextField/SyTextField.vue +2 -2
- package/src/components/DatePicker/CalendarMode/DatePicker.vue +1 -1
- package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +110 -6
- package/src/components/DatePicker/DateTextInput/DateTextInput.vue +28 -3
- package/src/components/NirField/NirField.stories.ts +74 -0
- package/src/components/NirField/NirField.vue +34 -9
- package/src/components/NirField/locales.ts +1 -3
- package/src/components/PasswordField/PasswordField.vue +39 -7
- package/src/components/PhoneField/PhoneField.vue +43 -10
- package/src/components/index.ts +1 -0
- package/src/composables/validation/useFormValidation.ts +46 -8
- package/src/composables/validation/useValidatable.ts +19 -8
- package/dist/main-DMXtXK3y.js +0 -33458
- package/src/components/Amelipro/AmeliproFooter/tests/AmeliproFooter.spec.ts +0 -15
- package/src/components/Amelipro/AmeliproFooter/tests/__snapshots__/AmeliproFooter.spec.ts.snap +0 -432
- package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/AmeliproHeaderBrandSection/tests/AmeliproHeaderBrandSection.spec.ts +0 -15
- package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/AmeliproHeaderBrandSection/tests/__snapshots__/AmeliproHeaderBrandSection.spec.ts.snap +0 -131
- package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/tests/AmeliproHeaderBar.spec.ts +0 -15
- package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/tests/__snapshots__/AmeliproHeaderBar.spec.ts.snap +0 -172
- package/src/components/Amelipro/AmeliproHeader/tests/AmeliproHeader.spec.ts +0 -159
- package/src/components/Amelipro/AmeliproHeader/tests/__snapshots__/AmeliproHeader.spec.ts.snap +0 -841
- package/src/components/Amelipro/AmeliproMenu/tests/AmeliproMenu.spec.ts +0 -85
- package/src/components/Amelipro/AmeliproMenu/tests/__snapshots__/AmeliproMenu.spec.ts.snap +0 -537
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/AmeliproDropdownMenuBtn/tests/AmeliproDropdownMenuBtn.spec.ts +0 -16
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/AmeliproDropdownMenuBtn/tests/__snapshots__/AmeliproDropdownMenuBtn.spec.ts.snap +0 -56
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/tests/AmeliproDropdownMenu.spec.ts +0 -28
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/tests/__snapshots__/AmeliproDropdownMenu.spec.ts.snap +0 -300
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproMessagingMenuBtn/tests/AmeliproMessagingMenuBtn.spec.ts +0 -16
- package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproMessagingMenuBtn/tests/__snapshots__/AmeliproMessagingMenuBtn.spec.ts.snap +0 -89
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
defineOptions({
|
|
4
4
|
inheritAttrs: false,
|
|
5
5
|
})
|
|
6
|
-
import { mdiInformation,
|
|
6
|
+
import { mdiInformation, mdiChevronDown, mdiCloseCircle } from '@mdi/js'
|
|
7
7
|
import { ref, watch, onMounted, computed, nextTick, type PropType } from 'vue'
|
|
8
8
|
import { useSySelectKeyboard } from './composables/useSySelectKeyboard'
|
|
9
9
|
import { vRgaaSvgFix } from '../../../../directives/rgaaSvgFix'
|
|
@@ -839,7 +839,7 @@
|
|
|
839
839
|
</button>
|
|
840
840
|
<SyIcon
|
|
841
841
|
class="arrow"
|
|
842
|
-
:icon="
|
|
842
|
+
:icon="mdiChevronDown"
|
|
843
843
|
:decorative="true"
|
|
844
844
|
/>
|
|
845
845
|
</template>
|
|
@@ -33,7 +33,7 @@ export const Basic: Story = {
|
|
|
33
33
|
setup() {
|
|
34
34
|
const name = ref('')
|
|
35
35
|
const email = ref('')
|
|
36
|
-
const form = ref<{ validate: () => Promise<boolean
|
|
36
|
+
const form = ref<{ validate: () => Promise<boolean>, reset: () => void, clearValidation: () => void } | null>(null)
|
|
37
37
|
|
|
38
38
|
// Règles de validation selon le design system
|
|
39
39
|
const emailRules = [
|
|
@@ -56,9 +56,11 @@ export const Basic: Story = {
|
|
|
56
56
|
template: `
|
|
57
57
|
<SyForm ref="form" v-bind="args" @submit="submitForm">
|
|
58
58
|
<div class="d-flex flex-column gap-4">
|
|
59
|
-
<SyTextField v-model="name" label="Nom" required />
|
|
60
|
-
<SyTextField v-model="email" label="Email" :custom-rules="emailRules" />
|
|
61
|
-
<
|
|
59
|
+
<SyTextField v-model="name" label="Nom" required class="mb-2" />
|
|
60
|
+
<SyTextField v-model="email" label="Email" :custom-rules="emailRules" class="mb-2" />
|
|
61
|
+
<div class="d-flex gap-3">
|
|
62
|
+
<v-btn type="submit" color="primary">Soumettre</v-btn>
|
|
63
|
+
</div>
|
|
62
64
|
</div>
|
|
63
65
|
</SyForm>
|
|
64
66
|
`,
|
|
@@ -74,9 +76,11 @@ export const Basic: Story = {
|
|
|
74
76
|
<template>
|
|
75
77
|
<SyForm ref="form" v-bind="args" @submit="submitForm">
|
|
76
78
|
<div class="d-flex flex-column gap-4">
|
|
77
|
-
<SyTextField v-model="name" label="Nom" required />
|
|
78
|
-
<SyTextField v-model="email" label="Email" :custom-rules="emailRules" />
|
|
79
|
-
<
|
|
79
|
+
<SyTextField v-model="name" label="Nom" required class="mb-2" />
|
|
80
|
+
<SyTextField v-model="email" label="Email" :custom-rules="emailRules" class="mb-2" />
|
|
81
|
+
<div class="d-flex gap-3">
|
|
82
|
+
<v-btn type="submit" color="primary">Soumettre</v-btn>
|
|
83
|
+
</div>
|
|
80
84
|
</div>
|
|
81
85
|
</SyForm>
|
|
82
86
|
</template>
|
|
@@ -118,7 +122,7 @@ export const CustomValidation: Story = {
|
|
|
118
122
|
const username = ref('')
|
|
119
123
|
const password = ref('')
|
|
120
124
|
const confirmPassword = ref('')
|
|
121
|
-
const form = ref<{ validate: () => Promise<boolean
|
|
125
|
+
const form = ref<{ validate: () => Promise<boolean>, reset: () => void, clearValidation: () => void } | null>(null)
|
|
122
126
|
|
|
123
127
|
// Règles de validation
|
|
124
128
|
const passwordRules = computed(() => [
|
|
@@ -160,17 +164,18 @@ export const CustomValidation: Story = {
|
|
|
160
164
|
<div>
|
|
161
165
|
<SyForm ref="form" v-bind="args" @submit="submitForm">
|
|
162
166
|
<div class="d-flex flex-column gap-4">
|
|
163
|
-
<SyTextField v-model="username" label="Nom d'utilisateur" required />
|
|
164
|
-
<SyTextField v-model="password" label="Mot de passe" type="password" :custom-rules="passwordRules" />
|
|
167
|
+
<SyTextField v-model="username" label="Nom d'utilisateur" required class="mb-2" />
|
|
168
|
+
<SyTextField v-model="password" label="Mot de passe" type="password" :custom-rules="passwordRules" class="mb-2" />
|
|
165
169
|
<SyTextField
|
|
166
170
|
v-model="confirmPassword"
|
|
167
171
|
label="Confirmer le mot de passe"
|
|
168
172
|
type="password"
|
|
169
173
|
required
|
|
170
|
-
:custom-rules="confirmPasswordRules"
|
|
174
|
+
:custom-rules="confirmPasswordRules"
|
|
175
|
+
class="mb-2"
|
|
171
176
|
/>
|
|
172
177
|
<div class="d-flex gap-3">
|
|
173
|
-
<v-btn type="submit" color="primary">S'inscrire</v-btn>
|
|
178
|
+
<v-btn type="submit" color="primary" class="mr-2">S'inscrire</v-btn>
|
|
174
179
|
<v-btn @click="validateManually" color="secondary">Valider sans soumettre</v-btn>
|
|
175
180
|
</div>
|
|
176
181
|
</div>
|
|
@@ -187,16 +192,17 @@ export const CustomValidation: Story = {
|
|
|
187
192
|
<div>
|
|
188
193
|
<SyForm ref="form" @submit="onSubmit">
|
|
189
194
|
<div class="d-flex flex-column gap-4">
|
|
190
|
-
<SyTextField v-model="username" label="Nom d'utilisateur" required />
|
|
191
|
-
<SyTextField v-model="password" label="Mot de passe" type="password" :custom-rules="passwordRules" />
|
|
195
|
+
<SyTextField v-model="username" label="Nom d'utilisateur" required class="mb-2" />
|
|
196
|
+
<SyTextField v-model="password" label="Mot de passe" type="password" :custom-rules="passwordRules" class="mb-2" />
|
|
192
197
|
<SyTextField
|
|
193
198
|
v-model="confirmPassword"
|
|
194
199
|
label="Confirmer le mot de passe"
|
|
195
200
|
type="password"
|
|
196
201
|
:custom-rules="confirmPasswordRules"
|
|
202
|
+
class="mb-2"
|
|
197
203
|
/>
|
|
198
204
|
<div class="d-flex gap-3">
|
|
199
|
-
<v-btn type="submit" color="primary">S'inscrire</v-btn>
|
|
205
|
+
<v-btn type="submit" color="primary" class="mr-2">S'inscrire</v-btn>
|
|
200
206
|
<v-btn @click="validateManually" color="secondary">Valider sans soumettre</v-btn>
|
|
201
207
|
</div>
|
|
202
208
|
</div>
|
|
@@ -296,10 +302,12 @@ export const MixedFields: Story = {
|
|
|
296
302
|
template: `
|
|
297
303
|
<SyForm ref="form" v-bind="args" @submit="submitForm">
|
|
298
304
|
<div class="d-flex flex-column gap-4">
|
|
299
|
-
<SyTextField v-model="formData.name" label="Nom complet" required />
|
|
300
|
-
<SyTextField v-model="formData.email" label="Email" :custom-rules="emailCustomRules" />
|
|
301
|
-
<SySelect v-model="formData.country" :items="countries" label="Pays" required />
|
|
302
|
-
<
|
|
305
|
+
<SyTextField v-model="formData.name" label="Nom complet" required class="mb-2" />
|
|
306
|
+
<SyTextField v-model="formData.email" label="Email" :custom-rules="emailCustomRules" class="mb-2" />
|
|
307
|
+
<SySelect v-model="formData.country" :items="countries" label="Pays" required class="mb-2" />
|
|
308
|
+
<div class="d-flex gap-3">
|
|
309
|
+
<v-btn type="submit" color="primary">Enregistrer</v-btn>
|
|
310
|
+
</div>
|
|
303
311
|
</div>
|
|
304
312
|
</SyForm>
|
|
305
313
|
`,
|
|
@@ -312,10 +320,12 @@ export const MixedFields: Story = {
|
|
|
312
320
|
<template>
|
|
313
321
|
<SyForm ref="form" v-bind="args" @submit="submitForm">
|
|
314
322
|
<div class="d-flex flex-column gap-4">
|
|
315
|
-
<SyTextField v-model="formData.name" label="Nom complet" required />
|
|
316
|
-
<SyTextField v-model="formData.email" label="Email" :customRules="emailCustomRules" />
|
|
317
|
-
<SySelect v-model="formData.country" :items="countries" label="Pays" required />
|
|
318
|
-
<
|
|
323
|
+
<SyTextField v-model="formData.name" label="Nom complet" required class="mb-2" />
|
|
324
|
+
<SyTextField v-model="formData.email" label="Email" :customRules="emailCustomRules" class="mb-2" />
|
|
325
|
+
<SySelect v-model="formData.country" :items="countries" label="Pays" required class="mb-2" />
|
|
326
|
+
<div class="d-flex gap-3">
|
|
327
|
+
<v-btn type="submit" color="primary">Enregistrer</v-btn>
|
|
328
|
+
</div>
|
|
319
329
|
</div>
|
|
320
330
|
</SyForm>
|
|
321
331
|
</template>
|
|
@@ -361,3 +371,103 @@ export const MixedFields: Story = {
|
|
|
361
371
|
],
|
|
362
372
|
},
|
|
363
373
|
}
|
|
374
|
+
|
|
375
|
+
export const Reset: Story = {
|
|
376
|
+
render: args => ({
|
|
377
|
+
components: { SyForm, SyTextField, VBtn },
|
|
378
|
+
setup() {
|
|
379
|
+
const name = ref('')
|
|
380
|
+
const email = ref('')
|
|
381
|
+
const form = ref<{ validate: () => Promise<boolean>, reset: () => void, clearValidation: () => void } | null>(null)
|
|
382
|
+
|
|
383
|
+
// Règles de validation selon le design system
|
|
384
|
+
const emailRules = [
|
|
385
|
+
{ type: 'email', options: { message: 'Format d\'email invalide' } },
|
|
386
|
+
{ type: 'required', options: { message: 'L\'email est obligatoire' } },
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
const submitForm = async () => {
|
|
390
|
+
const isValid = await form.value?.validate()
|
|
391
|
+
if (isValid) {
|
|
392
|
+
alert('Formulaire valide !')
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
alert('Formulaire invalide, veuillez corriger les erreurs.')
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function clearAll() {
|
|
400
|
+
form.value?.reset()
|
|
401
|
+
form.value?.clearValidation()
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return { name, email, emailRules, form, submitForm, clearAll, args }
|
|
405
|
+
},
|
|
406
|
+
template: `
|
|
407
|
+
<SyForm ref="form" v-bind="args" @submit="submitForm" @reset="onFormReset">
|
|
408
|
+
<div class="d-flex flex-column gap-4">
|
|
409
|
+
<SyTextField v-model="name" label="Nom" required class="mb-2" />
|
|
410
|
+
<SyTextField v-model="email" label="Email" :custom-rules="emailRules" class="mb-2" />
|
|
411
|
+
<div class="d-flex gap-3">
|
|
412
|
+
<v-btn color="secondary" class="mr-2" @click="clearAll">Reset</v-btn>
|
|
413
|
+
<v-btn type="submit" color="primary">Soumettre</v-btn>
|
|
414
|
+
</div>
|
|
415
|
+
</div>
|
|
416
|
+
</SyForm>
|
|
417
|
+
`,
|
|
418
|
+
}),
|
|
419
|
+
args: {
|
|
420
|
+
validateOnSubmit: true,
|
|
421
|
+
},
|
|
422
|
+
parameters: {
|
|
423
|
+
sourceCode: [
|
|
424
|
+
{
|
|
425
|
+
name: 'Template',
|
|
426
|
+
code: `
|
|
427
|
+
<template>
|
|
428
|
+
<SyForm ref="form" v-bind="args" @submit="submitForm" @reset="onFormReset">
|
|
429
|
+
<div class="d-flex flex-column gap-4">
|
|
430
|
+
<SyTextField v-model="name" label="Nom" required class="mb-2" />
|
|
431
|
+
<SyTextField v-model="email" label="Email" :custom-rules="emailRules" class="mb-2" />
|
|
432
|
+
<div class="d-flex gap-3">
|
|
433
|
+
<v-btn color="secondary" class="mr-2" @click="clearAll">Reset</v-btn>
|
|
434
|
+
<v-btn type="submit" color="primary">Soumettre</v-btn>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
</SyForm>
|
|
438
|
+
</template>
|
|
439
|
+
`,
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
name: 'Script',
|
|
443
|
+
code: `
|
|
444
|
+
<script setup lang="ts">
|
|
445
|
+
import { ref } from 'vue'
|
|
446
|
+
const name = ref('')
|
|
447
|
+
const email = ref('')
|
|
448
|
+
|
|
449
|
+
// Règles de validation selon le design system
|
|
450
|
+
const emailRules = [
|
|
451
|
+
{ type: 'email', options: { message: "Format d'email invalide" } },
|
|
452
|
+
{ type: 'required', options: { message: "L'email est obligatoire" } },
|
|
453
|
+
]
|
|
454
|
+
|
|
455
|
+
const onSubmit = (event: { isValid: boolean }) => {
|
|
456
|
+
if (event.isValid) {
|
|
457
|
+
alert('Formulaire valide !')
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
alert('Formulaire invalide, veuillez corriger les erreurs.')
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function clearAll() {
|
|
465
|
+
form.value?.reset()
|
|
466
|
+
form.value?.clearValidation()
|
|
467
|
+
}
|
|
468
|
+
</script>
|
|
469
|
+
`,
|
|
470
|
+
},
|
|
471
|
+
],
|
|
472
|
+
},
|
|
473
|
+
}
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
|
|
9
9
|
const emit = defineEmits<{
|
|
10
10
|
(e: 'submit', value: { isValid: boolean }): void
|
|
11
|
+
(e: 'reset'): void
|
|
11
12
|
}>()
|
|
12
13
|
|
|
13
14
|
// Reference vers le formulaire Vuetify
|
|
14
15
|
const form = ref<InstanceType<typeof import('vuetify/components').VForm> | null>(null)
|
|
15
16
|
|
|
16
|
-
const { validateAll } = useFormValidation()
|
|
17
|
+
const { validateAll, clearAll, resetAll } = useFormValidation()
|
|
17
18
|
|
|
18
19
|
// Methode de validation globale qui combine Vuetify et nos composants personnalises
|
|
19
20
|
const validate = async () => {
|
|
@@ -48,7 +49,21 @@
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
const reset = () => {
|
|
52
|
+
// Reset custom components values
|
|
53
|
+
resetAll()
|
|
54
|
+
// Reset field values and validations for Vuetify form
|
|
51
55
|
form.value?.reset()
|
|
56
|
+
form.value?.resetValidation()
|
|
57
|
+
// Notify consumers so they can clear external models (e.g., v-model refs)
|
|
58
|
+
emit('reset')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Clear only validation states (keep current values)
|
|
62
|
+
const clearValidation = () => {
|
|
63
|
+
// Clear Vuetify internal validation state
|
|
64
|
+
form.value?.resetValidation()
|
|
65
|
+
// Clear custom components validation states registered in the form
|
|
66
|
+
clearAll()
|
|
52
67
|
}
|
|
53
68
|
|
|
54
69
|
// Gestion de la soumission du formulaire
|
|
@@ -65,6 +80,7 @@
|
|
|
65
80
|
defineExpose({
|
|
66
81
|
validate,
|
|
67
82
|
reset,
|
|
83
|
+
clearValidation,
|
|
68
84
|
form,
|
|
69
85
|
})
|
|
70
86
|
</script>
|
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
const hasWarning = computed(() => validation.hasWarning.value || props.hasWarning)
|
|
285
285
|
const hasSuccess = computed(() => (validation.hasSuccess.value && !hasError.value && !hasWarning.value) || props.hasSuccess)
|
|
286
286
|
|
|
287
|
-
const errors = computed(() => validation.errors.value)
|
|
287
|
+
const errors = computed(() => [...validation.errors.value, ...(props.errorMessages || [])])
|
|
288
288
|
const warnings = computed(() => validation.warnings.value)
|
|
289
289
|
const successes = computed(() => validation.successes.value)
|
|
290
290
|
|
|
@@ -361,7 +361,7 @@
|
|
|
361
361
|
const syTextFieldRef = ref<ComponentPublicInstance | null>(null)
|
|
362
362
|
|
|
363
363
|
// Intégration avec le système de validation du formulaire
|
|
364
|
-
useValidatable(validateOnSubmit)
|
|
364
|
+
useValidatable(validateOnSubmit, validation.clearValidation)
|
|
365
365
|
|
|
366
366
|
onMounted(() => {
|
|
367
367
|
nextTick(() => {
|
|
@@ -246,6 +246,8 @@
|
|
|
246
246
|
|
|
247
247
|
const textInputValue = ref('')
|
|
248
248
|
const displayFormattedDate = ref('')
|
|
249
|
+
// Force re-render of DateTextInput/SyTextField when needed (e.g., after reset)
|
|
250
|
+
const fieldKey = ref(0)
|
|
249
251
|
const isManualInputActive = ref(false)
|
|
250
252
|
const isFormatting = ref(false)
|
|
251
253
|
const isUpdatingFromInternal = ref(false)
|
|
@@ -399,7 +401,10 @@
|
|
|
399
401
|
if (typeof newValue === 'string') {
|
|
400
402
|
if (props.dateFormatReturn) {
|
|
401
403
|
const date = parseDate(newValue, returnFormat.value)
|
|
402
|
-
if (date)
|
|
404
|
+
if (date) {
|
|
405
|
+
const formattedForDisplay = formatDate(date, props.format)
|
|
406
|
+
textInputValue.value = formattedForDisplay
|
|
407
|
+
}
|
|
403
408
|
}
|
|
404
409
|
else {
|
|
405
410
|
textInputValue.value = newValue
|
|
@@ -415,10 +420,18 @@
|
|
|
415
420
|
updateModel(formattedDate.value)
|
|
416
421
|
withInternalUpdate(() => {
|
|
417
422
|
if (Array.isArray(newValue)) {
|
|
418
|
-
if (newValue.length > 0)
|
|
423
|
+
if (newValue.length > 0) {
|
|
424
|
+
const newFormattedValue = formatDate(newValue[0], props.format)
|
|
425
|
+
if (textInputValue.value !== newFormattedValue) {
|
|
426
|
+
textInputValue.value = newFormattedValue
|
|
427
|
+
}
|
|
428
|
+
}
|
|
419
429
|
}
|
|
420
430
|
else {
|
|
421
|
-
|
|
431
|
+
const newFormattedValue = formatDate(newValue, props.format)
|
|
432
|
+
if (textInputValue.value !== newFormattedValue) {
|
|
433
|
+
textInputValue.value = newFormattedValue
|
|
434
|
+
}
|
|
422
435
|
}
|
|
423
436
|
})
|
|
424
437
|
}
|
|
@@ -436,6 +449,9 @@
|
|
|
436
449
|
|
|
437
450
|
// Handle manual typing sync → model/selection
|
|
438
451
|
watch(textInputValue, (newValue) => {
|
|
452
|
+
// En mode plage, on laisse DateTextInput + handleDateTextInputUpdate
|
|
453
|
+
// piloter la mise à jour du modèle et de selectedDates
|
|
454
|
+
if (props.displayRange) return
|
|
439
455
|
if (isUpdatingFromInternal.value) return
|
|
440
456
|
const date = parseDate(newValue, props.format)
|
|
441
457
|
if (date) {
|
|
@@ -711,6 +727,67 @@
|
|
|
711
727
|
successMessages,
|
|
712
728
|
})
|
|
713
729
|
|
|
730
|
+
/**
|
|
731
|
+
* Gère les mises à jour de DateTextInput avec contrôle
|
|
732
|
+
*/
|
|
733
|
+
const handleDateTextInputUpdate = (value: DateValue) => {
|
|
734
|
+
// Ne pas traiter les mises à jour internes pour éviter les boucles
|
|
735
|
+
if (isUpdatingFromInternal.value) return
|
|
736
|
+
|
|
737
|
+
try {
|
|
738
|
+
isUpdatingFromInternal.value = true
|
|
739
|
+
|
|
740
|
+
// 1) Propager la valeur brute vers le v-model externe
|
|
741
|
+
updateModel(value)
|
|
742
|
+
|
|
743
|
+
// 2) Mettre à jour selectedDates / displayFormattedDate pour refléter la saisie manuelle
|
|
744
|
+
if (!value) {
|
|
745
|
+
selectedDates.value = null
|
|
746
|
+
displayFormattedDate.value = ''
|
|
747
|
+
return
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (Array.isArray(value) && props.displayRange) {
|
|
751
|
+
const [startStr, endStr] = value
|
|
752
|
+
const rf = returnFormat.value
|
|
753
|
+
const startDate = startStr ? parseDate(startStr, rf) || parseDate(startStr, props.format) : null
|
|
754
|
+
const endDate = endStr ? parseDate(endStr, rf) || parseDate(endStr, props.format) : null
|
|
755
|
+
|
|
756
|
+
if (startDate && endDate) {
|
|
757
|
+
selectedDates.value = [startDate, endDate]
|
|
758
|
+
displayFormattedDate.value
|
|
759
|
+
= `${formatDate(startDate, props.format)} - ${formatDate(endDate, props.format)}`
|
|
760
|
+
}
|
|
761
|
+
else if (startDate) {
|
|
762
|
+
// Première date saisie uniquement
|
|
763
|
+
selectedDates.value = [startDate]
|
|
764
|
+
displayFormattedDate.value = formatDate(startDate, props.format)
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
selectedDates.value = null
|
|
768
|
+
displayFormattedDate.value = ''
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
else if (typeof value === 'string') {
|
|
772
|
+
const rf = returnFormat.value
|
|
773
|
+
const date = parseDate(value, rf) || parseDate(value, props.format)
|
|
774
|
+
if (date) {
|
|
775
|
+
selectedDates.value = date
|
|
776
|
+
displayFormattedDate.value = formatDate(date, props.format)
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
selectedDates.value = null
|
|
780
|
+
displayFormattedDate.value = ''
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
finally {
|
|
785
|
+
setTimeout(() => {
|
|
786
|
+
isUpdatingFromInternal.value = false
|
|
787
|
+
}, 0)
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
714
791
|
/**
|
|
715
792
|
* Sync from external v-model
|
|
716
793
|
*/
|
|
@@ -728,7 +805,8 @@
|
|
|
728
805
|
const firstDate = Array.isArray(selectedDates.value)
|
|
729
806
|
? selectedDates.value[0]
|
|
730
807
|
: selectedDates.value
|
|
731
|
-
|
|
808
|
+
const formattedForInput = formatDate(firstDate, props.format)
|
|
809
|
+
textInputValue.value = formattedForInput
|
|
732
810
|
displayFormattedDate.value = displayFormattedDateComputed.value || ''
|
|
733
811
|
}
|
|
734
812
|
validateDates()
|
|
@@ -896,8 +974,30 @@
|
|
|
896
974
|
return textInputValid && errors.value.length === 0
|
|
897
975
|
}
|
|
898
976
|
|
|
977
|
+
// Reset hook utilisé par SyForm.reset() via useValidatable
|
|
978
|
+
const reset = () => {
|
|
979
|
+
// 1) Nettoyer l'état de validation et d'interaction
|
|
980
|
+
clearValidation()
|
|
981
|
+
isDatePickerVisible.value = false
|
|
982
|
+
hasInteracted.value = false
|
|
983
|
+
isManualInputActive.value = false
|
|
984
|
+
|
|
985
|
+
// 2) Réinitialiser la valeur et la sélection SANS déclencher
|
|
986
|
+
// de validation "required" interactive
|
|
987
|
+
withInternalUpdate(() => {
|
|
988
|
+
selectedDates.value = null
|
|
989
|
+
textInputValue.value = ''
|
|
990
|
+
displayFormattedDate.value = ''
|
|
991
|
+
// Synchroniser le modèle externe
|
|
992
|
+
emit('update:modelValue', null)
|
|
993
|
+
})
|
|
994
|
+
|
|
995
|
+
// 3) Forcer la recréation du champ pour réinitialiser l'état interne de Vuetify
|
|
996
|
+
fieldKey.value++
|
|
997
|
+
}
|
|
998
|
+
|
|
899
999
|
// Intégration avec le système de validation du formulaire
|
|
900
|
-
useValidatable(validateOnSubmit)
|
|
1000
|
+
useValidatable(validateOnSubmit, clearValidation, reset)
|
|
901
1001
|
|
|
902
1002
|
defineExpose({
|
|
903
1003
|
validateOnSubmit,
|
|
@@ -923,6 +1023,7 @@
|
|
|
923
1023
|
// Expose for consumers
|
|
924
1024
|
handleDateSelected,
|
|
925
1025
|
resetViewMode,
|
|
1026
|
+
reset,
|
|
926
1027
|
})
|
|
927
1028
|
</script>
|
|
928
1029
|
|
|
@@ -937,6 +1038,7 @@
|
|
|
937
1038
|
<template v-if="props.noCalendar">
|
|
938
1039
|
<DateTextInput
|
|
939
1040
|
ref="dateTextInputRef"
|
|
1041
|
+
:key="fieldKey"
|
|
940
1042
|
v-model="textInputValue"
|
|
941
1043
|
:class="[getMessageClasses(), 'label-hidden-on-focus']"
|
|
942
1044
|
:date-format-return="props.dateFormatReturn"
|
|
@@ -984,7 +1086,8 @@
|
|
|
984
1086
|
<DateTextInput
|
|
985
1087
|
v-bind="menuProps"
|
|
986
1088
|
ref="dateCalendarTextInputRef"
|
|
987
|
-
|
|
1089
|
+
:key="fieldKey"
|
|
1090
|
+
:model-value="textInputValue"
|
|
988
1091
|
:label="labelWithAsterisk || ''"
|
|
989
1092
|
:placeholder="props.placeholder"
|
|
990
1093
|
:format="props.format"
|
|
@@ -1014,6 +1117,7 @@
|
|
|
1014
1117
|
:density="props.density"
|
|
1015
1118
|
:hint="props.hint"
|
|
1016
1119
|
:persistent-hint="props.persistentHint"
|
|
1120
|
+
@update:model-value="handleDateTextInputUpdate"
|
|
1017
1121
|
@click="openDatePickerOnClick"
|
|
1018
1122
|
@focus="openDatePickerOnFocus"
|
|
1019
1123
|
@blur="handleInputBlur"
|
|
@@ -200,6 +200,8 @@
|
|
|
200
200
|
const inputValue = ref('')
|
|
201
201
|
const inputRef = ref<InstanceType<typeof SyTextField> | null>(null)
|
|
202
202
|
const isFormatting = ref(false)
|
|
203
|
+
// Force re-render of SyTextField when needed (e.g., after reset)
|
|
204
|
+
const fieldKey = ref(0)
|
|
203
205
|
|
|
204
206
|
const updateDisplayValue = (dateDisplayText: string) => (inputValue.value = dateDisplayText)
|
|
205
207
|
const updateAriaLabel = (ariaLabelText: string) => (ariaLabel.value = ariaLabelText)
|
|
@@ -940,15 +942,37 @@
|
|
|
940
942
|
isValidating.value = true
|
|
941
943
|
hasInteracted.value = true
|
|
942
944
|
const ok = runRules(inputValue.value)
|
|
943
|
-
|
|
944
|
-
return
|
|
945
|
+
isValidating.value = false
|
|
946
|
+
return ok
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// Reset hook utilisé par SyForm.reset() via useValidatable
|
|
950
|
+
const reset = () => {
|
|
951
|
+
// 1) Nettoyer l'état de validation et d'interaction
|
|
952
|
+
clearValidation()
|
|
953
|
+
isFocused.value = false
|
|
954
|
+
hasInteracted.value = false
|
|
955
|
+
|
|
956
|
+
// 2) Réinitialiser la valeur sans déclencher de validation interactive
|
|
957
|
+
isFormatting.value = true
|
|
958
|
+
inputValue.value = ''
|
|
959
|
+
selectedDates.value = null
|
|
960
|
+
resetState()
|
|
961
|
+
isFormatting.value = false
|
|
962
|
+
|
|
963
|
+
// 3) Synchroniser le modèle externe
|
|
964
|
+
emitModel(null)
|
|
965
|
+
|
|
966
|
+
// 4) Forcer la recréation du champ pour réinitialiser l'état interne de Vuetify
|
|
967
|
+
fieldKey.value++
|
|
945
968
|
}
|
|
946
969
|
|
|
947
970
|
// Intégration avec le système de validation du formulaire
|
|
948
|
-
useValidatable(validateOnSubmit)
|
|
971
|
+
useValidatable(validateOnSubmit, clearValidation, reset)
|
|
949
972
|
|
|
950
973
|
defineExpose({
|
|
951
974
|
validateOnSubmit,
|
|
975
|
+
reset,
|
|
952
976
|
focus() {
|
|
953
977
|
const el: HTMLInputElement | null | undefined = inputRef.value?.$el?.querySelector?.('input:not([type="hidden"])')
|
|
954
978
|
el?.focus({ preventScroll: true })
|
|
@@ -1002,6 +1026,7 @@
|
|
|
1002
1026
|
|
|
1003
1027
|
<template>
|
|
1004
1028
|
<SyTextField
|
|
1029
|
+
:key="fieldKey"
|
|
1005
1030
|
ref="inputRef"
|
|
1006
1031
|
v-model="inputValue"
|
|
1007
1032
|
:append-icon="props.displayIcon && props.displayAppendIcon ? 'calendar' : undefined"
|
|
@@ -349,6 +349,16 @@ const meta: Meta<typeof NirField> = {
|
|
|
349
349
|
},
|
|
350
350
|
},
|
|
351
351
|
},
|
|
352
|
+
customLocale: {
|
|
353
|
+
description: 'Objet permettant de surcharger les messages du composant. Clés supportées : `errorRequiredNumber`, `errorInvalidNumber`, `errorRequiredKey`, `errorInvalidKey`, `successNumberValid`, `successKeyValid`.',
|
|
354
|
+
control: 'object',
|
|
355
|
+
table: {
|
|
356
|
+
type: {
|
|
357
|
+
summary: 'Partial<typeof locales>',
|
|
358
|
+
},
|
|
359
|
+
defaultValue: { summary: '{}' },
|
|
360
|
+
},
|
|
361
|
+
},
|
|
352
362
|
},
|
|
353
363
|
} satisfies Meta<typeof NirField>
|
|
354
364
|
|
|
@@ -1315,3 +1325,67 @@ mais gérer leur affichage différemment, ou utiliser la validation uniquement a
|
|
|
1315
1325
|
`,
|
|
1316
1326
|
}),
|
|
1317
1327
|
}
|
|
1328
|
+
|
|
1329
|
+
export const WithCustomLocale: Story = {
|
|
1330
|
+
args: {
|
|
1331
|
+
...Default.args,
|
|
1332
|
+
required: true,
|
|
1333
|
+
showSuccessMessages: true,
|
|
1334
|
+
customLocale: {
|
|
1335
|
+
errorRequiredNumber: 'Veuillez renseigner votre numéro de sécurité sociale (13 caractères).',
|
|
1336
|
+
errorInvalidNumber: 'Format NIR non reconnu, merci de vérifier.',
|
|
1337
|
+
errorRequiredKey: 'La clé (2 chiffres) est requise.',
|
|
1338
|
+
errorInvalidKey: 'La clé ne correspond pas au NIR saisi.',
|
|
1339
|
+
successNumberValid: 'Numéro reconnu ✅',
|
|
1340
|
+
successKeyValid: 'Clé correspondante ✅',
|
|
1341
|
+
},
|
|
1342
|
+
},
|
|
1343
|
+
parameters: {
|
|
1344
|
+
docs: {
|
|
1345
|
+
description: {
|
|
1346
|
+
story: `
|
|
1347
|
+
### Surcharger les messages avec customLocale
|
|
1348
|
+
|
|
1349
|
+
Utilisez la prop \`customLocale\` pour remplacer les messages par défaut sans toucher au composant.
|
|
1350
|
+
|
|
1351
|
+
Clés supportées :
|
|
1352
|
+
- \`errorRequiredNumber\`
|
|
1353
|
+
- \`erreurInvalidNumber\`
|
|
1354
|
+
- \`errorRequiredKey\`
|
|
1355
|
+
- \`errorInvalidKey\`
|
|
1356
|
+
- \`successNumberValid\`
|
|
1357
|
+
- \`successKeyValid\`
|
|
1358
|
+
`,
|
|
1359
|
+
},
|
|
1360
|
+
},
|
|
1361
|
+
sourceCode: [
|
|
1362
|
+
{
|
|
1363
|
+
name: 'Template',
|
|
1364
|
+
code: `<template>
|
|
1365
|
+
<NirField
|
|
1366
|
+
v-model="value"
|
|
1367
|
+
required
|
|
1368
|
+
show-success-messages
|
|
1369
|
+
:custom-locale="{
|
|
1370
|
+
errorRequiredNumber: 'Veuillez renseigner votre numéro de sécurité sociale (13 caractères).',
|
|
1371
|
+
errorInvalidNumber: 'Format NIR non reconnu, merci de vérifier.',
|
|
1372
|
+
errorRequiredKey: 'La clé (2 chiffres) est requise.',
|
|
1373
|
+
errorInvalidKey: 'La clé ne correspond pas au NIR saisi.',
|
|
1374
|
+
successNumberValid: 'Numéro reconnu ✅',
|
|
1375
|
+
successKeyValid: 'Clé correspondante ✅'
|
|
1376
|
+
}"
|
|
1377
|
+
/>
|
|
1378
|
+
</template>`,
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
name: 'Script',
|
|
1382
|
+
code: `<script setup lang="ts">
|
|
1383
|
+
import { NirField } from '@cnamts/synapse'
|
|
1384
|
+
import { ref } from 'vue'
|
|
1385
|
+
|
|
1386
|
+
const value = ref('')
|
|
1387
|
+
</script>`,
|
|
1388
|
+
},
|
|
1389
|
+
],
|
|
1390
|
+
},
|
|
1391
|
+
}
|