@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.
Files changed (124) hide show
  1. package/dist/{DateFilter-DoCcOfDW.js → DateFilter-_EFzsvvM.js} +1 -1
  2. package/dist/{NumberFilter-9uR8uo6p.js → NumberFilter-CUxEbKJh.js} +1 -1
  3. package/dist/{PeriodFilter-CxN5ini7.js → PeriodFilter-D5ueqtKy.js} +1 -1
  4. package/dist/{SelectFilter-bfxipgvt.js → SelectFilter-BciBNydy.js} +1 -1
  5. package/dist/{TextFilter-yCnWcmW2.js → TextFilter-DMN_WAQB.js} +1 -1
  6. package/dist/components/Amelipro/AmeliproAccordion/AmeliproAccordion.d.ts +1 -1
  7. package/dist/components/Amelipro/AmeliproAccordion/AmeliproAccordionTemplate/AmeliproAccordionTemplate.d.ts +1 -1
  8. package/dist/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.d.ts +1 -1
  9. package/dist/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResultTemplate/AmeliproAccordionResultTemplate.d.ts +1 -1
  10. package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +44 -62
  11. package/dist/components/Amelipro/AmeliproCard/AmeliproCard.d.ts +1 -1
  12. package/dist/components/Amelipro/AmeliproIcon/AmeliproIcon.d.ts +1 -1
  13. package/dist/components/Amelipro/AmeliproIconBtn/AmeliproIconBtn.d.ts +5 -5
  14. package/dist/components/Amelipro/AmeliproMultipleFoldingCard/AmeliproMultipleFoldingCard.d.ts +1 -1
  15. package/dist/components/Amelipro/AmeliproNumberedCard/AmeliproNumberedCard.d.ts +1 -1
  16. package/dist/components/Amelipro/AmeliproPostalAddressField/AmeliproPostalAddressCityRow/AmeliproPostalAddressCityRow.d.ts +24 -32
  17. package/dist/components/Amelipro/AmeliproPostalAddressField/AmeliproPostalAddressField.d.ts +36 -48
  18. package/dist/components/Amelipro/AmeliproSelect/AmeliproSelect.d.ts +44 -62
  19. package/dist/components/Amelipro/AmeliproTabs/AmeliproTabs.d.ts +44 -62
  20. package/dist/components/Amelipro/AmeliproTextArea/AmeliproTextArea.d.ts +0 -4
  21. package/dist/components/Amelipro/AmeliproTextField/AmeliproTextField.d.ts +12 -16
  22. package/dist/components/Captcha/Captcha.d.ts +68 -0
  23. package/dist/components/Captcha/CaptchaAlert.d.ts +13 -0
  24. package/dist/components/Captcha/CaptchaBase.d.ts +55 -0
  25. package/dist/components/Captcha/CaptchaBtn.d.ts +12 -0
  26. package/dist/components/Captcha/CaptchaForm.d.ts +16 -0
  27. package/dist/components/Captcha/CaptchaImg.d.ts +12 -0
  28. package/dist/components/Captcha/CaptchaInformation.d.ts +20 -0
  29. package/dist/components/Captcha/captchaApi.d.ts +41 -0
  30. package/dist/components/Captcha/icons/volumeUp.d.ts +2 -0
  31. package/dist/components/Captcha/locales.d.ts +35 -0
  32. package/dist/components/Captcha/types.d.ts +2 -0
  33. package/dist/components/ChipList/ChipList.d.ts +2 -2
  34. package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +2 -2
  35. package/dist/components/Customs/SyForm/SyForm.d.ts +6 -3
  36. package/dist/components/Customs/SyTextField/SyTextField.d.ts +13 -17
  37. package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +56 -64
  38. package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +47 -64
  39. package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +18 -17
  40. package/dist/components/DatePicker/tests/setup.d.ts +448 -512
  41. package/dist/components/HeaderToolbar/HeaderToolbar.d.ts +4 -4
  42. package/dist/components/NirField/NirField.d.ts +29 -34
  43. package/dist/components/NirField/locales.d.ts +1 -3
  44. package/dist/components/PasswordField/PasswordField.d.ts +2 -0
  45. package/dist/components/PeriodField/PeriodField.d.ts +112 -128
  46. package/dist/components/PhoneField/PhoneField.d.ts +13 -17
  47. package/dist/components/SearchListField/SearchListField.d.ts +2 -2
  48. package/dist/components/SyTextArea/SyTextArea.d.ts +0 -4
  49. package/dist/components/Tables/common/SyTablePagination.d.ts +2 -2
  50. package/dist/components/index.d.ts +1 -0
  51. package/dist/composables/validation/useFormValidation.d.ts +10 -0
  52. package/dist/composables/validation/useValidatable.d.ts +10 -2
  53. package/dist/design-system-v3.js +126 -125
  54. package/dist/design-system-v3.umd.cjs +155 -155
  55. package/dist/main-DISHlqcd.js +34217 -0
  56. package/dist/style.css +1 -1
  57. package/package.json +1 -1
  58. package/src/components/Amelipro/AmeliproFooter/AmeliproFooter.vue +6 -7
  59. package/src/components/Amelipro/AmeliproFooter/__tests__/AmeliproFooter.spec.ts +787 -0
  60. package/src/components/Amelipro/AmeliproFooter/__tests__/__snapshots__/AmeliproFooter.spec.ts.snap +318 -0
  61. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/AmeliproHeaderBrandSection/__tests__/AmeliproHeaderBrandSection.spec.ts +167 -0
  62. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/AmeliproHeaderBrandSection/__tests__/__snapshots__/AmeliproHeaderBrandSection.spec.ts.snap +100 -0
  63. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/__tests__/AmeliproHeaderBar.spec.ts +312 -0
  64. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/__tests__/__snapshots__/AmeliproHeaderBar.spec.ts.snap +98 -0
  65. package/src/components/Amelipro/AmeliproHeader/__tests__/AmeliproHeader.spec.ts +361 -0
  66. package/src/components/Amelipro/AmeliproHeader/__tests__/__snapshots__/AmeliproHeader.spec.ts.snap +22 -0
  67. package/src/components/Amelipro/AmeliproMenu/__tests__/AmeliproMenu.spec.ts +168 -0
  68. package/src/components/Amelipro/AmeliproMenu/__tests__/__snapshots__/AmeliproMenu.spec.ts.snap +295 -0
  69. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/AmeliproDropdownMenuBtn/__tests__/AmeliproDropdownMenuBtn.spec.ts +128 -0
  70. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/AmeliproDropdownMenuBtn/__tests__/__snapshots__/AmeliproDropdownMenuBtn.spec.ts.snap +67 -0
  71. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/__tests__/AmeliproDropdownMenu.spec.ts +266 -0
  72. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/__tests__/__snapshots__/AmeliproDropdownMenu.spec.ts.snap +134 -0
  73. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproMessagingMenuBtn/__tests__/AmeliproMessagingMenuBtn.spec.ts +72 -0
  74. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproMessagingMenuBtn/__tests__/__snapshots__/AmeliproMessagingMenuBtn.spec.ts.snap +71 -0
  75. package/src/components/Amelipro/AmeliproPageLayout/tests/__snapshots__/AmeliproPageLayout.spec.ts.snap +12 -0
  76. package/src/components/Captcha/Captcha.mdx +72 -0
  77. package/src/components/Captcha/Captcha.stories.ts +276 -0
  78. package/src/components/Captcha/Captcha.vue +325 -0
  79. package/src/components/Captcha/CaptchaAlert.vue +60 -0
  80. package/src/components/Captcha/CaptchaBase.vue +219 -0
  81. package/src/components/Captcha/CaptchaBtn.vue +35 -0
  82. package/src/components/Captcha/CaptchaForm.vue +58 -0
  83. package/src/components/Captcha/CaptchaImg.vue +41 -0
  84. package/src/components/Captcha/CaptchaInformation.vue +64 -0
  85. package/src/components/Captcha/captchaApi.ts +111 -0
  86. package/src/components/Captcha/icons/volumeUp.vue +11 -0
  87. package/src/components/Captcha/locales.ts +35 -0
  88. package/src/components/Captcha/readme.md +5 -0
  89. package/src/components/Captcha/tests/Captcha.spec.ts +298 -0
  90. package/src/components/Captcha/tests/__snapshots__/Captcha.spec.ts.snap +716 -0
  91. package/src/components/Captcha/types.ts +2 -0
  92. package/src/components/Customs/Selects/SySelect/SySelect.vue +2 -2
  93. package/src/components/Customs/SyCheckbox/SyCheckbox.vue +4 -0
  94. package/src/components/Customs/SyForm/SyForm.stories.ts +133 -23
  95. package/src/components/Customs/SyForm/SyForm.vue +17 -1
  96. package/src/components/Customs/SyTextField/SyTextField.vue +2 -2
  97. package/src/components/DatePicker/CalendarMode/DatePicker.vue +1 -1
  98. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +110 -6
  99. package/src/components/DatePicker/DateTextInput/DateTextInput.vue +28 -3
  100. package/src/components/NirField/NirField.stories.ts +74 -0
  101. package/src/components/NirField/NirField.vue +34 -9
  102. package/src/components/NirField/locales.ts +1 -3
  103. package/src/components/PasswordField/PasswordField.vue +39 -7
  104. package/src/components/PhoneField/PhoneField.vue +43 -10
  105. package/src/components/index.ts +1 -0
  106. package/src/composables/validation/useFormValidation.ts +46 -8
  107. package/src/composables/validation/useValidatable.ts +19 -8
  108. package/dist/main-DMXtXK3y.js +0 -33458
  109. package/src/components/Amelipro/AmeliproFooter/tests/AmeliproFooter.spec.ts +0 -15
  110. package/src/components/Amelipro/AmeliproFooter/tests/__snapshots__/AmeliproFooter.spec.ts.snap +0 -432
  111. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/AmeliproHeaderBrandSection/tests/AmeliproHeaderBrandSection.spec.ts +0 -15
  112. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/AmeliproHeaderBrandSection/tests/__snapshots__/AmeliproHeaderBrandSection.spec.ts.snap +0 -131
  113. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/tests/AmeliproHeaderBar.spec.ts +0 -15
  114. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/tests/__snapshots__/AmeliproHeaderBar.spec.ts.snap +0 -172
  115. package/src/components/Amelipro/AmeliproHeader/tests/AmeliproHeader.spec.ts +0 -159
  116. package/src/components/Amelipro/AmeliproHeader/tests/__snapshots__/AmeliproHeader.spec.ts.snap +0 -841
  117. package/src/components/Amelipro/AmeliproMenu/tests/AmeliproMenu.spec.ts +0 -85
  118. package/src/components/Amelipro/AmeliproMenu/tests/__snapshots__/AmeliproMenu.spec.ts.snap +0 -537
  119. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/AmeliproDropdownMenuBtn/tests/AmeliproDropdownMenuBtn.spec.ts +0 -16
  120. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/AmeliproDropdownMenuBtn/tests/__snapshots__/AmeliproDropdownMenuBtn.spec.ts.snap +0 -56
  121. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/tests/AmeliproDropdownMenu.spec.ts +0 -28
  122. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproDropdownMenu/tests/__snapshots__/AmeliproDropdownMenu.spec.ts.snap +0 -300
  123. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproMessagingMenuBtn/tests/AmeliproMessagingMenuBtn.spec.ts +0 -16
  124. package/src/components/Amelipro/AmeliproMessagingLayout/AmeliproMessagingMenuBtn/tests/__snapshots__/AmeliproMessagingMenuBtn.spec.ts.snap +0 -89
