@cnamts/synapse 1.0.4 → 1.0.6

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 (206) hide show
  1. package/dist/DateFilter-BlOpwEVq.js +98 -0
  2. package/dist/NumberFilter-BPUXE4wY.js +121 -0
  3. package/dist/PeriodFilter-B2yx329_.js +112 -0
  4. package/dist/SelectFilter-CedKn1oV.js +136 -0
  5. package/dist/TextFilter-DkhJjRtR.js +114 -0
  6. package/dist/components/Amelipro/AmeliproAccordion/AmeliproAccordion.d.ts +103 -0
  7. package/dist/components/Amelipro/AmeliproAccordion/AmeliproAccordionTemplate/AmeliproAccordionTemplate.d.ts +105 -0
  8. package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +3 -3
  9. package/dist/components/Amelipro/AmeliproCaptcha/AmeliproCaptcha.d.ts +132 -0
  10. package/dist/components/Amelipro/AmeliproCaptcha/types.d.ts +5 -0
  11. package/dist/components/Amelipro/AmeliproCard/AmeliproCard.d.ts +3 -3
  12. package/dist/components/Amelipro/AmeliproCustomSelector/AmeliproCustomSelector.d.ts +126 -0
  13. package/dist/components/Amelipro/AmeliproCustomSelector/types.d.ts +6 -0
  14. package/dist/components/Amelipro/AmeliproIllustratedDataTile/AmeliproIllustratedDataTile.d.ts +1 -1
  15. package/dist/components/Amelipro/AmeliproMultipleFoldingCard/AmeliproMultipleFoldingCard.d.ts +1 -1
  16. package/dist/components/Amelipro/AmeliproSelect/AmeliproSelect.d.ts +3 -3
  17. package/dist/components/Amelipro/AmeliproTable/AmeliproTable.d.ts +190 -0
  18. package/dist/components/Amelipro/AmeliproTable/types.d.ts +34 -0
  19. package/dist/components/Amelipro/AmeliproTabs/AmeliproTabs.d.ts +3 -3
  20. package/dist/components/Amelipro/AmeliproTextField/AmeliproTextField.d.ts +1 -1
  21. package/dist/components/Amelipro/AmeliproTileBtn/AmeliproTileBtn.d.ts +1 -1
  22. package/dist/components/Amelipro/types.d.ts +6 -0
  23. package/dist/components/CookieBanner/CookieBanner.d.ts +1 -1
  24. package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +11 -2
  25. package/dist/components/Customs/Selects/SySelect/composables/useSySelectKeyboard.d.ts +6 -1
  26. package/dist/components/Customs/SyTextField/SyTextField.d.ts +3 -1
  27. package/dist/components/DataList/DataList.d.ts +9 -0
  28. package/dist/components/DataListGroup/DataListGroup.d.ts +10 -1
  29. package/dist/components/DataListItem/DataListItem.d.ts +1 -1
  30. package/dist/components/DataListItem/config.d.ts +1 -1
  31. package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +18 -8
  32. package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +16 -6
  33. package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +6 -1
  34. package/dist/components/DatePicker/composables/useDateInputEditing.d.ts +17 -8
  35. package/dist/components/DatePicker/composables/useKeyboardEvents.d.ts +41 -0
  36. package/dist/components/DatePicker/composables/useManualDateValidation.d.ts +4 -9
  37. package/dist/components/DatePicker/utils/dateFormattingUtils.d.ts +72 -0
  38. package/dist/components/DatePicker/utils/validationUtils.d.ts +38 -0
  39. package/dist/components/HeaderBar/HeaderBurgerMenu/HeaderBurgerMenu.d.ts +9 -3
  40. package/dist/components/HeaderBar/HeaderBurgerMenu/HeaderMenuItem/HeaderMenuItem.d.ts +6 -1
  41. package/dist/components/HeaderBar/HeaderBurgerMenu/HeaderMenuSection/HeaderMenuSection.d.ts +11 -1
  42. package/dist/components/HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.d.ts +11 -1
  43. package/dist/components/HeaderBar/HeaderBurgerMenu/locals.d.ts +2 -0
  44. package/dist/components/HeaderBar/HeaderBurgerMenu/useMenuPosition.d.ts +4 -0
  45. package/dist/components/NirField/NirField.d.ts +14 -4
  46. package/dist/components/PeriodField/PeriodField.d.ts +24 -4
  47. package/dist/components/Tables/common/SyTablePagination.d.ts +10 -0
  48. package/dist/components/index.d.ts +4 -0
  49. package/dist/composables/index.d.ts +1 -0
  50. package/dist/composables/usePagination.d.ts +16 -0
  51. package/dist/design-system-v3.js +165 -160
  52. package/dist/design-system-v3.umd.cjs +120 -138
  53. package/dist/directives/lockFocus.d.ts +17 -0
  54. package/dist/{main-BzyNNvHX.js → main-BXPFSAB4.js} +14664 -13282
  55. package/dist/style.css +1 -0
  56. package/package.json +5 -2
  57. package/src/assets/amelipro/apTheme.scss +149 -0
  58. package/src/assets/amelipro/apTokens.scss +0 -148
  59. package/src/assets/overrides/_btns.scss +15 -0
  60. package/src/assets/overrides/_container.scss +36 -0
  61. package/src/assets/overrides/_forms.scss +7 -0
  62. package/src/assets/{_spacers.scss → overrides/_spacers.scss} +0 -7
  63. package/src/assets/overrides/_tables.scss +18 -0
  64. package/src/assets/overrides/_tooltips.scss +10 -0
  65. package/src/assets/overrides/_typography.scss +196 -0
  66. package/src/assets/settings.scss +11 -51
  67. package/src/assets/themes.scss +10 -0
  68. package/src/assets/tokens.scss +9 -156
  69. package/src/components/Accordion/composables/__tests__/useAccordionGroupCommunication.spec.ts +80 -40
  70. package/src/components/Amelipro/AmeliproAccordion/AmeliproAccordion.mdx +15 -0
  71. package/src/components/Amelipro/AmeliproAccordion/AmeliproAccordion.stories.ts +83 -0
  72. package/src/components/Amelipro/AmeliproAccordion/AmeliproAccordion.vue +86 -0
  73. package/src/components/Amelipro/AmeliproAccordion/AmeliproAccordionTemplate/AmeliproAccordionTemplate.vue +242 -0
  74. package/src/components/Amelipro/AmeliproAccordion/AmeliproAccordionTemplate/__tests__/AmeliproAccordionTemplate.spec.ts +20 -0
  75. package/src/components/Amelipro/AmeliproAccordion/AmeliproAccordionTemplate/__tests__/__snapshots__/AmeliproAccordionTemplate.spec.ts.snap +124 -0
  76. package/src/components/Amelipro/AmeliproAccordion/__tests__/AmeliproAccordion.spec.ts +20 -0
  77. package/src/components/Amelipro/AmeliproAccordion/__tests__/__snapshots__/AmeliproAccordion.spec.ts.snap +124 -0
  78. package/src/components/Amelipro/AmeliproCaptcha/AmeliproCaptcha.mdx +15 -0
  79. package/src/components/Amelipro/AmeliproCaptcha/AmeliproCaptcha.stories.ts +87 -0
  80. package/src/components/Amelipro/AmeliproCaptcha/AmeliproCaptcha.vue +233 -0
  81. package/src/components/Amelipro/AmeliproCaptcha/__tests__/AmeliproCaptcha.spec.ts +24 -0
  82. package/src/components/Amelipro/AmeliproCaptcha/__tests__/__snapshots__/AmeliproCaptcha.spec.ts.snap +384 -0
  83. package/src/components/Amelipro/AmeliproCaptcha/types.d.ts +5 -0
  84. package/src/components/Amelipro/AmeliproCustomSelector/AmeliproCustomSelector.mdx +15 -0
  85. package/src/components/Amelipro/AmeliproCustomSelector/AmeliproCustomSelector.stories.ts +143 -0
  86. package/src/components/Amelipro/AmeliproCustomSelector/AmeliproCustomSelector.vue +351 -0
  87. package/src/components/Amelipro/AmeliproCustomSelector/__tests__/AmeliproCustomSelector.spec.ts +50 -0
  88. package/src/components/Amelipro/AmeliproCustomSelector/__tests__/__snapshots__/AmeliproCustomSelector.spec.ts.snap +186 -0
  89. package/src/components/Amelipro/AmeliproCustomSelector/types.d.ts +6 -0
  90. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/AmeliproHeaderBrandSection/tests/__snapshots__/AmeliproHeaderBrandSection.spec.ts.snap +1 -1
  91. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/tests/__snapshots__/AmeliproHeaderBar.spec.ts.snap +1 -1
  92. package/src/components/Amelipro/AmeliproHeader/tests/__snapshots__/AmeliproHeader.spec.ts.snap +1 -708
  93. package/src/components/Amelipro/AmeliproMenu/tests/__snapshots__/AmeliproMenu.spec.ts.snap +1 -1
  94. package/src/components/Amelipro/AmeliproPageLayout/tests/__snapshots__/AmeliproPageLayout.spec.ts.snap +1 -708
  95. package/src/components/Amelipro/AmeliproTable/AmeliproTable.mdx +22 -0
  96. package/src/components/Amelipro/AmeliproTable/AmeliproTable.stories.ts +550 -0
  97. package/src/components/Amelipro/AmeliproTable/AmeliproTable.vue +421 -0
  98. package/src/components/Amelipro/AmeliproTable/__tests__/AmeliproTable.spec.ts +72 -0
  99. package/src/components/Amelipro/AmeliproTable/__tests__/__snapshots__/AmeliproTable.spec.ts.snap +427 -0
  100. package/src/components/Amelipro/AmeliproTable/types.d.ts +34 -0
  101. package/src/components/Amelipro/ServiceMenu/ServiceMenu.vue +12 -1
  102. package/src/components/Amelipro/ServiceMenu/tests/__snapshots__/ServiceMenu.spec.ts.snap +0 -820
  103. package/src/components/Amelipro/types.ts +8 -0
  104. package/src/components/CollapsibleList/CollapsibleList.vue +0 -2
  105. package/src/components/CookieBanner/CookieBanner.vue +1 -3
  106. package/src/components/CopyBtn/CopyBtn.vue +9 -2
  107. package/src/components/CopyBtn/tests/CopyBtn.spec.ts +3 -1
  108. package/src/components/Customs/Selects/SySelect/Accessibilite.mdx +7 -0
  109. package/src/components/Customs/Selects/SySelect/Accessibilite.stories.ts +4 -1
  110. package/src/components/Customs/Selects/SySelect/SySelect.stories.ts +80 -0
  111. package/src/components/Customs/Selects/SySelect/SySelect.vue +280 -34
  112. package/src/components/Customs/Selects/SySelect/composables/tests/useSySelectKeyboard.spec.ts +14 -5
  113. package/src/components/Customs/Selects/SySelect/composables/useSySelectKeyboard.ts +129 -12
  114. package/src/components/Customs/SyIcon/SyIcon.spec.ts +3 -0
  115. package/src/components/Customs/SyTextField/Accessibilite.stories.ts +3 -1
  116. package/src/components/Customs/SyTextField/SyTextField.stories.ts +79 -6
  117. package/src/components/Customs/SyTextField/SyTextField.vue +218 -24
  118. package/src/components/DataList/Accessibilite.stories.ts +4 -0
  119. package/src/components/DataList/DataList.vue +19 -12
  120. package/src/components/DataListGroup/Accessibilite.stories.ts +4 -0
  121. package/src/components/DataListGroup/DataListGroup.vue +32 -15
  122. package/src/components/DataListItem/DataListItem.vue +14 -11
  123. package/src/components/DataListItem/config.ts +1 -1
  124. package/src/components/DataListItem/tests/DataListItem.spec.ts +2 -2
  125. package/src/components/DatePicker/CalendarMode/DatePicker.vue +12 -7
  126. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.stories.ts +174 -0
  127. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +27 -5
  128. package/src/components/DatePicker/DatePickerValidationExample/DatePickerValidation.stories.ts +286 -0
  129. package/src/components/DatePicker/DateTextInput/DateRange.stories.ts +1 -1
  130. package/src/components/DatePicker/DateTextInput/DateTextInput.vue +29 -31
  131. package/src/components/DatePicker/composables/tests/useManualDateValidation.spec.ts +11 -3
  132. package/src/components/DatePicker/composables/useDateInputEditing.ts +73 -209
  133. package/src/components/DatePicker/composables/useKeyboardEvents.ts +149 -0
  134. package/src/components/DatePicker/composables/useManualDateValidation.ts +27 -68
  135. package/src/components/DatePicker/utils/dateFormattingUtils.ts +228 -0
  136. package/src/components/DatePicker/utils/validationUtils.ts +90 -0
  137. package/src/components/DialogBox/Accessibilite.stories.ts +4 -0
  138. package/src/components/DialogBox/DialogBox.stories.ts +10 -10
  139. package/src/components/DialogBox/DialogBox.vue +83 -21
  140. package/src/components/DialogBox/tests/__snapshots__/DialogBox.spec.ts.snap +12 -6
  141. package/src/components/FileUpload/tests/FileUpload.spec.ts +3 -0
  142. package/src/components/FooterBar/FooterBar.vue +1 -0
  143. package/src/components/HeaderBar/Accessibilite.stories.ts +4 -0
  144. package/src/components/HeaderBar/HeaderBar.mdx +47 -22
  145. package/src/components/HeaderBar/HeaderBar.stories.ts +54 -13
  146. package/src/components/HeaderBar/HeaderBar.vue +2 -1
  147. package/src/components/HeaderBar/HeaderBurgerMenu/Accessibilite.stories.ts +4 -0
  148. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderBurgerMenu.stories.ts +160 -82
  149. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderBurgerMenu.vue +41 -56
  150. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuItem/HeaderMenuItem.vue +10 -3
  151. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuSection/HeaderMenuSection.vue +41 -18
  152. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuSection/tests/HeaderMenuSection.spec.ts +7 -2
  153. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.stories.ts +36 -9
  154. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.vue +41 -9
  155. package/src/components/HeaderBar/HeaderBurgerMenu/locals.ts +2 -0
  156. package/src/components/HeaderBar/HeaderBurgerMenu/tests/HeaderBurgerMenu.spec.ts +1 -1
  157. package/src/components/HeaderBar/HeaderBurgerMenu/tests/__snapshots__/HeaderBurgerMenu.spec.ts.snap +8 -3
  158. package/src/components/HeaderBar/HeaderBurgerMenu/useMenuPosition.ts +50 -0
  159. package/src/components/HeaderBar/HeaderLogo/HeaderLogo.vue +5 -5
  160. package/src/components/HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue +7 -4
  161. package/src/components/HeaderBar/locales.ts +1 -1
  162. package/src/components/HeaderBar/tests/__snapshots__/HeaderBar.spec.ts.snap +2 -1
  163. package/src/components/HeaderLoading/HeaderLoading.vue +0 -1
  164. package/src/components/HeaderNavigationBar/tests/HeaderNavigationBar.spec.ts +2 -0
  165. package/src/components/LangBtn/LangBtn.vue +0 -3
  166. package/src/components/LogoBrandSection/Accessibilite.stories.ts +4 -1
  167. package/src/components/LogoBrandSection/LogoBrandSection.stories.ts +2 -2
  168. package/src/components/LogoBrandSection/LogoBrandSection.vue +13 -8
  169. package/src/components/LogoBrandSection/locales.ts +1 -1
  170. package/src/components/LogoBrandSection/tests/LogoBrandSection.spec.ts +1 -1
  171. package/src/components/LogoBrandSection/tests/__snapshots__/LogoBrandSection.spec.ts.snap +6 -4
  172. package/src/components/NirField/NirField.vue +5 -5
  173. package/src/components/NirField/tests/NirField.spec.ts +78 -12
  174. package/src/components/Tables/SyServerTable/tests/SyServerTable.spec.ts +104 -6
  175. package/src/components/Tables/common/TableHeader.vue +10 -7
  176. package/src/components/Tables/common/tableAccessibilityUtils.ts +13 -2
  177. package/src/components/Tables/common/useTableAria.ts +17 -1
  178. package/src/components/UserMenuBtn/tests/UserMenuBtn.spec.ts +2 -1
  179. package/src/components/index.ts +4 -0
  180. package/src/composables/date/tests/useDatePickerAccessibility.spec.ts +34 -5
  181. package/src/composables/index.ts +3 -0
  182. package/src/composables/useFilterable/useFilterable.ts +13 -1
  183. package/src/composables/usePagination.ts +103 -0
  184. package/src/directives/lockFocus.ts +48 -0
  185. package/src/main.ts +1 -2
  186. package/src/stories/Accessibilite/Aculturation/SensibilisationAccessibilite.mdx +1 -8
  187. package/src/stories/Accessibilite/{Aculturation/AuditDesignSystem.mdx → AuditDesignSystem.mdx} +1 -1
  188. package/src/stories/Accessibilite/KitDePreAudit/Outils/Tanaguru/FauxPositifs.mdx +102 -0
  189. package/src/stories/Accessibilite/KitDePreAudit/Outils/Tanaguru/FauxPositifs.stories.ts +219 -0
  190. package/src/stories/Accessibilite/KitDePreAudit/Outils/{Tanaguru.mdx → Tanaguru/Utilisation.mdx} +1 -1
  191. package/src/stories/DesignTokens/ColorIntegrationExample.vue +43 -0
  192. package/src/stories/DesignTokens/Colors.mdx +2 -0
  193. package/src/stories/DesignTokens/colors.stories.ts +9 -0
  194. package/src/vuetifyConfig.ts +3 -3
  195. package/dist/DateFilter-yrwJv_2R.js +0 -95
  196. package/dist/NumberFilter-BQXtywZI.js +0 -117
  197. package/dist/PeriodFilter-BYXVSzr5.js +0 -108
  198. package/dist/SelectFilter-CJV_mlN3.js +0 -133
  199. package/dist/TextFilter-DN0ejYIs.js +0 -110
  200. package/dist/design-system-v3.css +0 -1
  201. package/dist/directives/letterSpacing.d.ts +0 -27
  202. package/src/assets/_fonts.scss +0 -6
  203. package/src/assets/_typography.scss +0 -157
  204. package/src/directives/letterSpacing.ts +0 -233
  205. /package/src/assets/{_elevations.scss → overrides/_elevations.scss} +0 -0
  206. /package/src/assets/{_radius.scss → overrides/_radius.scss} +0 -0
