@cnamts/synapse 1.0.6 → 1.0.7

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 (223) hide show
  1. package/dist/{DateFilter-BlOpwEVq.js → DateFilter-CHDLz2EO.js} +1 -1
  2. package/dist/{NumberFilter-BPUXE4wY.js → NumberFilter-DXNQ4Uls.js} +1 -1
  3. package/dist/{PeriodFilter-B2yx329_.js → PeriodFilter-C8Qf3Jcn.js} +1 -1
  4. package/dist/{SelectFilter-CedKn1oV.js → SelectFilter-B2Ejs4Cb.js} +1 -1
  5. package/dist/{TextFilter-DkhJjRtR.js → TextFilter-CfR5_A1S.js} +1 -1
  6. package/dist/components/Amelipro/AmeliproAccordionGroup/AmeliproAccordionGroup.d.ts +116 -0
  7. package/dist/components/Amelipro/AmeliproAccordionGroup/types.d.ts +4 -0
  8. package/dist/components/Amelipro/AmeliproAccordionList/AmeliproAccordionList.d.ts +220 -0
  9. package/dist/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.d.ts +68 -0
  10. package/dist/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResultTemplate/AmeliproAccordionResultTemplate.d.ts +70 -0
  11. package/dist/components/Amelipro/AmeliproAccordionResultList/AmeliproAccordionResultList.d.ts +204 -0
  12. package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +26 -26
  13. package/dist/components/Amelipro/AmeliproBadge/AmeliproBadge.d.ts +59 -0
  14. package/dist/components/Amelipro/AmeliproBtn/AmeliproBtn.d.ts +3 -3
  15. package/dist/components/Amelipro/AmeliproCaptcha/AmeliproCaptcha.d.ts +1 -1
  16. package/dist/components/Amelipro/AmeliproCarousel/AmeliproCarousel.d.ts +214 -0
  17. package/dist/components/Amelipro/AmeliproCarousel/AmeliproCarouselItem/AmeliproCarouselItem.d.ts +70 -0
  18. package/dist/components/Amelipro/AmeliproCarousel/types.d.ts +7 -0
  19. package/dist/components/Amelipro/AmeliproClickableTile/AmeliproClickableTile.d.ts +125 -0
  20. package/dist/components/Amelipro/AmeliproIconBtn/AmeliproIconBtn.d.ts +1 -1
  21. package/dist/components/Amelipro/AmeliproIllustratedDataTile/AmeliproIllustratedDataTile.d.ts +2 -2
  22. package/dist/components/Amelipro/AmeliproResultList/AmeliproResultList.d.ts +164 -0
  23. package/dist/components/Amelipro/AmeliproSelect/AmeliproSelect.d.ts +27 -27
  24. package/dist/components/Amelipro/AmeliproStateTile/AmeliproStateTile.d.ts +1 -1
  25. package/dist/components/Amelipro/AmeliproTable/AmeliproTable.d.ts +1 -1
  26. package/dist/components/Amelipro/AmeliproTabs/AmeliproTabs.d.ts +32 -32
  27. package/dist/components/Amelipro/AmeliproTextArea/AmeliproTextArea.d.ts +6 -6
  28. package/dist/components/Amelipro/AmeliproTextField/AmeliproTextField.d.ts +7 -7
  29. package/dist/components/Amelipro/AmeliproTileBtn/AmeliproTileBtn.d.ts +1 -1
  30. package/dist/components/Amelipro/AmeliproTooltips/AmeliproTooltips.d.ts +2 -2
  31. package/dist/components/ChipList/ChipList.d.ts +4 -0
  32. package/dist/components/ChipList/locales.d.ts +4 -2
  33. package/dist/components/CookiesSelection/CookiesInformation/CookiesInformation.d.ts +8 -8
  34. package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +445 -8
  35. package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +2 -0
  36. package/dist/components/Customs/SyTabs/SyTabs.d.ts +71 -0
  37. package/dist/components/Customs/SyTabs/config.d.ts +17 -0
  38. package/dist/components/Customs/SyTabs/types.d.ts +11 -0
  39. package/dist/components/Customs/SyTextField/SyTextField.d.ts +9 -9
  40. package/dist/components/DataList/DataList.d.ts +1 -1
  41. package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +4811 -240
  42. package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +52 -33
  43. package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +23 -10
  44. package/dist/components/DatePicker/composables/useDateInputEditing.d.ts +1 -0
  45. package/dist/components/DatePicker/composables/useTodayButton.d.ts +1 -0
  46. package/dist/components/DialogBox/DialogBox.d.ts +219 -0
  47. package/dist/components/HeaderLoading/HeaderLoading.d.ts +27 -0
  48. package/dist/components/HeaderNavigationBar/HeaderNavigationBar.d.ts +110 -3
  49. package/dist/components/HeaderNavigationBar/HorizontalNavbar/HorizontalNavbar.d.ts +19 -1
  50. package/dist/components/LangBtn/LangBtn.d.ts +2 -2
  51. package/dist/components/NirField/NirField.d.ts +18 -18
  52. package/dist/components/PeriodField/PeriodField.d.ts +10766 -1620
  53. package/dist/components/PhoneField/PhoneField.d.ts +1866 -2
  54. package/dist/components/PhoneField/indicatifs.d.ts +1 -0
  55. package/dist/components/PhoneField/locales.d.ts +1 -0
  56. package/dist/components/RangeField/RangeField.d.ts +1 -1
  57. package/dist/components/RangeField/RangeSlider/RangeSlider.d.ts +1 -1
  58. package/dist/components/SubHeader/SubHeader.d.ts +8 -0
  59. package/dist/components/SubHeader/locales.d.ts +1 -0
  60. package/dist/components/SyTextArea/SyTextArea.d.ts +6 -6
  61. package/dist/components/Tables/SyServerTable/SyServerTable.d.ts +5 -4
  62. package/dist/components/Tables/SyTable/SyTable.d.ts +5 -4
  63. package/dist/components/Tables/common/SyTablePagination.d.ts +448 -7
  64. package/dist/components/Tables/common/organizeColumns/OrganizeColumns.d.ts +2 -2
  65. package/dist/components/Tables/common/types.d.ts +2 -0
  66. package/dist/components/index.d.ts +9 -0
  67. package/dist/design-system-v3.js +173 -164
  68. package/dist/design-system-v3.umd.cjs +288 -261
  69. package/dist/{main-BXPFSAB4.js → main-C66C1BkG.js} +12984 -11291
  70. package/dist/style.css +1 -1
  71. package/package.json +1 -1
  72. package/src/assets/amelipro/icons.ts +38 -11
  73. package/src/components/Amelipro/AmeliproAccordionGroup/AmeliproAccordionGroup.mdx +20 -0
  74. package/src/components/Amelipro/AmeliproAccordionGroup/AmeliproAccordionGroup.stories.ts +135 -0
  75. package/src/components/Amelipro/AmeliproAccordionGroup/AmeliproAccordionGroup.vue +107 -0
  76. package/src/components/Amelipro/AmeliproAccordionGroup/__tests__/AmeliproAccordionGroup.spec.ts +37 -0
  77. package/src/components/Amelipro/AmeliproAccordionGroup/__tests__/__snapshots__/AmeliproAccordionGroup.spec.ts.snap +513 -0
  78. package/src/components/Amelipro/AmeliproAccordionGroup/types.d.ts +4 -0
  79. package/src/components/Amelipro/AmeliproAccordionList/AmeliproAccordionList.mdx +16 -0
  80. package/src/components/Amelipro/AmeliproAccordionList/AmeliproAccordionList.stories.ts +300 -0
  81. package/src/components/Amelipro/AmeliproAccordionList/AmeliproAccordionList.vue +288 -0
  82. package/src/components/Amelipro/AmeliproAccordionList/__tests__/AmeliproAccordionList.spec.ts +38 -0
  83. package/src/components/Amelipro/AmeliproAccordionList/__tests__/__snapshots__/AmeliproAccordionList.spec.ts.snap +1712 -0
  84. package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.mdx +19 -0
  85. package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.stories.ts +68 -0
  86. package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.vue +66 -0
  87. package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResultTemplate/AmeliproAccordionResultTemplate.vue +145 -0
  88. package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResultTemplate/__tests__/AmeliproAccordionResultTemplate.spec.ts +24 -0
  89. package/src/components/Amelipro/AmeliproAccordionResult/AmeliproAccordionResultTemplate/__tests__/__snapshots__/AmeliproAccordionResultTemplate.spec.ts.snap +127 -0
  90. package/src/components/Amelipro/AmeliproAccordionResult/__tests__/AmeliproAccordionResult.spec.ts +24 -0
  91. package/src/components/Amelipro/AmeliproAccordionResult/__tests__/__snapshots__/AmeliproAccordionResult.spec.ts.snap +123 -0
  92. package/src/components/Amelipro/AmeliproAccordionResultList/AmeliproAccordionResultList.mdx +20 -0
  93. package/src/components/Amelipro/AmeliproAccordionResultList/AmeliproAccordionResultList.stories.ts +273 -0
  94. package/src/components/Amelipro/AmeliproAccordionResultList/AmeliproAccordionResultList.vue +275 -0
  95. package/src/components/Amelipro/AmeliproAccordionResultList/__tests__/AmeliproAccordionResultList.spec.ts +38 -0
  96. package/src/components/Amelipro/AmeliproAccordionResultList/__tests__/__snapshots__/AmeliproAccordionResultList.spec.ts.snap +1593 -0
  97. package/src/components/Amelipro/AmeliproBadge/AmeliproBadge.mdx +15 -0
  98. package/src/components/Amelipro/AmeliproBadge/AmeliproBadge.stories.ts +54 -0
  99. package/src/components/Amelipro/AmeliproBadge/AmeliproBadge.vue +76 -0
  100. package/src/components/Amelipro/AmeliproBadge/__tests__/AmeliproBadge.spec.ts +20 -0
  101. package/src/components/Amelipro/AmeliproBadge/__tests__/__snapshots__/AmeliproBadge.spec.ts.snap +19 -0
  102. package/src/components/Amelipro/AmeliproCarousel/AmeliproCarousel.mdx +15 -0
  103. package/src/components/Amelipro/AmeliproCarousel/AmeliproCarousel.stories.ts +191 -0
  104. package/src/components/Amelipro/AmeliproCarousel/AmeliproCarousel.vue +263 -0
  105. package/src/components/Amelipro/AmeliproCarousel/AmeliproCarouselItem/AmeliproCarouselItem.vue +93 -0
  106. package/src/components/Amelipro/AmeliproCarousel/AmeliproCarouselItem/__tests__/AmeliproCarouselItem.spec.ts +24 -0
  107. package/src/components/Amelipro/AmeliproCarousel/AmeliproCarouselItem/__tests__/__snapshots__/AmeliproCarouselItem.spec.ts.snap +43 -0
  108. package/src/components/Amelipro/AmeliproCarousel/__tests__/AmeliproCarousel.spec.ts +40 -0
  109. package/src/components/Amelipro/AmeliproCarousel/__tests__/__snapshots__/AmeliproCarousel.spec.ts.snap +342 -0
  110. package/src/components/Amelipro/AmeliproCarousel/types.d.ts +8 -0
  111. package/src/components/Amelipro/AmeliproClickableTile/AmeliproClickableTile.mdx +18 -0
  112. package/src/components/Amelipro/AmeliproClickableTile/AmeliproClickableTile.stories.ts +67 -0
  113. package/src/components/Amelipro/AmeliproClickableTile/AmeliproClickableTile.vue +233 -0
  114. package/src/components/Amelipro/AmeliproClickableTile/tests/AmeliproClickableTile.spec.ts +21 -0
  115. package/src/components/Amelipro/AmeliproClickableTile/tests/__snapshots__/AmeliproClickableTile.spec.ts.snap +140 -0
  116. package/src/components/Amelipro/AmeliproHeader/AmeliproHeader.vue +7 -1
  117. package/src/components/Amelipro/AmeliproHeader/tests/__snapshots__/AmeliproHeader.spec.ts.snap +5 -4
  118. package/src/components/Amelipro/AmeliproIcon/iconList.ts +6 -0
  119. package/src/components/Amelipro/AmeliproPageLayout/tests/__snapshots__/AmeliproPageLayout.spec.ts.snap +5 -4
  120. package/src/components/Amelipro/AmeliproResultList/AmeliproResultList.mdx +15 -0
  121. package/src/components/Amelipro/AmeliproResultList/AmeliproResultList.stories.ts +264 -0
  122. package/src/components/Amelipro/AmeliproResultList/AmeliproResultList.vue +231 -0
  123. package/src/components/Amelipro/AmeliproResultList/__tests__/AmeliproResultList.spec.ts +37 -0
  124. package/src/components/Amelipro/AmeliproResultList/__tests__/__snapshots__/AmeliproResultList.spec.ts.snap +434 -0
  125. package/src/components/Amelipro/AmeliproTable/AmeliproTable.vue +6 -5
  126. package/src/components/Amelipro/AmeliproTable/__tests__/__snapshots__/AmeliproTable.spec.ts.snap +23 -26
  127. package/src/components/Amelipro/AmeliproTileBtn/AmeliproTileBtn.stories.ts +2 -2
  128. package/src/components/ChipList/Accessibilite.stories.ts +4 -0
  129. package/src/components/ChipList/ChipList.vue +185 -42
  130. package/src/components/ChipList/locales.ts +4 -2
  131. package/src/components/ChipList/tests/chipList.spec.ts +7 -4
  132. package/src/components/Customs/Selects/SelectOverview.mdx +18 -15
  133. package/src/components/Customs/Selects/SyBtnSelect/SyBtnSelect.stories.ts +10 -10
  134. package/src/components/Customs/Selects/SySelect/SySelect.stories.ts +13 -5
  135. package/src/components/Customs/Selects/SySelect/SySelect.vue +108 -37
  136. package/src/components/Customs/SyCheckbox/SyCheckbox.mdx +3 -1
  137. package/src/components/Customs/SyCheckbox/SyCheckbox.stories.ts +165 -0
  138. package/src/components/Customs/SyCheckbox/SyCheckbox.vue +28 -9
  139. package/src/components/Customs/SyTabs/Accessibilite.mdx +309 -0
  140. package/src/components/Customs/SyTabs/SyTabs.mdx +117 -0
  141. package/src/components/Customs/SyTabs/SyTabs.stories.ts +354 -0
  142. package/src/components/Customs/SyTabs/SyTabs.vue +350 -0
  143. package/src/components/Customs/SyTabs/config.ts +17 -0
  144. package/src/components/Customs/SyTabs/tests/SyTabs.spec.ts +425 -0
  145. package/src/components/Customs/SyTabs/types.ts +12 -0
  146. package/src/components/Customs/SyTextField/SyTextField.mdx +3 -0
  147. package/src/components/Customs/SyTextField/SyTextField.stories.ts +142 -1
  148. package/src/components/Customs/SyTextField/SyTextField.vue +19 -16
  149. package/src/components/DataList/DataList.vue +47 -49
  150. package/src/components/DataListGroup/DataListGroup.vue +1 -1
  151. package/src/components/DataListItem/DataListItem.vue +67 -63
  152. package/src/components/DataListItem/tests/DataListItem.spec.ts +2 -2
  153. package/src/components/DatePicker/CalendarMode/DatePicker.stories.ts +3 -3
  154. package/src/components/DatePicker/CalendarMode/DatePicker.vue +49 -13
  155. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +412 -649
  156. package/src/components/DatePicker/DatePickerValidationExample/CalendarMode.stories.ts +215 -0
  157. package/src/components/DatePicker/DatePickerValidationExample/ComplexDatePicker.stories.ts +218 -0
  158. package/src/components/DatePicker/DatePickerValidationExample/DatePickerValidation.mdx +2 -0
  159. package/src/components/DatePicker/DatePickerValidationExample/DatePickerValidation.stories.ts +1 -1
  160. package/src/components/DatePicker/DatePickerValidationExample/DateTextInput.stories.ts +218 -0
  161. package/src/components/DatePicker/DatePickerValidationExample/MultiMode.stories.ts +281 -0
  162. package/src/components/DatePicker/DateTextInput/DateTextInput.events.spec.ts +17 -4
  163. package/src/components/DatePicker/DateTextInput/DateTextInput.range.spec.ts +111 -18
  164. package/src/components/DatePicker/DateTextInput/DateTextInput.spec.ts +238 -6
  165. package/src/components/DatePicker/DateTextInput/DateTextInput.vue +716 -757
  166. package/src/components/DatePicker/composables/tests/useDateInputEditing.spec.ts +4 -4
  167. package/src/components/DatePicker/composables/tests/useDisplayedDateString.spec.ts +17 -10
  168. package/src/components/DatePicker/composables/useDateInputEditing.ts +52 -22
  169. package/src/components/DatePicker/composables/useDisplayedDateString.ts +18 -4
  170. package/src/components/DatePicker/composables/useTodayButton.ts +13 -1
  171. package/src/components/DatePicker/utils/dateFormattingUtils.ts +79 -14
  172. package/src/components/DialogBox/DialogBox.stories.ts +12 -0
  173. package/src/components/DialogBox/DialogBox.vue +16 -11
  174. package/src/components/DialogBox/tests/DialogBox.spec.ts +22 -0
  175. package/src/components/HeaderLoading/Accessibilite.mdx +429 -8
  176. package/src/components/HeaderLoading/Accessibilite.stories.ts +4 -0
  177. package/src/components/HeaderLoading/HeaderLoading.vue +59 -0
  178. package/src/components/HeaderNavigationBar/HeaderNavigationBar.mdx +17 -2
  179. package/src/components/HeaderNavigationBar/HeaderNavigationBar.stories.ts +91 -2
  180. package/src/components/HeaderNavigationBar/HeaderNavigationBar.vue +37 -1
  181. package/src/components/HeaderNavigationBar/HorizontalNavbar/HorizontalNavbar.vue +276 -21
  182. package/src/components/HeaderNavigationBar/tests/HeaderNavigationBar.spec.ts +2 -2
  183. package/src/components/NirField/NirField.mdx +3 -0
  184. package/src/components/NirField/NirField.vue +10 -1
  185. package/src/components/NirField/tests/NirField.spec.ts +81 -0
  186. package/src/components/PasswordField/PasswordField.mdx +3 -0
  187. package/src/components/PeriodField/PeriodField.mdx +2 -0
  188. package/src/components/PeriodField/PeriodField.stories.ts +195 -0
  189. package/src/components/PhoneField/Accessibilite.stories.ts +4 -0
  190. package/src/components/PhoneField/PhoneField.mdx +3 -1
  191. package/src/components/PhoneField/PhoneField.stories.ts +285 -1
  192. package/src/components/PhoneField/PhoneField.vue +228 -95
  193. package/src/components/PhoneField/indicatifs.ts +102 -102
  194. package/src/components/PhoneField/locales.ts +1 -0
  195. package/src/components/PhoneField/tests/PhoneField.spec.ts +429 -2
  196. package/src/components/SkipLink/SkipLink.vue +3 -31
  197. package/src/components/SkipLink/tests/skipLink.spec.ts +0 -21
  198. package/src/components/SubHeader/Accessibilite.stories.ts +8 -0
  199. package/src/components/SubHeader/SubHeader.mdx +1 -0
  200. package/src/components/SubHeader/SubHeader.stories.ts +179 -60
  201. package/src/components/SubHeader/SubHeader.vue +45 -15
  202. package/src/components/SubHeader/locales.ts +1 -0
  203. package/src/components/SyAlert/SyAlert.vue +6 -0
  204. package/src/components/Tables/SyServerTable/SyServerTable.mdx +3 -10
  205. package/src/components/Tables/SyServerTable/SyServerTable.stories.ts +242 -0
  206. package/src/components/Tables/SyServerTable/SyServerTable.vue +29 -10
  207. package/src/components/Tables/SyTable/SyTable.mdx +3 -10
  208. package/src/components/Tables/SyTable/SyTable.stories.ts +242 -0
  209. package/src/components/Tables/SyTable/SyTable.vue +2 -0
  210. package/src/components/Tables/common/SyTablePagination.vue +13 -6
  211. package/src/components/Tables/common/tests/SyTablePagination.spec.ts +157 -0
  212. package/src/components/Tables/common/types.ts +2 -0
  213. package/src/components/index.ts +9 -0
  214. package/src/composables/useFilterable/useFilterable.ts +10 -0
  215. package/src/designTokens/tokens/amelipro/apColors.ts +1 -1
  216. package/src/designTokens/tokens/cnam/cnamSemantic.ts +3 -3
  217. package/src/stories/Components/Components.stories.ts +1 -1
  218. package/src/stories/GuideDuDev/FormValidationGuide.mdx +342 -0
  219. package/src/stories/Templates/Templates.stories.ts +1 -1
  220. package/src/utils/functions/ameliproColors/ameliproColors.ts +1 -1
  221. package/dist/components/DataList/locales.d.ts +0 -3
  222. package/src/components/DataList/locales.ts +0 -3
  223. package/src/components/PhoneField/tests/PhoneField.additional.spec.ts +0 -266