@@ -43,6 +43,7 @@
43
43
  disableErrorHandling?: boolean
44
44
  nirType?: 'simple' | 'complexe'
45
45
  withoutFieldset?: boolean
46
+ customLocale?: Partial<Record<keyof typeof locales, string>>
46
47
  }>(), {
47
48
  modelValue: undefined,
48
49
  label: 'Identifiant d\'assuré',
@@ -79,6 +80,14 @@
79
80
  disableErrorHandling: false,
80
81
  nirType: 'simple',
81
82
  withoutFieldset: false,
83
+ customLocale: () => ({
84
+ errorRequiredNumber: locales.errorRequiredNumber,
85
+ errorInvalidNumber: locales.errorInvalidNumber,
86
+ errorRequiredKey: locales.errorRequiredKey,
87
+ errorInvalidKey: locales.errorInvalidKey,
88
+ successNumberValid: locales.successNumberValid,
89
+ successKeyValid: locales.successKeyValid,
90
+ } as Partial<Record<keyof typeof locales, string>>),
82
91
  })
83
92
 
84
93
  const emit = defineEmits(['update:modelValue'])
@@ -199,7 +208,7 @@
199
208
  rules.push({
200
209
  type: 'required',
201
210
  options: {
202
- message: locales.errorRequiredNumber,
211
+ message: props.customLocale.errorRequiredNumber,
203
212
  fieldIdentifier: props.numberLabel,
204
213
  },
205
214
  })
