@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
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Utilitaires de formatage de dates pour les composants DatePicker
3
+ * Extrait et centralisé à partir des différents composables
4
+ */
5
+
6
+ /**
7
+ * Résultat du formatage d'une date
8
+ */
9
+ export interface FormatDateInputResult {
10
+ /**
11
+ * Valeur formatée
12
+ */
13
+ formatted: string
14
+ /**
15
+ * Position du curseur après formatage
16
+ */
17
+ cursorPos: number
18
+ }
19
+
20
+ /**
21
+ * Options pour le formatage d'une date
22
+ */
23
+ export interface FormatDateOptions {
24
+ /**
25
+ * Position actuelle du curseur
26
+ */
27
+ cursorPosition?: number
28
+ /**
29
+ * Caractère à utiliser pour les positions non remplies
30
+ */
31
+ placeholderChar?: string
32
+ }
33
+
34
+ /**
35
+ * Formate une entrée de date en ajoutant les séparateurs appropriés
36
+ *
37
+ * @param input - Chaîne de caractères saisie
38
+ * @param format - Format de date (ex: 'DD/MM/YYYY')
39
+ * @param options - Options de formatage
40
+ * @returns Objet contenant la chaîne formatée et la nouvelle position du curseur
41
+ */
42
+ export const formatDateInput = (
43
+ input: string,
44
+ format: string,
45
+ options: FormatDateOptions = {},
46
+ ): FormatDateInputResult => {
47
+ const {
48
+ cursorPosition,
49
+ placeholderChar = '_',
50
+ } = options
51
+
52
+ // Nettoyer l'entrée pour ne garder que les chiffres
53
+ let cleanedInput = input.replace(/[^\d]/g, '')
54
+
55
+ // Déterminer le séparateur utilisé dans le format
56
+ const separator = format.match(/[^DMY]/)?.[0] || '/'
57
+
58
+ // Calculer la position du curseur dans l'entrée nettoyée (sans séparateurs)
59
+ const inputBeforeCursor = input.substring(0, cursorPosition || 0)
60
+ const digitsBeforeCursor = inputBeforeCursor.replace(/[^\d]/g, '').length
61
+
62
+ // Extraire les groupes de chiffres du format (DD, MM, YYYY)
63
+ const digitGroups: string[] = []
64
+ let currentGroup = ''
65
+
66
+ for (const char of format) {
67
+ if (['D', 'M', 'Y'].includes(char.toUpperCase())) {
68
+ currentGroup += char
69
+ }
70
+ else if (currentGroup) {
71
+ digitGroups.push(currentGroup)
72
+ currentGroup = ''
73
+ }
74
+ }
75
+
76
+ // Ajouter le dernier groupe s'il existe
77
+ if (currentGroup) {
78
+ digitGroups.push(currentGroup)
79
+ }
80
+
81
+ // Calculer le nombre total de chiffres attendus dans le format
82
+ const expectedDigits = format.replace(/[^DMY]/g, '').length
83
+
84
+ // Limiter le nombre de chiffres saisis au nombre attendu
85
+ if (cleanedInput.length > expectedDigits) {
86
+ cleanedInput = cleanedInput.substring(0, expectedDigits)
87
+ }
88
+
89
+ // Construire la chaîne formatée
90
+ let result = ''
91
+ let digitIndex = 0
92
+
93
+ // Parcourir les groupes de chiffres pour construire la date formatée
94
+ for (let groupIndex = 0; groupIndex < digitGroups.length; groupIndex++) {
95
+ const group = digitGroups[groupIndex]
96
+ const groupLength = group.length
97
+
98
+ // Ajouter les chiffres pour ce groupe
99
+ for (let j = 0; j < groupLength; j++) {
100
+ if (digitIndex < cleanedInput.length) {
101
+ result += cleanedInput[digitIndex]
102
+ digitIndex++
103
+ }
104
+ else {
105
+ // Utiliser le caractère de placeholder configuré pour les positions non remplies
106
+ result += placeholderChar
107
+ }
108
+ }
109
+
110
+ // Ajouter le séparateur après chaque groupe sauf le dernier
111
+ if (groupIndex < digitGroups.length - 1) {
112
+ result += separator
113
+ }
114
+ }
115
+
116
+ // Calculer la nouvelle position du curseur
117
+ let newCursorPos = 0
118
+ let digitCount = 0
119
+
120
+ // Parcourir le résultat formaté pour trouver la position du curseur
121
+ for (let i = 0; i < result.length && digitCount < digitsBeforeCursor; i++) {
122
+ newCursorPos++
123
+ if (/\d/.test(result[i])) {
124
+ digitCount++
125
+ }
126
+ }
127
+
128
+ return {
129
+ formatted: result,
130
+ cursorPos: Math.min(newCursorPos, result.length),
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Crée une description accessible de la date pour les lecteurs d'écran
136
+ *
137
+ * @param dateStr - La chaîne de date à décrire
138
+ * @param format - Le format de la date
139
+ * @param placeholderChar - Caractère utilisé pour les positions vides
140
+ * @returns Une description de la date adaptée aux lecteurs d'écran
141
+ */
142
+ export const getDateDescription = (
143
+ dateStr: string,
144
+ format: string,
145
+ placeholderChar = '_',
146
+ ): string => {
147
+ // Si la chaîne est vide, retourner un message simple
148
+ if (!dateStr.trim()) {
149
+ return 'Aucune date saisie'
150
+ }
151
+
152
+ // Déterminer le séparateur utilisé dans le format
153
+ const separator = format.match(/[^DMY]/)?.[0] || '/'
154
+
155
+ // Extraire les parties de la date
156
+ const dateParts = dateStr.split(separator)
157
+ const formatParts = format.split(separator)
158
+
159
+ // Créer une description en fonction du format
160
+ let description = 'Date en cours de saisie: '
161
+
162
+ for (let i = 0; i < formatParts.length; i++) {
163
+ if (i >= dateParts.length) break
164
+
165
+ const part = dateParts[i].trim()
166
+ const formatPart = formatParts[i].charAt(0).toUpperCase()
167
+
168
+ // Ignorer les parties vides ou contenant uniquement des placeholders
169
+ if (!part || part.replace(new RegExp(placeholderChar, 'g'), '').length === 0) {
170
+ continue
171
+ }
172
+
173
+ switch (formatPart) {
174
+ case 'D':
175
+ description += `jour ${part}, `
176
+ break
177
+ case 'M':
178
+ description += `mois ${part}, `
179
+ break
180
+ case 'Y':
181
+ description += `année ${part}, `
182
+ break
183
+ }
184
+ }
185
+
186
+ // Supprimer la virgule finale si elle existe
187
+ return description.endsWith(', ')
188
+ ? description.slice(0, -2)
189
+ : description
190
+ }
191
+
192
+ /**
193
+ * Extrait les deux parties d'une plage de dates
194
+ *
195
+ * @param value - Chaîne de caractères contenant une plage de dates
196
+ * @param separator - Séparateur de plage (par défaut: ' - ')
197
+ * @returns Tableau contenant les deux parties de la plage
198
+ */
199
+ export const extractRangeParts = (value: string, separator = ' - '): [string, string] => {
200
+ const parts = value.split(separator)
201
+ return [
202
+ parts[0]?.trim() || '',
203
+ parts[1]?.trim() || '',
204
+ ]
205
+ }
206
+
207
+ /**
208
+ * Vérifie si une chaîne de caractères contient un séparateur de plage
209
+ *
210
+ * @param value - Chaîne de caractères à vérifier
211
+ * @param separator - Séparateur de plage (par défaut: ' - ')
212
+ * @returns Booléen indiquant si la chaîne contient le séparateur
213
+ */
214
+ export const hasRangeSeparator = (value: string, separator = ' - '): boolean => {
215
+ return value.includes(separator)
216
+ }
217
+
218
+ /**
219
+ * Vérifie si une plage de dates est valide (la date de début est antérieure à la date de fin)
220
+ *
221
+ * @param startDate - Date de début
222
+ * @param endDate - Date de fin
223
+ * @returns Booléen indiquant si la plage est valide
224
+ */
225
+ export const isValidDateRange = (startDate: Date | null, endDate: Date | null): boolean => {
226
+ if (!startDate || !endDate) return true
227
+ return startDate.getTime() <= endDate.getTime()
228
+ }
@@ -0,0 +1,90 @@
1
+ import { formatDate } from '@/utils/formatDate'
2
+ import dayjs from 'dayjs'
3
+
4
+ /**
5
+ * Type pour une règle de validation personnalisée
6
+ */
7
+ export interface CustomRule {
8
+ type: string
9
+ options: {
10
+ validate: (value: unknown) => boolean
11
+ message?: string
12
+ [key: string]: unknown
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Adapte les règles personnalisées pour assurer la compatibilité avec différents types de valeurs
18
+ *
19
+ * @param rules - Règles personnalisées à adapter
20
+ * @param format - Format de date à utiliser pour la conversion
21
+ * @returns Règles adaptées pour fonctionner avec les dates et chaînes
22
+ */
23
+ export const adaptCustomRules = (rules: CustomRule[] = [], format: string): CustomRule[] => {
24
+ return rules.map((rule) => {
25
+ if (rule.type === 'custom' && rule.options && rule.options.validate) {
26
+ // Créer une copie de la règle pour ne pas modifier l'original
27
+ const safeCopy: CustomRule = { ...rule }
28
+ const originalValidate = rule.options.validate
29
+
30
+ // Remplacer la fonction validate par une version sécurisée
31
+ safeCopy.options = { ...rule.options }
32
+ safeCopy.options.validate = (val: unknown) => {
33
+ // Si la valeur est une Date mais que la fonction originale attend une chaîne
34
+ // (détecté par la présence de includes dans le code source)
35
+ if (val instanceof Date && originalValidate.toString().includes('.includes')) {
36
+ // Convertir la date en chaîne au format spécifié
37
+ return originalValidate(format ? formatDate(dayjs(val), format) : val.toISOString())
38
+ }
39
+ return originalValidate(val)
40
+ }
41
+ return safeCopy
42
+ }
43
+ return rule
44
+ })
45
+ }
46
+
47
+ /**
48
+ * Vérifie si une chaîne de date est vide ou incomplète
49
+ *
50
+ * @param value - Chaîne de date à vérifier
51
+ * @param required - Indique si le champ est requis
52
+ * @returns Objet indiquant si la validation doit continuer et si la valeur est valide
53
+ */
54
+ export const validateEmptyOrIncompleteDate = (
55
+ value: string,
56
+ required: boolean,
57
+ isDateComplete: (value: string) => boolean,
58
+ hasInteracted: boolean,
59
+ ): { shouldContinue: boolean, isValid: boolean, errorMessage?: string } => {
60
+ // Vérifier si le champ est requis et vide
61
+ if (!value && required && hasInteracted) {
62
+ return {
63
+ shouldContinue: false,
64
+ isValid: false,
65
+ errorMessage: 'Ce champ est requis',
66
+ }
67
+ }
68
+
69
+ // Si le champ est vide et non requis, c'est valide
70
+ if (!value && !required) {
71
+ return { shouldContinue: false, isValid: true }
72
+ }
73
+
74
+ // Vérifier si la saisie est complète avant de valider le format
75
+ if (!isDateComplete(value)) {
76
+ // La saisie n'est pas complète, ne pas afficher d'erreur
77
+ return { shouldContinue: false, isValid: true }
78
+ }
79
+
80
+ // La validation doit continuer
81
+ return { shouldContinue: true, isValid: true }
82
+ }
83
+
84
+ /**
85
+ * Structure de résultat de validation de format
86
+ */
87
+ export interface FormatValidationResult {
88
+ isValid: boolean
89
+ message: string
90
+ }
@@ -182,6 +182,10 @@ export const Legende: StoryObj = {
182
182
  </div>
183
183
  </div>
184
184
  </div>
185
+ <div class="mt-4">
186
+ <p>Rapport d’audit manuel : <a href="/audits/DialogBox.xlsx" style="color:#0C41BD;">Voir le rapport</a></p>
187
+ <p style="color: grey; font-size: 14px">Correctifs associés (<a href="https://github.com/assurance-maladie-digital/design-system-v3/issues/798" target="_blank" style="color:#0C41BD;">issue #798</a>)</p>
188
+ </div>
185
189
  `,
186
190
  }
187
191
  },
@@ -2,6 +2,7 @@ import { type Meta, type StoryObj } from '@storybook/vue3'
2
2
  import { VBtn } from 'vuetify/components'
3
3
  import DialogBox from './DialogBox.vue'
4
4
  import { fn } from '@storybook/test'
5
+ import { computed } from 'vue'
5
6
 
6
7
  const meta: Meta<typeof DialogBox> = {
7
8
  title: 'Composants/Feedback/DialogBox',
@@ -189,7 +190,12 @@ export const Default: Story = {
189
190
  return {
190
191
  components: { DialogBox, VBtn },
191
192
  setup() {
192
- return { args }
193
+ const rest = computed(() => {
194
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
195
+ const { default: _, ...rest } = args
196
+ return rest
197
+ })
198
+ return { args, rest }
193
199
  },
194
200
  template: `
195
201
  <div class="pa-4">
@@ -198,7 +204,7 @@ export const Default: Story = {
198
204
  color="primary"
199
205
  >Toggle DialogBox</VBtn>
200
206
  <DialogBox
201
- v-bind="args"
207
+ v-bind="rest"
202
208
  @confirm="args.modelValue = false"
203
209
  @cancel="args.modelValue = false"
204
210
  @update:modelValue="args.modelValue = $event"
@@ -560,10 +566,7 @@ export const TitleSlot: Story = {
560
566
  @cancel="args.modelValue = false"
561
567
  >
562
568
  <template #title>
563
- <VBtn
564
- color="primary"
565
- @click="args.modelValue = false"
566
- >Title slot</VBtn>
569
+ Title slot
567
570
  </template>
568
571
  {{ args.default }}
569
572
  </DialogBox>
@@ -585,10 +588,7 @@ export const TitleSlot: Story = {
585
588
  v-model="dialogOpen"
586
589
  >
587
590
  <template #title>
588
- <VBtn
589
- color="primary"
590
- @click="dialogOpen = false"
591
- >Title slot</VBtn>
591
+ Title slot
592
592
  </template>
593
593
  DialogBox content
594
594
  </DialogBox>
@@ -1,10 +1,12 @@
1
1
  <script setup lang="ts">
2
2
  import useCustomizableOptions, { type CustomizableOptions } from '@/composables/useCustomizableOptions'
3
3
  import { mdiClose } from '@mdi/js'
4
- import { ref } from 'vue'
4
+ import { ref, useId, watch } from 'vue'
5
5
  import type { VDialog } from 'vuetify/components'
6
6
  import { config } from './config'
7
7
  import { locales } from './locales'
8
+ import { useDisplay } from 'vuetify/lib/framework.mjs'
9
+ import SyIcon from '../Customs/SyIcon/SyIcon.vue'
8
10
 
9
11
  const props = withDefaults(defineProps<{
10
12
  title?: string
@@ -33,6 +35,18 @@
33
35
  default: false,
34
36
  })
35
37
 
38
+ // Restor the focus to the last active element when the dialog is closed
39
+ let activeElement: HTMLElement | null = null
40
+ watch(dialog, (newValue) => {
41
+ if (newValue) {
42
+ activeElement = document.activeElement as HTMLElement
43
+ }
44
+ else if (activeElement) {
45
+ activeElement.focus()
46
+ }
47
+ })
48
+
49
+ const id = `dialog-${useId()}`
36
50
  const dialogContent = ref<VDialog | undefined>(undefined)
37
51
 
38
52
  const options = useCustomizableOptions(config, props)
@@ -75,6 +89,9 @@
75
89
  selectableElements[lastElement].focus()
76
90
  }
77
91
  }
92
+
93
+ const display = useDisplay()
94
+
78
95
  </script>
79
96
 
80
97
  <template>
@@ -86,18 +103,19 @@
86
103
  :retain-focus="false"
87
104
  aria-modal="true"
88
105
  class="sy-dialog-box"
106
+ :aria-labelledby="id"
89
107
  @keydown.tab="handleFocus"
90
108
  >
91
109
  <VCard
92
110
  v-bind="options.card"
93
- id="dialogContent"
111
+ id="dialog-content"
94
112
  ref="dialogContent"
95
- :aria-labelledby="props.title ? props.title : 'dialogContent'"
96
113
  >
97
114
  <VCardTitle v-bind="options.cardTitle">
98
115
  <slot name="title">
99
116
  <h2
100
117
  v-if="title"
118
+ :id="id"
101
119
  class="text-h6 font-weight-bold"
102
120
  >
103
121
  {{ props.title }}
@@ -108,13 +126,16 @@
108
126
 
109
127
  <VBtn
110
128
  v-if="!props.persistent"
129
+ class="sy-dialog-box-close-btn"
111
130
  v-bind="options.closeBtn"
112
131
  :aria-label="locales.closeBtn"
113
132
  @click="dialog = false"
114
133
  >
115
- <VIcon v-bind="options.icon">
116
- {{ closeIcon }}
117
- </VIcon>
134
+ <SyIcon
135
+ :icon="closeIcon"
136
+ :decorative="true"
137
+ v-bind="options.icon"
138
+ />
118
139
  </VBtn>
119
140
  </VCardTitle>
120
141
  <slot />
@@ -127,20 +148,40 @@
127
148
  <VSpacer v-bind="options.actionsSpacer" />
128
149
 
129
150
  <slot name="actions">
130
- <VBtn
131
- v-bind="options.cancelBtn"
132
- @click="$emit('cancel')"
133
- >
134
- {{ props.cancelBtnText }}
135
- </VBtn>
136
-
137
- <VBtn
138
- v-bind="options.confirmBtn"
139
- data-test-id="confirm-btn"
140
- @click="$emit('confirm')"
141
- >
142
- {{ props.confirmBtnText }}
143
- </VBtn>
151
+ <template v-if="display.xs.value">
152
+ <VBtn
153
+ v-bind="options.confirmBtn"
154
+ data-test-id="confirm-btn"
155
+ @click="$emit('confirm')"
156
+ >
157
+ {{ props.confirmBtnText }}
158
+ </VBtn>
159
+
160
+ <VBtn
161
+ v-bind="options.cancelBtn"
162
+ @click="$emit('cancel')"
163
+ >
164
+ {{ props.cancelBtnText }}
165
+ </VBtn>
166
+ </template>
167
+ <template v-else>
168
+ <VBtn
169
+ class="sy-dialog-box-cancel-btn"
170
+ v-bind="options.cancelBtn"
171
+ @click="$emit('cancel')"
172
+ >
173
+ {{ props.cancelBtnText }}
174
+ </VBtn>
175
+
176
+ <VBtn
177
+ class="sy-dialog-box-confirm-btn"
178
+ v-bind="options.confirmBtn"
179
+ data-test-id="confirm-btn"
180
+ @click="$emit('confirm')"
181
+ >
182
+ {{ props.confirmBtnText }}
183
+ </VBtn>
184
+ </template>
144
185
  </slot>
145
186
  </div>
146
187
  </VCard>
@@ -165,9 +206,30 @@ h2 {
165
206
  text-wrap: balance;
166
207
  }
167
208
 
209
+ .sy-dialog-box-close-btn:focus-visible,
210
+ .sy-dialog-box-cancel-btn:focus-visible,
211
+ .sy-dialog-box-confirm-btn:focus-visible {
212
+ :deep(.v-btn__overlay) {
213
+ display: none;
214
+ }
215
+
216
+ &::after {
217
+ opacity: 1;
218
+ border: transparent;
219
+ outline: 2px solid rgb(var(--v-theme-primary));
220
+ outline-offset: 2px;
221
+ }
222
+ }
223
+
224
+ .sy-dialog-box-confirm-btn:focus-visible {
225
+ &::after {
226
+ outline-offset: 2px;
227
+ }
228
+ }
229
+
168
230
  .sy-dialog-box-actions-ctn {
169
231
  display: flex;
170
- flex-direction: column-reverse;
232
+ flex-direction: column;
171
233
  justify-content: stretch;
172
234
  gap: $spacing-small;
173
235
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  exports[`DialogBox > rendering and props > renders correctly with props 1`] = `
4
4
  <div
5
+ aria-labelledby="dialog-v-0"
5
6
  aria-modal="true"
6
7
  class="sy-dialog-box"
7
8
  modelvalue="true"
@@ -10,7 +11,6 @@ exports[`DialogBox > rendering and props > renders correctly with props 1`] = `
10
11
  width="600px"
11
12
  >
12
13
  <div
13
- aria-labelledby="Test title"
14
14
  class="
15
15
  pa-6
16
16
  v-card
@@ -18,7 +18,7 @@ exports[`DialogBox > rendering and props > renders correctly with props 1`] = `
18
18
  v-card--variant-elevated
19
19
  v-theme--light
20
20
  "
21
- id="dialogContent"
21
+ id="dialog-content"
22
22
  >
23
23
  <!---->
24
24
  <div class="v-card__loader">
@@ -81,10 +81,13 @@ exports[`DialogBox > rendering and props > renders correctly with props 1`] = `
81
81
  pa-0
82
82
  v-card-title
83
83
  ">
84
- <h2 class="
85
- font-weight-bold
86
- text-h6
87
- ">
84
+ <h2
85
+ class="
86
+ font-weight-bold
87
+ text-h6
88
+ "
89
+ id="dialog-v-0"
90
+ >
88
91
  Test title
89
92
  </h2>
90
93
  <div class="v-spacer"></div>
@@ -95,6 +98,7 @@ exports[`DialogBox > rendering and props > renders correctly with props 1`] = `
95
98
  ml-4
96
99
  mr-n2
97
100
  mt-n2
101
+ sy-dialog-box-close-btn
98
102
  v-btn
99
103
  v-btn--density-default
100
104
  v-btn--elevated
@@ -141,6 +145,7 @@ exports[`DialogBox > rendering and props > renders correctly with props 1`] = `
141
145
  <div class="v-spacer"></div>
142
146
  <button
143
147
  class="
148
+ sy-dialog-box-cancel-btn
144
149
  text-primary
145
150
  v-btn
146
151
  v-btn--density-default
@@ -165,6 +170,7 @@ exports[`DialogBox > rendering and props > renders correctly with props 1`] = `
165
170
  <button
166
171
  class="
167
172
  bg-primary
173
+ sy-dialog-box-confirm-btn
168
174
  v-btn
169
175
  v-btn--density-default
170
176
  v-btn--elevated
@@ -57,6 +57,9 @@ describe('FileUpload', () => {
57
57
  global: {
58
58
  plugins: [vuetify],
59
59
  },
60
+ props: {
61
+ modelValue: [], // Ajouter la prop modelValue obligatoire
62
+ },
60
63
  })
61
64
 
62
65
  const label = wrapper.find('label')
@@ -184,6 +184,7 @@
184
184
  v-bind="options.goTopBtnIcon"
185
185
  :icon="arrowTopIcon"
186
186
  class="scroll"
187
+ label="Aller en haut de la page"
187
188
  />
188
189
  </VBtn>
189
190
  </div>
@@ -216,6 +216,10 @@ export const Legende: StoryObj = {
216
216
  </div>
217
217
  </div>
218
218
  </div>
219
+ <div class="mt-4">
220
+ <p>Rapport d’audit manuel : <a href="/audits/HeaderBar.xlsx" style="color:#0C41BD;">Voir le rapport</a></p>
221
+ <p style="color: grey; font-size: 14px">Correctifs associés (<a href="https://github.com/assurance-maladie-digital/design-system-v3/issues/643" target="_blank" style="color:#0C41BD;">issue #643</a>)</p>
222
+ </div>
219
223
  `,
220
224
  }
221
225
  },