@@ -166,8 +166,8 @@ describe('useManualDateValidation', () => {
166
166
 
167
167
  it('devrait appeler validateField avec les règles personnalisées', () => {
168
168
  const date = new Date('2023-01-01')
169
- const customRules = [{ type: 'custom', options: {} }]
170
- const customWarningRules = [{ type: 'warning', options: {} }]
169
+ const customRules = [{ type: 'custom', options: { validate: () => true } }]
170
+ const customWarningRules = [{ type: 'warning', options: { validate: () => true } }]
171
171
 
172
172
  mockIsDateComplete.mockReturnValue(true)
173
173
  mockValidateDateFormat.mockReturnValue({ isValid: true, message: '' })
@@ -189,7 +189,15 @@ describe('useManualDateValidation', () => {
189
189
 
190
190
  validateManualInput('01/01/2023')
191
191
 
192
- expect(mockValidateField).toHaveBeenCalledWith(date, customRules, customWarningRules)
192
+ // Vérifier que validateField a été appelé
193
+ expect(mockValidateField).toHaveBeenCalled()
194
+ // Vérifier que le premier argument est la date attendue
195
+ expect(mockValidateField.mock.calls[0][0]).toBe(date)
196
+ // Vérifier la structure des règles sans comparer les références de fonctions
197
+ expect(mockValidateField.mock.calls[0][1][0].type).toBe('custom')
198
+ expect(typeof mockValidateField.mock.calls[0][1][0].options.validate).toBe('function')
199
+ expect(mockValidateField.mock.calls[0][2][0].type).toBe('warning')
200
+ expect(typeof mockValidateField.mock.calls[0][2][0].options.validate).toBe('function')
193
201
  })
194
202
 
195
203
  it('devrait retourner le résultat de validateField', () => {
@@ -1,31 +1,36 @@
1
- // Pas besoin d'importer Ref car il n'est pas utilisé
1
+ import { useKeyboardEvents } from './useKeyboardEvents'
2
+ import {
3
+ formatDateInput as formatDateInputUtil,
4
+ getDateDescription as getDateDescriptionUtil,
5
+ type FormatDateInputResult,
6
+ } from '../utils/dateFormattingUtils'
2
7
 
3
8
  /**
4
9
  * Options pour le composable useDateInputEditing
5
10
  */
6
11
  export interface DateInputEditingOptions {
7
- // Format de date (ex: 'DD/MM/YYYY')
12
+ /**
13
+ * Format de date (ex: 'DD/MM/YYYY')
14
+ */
8
15
  format: string
9
- // Fonction pour mettre à jour la valeur d'affichage
16
+ /**
17
+ * Fonction pour mettre à jour la valeur d'affichage
18
+ */
10
19
  updateDisplayValue: (value: string) => void
11
- // Fonction pour mettre à jour l'attribut aria-label (pour l'accessibilité)
20
+ /**
21
+ * Fonction pour mettre à jour l'attribut aria-label (pour l'accessibilité)
22
+ */
12
23
  updateAriaLabel?: (value: string) => void
13
- // Caractère à utiliser pour les positions non remplies (défaut: '_')
24
+ /**
25
+ * Caractère à utiliser pour les positions non remplies
26
+ */
14
27
  placeholderChar?: string
15
- // Si true, utilise des caractères invisibles pour les lecteurs d'écran
28
+ /**
29
+ * Si true, utilise des caractères invisibles pour les lecteurs d'écran
30
+ */
16
31
  accessiblePlaceholders?: boolean
17
32
  }
18
33
 
19
- /**
20
- * Résultat du formatage d'une date
21
- */
22
- export interface FormatDateInputResult {
23
- // Valeur formatée
24
- formatted: string
25
- // Position du curseur après formatage
26
- cursorPos: number
27
- }
28
-
29
34
  /**
30
35
  * Composable pour gérer l'édition manuelle des dates
31
36
  * Ce composable fournit des fonctions pour formater les dates pendant la saisie
@@ -44,162 +49,63 @@ export const useDateInputEditing = (options: DateInputEditingOptions) => {
44
49
  } = options
45
50
 
46
51
  /**
47
- * Formate une entrée de date en ajoutant les séparateurs appropriés
48
- *
49
- * @param input - Chaîne de caractères saisie
50
- * @param cursorPosition - Position actuelle du curseur
51
- * @returns Objet contenant la chaîne formatée et la nouvelle position du curseur
52
- */
53
- const formatDateInput = (input: string, cursorPosition?: number): FormatDateInputResult => {
54
- // Nettoyer l'entrée pour ne garder que les chiffres
55
- let cleanedInput = input.replace(/[^\d]/g, '')
56
-
57
- // Déterminer le séparateur utilisé dans le format
58
- const separator = format.match(/[^DMY]/)?.[0] || '/'
59
-
60
- // Calculer la position du curseur dans l'entrée nettoyée (sans séparateurs)
61
- const inputBeforeCursor = input.substring(0, cursorPosition || 0)
62
- const digitsBeforeCursor = inputBeforeCursor.replace(/[^\d]/g, '').length
52
+ * Détermine le séparateur utilisé dans le format
53
+ */
54
+ const separator = format.match(/[^DMY]/)?.[0] || '/'
63
55
 
64
- // Extraire les groupes de chiffres du format (DD, MM, YYYY)
65
- const digitGroups: string[] = []
66
- let currentGroup = ''
67
-
68
- for (const char of format) {
69
- if (['D', 'M', 'Y'].includes(char.toUpperCase())) {
70
- currentGroup += char
71
- }
72
- else if (currentGroup) {
73
- digitGroups.push(currentGroup)
74
- currentGroup = ''
75
- }
76
- }
77
-
78
- // Ajouter le dernier groupe s'il existe
79
- if (currentGroup) {
80
- digitGroups.push(currentGroup)
81
- }
82
-
83
- // Calculer le nombre total de chiffres attendus dans le format
84
- const expectedDigits = format.replace(/[^DMY]/g, '').length
56
+ /**
57
+ * Wrapper pour formatDateInput avec les options prédéfinies
58
+ */
59
+ const formatDateInput = (input: string, cursorPosition?: number): FormatDateInputResult => {
60
+ return formatDateInputUtil(input, format, {
61
+ cursorPosition,
62
+ placeholderChar,
63
+ })
64
+ }
85
65
 
86
- // Limiter le nombre de chiffres saisis au nombre attendu
87
- if (cleanedInput.length > expectedDigits) {
88
- cleanedInput = cleanedInput.substring(0, expectedDigits)
66
+ /**
67
+ * Gère la suppression des séparateurs
68
+ */
69
+ const handleBackspace = (event: KeyboardEvent & { target: HTMLInputElement }): void => {
70
+ const input = event.target
71
+ if (!input.selectionStart || input.selectionStart !== input.selectionEnd) {
72
+ return
89
73
  }
90
74
 
91
- // Construire la chaîne formatée
92
- let result = ''
93
- let digitIndex = 0
94
-
95
- // Parcourir les groupes de chiffres pour construire la date formatée
96
- for (let groupIndex = 0; groupIndex < digitGroups.length; groupIndex++) {
97
- const group = digitGroups[groupIndex]
98
- const groupLength = group.length
99
-
100
- // Ajouter les chiffres pour ce groupe
101
- for (let j = 0; j < groupLength; j++) {
102
- if (digitIndex < cleanedInput.length) {
103
- result += cleanedInput[digitIndex]
104
- digitIndex++
105
- }
106
- else {
107
- // Utiliser le caractère de placeholder configuré pour les positions non remplies
108
- result += placeholderChar
109
- }
110
- }
111
-
112
- // Ajouter le séparateur après chaque groupe sauf le dernier
113
- if (groupIndex < digitGroups.length - 1) {
114
- result += separator
115
- }
116
- }
75
+ const cursorPos = input.selectionStart
76
+ const charBeforeCursor = input.value[cursorPos - 1]
117
77
 
118
- // Calculer la nouvelle position du curseur
119
- let newCursorPos = 0
120
- let digitCount = 0
78
+ // Si le caractère avant le curseur n'est pas un chiffre
79
+ if (!/\d/.test(charBeforeCursor)) {
80
+ event.preventDefault()
121
81
 
122
- // Parcourir le résultat formaté pour trouver la position du curseur
123
- for (let i = 0; i < result.length && digitCount < digitsBeforeCursor; i++) {
124
- newCursorPos++
125
- if (/\d/.test(result[i])) {
126
- digitCount++
127
- }
128
- }
82
+ const newValue = input.value.substring(0, cursorPos - 2) + input.value.substring(cursorPos)
83
+ updateDisplayValue(newValue)
129
84
 
130
- return {
131
- formatted: result,
132
- cursorPos: Math.min(newCursorPos, result.length),
85
+ // Positionner le curseur après un court délai
86
+ setTimeout(() => {
87
+ const newCursorPos = cursorPos - 2
88
+ input.setSelectionRange(newCursorPos, newCursorPos)
89
+ }, 0)
133
90
  }
134
91
  }
135
92
 
136
93
  /**
137
- * Gère l'événement keydown pour les touches spéciales
138
- *
139
- * @param event - Événement keydown
140
- */
141
- const handleKeydown = (event: KeyboardEvent & { target: HTMLInputElement }): void => {
142
- // Bloquer la saisie de caractères non numériques
143
- // Autoriser uniquement : chiffres, touches de navigation, touches de modification et touches de contrôle
144
- if (
145
- // Si la touche n'est pas un chiffre
146
- !/^\d$/.test(event.key)
147
- // Et n'est pas une touche spéciale autorisée
148
- && ![
149
- 'Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',
150
- 'Home', 'End', 'Tab', 'Escape', 'Enter',
151
- 'Control', 'Alt', 'Shift', 'Meta',
152
- ].includes(event.key)
153
- // Et n'est pas une combinaison de touches (Ctrl+A, Ctrl+C, Ctrl+V, etc.)
154
- && !(event.ctrlKey || event.metaKey)
155
- ) {
156
- // Empêcher la saisie de caractères non numériques
157
- event.preventDefault()
158
- return
159
- }
160
-
161
- // Gérer la suppression des séparateurs
94
+ * Gestionnaire d'événement keydown personnalisé
95
+ */
96
+ const onSpecialKeyDown = (event: KeyboardEvent & { target: HTMLInputElement }): void => {
162
97
  if (event.key === 'Backspace') {
163
- const input = event.target
164
- if (!input.selectionStart || input.selectionStart !== input.selectionEnd) {
165
- return
166
- }
167
-
168
- const cursorPos = input.selectionStart
169
- const charBeforeCursor = input.value[cursorPos - 1]
170
-
171
- if (!/\d/.test(charBeforeCursor)) {
172
- // Si le caractère avant le curseur n'est pas un chiffre, on le supprime aussi
173
- // et on supprime le chiffre avant le séparateur
174
- event.preventDefault() // Empêcher le comportement par défaut
175
-
176
- const newValue = input.value.substring(0, cursorPos - 2)
177
- + input.value.substring(cursorPos)
178
-
179
- // Mettre à jour la valeur
180
- updateDisplayValue(newValue)
181
-
182
- // Positionner le curseur après un court délai
183
- setTimeout(() => {
184
- const newCursorPos = cursorPos - 2
185
- input.setSelectionRange(newCursorPos, newCursorPos)
186
- }, 0)
187
- }
98
+ handleBackspace(event)
188
99
  }
189
-
190
- // Gérer les touches de direction pour éviter de se retrouver entre un séparateur et un chiffre
191
- if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
100
+ else if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
101
+ // Handle arrow key navigation around separators
192
102
  const input = event.target
193
103
  const cursorPos = input.selectionStart || 0
194
104
 
195
- // Déterminer le séparateur utilisé dans le format
196
- const separator = format.match(/[^DMY]/)?.[0] || '/'
197
-
198
105
  if (event.key === 'ArrowLeft' && cursorPos > 0) {
199
106
  const charBeforeCursor = input.value[cursorPos - 1]
200
107
 
201
108
  if (charBeforeCursor === separator) {
202
- // Si on se déplace à gauche et qu'on rencontre un séparateur, sauter par-dessus
203
109
  event.preventDefault()
204
110
  input.setSelectionRange(cursorPos - 2, cursorPos - 2)
205
111
  }
@@ -208,7 +114,6 @@ export const useDateInputEditing = (options: DateInputEditingOptions) => {
208
114
  const charAtCursor = input.value[cursorPos]
209
115
 
210
116
  if (charAtCursor === separator) {
211
- // Si on se déplace à droite et qu'on rencontre un séparateur, sauter par-dessus
212
117
  event.preventDefault()
213
118
  input.setSelectionRange(cursorPos + 2, cursorPos + 2)
214
119
  }
@@ -216,11 +121,16 @@ export const useDateInputEditing = (options: DateInputEditingOptions) => {
216
121
  }
217
122
  }
218
123
 
124
+ // Utiliser le composable commun pour la gestion des événements clavier
125
+ const keyboardEvents = useKeyboardEvents({
126
+ allowedCharacters: /^\d$/,
127
+ onKeyDown: onSpecialKeyDown,
128
+ separator,
129
+ })
130
+
219
131
  /**
220
- * Gère l'événement paste pour formater correctement les dates collées
221
- *
222
- * @param event - Événement paste
223
- */
132
+ * Gère l'événement paste pour formater correctement les dates collées
133
+ */
224
134
  const handlePaste = (event: ClipboardEvent): void => {
225
135
  if (!event.clipboardData) return
226
136
 
@@ -254,7 +164,7 @@ export const useDateInputEditing = (options: DateInputEditingOptions) => {
254
164
  const accessibleValue = formatted.replace(new RegExp(placeholderChar, 'g'), ' ')
255
165
 
256
166
  // Créer un message descriptif pour le lecteur d'écran
257
- const dateDescription = getDateDescription(accessibleValue, format)
167
+ const dateDescription = getDateDescription(accessibleValue)
258
168
  updateAriaLabel(dateDescription)
259
169
  }
260
170
 
@@ -265,61 +175,15 @@ export const useDateInputEditing = (options: DateInputEditingOptions) => {
265
175
  }
266
176
 
267
177
  /**
268
- * Crée une description accessible de la date pour les lecteurs d'écran
269
- *
270
- * @param dateStr - La chaîne de date à décrire
271
- * @param format - Le format de la date
272
- * @returns Une description de la date adaptée aux lecteurs d'écran
273
- */
274
- const getDateDescription = (dateStr: string, format: string): string => {
275
- // Si la chaîne est vide, retourner un message simple
276
- if (!dateStr.trim()) {
277
- return 'Aucune date saisie'
278
- }
279
-
280
- // Déterminer le séparateur utilisé dans le format
281
- const separator = format.match(/[^DMY]/)?.[0] || '/'
282
-
283
- // Extraire les parties de la date
284
- const dateParts = dateStr.split(separator)
285
- const formatParts = format.split(separator)
286
-
287
- // Créer une description en fonction du format
288
- let description = 'Date en cours de saisie: '
289
-
290
- for (let i = 0; i < formatParts.length; i++) {
291
- if (i >= dateParts.length) break
292
-
293
- const part = dateParts[i].trim()
294
- const formatPart = formatParts[i].charAt(0).toUpperCase()
295
-
296
- // Ignorer les parties vides ou contenant uniquement des placeholders
297
- if (!part || part.replace(new RegExp(placeholderChar, 'g'), '').length === 0) {
298
- continue
299
- }
300
-
301
- switch (formatPart) {
302
- case 'D':
303
- description += `jour ${part}, `
304
- break
305
- case 'M':
306
- description += `mois ${part}, `
307
- break
308
- case 'Y':
309
- description += `année ${part}, `
310
- break
311
- }
312
- }
313
-
314
- // Supprimer la virgule finale si elle existe
315
- return description.endsWith(', ')
316
- ? description.slice(0, -2)
317
- : description
178
+ * Wrapper pour getDateDescription avec les options prédéfinies
179
+ */
180
+ const getDateDescription = (dateStr: string): string => {
181
+ return getDateDescriptionUtil(dateStr, format, placeholderChar)
318
182
  }
319
183
 
320
184
  return {
321
185
  formatDateInput,
322
- handleKeydown,
186
+ handleKeydown: keyboardEvents.handleKeyDown,
323
187
  handlePaste,
324
188
  getDateDescription,
325
189
  }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Composable pour centraliser la gestion des événements clavier
3
+ * Utilisé dans les différents composants de saisie de date
4
+ */
5
+
6
+ /**
7
+ * Options pour la gestion des événements clavier
8
+ */
9
+ export interface KeyboardEventsOptions {
10
+ /**
11
+ * Expression régulière pour les caractères autorisés
12
+ * Par défaut, seuls les chiffres sont autorisés
13
+ */
14
+ allowedCharacters?: RegExp
15
+ /**
16
+ * Fonction à appeler lors d'un appui sur une touche
17
+ * Appelée après la validation des caractères
18
+ */
19
+ onKeyDown?: (event: KeyboardEvent & { target: HTMLInputElement }) => void
20
+ /**
21
+ * Séparateur utilisé dans le format de date
22
+ * Utilisé pour gérer les touches de navigation
23
+ */
24
+ separator?: string
25
+ }
26
+
27
+ /**
28
+ * Composable pour gérer les événements clavier communs aux champs de date
29
+ *
30
+ * @param options - Options de configuration
31
+ * @returns Fonctions pour gérer les événements clavier
32
+ */
33
+ export const useKeyboardEvents = (options: KeyboardEventsOptions = {}) => {
34
+ const {
35
+ allowedCharacters = /^\d$/,
36
+ onKeyDown,
37
+ separator = '/',
38
+ } = options
39
+
40
+ /**
41
+ * Liste des touches spéciales toujours autorisées
42
+ */
43
+ const SPECIAL_KEYS = [
44
+ 'Backspace', 'Delete',
45
+ 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',
46
+ 'Home', 'End', 'Tab', 'Escape', 'Enter',
47
+ 'Control', 'Alt', 'Shift', 'Meta',
48
+ ]
49
+
50
+ /**
51
+ * Traite les événements keydown en filtrant les caractères non autorisés
52
+ */
53
+ const handleKeyDown = (event: KeyboardEvent & { target: HTMLInputElement }): void => {
54
+ // Autoriser uniquement : caractères autorisés, touches spéciales et combinaisons de touches
55
+ if (
56
+ !allowedCharacters.test(event.key)
57
+ && !SPECIAL_KEYS.includes(event.key)
58
+ && !(event.ctrlKey || event.metaKey)
59
+ ) {
60
+ event.preventDefault()
61
+ return
62
+ }
63
+
64
+ // Appeler le gestionnaire personnalisé s'il existe
65
+ if (onKeyDown) {
66
+ onKeyDown(event)
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Gère la navigation autour des séparateurs
72
+ * Évite que le curseur se positionne entre un chiffre et un séparateur
73
+ */
74
+ const handleArrowKeys = (event: KeyboardEvent & { target: HTMLInputElement }): void => {
75
+ const input = event.target
76
+ const cursorPos = input.selectionStart || 0
77
+
78
+ if (event.key === 'ArrowLeft' && cursorPos > 0) {
79
+ const charBeforeCursor = input.value[cursorPos - 1]
80
+
81
+ if (charBeforeCursor === separator) {
82
+ event.preventDefault()
83
+ input.setSelectionRange(cursorPos - 2, cursorPos - 2)
84
+ }
85
+ }
86
+ else if (event.key === 'ArrowRight' && cursorPos < input.value.length) {
87
+ const charAtCursor = input.value[cursorPos]
88
+
89
+ if (charAtCursor === separator) {
90
+ event.preventDefault()
91
+ input.setSelectionRange(cursorPos + 2, cursorPos + 2)
92
+ }
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Gère les événements paste pour filtrer les caractères non autorisés
98
+ */
99
+ const handlePaste = (event: ClipboardEvent): void => {
100
+ if (!event.clipboardData) return
101
+
102
+ const pastedText = event.clipboardData.getData('text')
103
+ if (!pastedText) return
104
+
105
+ // Filtrer pour ne garder que les caractères autorisés
106
+ const filteredText = pastedText
107
+ .split('')
108
+ .filter(char => allowedCharacters.test(char))
109
+ .join('')
110
+
111
+ // Si le texte collé ne contient pas de caractères autorisés, annuler l'opération
112
+ if (filteredText.length === 0) {
113
+ event.preventDefault()
114
+ return
115
+ }
116
+
117
+ // Si le texte a été modifié (des caractères non autorisés ont été supprimés)
118
+ if (filteredText !== pastedText) {
119
+ event.preventDefault()
120
+
121
+ const input = event.target as HTMLInputElement
122
+ if (!input) return
123
+
124
+ const start = input.selectionStart || 0
125
+ const end = input.selectionEnd || 0
126
+
127
+ // Construire la nouvelle valeur
128
+ const newValue = input.value.substring(0, start) + filteredText + input.value.substring(end)
129
+
130
+ // Mettre à jour la valeur (via l'événement input)
131
+ const inputEvent = new InputEvent('input', { bubbles: true, cancelable: true, data: newValue })
132
+ Object.defineProperty(inputEvent, 'target', { value: input, enumerable: true })
133
+ input.value = newValue
134
+ input.dispatchEvent(inputEvent)
135
+
136
+ // Positionner le curseur après le texte collé
137
+ setTimeout(() => {
138
+ const newCursorPos = start + filteredText.length
139
+ input.setSelectionRange(newCursorPos, newCursorPos)
140
+ }, 0)
141
+ }
142
+ }
143
+
144
+ return {
145
+ handleKeyDown,
146
+ handleArrowKeys,
147
+ handlePaste,
148
+ }
149
+ }
@@ -1,8 +1,11 @@
1
1
  import { type Ref } from 'vue'
2
2
  import { type ValidationResult } from '@/composables/validation/useValidation'
3
3
  import { DATE_PICKER_MESSAGES } from '../constants/messages'
4
- import { formatDate } from '@/utils/formatDate'
5
- import dayjs from 'dayjs'
4
+ import {
5
+ adaptCustomRules,
6
+ validateEmptyOrIncompleteDate,
7
+ type CustomRule,
8
+ } from '../utils/validationUtils'
6
9
 
7
10
  /**
8
11
  * Composable pour la validation manuelle des dates saisies
@@ -15,10 +18,8 @@ export const useManualDateValidation = (options: {
15
18
  format: string
16
19
  required?: boolean
17
20
  disableErrorHandling?: boolean
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Règles personnalisées
19
- customRules?: { type: string, options: any }[]
20
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Règles d'avertissement personnalisées
21
- customWarningRules?: { type: string, options: any }[]
21
+ customRules?: CustomRule[]
22
+ customWarningRules?: CustomRule[]
22
23
 
23
24
  // Références réactives
24
25
  hasInteracted: Ref<boolean>
@@ -29,8 +30,7 @@ export const useManualDateValidation = (options: {
29
30
  validateDateFormat: (dateStr: string) => { isValid: boolean, message: string }
30
31
  isDateComplete: (value: string) => boolean
31
32
  parseDate: (dateStr: string, format: string) => Date | null
32
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Règles personnalisées
33
- validateField: (value: any, rules?: any[], warningRules?: any[]) => ValidationResult
33
+ validateField: (value: unknown, rules?: CustomRule[], warningRules?: CustomRule[]) => ValidationResult
34
34
  }) => {
35
35
  const {
36
36
  format,
@@ -57,23 +57,22 @@ export const useManualDateValidation = (options: {
57
57
  // Réinitialiser la validation
58
58
  clearValidation()
59
59
 
60
- // Vérifier si le champ est requis et vide
61
- if (!value && required && hasInteracted.value) {
62
- if (!disableErrorHandling) {
63
- errors.value.push(DATE_PICKER_MESSAGES.ERROR_REQUIRED)
64
- }
65
- return false
66
- }
67
-
68
- // Si le champ est vide et non requis, c'est valide
69
- if (!value && !required) {
70
- return true
60
+ // Vérifier les cas de champ vide ou incomplet
61
+ const emptyCheck = validateEmptyOrIncompleteDate(
62
+ value,
63
+ required,
64
+ isDateComplete,
65
+ hasInteracted.value,
66
+ )
67
+
68
+ // Gérer les erreurs pour champ vide requis
69
+ if (!emptyCheck.isValid && !disableErrorHandling && emptyCheck.errorMessage) {
70
+ errors.value.push(DATE_PICKER_MESSAGES.ERROR_REQUIRED)
71
71
  }
72
72
 
73
- // Vérifier si la saisie est complète avant de valider le format
74
- if (!isDateComplete(value)) {
75
- // La saisie n'est pas complète, ne pas afficher d'erreur
76
- return true
73
+ // Si on ne doit pas continuer la validation (champ vide/incomplet)
74
+ if (!emptyCheck.shouldContinue) {
75
+ return emptyCheck.isValid
77
76
  }
78
77
 
79
78
  // Valider le format de la date
@@ -97,52 +96,12 @@ export const useManualDateValidation = (options: {
97
96
 
98
97
  // Valider les règles personnalisées
99
98
  if (!disableErrorHandling) {
100
- // Pour maintenir la compatibilité avec les tests existants, nous devons appeler validateField
101
- // avec tous les paramètres comme avant, mais nous devons aussi gérer correctement
102
- // les règles personnalisées qui utilisent includes() sur des chaînes
103
-
104
- // Pré-traitement des règles personnalisées pour éviter l'erreur "value.includes is not a function"
105
- const safeCustomRules = customRules.map((rule) => {
106
- if (rule.type === 'custom' && rule.options && rule.options.validate) {
107
- // Créer une copie de la règle pour ne pas modifier l'original
108
- const safeCopy = { ...rule }
109
- const originalValidate = rule.options.validate
110
-
111
- // Remplacer la fonction validate par une version sécurisée
112
- safeCopy.options = { ...rule.options }
113
- safeCopy.options.validate = (val: string | Date | null | undefined) => {
114
- // Si la valeur est une Date mais que la fonction originale attend une chaîne
115
- // (détecté par la présence de includes dans le code source)
116
- if (val instanceof Date && originalValidate.toString().includes('.includes')) {
117
- // Convertir la date en chaîne au format spécifié
118
- return originalValidate(format ? formatDate(dayjs(val), format) : val.toISOString())
119
- }
120
- return originalValidate(val)
121
- }
122
- return safeCopy
123
- }
124
- return rule
125
- })
126
-
127
- // Faire de même pour les règles d'avertissement
128
- const safeWarningRules = customWarningRules.map((rule) => {
129
- if (rule.type === 'custom' && rule.options && rule.options.validate) {
130
- const safeCopy = { ...rule }
131
- const originalValidate = rule.options.validate
132
-
133
- safeCopy.options = { ...rule.options }
134
- safeCopy.options.validate = (val: string | Date | null | undefined) => {
135
- if (val instanceof Date && originalValidate.toString().includes('.includes')) {
136
- return originalValidate(format ? formatDate(dayjs(val), format) : val.toISOString())
137
- }
138
- return originalValidate(val)
139
- }
140
- return safeCopy
141
- }
142
- return rule
143
- })
99
+ // Adapter les règles pour maintenir la compatibilité avec les tests existants
100
+ // en utilisant notre utilitaire pour éviter les erreurs de type
101
+ const safeCustomRules = adaptCustomRules(customRules, format)
102
+ const safeWarningRules = adaptCustomRules(customWarningRules, format)
144
103
 
145
- // Appeler validateField comme avant pour maintenir la compatibilité avec les tests
104
+ // Appeler validateField pour évaluer les règles
146
105
  const result = validateField(
147
106
  date,
148
107
  safeCustomRules,