@@ -221,13 +230,13 @@
221
230
  if (!value) return true
222
231
  // Ne valider que si tous les caractères sont saisis
223
232
  if (value.length < 13) {
224
- return locales.erreurInvalidNumber
233
+ return props.customLocale.errorInvalidNumber || locales.errorInvalidNumber
225
234
  }
226
235
  const result = checkNIR(value, props.nirType)
227
- return result === true ? true : locales.erreurInvalidNumber
236
+ return result ? true : props.customLocale.errorInvalidNumber || locales.errorInvalidNumber
228
237
  },
229
- message: locales.erreurInvalidNumber,
230
- successMessage: locales.successNumberValid,
238
+ message: props.customLocale.errorInvalidNumber,
239
+ successMessage: props.customLocale.successNumberValid,
231
240
  fieldIdentifier: props.numberLabel,
232
241
  },
233
242
  })
@@ -250,7 +259,7 @@
250
259
  rules.push({
251
260
  type: 'required',
252
261
  options: {
253
- message: locales.errorRequiredKey,
262
+ message: props.customLocale.errorRequiredKey,
254
263
  fieldIdentifier: props.keyLabel,
255
264
  },
256
265
  })
@@ -274,8 +283,8 @@
274
283
  type: 'custom',
275
284
  options: {
276
285
  validate: validateKey,
277
- message: locales.errorInvalidKey,
278
- successMessage: locales.successKeyValid,
286
+ message: props.customLocale.errorInvalidKey,
287
+ successMessage: props.customLocale.successKeyValid,
279
288
  fieldIdentifier: props.keyLabel,
280
289
  },