@@ -0,0 +1,354 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+
3
+ import SyTabs from './SyTabs.vue'
4
+ import { ref } from 'vue'
5
+
6
+ // Plus d'informations sur la configuration de Storybook pour Vue:
7
+ // https://storybook.js.org/docs/vue/writing-stories/introduction
8
+
9
+ /**
10
+ * `SyTabs` est un composant de navigation par onglets accessible et personnalisable.
11
+ * Il permet d'organiser le contenu en sections navigables facilement via une interface à onglets.
12
+ * Le composant implémente toutes les bonnes pratiques d'accessibilité ARIA et supporte la navigation complète au clavier.
13
+ */
14
+ const meta = {
15
+ title: 'Composants/Navigation/SyTabs',
16
+ component: SyTabs,
17
+ parameters: {
18
+ layout: 'fullscreen',
19
+ controls: { exclude: ['confirmationMessage'] },
20
+ },
21
+ argTypes: {
22
+ items: {
23
+ description: 'Liste des éléments à afficher dans les onglets',
24
+ control: 'object',
25
+ },
26
+ modelValue: {
27
+ description: 'Index ou valeur de l\'onglet actuellement sélectionné (utilisé avec v-model)',
28
+ control: 'text',
29
+ },
30
+ confirmTabChange: {
31
+ description: 'Si activé, une confirmation sera demandée avant de changer d\'onglet',
32
+ control: 'boolean',
33
+ },
34
+ },
35
+ args: {
36
+ items: [
37
+ { label: 'Onglet 1', value: 'tab1', content: 'Contenu de l\'onglet 1' },
38
+ { label: 'Onglet 2', value: 'tab2', content: 'Contenu de l\'onglet 2' },
39
+ { label: 'Onglet 3', value: 'tab3', content: 'Contenu de l\'onglet 3' },
40
+ ],
41
+ },
42
+ } as Meta<typeof SyTabs>
43
+
44
+ export default meta
45
+ type Story = StoryObj<typeof meta>
46
+
47
+ /**
48
+ * Exemple de base du composant SyTabs avec des onglets simples.
49
+ */
50
+ export const Default: Story = {
51
+ args: {},
52
+ parameters: {
53
+ sourceCode: [
54
+ {
55
+ name: 'Template',
56
+ code: `<SyTabs :items="items" />`,
57
+ },
58
+ {
59
+ name: 'Script',
60
+ code: `
61
+ const items = [
62
+ { label: 'Onglet 1', value: 'tab1', content: "Contenu de l'onglet 1" },
63
+ { label: 'Onglet 2', value: 'tab2', content: "Contenu de l'onglet 2" },
64
+ { label: 'Onglet 3', value: 'tab3', content: "Contenu de l'onglet 3" },
65
+ ]
66
+ `,
67
+ },
68
+ ],
69
+ },
70
+ }
71
+
72
+ /**
73
+ * Exemple avec v-model pour contrôler l'onglet actif de façon externe.
74
+ */
75
+ export const WithVModel: Story = {
76
+ render: args => ({
77
+ components: { SyTabs },
78
+ setup() {
79
+ const activeTab = ref('tab2')
80
+
81
+ return { args, activeTab }
82
+ },
83
+ template: `
84
+ <div>
85
+ <div class="mb-4">
86
+ Onglet actif: {{ activeTab }}
87
+ <button
88
+ class="ml-4 px-2 py-1 bg-primary text-white rounded"
89
+ @click="activeTab = activeTab === 'tab1' ? 'tab2' : 'tab1'"
90
+ >
91
+ Changer d'onglet
92
+ </button>
93
+ </div>
94
+ <SyTabs v-model="activeTab" :items="args.items" />
95
+ </div>
96
+ `,
97
+ }),
98
+ parameters: {
99
+ sourceCode: [
100
+ {
101
+ name: 'Template',
102
+ code: `
103
+ <template>
104
+ <div>
105
+ <div class="mb-4">
106
+ Onglet actif: {{ activeTab }}
107
+ <button
108
+ class="ml-4 px-2 py-1 bg-primary text-white rounded"
109
+ @click="activeTab = activeTab === 'tab1' ? 'tab2' : 'tab1'"
110
+ >
111
+ Changer d'onglet
112
+ </button>
113
+ </div>
114
+ <SyTabs v-model="activeTab" :items="items" />
115
+ </div>
116
+ </template>`,
117
+ },
118
+ {
119
+ name: 'Script',
120
+ code: `
121
+ <script setup>
122
+ import { ref } from 'vue'
123
+
124
+ const activeTab = ref('tab2')
125
+ const items = [
126
+ { label: 'Onglet 1', value: 'tab1', content: "Contenu de l'onglet 1" },
127
+ { label: 'Onglet 2', value: 'tab2', content: "Contenu de l'onglet 2" },
128
+ { label: 'Onglet 3', value: 'tab3', content: "Contenu de l'onglet 3" }
129
+ ]
130
+ </script>`,
131
+ },
132
+ ],
133
+ },
134
+ }
135
+
136
+ /**
137
+ * Exemple avec slots personnalisés pour le contenu des onglets.
138
+ */
139
+ export const WithCustomContent: Story = {
140
+ render: args => ({
141
+ components: { SyTabs },
142
+ setup() {
143
+ return { args }
144
+ },
145
+ template: `
146
+ <SyTabs :items="args.items">
147
+ <template #panel-0>
148
+ <div class="p-4 bg-info-light rounded">
149
+ <h3 class="text-h6 font-weight-bold">Contenu personnalisé pour l'onglet 1</h3>
150
+ <p>Vous pouvez utiliser des slots nommés <code>panel-{index}</code> pour personnaliser le contenu de chaque onglet.</p>
151
+ </div>
152
+ </template>
153
+ <template #panel-1>
154
+ <div class="p-4 bg-success-light rounded">
155
+ <h3 class="text-h6 font-weight-bold">Contenu personnalisé pour l'onglet 2</h3>
156
+ <p>Ce panneau utilise un style différent.</p>
157
+ </div>
158
+ </template>
159
+ </SyTabs>
160
+ `,
161
+ }),
162
+ parameters: {
163
+ sourceCode: [
164
+ {
165
+ name: 'Template',
166
+ code: `
167
+ <SyTabs :items="args.items">
168
+ <template #panel-0>
169
+ <div class="p-4 bg-info-light rounded">
170
+ <h3 class="text-h6 font-weight-bold">Contenu personnalisé pour l'onglet 1</h3>
171
+ <p>Vous pouvez utiliser des slots nommés <code>panel-{index}</code> pour personnaliser le contenu de chaque onglet.</p>
172
+ </div>
173
+ </template>
174
+ <template #panel-1>
175
+ <div class="p-4 bg-success-light rounded">
176
+ <h3 class="text-h6 font-weight-bold">Contenu personnalisé pour l'onglet 2</h3>
177
+ <p>Ce panneau utilise un style différent.</p>
178
+ </div>
179
+ </template>
180
+ </SyTabs>
181
+ `,
182
+ },
183
+ {
184
+ name: 'Script',
185
+ code: `
186
+
187
+ `,
188
+
189
+ },
190
+ ],
191
+ },
192
+ }
193
+
194
+ /**
195
+ * Exemple avec de nombreux onglets pour démontrer le comportement de scrolling.
196
+ */
197
+ export const ManyTabs: Story = {
198
+ args: {
199
+ items: Array.from({ length: 10 }, (_, i) => ({
200
+ label: `Onglet ${i + 1}`,
201
+ value: `tab${i + 1}`,
202
+ content: `Contenu de l'onglet ${i + 1}`,
203
+ })),
204
+ },
205
+ parameters: {
206
+ sourceCode: [
207
+ {
208
+ name: 'Template',
209
+ code: `<SyTabs :items="items" />`,
210
+ },
211
+ {
212
+ name: 'Script',
213
+ code: `
214
+ // Création d'un grand nombre d'onglets pour tester le comportement de défilement
215
+ const items = Array.from({ length: 10 }, (_, i) => ({
216
+ label: \`Onglet \${i + 1}\`,
217
+ value: \`tab\${i + 1}\`,
218
+ content: \`Contenu de l'onglet \${i + 1}\`,
219
+ }))
220
+ `,
221
+ },
222
+ ],
223
+ },
224
+ }
225
+
226
+ /**
227
+ * Exemple avec des options de personnalisation du thème.
228
+ */
229
+ export const CustomTheme: Story = {
230
+ render: args => ({
231
+ components: { SyTabs },
232
+ setup() {
233
+ return { args }
234
+ },
235
+ template: `
236
+ <SyTabs
237
+ :items="args.items"
238
+ :vuetifyOptions="{
239
+ sheet: { theme: 'dark', color: '#0C419A' },
240
+ tab: { 'base-color': '#ffffff', 'active-color': '#ffffff', 'slider-color': '#42b983' },
241
+ tabs: { height: '60' }
242
+ }"
243
+ />
244
+ `,
245
+ }),
246
+ parameters: {
247
+ sourceCode: [
248
+ {
249
+ name: 'Template',
250
+ code: `
251
+ <SyTabs
252
+ :items="items"
253
+ :vuetifyOptions="{
254
+ sheet: { theme: 'dark', color: '#0C419A' },
255
+ tab: { 'base-color': '#ffffff', 'active-color': '#ffffff', 'slider-color': '#42b983' },
256
+ tabs: { height: '60' }
257
+ }"
258
+ />
259
+ `,
260
+ },
261
+ {
262
+ name: 'Script',
263
+ code: `
264
+ <script setup>
265
+ const items = [
266
+ { label: 'Onglet 1', value: 'tab1', content: "Contenu de l'onglet 1" },
267
+ { label: 'Onglet 2', value: 'tab2', content: "Contenu de l'onglet 2" },
268
+ { label: 'Onglet 3', value: 'tab3', content: "Contenu de l'onglet 3" },
269
+ ]
270
+ </script>
271
+ `,
272
+ },
273
+ ],
274
+ },
275
+ }
276
+
277
+ /**
278
+ * Exemple avec confirmation avant changement d'onglet.
279
+ * Démontre comment utiliser la propriété confirmTabChange et gérer l'événement confirm-tab-change.
280
+ */
281
+ export const WithTabConfirmation: Story = {
282
+ render: args => ({
283
+ components: { SyTabs },
284
+ setup() {
285
+ return {
286
+ args,
287
+ showConfirmDialog: (message: string, callback: (confirmed: boolean) => void) => {
288
+ // Dans un cas réel, vous afficheriez une boîte de dialogue personnalisée
289
+ // Ici nous utilisons window.confirm pour la démonstration
290
+ const confirmed = window.confirm('Voulez-vous vraiment changer d\'onglet ?')
291
+ // Appelons le callback avec le résultat de la confirmation
292
+ callback(confirmed)
293
+ },
294
+ }
295
+ },
296
+ template: `
297
+ <div>
298
+ <div class="mb-4 pa-2 bg-warning-light">
299
+ <strong>Note :</strong> Essayez de changer d'onglet. Une boîte de dialogue de confirmation s'affichera.
300
+ </div>
301
+
302
+ <SyTabs
303
+ :items="args.items"
304
+ :confirmTabChange="true"
305
+ @confirm-tab-change="showConfirmDialog"
306
+ />
307
+ </div>
308
+ `,
309
+ }),
310
+ parameters: {
311
+ sourceCode: [
312
+ {
313
+ name: 'Template',
314
+ code: `
315
+ <template>
316
+ <div>
317
+ <div class="mb-4 pa-2 bg-warning-light">
318
+ <strong>Note :</strong> Essayez de changer d'onglet. Une boîte de dialogue de confirmation s'affichera.
319
+ </div>
320
+ <SyTabs
321
+ :items="items"
322
+ :confirmTabChange="true"
323
+ @confirm-tab-change="showConfirmDialog"
324
+ />
325
+ </div>
326
+ </template>
327
+ `,
328
+ },
329
+ {
330
+ name: 'Script',
331
+ code: `
332
+ <script setup>
333
+ import { ref } from 'vue'
334
+
335
+ const items = [
336
+ { label: 'Onglet 1', value: 'tab1', content: "Contenu de l'onglet 1" },
337
+ { label: 'Onglet 2', value: 'tab2', content: "Contenu de l'onglet 2" },
338
+ { label: 'Onglet 3', value: 'tab3', content: "Contenu de l'onglet 3" }
339
+ ]
340
+
341
+ // Fonction pour afficher une boîte de dialogue de confirmation
342
+ function showConfirmDialog(message, callback) {
343
+ // Dans un cas réel, vous afficheriez une boîte de dialogue personnalisée
344
+ // Ici nous utilisons window.confirm pour la démonstration
345
+ const confirmed = window.confirm("Voulez-vous vraiment changer d'onglet ?")
346
+ // Appelons le callback avec le résultat de la confirmation
347
+ callback(confirmed)
348
+ }
349
+ </script>
350
+ `,
351
+ },
352
+ ],
353
+ },
354
+ }
@@ -0,0 +1,350 @@
1
+ <script setup lang="ts">
2
+ import type { TabItem } from './types'
3
+ import useCustomizableOptions from '@/composables/useCustomizableOptions'
4
+ import { config } from './config'
5
+ import { ref, watch, onMounted, onUnmounted } from 'vue'
6
+
7
+ const props = withDefaults(defineProps<{
8
+ items: TabItem[]
9
+ modelValue?: number | string
10
+ /** Si activé, une confirmation sera demandée avant de changer d'onglet */
11
+ confirmTabChange?: boolean
12
+ /** Message affiché dans la boîte de dialogue de confirmation */
13
+ confirmationMessage?: boolean
14
+ /** Options personnalisées pour les composants Vuetify */
15
+ vuetifyOptions?: {
16
+ sheet?: {
17
+ theme?: string
18
+ dense?: boolean
19
+ color?: string
20
+ }
21
+ tabs?: {
22
+ height?: string
23
+ showArrows?: boolean
24
+ }
25
+ tab?: {
26
+ 'base-color'?: string
27
+ 'active-color'?: string
28
+ 'slider-color'?: string
29
+ 'ripple'?: boolean
30
+ }
31
+ }
32
+ }>(), {
33
+ modelValue: undefined,
34
+ confirmTabChange: false,
35
+ confirmationMessage: false,
36
+ vuetifyOptions: () => ({}),
37
+ })
38
+
39
+ const emit = defineEmits(['update:modelValue', 'cancel-navigation', 'confirm-tab-change'])
40
+
41
+ defineSlots<{
42
+ 'tabs-prepend': () => unknown
43
+ 'tabs-append': () => unknown
44
+ 'default': () => unknown
45
+ }>()
46
+
47
+ const options = useCustomizableOptions(config, { vuetifyOptions: props.vuetifyOptions })
48
+
49
+ // État pour suivre l'élément activement sélectionné
50
+ const activeItemIndex = ref<number>(0)
51
+ // Élément actuellement focusé (pour la navigation clavier)
52
+ const focusedItemIndex = ref<number>(-1)
53
+
54
+ // Émet un événement pour gérer la confirmation de changement d'onglet
55
+ async function handleTabChangeConfirmation(message: string): Promise<boolean> {
56
+ // Émettre l'événement avec le message et retourner une promesse
57
+ let resolver: (value: boolean) => void
58
+ const promise = new Promise<boolean>((resolve) => {
59
+ resolver = resolve
60
+ })
61
+
62
+ // Émettre l'événement avec le message et un callback pour résoudre la promesse
63
+ emit('confirm-tab-change', message, (confirmed: boolean) => {
64
+ resolver(confirmed)
65
+ })
66
+
67
+ return promise
68
+ }
69
+
70
+ // Fonction pour activer un élément au clic avec confirmation si nécessaire
71
+ async function setActiveItem(index: number) {
72
+ // Si l'index est déjà actif, ne rien faire
73
+ if (index === activeItemIndex.value) return
74
+
75
+ // Récupérer l'item pour la navigation potentielle
76
+ const item = props.items[index]
77
+ const hasHref = item && (item.href || item.to)
78
+
79
+ // Si la confirmation est activée, demander confirmation
80
+ if (props.confirmTabChange) {
81
+ const confirmMessage = props.confirmationMessage
82
+ const confirmed = await handleTabChangeConfirmation(confirmMessage.toString())
83
+
84
+ if (!confirmed) {
85
+ // L'utilisateur a annulé, émettre un événement d'annulation
86
+ emit('cancel-navigation')
87
+ return
88
+ }
89
+
90
+ // Si l'utilisateur a confirmé et qu'il y a un href, naviguer
91
+ if (hasHref) {
92
+ if (item.href) {
93
+ window.location.href = item.href
94
+ return // Arrêter ici car on navigue ailleurs
95
+ }
96
+ else if (item.to) {
97
+ // Pour les cas où vue-router est utilisé
98
+ // Notez que cela nécessiterait un accès au router,
99
+ // donc on se limite au href pour l'instant
100
+ }
101
+ }
102
+ }
103
+ // Sinon pas de confirmation nécessaire, naviguer directement si href présent
104
+ else if (hasHref && item.href) {
105
+ window.location.href = item.href
106
+ return
107
+ }
108
+
109
+ // Mettre à jour l'onglet actif
110
+ activeItemIndex.value = index
111
+ emit('update:modelValue', typeof props.modelValue === 'string' ? props.items[index].value : index)
112
+ }
113
+
114
+ // Fonction pour gérer les touches Enter et Space
115
+ function handleKeyPress(event: KeyboardEvent, index: number) {
116
+ if (event.key === 'Enter' || event.key === ' ') {
117
+ event.preventDefault()
118
+ void setActiveItem(index) // void pour ignorer la promesse
119
+ }
120
+ }
121
+
122
+ // Configuration des attributs ARIA et gestion du focus global
123
+ function setupAccessibilityFeatures() {
124
+ // Ajouter un écouteur global pour gérer l'escape key
125
+ const handleEscape = (event: KeyboardEvent) => {
126
+ if (event.key === 'Escape' && focusedItemIndex.value >= 0) {
127
+ // Retirer le focus des éléments du menu
128
+ focusedItemIndex.value = -1
129
+ }
130
+ }
131
+
132
+ window.addEventListener('keydown', handleEscape)
133
+
134
+ // Cleanup
135
+ onUnmounted(() => {
136
+ window.removeEventListener('keydown', handleEscape)
137
+ })
138
+ }
139
+
140
+ // Navigation clavier entre les tabs (touches de direction)
141
+ function handleArrowNavigation(event: KeyboardEvent, currentIndex: number) {
142
+ if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return
143
+ event.preventDefault()
144
+ const itemCount = props.items?.length || 0
145
+ if (itemCount === 0) return
146
+
147
+ let newIndex = currentIndex
148
+ switch (event.key) {
149
+ case 'ArrowLeft':
150
+ newIndex = currentIndex <= 0 ? itemCount - 1 : currentIndex - 1
151
+ break
152
+ case 'ArrowRight':
153
+ newIndex = currentIndex >= itemCount - 1 ? 0 : currentIndex + 1
154
+ break
155
+ case 'Home':
156
+ newIndex = 0
157
+ break
158
+ case 'End':
159
+ newIndex = itemCount - 1
160
+ break
161
+ }
162
+
163
+ // Mettre à jour l'index de l'élément focusé et actif
164
+ focusedItemIndex.value = newIndex
165
+
166
+ // Focus sur le nouvel élément et l'activer
167
+ // Vérifier si l'élément existe et se focaliser sur le bouton
168
+ const tabButton = document.getElementById(`tab-${newIndex}`)
169
+ if (tabButton) {
170
+ tabButton.focus()
171
+ setActiveItem(newIndex)
172
+ }
173
+ }
174
+
175
+ // Initialiser l'élément actif au montage
176
+ onMounted(() => {
177
+ // Configurer l'accessibilité
178
+ setupAccessibilityFeatures()
179
+
180
+ // Si les items ne sont pas un tableau ou vides, ne rien faire
181
+ if (!Array.isArray(props.items) || props.items.length === 0) return
182
+
183
+ // Si modelValue est défini, utiliser cette valeur pour déterminer l'index actif
184
+ if (props.modelValue !== undefined) {
185
+ if (typeof props.modelValue === 'number') {
186
+ activeItemIndex.value = props.modelValue
187
+ }
188
+ else {
189
+ // Chercher l'index de l'item avec la valeur correspondante
190
+ const index = props.items.findIndex(item => item.value === props.modelValue)
191
+ if (index !== -1) {
192
+ activeItemIndex.value = index
193
+ }
194
+ }
195
+ }
196
+ else {
197
+ // Par défaut, sélectionner le premier élément
198
+ activeItemIndex.value = 0
199
+ }
200
+ })
201
+
202
+ // Observer les changements du modelValue pour mettre à jour l'élément actif
203
+ watch(() => props.modelValue, (newValue) => {
204
+ if (newValue !== undefined) {
205
+ if (typeof newValue === 'number') {
206
+ activeItemIndex.value = newValue
207
+ }
208
+ else {
209
+ const index = props.items.findIndex(item => item.value === newValue)
210
+ if (index !== -1) {
211
+ activeItemIndex.value = index
212
+ }
213
+ }
214
+ }
215
+ })
216
+ </script>
217
+
218
+ <template>
219
+ <VSheet
220
+ :theme="options.sheet.theme"
221
+ :color="options.sheet.color"
222
+ :class="{ 'v-sheet--dense': options.sheet.dense }"
223
+ >
224
+ <div class="sy-tabs px-xl-0 px-4">
225
+ <slot name="tabs-prepend" />
226
+ <slot>
227
+ <nav
228
+ role="tablist"
229
+ aria-label="Onglets de navigation"
230
+ class="sy-tabs__nav"
231
+ >
232
+ <ul
233
+ class="sy-tabs__list"
234
+ >
235
+ <li
236
+ v-for="(item, index) in Array.isArray(items) ? items : []"
237
+ :key="index"
238
+ class="sy-tabs__item"
239
+ role="presentation"
240
+ >
241
+ <a
242
+ :id="`tab-${index}`"
243
+ class="sy-tabs__button"
244
+ :class="{ 'sy-tabs__button--active': activeItemIndex === index }"
245
+ role="tab"
246
+ :aria-selected="activeItemIndex === index"
247
+ :aria-controls="`panel-${index}`"
248
+ tabindex="0"
249
+ @click="setActiveItem(index)"
250
+ @keydown="(event) => {
251
+ handleKeyPress(event, index);
252
+ handleArrowNavigation(event, index);
253
+ }"
254
+ >
255
+ {{ item.label.toUpperCase() }}
256
+ </a>
257
+ </li>
258
+ </ul>
259
+ </nav>
260
+ </slot>
261
+ <slot name="tabs-append" />
262
+ </div>
263
+ </VSheet>
264
+
265
+ <!-- Panneau de contenu des onglets -->
266
+ <div class="sy-tabs-panels">
267
+ <div
268
+ v-for="(item, index) in Array.isArray(items) ? items : []"
269
+ :id="`panel-${index}`"
270
+ :key="`panel-${index}`"
271
+ class="sy-tabs-panel"
272
+ role="tabpanel"
273
+ :aria-labelledby="`tab-${index}`"
274
+ :hidden="activeItemIndex !== index"
275
+ >
276
+ <slot
277
+ :name="`panel-${index}`"
278
+ :active="activeItemIndex === index"
279
+ >
280
+ {{ item.content || '' }}
281
+ </slot>
282
+ </div>
283
+ </div>
284
+ </template>
285
+
286
+ <style lang="scss" scoped>
287
+ @use '@/assets/tokens.scss' as *;
288
+
289
+ .sy-tabs {
290
+ display: flex;
291
+ align-items: center;
292
+ }
293
+
294
+ .sy-tabs__nav {
295
+ flex: 1 1 0;
296
+ width: 100%;
297
+ }
298
+
299
+ .sy-tabs__list {
300
+ display: flex;
301
+ list-style-type: none;
302
+ padding: 0;
303
+ margin: 0;
304
+ width: 100%;
305
+ }
306
+
307
+ .sy-tabs__item {
308
+ cursor: pointer;
309
+ display: flex;
310
+ align-items: stretch;
311
+ }
312
+
313
+ .sy-tabs__button {
314
+ display: flex;
315
+ align-items: center;
316
+ justify-content: center;
317
+ padding: 0 16px;
318
+ min-height: v-bind("options.tabs.height + 'px'");
319
+ font-size: 0.875rem;
320
+ font-weight: 700;
321
+ background: none;
322
+ border: none;
323
+ color: v-bind("options.tab['base-color']");
324
+ transition: color 0.2s ease;
325
+
326
+ &:hover {
327
+ color: v-bind("options.tab['active-color']");
328
+ }
329
+
330
+ &:focus-visible {
331
+ outline: 3px solid v-bind("options.tab['active-color']");
332
+ outline-offset: -3px;
333
+ }
334
+
335
+ &--active {
336
+ color: v-bind("options.tab['active-color']");
337
+ border-bottom: 3px solid v-bind("options.tab['slider-color']");
338
+ }
339
+ }
340
+
341
+ .sy-tabs-panels {
342
+ padding: 16px;
343
+ }
344
+
345
+ .sy-tabs-panel {
346
+ &[hidden] {
347
+ display: none;
348
+ }
349
+ }
350
+ </style>