281
290
  })
@@ -506,7 +515,23 @@
506
515
  })
507
516
 
508
517
  // Rendre le composant auto-validable dans un SyForm
509
- useValidatable(validateOnSubmit)
518
+ useValidatable(
519
+ validateOnSubmit,
520
+ () => {
521
+ try {
522
+ numberValidation.clearValidation()
523
+ }
524
+ catch {
525
+ void 0
526
+ }
527
+ try {
528
+ keyValidation.clearValidation()
529
+ }
530
+ catch {
531
+ void 0
532
+ }
533
+ },
534
+ )
510
535
 
511
536
  defineExpose({
512
537
  validateOnSubmit,
@@ -1,9 +1,7 @@
1
1
  export const locales = {
2
2
  errorRequiredNumber: 'Le numéro de sécurité sociale est requis, ce sont les 13 premiers chiffres sur votre carte vitale.',
3
- errorLengthNumber: (length: number) => `Le numéro de sécurité sociale doit contenir ${length} caractères.`,
4
- erreurInvalidNumber: 'Le numéro de sécurité sociale est invalide.',
3
+ errorInvalidNumber: 'Le numéro de sécurité sociale est invalide.',
5
4
  errorRequiredKey: 'La clé de contrôle est requise, ce sont les 2 derniers chiffres sur votre carte vitale.',
6
- errorLengthKey: (length: number) => `La clé du numéro de sécurité sociale doit contenir ${length} caractères.`,
7
5
  errorInvalidKey: 'La clé de contrôle est invalide.',
8
6
  successNumberValid: 'Le numéro de sécurité sociale est valide.',
9
7
  successKeyValid: 'La clé de contrôle est valide.',
@@ -68,10 +68,13 @@
68
68
  const showEyeIcon = ref(false)
69
69
  const passwordFieldId = ref(`password-field-${Math.random().toString(36).substring(2, 10)}`)
70
70
  const alertMessage = ref('')
71
+ // Force re-render of SyTextField when needed (e.g., after reset)
72
+ const fieldKey = ref(0)
71
73
 
72
74
  const btnLabel = locales.showPassword
73
75
 
74
76
  const password = ref<string | null>(props.modelValue)
77
+ const isProgrammaticChange = ref(false)
75
78
  watch(
76
79
  () => props.modelValue,
77
80
  (newVal) => {
@@ -160,11 +163,14 @@
160
163
  }
161
164
  }, { immediate: true })
162
165
 
163
- watch(() => password.value, () => {
164
- if (props.readonly) return
165
- validateField(password.value, [...defaultRules.value, ...(props.customRules || [])], props.customWarningRules || [], props.customSuccessRules || [])
166
- emit('update:modelValue', password.value)
167
- })
166
+ // Ne pas revalider automatiquement à chaque changement de valeur.
167
+ // La validation est gérée explicitement au blur et à la soumission.
168
+ watch(
169
+ () => password.value,
170
+ (newVal) => {
171
+ emit('update:modelValue', newVal)
172
+ },
173
+ )
168
174
 
169
175
  function togglePasswordVisibility() {
170
176
  showEyeIcon.value = !showEyeIcon.value
@@ -209,8 +215,32 @@
209
215
  return isValid
210
216
  }
211
217
 
218
+ // Nettoie uniquement l'état de validation (messages) sans modifier la valeur
219
+ const clearValidation = () => {
220
+ errors.value = []
221
+ warnings.value = []
222
+ successes.value = []
223
+ }
224
+
225
+ // Reset hook utilisé par SyForm.reset() via useValidatable
226
+ const reset = () => {
227
+ // Réinitialiser d'abord l'état de validation et d'interaction
228
+ clearValidation()
229
+ alertMessage.value = ''
230
+ showEyeIcon.value = false
231
+
232
+ // Réinitialiser le contenu du champ
233
+ isProgrammaticChange.value = true
234
+ password.value = null
235
+ emit('update:modelValue', null)
236
+ isProgrammaticChange.value = false
237
+
238
+ // Forcer la recréation du champ pour réinitialiser l'état interne de Vuetify
239
+ fieldKey.value++
240
+ }
241
+
212
242
  // Intégration avec le système de validation du formulaire
213
- useValidatable(validateOnSubmit)
243
+ useValidatable(validateOnSubmit, clearValidation, reset)
214
244
 
215
245
  defineExpose({
216
246
  showEyeIcon,
@@ -221,6 +251,8 @@
221
251
  hasWarning,
222
252
  hasSuccess,
223
253
  validateOnSubmit,
254
+ clearValidation,
255
+ reset,
224
256
  })
225
257
  </script>
226
258
 
@@ -228,6 +260,7 @@
228
260
  <SyTextField
229
261
  v-bind="Object.fromEntries(Object.entries(options).filter(([key]) => key !== 'btn' && key !== 'icon' && key !== 'variant'))"
230
262
  :id="passwordFieldId"
263
+ :key="fieldKey"
231
264
  v-model="password"
232
265
  :variant-style="props.variantStyle"
233
266
  :color="props.color"
@@ -244,7 +277,6 @@
244
277
  :aria-invalid="hasError"
245
278
  :aria-describedby="`${passwordFieldId}-status${props.customRules && props.customRules.length > 0 ? ' ' + passwordFieldId + '-guidelines' : ''}`"
246
279
  :display-asterisk="props.displayAsterisk"
247
- :rules="[...defaultRules, ...props.customRules]"
248
280
  :autocomplete="props.autocompleteType"
249
281
  class="vd-password"
250
282
  :validate-on="props.isValidateOnBlur ? 'blur lazy' : 'lazy'"
@@ -55,6 +55,8 @@
55
55
  const phoneNumber = ref(props.modelValue || '')
56
56
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
57
57
  const dialCode = ref<string | Record<string, any>>(props.dialCodeModel || '')
58
+ // Force re-render of SySelect when needed (e.g., after reset)
59
+ const dialSelectKey = ref(0)
58
60
  const counter = ref(10)
59
61
  const phoneMask = ref('## ## ## ## ##')
60
62
  const onBlur = ref(false)
@@ -241,6 +243,13 @@
241
243
 
242
244
  const shouldDisableErrorHandling = computed(() => props.disableErrorHandling || props.readonly)
243
245
 
246
+ // When disabling error handling, immediately clear any existing validation state
247
+ watch(shouldDisableErrorHandling, (disabled) => {
248
+ if (disabled) {
249
+ validation.clearValidation()
250
+ }
251
+ })
252
+
244
253
  const validation = useValidation({
245
254
  customRules: validationRules.value,
246
255
  showSuccessMessages: true,
@@ -248,9 +257,9 @@
248
257
  disableErrorHandling: shouldDisableErrorHandling.value,
249
258
  })
250
259
 
251
- const hasError = computed(() => validation.hasError.value)
252
- const hasWarning = computed(() => validation.hasWarning.value)
253
- const hasSuccess = computed(() => validation.hasSuccess.value)
260
+ const hasError = computed(() => !shouldDisableErrorHandling.value && validation.hasError.value)
261
+ const hasWarning = computed(() => !shouldDisableErrorHandling.value && validation.hasWarning.value)
262
+ const hasSuccess = computed(() => !shouldDisableErrorHandling.value && validation.hasSuccess.value)
254
263
 
255
264
  const iconColor = computed(() => {
256
265
  if (shouldDisableErrorHandling.value) return '#222324'
@@ -260,9 +269,9 @@
260
269
  return '#222324'
261
270
  })
262
271
 
263
- const errors = computed(() => validation.errors.value)
264
- const warnings = computed(() => validation.warnings.value)
265
- const successes = computed(() => validation.successes.value)
272
+ const errors = computed(() => shouldDisableErrorHandling.value ? [] : validation.errors.value)
273
+ const warnings = computed(() => shouldDisableErrorHandling.value ? [] : validation.warnings.value)
274
+ const successes = computed(() => shouldDisableErrorHandling.value ? [] : validation.successes.value)
266
275
 
267
276
  const showHelpTextBelow = computed(() => {
268
277
  // Display help text below by default if it exists
@@ -312,8 +321,28 @@
312
321
  return !validation.hasError.value
313
322
  }
314
323
 
324
+ // Reset hook used by SyForm.reset() via useValidatable
325
+ const reset = () => {
326
+ // Reset interaction state and validation FIRST to avoid triggering watchers with errors
327
+ onBlur.value = false
328
+ validation.clearValidation()
329
+
330
+ // Clear content
331
+ phoneNumber.value = ''
332
+ emit('update:modelValue', '')
333
+
334
+ // Clear dial code and restore defaults
335
+ dialCode.value = ''
336
+ emit('update:selectedDialCode', '')
337
+ counter.value = 10
338
+ phoneMask.value = '## ## ## ## ##'
339
+
340
+ // Force SySelect to be recreated to ensure internal classes are reset
341
+ dialSelectKey.value++
342
+ }
343
+
315
344
  // Intégration avec le système de validation du formulaire
316
- useValidatable(validateOnSubmit)
345
+ useValidatable(validateOnSubmit, validation.clearValidation, reset)
317
346
 
318
347
  defineExpose({
319
348
  computedValue,
@@ -342,14 +371,15 @@
342
371
  <div class="phone-field-container">
343
372
  <SySelect
344
373
  v-if="withCountryCode"
374
+ :key="dialSelectKey"
345
375
  v-model="dialCode"
346
376
  :items="dialCodeOptions"
347
377
  :label="locales.indicatifLabel"
348
378
  :outlined="outlinedIndicatif"
349
379
  :required="countryCodeRequired"
350
380
  :aria-required="countryCodeRequired"
351
- :error="hasError"
352
- :error-messages="errors[1]"
381
+ :error="!!errors[1]"
382
+ :error-messages="errors[1] ? [errors[1]] : []"
353
383
  :display-asterisk="displayAsterisk"
354
384
  :disable-error-handling="shouldDisableErrorHandling"
355
385
  :return-object="true"
@@ -426,7 +456,10 @@
426
456
  </div>
427
457
  <div
428
458
  v-if="showHelpTextBelow"
429
- class="help-text-below px-4 mt-1"
459
+ class="help-text-below px-4"
460
+ :style="{
461
+ marginTop: hasError || hasWarning || hasSuccess ? '0.25rem' : '-1rem',
462
+ }"
430
463
  :class="{ 'text-disabled': disabled }"
431
464
  >
432
465
  {{ helpText }}
@@ -63,6 +63,7 @@ export { default as SySelect } from './Customs/Selects/SySelect/SySelect.vue'
63
63
  export { default as SyTextArea } from './SyTextArea/SyTextArea.vue'
64
64
  export { default as SyTextField } from './Customs/SyTextField/SyTextField.vue'
65
65
  export { default as UploadWorkflow } from './UploadWorkflow/UploadWorkflow.vue'
66
+ export { default as Captcha } from './Captcha/Captcha.vue'
66
67
  export { default as SyForm } from './Customs/SyForm/SyForm.vue'
67
68
 
68
69
  // ===========================
@@ -3,12 +3,16 @@ import { provide, inject, ref, type InjectionKey, type Ref } from 'vue'
3
3
  // Type pour les composants pouvant être validés
4
4
  export interface ValidatableComponent {
5
5
  validateOnSubmit: () => Promise<boolean> | boolean
6
+ clearValidation?: () => void
7
+ reset?: () => void
6
8
  }
7
9
 
8
10
  // Clé d'injection pour le registre des composants validables
9
11
  const ValidatableComponentsKey: InjectionKey<{
10
12
  register: (component: ValidatableComponent) => void
11
13
  unregister: (component: ValidatableComponent) => void
14
+ clearAll: () => void
15
+ resetAll: () => void
12
16
  components: Ref<ValidatableComponent[]>
13
17
  }> = Symbol('ValidatableComponents')
14
18
 
@@ -29,23 +33,55 @@ export function useFormValidation() {
29
33
 
30
34
  // Fonction pour supprimer un composant validable du registre
31
35
  const unregister = (component: ValidatableComponent) => {
32
- const index = validatableComponents.value.indexOf(component)
36
+ // Prefer direct reference removal
37
+ let index = validatableComponents.value.indexOf(component)
38
+ // Fallback: locate by matching validateOnSubmit reference
39
+ if (index === -1) {
40
+ index = validatableComponents.value.findIndex(c => c.validateOnSubmit === component.validateOnSubmit)
41
+ }
33
42
  if (index !== -1) {
34
43
  validatableComponents.value.splice(index, 1)
35
44
  }
36
45
  }
37
46
 
47
+ // Fonction pour nettoyer les validations de tous les composants enregistrés
48
+ const clearAll = () => {
49
+ if (validatableComponents.value.length === 0) return
50
+ validatableComponents.value.forEach((component) => {
51
+ try {
52
+ component.clearValidation?.()
53
+ }
54
+ catch {
55
+ // no-op: un composant peut ne pas implémenter clearValidation
56
+ }
57
+ })
58
+ }
59
+
60
+ const resetAll = () => {
61
+ if (validatableComponents.value.length === 0) return
62
+ validatableComponents.value.forEach((component) => {
63
+ try {
64
+ component.reset?.()
65
+ }
66
+ catch {
67
+ // no-op: un composant peut ne pas implémenter reset
68
+ }
69
+ })
70
+ }
71
+
38
72
  // Fournir le registre aux composants enfants
39
73
  provide(ValidatableComponentsKey, {
40
74
  register,
41
75
  unregister,
76
+ clearAll,
77
+ resetAll,
42
78
  components: validatableComponents,
43
79
  })
44
80
 
45
81
  /**
46
- * Valide tous les composants enfants enregistrés
47
- * @returns Promise<boolean> - true si tous les composants sont valides
48
- */
82
+ * Valide tous les composants enfants enregistrés
83
+ * @returns Promise<boolean> - true si tous les composants sont valides
84
+ */
49
85
  const validateAll = async (): Promise<boolean> => {
50
86
  if (validatableComponents.value.length === 0) {
51
87
  return true
@@ -65,6 +101,8 @@ export function useFormValidation() {
65
101
  return {
66
102
  validateAll,
67
103
  validatableComponents,
104
+ clearAll,
105
+ resetAll,
68
106
  }
69
107
  }
70
108
 
@@ -73,19 +111,19 @@ export function useFormValidation() {
73
111
  * @returns Fonction pour s'enregistrer et se désinscrire du formulaire parent
74
112
  */
75
113
  export function useValidatableComponent() {
76
- // Récupérer le registre du formulaire parent
77
114
  const formRegistry = inject(ValidatableComponentsKey, null)
78
-
79
- // Si le composant n'est pas dans un formulaire avec useFormValidation, ne rien faire
80
115
  if (!formRegistry) {
81
116
  return {
82
117
  register: () => {},
83
118
  unregister: () => {},
119
+ clearAll: () => {},
120
+ resetAll: () => {},
84
121
  }
85
122
  }
86
-
87
123
  return {
88
124
  register: formRegistry.register,
89
125
  unregister: formRegistry.unregister,
126
+ clearAll: formRegistry.clearAll,
127
+ resetAll: formRegistry.resetAll,
90
128
  }
91
129
  }
@@ -14,26 +14,37 @@ import { useValidatableComponent } from './useFormValidation'
14
14
  * return isValid
15
15
  * }
16
16
  *
17
+ * const clearValidation = () => {
18
+ * // Logique de nettoyage de la validation
19
+ * }
20
+ *
21
+ * const reset = () => {
22
+ * // Logique de réinitialisation
23
+ * }
24
+ *
17
25
  * // Enregistrer le composant auprès du formulaire parent
18
- * useValidatable(validateOnSubmit)
26
+ * useValidatable(validateOnSubmit, clearValidation, reset)
19
27
  */
20
- export function useValidatable(validateMethod: () => Promise<boolean> | boolean) {
28
+ export function useValidatable(
29
+ validateMethod: () => Promise<boolean> | boolean,
30
+ clearValidation?: () => void,
31
+ reset?: () => void,
32
+ ) {
21
33
  const { register, unregister } = useValidatableComponent()
22
34
  const instance = getCurrentInstance()
23
35
 
36
+ // Keep a stable object reference for register/unregister symmetry
37
+ const componentRef = { validateOnSubmit: validateMethod, clearValidation, reset }
38
+
24
39
  onMounted(() => {
25
40
  if (instance) {
26
- register({
27
- validateOnSubmit: validateMethod,
28
- })
41
+ register(componentRef)
29
42
  }
30
43
  })
31
44
 
32
45
  onBeforeUnmount(() => {
33
46
  if (instance) {
34
- unregister({
35
- validateOnSubmit: validateMethod,
36
- })
47
+ unregister(componentRef)
37
48
  }
38
49
  })
39
50