@cnamts/synapse 1.0.0 → 1.0.2

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 (533) hide show
  1. package/README.md +1 -1
  2. package/dist/DateFilter-YWOTbfeL.js +98 -0
  3. package/dist/NumberFilter-DMmMgALM.js +121 -0
  4. package/dist/PeriodFilter-Bok5BHcn.js +112 -0
  5. package/dist/SelectFilter-BKud2WhN.js +136 -0
  6. package/dist/TextFilter-DvMf2thH.js +114 -0
  7. package/dist/{components/BackBtn → common}/constants/ExpertiseLevelEnum.d.ts +3 -0
  8. package/dist/components/Accordion/Accordion.d.ts +2 -1
  9. package/dist/components/Accordion/composables/useAccordionGroupCommunication.d.ts +5 -0
  10. package/dist/components/Accordion/composables/useAccordionKeyboardNavigation.d.ts +12 -0
  11. package/dist/components/Accordion/composables/useAccordionState.d.ts +13 -0
  12. package/dist/components/BackBtn/AccessibiliteItems.d.ts +1 -1
  13. package/dist/components/BackToTopBtn/AccessibiliteItems.d.ts +1 -1
  14. package/dist/components/ChipList/AccessibiliteItems.d.ts +1 -1
  15. package/dist/components/CollapsibleList/AccessibiliteItems.d.ts +1 -1
  16. package/dist/components/ContextualMenu/AccessibiliteItems.d.ts +1 -1
  17. package/dist/components/CookieBanner/AccessibiliteItems.d.ts +1 -1
  18. package/dist/components/CopyBtn/AccessibiliteItems.d.ts +1 -1
  19. package/dist/components/Customs/SyBtnSelect/AccessibiliteItems.d.ts +1 -1
  20. package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +85 -0
  21. package/dist/components/Customs/SyInputSelect/AccessibiliteItems.d.ts +1 -1
  22. package/dist/components/Customs/SyInputSelect/SyInputSelect.d.ts +2 -0
  23. package/dist/components/Customs/SySelect/AccessibiliteItems.d.ts +1 -1
  24. package/dist/components/Customs/SySelect/SySelect.d.ts +33 -13
  25. package/dist/components/Customs/SyTextField/AccessibiliteItems.d.ts +1 -1
  26. package/dist/components/Customs/SyTextField/SyTextField.d.ts +2 -2
  27. package/dist/components/DataList/AccessibiliteItems.d.ts +1 -1
  28. package/dist/components/DataListGroup/AccessibiliteItems.d.ts +1 -1
  29. package/dist/components/DatePicker/AccessibiliteItems.d.ts +1 -1
  30. package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +4295 -0
  31. package/dist/components/DatePicker/{DatePicker.d.ts → DatePicker/DatePicker.d.ts} +158 -104
  32. package/dist/components/DatePicker/{DateTextInput.d.ts → DateTextInput/DateTextInput.d.ts} +35 -27
  33. package/dist/components/DatePicker/composables/index.d.ts +17 -0
  34. package/dist/components/DatePicker/composables/useAsteriskDisplay.d.ts +14 -0
  35. package/dist/components/DatePicker/composables/useDateAutoClamp.d.ts +16 -0
  36. package/dist/components/DatePicker/composables/useDateFormatValidation.d.ts +25 -0
  37. package/dist/components/DatePicker/composables/useDateInputEditing.d.ts +33 -0
  38. package/dist/components/DatePicker/composables/useDatePickerViewMode.d.ts +16 -0
  39. package/dist/components/DatePicker/composables/useDatePickerVisibility.d.ts +27 -0
  40. package/dist/components/DatePicker/composables/useDateRangeInput.d.ts +29 -0
  41. package/dist/components/DatePicker/composables/useDateRangeValidation.d.ts +11 -0
  42. package/dist/components/DatePicker/composables/useDateSelection.d.ts +10 -0
  43. package/dist/components/DatePicker/composables/useDateValidation.d.ts +35 -0
  44. package/dist/components/DatePicker/composables/useDisplayedDateString.d.ts +19 -0
  45. package/dist/components/DatePicker/composables/useIconState.d.ts +17 -0
  46. package/dist/components/DatePicker/composables/useInputBlurHandler.d.ts +31 -0
  47. package/dist/components/DatePicker/composables/useInputHandler.d.ts +32 -0
  48. package/dist/components/DatePicker/composables/useManualDateValidation.d.ts +33 -0
  49. package/dist/components/DatePicker/composables/useMonthButtonCustomization.d.ts +12 -0
  50. package/dist/components/DatePicker/composables/useTodayButton.d.ts +16 -0
  51. package/dist/components/DatePicker/composables/useWeekendDays.d.ts +11 -0
  52. package/dist/components/DatePicker/constants/messages.d.ts +29 -0
  53. package/dist/components/DatePicker/types.d.ts +13 -0
  54. package/dist/components/DialogBox/AccessibiliteItems.d.ts +1 -1
  55. package/dist/components/DownloadBtn/AccessibiliteItems.d.ts +1 -1
  56. package/dist/components/ErrorPage/AccessibiliteItems.d.ts +1 -1
  57. package/dist/components/ExternalLinks/AccessibiliteItems.d.ts +1 -1
  58. package/dist/components/FileList/AccessibiliteItems.d.ts +1 -1
  59. package/dist/components/FilePreview/AccessibiliteItems.d.ts +1 -1
  60. package/dist/components/FileUpload/AccessibiliteItems.d.ts +1 -1
  61. package/dist/components/FilterInline/AccessibiliteItems.d.ts +1 -1
  62. package/dist/components/FilterSideBar/AccessibiliteItems.d.ts +1 -1
  63. package/dist/components/FilterSideBar/FilterSideBar.d.ts +395 -1
  64. package/dist/components/FooterBar/AccessibiliteItems.d.ts +1 -1
  65. package/dist/components/FranceConnectBtn/AccessibiliteItems.d.ts +1 -1
  66. package/dist/components/HeaderBar/AccessibiliteItems.d.ts +1 -1
  67. package/dist/components/HeaderBar/HeaderBurgerMenu/AccessibiliteItems.d.ts +1 -1
  68. package/dist/components/HeaderLoading/AccessibiliteItems.d.ts +1 -1
  69. package/dist/components/HeaderToolbar/AccessibiliteItems.d.ts +1 -1
  70. package/dist/components/LangBtn/AccessibiliteItems.d.ts +1 -1
  71. package/dist/components/Logo/AccessibiliteItems.d.ts +1 -1
  72. package/dist/components/LogoBrandSection/AccessibiliteItems.d.ts +1 -1
  73. package/dist/components/MaintenancePage/AccessibiliteItems.d.ts +1 -1
  74. package/dist/components/NirField/AccessibiliteItems.d.ts +1 -1
  75. package/dist/components/NirField/NirField.d.ts +7 -3
  76. package/dist/components/NirField/nirValidation.d.ts +1 -1
  77. package/dist/components/NotFoundPage/AccessibiliteItems.d.ts +1 -1
  78. package/dist/components/NotificationBar/AccessibiliteItems.d.ts +1 -1
  79. package/dist/components/PageContainer/AccessibiliteItems.d.ts +1 -1
  80. package/dist/components/PaginatedTable/AccessibiliteItems.d.ts +1 -1
  81. package/dist/components/PasswordField/AccessibiliteItems.d.ts +1 -1
  82. package/dist/components/PasswordField/PasswordField.d.ts +2 -0
  83. package/dist/components/PeriodField/AccessibiliteItems.d.ts +1 -1
  84. package/dist/components/PeriodField/PeriodField.d.ts +219 -41
  85. package/dist/components/PhoneField/AccessibiliteItems.d.ts +1 -1
  86. package/dist/components/PhoneField/PhoneField.d.ts +2 -2
  87. package/dist/components/RangeField/AccessibiliteItems.d.ts +1 -1
  88. package/dist/components/RangeField/RangeField.d.ts +2 -0
  89. package/dist/components/RatingPicker/AccessibiliteItems.d.ts +1 -1
  90. package/dist/components/SearchListField/AccessibiliteItems.d.ts +1 -1
  91. package/dist/components/SearchListField/SearchListField.d.ts +9 -0
  92. package/dist/components/SelectBtnField/AccessibiliteItems.d.ts +1 -1
  93. package/dist/components/SkipLink/AccessibiliteItems.d.ts +1 -1
  94. package/dist/components/SocialMediaLinks/AccessibiliteItems.d.ts +1 -1
  95. package/dist/components/SubHeader/AccessibiliteItems.d.ts +1 -1
  96. package/dist/components/SyAlert/AccessibiliteItems.d.ts +1 -1
  97. package/dist/components/SyTextArea/SyTextArea.d.ts +2 -0
  98. package/dist/components/TableToolbar/AccessibiliteItems.d.ts +1 -1
  99. package/dist/components/Tables/SyServerTable/SyServerTable.d.ts +652 -0
  100. package/dist/components/Tables/SyTable/SyTable.d.ts +671 -0
  101. package/dist/components/Tables/common/SyTableFilter.d.ts +126 -0
  102. package/dist/components/Tables/common/SyTablePagination.d.ts +1636 -0
  103. package/dist/components/Tables/common/TableHeader.d.ts +15 -0
  104. package/dist/components/Tables/common/constants/StateEnum.d.ts +6 -0
  105. package/dist/components/Tables/common/filters/DateFilter.d.ts +121 -0
  106. package/dist/components/Tables/common/filters/NumberFilter.d.ts +132 -0
  107. package/dist/components/Tables/common/filters/PeriodFilter.d.ts +135 -0
  108. package/dist/components/Tables/common/filters/SelectFilter.d.ts +120 -0
  109. package/dist/components/Tables/common/filters/TextFilter.d.ts +132 -0
  110. package/dist/components/Tables/common/filters/getFilterComponent.d.ts +1 -0
  111. package/dist/components/Tables/common/filters/locales.d.ts +4 -0
  112. package/dist/components/Tables/common/filters/logics/date.d.ts +1 -0
  113. package/dist/components/Tables/common/filters/logics/number.d.ts +1 -0
  114. package/dist/components/Tables/common/filters/logics/period.d.ts +1 -0
  115. package/dist/components/Tables/common/filters/logics/select.d.ts +1 -0
  116. package/dist/components/Tables/common/filters/logics/text.d.ts +1 -0
  117. package/dist/components/Tables/common/formatters.d.ts +17 -0
  118. package/dist/components/Tables/common/locales.d.ts +28 -0
  119. package/dist/components/Tables/common/organizeColumns/OrganizeColumns.d.ts +267 -0
  120. package/dist/components/Tables/common/organizeColumns/sortHeaders.d.ts +2 -0
  121. package/dist/components/Tables/common/tableAccessibilityUtils.d.ts +8 -0
  122. package/dist/components/Tables/common/tableFilterUtils.d.ts +3 -0
  123. package/dist/components/Tables/common/tableStorageUtils.d.ts +69 -0
  124. package/dist/components/Tables/common/tableUtils.d.ts +79 -0
  125. package/dist/components/Tables/common/types.d.ts +91 -0
  126. package/dist/components/Tables/common/usePagination.d.ts +22 -0
  127. package/dist/components/Tables/common/useTableCheckbox.d.ts +20 -0
  128. package/dist/components/Tables/common/useTableFilter.d.ts +9 -0
  129. package/dist/components/Tables/common/useTableHeaders.d.ts +76 -0
  130. package/dist/components/Tables/common/useTableItems.d.ts +24 -0
  131. package/dist/components/Tables/common/useTableOptions.d.ts +18 -0
  132. package/dist/components/Tables/index.d.ts +2 -0
  133. package/dist/components/ToolbarContainer/ToolbarContainer.d.ts +11 -0
  134. package/dist/components/UploadWorkflow/AccessibiliteItems.d.ts +1 -1
  135. package/dist/components/UserMenuBtn/AccessibiliteItems.d.ts +1 -1
  136. package/dist/components/UserMenuBtn/UserMenuBtn.d.ts +9 -2
  137. package/dist/components/index.d.ts +9 -5
  138. package/dist/design-system-v3.js +102 -11944
  139. package/dist/design-system-v3.umd.cjs +22 -4
  140. package/dist/designTokens/index.d.ts +3 -1
  141. package/dist/designTokens/tokens/cnam/cnamFonts.d.ts +140 -0
  142. package/dist/designTokens/tokens/pa/paFonts.d.ts +140 -0
  143. package/dist/designTokens/utils/createFontVariables.d.ts +39 -0
  144. package/dist/designTokens/utils/index.d.ts +2 -1
  145. package/dist/main-Cx8qG7YR.js +16344 -0
  146. package/dist/stories/Accessibilite/Vuetify/VuetifyItems.d.ts +14 -2
  147. package/dist/stories/DesignTokens/StylesTypographiques.stories.new.d.ts +8 -0
  148. package/dist/stories/DesignTokens/TypographyDisplay.d.ts +28 -0
  149. package/dist/stories/DesignTokens/vue-shims.d.ts +6 -0
  150. package/dist/style.css +1 -1
  151. package/dist/utils/rules/isRequired/index.d.ts +1 -1
  152. package/dist/vite-env.d.ts +12 -0
  153. package/package.json +3 -3
  154. package/src/assets/_spacers.scss +37 -1
  155. package/src/assets/_typography.scss +158 -0
  156. package/src/assets/settings.scss +7 -0
  157. package/src/common/constants/ExpertiseLevelEnum.ts +7 -0
  158. package/src/common/imgs/accessibility-svgrepo-com.svg +4 -0
  159. package/src/components/Accordion/Accessibilite/AccessibilityGuide.mdx +249 -0
  160. package/src/components/Accordion/Accordion.vue +48 -76
  161. package/src/components/Accordion/composables/__tests__/useAccordionGroupCommunication.spec.ts +146 -0
  162. package/src/components/Accordion/composables/__tests__/useAccordionKeyboardNavigation.spec.ts +209 -0
  163. package/src/components/Accordion/composables/__tests__/useAccordionState.spec.ts +144 -0
  164. package/src/components/Accordion/composables/useAccordionGroupCommunication.ts +52 -0
  165. package/src/components/Accordion/composables/useAccordionKeyboardNavigation.ts +111 -0
  166. package/src/components/Accordion/composables/useAccordionState.ts +59 -0
  167. package/src/components/Accordion/tests/__snapshots__/accordion.spec.ts.snap +3 -0
  168. package/src/components/BackBtn/AccessibiliteItems.ts +1 -1
  169. package/src/components/BackToTopBtn/AccessibiliteItems.ts +1 -1
  170. package/src/components/ChipList/AccessibiliteItems.ts +1 -1
  171. package/src/components/CollapsibleList/AccessibiliteItems.ts +1 -1
  172. package/src/components/ContextualMenu/AccessibiliteItems.ts +1 -1
  173. package/src/components/CookieBanner/AccessibiliteItems.ts +1 -1
  174. package/src/components/CopyBtn/AccessibiliteItems.ts +1 -1
  175. package/src/components/Customs/SyBtnSelect/AccessibiliteItems.ts +1 -1
  176. package/src/components/Customs/SyCheckbox/Accessibilite.mdx +303 -0
  177. package/src/components/Customs/SyCheckbox/SyCheckbox.mdx +50 -0
  178. package/src/components/Customs/SyCheckbox/SyCheckbox.stories.ts +630 -0
  179. package/src/components/Customs/SyCheckbox/SyCheckbox.vue +326 -0
  180. package/src/components/Customs/SyCheckbox/tests/SyCheckbox.spec.ts +201 -0
  181. package/src/components/Customs/SyInputSelect/AccessibiliteItems.ts +1 -1
  182. package/src/components/Customs/SyInputSelect/SyInputSelect.stories.ts +1 -0
  183. package/src/components/Customs/SyInputSelect/SyInputSelect.vue +8 -1
  184. package/src/components/Customs/SySelect/AccessibiliteItems.ts +1 -1
  185. package/src/components/Customs/SySelect/SySelect.stories.ts +160 -0
  186. package/src/components/Customs/SySelect/SySelect.vue +292 -39
  187. package/src/components/Customs/SySelect/tests/SySelect.spec.ts +234 -2
  188. package/src/components/Customs/SyTextField/AccessibiliteItems.ts +1 -1
  189. package/src/components/Customs/SyTextField/SyTextField.stories.ts +3 -2
  190. package/src/components/Customs/SyTextField/SyTextField.vue +19 -8
  191. package/src/components/DataList/AccessibiliteItems.ts +1 -1
  192. package/src/components/DataListGroup/AccessibiliteItems.ts +1 -1
  193. package/src/components/DatePicker/AccessibiliteItems.ts +1 -1
  194. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.mdx +212 -0
  195. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.stories.ts +1307 -0
  196. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +769 -204
  197. package/src/components/DatePicker/ComplexDatePicker/tests/ComplexDatePicker.events.spec.ts +161 -0
  198. package/src/components/DatePicker/ComplexDatePicker/tests/ComplexDatePicker.spec.ts +943 -0
  199. package/src/components/DatePicker/DatePicker/DatePicker.mdx +176 -0
  200. package/src/components/DatePicker/{DatePicker.stories.ts → DatePicker/DatePicker.stories.ts} +356 -519
  201. package/src/components/DatePicker/{DatePicker.vue → DatePicker/DatePicker.vue} +435 -89
  202. package/src/components/DatePicker/DatePicker/tests/DatePicker.events.spec.ts +189 -0
  203. package/src/components/DatePicker/{tests → DatePicker/tests}/DatePicker.spec.ts +0 -14
  204. package/src/components/DatePicker/DatePickerOverview.mdx +227 -0
  205. package/src/components/DatePicker/{DatePickerValidation.mdx → DatePickerValidationExample/DatePickerValidation.mdx} +3 -3
  206. package/src/components/DatePicker/{DatePickerValidation.stories.ts → DatePickerValidationExample/DatePickerValidation.stories.ts} +2 -2
  207. package/src/components/DatePicker/DateTextInput/DateRange.stories.ts +320 -0
  208. package/src/components/DatePicker/DateTextInput/DateTextInput.events.spec.ts +148 -0
  209. package/src/components/DatePicker/DateTextInput/DateTextInput.range.spec.ts +278 -0
  210. package/src/components/DatePicker/{tests → DateTextInput}/DateTextInput.spec.ts +10 -7
  211. package/src/components/DatePicker/DateTextInput/DateTextInput.vue +1111 -0
  212. package/src/components/DatePicker/DateTextInput/NoCalendar.mdx +64 -0
  213. package/src/components/DatePicker/DateTextInput/NoCalendar.stories.ts +782 -0
  214. package/src/components/DatePicker/composables/index.ts +24 -0
  215. package/src/components/DatePicker/composables/tests/useDateAutoClamp.spec.ts +190 -0
  216. package/src/components/DatePicker/composables/tests/useDateFormatValidation.spec.ts +165 -0
  217. package/src/components/DatePicker/composables/tests/useDateInputEditing.spec.ts +359 -0
  218. package/src/components/DatePicker/composables/tests/useDatePickerViewMode.spec.ts +160 -0
  219. package/src/components/DatePicker/composables/tests/useDatePickerVisibility.spec.ts +339 -0
  220. package/src/components/DatePicker/composables/tests/useDateRangeInput.spec.ts +277 -0
  221. package/src/components/DatePicker/composables/tests/useDateRangeValidation.spec.ts +107 -0
  222. package/src/components/DatePicker/composables/tests/useDateSelection.spec.ts +171 -0
  223. package/src/components/DatePicker/composables/tests/useDateValidation.spec.ts +245 -0
  224. package/src/components/DatePicker/composables/tests/useDisplayedDateString.spec.ts +98 -0
  225. package/src/components/DatePicker/composables/tests/useIconState.spec.ts +130 -0
  226. package/src/components/DatePicker/composables/tests/useInputBlurHandler.spec.ts +456 -0
  227. package/src/components/DatePicker/composables/tests/useManualDateValidation.spec.ts +233 -0
  228. package/src/components/DatePicker/composables/tests/useMonthButtonCustomization.spec.ts +125 -0
  229. package/src/components/DatePicker/composables/tests/useTodayButton.spec.ts +97 -0
  230. package/src/components/DatePicker/composables/tests/useWeekendDays.spec.ts +28 -0
  231. package/src/components/DatePicker/composables/useAsteriskDisplay.ts +31 -0
  232. package/src/components/DatePicker/composables/useDateAutoClamp.ts +136 -0
  233. package/src/components/DatePicker/composables/useDateFormatValidation.ts +95 -0
  234. package/src/components/DatePicker/composables/useDateInputEditing.ts +326 -0
  235. package/src/components/DatePicker/composables/useDatePickerViewMode.ts +61 -0
  236. package/src/components/DatePicker/composables/useDatePickerVisibility.ts +146 -0
  237. package/src/components/DatePicker/composables/useDateRangeInput.ts +378 -0
  238. package/src/components/DatePicker/composables/useDateRangeValidation.ts +48 -0
  239. package/src/components/DatePicker/composables/useDateSelection.ts +121 -0
  240. package/src/components/DatePicker/composables/useDateValidation.ts +225 -0
  241. package/src/components/DatePicker/composables/useDisplayedDateString.ts +70 -0
  242. package/src/components/DatePicker/composables/useIconState.ts +53 -0
  243. package/src/components/DatePicker/composables/useInputBlurHandler.ts +165 -0
  244. package/src/components/DatePicker/composables/useInputHandler.ts +436 -0
  245. package/src/components/DatePicker/composables/useManualDateValidation.ts +161 -0
  246. package/src/components/DatePicker/composables/useMonthButtonCustomization.ts +187 -0
  247. package/src/components/DatePicker/composables/useTodayButton.ts +43 -0
  248. package/src/components/DatePicker/composables/useWeekendDays.ts +21 -0
  249. package/src/components/DatePicker/constants/messages.ts +50 -0
  250. package/src/components/DatePicker/docExamples/DatePickerBidirectionalValidation.vue +4 -4
  251. package/src/components/DatePicker/docExamples/DatePickerValidationExamples.vue +1 -1
  252. package/src/components/DatePicker/playground/ComplexDatePickerPlayground.vue +172 -0
  253. package/src/components/DatePicker/types.ts +15 -0
  254. package/src/components/DiacriticPicker/DiacriticPicker.stories.ts +10 -0
  255. package/src/components/DialogBox/AccessibiliteItems.ts +1 -1
  256. package/src/components/DownloadBtn/AccessibiliteItems.ts +1 -1
  257. package/src/components/ErrorPage/Accessibilite.stories.ts +8 -0
  258. package/src/components/ErrorPage/AccessibiliteItems.ts +1 -1
  259. package/src/components/ErrorPage/ErrorPage.vue +12 -6
  260. package/src/components/ErrorPage/tests/__snapshots__/ErrorPage.spec.ts.snap +4 -4
  261. package/src/components/ExternalLinks/AccessibiliteItems.ts +1 -1
  262. package/src/components/FileList/AccessibiliteItems.ts +1 -1
  263. package/src/components/FilePreview/AccessibiliteItems.ts +1 -1
  264. package/src/components/FileUpload/AccessibiliteItems.ts +1 -1
  265. package/src/components/FilterInline/AccessibiliteItems.ts +1 -1
  266. package/src/components/FilterSideBar/AccessibiliteItems.ts +1 -1
  267. package/src/components/FilterSideBar/FilterSideBar.vue +108 -90
  268. package/src/components/FilterSideBar/tests/FilterSideBar.spec.ts +27 -0
  269. package/src/components/FilterSideBar/tests/__snapshots__/FilterSideBar.spec.ts.snap +114 -109
  270. package/src/components/FooterBar/AccessibiliteItems.ts +1 -1
  271. package/src/components/FooterBar/FooterBar.vue +2 -1
  272. package/src/components/FranceConnectBtn/AccessibiliteItems.ts +1 -1
  273. package/src/components/HeaderBar/AccessibiliteItems.ts +1 -1
  274. package/src/components/HeaderBar/HeaderBurgerMenu/AccessibiliteItems.ts +1 -1
  275. package/src/components/HeaderLoading/AccessibiliteItems.ts +1 -1
  276. package/src/components/HeaderToolbar/AccessibiliteItems.ts +1 -1
  277. package/src/components/LangBtn/AccessibiliteItems.ts +1 -1
  278. package/src/components/Logo/Accessibilite.stories.ts +4 -0
  279. package/src/components/Logo/AccessibiliteItems.ts +1 -1
  280. package/src/components/LogoBrandSection/AccessibiliteItems.ts +1 -1
  281. package/src/components/MaintenancePage/AccessibiliteItems.ts +1 -1
  282. package/src/components/NirField/AccessibiliteItems.ts +1 -1
  283. package/src/components/NirField/NirField.mdx +22 -9
  284. package/src/components/NirField/NirField.stories.ts +26 -2
  285. package/src/components/NirField/NirField.vue +209 -22
  286. package/src/components/NirField/nirValidation.ts +17 -3
  287. package/src/components/NirField/tests/NirField.spec.ts +2 -2
  288. package/src/components/NotFoundPage/Accessibilite.stories.ts +8 -0
  289. package/src/components/NotFoundPage/AccessibiliteItems.ts +1 -1
  290. package/src/components/NotFoundPage/NotFoundPage.vue +2 -1
  291. package/src/components/NotFoundPage/tests/__snapshots__/NotFoundPage.spec.ts.snap +8 -6
  292. package/src/components/NotificationBar/AccessibiliteItems.ts +1 -1
  293. package/src/components/PageContainer/AccessibiliteItems.ts +1 -1
  294. package/src/components/PaginatedTable/AccessibiliteItems.ts +1 -1
  295. package/src/components/PaginatedTable/PaginatedTable.mdx +2 -0
  296. package/src/components/PaginatedTable/PaginatedTable.stories.ts +19 -0
  297. package/src/components/PaginatedTable/PaginatedTable.vue +51 -13
  298. package/src/components/PaginatedTable/tests/PaginatedTable.spec.ts +0 -2
  299. package/src/components/PasswordField/AccessibiliteItems.ts +1 -1
  300. package/src/components/PasswordField/PasswordField.stories.ts +4 -0
  301. package/src/components/PasswordField/PasswordField.vue +3 -0
  302. package/src/components/PeriodField/AccessibiliteItems.ts +1 -1
  303. package/src/components/PeriodField/PeriodField.vue +15 -1
  304. package/src/components/PhoneField/AccessibiliteItems.ts +1 -1
  305. package/src/components/PhoneField/PhoneField.stories.ts +15 -15
  306. package/src/components/PhoneField/PhoneField.vue +1 -1
  307. package/src/components/RangeField/AccessibiliteItems.ts +1 -1
  308. package/src/components/RangeField/RangeField.stories.ts +9 -0
  309. package/src/components/RangeField/RangeField.vue +4 -0
  310. package/src/components/RangeField/tests/__snapshots__/RangeField.spec.ts.snap +12 -0
  311. package/src/components/RatingPicker/AccessibiliteItems.ts +1 -1
  312. package/src/components/SearchListField/AccessibiliteItems.ts +1 -1
  313. package/src/components/SearchListField/SearchListField.vue +5 -0
  314. package/src/components/SelectBtnField/AccessibiliteItems.ts +1 -1
  315. package/src/components/SkipLink/AccessibiliteItems.ts +1 -1
  316. package/src/components/SocialMediaLinks/AccessibiliteItems.ts +1 -1
  317. package/src/components/SubHeader/AccessibiliteItems.ts +1 -1
  318. package/src/components/SyAlert/AccessibiliteItems.ts +1 -1
  319. package/src/components/SyTextArea/SyTextArea.vue +3 -0
  320. package/src/components/SyTextArea/tests/SyTextArea.spec.ts +0 -1
  321. package/src/components/TableToolbar/AccessibiliteItems.ts +1 -1
  322. package/src/components/TableToolbar/TableToolbar.stories.ts +110 -56
  323. package/src/components/Tables/SyServerTable/FilterRules.stories.ts +700 -0
  324. package/src/components/Tables/SyServerTable/SyServerTable.mdx +170 -0
  325. package/src/components/Tables/SyServerTable/SyServerTable.stories.ts +4354 -0
  326. package/src/components/Tables/SyServerTable/SyServerTable.vue +391 -0
  327. package/src/components/Tables/SyServerTable/tests/SyServerTable.spec.ts +702 -0
  328. package/src/components/Tables/SyTable/FilterRules.stories.ts +418 -0
  329. package/src/components/Tables/SyTable/SyTable.mdx +139 -0
  330. package/src/components/Tables/SyTable/SyTable.stories.ts +2754 -0
  331. package/src/components/Tables/SyTable/SyTable.vue +397 -0
  332. package/src/components/Tables/SyTable/tests/SyTable.spec.ts +707 -0
  333. package/src/components/Tables/common/SyTableFilter.vue +289 -0
  334. package/src/components/Tables/common/SyTablePagination.vue +375 -0
  335. package/src/components/Tables/common/TableHeader.vue +205 -0
  336. package/src/components/Tables/common/constants/StateEnum.ts +6 -0
  337. package/src/components/Tables/common/filters/DateFilter.vue +140 -0
  338. package/src/components/Tables/common/filters/NumberFilter.vue +234 -0
  339. package/src/components/Tables/common/filters/PeriodFilter.vue +147 -0
  340. package/src/components/Tables/common/filters/SelectFilter.vue +235 -0
  341. package/src/components/Tables/common/filters/TextFilter.vue +191 -0
  342. package/src/components/Tables/common/filters/getFilterComponent.ts +54 -0
  343. package/src/components/Tables/common/filters/locales.ts +4 -0
  344. package/src/components/Tables/common/filters/logics/date.ts +12 -0
  345. package/src/components/Tables/common/filters/logics/number.ts +48 -0
  346. package/src/components/Tables/common/filters/logics/period.ts +25 -0
  347. package/src/components/Tables/common/filters/logics/select.ts +27 -0
  348. package/src/components/Tables/common/filters/logics/tests/TextFilterLogic.spec.ts +177 -0
  349. package/src/components/Tables/common/filters/logics/text.ts +62 -0
  350. package/src/components/Tables/common/filters/tests/DateFilter.spec.ts +187 -0
  351. package/src/components/Tables/common/filters/tests/NumberFilter.spec.ts +280 -0
  352. package/src/components/Tables/common/filters/tests/PeriodFilter.spec.ts +192 -0
  353. package/src/components/Tables/common/filters/tests/SelectFilter.spec.ts +219 -0
  354. package/src/components/Tables/common/filters/tests/TextFilter.spec.ts +260 -0
  355. package/src/components/Tables/common/formatters.ts +72 -0
  356. package/src/components/Tables/common/locales.ts +31 -0
  357. package/src/components/Tables/common/organizeColumns/OrganizeColumns.vue +269 -0
  358. package/src/components/Tables/common/organizeColumns/sortHeaders.ts +9 -0
  359. package/src/components/Tables/common/tableAccessibilityUtils.ts +61 -0
  360. package/src/components/Tables/common/tableFilterUtils.ts +75 -0
  361. package/src/components/Tables/common/tableStorageUtils.ts +127 -0
  362. package/src/components/Tables/common/tableStyles.scss +80 -0
  363. package/src/components/Tables/common/tableUtils.ts +102 -0
  364. package/src/components/Tables/common/tests/SyTableFilter.spec.ts +312 -0
  365. package/src/components/Tables/common/tests/SyTablePagination.spec.ts +170 -0
  366. package/src/components/Tables/common/tests/filterByRange.spec.ts +215 -0
  367. package/src/components/Tables/common/tests/resize.spec.ts +161 -0
  368. package/src/components/Tables/common/tests/tableFilterUtils.spec.ts +92 -0
  369. package/src/components/Tables/common/tests/tableUtils.spec.ts +228 -0
  370. package/src/components/Tables/common/types.ts +92 -0
  371. package/src/components/Tables/common/usePagination.ts +83 -0
  372. package/src/components/Tables/common/useTableCheckbox.ts +58 -0
  373. package/src/components/Tables/common/useTableFilter.ts +19 -0
  374. package/src/components/Tables/common/useTableHeaders.ts +88 -0
  375. package/src/components/Tables/common/useTableItems.ts +87 -0
  376. package/src/components/Tables/common/useTableOptions.ts +93 -0
  377. package/src/components/Tables/index.ts +3 -0
  378. package/src/components/ToolbarContainer/ToolbarContainer.mdx +16 -0
  379. package/src/components/ToolbarContainer/ToolbarContainer.stories.ts +675 -0
  380. package/src/components/ToolbarContainer/ToolbarContainer.vue +128 -0
  381. package/src/components/ToolbarContainer/tests/ToolbarContainer.spec.ts +156 -0
  382. package/src/components/UploadWorkflow/AccessibiliteItems.ts +1 -1
  383. package/src/components/UserMenuBtn/AccessibiliteItems.ts +1 -1
  384. package/src/components/UserMenuBtn/UserMenuBtn.stories.ts +74 -0
  385. package/src/components/UserMenuBtn/UserMenuBtn.vue +19 -17
  386. package/src/components/index.ts +9 -5
  387. package/src/composables/date/useDateFormatDayjs.ts +8 -3
  388. package/src/composables/date/useDateInitializationDayjs.ts +28 -36
  389. package/src/composables/rules/useFieldValidation.ts +1 -2
  390. package/src/designTokens/index.ts +4 -0
  391. package/src/designTokens/tokens/cnam/cnamFonts.ts +140 -0
  392. package/src/designTokens/tokens/pa/paFonts.ts +140 -0
  393. package/src/designTokens/utils/createFontVariables.ts +143 -0
  394. package/src/designTokens/utils/index.ts +2 -1
  395. package/src/stories/Accessibilite/Aculturation/AuditDesignSystem.mdx +293 -20
  396. package/src/stories/Accessibilite/Aculturation/SensibilisationAccessibilite.mdx +448 -54
  397. package/src/stories/Accessibilite/Audit/RGAA.mdx +231 -23
  398. package/src/stories/Accessibilite/Avancement/Avancement.mdx +591 -7
  399. package/src/stories/Accessibilite/Avancement/Avancement.stories.ts +139 -38
  400. package/src/stories/Accessibilite/Introduction.mdx +258 -18
  401. package/src/stories/Accessibilite/KitDePreAudit/Echantillonnage.mdx +221 -31
  402. package/src/stories/Accessibilite/KitDePreAudit/Introduction.mdx +204 -22
  403. package/src/stories/Accessibilite/KitDePreAudit/Outils/Introduction.mdx +537 -24
  404. package/src/stories/Accessibilite/KitDePreAudit/Outils/LecteursDEcran.mdx +577 -70
  405. package/src/stories/Accessibilite/KitDePreAudit/Outils/Tanaguru.mdx +382 -31
  406. package/src/stories/Accessibilite/KitDePreAudit/Preaudit.mdx +419 -81
  407. package/src/stories/Accessibilite/Vuetify/Vuetify.mdx +132 -6
  408. package/src/stories/Accessibilite/Vuetify/Vuetify.stories.ts +370 -146
  409. package/src/stories/Accessibilite/Vuetify/VuetifyItems.ts +35 -57
  410. package/src/stories/Demarrer/Accueil.stories.ts +32 -8
  411. package/src/stories/DesignTokens/StylesTypographiques.mdx +10 -9
  412. package/src/stories/DesignTokens/StylesTypographiques.stories.new.ts +397 -0
  413. package/src/stories/DesignTokens/StylesTypographiques.stories.ts +397 -0
  414. package/src/stories/DesignTokens/TypographyDisplay.vue +155 -0
  415. package/src/stories/DesignTokens/vue-shims.d.ts +6 -0
  416. package/src/stories/GuideDuDev/LesBreackingChanges.mdx +0 -2
  417. package/src/stories/GuideDuDev/MigrationDepuisBridge.mdx +1 -1
  418. package/src/stories/GuideDuDev/MigrationDepuisVue2.mdx +1 -1
  419. package/src/stories/GuideDuDev/PortailAgent.mdx +10 -0
  420. package/src/stories/GuideDuDev/PortailAgent.stories.ts +506 -0
  421. package/src/stories/GuideDuDev/Theme.mdx +41 -0
  422. package/src/utils/rules/isHolidayDay/IsHolidayDay.stories.ts +1 -1
  423. package/src/utils/rules/isRequired/index.ts +2 -1
  424. package/src/vite-env.d.ts +12 -0
  425. package/src/vuetifyConfig.ts +10 -3
  426. package/dist/components/BackToTopBtn/constants/ExpertiseLevelEnum.d.ts +0 -4
  427. package/dist/components/ChipList/constants/ExpertiseLevelEnum.d.ts +0 -4
  428. package/dist/components/CollapsibleList/constants/ExpertiseLevelEnum.d.ts +0 -4
  429. package/dist/components/ContextualMenu/constants/ExpertiseLevelEnum.d.ts +0 -4
  430. package/dist/components/CookieBanner/constants/ExpertiseLevelEnum.d.ts +0 -4
  431. package/dist/components/CopyBtn/constants/ExpertiseLevelEnum.d.ts +0 -4
  432. package/dist/components/Customs/SyBtnSelect/constants/ExpertiseLevelEnum.d.ts +0 -4
  433. package/dist/components/Customs/SyInputSelect/constants/ExpertiseLevelEnum.d.ts +0 -4
  434. package/dist/components/Customs/SySelect/constants/ExpertiseLevelEnum.d.ts +0 -4
  435. package/dist/components/Customs/SyTextField/constants/ExpertiseLevelEnum.d.ts +0 -4
  436. package/dist/components/DataList/constants/ExpertiseLevelEnum.d.ts +0 -4
  437. package/dist/components/DataListGroup/constants/ExpertiseLevelEnum.d.ts +0 -4
  438. package/dist/components/DatePicker/constants/ExpertiseLevelEnum.d.ts +0 -4
  439. package/dist/components/DialogBox/constants/ExpertiseLevelEnum.d.ts +0 -4
  440. package/dist/components/DownloadBtn/constants/ExpertiseLevelEnum.d.ts +0 -4
  441. package/dist/components/ErrorPage/constants/ExpertiseLevelEnum.d.ts +0 -4
  442. package/dist/components/ExternalLinks/constants/ExpertiseLevelEnum.d.ts +0 -4
  443. package/dist/components/FileList/constants/ExpertiseLevelEnum.d.ts +0 -4
  444. package/dist/components/FilePreview/constants/ExpertiseLevelEnum.d.ts +0 -4
  445. package/dist/components/FileUpload/constants/ExpertiseLevelEnum.d.ts +0 -4
  446. package/dist/components/FilterInline/constants/ExpertiseLevelEnum.d.ts +0 -4
  447. package/dist/components/FilterSideBar/constants/ExpertiseLevelEnum.d.ts +0 -4
  448. package/dist/components/FooterBar/constants/ExpertiseLevelEnum.d.ts +0 -4
  449. package/dist/components/FranceConnectBtn/constants/ExpertiseLevelEnum.d.ts +0 -4
  450. package/dist/components/HeaderBar/HeaderBurgerMenu/constants/ExpertiseLevelEnum.d.ts +0 -4
  451. package/dist/components/HeaderBar/constants/ExpertiseLevelEnum.d.ts +0 -4
  452. package/dist/components/HeaderLoading/constants/ExpertiseLevelEnum.d.ts +0 -4
  453. package/dist/components/HeaderToolbar/constants/ExpertiseLevelEnum.d.ts +0 -4
  454. package/dist/components/LangBtn/constants/ExpertiseLevelEnum.d.ts +0 -4
  455. package/dist/components/Logo/constants/ExpertiseLevelEnum.d.ts +0 -4
  456. package/dist/components/LogoBrandSection/constants/ExpertiseLevelEnum.d.ts +0 -4
  457. package/dist/components/MaintenancePage/constants/ExpertiseLevelEnum.d.ts +0 -4
  458. package/dist/components/NirField/constants/ExpertiseLevelEnum.d.ts +0 -4
  459. package/dist/components/NotFoundPage/constants/ExpertiseLevelEnum.d.ts +0 -4
  460. package/dist/components/NotificationBar/constants/ExpertiseLevelEnum.d.ts +0 -4
  461. package/dist/components/PageContainer/constants/ExpertiseLevelEnum.d.ts +0 -4
  462. package/dist/components/PaginatedTable/constants/ExpertiseLevelEnum.d.ts +0 -4
  463. package/dist/components/PasswordField/constants/ExpertiseLevelEnum.d.ts +0 -4
  464. package/dist/components/PeriodField/constants/ExpertiseLevelEnum.d.ts +0 -4
  465. package/dist/components/PhoneField/constants/ExpertiseLevelEnum.d.ts +0 -4
  466. package/dist/components/RangeField/constants/ExpertiseLevelEnum.d.ts +0 -4
  467. package/dist/components/RatingPicker/constants/ExpertiseLevelEnum.d.ts +0 -4
  468. package/dist/components/SearchListField/constants/ExpertiseLevelEnum.d.ts +0 -4
  469. package/dist/components/SelectBtnField/constants/ExpertiseLevelEnum.d.ts +0 -4
  470. package/dist/components/SkipLink/constants/ExpertiseLevelEnum.d.ts +0 -4
  471. package/dist/components/SocialMediaLinks/constants/ExpertiseLevelEnum.d.ts +0 -4
  472. package/dist/components/SubHeader/constants/ExpertiseLevelEnum.d.ts +0 -4
  473. package/dist/components/SyAlert/constants/ExpertiseLevelEnum.d.ts +0 -4
  474. package/dist/components/TableToolbar/constants/ExpertiseLevelEnum.d.ts +0 -4
  475. package/dist/components/UploadWorkflow/constants/ExpertiseLevelEnum.d.ts +0 -4
  476. package/dist/components/UserMenuBtn/constants/ExpertiseLevelEnum.d.ts +0 -4
  477. package/src/components/BackBtn/constants/ExpertiseLevelEnum.ts +0 -4
  478. package/src/components/BackToTopBtn/constants/ExpertiseLevelEnum.ts +0 -4
  479. package/src/components/ChipList/constants/ExpertiseLevelEnum.ts +0 -4
  480. package/src/components/CollapsibleList/constants/ExpertiseLevelEnum.ts +0 -4
  481. package/src/components/ContextualMenu/constants/ExpertiseLevelEnum.ts +0 -4
  482. package/src/components/CookieBanner/constants/ExpertiseLevelEnum.ts +0 -4
  483. package/src/components/CopyBtn/constants/ExpertiseLevelEnum.ts +0 -4
  484. package/src/components/Customs/SyBtnSelect/constants/ExpertiseLevelEnum.ts +0 -4
  485. package/src/components/Customs/SyInputSelect/constants/ExpertiseLevelEnum.ts +0 -4
  486. package/src/components/Customs/SySelect/constants/ExpertiseLevelEnum.ts +0 -4
  487. package/src/components/Customs/SyTextField/constants/ExpertiseLevelEnum.ts +0 -4
  488. package/src/components/DataList/constants/ExpertiseLevelEnum.ts +0 -4
  489. package/src/components/DataListGroup/constants/ExpertiseLevelEnum.ts +0 -4
  490. package/src/components/DatePicker/DatePicker.mdx +0 -222
  491. package/src/components/DatePicker/DateTextInput.vue +0 -504
  492. package/src/components/DatePicker/constants/ExpertiseLevelEnum.ts +0 -4
  493. package/src/components/DialogBox/constants/ExpertiseLevelEnum.ts +0 -4
  494. package/src/components/DownloadBtn/constants/ExpertiseLevelEnum.ts +0 -4
  495. package/src/components/ErrorPage/constants/ExpertiseLevelEnum.ts +0 -4
  496. package/src/components/ExternalLinks/constants/ExpertiseLevelEnum.ts +0 -4
  497. package/src/components/FileList/constants/ExpertiseLevelEnum.ts +0 -4
  498. package/src/components/FilePreview/constants/ExpertiseLevelEnum.ts +0 -4
  499. package/src/components/FileUpload/constants/ExpertiseLevelEnum.ts +0 -4
  500. package/src/components/FilterInline/constants/ExpertiseLevelEnum.ts +0 -4
  501. package/src/components/FilterSideBar/constants/ExpertiseLevelEnum.ts +0 -4
  502. package/src/components/FooterBar/constants/ExpertiseLevelEnum.ts +0 -4
  503. package/src/components/FranceConnectBtn/constants/ExpertiseLevelEnum.ts +0 -4
  504. package/src/components/HeaderBar/HeaderBurgerMenu/constants/ExpertiseLevelEnum.ts +0 -4
  505. package/src/components/HeaderBar/constants/ExpertiseLevelEnum.ts +0 -4
  506. package/src/components/HeaderLoading/constants/ExpertiseLevelEnum.ts +0 -4
  507. package/src/components/HeaderToolbar/constants/ExpertiseLevelEnum.ts +0 -4
  508. package/src/components/LangBtn/constants/ExpertiseLevelEnum.ts +0 -4
  509. package/src/components/Logo/constants/ExpertiseLevelEnum.ts +0 -4
  510. package/src/components/LogoBrandSection/constants/ExpertiseLevelEnum.ts +0 -4
  511. package/src/components/MaintenancePage/constants/ExpertiseLevelEnum.ts +0 -4
  512. package/src/components/NirField/constants/ExpertiseLevelEnum.ts +0 -4
  513. package/src/components/NotFoundPage/constants/ExpertiseLevelEnum.ts +0 -4
  514. package/src/components/NotificationBar/constants/ExpertiseLevelEnum.ts +0 -4
  515. package/src/components/PageContainer/constants/ExpertiseLevelEnum.ts +0 -4
  516. package/src/components/PaginatedTable/constants/ExpertiseLevelEnum.ts +0 -4
  517. package/src/components/PaginatedTable/tests/__snapshots__/PaginatedTable.spec.ts.snap +0 -886
  518. package/src/components/PasswordField/constants/ExpertiseLevelEnum.ts +0 -4
  519. package/src/components/PeriodField/constants/ExpertiseLevelEnum.ts +0 -4
  520. package/src/components/PhoneField/constants/ExpertiseLevelEnum.ts +0 -4
  521. package/src/components/RangeField/constants/ExpertiseLevelEnum.ts +0 -4
  522. package/src/components/RatingPicker/constants/ExpertiseLevelEnum.ts +0 -4
  523. package/src/components/SearchListField/constants/ExpertiseLevelEnum.ts +0 -4
  524. package/src/components/SelectBtnField/constants/ExpertiseLevelEnum.ts +0 -4
  525. package/src/components/SkipLink/constants/ExpertiseLevelEnum.ts +0 -4
  526. package/src/components/SocialMediaLinks/constants/ExpertiseLevelEnum.ts +0 -4
  527. package/src/components/SubHeader/constants/ExpertiseLevelEnum.ts +0 -4
  528. package/src/components/SyAlert/constants/ExpertiseLevelEnum.ts +0 -4
  529. package/src/components/TableToolbar/constants/ExpertiseLevelEnum.ts +0 -4
  530. package/src/components/UploadWorkflow/constants/ExpertiseLevelEnum.ts +0 -4
  531. package/src/components/UserMenuBtn/constants/ExpertiseLevelEnum.ts +0 -4
  532. package/src/stories/DesignTokens/ThemePA.mdx +0 -35
  533. /package/src/components/DatePicker/{examples → playground}/DatePickerHolidayRule.vue +0 -0
@@ -0,0 +1,4354 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import SyServerTable from './SyServerTable.vue'
3
+ import { StateEnum } from '../common/constants/StateEnum'
4
+ import type { DataOptions, FilterType } from '../common/types'
5
+ import { ref, watch } from 'vue'
6
+ import type { VDataTable } from 'vuetify/components'
7
+ import dayjs from 'dayjs'
8
+ import { fn } from '@storybook/test'
9
+
10
+ interface User {
11
+ [key: string]: string
12
+ firstname: string
13
+ lastname: string
14
+ email: string
15
+ }
16
+
17
+ interface DataObj {
18
+ items: User[]
19
+ total: number
20
+ }
21
+
22
+ const meta = {
23
+ title: 'Composants/Tableaux/SyServerTable',
24
+ component: SyServerTable,
25
+ decorators: [
26
+ () => ({
27
+ template: '<div style="padding: 20px;"><story/></div>',
28
+ }),
29
+ ],
30
+ parameters: {
31
+ layout: 'fullscreen',
32
+ },
33
+ argTypes: {
34
+ headers: {
35
+ description: 'Liste des colonnes du tableau (voir : https://vuetifyjs.com/en/api/v-data-table/#props-headers)',
36
+ control: { type: 'object' },
37
+ table: {
38
+ category: 'props',
39
+ },
40
+ },
41
+ items: {
42
+ description: 'Liste des éléments à afficher dans le tableau',
43
+ control: { type: 'object' },
44
+ table: {
45
+ category: 'props',
46
+ defaultValue: {
47
+ summary: '[]',
48
+ },
49
+ },
50
+ },
51
+ density: {
52
+ description: 'Définit la densité du tableau',
53
+ control: { type: 'select' },
54
+ options: ['default', 'comfortable', 'compact'],
55
+ table: {
56
+ category: 'props',
57
+ type: { summary: 'string', detail: `'default' | 'comfortable' | 'compact'` },
58
+ },
59
+ },
60
+ striped: {
61
+ description: 'Affiche les lignes du tableau avec un fond rayé',
62
+ control: { type: 'boolean' },
63
+ table: {
64
+ category: 'props',
65
+ type: { summary: 'boolean' },
66
+ },
67
+ },
68
+ options: {
69
+ description: 'Options de configuration du tableau',
70
+ name: 'v-model:options',
71
+ control: { type: 'object' },
72
+ table: {
73
+ category: 'props',
74
+ type: { summary: 'DataOptions', detail: '{ page: number, itemsPerPage: number, sortBy: SortOptions[], groupBy?: SortOptions[], multiSort?: boolean, mustSort?: boolean, filters?: FilterOption[] }' },
75
+ },
76
+ },
77
+ serverItemsLength: {
78
+ description: 'Nombre total d\'éléments à afficher',
79
+ control: { type: 'number' },
80
+ },
81
+ suffix: {
82
+ description: 'Suffixe permettant de gérer individuellement le stockage des options d\'un tableau d\'une page à l\'autre. Ce prop est obligatoire pour garantir un stockage unique pour chaque tableau.',
83
+ control: { type: 'text' },
84
+ table: {
85
+ category: 'props',
86
+ type: { summary: 'string' },
87
+ },
88
+ required: true,
89
+ },
90
+ caption: {
91
+ description: 'Texte de la légende du tableau',
92
+ control: { type: 'text' },
93
+ },
94
+ resizableColumns: {
95
+ description: 'Permet de redimensionner les colonnes du tableau',
96
+ },
97
+ enableColumnControls: {
98
+ description: 'Allow the users to re-organize the columns',
99
+ table: {
100
+ defaultValue: {
101
+ summary: 'false',
102
+ },
103
+ type: { summary: 'boolean' },
104
+ category: 'props',
105
+ },
106
+ control: { type: 'boolean' },
107
+ },
108
+ showSelect: {
109
+ description: 'Affiche des cases à cocher pour sélectionner des lignes',
110
+ control: { type: 'boolean' },
111
+ table: {
112
+ category: 'props',
113
+ type: { summary: 'boolean' },
114
+ },
115
+ },
116
+ },
117
+ } satisfies Meta<typeof SyServerTable & typeof VDataTable>
118
+
119
+ export default meta
120
+
121
+ type Story = StoryObj<typeof meta>
122
+
123
+ export const Default: Story = {
124
+ parameters: {
125
+ sourceCode: [
126
+ {
127
+ name: 'Template',
128
+ code: `
129
+ <template>
130
+ <SyServerTable
131
+ v-model:options="options"
132
+ :items="users"
133
+ :headers="headers"
134
+ :server-items-length="totalUsers"
135
+ :loading="state === StateEnum.PENDING"
136
+ suffix="server-default"
137
+ @update:options="fetchData"
138
+ />
139
+ </template>
140
+ `,
141
+ },
142
+ {
143
+ name: 'Script',
144
+ code: `
145
+ <script setup lang="ts">
146
+ import { ref, watch } from 'vue'
147
+ import { SyServerTable } from '@cnamts/synapse'
148
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
149
+ import type { DataOptions } from '@cnamts/synapse/src/components/Tables/common/types'
150
+
151
+ interface User {
152
+ [key: string]: string
153
+ firstname: string
154
+ lastname: string
155
+ email: string
156
+ }
157
+
158
+ interface DataObj {
159
+ items: User[]
160
+ total: number
161
+ }
162
+
163
+ const totalUsers = ref(0)
164
+ const users = ref<User[]>([])
165
+ const state = ref(StateEnum.IDLE)
166
+
167
+ const options = ref({
168
+ itemsPerPage: 5,
169
+ sortBy: [{ key: 'lastname', order: 'asc' }],
170
+ page: 1,
171
+ })
172
+
173
+ const headers = [
174
+ { title: 'Nom', key: 'lastname' },
175
+ { title: 'Prénom', key: 'firstname' },
176
+ { title: 'Email', key: 'email' },
177
+ ]
178
+
179
+ const fetchData = async (): Promise<void> => {
180
+ const { items, total } = await getDataFromApi(options.value)
181
+ users.value = items
182
+ totalUsers.value = total
183
+ }
184
+
185
+ const wait = async (ms: number) => {
186
+ return new Promise(resolve => setTimeout(resolve, ms))
187
+ }
188
+
189
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
190
+ state.value = StateEnum.PENDING
191
+ await wait(1000)
192
+
193
+ return new Promise((resolve) => {
194
+ let items: User[] = getUsers()
195
+ const total = items.length
196
+
197
+ if (sortBy && sortBy.length > 0) {
198
+ items = items.sort((a, b) => {
199
+ const key = sortBy[0].key
200
+ const order = sortBy[0].order === 'asc' ? 1 : -1
201
+
202
+ return a[key] > b[key] ? order : -order
203
+ })
204
+ }
205
+
206
+ if (itemsPerPage > 0) {
207
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
208
+ }
209
+
210
+ resolve({ items, total })
211
+ state.value = StateEnum.RESOLVED
212
+ })
213
+ }
214
+
215
+ const getUsers = (): User[] => {
216
+ return [
217
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
218
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
219
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
220
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
221
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
222
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
223
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
224
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
225
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
226
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
227
+ { firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
228
+ { firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
229
+ { firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
230
+ { firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
231
+ { firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
232
+ ]
233
+ }
234
+
235
+ // Initialize data
236
+ fetchData()
237
+ </script>
238
+ `,
239
+ },
240
+ ],
241
+ },
242
+ args: {
243
+ 'options': {
244
+ itemsPerPage: 5,
245
+ sortBy: [{ key: 'lastname', order: 'asc' }],
246
+ page: 1,
247
+ },
248
+ 'headers': [
249
+ { title: 'Nom', key: 'lastname' },
250
+ { title: 'Prénom', key: 'firstname' },
251
+ { title: 'Email', key: 'email' },
252
+ ],
253
+ 'caption': '',
254
+ 'serverItemsLength': 15,
255
+ 'suffix': 'server-default',
256
+ 'density': 'default',
257
+ 'striped': false,
258
+ 'onUpdate:options': fn(),
259
+ },
260
+ render: (args) => {
261
+ return {
262
+ components: { SyServerTable },
263
+ setup() {
264
+ const totalUsers = ref(0)
265
+ const users = ref<User[]>([])
266
+ const state = ref(StateEnum.IDLE)
267
+
268
+ const options = ref({ ...args.options })
269
+
270
+ watch(options, (newVal) => {
271
+ if (args.options) {
272
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
273
+ }
274
+ }, { deep: true })
275
+
276
+ const fetchData = async (): Promise<void> => {
277
+ const { items, total } = await getDataFromApi(options.value as DataOptions)
278
+ users.value = items
279
+ totalUsers.value = total
280
+ }
281
+
282
+ const wait = async (ms: number) => {
283
+ return new Promise(resolve => setTimeout(resolve, ms))
284
+ }
285
+
286
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise<DataObj> => {
287
+ state.value = StateEnum.PENDING
288
+ await wait(1000)
289
+
290
+ return new Promise((resolve) => {
291
+ let items: User[] = getUsers()
292
+ const total = items.length
293
+
294
+ if (sortBy && sortBy.length > 0) {
295
+ items = items.sort((a, b) => {
296
+ const key = sortBy[0].key
297
+ const order = sortBy[0].order === 'asc' ? 1 : -1
298
+
299
+ return a[key] > b[key] ? order : -order
300
+ })
301
+ }
302
+
303
+ if (itemsPerPage > 0) {
304
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
305
+ }
306
+
307
+ resolve({ items, total })
308
+ state.value = StateEnum.RESOLVED
309
+ })
310
+ }
311
+
312
+ const getUsers = (): User[] => {
313
+ return [
314
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
315
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
316
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
317
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
318
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
319
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
320
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
321
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
322
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
323
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
324
+ { firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
325
+ { firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
326
+ { firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
327
+ { firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
328
+ { firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
329
+ ]
330
+ }
331
+
332
+ // Initialize data
333
+ fetchData()
334
+
335
+ return { args, users, state, fetchData, options, totalUsers, StateEnum }
336
+ },
337
+ template: `
338
+ <div>
339
+ <SyServerTable
340
+ v-model:options="options"
341
+ :items="users"
342
+ :server-items-length="totalUsers"
343
+ :loading="state === StateEnum.PENDING"
344
+ v-bind="args"
345
+ @update:options="fetchData"
346
+ />
347
+ </div>
348
+ `,
349
+ }
350
+ },
351
+ }
352
+
353
+ export const ServerSortBy: Story = {
354
+ parameters: {
355
+ sourceCode: [
356
+ {
357
+ name: 'Template',
358
+ code: `
359
+ <template>
360
+ <SyServerTable
361
+ v-model:options="options"
362
+ :items="users"
363
+ :headers="headers"
364
+ :server-items-length="totalUsers"
365
+ :loading="state === StateEnum.PENDING"
366
+ suffix="server-sort"
367
+ @update:options="fetchData"
368
+ />
369
+ </template>
370
+ `,
371
+ },
372
+ {
373
+ name: 'Script',
374
+ code: `
375
+ <script setup lang="ts">
376
+ import { ref } from 'vue'
377
+ import { SyServerTable } from '@cnamts/synapse'
378
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
379
+ import type { DataOptions } from '@cnamts/synapse/src/components/Tables/common/types'
380
+
381
+ interface User {
382
+ [key: string]: string
383
+ firstname: string
384
+ lastname: string
385
+ email: string
386
+ }
387
+
388
+ interface DataObj {
389
+ items: User[]
390
+ total: number
391
+ }
392
+
393
+ const totalUsers = ref(0)
394
+ const users = ref<User[]>([])
395
+ const state = ref(StateEnum.IDLE)
396
+
397
+ const options = ref<DataOptions>({
398
+ itemsPerPage: 5,
399
+ sortBy: [{ key: 'lastname', order: 'desc' }],
400
+ page: 1,
401
+ })
402
+
403
+ const headers = [
404
+ { title: 'Nom', key: 'lastname' },
405
+ { title: 'Prénom', key: 'firstname' },
406
+ { title: 'Email', key: 'email' },
407
+ ]
408
+
409
+ const fetchData = async (): Promise<void> => {
410
+ const { items, total } = await getDataFromApi(options.value)
411
+ users.value = items
412
+ totalUsers.value = total
413
+ }
414
+
415
+ const wait = async (ms: number) => {
416
+ return new Promise(resolve => setTimeout(resolve, ms))
417
+ }
418
+
419
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise<DataObj> => {
420
+ state.value = StateEnum.PENDING
421
+ await wait(1000)
422
+
423
+ return new Promise((resolve) => {
424
+ let items: User[] = getUsers()
425
+ const total = items.length
426
+
427
+ if (sortBy && sortBy.length > 0) {
428
+ items = items.sort((a, b) => {
429
+ const key = sortBy[0].key
430
+ const order = sortBy[0].order === 'asc' ? 1 : -1
431
+
432
+ return a[key] > b[key] ? order : -order
433
+ })
434
+ }
435
+
436
+ if (itemsPerPage > 0) {
437
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
438
+ }
439
+
440
+ resolve({ items, total })
441
+ state.value = StateEnum.RESOLVED
442
+ })
443
+ }
444
+
445
+ const getUsers = (): User[] => {
446
+ return [
447
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
448
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
449
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
450
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
451
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
452
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
453
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
454
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
455
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
456
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
457
+ { firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
458
+ { firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
459
+ { firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
460
+ { firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
461
+ { firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
462
+ ]
463
+ }
464
+
465
+ // Initialize data
466
+ fetchData()
467
+ </script>
468
+ `,
469
+ },
470
+ ],
471
+ },
472
+ args: {
473
+ 'options': {
474
+ itemsPerPage: 5,
475
+ sortBy: [{ key: 'lastname', order: 'desc' }],
476
+ page: 1,
477
+ },
478
+ 'headers': [
479
+ { title: 'Nom', key: 'lastname' },
480
+ { title: 'Prénom', key: 'firstname' },
481
+ { title: 'Email', key: 'email' },
482
+ ],
483
+ 'caption': '',
484
+ 'serverItemsLength': 15,
485
+ 'suffix': 'server-sort',
486
+ 'density': 'default',
487
+ 'striped': false,
488
+ 'onUpdate:options': fn(),
489
+ },
490
+ render: (args) => {
491
+ return {
492
+ components: { SyServerTable },
493
+ setup() {
494
+ const totalUsers = ref(0)
495
+ const users = ref<User[]>([])
496
+ const state = ref(StateEnum.IDLE)
497
+
498
+ const options = ref({ ...args.options })
499
+
500
+ watch(options, (newVal) => {
501
+ if (args.options) {
502
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
503
+ }
504
+ }, { deep: true })
505
+
506
+ const fetchData = async (): Promise<void> => {
507
+ const { items, total } = await getDataFromApi(options.value as DataOptions)
508
+ users.value = items
509
+ totalUsers.value = total
510
+ }
511
+
512
+ const wait = async (ms: number) => {
513
+ return new Promise(resolve => setTimeout(resolve, ms))
514
+ }
515
+
516
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise<DataObj> => {
517
+ state.value = StateEnum.PENDING
518
+ await wait(1000)
519
+
520
+ return new Promise((resolve) => {
521
+ let items: User[] = getUsers()
522
+ const total = items.length
523
+
524
+ if (sortBy && sortBy.length > 0) {
525
+ items = items.sort((a, b) => {
526
+ const key = sortBy[0].key
527
+ const order = sortBy[0].order === 'asc' ? 1 : -1
528
+
529
+ return a[key] > b[key] ? order : -order
530
+ })
531
+ }
532
+
533
+ if (itemsPerPage > 0) {
534
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
535
+ }
536
+
537
+ resolve({ items, total })
538
+ state.value = StateEnum.RESOLVED
539
+ })
540
+ }
541
+
542
+ const getUsers = (): User[] => {
543
+ return [
544
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
545
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
546
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
547
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
548
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
549
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
550
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
551
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
552
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
553
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
554
+ { firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
555
+ { firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
556
+ { firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
557
+ { firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
558
+ { firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
559
+ ]
560
+ }
561
+
562
+ // Initialize data
563
+ fetchData()
564
+
565
+ return { args, users, state, fetchData, options, totalUsers, StateEnum }
566
+ },
567
+ template: `
568
+ <div>
569
+ <SyServerTable
570
+ v-model:options="options"
571
+ :items="users"
572
+ :server-items-length="totalUsers"
573
+ :loading="state === StateEnum.PENDING"
574
+ v-bind="args"
575
+ @update:options="fetchData"
576
+ />
577
+ </div>
578
+ `,
579
+ }
580
+ },
581
+ }
582
+
583
+ export const ServerFilterByText: Story = {
584
+ parameters: {
585
+ sourceCode: [
586
+ {
587
+ name: 'Template',
588
+ code: `
589
+ <template>
590
+ <SyServerTable
591
+ v-model:options="options"
592
+ :items="filteredUsers"
593
+ :headers="headers"
594
+ :server-items-length="totalFilteredUsers"
595
+ :loading="state === StateEnum.PENDING"
596
+ suffix="server-filter-text"
597
+ :show-filters="true"
598
+ @update:options="fetchData"
599
+ />
600
+ </template>
601
+ `,
602
+ },
603
+ {
604
+ name: 'Script',
605
+ code: `
606
+ <script setup lang="ts">
607
+ import { ref } from 'vue'
608
+ import { SyServerTable } from '@cnamts/synapse'
609
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
610
+ import type { DataOptions, FilterOption } from '@cnamts/synapse/src/components/Tables/common/types'
611
+
612
+ interface User {
613
+ firstname: string
614
+ lastname: string
615
+ email: string
616
+ }
617
+
618
+ interface DataObj {
619
+ items: User[]
620
+ total: number
621
+ }
622
+
623
+ const totalFilteredUsers = ref(0)
624
+ const filteredUsers = ref<User[]>([])
625
+ const state = ref(StateEnum.IDLE)
626
+
627
+ const options = ref<DataOptions>({
628
+ itemsPerPage: 5,
629
+ page: 1,
630
+ filters: [],
631
+ })
632
+
633
+ const headers = [
634
+ {
635
+ title: 'Prénom',
636
+ key: 'firstname',
637
+ filterable: true,
638
+ filterType: 'text'
639
+ },
640
+ {
641
+ title: 'Nom',
642
+ key: 'lastname',
643
+ filterable: true,
644
+ filterType: 'text'
645
+ },
646
+ {
647
+ title: 'Email',
648
+ key: 'email',
649
+ filterable: true,
650
+ filterType: 'text'
651
+ }
652
+ ]
653
+
654
+ const fetchData = async (): Promise<void> => {
655
+ const { items, total } = await getDataFromApi(options.value)
656
+ filteredUsers.value = items
657
+ totalFilteredUsers.value = total
658
+ }
659
+
660
+ const wait = async (ms: number) => {
661
+ return new Promise(resolve => setTimeout(resolve, ms))
662
+ }
663
+
664
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
665
+ state.value = StateEnum.PENDING
666
+ await wait(1000)
667
+
668
+ return new Promise((resolve) => {
669
+ // Get all users
670
+ let items: User[] = getUsers()
671
+
672
+ // Apply filters on server side
673
+ if (filters && filters.length > 0) {
674
+ filters.forEach((filter: FilterOption) => {
675
+ const { key, value } = filter
676
+
677
+ items = items.filter(item => {
678
+ const itemValue = item[key as keyof User]
679
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
680
+ })
681
+ })
682
+ }
683
+
684
+ const total = items.length
685
+
686
+ // Apply sorting
687
+ if (sortBy && sortBy.length > 0) {
688
+ items = items.sort((a, b) => {
689
+ const key = sortBy[0].key as keyof User
690
+ const order = sortBy[0].order === 'asc' ? 1 : -1
691
+
692
+ return String(a[key]) > String(b[key]) ? order : -order
693
+ })
694
+ }
695
+
696
+ // Apply pagination
697
+ if (itemsPerPage > 0) {
698
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
699
+ }
700
+
701
+ resolve({ items, total })
702
+ state.value = StateEnum.RESOLVED
703
+ })
704
+ }
705
+
706
+ const getUsers = (): User[] => {
707
+ return [
708
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
709
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
710
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
711
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
712
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
713
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
714
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
715
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
716
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
717
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
718
+ { firstname: 'Martin', lastname: 'Lavoie', email: 'martin.lavoie@example.com' },
719
+ { firstname: 'Céline', lastname: 'Tremblay', email: 'celine.tremblay@example.com' },
720
+ { firstname: 'Jacques', lastname: 'Gagnon', email: 'jacques.gagnon@example.com' },
721
+ { firstname: 'Isabelle', lastname: 'Côté', email: 'isabelle.cote@example.com' },
722
+ { firstname: 'Philippe', lastname: 'Bouchard', email: 'philippe.bouchard@example.com' },
723
+ ]
724
+ }
725
+
726
+ // Initialize data
727
+ fetchData()
728
+ </script>
729
+ `,
730
+ },
731
+ ],
732
+ },
733
+ args: {
734
+ 'serverItemsLength': 15,
735
+ 'headers': [
736
+ {
737
+ title: 'Prénom',
738
+ key: 'firstname',
739
+ filterable: true,
740
+ filterType: 'text',
741
+ },
742
+ {
743
+ title: 'Nom',
744
+ key: 'lastname',
745
+ filterable: true,
746
+ filterType: 'text',
747
+ },
748
+ {
749
+ title: 'Email',
750
+ key: 'email',
751
+ filterable: true,
752
+ filterType: 'text',
753
+ },
754
+ ],
755
+ 'caption': '',
756
+ 'options': {
757
+ itemsPerPage: 5,
758
+ page: 1,
759
+ filters: [],
760
+ },
761
+ 'showFilters': true,
762
+ 'suffix': 'server-filter-text',
763
+ 'density': 'default',
764
+ 'striped': false,
765
+ 'onUpdate:options': fn(),
766
+ },
767
+ render(args) {
768
+ return {
769
+ components: { SyServerTable },
770
+ setup() {
771
+ const options = ref({ ...args.options })
772
+
773
+ watch(options, (newVal) => {
774
+ if (args.options) {
775
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
776
+ }
777
+ }, { deep: true })
778
+
779
+ const totalFilteredUsers = ref(0)
780
+ const filteredUsers = ref<Record<string, unknown>[]>([])
781
+ const state = ref(StateEnum.IDLE)
782
+
783
+ const fetchData = async (): Promise<void> => {
784
+ state.value = StateEnum.PENDING
785
+
786
+ // Simulate API call
787
+ await new Promise(resolve => setTimeout(resolve, 1000))
788
+
789
+ // Get all users
790
+ let items = [
791
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
792
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
793
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
794
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
795
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
796
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
797
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
798
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
799
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
800
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
801
+ { firstname: 'Martin', lastname: 'Lavoie', email: 'martin.lavoie@example.com' },
802
+ { firstname: 'Céline', lastname: 'Tremblay', email: 'celine.tremblay@example.com' },
803
+ { firstname: 'Jacques', lastname: 'Gagnon', email: 'jacques.gagnon@example.com' },
804
+ { firstname: 'Isabelle', lastname: 'Côté', email: 'isabelle.cote@example.com' },
805
+ { firstname: 'Philippe', lastname: 'Bouchard', email: 'philippe.bouchard@example.com' },
806
+ ]
807
+
808
+ // Apply filters on server side
809
+ if (options.value?.filters && options.value.filters.length > 0) {
810
+ options.value.filters.forEach((filter) => {
811
+ const { key, value } = filter
812
+
813
+ items = items.filter((item) => {
814
+ const itemValue = item[key]
815
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
816
+ })
817
+ })
818
+ }
819
+
820
+ const total = items.length
821
+
822
+ // Apply pagination
823
+ const { page = 1, itemsPerPage = 10 } = options.value || {}
824
+ if (itemsPerPage > 0) {
825
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
826
+ }
827
+
828
+ filteredUsers.value = items as Record<string, unknown>[]
829
+ totalFilteredUsers.value = total
830
+ state.value = StateEnum.RESOLVED
831
+ }
832
+
833
+ // Initialize data
834
+ fetchData()
835
+
836
+ return {
837
+ args,
838
+ filteredUsers,
839
+ totalFilteredUsers,
840
+ state,
841
+ options,
842
+ fetchData,
843
+ StateEnum,
844
+ }
845
+ },
846
+ template: `
847
+ <div>
848
+ <SyServerTable
849
+ v-bind="args"
850
+ v-model:options="options"
851
+ :items="filteredUsers"
852
+ :server-items-length="totalFilteredUsers"
853
+ :loading="state === StateEnum.PENDING"
854
+ suffix="server-filter-text"
855
+ @update:options="fetchData"
856
+ />
857
+ </div>
858
+ `,
859
+ }
860
+ },
861
+ }
862
+
863
+ export const ServerFilterByNumber: Story = {
864
+ parameters: {
865
+ sourceCode: [
866
+ {
867
+ name: 'Template',
868
+ code: `
869
+ <template>
870
+ <SyServerTable
871
+ v-model:options="options"
872
+ :items="filteredUsers"
873
+ :headers="headers"
874
+ :server-items-length="totalFilteredUsers"
875
+ :loading="state === StateEnum.PENDING"
876
+ suffix="server-filter-number"
877
+ :show-filters="true"
878
+ @update:options="fetchData"
879
+ />
880
+ </template>
881
+ `,
882
+ },
883
+ {
884
+ name: 'Script',
885
+ code: `
886
+ <script setup lang="ts">
887
+ import { ref } from 'vue'
888
+ import { SyServerTable } from '@cnamts/synapse'
889
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
890
+ import type { DataOptions, FilterOption } from '@cnamts/synapse/src/components/Tables/common/types'
891
+
892
+ interface User {
893
+ name: string
894
+ age: number
895
+ salary: number
896
+ }
897
+
898
+ interface DataObj {
899
+ items: User[]
900
+ total: number
901
+ }
902
+
903
+ const totalFilteredUsers = ref(0)
904
+ const filteredUsers = ref<User[]>([])
905
+ const state = ref(StateEnum.IDLE)
906
+
907
+ const options = ref<DataOptions>({
908
+ itemsPerPage: 5,
909
+ page: 1,
910
+ filters: [],
911
+ })
912
+
913
+ const headers = [
914
+ {
915
+ title: 'Nom',
916
+ key: 'name',
917
+ filterable: true,
918
+ filterType: 'text'
919
+ },
920
+ {
921
+ title: 'Âge',
922
+ key: 'age',
923
+ filterable: true,
924
+ filterType: 'number'
925
+ },
926
+ {
927
+ title: 'Salaire',
928
+ key: 'salary',
929
+ filterable: true,
930
+ filterType: 'number'
931
+ }
932
+ ]
933
+
934
+ const fetchData = async (): Promise<void> => {
935
+ const { items, total } = await getDataFromApi(options.value)
936
+ filteredUsers.value = items
937
+ totalFilteredUsers.value = total
938
+ }
939
+
940
+ const wait = async (ms: number) => {
941
+ return new Promise(resolve => setTimeout(resolve, ms))
942
+ }
943
+
944
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
945
+ state.value = StateEnum.PENDING
946
+ await wait(1000)
947
+
948
+ return new Promise((resolve) => {
949
+ // Get all users
950
+ let items: User[] = getUsers()
951
+
952
+ // Apply filters on server side
953
+ if (filters && filters.length > 0) {
954
+ filters.forEach((filter: FilterOption) => {
955
+ const { key, value, type } = filter
956
+
957
+ items = items.filter(item => {
958
+ const itemValue = item[key as keyof User]
959
+
960
+ if (type === 'number') {
961
+ return Number(itemValue) === Number(value)
962
+ } else {
963
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
964
+ }
965
+ })
966
+ })
967
+ }
968
+
969
+ const total = items.length
970
+
971
+ // Apply sorting
972
+ if (sortBy && sortBy.length > 0) {
973
+ items = items.sort((a, b) => {
974
+ const key = sortBy[0].key as keyof User
975
+ const order = sortBy[0].order === 'asc' ? 1 : -1
976
+
977
+ if (typeof a[key] === 'number' && typeof b[key] === 'number') {
978
+ return (a[key] as number) > (b[key] as number) ? order : -order
979
+ }
980
+
981
+ return String(a[key]) > String(b[key]) ? order : -order
982
+ })
983
+ }
984
+
985
+ // Apply pagination
986
+ if (itemsPerPage > 0) {
987
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
988
+ }
989
+
990
+ resolve({ items, total })
991
+ state.value = StateEnum.RESOLVED
992
+ })
993
+ }
994
+
995
+ const getUsers = (): User[] => {
996
+ return [
997
+ { name: 'Jean Dupont', age: 32, salary: 45000 },
998
+ { name: 'Marie Martin', age: 28, salary: 52000 },
999
+ { name: 'Pierre Durand', age: 45, salary: 65000 },
1000
+ { name: 'Sophie Petit', age: 36, salary: 48000 },
1001
+ { name: 'Thomas Leroy', age: 41, salary: 58000 },
1002
+ { name: 'Julie Bernard', age: 29, salary: 47000 },
1003
+ { name: 'Nicolas Moreau', age: 38, salary: 61000 },
1004
+ { name: 'Camille Dubois', age: 33, salary: 49000 },
1005
+ { name: 'Alexandre Lefebvre', age: 44, salary: 67000 },
1006
+ { name: 'Émilie Girard', age: 31, salary: 51000 },
1007
+ { name: 'Lucas Roux', age: 39, salary: 59000 },
1008
+ { name: 'Chloé Lambert', age: 27, salary: 46000 },
1009
+ { name: 'Maxime Simon', age: 42, salary: 63000 },
1010
+ { name: 'Laura Fournier', age: 35, salary: 54000 },
1011
+ { name: 'Antoine Mercier', age: 40, salary: 60000 },
1012
+ ]
1013
+ }
1014
+
1015
+ // Initialize data
1016
+ fetchData()
1017
+ </script>
1018
+ `,
1019
+ },
1020
+ ],
1021
+ },
1022
+ args: {
1023
+ 'headers': [
1024
+ {
1025
+ title: 'Nom',
1026
+ key: 'name',
1027
+ filterable: true,
1028
+ filterType: 'text',
1029
+ },
1030
+ {
1031
+ title: 'Âge',
1032
+ key: 'age',
1033
+ filterable: true,
1034
+ filterType: 'number',
1035
+ },
1036
+ {
1037
+ title: 'Salaire',
1038
+ key: 'salary',
1039
+ filterable: true,
1040
+ filterType: 'number',
1041
+ },
1042
+ ],
1043
+ 'caption': '',
1044
+ 'options': {
1045
+ itemsPerPage: 5,
1046
+ page: 1,
1047
+ filters: [],
1048
+ },
1049
+ 'serverItemsLength': 15,
1050
+ 'showFilters': true,
1051
+ 'suffix': 'server-filter-number',
1052
+ 'density': 'default',
1053
+ 'striped': false,
1054
+ 'onUpdate:options': fn(),
1055
+ },
1056
+ render(args) {
1057
+ return {
1058
+ components: { SyServerTable },
1059
+ setup() {
1060
+ const options = ref({ ...args.options })
1061
+
1062
+ watch(options, (newVal) => {
1063
+ if (args.options) {
1064
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
1065
+ }
1066
+ }, { deep: true })
1067
+
1068
+ const totalFilteredUsers = ref(0)
1069
+ const filteredUsers = ref<Record<string, unknown>[]>([])
1070
+ const state = ref(StateEnum.IDLE)
1071
+
1072
+ const fetchData = async (): Promise<void> => {
1073
+ state.value = StateEnum.PENDING
1074
+
1075
+ // Simulate API call
1076
+ await new Promise(resolve => setTimeout(resolve, 1000))
1077
+
1078
+ // Get all users
1079
+ let items = [
1080
+ { name: 'Jean Dupont', age: 32, salary: 45000 },
1081
+ { name: 'Marie Martin', age: 28, salary: 52000 },
1082
+ { name: 'Pierre Durand', age: 45, salary: 65000 },
1083
+ { name: 'Sophie Petit', age: 36, salary: 48000 },
1084
+ { name: 'Thomas Leroy', age: 41, salary: 58000 },
1085
+ { name: 'Julie Bernard', age: 29, salary: 47000 },
1086
+ { name: 'Nicolas Moreau', age: 38, salary: 61000 },
1087
+ { name: 'Camille Dubois', age: 33, salary: 49000 },
1088
+ { name: 'Alexandre Lefebvre', age: 44, salary: 67000 },
1089
+ { name: 'Émilie Girard', age: 31, salary: 51000 },
1090
+ { name: 'Lucas Roux', age: 39, salary: 59000 },
1091
+ { name: 'Chloé Lambert', age: 27, salary: 46000 },
1092
+ { name: 'Maxime Simon', age: 42, salary: 63000 },
1093
+ { name: 'Laura Fournier', age: 35, salary: 54000 },
1094
+ { name: 'Antoine Mercier', age: 40, salary: 60000 },
1095
+ ]
1096
+
1097
+ // Apply filters on server side
1098
+ if (options.value?.filters && options.value.filters.length > 0) {
1099
+ options.value.filters.forEach((filter) => {
1100
+ const { key, value, type } = filter
1101
+
1102
+ items = items.filter((item) => {
1103
+ const itemValue = item[key]
1104
+
1105
+ if (type === 'number') {
1106
+ return Number(itemValue) === Number(value)
1107
+ }
1108
+ else {
1109
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
1110
+ }
1111
+ })
1112
+ })
1113
+ }
1114
+
1115
+ const total = items.length
1116
+
1117
+ // Apply pagination
1118
+ const { page = 1, itemsPerPage = 10 } = options.value || {}
1119
+ if (itemsPerPage > 0) {
1120
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
1121
+ }
1122
+
1123
+ filteredUsers.value = items as Record<string, unknown>[]
1124
+ totalFilteredUsers.value = total
1125
+ state.value = StateEnum.RESOLVED
1126
+ }
1127
+
1128
+ // Initialize data
1129
+ fetchData()
1130
+
1131
+ return {
1132
+ args,
1133
+ filteredUsers,
1134
+ totalFilteredUsers,
1135
+ options,
1136
+ state,
1137
+ fetchData,
1138
+ StateEnum,
1139
+ }
1140
+ },
1141
+ template: `
1142
+ <div>
1143
+ <SyServerTable
1144
+ v-bind="args"
1145
+ v-model:options="options"
1146
+ :items="filteredUsers"
1147
+ :server-items-length="totalFilteredUsers"
1148
+ :loading="state === StateEnum.PENDING"
1149
+ @update:options="fetchData"
1150
+ />
1151
+ </div>
1152
+ `,
1153
+ }
1154
+ },
1155
+ }
1156
+
1157
+ export const ServerFilterBySelect: Story = {
1158
+ parameters: {
1159
+ sourceCode: [
1160
+ {
1161
+ name: 'Template',
1162
+ code: `
1163
+ <template>
1164
+ <SyServerTable
1165
+ v-model:options="options"
1166
+ :items="filteredUsers"
1167
+ :headers="headers"
1168
+ :server-items-length="totalFilteredUsers"
1169
+ :loading="state === StateEnum.PENDING"
1170
+ suffix="server-filter-select"
1171
+ :show-filters="true"
1172
+ @update:options="fetchData"
1173
+ />
1174
+ </template>
1175
+ `,
1176
+ },
1177
+ {
1178
+ name: 'Script',
1179
+ code: `
1180
+ <script setup lang="ts">
1181
+ import { ref } from 'vue'
1182
+ import { SyServerTable } from '@cnamts/synapse'
1183
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
1184
+ import type { DataOptions, FilterOption } from '@cnamts/synapse/src/components/Tables/common/types'
1185
+
1186
+ interface User {
1187
+ name: string
1188
+ department: string
1189
+ status: string
1190
+ }
1191
+
1192
+ interface DataObj {
1193
+ items: User[]
1194
+ total: number
1195
+ }
1196
+
1197
+ const totalFilteredUsers = ref(0)
1198
+ const filteredUsers = ref<User[]>([])
1199
+ const state = ref(StateEnum.IDLE)
1200
+
1201
+ const options = ref<DataOptions>({
1202
+ itemsPerPage: 5,
1203
+ page: 1,
1204
+ filters: [],
1205
+ })
1206
+
1207
+ const headers = [
1208
+ {
1209
+ title: 'Nom',
1210
+ key: 'name',
1211
+ filterable: true,
1212
+ filterType: 'text'
1213
+ },
1214
+ {
1215
+ title: 'Département',
1216
+ key: 'department',
1217
+ filterable: true,
1218
+ filterType: 'select',
1219
+ hideMessages: true,
1220
+ filterOptions: [
1221
+ { text: 'RH', value: 'RH' },
1222
+ { text: 'IT', value: 'IT' },
1223
+ { text: 'Finance', value: 'Finance' },
1224
+ { text: 'Marketing', value: 'Marketing' },
1225
+ ]
1226
+ },
1227
+ {
1228
+ title: 'Statut',
1229
+ key: 'status',
1230
+ filterable: true,
1231
+ filterType: 'select',
1232
+ hideMessages: true,
1233
+ filterOptions: [
1234
+ { text: 'Actif', value: 'Actif' },
1235
+ { text: 'En congé', value: 'En congé' },
1236
+ { text: 'Inactif', value: 'Inactif' },
1237
+ ]
1238
+ }
1239
+ ]
1240
+
1241
+ const fetchData = async (): Promise<void> => {
1242
+ const { items, total } = await getDataFromApi(options.value)
1243
+ filteredUsers.value = items
1244
+ totalFilteredUsers.value = total
1245
+ }
1246
+
1247
+ const wait = async (ms: number) => {
1248
+ return new Promise(resolve => setTimeout(resolve, ms))
1249
+ }
1250
+
1251
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
1252
+ state.value = StateEnum.PENDING
1253
+ await wait(1000)
1254
+
1255
+ return new Promise((resolve) => {
1256
+ // Get all users
1257
+ let items: User[] = getUsers()
1258
+
1259
+ // Apply filters on server side
1260
+ if (filters && filters.length > 0) {
1261
+ filters.forEach((filter: FilterOption) => {
1262
+ const { key, value, type } = filter
1263
+
1264
+ items = items.filter(item => {
1265
+ const itemValue = item[key as keyof User]
1266
+
1267
+ if (type === 'select') {
1268
+ return itemValue === value
1269
+ } else {
1270
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
1271
+ }
1272
+ })
1273
+ })
1274
+ }
1275
+
1276
+ const total = items.length
1277
+
1278
+ // Apply sorting
1279
+ if (sortBy && sortBy.length > 0) {
1280
+ items = items.sort((a, b) => {
1281
+ const key = sortBy[0].key as keyof User
1282
+ const order = sortBy[0].order === 'asc' ? 1 : -1
1283
+
1284
+ return String(a[key]) > String(b[key]) ? order : -order
1285
+ })
1286
+ }
1287
+
1288
+ // Apply pagination
1289
+ if (itemsPerPage > 0) {
1290
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
1291
+ }
1292
+
1293
+ resolve({ items, total })
1294
+ state.value = StateEnum.RESOLVED
1295
+ })
1296
+ }
1297
+
1298
+ const getUsers = (): User[] => {
1299
+ return [
1300
+ { name: 'Jean Dupont', department: 'RH', status: 'Actif' },
1301
+ { name: 'Marie Martin', department: 'IT', status: 'En congé' },
1302
+ { name: 'Pierre Durand', department: 'Finance', status: 'Actif' },
1303
+ { name: 'Sophie Petit', department: 'Marketing', status: 'Actif' },
1304
+ { name: 'Thomas Leroy', department: 'IT', status: 'Inactif' },
1305
+ { name: 'Julie Bernard', department: 'RH', status: 'Actif' },
1306
+ { name: 'Nicolas Moreau', department: 'Finance', status: 'En congé' },
1307
+ { name: 'Camille Dubois', department: 'Marketing', status: 'Inactif' },
1308
+ { name: 'Alexandre Lefebvre', department: 'IT', status: 'Actif' },
1309
+ { name: 'Émilie Girard', department: 'RH', status: 'En congé' },
1310
+ { name: 'Lucas Roux', department: 'Finance', status: 'Actif' },
1311
+ { name: 'Chloé Lambert', department: 'Marketing', status: 'Actif' },
1312
+ { name: 'Maxime Simon', department: 'IT', status: 'Inactif' },
1313
+ { name: 'Laura Fournier', department: 'RH', status: 'Actif' },
1314
+ { name: 'Antoine Mercier', department: 'Finance', status: 'En congé' },
1315
+ ]
1316
+ }
1317
+
1318
+ // Initialize data
1319
+ fetchData()
1320
+ </script>
1321
+ `,
1322
+ },
1323
+ ],
1324
+ },
1325
+ args: {
1326
+ 'headers': [
1327
+ {
1328
+ title: 'Nom',
1329
+ key: 'name',
1330
+ filterable: true,
1331
+ filterType: 'text',
1332
+ },
1333
+ {
1334
+ title: 'Département',
1335
+ key: 'department',
1336
+ filterable: true,
1337
+ filterType: 'select',
1338
+ hideMessages: true,
1339
+ filterOptions: [
1340
+ { text: 'RH', value: 'RH' },
1341
+ { text: 'IT', value: 'IT' },
1342
+ { text: 'Finance', value: 'Finance' },
1343
+ { text: 'Marketing', value: 'Marketing' },
1344
+ ],
1345
+ },
1346
+ {
1347
+ title: 'Statut',
1348
+ key: 'status',
1349
+ filterable: true,
1350
+ filterType: 'select',
1351
+ hideMessages: true,
1352
+ filterOptions: [
1353
+ { text: 'Actif', value: 'Actif' },
1354
+ { text: 'En congé', value: 'En congé' },
1355
+ { text: 'Inactif', value: 'Inactif' },
1356
+ ],
1357
+ },
1358
+ ],
1359
+ 'caption': '',
1360
+ 'options': {
1361
+ itemsPerPage: 5,
1362
+ page: 1,
1363
+ filters: [],
1364
+ },
1365
+ 'serverItemsLength': 15,
1366
+ 'showFilters': true,
1367
+ 'suffix': 'server-filter-select',
1368
+ 'density': 'default',
1369
+ 'striped': false,
1370
+ 'onUpdate:options': fn(),
1371
+ },
1372
+ render(args) {
1373
+ return {
1374
+ components: { SyServerTable },
1375
+ setup() {
1376
+ const options = ref({ ...args.options })
1377
+
1378
+ watch(options, (newVal) => {
1379
+ if (args.options) {
1380
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
1381
+ }
1382
+ }, { deep: true })
1383
+
1384
+ const totalFilteredUsers = ref(0)
1385
+ const filteredUsers = ref<Record<string, unknown>[]>([])
1386
+ const state = ref(StateEnum.IDLE)
1387
+
1388
+ const fetchData = async (): Promise<void> => {
1389
+ state.value = StateEnum.PENDING
1390
+
1391
+ // Simulate API call
1392
+ await new Promise(resolve => setTimeout(resolve, 1000))
1393
+
1394
+ // Get all users
1395
+ let items = [
1396
+ { name: 'Jean Dupont', department: 'RH', status: 'Actif' },
1397
+ { name: 'Marie Martin', department: 'IT', status: 'En congé' },
1398
+ { name: 'Pierre Durand', department: 'Finance', status: 'Actif' },
1399
+ { name: 'Sophie Petit', department: 'Marketing', status: 'Actif' },
1400
+ { name: 'Thomas Leroy', department: 'IT', status: 'Inactif' },
1401
+ { name: 'Julie Bernard', department: 'RH', status: 'Actif' },
1402
+ { name: 'Nicolas Moreau', department: 'Finance', status: 'En congé' },
1403
+ { name: 'Camille Dubois', department: 'Marketing', status: 'Inactif' },
1404
+ { name: 'Alexandre Lefebvre', department: 'IT', status: 'Actif' },
1405
+ { name: 'Émilie Girard', department: 'RH', status: 'En congé' },
1406
+ { name: 'Lucas Roux', department: 'Finance', status: 'Actif' },
1407
+ { name: 'Chloé Lambert', department: 'Marketing', status: 'Actif' },
1408
+ { name: 'Maxime Simon', department: 'IT', status: 'Inactif' },
1409
+ { name: 'Laura Fournier', department: 'RH', status: 'Actif' },
1410
+ { name: 'Antoine Mercier', department: 'Finance', status: 'En congé' },
1411
+ ]
1412
+
1413
+ // Apply filters on server side
1414
+ if (options.value?.filters && options.value.filters.length > 0) {
1415
+ options.value.filters.forEach((filter) => {
1416
+ const { key, value, type } = filter
1417
+
1418
+ items = items.filter((item) => {
1419
+ const itemValue = item[key]
1420
+
1421
+ if (type === 'select') {
1422
+ return itemValue === value
1423
+ }
1424
+ else {
1425
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
1426
+ }
1427
+ })
1428
+ })
1429
+ }
1430
+
1431
+ const total = items.length
1432
+
1433
+ // Apply pagination
1434
+ const { page = 1, itemsPerPage = 10 } = options.value || {}
1435
+ if (itemsPerPage > 0) {
1436
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
1437
+ }
1438
+
1439
+ filteredUsers.value = items as Record<string, unknown>[]
1440
+ totalFilteredUsers.value = total
1441
+ state.value = StateEnum.RESOLVED
1442
+ }
1443
+
1444
+ // Initialize data
1445
+ fetchData()
1446
+
1447
+ return {
1448
+ args,
1449
+ filteredUsers,
1450
+ totalFilteredUsers,
1451
+ options,
1452
+ state,
1453
+ fetchData,
1454
+ StateEnum,
1455
+ }
1456
+ },
1457
+ template: `
1458
+ <div>
1459
+ <SyServerTable
1460
+ v-bind="args"
1461
+ v-model:options="options"
1462
+ :items="filteredUsers"
1463
+ :server-items-length="totalFilteredUsers"
1464
+ :loading="state === StateEnum.PENDING"
1465
+ @update:options="fetchData"
1466
+ />
1467
+ </div>
1468
+ `,
1469
+ }
1470
+ },
1471
+ }
1472
+
1473
+ export const ServerFilterBySelectMultiple: Story = {
1474
+ parameters: {
1475
+ sourceCode: [
1476
+ {
1477
+ name: 'Template',
1478
+ code: `
1479
+ <template>
1480
+ <SyServerTable
1481
+ v-model:options="options"
1482
+ :items="filteredUsers"
1483
+ :headers="headers"
1484
+ :server-items-length="totalFilteredUsers"
1485
+ :loading="state === StateEnum.PENDING"
1486
+ suffix="server-filter-select"
1487
+ :show-filters="true"
1488
+ @update:options="fetchData"
1489
+ />
1490
+ </template>
1491
+ `,
1492
+ },
1493
+ {
1494
+ name: 'Script',
1495
+ code: `
1496
+ <script setup lang="ts">
1497
+ import { ref } from 'vue'
1498
+ import { SyServerTable } from '@cnamts/synapse'
1499
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
1500
+ import type { DataOptions, FilterOption } from '@cnamts/synapse/src/components/Tables/common/types'
1501
+
1502
+ interface User {
1503
+ name: string
1504
+ department: string
1505
+ status: string
1506
+ }
1507
+
1508
+ interface DataObj {
1509
+ items: User[]
1510
+ total: number
1511
+ }
1512
+
1513
+ const totalFilteredUsers = ref(0)
1514
+ const filteredUsers = ref<User[]>([])
1515
+ const state = ref(StateEnum.IDLE)
1516
+
1517
+ const options = ref<DataOptions>({
1518
+ itemsPerPage: 5,
1519
+ page: 1,
1520
+ filters: [],
1521
+ })
1522
+
1523
+ const headers = [
1524
+ {
1525
+ title: 'Nom',
1526
+ key: 'name',
1527
+ filterable: true,
1528
+ filterType: 'text'
1529
+ },
1530
+ {
1531
+ title: 'Département',
1532
+ key: 'department',
1533
+ filterable: true,
1534
+ filterType: 'select',
1535
+ multiple: true,
1536
+ chips: true,
1537
+ hideMessages: true,
1538
+ filterOptions: [
1539
+ { text: 'RH', value: 'RH' },
1540
+ { text: 'IT', value: 'IT' },
1541
+ { text: 'Finance', value: 'Finance' },
1542
+ { text: 'Marketing', value: 'Marketing' },
1543
+ ]
1544
+ },
1545
+ {
1546
+ title: 'Statut',
1547
+ key: 'status',
1548
+ filterable: true,
1549
+ filterType: 'select',
1550
+ multiple: true,
1551
+ chips: true,
1552
+ hideMessages: true,
1553
+ filterOptions: [
1554
+ { text: 'Actif', value: 'Actif' },
1555
+ { text: 'En congé', value: 'En congé' },
1556
+ { text: 'Inactif', value: 'Inactif' },
1557
+ ]
1558
+ }
1559
+ ]
1560
+
1561
+ const fetchData = async (): Promise<void> => {
1562
+ const { items, total } = await getDataFromApi(options.value)
1563
+ filteredUsers.value = items
1564
+ totalFilteredUsers.value = total
1565
+ }
1566
+
1567
+ const wait = async (ms: number) => {
1568
+ return new Promise(resolve => setTimeout(resolve, ms))
1569
+ }
1570
+
1571
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
1572
+ state.value = StateEnum.PENDING
1573
+ await wait(1000)
1574
+
1575
+ return new Promise((resolve) => {
1576
+ // Get all users
1577
+ let items: User[] = getUsers()
1578
+
1579
+ // Apply filters on server side
1580
+ if (filters && filters.length > 0) {
1581
+ filters.forEach((filter: FilterOption) => {
1582
+ const { key, value, type } = filter
1583
+
1584
+ items = items.filter(item => {
1585
+ const itemValue = item[key as keyof User]
1586
+
1587
+ if (type === 'select') {
1588
+ return itemValue === value
1589
+ } else {
1590
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
1591
+ }
1592
+ })
1593
+ })
1594
+ }
1595
+
1596
+ const total = items.length
1597
+
1598
+ // Apply sorting
1599
+ if (sortBy && sortBy.length > 0) {
1600
+ items = items.sort((a, b) => {
1601
+ const key = sortBy[0].key as keyof User
1602
+ const order = sortBy[0].order === 'asc' ? 1 : -1
1603
+
1604
+ return String(a[key]) > String(b[key]) ? order : -order
1605
+ })
1606
+ }
1607
+
1608
+ // Apply pagination
1609
+ if (itemsPerPage > 0) {
1610
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
1611
+ }
1612
+
1613
+ resolve({ items, total })
1614
+ state.value = StateEnum.RESOLVED
1615
+ })
1616
+ }
1617
+
1618
+ const getUsers = (): User[] => {
1619
+ return [
1620
+ { name: 'Jean Dupont', department: 'RH', status: 'Actif' },
1621
+ { name: 'Marie Martin', department: 'IT', status: 'En congé' },
1622
+ { name: 'Pierre Durand', department: 'Finance', status: 'Actif' },
1623
+ { name: 'Sophie Petit', department: 'Marketing', status: 'Actif' },
1624
+ { name: 'Thomas Leroy', department: 'IT', status: 'Inactif' },
1625
+ { name: 'Julie Bernard', department: 'RH', status: 'Actif' },
1626
+ { name: 'Nicolas Moreau', department: 'Finance', status: 'En congé' },
1627
+ { name: 'Camille Dubois', department: 'Marketing', status: 'Inactif' },
1628
+ { name: 'Alexandre Lefebvre', department: 'IT', status: 'Actif' },
1629
+ { name: 'Émilie Girard', department: 'RH', status: 'En congé' },
1630
+ { name: 'Lucas Roux', department: 'Finance', status: 'Actif' },
1631
+ { name: 'Chloé Lambert', department: 'Marketing', status: 'Actif' },
1632
+ { name: 'Maxime Simon', department: 'IT', status: 'Inactif' },
1633
+ { name: 'Laura Fournier', department: 'RH', status: 'Actif' },
1634
+ { name: 'Antoine Mercier', department: 'Finance', status: 'En congé' },
1635
+ ]
1636
+ }
1637
+
1638
+ // Initialize data
1639
+ fetchData()
1640
+ </script>
1641
+ `,
1642
+ },
1643
+ ],
1644
+ },
1645
+ args: {
1646
+ 'headers': [
1647
+ {
1648
+ title: 'Nom',
1649
+ key: 'name',
1650
+ filterable: true,
1651
+ filterType: 'text',
1652
+ },
1653
+ {
1654
+ title: 'Département',
1655
+ key: 'department',
1656
+ filterable: true,
1657
+ filterType: 'select',
1658
+ multiple: true,
1659
+ chips: true,
1660
+ hideMessages: true,
1661
+ filterOptions: [
1662
+ { text: 'RH', value: 'RH' },
1663
+ { text: 'IT', value: 'IT' },
1664
+ { text: 'Finance', value: 'Finance' },
1665
+ { text: 'Marketing', value: 'Marketing' },
1666
+ ],
1667
+ },
1668
+ {
1669
+ title: 'Statut',
1670
+ key: 'status',
1671
+ filterable: true,
1672
+ filterType: 'select',
1673
+ multiple: true,
1674
+ chips: true,
1675
+ hideMessages: true,
1676
+ filterOptions: [
1677
+ { text: 'Actif', value: 'Actif' },
1678
+ { text: 'En congé', value: 'En congé' },
1679
+ { text: 'Inactif', value: 'Inactif' },
1680
+ ],
1681
+ },
1682
+ ],
1683
+ 'caption': '',
1684
+ 'options': {
1685
+ itemsPerPage: 5,
1686
+ page: 1,
1687
+ filters: [],
1688
+ },
1689
+ 'serverItemsLength': 15,
1690
+ 'showFilters': true,
1691
+ 'suffix': 'server-filter-select',
1692
+ 'density': 'default',
1693
+ 'striped': false,
1694
+ 'onUpdate:options': fn(),
1695
+ },
1696
+ render(args) {
1697
+ return {
1698
+ components: { SyServerTable },
1699
+ setup() {
1700
+ const options = ref({ ...args.options })
1701
+
1702
+ watch(options, (newVal) => {
1703
+ if (args.options) {
1704
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
1705
+ }
1706
+ }, { deep: true })
1707
+
1708
+ const totalFilteredUsers = ref(0)
1709
+ const filteredUsers = ref<Record<string, unknown>[]>([])
1710
+ const state = ref(StateEnum.IDLE)
1711
+
1712
+ const fetchData = async (): Promise<void> => {
1713
+ state.value = StateEnum.PENDING
1714
+
1715
+ // Simulate API call
1716
+ await new Promise(resolve => setTimeout(resolve, 1000))
1717
+
1718
+ // Get all users
1719
+ let items = [
1720
+ { name: 'Jean Dupont', department: 'RH', status: 'Actif' },
1721
+ { name: 'Marie Martin', department: 'IT', status: 'En congé' },
1722
+ { name: 'Pierre Durand', department: 'Finance', status: 'Actif' },
1723
+ { name: 'Sophie Petit', department: 'Marketing', status: 'Actif' },
1724
+ { name: 'Thomas Leroy', department: 'IT', status: 'Inactif' },
1725
+ { name: 'Julie Bernard', department: 'RH', status: 'Actif' },
1726
+ { name: 'Nicolas Moreau', department: 'Finance', status: 'En congé' },
1727
+ { name: 'Camille Dubois', department: 'Marketing', status: 'Inactif' },
1728
+ { name: 'Alexandre Lefebvre', department: 'IT', status: 'Actif' },
1729
+ { name: 'Émilie Girard', department: 'RH', status: 'En congé' },
1730
+ { name: 'Lucas Roux', department: 'Finance', status: 'Actif' },
1731
+ { name: 'Chloé Lambert', department: 'Marketing', status: 'Actif' },
1732
+ { name: 'Maxime Simon', department: 'IT', status: 'Inactif' },
1733
+ { name: 'Laura Fournier', department: 'RH', status: 'Actif' },
1734
+ { name: 'Antoine Mercier', department: 'Finance', status: 'En congé' },
1735
+ ]
1736
+
1737
+ // Apply filters on server side
1738
+ if (options.value?.filters && options.value.filters.length > 0) {
1739
+ options.value.filters.forEach((filter) => {
1740
+ const { key, value, type } = filter
1741
+
1742
+ items = items.filter((item) => {
1743
+ const itemValue = item[key]
1744
+
1745
+ if (type === 'select') {
1746
+ if (Array.isArray(value)) {
1747
+ // Empty array means no filter applied
1748
+ if (value.length === 0) return true
1749
+ // Check if item value is in the selected values
1750
+ return value.includes(itemValue)
1751
+ }
1752
+ else {
1753
+ return itemValue === value
1754
+ }
1755
+ }
1756
+ else {
1757
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
1758
+ }
1759
+ })
1760
+ })
1761
+ }
1762
+
1763
+ const total = items.length
1764
+
1765
+ // Apply pagination
1766
+ const { page = 1, itemsPerPage = 10 } = options.value || {}
1767
+ if (itemsPerPage > 0) {
1768
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
1769
+ }
1770
+
1771
+ filteredUsers.value = items as Record<string, unknown>[]
1772
+ totalFilteredUsers.value = total
1773
+ state.value = StateEnum.RESOLVED
1774
+ }
1775
+
1776
+ // Initialize data
1777
+ fetchData()
1778
+
1779
+ return {
1780
+ args,
1781
+ filteredUsers,
1782
+ totalFilteredUsers,
1783
+ options,
1784
+ state,
1785
+ fetchData,
1786
+ StateEnum,
1787
+ }
1788
+ },
1789
+ template: `
1790
+ <div>
1791
+ <SyServerTable
1792
+ v-bind="args"
1793
+ v-model:options="options"
1794
+ :items="filteredUsers"
1795
+ :server-items-length="totalFilteredUsers"
1796
+ :loading="state === StateEnum.PENDING"
1797
+ @update:options="fetchData"
1798
+ />
1799
+ </div>
1800
+ `,
1801
+ }
1802
+ },
1803
+ }
1804
+
1805
+ export const ServerFilterByExacteDate: Story = {
1806
+ parameters: {
1807
+ sourceCode: [
1808
+ {
1809
+ name: 'Template',
1810
+ code: `
1811
+ <template>
1812
+ <SyServerTable
1813
+ v-model:options="options"
1814
+ :items="filteredUsers"
1815
+ :headers="headers"
1816
+ :server-items-length="totalFilteredUsers"
1817
+ :loading="state === StateEnum.PENDING"
1818
+ suffix="server-filter-date"
1819
+ :show-filters="true"
1820
+ @update:options="fetchData"
1821
+ />
1822
+ </template>
1823
+ `,
1824
+ },
1825
+ {
1826
+ name: 'Script',
1827
+ code: `
1828
+ <script setup lang="ts">
1829
+ import { ref } from 'vue'
1830
+ import { SyServerTable } from '@cnamts/synapse'
1831
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
1832
+ import type { DataOptions, FilterOption } from '@cnamts/synapse/src/components/Tables/common/types'
1833
+
1834
+ interface User {
1835
+ name: string
1836
+ hireDate: string
1837
+ }
1838
+
1839
+ interface DataObj {
1840
+ items: User[]
1841
+ total: number
1842
+ }
1843
+
1844
+ const totalFilteredUsers = ref(0)
1845
+ const filteredUsers = ref<User[]>([])
1846
+ const state = ref(StateEnum.IDLE)
1847
+
1848
+ const options = ref<DataOptions>({
1849
+ itemsPerPage: 5,
1850
+ page: 1,
1851
+ filters: [],
1852
+ })
1853
+
1854
+ const headers = [
1855
+ {
1856
+ title: 'Nom',
1857
+ key: 'name',
1858
+ filterable: true,
1859
+ filterType: 'text'
1860
+ },
1861
+ {
1862
+ title: 'Date d'embauche',
1863
+ key: 'hireDate',
1864
+ filterable: true,
1865
+ filterType: 'date',
1866
+ dateFormat: 'DD/MM/YYYY'
1867
+ },
1868
+ ]
1869
+
1870
+ const fetchData = async (): Promise<void> => {
1871
+ const { items, total } = await getDataFromApi(options.value)
1872
+ filteredUsers.value = items
1873
+ totalFilteredUsers.value = total
1874
+ }
1875
+
1876
+ const wait = async (ms: number) => {
1877
+ return new Promise(resolve => setTimeout(resolve, ms))
1878
+ }
1879
+
1880
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
1881
+ state.value = StateEnum.PENDING
1882
+ await wait(1000)
1883
+
1884
+ return new Promise((resolve) => {
1885
+ // Get all users
1886
+ let items: User[] = getUsers()
1887
+
1888
+ // Apply filters on server side
1889
+ if (filters && filters.length > 0) {
1890
+ filters.forEach((filter: FilterOption) => {
1891
+ const { key, value, type } = filter
1892
+
1893
+ items = items.filter(item => {
1894
+ const itemValue = item[key as keyof User]
1895
+
1896
+ if (type === 'date') {
1897
+ // Simple date comparison for demo purposes
1898
+ return itemValue === value
1899
+ } else {
1900
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
1901
+ }
1902
+ })
1903
+ })
1904
+ }
1905
+
1906
+ const total = items.length
1907
+
1908
+ // Apply sorting
1909
+ if (sortBy && sortBy.length > 0) {
1910
+ items = items.sort((a, b) => {
1911
+ const key = sortBy[0].key as keyof User
1912
+ const order = sortBy[0].order === 'asc' ? 1 : -1
1913
+
1914
+ return String(a[key]) > String(b[key]) ? order : -order
1915
+ })
1916
+ }
1917
+
1918
+ // Apply pagination
1919
+ if (itemsPerPage > 0) {
1920
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
1921
+ }
1922
+
1923
+ resolve({ items, total })
1924
+ state.value = StateEnum.RESOLVED
1925
+ })
1926
+ })
1927
+ }
1928
+
1929
+ // Update the displayed data
1930
+ users.value = filteredData
1931
+ totalUsers.value = filteredData.length
1932
+
1933
+ state.value = StateEnum.RESOLVED
1934
+ }
1935
+ // Initialize data
1936
+ fetchData()
1937
+ </script>
1938
+ `,
1939
+ },
1940
+ ],
1941
+ },
1942
+ args: {
1943
+ 'serverItemsLength': 5,
1944
+ 'showFilters': true,
1945
+ 'headers': [
1946
+ {
1947
+ title: 'Nom',
1948
+ key: 'name',
1949
+ filterable: true,
1950
+ filterType: 'text',
1951
+ },
1952
+ {
1953
+ title: 'Date d\'embauche',
1954
+ key: 'hireDate',
1955
+ filterable: true,
1956
+ filterType: 'date',
1957
+ dateFormat: 'DD/MM/YYYY',
1958
+ },
1959
+ ],
1960
+ 'options': {
1961
+ itemsPerPage: 5,
1962
+ page: 1,
1963
+ filters: [],
1964
+ },
1965
+ 'caption': '',
1966
+ 'suffix': 'server-filter-date',
1967
+ 'density': 'default',
1968
+ 'striped': false,
1969
+ 'onUpdate:options': fn(),
1970
+ },
1971
+ render(args) {
1972
+ return {
1973
+ components: { SyServerTable },
1974
+ setup() {
1975
+ // Original data that never changes
1976
+ const originalUsers = [
1977
+ {
1978
+ name: 'Jean-Pierre Dubois',
1979
+ hireDate: dayjs('2025-05-15').format('DD/MM/YYYY'),
1980
+ },
1981
+ {
1982
+ name: 'Marie-Claire Lefèvre',
1983
+ hireDate: dayjs('2025-03-10').format('DD/MM/YYYY'),
1984
+ },
1985
+ {
1986
+ name: 'François Moreau',
1987
+ hireDate: dayjs('2025-11-22').format('DD/MM/YYYY'),
1988
+ },
1989
+ {
1990
+ name: 'Céline Rousseau',
1991
+ hireDate: dayjs('2025-01-08').format('DD/MM/YYYY'),
1992
+ },
1993
+ {
1994
+ name: 'Thierry Bertrand',
1995
+ hireDate: dayjs('2025-07-30').format('DD/MM/YYYY'),
1996
+ },
1997
+ ]
1998
+
1999
+ const options = ref({ ...args.options })
2000
+
2001
+ watch(options, (newVal) => {
2002
+ if (args.options) {
2003
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
2004
+ }
2005
+ }, { deep: true })
2006
+
2007
+ const totalUsers = ref(originalUsers.length)
2008
+ const users = ref([...originalUsers])
2009
+ const state = ref(StateEnum.IDLE)
2010
+
2011
+ const fetchData = async () => {
2012
+ state.value = StateEnum.PENDING
2013
+
2014
+ // Simulate API delay
2015
+ await new Promise(resolve => setTimeout(resolve, 1000))
2016
+
2017
+ // Start with original data
2018
+ let filteredData = [...originalUsers]
2019
+
2020
+ // Apply filters if any
2021
+ if (options.value.filters && options.value.filters.length > 0) {
2022
+ filteredData = filteredData.filter((user) => {
2023
+ return options.value.filters!.every((filter) => {
2024
+ const { key, value, type } = filter
2025
+ const itemValue = user[key as keyof typeof user]
2026
+
2027
+ if (!value) return true
2028
+
2029
+ if (type === 'date') {
2030
+ // Simple date comparison for demo purposes
2031
+ return itemValue === value
2032
+ }
2033
+ else if (type === 'period') {
2034
+ const filter = value as { from: string, to: string }
2035
+ const start = new Date(filter.from)
2036
+ const end = new Date(filter.to)
2037
+ const itemDate = new Date(itemValue)
2038
+
2039
+ if (itemDate) {
2040
+ if (end && itemDate < end) {
2041
+ return false
2042
+ }
2043
+ if (start && itemDate > start) {
2044
+ return false
2045
+ }
2046
+ }
2047
+
2048
+ return true
2049
+ }
2050
+ else {
2051
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
2052
+ }
2053
+ })
2054
+ })
2055
+ }
2056
+
2057
+ // Update the displayed data
2058
+ users.value = filteredData
2059
+ totalUsers.value = filteredData.length
2060
+
2061
+ state.value = StateEnum.RESOLVED
2062
+ }
2063
+
2064
+ // Initialize data
2065
+ fetchData()
2066
+
2067
+ return {
2068
+ args,
2069
+ users,
2070
+ totalUsers,
2071
+ options,
2072
+ state,
2073
+ fetchData,
2074
+ StateEnum,
2075
+ }
2076
+ },
2077
+ template: `
2078
+ <div>
2079
+ <SyServerTable
2080
+ v-bind="args"
2081
+ v-model:options="options"
2082
+ :items="users"
2083
+ :server-items-length="totalUsers"
2084
+ :loading="state === StateEnum.PENDING"
2085
+ @update:options="fetchData"
2086
+ />
2087
+ </div>
2088
+ `,
2089
+ }
2090
+ },
2091
+ }
2092
+
2093
+ export const ServerFilterByPeriod: Story = {
2094
+ parameters: {
2095
+ sourceCode: [
2096
+ {
2097
+ name: 'Template',
2098
+ code: `
2099
+ <template>
2100
+ <SyServerTable
2101
+ v-model:options="options"
2102
+ :items="filteredUsers"
2103
+ :headers="headers"
2104
+ :server-items-length="totalFilteredUsers"
2105
+ :loading="state === StateEnum.PENDING"
2106
+ suffix="server-filter-date"
2107
+ :show-filters="true"
2108
+ @update:options="fetchData"
2109
+ />
2110
+ </template>
2111
+ `,
2112
+ },
2113
+ {
2114
+ name: 'Script',
2115
+ code: `
2116
+ <script setup lang="ts">
2117
+ import { ref } from 'vue'
2118
+ import { SyServerTable } from '@cnamts/synapse'
2119
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
2120
+ import type { DataOptions, FilterOption } from '@cnamts/synapse/src/components/Tables/common/types'
2121
+
2122
+ interface User {
2123
+ name: string
2124
+ hireDate: string
2125
+ }
2126
+
2127
+ interface DataObj {
2128
+ items: User[]
2129
+ total: number
2130
+ }
2131
+
2132
+ const totalFilteredUsers = ref(0)
2133
+ const filteredUsers = ref<User[]>([])
2134
+ const state = ref(StateEnum.IDLE)
2135
+
2136
+ const options = ref<DataOptions>({
2137
+ itemsPerPage: 5,
2138
+ page: 1,
2139
+ filters: [],
2140
+ })
2141
+
2142
+ const headers = [
2143
+ {
2144
+ title: 'Nom',
2145
+ key: 'name',
2146
+ filterable: true,
2147
+ filterType: 'text'
2148
+ },
2149
+ {
2150
+ title: 'Date d'embauche',
2151
+ key: 'hireDate',
2152
+ filterable: true,
2153
+ filterType: 'periode',
2154
+ dateFormat: 'DD/MM/YYYY'
2155
+ },
2156
+ ]
2157
+
2158
+ const fetchData = async (): Promise<void> => {
2159
+ const { items, total } = await getDataFromApi(options.value)
2160
+ filteredUsers.value = items
2161
+ totalFilteredUsers.value = total
2162
+ }
2163
+
2164
+ const wait = async (ms: number) => {
2165
+ return new Promise(resolve => setTimeout(resolve, ms))
2166
+ }
2167
+
2168
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
2169
+ state.value = StateEnum.PENDING
2170
+ await wait(1000)
2171
+
2172
+ return new Promise((resolve) => {
2173
+ // Get all users
2174
+ let items: User[] = getUsers()
2175
+
2176
+ // Apply filters on server side
2177
+ if (filters && filters.length > 0) {
2178
+ filters.forEach((filter: FilterOption) => {
2179
+ const { key, value, type } = filter
2180
+
2181
+ items = items.filter(item => {
2182
+ const itemValue = item[key as keyof User]
2183
+
2184
+ if (type === 'period') {
2185
+ function formatDate(date: string): Date | null {
2186
+ if (!date) return null
2187
+ const parsedDate = dayjs(date, 'DD/MM/YYYY')
2188
+ return parsedDate.isValid() ? parsedDate.toDate() : null
2189
+ }
2190
+ const filter = value as { from: string, to: string }
2191
+ const start = formatDate(filter.from)
2192
+ const end = formatDate(filter.to)
2193
+ const itemDate = formatDate(itemValue)
2194
+
2195
+ if (itemDate) {
2196
+ if (end && itemDate > end) {
2197
+ return false
2198
+ }
2199
+ if (start && itemDate < start) {
2200
+ return false
2201
+ }
2202
+ }
2203
+ return true
2204
+ } else {
2205
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
2206
+ }
2207
+ })
2208
+ })
2209
+ }
2210
+
2211
+ const total = items.length
2212
+
2213
+ // Apply sorting
2214
+ if (sortBy && sortBy.length > 0) {
2215
+ items = items.sort((a, b) => {
2216
+ const key = sortBy[0].key as keyof User
2217
+ const order = sortBy[0].order === 'asc' ? 1 : -1
2218
+
2219
+ return String(a[key]) > String(b[key]) ? order : -order
2220
+ })
2221
+ }
2222
+
2223
+ // Apply pagination
2224
+ if (itemsPerPage > 0) {
2225
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
2226
+ }
2227
+
2228
+ resolve({ items, total })
2229
+ state.value = StateEnum.RESOLVED
2230
+ })
2231
+ })
2232
+ }
2233
+
2234
+ // Update the displayed data
2235
+ users.value = filteredData
2236
+ totalUsers.value = filteredData.length
2237
+
2238
+ state.value = StateEnum.RESOLVED
2239
+ }
2240
+ // Initialize data
2241
+ fetchData()
2242
+ </script>
2243
+ `,
2244
+ },
2245
+ ],
2246
+ },
2247
+ args: {
2248
+ 'serverItemsLength': 5,
2249
+ 'showFilters': true,
2250
+ 'headers': [
2251
+ {
2252
+ title: 'Nom',
2253
+ key: 'name',
2254
+ filterable: true,
2255
+ filterType: 'text',
2256
+ },
2257
+ {
2258
+ title: 'Date d\'embauche',
2259
+ key: 'hireDate',
2260
+ filterable: true,
2261
+ filterType: 'period',
2262
+ dateFormat: 'DD/MM/YYYY',
2263
+ },
2264
+ ],
2265
+ 'options': {
2266
+ itemsPerPage: 5,
2267
+ page: 1,
2268
+ filters: [],
2269
+ },
2270
+ 'caption': '',
2271
+ 'suffix': 'server-filter-date',
2272
+ 'density': 'default',
2273
+ 'striped': false,
2274
+ 'onUpdate:options': fn(),
2275
+ },
2276
+ render(args) {
2277
+ return {
2278
+ components: { SyServerTable },
2279
+ setup() {
2280
+ // Original data that never changes
2281
+ const originalUsers = [
2282
+ {
2283
+ name: 'Jean-Pierre Dubois',
2284
+ hireDate: dayjs('2025-05-15').format('DD/MM/YYYY'),
2285
+ },
2286
+ {
2287
+ name: 'Marie-Claire Lefèvre',
2288
+ hireDate: dayjs('2025-03-10').format('DD/MM/YYYY'),
2289
+ },
2290
+ {
2291
+ name: 'François Moreau',
2292
+ hireDate: dayjs('2025-11-22').format('DD/MM/YYYY'),
2293
+ },
2294
+ {
2295
+ name: 'Céline Rousseau',
2296
+ hireDate: dayjs('2025-01-08').format('DD/MM/YYYY'),
2297
+ },
2298
+ {
2299
+ name: 'Thierry Bertrand',
2300
+ hireDate: dayjs('2025-07-30').format('DD/MM/YYYY'),
2301
+ },
2302
+ ]
2303
+
2304
+ const options = ref({ ...args.options })
2305
+
2306
+ watch(options, (newVal) => {
2307
+ if (args.options) {
2308
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
2309
+ }
2310
+ }, { deep: true })
2311
+
2312
+ const totalUsers = ref(originalUsers.length)
2313
+ const users = ref([...originalUsers])
2314
+ const state = ref(StateEnum.IDLE)
2315
+
2316
+ const fetchData = async () => {
2317
+ state.value = StateEnum.PENDING
2318
+
2319
+ // Simulate API delay
2320
+ await new Promise(resolve => setTimeout(resolve, 1000))
2321
+
2322
+ // Start with original data
2323
+ let filteredData = [...originalUsers]
2324
+
2325
+ // Apply filters if any
2326
+ if (options.value.filters && options.value.filters.length > 0) {
2327
+ filteredData = filteredData.filter((user) => {
2328
+ return options.value.filters!.every((filter) => {
2329
+ const { key, value, type } = filter
2330
+ const itemValue = user[key as keyof typeof user]
2331
+
2332
+ if (!value) return true
2333
+
2334
+ else if (type === 'period') {
2335
+ function formatDate(date: string): Date | null {
2336
+ if (!date) return null
2337
+ const parsedDate = dayjs(date, 'DD/MM/YYYY')
2338
+ return parsedDate.isValid() ? parsedDate.toDate() : null
2339
+ }
2340
+ const filter = value as { from: string, to: string }
2341
+ const start = formatDate(filter.from)
2342
+ const end = formatDate(filter.to)
2343
+ const itemDate = formatDate(itemValue)
2344
+
2345
+ if (itemDate) {
2346
+ if (end && itemDate > end) {
2347
+ return false
2348
+ }
2349
+ if (start && itemDate < start) {
2350
+ return false
2351
+ }
2352
+ }
2353
+ return true
2354
+ }
2355
+ else {
2356
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
2357
+ }
2358
+ })
2359
+ })
2360
+ }
2361
+
2362
+ // Update the displayed data
2363
+ users.value = filteredData
2364
+ totalUsers.value = filteredData.length
2365
+
2366
+ state.value = StateEnum.RESOLVED
2367
+ }
2368
+
2369
+ // Initialize data
2370
+ fetchData()
2371
+
2372
+ return {
2373
+ args,
2374
+ users,
2375
+ totalUsers,
2376
+ options,
2377
+ state,
2378
+ fetchData,
2379
+ StateEnum,
2380
+ }
2381
+ },
2382
+ template: `
2383
+ <div>
2384
+ <SyServerTable
2385
+ v-bind="args"
2386
+ v-model:options="options"
2387
+ :items="users"
2388
+ :server-items-length="totalUsers"
2389
+ :loading="state === StateEnum.PENDING"
2390
+ @update:options="fetchData"
2391
+ />
2392
+ </div>
2393
+ `,
2394
+ }
2395
+ },
2396
+ }
2397
+
2398
+ export const CustomFilterSlot: Story = {
2399
+ parameters: {
2400
+ sourceCode: [
2401
+ {
2402
+ name: 'Template',
2403
+ code: `
2404
+ <template>
2405
+ <SyServerTable
2406
+ v-model:options="options"
2407
+ :headers="headers"
2408
+ :items="items"
2409
+ :server-items-length="serverItemsLength"
2410
+ :loading="loading"
2411
+ show-filters
2412
+ suffix="server-custom-filter-slot"
2413
+ @update:options="fetchData"
2414
+ >
2415
+ <template #filter.custom="{ header, value, updateFilter }">
2416
+ <div class="custom-filter-container">
2417
+ <div class="custom-filter-info mb-2">
2418
+ Filtre personnalisé :
2419
+ </div>
2420
+ <v-select
2421
+ v-model="customFilterValue"
2422
+ :items="statusOptions"
2423
+ label="Statut"
2424
+ variant="outlined"
2425
+ density="compact"
2426
+ color="primary"
2427
+ bg-color="white"
2428
+ @update:model-value="(val) => {
2429
+ // Utiliser la fonction updateFilter fournie par le slot
2430
+ updateFilter(val)
2431
+ }"
2432
+ />
2433
+ </div>
2434
+ </template>
2435
+ </SyServerTable>
2436
+ </template>
2437
+ `,
2438
+ },
2439
+ {
2440
+ name: 'Script',
2441
+ code: `
2442
+ <script setup lang="ts">
2443
+ import { ref, watch } from 'vue'
2444
+ import { SyServerTable } from '@cnamts/synapse'
2445
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
2446
+ import type { DataOptions, FilterOption } from '@cnamts/synapse/src/components/Tables/common/types'
2447
+
2448
+ const options = ref<Partial<DataOptions>>({
2449
+ itemsPerPage: 4,
2450
+ filters: []
2451
+ })
2452
+
2453
+ const customFilterValue = ref('')
2454
+ const statusOptions = ['Actif', 'Inactif', 'En attente']
2455
+ const loading = ref(false)
2456
+ const serverItemsLength = ref(6)
2457
+
2458
+ const headers = [
2459
+ {
2460
+ title: 'Nom',
2461
+ key: 'lastname',
2462
+ filterable: true,
2463
+ filterType: 'text'
2464
+ },
2465
+ {
2466
+ title: 'Prénom',
2467
+ key: 'firstname',
2468
+ filterable: true,
2469
+ filterType: 'text'
2470
+ },
2471
+ {
2472
+ title: 'Statut',
2473
+ key: 'status',
2474
+ filterable: true,
2475
+ filterType: 'custom' // Utilisation du type 'custom' pour activer le slot personnalisé
2476
+ },
2477
+ ]
2478
+
2479
+ const allItems = [
2480
+ {
2481
+ firstname: 'Virginie',
2482
+ lastname: 'Beauchesne',
2483
+ status: 'Actif',
2484
+ },
2485
+ {
2486
+ firstname: 'Simone',
2487
+ lastname: 'Bellefeuille',
2488
+ status: 'Inactif',
2489
+ },
2490
+ {
2491
+ firstname: 'Étienne',
2492
+ lastname: 'Salois',
2493
+ status: 'En attente',
2494
+ },
2495
+ {
2496
+ firstname: 'Thierry',
2497
+ lastname: 'Bobu',
2498
+ status: 'Actif',
2499
+ },
2500
+ {
2501
+ firstname: 'Bernadette',
2502
+ lastname: 'Langelier',
2503
+ status: 'Inactif',
2504
+ },
2505
+ {
2506
+ firstname: 'Agate',
2507
+ lastname: 'Roy',
2508
+ status: 'En attente',
2509
+ },
2510
+ ]
2511
+
2512
+ const items = ref(allItems)
2513
+
2514
+ // Fonction pour simuler une requête API avec filtrage côté serveur
2515
+ const fetchData = async () => {
2516
+ loading.value = true
2517
+
2518
+ // Simuler un délai réseau
2519
+ await new Promise(resolve => setTimeout(resolve, 300))
2520
+
2521
+ // Récupérer les filtres
2522
+ const filters = options.value.filters || []
2523
+
2524
+ // Filtrer les éléments côté "serveur"
2525
+ let filteredItems = [...allItems]
2526
+
2527
+ for (const filter of filters) {
2528
+ if (filter.type === 'text') {
2529
+ filteredItems = filteredItems.filter(item =>
2530
+ String(item[filter.key]).toLowerCase().includes(String(filter.value).toLowerCase())
2531
+ )
2532
+ } else if (filter.type === 'select' || filter.type === 'custom') {
2533
+ // Traiter les filtres de type 'select' et 'custom' de la même manière
2534
+ filteredItems = filteredItems.filter(item =>
2535
+ item[filter.key] === filter.value
2536
+ )
2537
+ }
2538
+ }
2539
+
2540
+ // Mettre à jour le nombre total d'éléments
2541
+ serverItemsLength.value = filteredItems.length
2542
+
2543
+ // Appliquer la pagination
2544
+ const page = options.value.page || 1
2545
+ const itemsPerPage = options.value.itemsPerPage || 4
2546
+ const start = (page - 1) * itemsPerPage
2547
+ const end = start + itemsPerPage
2548
+
2549
+ items.value = filteredItems.slice(start, end)
2550
+ loading.value = false
2551
+ }
2552
+
2553
+ // Charger les données initiales
2554
+ fetchData()
2555
+ </script>
2556
+ `,
2557
+ },
2558
+ {
2559
+ name: 'Style',
2560
+ code: `
2561
+ <style scoped>
2562
+ .custom-filter-container {
2563
+ display: flex;
2564
+ flex-direction: column;
2565
+ gap: 4px;
2566
+ }
2567
+
2568
+ .custom-filter-info {
2569
+ font-size: 12px;
2570
+ color: #666;
2571
+ margin-top: 4px;
2572
+ }
2573
+ </style>
2574
+ `,
2575
+ },
2576
+ ],
2577
+ },
2578
+ args: {
2579
+ 'serverItemsLength': 6,
2580
+ 'headers': [
2581
+ {
2582
+ title: 'Nom',
2583
+ key: 'lastname',
2584
+ filterable: true,
2585
+ filterType: 'text',
2586
+ },
2587
+ {
2588
+ title: 'Prénom',
2589
+ key: 'firstname',
2590
+ filterable: true,
2591
+ filterType: 'text',
2592
+ },
2593
+ {
2594
+ title: 'Statut',
2595
+ key: 'status',
2596
+ filterable: true,
2597
+ filterType: 'custom' as FilterType,
2598
+ },
2599
+ ],
2600
+ 'items': [
2601
+ {
2602
+ firstname: 'Virginie',
2603
+ lastname: 'Beauchesne',
2604
+ status: 'Actif',
2605
+ },
2606
+ {
2607
+ firstname: 'Simone',
2608
+ lastname: 'Bellefeuille',
2609
+ status: 'Inactif',
2610
+ },
2611
+ {
2612
+ firstname: 'Étienne',
2613
+ lastname: 'Salois',
2614
+ status: 'En attente',
2615
+ },
2616
+ {
2617
+ firstname: 'Thierry',
2618
+ lastname: 'Bobu',
2619
+ status: 'Actif',
2620
+ },
2621
+ {
2622
+ firstname: 'Bernadette',
2623
+ lastname: 'Langelier',
2624
+ status: 'Inactif',
2625
+ },
2626
+ {
2627
+ firstname: 'Agate',
2628
+ lastname: 'Roy',
2629
+ status: 'En attente',
2630
+ },
2631
+ ],
2632
+ 'caption': '',
2633
+ 'options': {
2634
+ itemsPerPage: 4,
2635
+ page: 1,
2636
+ filters: [],
2637
+ },
2638
+ 'showFilters': true,
2639
+ 'suffix': 'server-custom-filter-slot',
2640
+ 'density': 'default',
2641
+ 'striped': false,
2642
+ 'onUpdate:options': fn(),
2643
+ },
2644
+ render(args) {
2645
+ return {
2646
+ components: { SyServerTable },
2647
+ setup() {
2648
+ // Create reactive references
2649
+ const options = ref({ ...args.options })
2650
+
2651
+ watch(options, (newVal) => {
2652
+ if (args.options) {
2653
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
2654
+ }
2655
+ }, { deep: true })
2656
+
2657
+ const items = ref(args.items)
2658
+ const customFilterValue = ref('')
2659
+ const statusOptions = ['Actif', 'Inactif', 'En attente']
2660
+ const loading = ref(false)
2661
+ const serverItemsLength = ref(args.serverItemsLength)
2662
+
2663
+ // Fonction pour simuler une requête API avec filtrage côté serveur
2664
+ const fetchData = async () => {
2665
+ loading.value = true
2666
+
2667
+ // Simuler un délai réseau
2668
+ await new Promise(resolve => setTimeout(resolve, 300))
2669
+
2670
+ // Récupérer les filtres
2671
+ const filters = options.value?.filters || []
2672
+
2673
+ // Filtrer les éléments côté "serveur"
2674
+ let filteredItems = [...(args.items || [])]
2675
+
2676
+ for (const filter of filters) {
2677
+ if (filter.type === 'text') {
2678
+ filteredItems = filteredItems.filter(item =>
2679
+ String(item[filter.key]).toLowerCase().includes(String(filter.value).toLowerCase()),
2680
+ )
2681
+ }
2682
+ else if (filter.type === 'select' || filter.type === 'custom') {
2683
+ // Traiter les filtres de type 'select' et 'custom' de la même manière
2684
+ filteredItems = filteredItems.filter(item =>
2685
+ item[filter.key] === filter.value,
2686
+ )
2687
+ }
2688
+ }
2689
+
2690
+ // Mettre à jour le nombre total d'éléments
2691
+ serverItemsLength.value = filteredItems.length
2692
+
2693
+ // Appliquer la pagination
2694
+ const page = options.value?.page || 1
2695
+ const itemsPerPage = options.value?.itemsPerPage || 4
2696
+ const start = (page - 1) * itemsPerPage
2697
+ const end = start + itemsPerPage
2698
+
2699
+ items.value = filteredItems.slice(start, end)
2700
+ loading.value = false
2701
+ }
2702
+
2703
+ function handleFilterChange(val) {
2704
+ // Ensure options.value.filters is initialized
2705
+ if (!options.value.filters) {
2706
+ options.value.filters = []
2707
+ }
2708
+
2709
+ // Create a new filters array with proper typing
2710
+ const currentFilters = options.value.filters as import('../common/types').FilterOption[]
2711
+ const newFilters = [...currentFilters].filter(f => f.key !== 'status')
2712
+
2713
+ // Add the new filter if a value is selected
2714
+ if (val) {
2715
+ newFilters.push({
2716
+ key: 'status',
2717
+ value: val,
2718
+ type: 'select' as FilterType, // Use 'select' type for compatibility with filtering logic
2719
+ })
2720
+ }
2721
+
2722
+ // Update the options with the new filters
2723
+ options.value = {
2724
+ ...options.value,
2725
+ filters: newFilters,
2726
+ }
2727
+ }
2728
+
2729
+ // Initialize data
2730
+ fetchData()
2731
+
2732
+ return {
2733
+ args,
2734
+ options,
2735
+ items,
2736
+ customFilterValue,
2737
+ statusOptions,
2738
+ loading,
2739
+ serverItemsLength,
2740
+ handleFilterChange,
2741
+ fetchData,
2742
+ }
2743
+ },
2744
+ template: `
2745
+ <SyServerTable
2746
+ v-bind="args"
2747
+ v-model:options="options"
2748
+ :items="items"
2749
+ :server-items-length="serverItemsLength"
2750
+ :loading="loading"
2751
+ @update:options="fetchData"
2752
+ >
2753
+ <template #filter.custom="{ header, value, updateFilter }">
2754
+ <div class="custom-filter-container">
2755
+ <div class="custom-filter-info mb-2">
2756
+ Filtre personnalisé :
2757
+ </div>
2758
+ <v-select
2759
+ v-model="customFilterValue"
2760
+ :items="statusOptions"
2761
+ label="Statut"
2762
+ variant="outlined"
2763
+ density="compact"
2764
+ color="primary"
2765
+ bg-color="white"
2766
+ @update:model-value="(val) => {
2767
+ // Use updateFilter provided by the slot props
2768
+ updateFilter(val);
2769
+ // Also update our local state
2770
+ handleFilterChange(val);
2771
+ }"
2772
+ />
2773
+ </div>
2774
+ </template>
2775
+ </SyServerTable>
2776
+ `,
2777
+ }
2778
+ },
2779
+ }
2780
+
2781
+ export const CustomFilterInputs: Story = {
2782
+ parameters: {
2783
+ sourceCode: [
2784
+ {
2785
+ name: 'Template',
2786
+ code: `
2787
+ <template>
2788
+ <SyServerTable
2789
+ v-model:options="options"
2790
+ :items="filteredUsers"
2791
+ :headers="headers"
2792
+ :server-items-length="totalFilteredUsers"
2793
+ :filter-input-config="filterInputConfig"
2794
+ :loading="state === StateEnum.PENDING"
2795
+ suffix="server-filter-text"
2796
+ :show-filters="true"
2797
+ @update:options="fetchData"
2798
+ />
2799
+ </template>
2800
+ `,
2801
+ },
2802
+ {
2803
+ name: 'Script',
2804
+ code: `
2805
+ <script setup lang="ts">
2806
+ import { ref } from 'vue'
2807
+ import { SyServerTable } from '@cnamts/synapse'
2808
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
2809
+ import type { DataOptions, FilterOption } from '@cnamts/synapse/src/components/Tables/common/types'
2810
+
2811
+ interface User {
2812
+ firstname: string
2813
+ lastname: string
2814
+ email: string
2815
+ }
2816
+
2817
+ interface DataObj {
2818
+ items: User[]
2819
+ total: number
2820
+ }
2821
+
2822
+ const totalFilteredUsers = ref(0)
2823
+ const filteredUsers = ref<User[]>([])
2824
+ const state = ref(StateEnum.IDLE)
2825
+
2826
+ const options = ref<DataOptions>({
2827
+ itemsPerPage: 5,
2828
+ page: 1,
2829
+ filters: [],
2830
+ })
2831
+
2832
+ const headers = [
2833
+ {
2834
+ title: 'Prénom',
2835
+ key: 'firstname',
2836
+ filterable: true,
2837
+ filterType: 'text'
2838
+ },
2839
+ {
2840
+ title: 'Nom',
2841
+ key: 'lastname',
2842
+ filterable: true,
2843
+ filterType: 'text'
2844
+ },
2845
+ {
2846
+ title: 'Email',
2847
+ key: 'email',
2848
+ filterable: true,
2849
+ filterType: 'text'
2850
+ }
2851
+ ]
2852
+
2853
+ const filterInputConfig = {
2854
+ filterInputConfig: {
2855
+ variant: 'outlined',
2856
+ density: 'comfortable',
2857
+ hideDetails: true,
2858
+ clearable: false,
2859
+ disableErrorHandling: true,
2860
+ },
2861
+ }
2862
+
2863
+ const fetchData = async (): Promise<void> => {
2864
+ const { items, total } = await getDataFromApi(options.value)
2865
+ filteredUsers.value = items
2866
+ totalFilteredUsers.value = total
2867
+ }
2868
+
2869
+ const wait = async (ms: number) => {
2870
+ return new Promise(resolve => setTimeout(resolve, ms))
2871
+ }
2872
+
2873
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
2874
+ state.value = StateEnum.PENDING
2875
+ await wait(1000)
2876
+
2877
+ return new Promise((resolve) => {
2878
+ // Get all users
2879
+ let items: User[] = getUsers()
2880
+
2881
+ // Apply filters on server side
2882
+ if (filters && filters.length > 0) {
2883
+ filters.forEach((filter: FilterOption) => {
2884
+ const { key, value } = filter
2885
+
2886
+ items = items.filter(item => {
2887
+ const itemValue = item[key as keyof User]
2888
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
2889
+ })
2890
+ })
2891
+ }
2892
+
2893
+ const total = items.length
2894
+
2895
+ // Apply sorting
2896
+ if (sortBy && sortBy.length > 0) {
2897
+ items = items.sort((a, b) => {
2898
+ const key = sortBy[0].key as keyof User
2899
+ const order = sortBy[0].order === 'asc' ? 1 : -1
2900
+
2901
+ return String(a[key]) > String(b[key]) ? order : -order
2902
+ })
2903
+ }
2904
+
2905
+ // Apply pagination
2906
+ if (itemsPerPage > 0) {
2907
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
2908
+ }
2909
+
2910
+ resolve({ items, total })
2911
+ state.value = StateEnum.RESOLVED
2912
+ })
2913
+ }
2914
+
2915
+ const getUsers = (): User[] => {
2916
+ return [
2917
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
2918
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
2919
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
2920
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
2921
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
2922
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
2923
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
2924
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
2925
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
2926
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
2927
+ { firstname: 'Martin', lastname: 'Lavoie', email: 'martin.lavoie@example.com' },
2928
+ { firstname: 'Céline', lastname: 'Tremblay', email: 'celine.tremblay@example.com' },
2929
+ { firstname: 'Jacques', lastname: 'Gagnon', email: 'jacques.gagnon@example.com' },
2930
+ { firstname: 'Isabelle', lastname: 'Côté', email: 'isabelle.cote@example.com' },
2931
+ { firstname: 'Philippe', lastname: 'Bouchard', email: 'philippe.bouchard@example.com' },
2932
+ ]
2933
+ }
2934
+
2935
+ // Initialize data
2936
+ fetchData()
2937
+ </script>
2938
+ `,
2939
+ },
2940
+ ],
2941
+ },
2942
+ args: {
2943
+ 'serverItemsLength': 15,
2944
+ 'headers': [
2945
+ {
2946
+ title: 'Prénom',
2947
+ key: 'firstname',
2948
+ filterable: true,
2949
+ filterType: 'text',
2950
+ },
2951
+ {
2952
+ title: 'Nom',
2953
+ key: 'lastname',
2954
+ filterable: true,
2955
+ filterType: 'text',
2956
+ },
2957
+ {
2958
+ title: 'Email',
2959
+ key: 'email',
2960
+ filterable: true,
2961
+ filterType: 'text',
2962
+ },
2963
+ ],
2964
+ 'caption': '',
2965
+ 'options': {
2966
+ itemsPerPage: 5,
2967
+ page: 1,
2968
+ filters: [],
2969
+ },
2970
+ 'filterInputConfig': {
2971
+ variant: 'outlined',
2972
+ density: 'comfortable',
2973
+ hideDetails: true,
2974
+ clearable: false,
2975
+ disableErrorHandling: true,
2976
+ },
2977
+ 'showFilters': true,
2978
+ 'suffix': 'server-filter-text',
2979
+ 'density': 'default',
2980
+ 'striped': false,
2981
+ 'onUpdate:options': fn(),
2982
+ },
2983
+ render(args) {
2984
+ return {
2985
+ components: { SyServerTable },
2986
+ setup() {
2987
+ const options = ref({ ...args.options })
2988
+
2989
+ watch(options, (newVal) => {
2990
+ if (args.options) {
2991
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
2992
+ }
2993
+ }, { deep: true })
2994
+
2995
+ const totalFilteredUsers = ref(0)
2996
+ const filteredUsers = ref<Record<string, unknown>[]>([])
2997
+ const state = ref(StateEnum.IDLE)
2998
+
2999
+ const fetchData = async (): Promise<void> => {
3000
+ state.value = StateEnum.PENDING
3001
+
3002
+ // Simulate API call
3003
+ await new Promise(resolve => setTimeout(resolve, 1000))
3004
+
3005
+ // Get all users
3006
+ let items = [
3007
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
3008
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
3009
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
3010
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
3011
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
3012
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
3013
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
3014
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
3015
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
3016
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
3017
+ { firstname: 'Martin', lastname: 'Lavoie', email: 'martin.lavoie@example.com' },
3018
+ { firstname: 'Céline', lastname: 'Tremblay', email: 'celine.tremblay@example.com' },
3019
+ { firstname: 'Jacques', lastname: 'Gagnon', email: 'jacques.gagnon@example.com' },
3020
+ { firstname: 'Isabelle', lastname: 'Côté', email: 'isabelle.cote@example.com' },
3021
+ { firstname: 'Philippe', lastname: 'Bouchard', email: 'philippe.bouchard@example.com' },
3022
+ ]
3023
+
3024
+ // Apply filters on server side
3025
+ if (options.value?.filters && options.value.filters.length > 0) {
3026
+ options.value.filters.forEach((filter) => {
3027
+ const { key, value } = filter
3028
+
3029
+ items = items.filter((item) => {
3030
+ const itemValue = item[key]
3031
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
3032
+ })
3033
+ })
3034
+ }
3035
+
3036
+ const total = items.length
3037
+
3038
+ // Apply pagination
3039
+ const { page = 1, itemsPerPage = 10 } = options.value || {}
3040
+ if (itemsPerPage > 0) {
3041
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
3042
+ }
3043
+
3044
+ filteredUsers.value = items as Record<string, unknown>[]
3045
+ totalFilteredUsers.value = total
3046
+ state.value = StateEnum.RESOLVED
3047
+ }
3048
+
3049
+ // Initialize data
3050
+ fetchData()
3051
+
3052
+ return {
3053
+ args,
3054
+ filteredUsers,
3055
+ totalFilteredUsers,
3056
+ options,
3057
+ state,
3058
+ fetchData,
3059
+ StateEnum,
3060
+ }
3061
+ },
3062
+ template: `
3063
+ <div>
3064
+ <SyServerTable
3065
+ v-bind="args"
3066
+ v-model:options="options"
3067
+ :items="filteredUsers"
3068
+ :server-items-length="totalFilteredUsers"
3069
+ :loading="state === StateEnum.PENDING"
3070
+ @update:options="fetchData"
3071
+ />
3072
+ </div>
3073
+ `,
3074
+ }
3075
+ },
3076
+ }
3077
+
3078
+ export const ManyServerTables: Story = {
3079
+ parameters: {
3080
+ sourceCode: [
3081
+ {
3082
+ name: 'Template',
3083
+ code: `
3084
+ <template>
3085
+ <div>
3086
+ <SyServerTable
3087
+ v-model:options="optionsTable1"
3088
+ :items="usersTable1"
3089
+ :headers="headers"
3090
+ :server-items-length="totalUsersTable1"
3091
+ :loading="stateTable1 === StateEnum.PENDING"
3092
+ suffix="table1"
3093
+ @update:options="fetchDataTable1"
3094
+ />
3095
+ <SyServerTable
3096
+ v-model:options="optionsTable2"
3097
+ :items="usersTable2"
3098
+ :headers="headers"
3099
+ :server-items-length="totalUsersTable2"
3100
+ :loading="stateTable2 === StateEnum.PENDING"
3101
+ suffix="table2"
3102
+ @update:options="fetchDataTable2"
3103
+ />
3104
+ </div>
3105
+ </template>
3106
+ `,
3107
+ },
3108
+ {
3109
+ name: 'Script',
3110
+ code: `
3111
+ <script setup lang="ts">
3112
+ import { ref } from 'vue'
3113
+ import { SyServerTable } from '@cnamts/synapse'
3114
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
3115
+ import type { DataOptions } from '@cnamts/synapse/src/components/Tables/common/types'
3116
+
3117
+ interface User {
3118
+ [key: string]: string
3119
+ firstname: string
3120
+ lastname: string
3121
+ email: string
3122
+ }
3123
+
3124
+ interface DataObj {
3125
+ items: User[]
3126
+ total: number
3127
+ }
3128
+
3129
+ const headers = [
3130
+ { title: 'Nom', key: 'lastname' },
3131
+ { title: 'Prénom', key: 'firstname' },
3132
+ { title: 'Email', key: 'email' },
3133
+ ]
3134
+
3135
+ // Table 1
3136
+ const totalUsersTable1 = ref(0)
3137
+ const usersTable1 = ref<User[]>([])
3138
+ const stateTable1 = ref(StateEnum.IDLE)
3139
+ const optionsTable1 = ref<DataOptions>({
3140
+ itemsPerPage: 5,
3141
+ sortBy: [{ key: 'lastname', order: 'asc' }],
3142
+ page: 1,
3143
+ })
3144
+
3145
+ const fetchDataTable1 = async (options?: DataOptions): Promise<void> => {
3146
+ const optionsToUse = options || optionsTable1.value
3147
+ const { items, total } = await getDataFromApi(optionsToUse)
3148
+ usersTable1.value = items
3149
+ totalUsersTable1.value = total
3150
+ }
3151
+
3152
+ // Table 2
3153
+ const totalUsersTable2 = ref(0)
3154
+ const usersTable2 = ref<User[]>([])
3155
+ const stateTable2 = ref(StateEnum.IDLE)
3156
+ const optionsTable2 = ref<DataOptions>({
3157
+ itemsPerPage: 3,
3158
+ sortBy: [{ key: 'firstname', order: 'asc' }],
3159
+ page: 1,
3160
+ })
3161
+
3162
+ const fetchDataTable2 = async (options?: DataOptions): Promise<void> => {
3163
+ const optionsToUse = options || optionsTable2.value
3164
+ const { items, total } = await getDataFromApi(optionsToUse)
3165
+ usersTable2.value = items
3166
+ totalUsersTable2.value = total
3167
+ }
3168
+
3169
+ const wait = async (ms: number) => {
3170
+ return new Promise(resolve => setTimeout(resolve, ms))
3171
+ }
3172
+
3173
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise<DataObj> => {
3174
+ const state = sortBy[0].key === 'lastname' ? stateTable1 : stateTable2
3175
+ state.value = StateEnum.PENDING
3176
+ await wait(1000)
3177
+
3178
+ return new Promise((resolve) => {
3179
+ let items: User[] = getUsers()
3180
+ const total = items.length
3181
+
3182
+ if (sortBy && sortBy.length > 0) {
3183
+ items = items.sort((a, b) => {
3184
+ const key = sortBy[0].key
3185
+ const order = sortBy[0].order === 'asc' ? 1 : -1
3186
+
3187
+ return a[key] > b[key] ? order : -order
3188
+ })
3189
+ }
3190
+
3191
+ if (itemsPerPage > 0) {
3192
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
3193
+ }
3194
+
3195
+ resolve({ items, total })
3196
+ state.value = StateEnum.RESOLVED
3197
+ })
3198
+ }
3199
+
3200
+ const getUsers = (): User[] => {
3201
+ return [
3202
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
3203
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
3204
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
3205
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
3206
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
3207
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
3208
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
3209
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
3210
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
3211
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
3212
+ ]
3213
+ }
3214
+
3215
+ fetchDataTable1()
3216
+ fetchDataTable2()
3217
+ </script>
3218
+ `,
3219
+ },
3220
+ ],
3221
+ },
3222
+ args: {
3223
+ 'serverItemsLength': 15, // Add required serverItemsLength property
3224
+ 'headers': [
3225
+ { title: 'Nom', key: 'lastname' },
3226
+ { title: 'Prénom', key: 'firstname' },
3227
+ { title: 'Email', key: 'email' },
3228
+ ],
3229
+ 'caption': '',
3230
+ 'suffix': 'multi',
3231
+ 'density': 'default',
3232
+ 'striped': false,
3233
+ 'onUpdate:options': fn(),
3234
+ },
3235
+ render: (args) => {
3236
+ return {
3237
+ components: { SyServerTable },
3238
+ setup() {
3239
+ // Table 1
3240
+ const totalUsersTable1 = ref(0)
3241
+ const usersTable1 = ref<User[]>([])
3242
+ const stateTable1 = ref(StateEnum.IDLE)
3243
+
3244
+ const optionsTable1 = ref<Partial<DataOptions>>({
3245
+ itemsPerPage: 5,
3246
+ sortBy: [{ key: 'lastname', order: 'asc' }],
3247
+ page: 1,
3248
+ })
3249
+
3250
+ const fetchDataTable1 = async (options?: DataOptions): Promise<void> => {
3251
+ const optionsToUse = options || optionsTable1.value as DataOptions
3252
+ const { items, total } = await getDataFromApi(optionsToUse)
3253
+ usersTable1.value = items
3254
+ totalUsersTable1.value = total
3255
+ }
3256
+
3257
+ // Table 2
3258
+ const totalUsersTable2 = ref(0)
3259
+ const usersTable2 = ref<User[]>([])
3260
+ const stateTable2 = ref(StateEnum.IDLE)
3261
+
3262
+ const optionsTable2 = ref<Partial<DataOptions>>({
3263
+ itemsPerPage: 3,
3264
+ sortBy: [{ key: 'firstname', order: 'asc' }],
3265
+ page: 1,
3266
+ })
3267
+
3268
+ const fetchDataTable2 = async (options?: DataOptions): Promise<void> => {
3269
+ const optionsToUse = options || optionsTable2.value as DataOptions
3270
+ const { items, total } = await getDataFromApi(optionsToUse)
3271
+ usersTable2.value = items
3272
+ totalUsersTable2.value = total
3273
+ }
3274
+
3275
+ const wait = async (ms: number) => {
3276
+ return new Promise(resolve => setTimeout(resolve, ms))
3277
+ }
3278
+
3279
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise<DataObj> => {
3280
+ const state = sortBy[0].key === 'lastname' ? stateTable1 : stateTable2
3281
+ state.value = StateEnum.PENDING
3282
+ await wait(1000)
3283
+
3284
+ return new Promise((resolve) => {
3285
+ let items: User[] = getUsers()
3286
+ const total = items.length
3287
+
3288
+ if (sortBy && sortBy.length > 0) {
3289
+ items = items.sort((a, b) => {
3290
+ const key = sortBy[0].key
3291
+ const order = sortBy[0].order === 'asc' ? 1 : -1
3292
+
3293
+ return a[key] > b[key] ? order : -order
3294
+ })
3295
+ }
3296
+
3297
+ if (itemsPerPage > 0) {
3298
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
3299
+ }
3300
+
3301
+ resolve({ items, total })
3302
+ state.value = StateEnum.RESOLVED
3303
+ })
3304
+ }
3305
+
3306
+ const getUsers = (): User[] => {
3307
+ return [
3308
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
3309
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
3310
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
3311
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
3312
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
3313
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
3314
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
3315
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
3316
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
3317
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
3318
+ ]
3319
+ }
3320
+
3321
+ // Chargement initial des données
3322
+ fetchDataTable1()
3323
+ fetchDataTable2()
3324
+
3325
+ return {
3326
+ args,
3327
+ usersTable1,
3328
+ totalUsersTable1,
3329
+ optionsTable1,
3330
+ stateTable1,
3331
+ fetchDataTable1,
3332
+ usersTable2,
3333
+ totalUsersTable2,
3334
+ optionsTable2,
3335
+ stateTable2,
3336
+ fetchDataTable2,
3337
+ StateEnum,
3338
+ }
3339
+ },
3340
+ template: `
3341
+ <div>
3342
+ <SyServerTable
3343
+ v-bind="args"
3344
+ v-model:options="optionsTable1"
3345
+ :items="usersTable1"
3346
+ :server-items-length="totalUsersTable1"
3347
+ :loading="stateTable1 === StateEnum.PENDING"
3348
+ suffix="table1"
3349
+ class="mb-10"
3350
+ @update:options="fetchDataTable1"
3351
+ />
3352
+ <SyServerTable
3353
+ v-bind="args"
3354
+ v-model:options="optionsTable2"
3355
+ :items="usersTable2"
3356
+ :server-items-length="totalUsersTable2"
3357
+ :loading="stateTable2 === StateEnum.PENDING"
3358
+ suffix="table2"
3359
+ @update:options="fetchDataTable2"
3360
+ />
3361
+ </div>
3362
+ `,
3363
+ }
3364
+ },
3365
+ }
3366
+
3367
+ export const DataAlignment: Story = {
3368
+ parameters: {
3369
+ sourceCode: [
3370
+ {
3371
+ name: 'Template',
3372
+ code: `
3373
+ <template>
3374
+ <SyServerTable
3375
+ v-model:options="options"
3376
+ :items="users"
3377
+ :headers="headers"
3378
+ :server-items-length="totalUsers"
3379
+ :loading="state === StateEnum.PENDING"
3380
+ suffix="server-resizable-columns"
3381
+ @update:options="fetchData"
3382
+ />
3383
+ </template>
3384
+ `,
3385
+ },
3386
+ {
3387
+ name: 'Script',
3388
+ code: `
3389
+ <script setup lang="ts">
3390
+ import { ref, watch } from 'vue'
3391
+ import { SyServerTable } from '@cnamts/synapse'
3392
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
3393
+ import type { DataOptions } from '@cnamts/synapse/src/components/Tables/common/types'
3394
+
3395
+ const totalUsers = ref(0)
3396
+ const users = ref<User[]>([])
3397
+ const state = ref(StateEnum.IDLE)
3398
+
3399
+ const options = ref({
3400
+ itemsPerPage: 5,
3401
+ sortBy: [{ key: 'lastname', order: 'asc' }],
3402
+ page: 1,
3403
+ })
3404
+
3405
+ const headers = [
3406
+ {
3407
+ title: 'ID',
3408
+ key: 'id',
3409
+ align: 'center',
3410
+ },
3411
+ {
3412
+ title: 'Nom',
3413
+ key: 'lastname',
3414
+ align: 'start',
3415
+ },
3416
+ {
3417
+ title: 'Date de naissance',
3418
+ key: 'birthdate',
3419
+ align: 'center',
3420
+ },
3421
+ {
3422
+ title: 'NIR',
3423
+ key: 'nir',
3424
+ align: 'end',
3425
+ },
3426
+ ]
3427
+
3428
+ const fetchData = async (): Promise<void> => {
3429
+ const { items, total } = await getDataFromApi(options.value)
3430
+ users.value = items
3431
+ totalUsers.value = total
3432
+ }
3433
+
3434
+ const wait = async (ms: number) => {
3435
+ return new Promise(resolve => setTimeout(resolve, ms))
3436
+ }
3437
+
3438
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions) => {
3439
+ state.value = StateEnum.PENDING
3440
+ await wait(1000)
3441
+
3442
+ return new Promise((resolve) => {
3443
+ let items: User[] = getUsers()
3444
+ const total = items.length
3445
+
3446
+ if (sortBy && sortBy.length > 0) {
3447
+ items = items.sort((a, b) => {
3448
+ const key = sortBy[0].key
3449
+ const order = sortBy[0].order === 'asc' ? 1 : -1
3450
+
3451
+ return a[key] > b[key] ? order : -order
3452
+ })
3453
+ }
3454
+
3455
+ if (itemsPerPage > 0) {
3456
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
3457
+ }
3458
+
3459
+ resolve({ items, total })
3460
+ state.value = StateEnum.RESOLVED
3461
+ })
3462
+ }
3463
+
3464
+ const getUsers = () => {
3465
+ return [
3466
+ {
3467
+ id: '1',
3468
+ lastname: 'Lefebvre',
3469
+ birthdate: '18/02/1989',
3470
+ nir: '1 89 02 75 120 005 79',
3471
+ },
3472
+ {
3473
+ id: '2',
3474
+ lastname: 'Richard',
3475
+ birthdate: '22/05/1991',
3476
+ nir: '2 91 05 75 120 005 76',
3477
+ },
3478
+ {
3479
+ id: '3',
3480
+ lastname: 'Fournier',
3481
+ birthdate: '11/11/2000',
3482
+ nir: '2 00 11 42 120 008 87',
3483
+ },
3484
+ ]
3485
+ }
3486
+
3487
+ fetchData()
3488
+ </script>
3489
+ `,
3490
+ },
3491
+ ],
3492
+ },
3493
+ args: {
3494
+ 'options': {
3495
+ itemsPerPage: 5,
3496
+ sortBy: [{ key: 'lastname', order: 'asc' }],
3497
+ page: 1,
3498
+ },
3499
+ 'headers': [
3500
+ {
3501
+ title: 'ID',
3502
+ key: 'id',
3503
+ align: 'center',
3504
+ },
3505
+ {
3506
+ title: 'Nom',
3507
+ key: 'lastname',
3508
+ align: 'start',
3509
+ },
3510
+ {
3511
+ title: 'Date de naissance',
3512
+ key: 'birthdate',
3513
+ align: 'center',
3514
+ },
3515
+ {
3516
+ title: 'NIR',
3517
+ key: 'nir',
3518
+ align: 'end',
3519
+ },
3520
+ ],
3521
+ 'caption': '',
3522
+ 'serverItemsLength': 3,
3523
+ 'suffix': 'server-resizable-columns',
3524
+ 'density': 'default',
3525
+ 'striped': false,
3526
+ 'onUpdate:options': fn(),
3527
+ },
3528
+ render: (args) => {
3529
+ return {
3530
+ components: { SyServerTable },
3531
+ setup() {
3532
+ const totalUsers = ref(0)
3533
+ const users = ref<User[]>([])
3534
+ const state = ref(StateEnum.IDLE)
3535
+
3536
+ const options = ref({ ...args.options })
3537
+
3538
+ watch(options, (newVal) => {
3539
+ if (args.options) {
3540
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
3541
+ }
3542
+ }, { deep: true })
3543
+
3544
+ const fetchData = async (): Promise<void> => {
3545
+ // @ts-expect-error - fetchData is not defined
3546
+ const { items, total } = await getDataFromApi(options.value)
3547
+ users.value = items
3548
+ totalUsers.value = total
3549
+ }
3550
+
3551
+ const wait = async (ms: number) => {
3552
+ return new Promise(resolve => setTimeout(resolve, ms))
3553
+ }
3554
+
3555
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions) => {
3556
+ state.value = StateEnum.PENDING
3557
+ await wait(1000)
3558
+
3559
+ return new Promise((resolve) => {
3560
+ let items = getUsers()
3561
+ const total = items.length
3562
+
3563
+ if (sortBy && sortBy.length > 0) {
3564
+ items = items.sort((a, b) => {
3565
+ const key = sortBy[0].key
3566
+ const order = sortBy[0].order === 'asc' ? 1 : -1
3567
+
3568
+ return a[key] > b[key] ? order : -order
3569
+ })
3570
+ }
3571
+
3572
+ if (itemsPerPage > 0) {
3573
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
3574
+ }
3575
+
3576
+ resolve({ items, total })
3577
+ state.value = StateEnum.RESOLVED
3578
+ })
3579
+ }
3580
+
3581
+ const getUsers = () => {
3582
+ return [
3583
+ {
3584
+ id: '1',
3585
+ lastname: 'Lefebvre',
3586
+ birthdate: '18/02/1989',
3587
+ nir: '1 89 02 75 120 005 79',
3588
+ },
3589
+ {
3590
+ id: '2',
3591
+ lastname: 'Richard',
3592
+ birthdate: '22/05/1991',
3593
+ nir: '2 91 05 75 120 005 76',
3594
+ },
3595
+ {
3596
+ id: '3',
3597
+ lastname: 'Fournier',
3598
+ birthdate: '11/11/2000',
3599
+ nir: '2 00 11 42 120 008 87',
3600
+ },
3601
+ ]
3602
+ }
3603
+
3604
+ fetchData()
3605
+
3606
+ return { args, users, state, fetchData, options, totalUsers, StateEnum }
3607
+ },
3608
+ template: `
3609
+ <SyServerTable
3610
+ v-bind="args"
3611
+ v-model:options="options"
3612
+ :items="users"
3613
+ :server-items-length="totalUsers"
3614
+ :loading="state === StateEnum.PENDING"
3615
+ suffix="server-data-alignment"
3616
+ @update:options="[fetchData, args['onUpdate:options']]"
3617
+ />
3618
+ `,
3619
+ }
3620
+ },
3621
+ }
3622
+
3623
+ export const ResizableColumns: Story = {
3624
+ parameters: {
3625
+ sourceCode: [
3626
+ {
3627
+ name: 'Template',
3628
+ code: `
3629
+ <template>
3630
+ <SyServerTable
3631
+ v-model:options="options"
3632
+ :items="users"
3633
+ :headers="headers"
3634
+ :server-items-length="totalUsers"
3635
+ :loading="state === StateEnum.PENDING"
3636
+ :resizable-columns="true"
3637
+ suffix="server-resizable-columns"
3638
+ @update:options="fetchData"
3639
+ />
3640
+ </template>
3641
+ `,
3642
+ },
3643
+ {
3644
+ name: 'Script',
3645
+ code: `
3646
+ <script setup lang="ts">
3647
+ import { ref, watch } from 'vue'
3648
+ import { SyServerTable } from '@cnamts/synapse'
3649
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
3650
+ import type { DataOptions } from '@cnamts/synapse/src/components/Tables/common/types'
3651
+
3652
+ interface User {
3653
+ [key: string]: string
3654
+ firstname: string
3655
+ lastname: string
3656
+ email: string
3657
+ }
3658
+
3659
+ interface DataObj {
3660
+ items: User[]
3661
+ total: number
3662
+ }
3663
+
3664
+ const totalUsers = ref(0)
3665
+ const users = ref<User[]>([])
3666
+ const state = ref(StateEnum.IDLE)
3667
+
3668
+ const options = ref({
3669
+ itemsPerPage: 5,
3670
+ sortBy: [{ key: 'lastname', order: 'asc' }],
3671
+ page: 1,
3672
+ })
3673
+
3674
+ const headers = [
3675
+ { title: 'Nom', key: 'lastname' },
3676
+ { title: 'Prénom', key: 'firstname' },
3677
+ { title: 'Email', key: 'email' },
3678
+ ]
3679
+
3680
+ const fetchData = async (): Promise<void> => {
3681
+ const { items, total } = await getDataFromApi(options.value)
3682
+ users.value = items
3683
+ totalUsers.value = total
3684
+ }
3685
+
3686
+ const wait = async (ms: number) => {
3687
+ return new Promise(resolve => setTimeout(resolve, ms))
3688
+ }
3689
+
3690
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
3691
+ state.value = StateEnum.PENDING
3692
+ await wait(1000)
3693
+
3694
+ return new Promise((resolve) => {
3695
+ let items: User[] = getUsers()
3696
+ const total = items.length
3697
+
3698
+ if (sortBy && sortBy.length > 0) {
3699
+ items = items.sort((a, b) => {
3700
+ const key = sortBy[0].key
3701
+ const order = sortBy[0].order === 'asc' ? 1 : -1
3702
+
3703
+ return a[key] > b[key] ? order : -order
3704
+ })
3705
+ }
3706
+
3707
+ if (itemsPerPage > 0) {
3708
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
3709
+ }
3710
+
3711
+ resolve({ items, total })
3712
+ state.value = StateEnum.RESOLVED
3713
+ })
3714
+ }
3715
+
3716
+ const getUsers = (): User[] => {
3717
+ return [
3718
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
3719
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
3720
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
3721
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
3722
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
3723
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
3724
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
3725
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
3726
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
3727
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
3728
+ { firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
3729
+ { firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
3730
+ { firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
3731
+ { firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
3732
+ { firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
3733
+ ]
3734
+ }
3735
+
3736
+ fetchData()
3737
+ </script>
3738
+ `,
3739
+ },
3740
+ ],
3741
+ },
3742
+ args: {
3743
+ 'options': {
3744
+ itemsPerPage: 5,
3745
+ sortBy: [{ key: 'lastname', order: 'asc' }],
3746
+ page: 1,
3747
+ },
3748
+ 'headers': [
3749
+ { title: 'Nom', key: 'lastname' },
3750
+ { title: 'Prénom', key: 'firstname' },
3751
+ { title: 'Email', key: 'email' },
3752
+ ],
3753
+ 'caption': '',
3754
+ 'serverItemsLength': 15,
3755
+ 'suffix': 'server-resizable-columns',
3756
+ 'density': 'default',
3757
+ 'striped': false,
3758
+ 'resizableColumns': true,
3759
+ 'onUpdate:options': fn(),
3760
+ },
3761
+ render: (args) => {
3762
+ return {
3763
+ components: { SyServerTable },
3764
+ setup() {
3765
+ const totalUsers = ref(0)
3766
+ const users = ref<User[]>([])
3767
+ const state = ref(StateEnum.IDLE)
3768
+
3769
+ const options = ref({ ...args.options })
3770
+
3771
+ watch(options, (newVal) => {
3772
+ if (args.options) {
3773
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
3774
+ }
3775
+ }, { deep: true })
3776
+
3777
+ const fetchData = async (): Promise<void> => {
3778
+ // @ts-expect-error - fetchData is not defined
3779
+ const { items, total } = await getDataFromApi(options.value)
3780
+ users.value = items
3781
+ totalUsers.value = total
3782
+ }
3783
+
3784
+ const wait = async (ms: number) => {
3785
+ return new Promise(resolve => setTimeout(resolve, ms))
3786
+ }
3787
+
3788
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise<DataObj> => {
3789
+ state.value = StateEnum.PENDING
3790
+ await wait(1000)
3791
+
3792
+ return new Promise((resolve) => {
3793
+ let items: User[] = getUsers()
3794
+ const total = items.length
3795
+
3796
+ if (sortBy && sortBy.length > 0) {
3797
+ items = items.sort((a, b) => {
3798
+ const key = sortBy[0].key
3799
+ const order = sortBy[0].order === 'asc' ? 1 : -1
3800
+
3801
+ return a[key] > b[key] ? order : -order
3802
+ })
3803
+ }
3804
+
3805
+ if (itemsPerPage > 0) {
3806
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
3807
+ }
3808
+
3809
+ resolve({ items, total })
3810
+ state.value = StateEnum.RESOLVED
3811
+ })
3812
+ }
3813
+
3814
+ const getUsers = (): User[] => {
3815
+ return [
3816
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
3817
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
3818
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
3819
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
3820
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
3821
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
3822
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
3823
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
3824
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
3825
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
3826
+ { firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
3827
+ { firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
3828
+ { firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
3829
+ { firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
3830
+ { firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
3831
+ ]
3832
+ }
3833
+
3834
+ fetchData()
3835
+
3836
+ return { args, users, state, fetchData, options, totalUsers, StateEnum }
3837
+ },
3838
+ template: `
3839
+ <div>
3840
+ <SyServerTable
3841
+ v-bind="args"
3842
+ v-model:options="options"
3843
+ :items="users"
3844
+ :server-items-length="totalUsers"
3845
+ :loading="state === StateEnum.PENDING"
3846
+ suffix="server-resizable-columns"
3847
+ @update:options="fetchData"
3848
+ />
3849
+ </div>
3850
+ `,
3851
+ }
3852
+ },
3853
+ }
3854
+
3855
+ export const RowSelection: Story = {
3856
+ name: 'Row Selection',
3857
+ parameters: {
3858
+ sourceCode: [
3859
+ {
3860
+ name: 'Template',
3861
+ code: `
3862
+ <template>
3863
+ <SyServerTable
3864
+ v-model:options="options"
3865
+ v-model="selection"
3866
+ :headers="headers"
3867
+ :items="items"
3868
+ :serverItemsLength="items.length"
3869
+ show-select
3870
+ show-filters
3871
+ suffix="selection-server-table"
3872
+ />
3873
+ <div v-if="selection.length" class="mt-4 pa-4 bg-grey-lighten-4">
3874
+ <h3 class="text-h6 mb-3">Item(s) sélectionné(s) ({{ selection.length }})</h3>
3875
+ <div v-for="(item, index) in selection" :key="index" class="mb-2 pa-2 bg-grey-lighten-3">
3876
+ <div><strong>Nom:</strong> {{ typeof item === 'object' ? item.lastname : users.find(i => JSON.stringify(i) === item)?.lastname }}</div>
3877
+ <div><strong>Prénom:</strong> {{ typeof item === 'object' ? item.firstname : users.find(i => JSON.stringify(i) === item)?.firstname }}</div>
3878
+ <div><strong>Email:</strong> {{ typeof item === 'object' ? item.email : users.find(i => JSON.stringify(i) === item)?.email }}</div>
3879
+ </div>
3880
+ </div>
3881
+ </div>
3882
+ </template>
3883
+ `,
3884
+ },
3885
+ {
3886
+ name: 'Script',
3887
+ code: `
3888
+ <script setup lang="ts">
3889
+ import { ref } from 'vue'
3890
+ import { SyServerTable } from '@cnamts/synapse'
3891
+
3892
+ const options = ref({
3893
+ itemsPerPage: 4,
3894
+ })
3895
+
3896
+ const headers = ref([
3897
+ {
3898
+ title: 'Nom',
3899
+ key: 'lastname',
3900
+ },
3901
+ {
3902
+ title: 'Prénom',
3903
+ key: 'firstname',
3904
+ },
3905
+ {
3906
+ title: 'Email',
3907
+ value: 'email',
3908
+ },
3909
+ ])
3910
+
3911
+ const items = ref([
3912
+ {
3913
+ firstname: 'Virginie',
3914
+ lastname: 'Beauchesne',
3915
+ email: 'virginie.beauchesne@example.com',
3916
+ },
3917
+ {
3918
+ firstname: 'Simone',
3919
+ lastname: 'Bellefeuille',
3920
+ email: 'simone.bellefeuille@example.com',
3921
+ },
3922
+ {
3923
+ firstname: 'Étienne',
3924
+ lastname: 'Salois',
3925
+ email: 'etienne.salois@example.com',
3926
+ },
3927
+ {
3928
+ firstname: 'Thierry',
3929
+ lastname: 'Bobu',
3930
+ email: 'thierry.bobu@example.com',
3931
+ },
3932
+ {
3933
+ firstname: 'Bernadette',
3934
+ lastname: 'Langelier',
3935
+ email: 'bernadette.langelier@exemple.com'
3936
+ },
3937
+ {
3938
+ firstname: 'Agate',
3939
+ lastname: 'Roy',
3940
+ email: 'agate.roy@exemple.com'
3941
+ }
3942
+ ])
3943
+ </script>
3944
+ `,
3945
+ },
3946
+ ],
3947
+ },
3948
+ args: {
3949
+ 'headers': [
3950
+ {
3951
+ title: 'Nom',
3952
+ key: 'lastname',
3953
+ },
3954
+ {
3955
+ title: 'Prénom',
3956
+ key: 'firstname',
3957
+ },
3958
+ {
3959
+ title: 'Email',
3960
+ value: 'email',
3961
+ },
3962
+ ],
3963
+ 'items': [
3964
+ {
3965
+ firstname: 'Virginie',
3966
+ lastname: 'Beauchesne',
3967
+ email: 'virginie.beauchesne@example.com',
3968
+ },
3969
+ {
3970
+ firstname: 'Simone',
3971
+ lastname: 'Bellefeuille',
3972
+ email: 'simone.bellefeuille@example.com',
3973
+ },
3974
+ {
3975
+ firstname: 'Étienne',
3976
+ lastname: 'Salois',
3977
+ email: 'etienne.salois@example.com',
3978
+ },
3979
+ {
3980
+ firstname: 'Thierry',
3981
+ lastname: 'Bobu',
3982
+ email: 'thierry.bobu@example.com',
3983
+ },
3984
+ {
3985
+ firstname: 'Bernadette',
3986
+ lastname: 'Langelier',
3987
+ email: 'bernadette.langelier@exemple.com',
3988
+ },
3989
+ {
3990
+ firstname: 'Agate',
3991
+ lastname: 'Roy',
3992
+ email: 'agate.roy@exemple.com',
3993
+ },
3994
+ ],
3995
+ 'options': {
3996
+ itemsPerPage: 4,
3997
+ page: 1,
3998
+ filters: [],
3999
+ },
4000
+ 'caption': '',
4001
+ 'suffix': 'selection-server-table',
4002
+ 'density': 'default',
4003
+ 'striped': false,
4004
+ 'showSelect': true,
4005
+ 'showFilters': true,
4006
+ 'serverItemsLength': 6,
4007
+ 'onUpdate:options': fn(),
4008
+ },
4009
+ render(args) {
4010
+ return {
4011
+ components: { SyServerTable },
4012
+ setup() {
4013
+ const totalUsers = ref(0)
4014
+ const users = ref<User[]>([])
4015
+ const selection = ref([])
4016
+ const state = ref(StateEnum.IDLE)
4017
+
4018
+ const fetchData = async (): Promise<void> => {
4019
+ // Create a complete DataOptions object with all required properties
4020
+ const defaultOptions: DataOptions = {
4021
+ page: 1,
4022
+ itemsPerPage: 10,
4023
+ sortBy: [],
4024
+ multiSort: false,
4025
+ }
4026
+ const options = args.options ? { ...defaultOptions, ...args.options } : defaultOptions
4027
+ const { items, total } = await getDataFromApi(options)
4028
+ users.value = items
4029
+ totalUsers.value = total
4030
+ }
4031
+
4032
+ const wait = async (ms: number) => {
4033
+ return new Promise(resolve => setTimeout(resolve, ms))
4034
+ }
4035
+
4036
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
4037
+ state.value = StateEnum.PENDING
4038
+ await wait(1000)
4039
+
4040
+ return new Promise((resolve) => {
4041
+ let items: User[] = getUsers()
4042
+ let total = items.length // Changed from const to let
4043
+
4044
+ // Add filtering logic here
4045
+ if (filters && filters.length > 0) {
4046
+ filters.forEach((filter) => {
4047
+ const { key, value } = filter
4048
+
4049
+ items = items.filter((item) => {
4050
+ const itemValue = item[key]
4051
+ return String(itemValue).toLowerCase().includes(String(value).toLowerCase())
4052
+ })
4053
+ })
4054
+ // Update total after filtering
4055
+ total = items.length
4056
+ }
4057
+
4058
+ if (sortBy && sortBy.length > 0) {
4059
+ items = items.sort((a, b) => {
4060
+ const key = sortBy[0].key
4061
+ const order = sortBy[0].order === 'asc' ? 1 : -1
4062
+
4063
+ return a[key] > b[key] ? order : -order
4064
+ })
4065
+ }
4066
+
4067
+ if (itemsPerPage > 0) {
4068
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
4069
+ }
4070
+
4071
+ resolve({ items, total })
4072
+ state.value = StateEnum.RESOLVED
4073
+ })
4074
+ }
4075
+
4076
+ const getUsers = (): User[] => {
4077
+ return [
4078
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
4079
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
4080
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
4081
+ { firstname: 'Thierry', lastname: 'Bobu', email: 'thierry.bobu@example.com' },
4082
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@exemple.com' },
4083
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@exemple.com' },
4084
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
4085
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
4086
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
4087
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
4088
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
4089
+ { firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
4090
+ ]
4091
+ }
4092
+
4093
+ // Call fetchData on mount
4094
+ fetchData()
4095
+
4096
+ return { args, users, state, fetchData, totalUsers, selection, StateEnum }
4097
+ },
4098
+ template: `
4099
+ <div>
4100
+ <SyServerTable
4101
+ v-bind="args"
4102
+ v-model:options="args.options"
4103
+ v-model="selection"
4104
+ :items="users"
4105
+ :server-items-length="totalUsers"
4106
+ :loading="state === StateEnum.PENDING"
4107
+ suffix="selection-server-table"
4108
+ @update:options="fetchData"
4109
+ />
4110
+ <div v-if="selection.length" class="mt-4 pa-4 bg-grey-lighten-4">
4111
+ <h3 class="text-h6 mb-3">Item(s) sélectionné(s) ({{ selection.length }})</h3>
4112
+ <div v-for="(item, index) in selection" :key="index" class="mb-2 pa-2 bg-grey-lighten-3">
4113
+ <div><strong>Nom:</strong> {{ typeof item === 'object' ? item.lastname : users.find(i => JSON.stringify(i) === item)?.lastname }}</div>
4114
+ <div><strong>Prénom:</strong> {{ typeof item === 'object' ? item.firstname : users.find(i => JSON.stringify(i) === item)?.firstname }}</div>
4115
+ <div><strong>Email:</strong> {{ typeof item === 'object' ? item.email : users.find(i => JSON.stringify(i) === item)?.email }}</div>
4116
+ </div>
4117
+ </div>
4118
+ </div>
4119
+ `,
4120
+ }
4121
+ },
4122
+ }
4123
+
4124
+ export const ColumnControls: StoryObj<typeof SyServerTable> = {
4125
+ parameters: {
4126
+ sourceCode: [
4127
+ {
4128
+ name: 'Template',
4129
+ code: `
4130
+ <template>
4131
+ <SyServerTable
4132
+ v-model:options="options"
4133
+ :items="users"
4134
+ :headers="headers"
4135
+ :server-items-length="totalUsers"
4136
+ :loading="state === StateEnum.PENDING"
4137
+ suffix="server-control-columns"
4138
+ enable-column-controls
4139
+ @update:options="fetchData"
4140
+ />
4141
+ </template>
4142
+ `,
4143
+ },
4144
+ {
4145
+ name: 'Script',
4146
+ code: `
4147
+ <script setup lang="ts">
4148
+ import { ref, watch } from 'vue'
4149
+ import { SyServerTable } from '@cnamts/synapse'
4150
+ import { StateEnum } from '@cnamts/synapse/src/components/Tables/common/constants/StateEnum'
4151
+ import type { DataOptions } from '@cnamts/synapse/src/components/Tables/common/types'
4152
+
4153
+ interface User {
4154
+ [key: string]: string
4155
+ firstname: string
4156
+ lastname: string
4157
+ email: string
4158
+ }
4159
+
4160
+ interface DataObj {
4161
+ items: User[]
4162
+ total: number
4163
+ }
4164
+
4165
+ const totalUsers = ref(0)
4166
+ const users = ref<User[]>([])
4167
+ const state = ref(StateEnum.IDLE)
4168
+
4169
+ const options = ref({
4170
+ itemsPerPage: 5,
4171
+ sortBy: [{ key: 'lastname', order: 'asc' }],
4172
+ page: 1,
4173
+ })
4174
+
4175
+ const headers = [
4176
+ { title: 'Nom', key: 'lastname' },
4177
+ { title: 'Prénom', key: 'firstname' },
4178
+ { title: 'Email', key: 'email' },
4179
+ ]
4180
+
4181
+ const fetchData = async (): Promise<void> => {
4182
+ const { items, total } = await getDataFromApi(options.value)
4183
+ users.value = items
4184
+ totalUsers.value = total
4185
+ }
4186
+
4187
+ const wait = async (ms: number) => {
4188
+ return new Promise(resolve => setTimeout(resolve, ms))
4189
+ }
4190
+
4191
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage, filters }: DataOptions): Promise<DataObj> => {
4192
+ state.value = StateEnum.PENDING
4193
+ await wait(1000)
4194
+
4195
+ return new Promise((resolve) => {
4196
+ let items: User[] = getUsers()
4197
+ const total = items.length
4198
+
4199
+ if (sortBy && sortBy.length > 0) {
4200
+ items = items.sort((a, b) => {
4201
+ const key = sortBy[0].key
4202
+ const order = sortBy[0].order === 'asc' ? 1 : -1
4203
+
4204
+ return a[key] > b[key] ? order : -order
4205
+ })
4206
+ }
4207
+
4208
+ if (itemsPerPage > 0) {
4209
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
4210
+ }
4211
+
4212
+ resolve({ items, total })
4213
+ state.value = StateEnum.RESOLVED
4214
+ })
4215
+ }
4216
+
4217
+ const getUsers = (): User[] => {
4218
+ return [
4219
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
4220
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
4221
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
4222
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
4223
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
4224
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
4225
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
4226
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
4227
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
4228
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
4229
+ { firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
4230
+ { firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
4231
+ { firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
4232
+ { firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
4233
+ { firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
4234
+ ]
4235
+ }
4236
+
4237
+ fetchData()
4238
+ </script>
4239
+ `,
4240
+ },
4241
+ ],
4242
+ },
4243
+ args: {
4244
+ 'options': {
4245
+ itemsPerPage: 5,
4246
+ sortBy: [{ key: 'lastname', order: 'asc' }],
4247
+ page: 1,
4248
+ },
4249
+ 'headers': [
4250
+ { title: 'Nom', key: 'lastname' },
4251
+ { title: 'Prénom', key: 'firstname' },
4252
+ { title: 'Email', key: 'email' },
4253
+ ],
4254
+ 'caption': '',
4255
+ 'serverItemsLength': 15,
4256
+ 'suffix': 'server-control-columns',
4257
+ 'density': 'default',
4258
+ 'striped': false,
4259
+ 'enableColumnControls': true,
4260
+ 'onUpdate:options': fn(),
4261
+ },
4262
+ render: (args) => {
4263
+ return {
4264
+ components: { SyServerTable },
4265
+ setup() {
4266
+ const totalUsers = ref(0)
4267
+ const users = ref<User[]>([])
4268
+ const state = ref(StateEnum.IDLE)
4269
+
4270
+ const options = ref({ ...args.options })
4271
+
4272
+ watch(options, (newVal) => {
4273
+ if (args.options) {
4274
+ Object.assign(args.options, JSON.parse(JSON.stringify(newVal)))
4275
+ }
4276
+ }, { deep: true })
4277
+
4278
+ const fetchData = async (): Promise<void> => {
4279
+ // @ts-expect-error - fetchData is not defined
4280
+ const { items, total } = await getDataFromApi(options.value)
4281
+ users.value = items
4282
+ totalUsers.value = total
4283
+ }
4284
+
4285
+ const wait = async (ms: number) => {
4286
+ return new Promise(resolve => setTimeout(resolve, ms))
4287
+ }
4288
+
4289
+ const getDataFromApi = async ({ sortBy, page, itemsPerPage }: DataOptions): Promise<DataObj> => {
4290
+ state.value = StateEnum.PENDING
4291
+ await wait(1000)
4292
+
4293
+ return new Promise((resolve) => {
4294
+ let items: User[] = getUsers()
4295
+ const total = items.length
4296
+
4297
+ if (sortBy && sortBy.length > 0) {
4298
+ items = items.sort((a, b) => {
4299
+ const key = sortBy[0].key
4300
+ const order = sortBy[0].order === 'asc' ? 1 : -1
4301
+
4302
+ return a[key] > b[key] ? order : -order
4303
+ })
4304
+ }
4305
+
4306
+ if (itemsPerPage > 0) {
4307
+ items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
4308
+ }
4309
+
4310
+ resolve({ items, total })
4311
+ state.value = StateEnum.RESOLVED
4312
+ })
4313
+ }
4314
+
4315
+ const getUsers = (): User[] => {
4316
+ return [
4317
+ { firstname: 'Virginie', lastname: 'Beauchesne', email: 'virginie.beauchesne@example.com' },
4318
+ { firstname: 'Simone', lastname: 'Bellefeuille', email: 'simone.bellefeuille@example.com' },
4319
+ { firstname: 'Étienne', lastname: 'Salois', email: 'etienne.salois@example.com' },
4320
+ { firstname: 'Bernadette', lastname: 'Langelier', email: 'bernadette.langelier@example.com' },
4321
+ { firstname: 'Agate', lastname: 'Roy', email: 'agate.roy@example.com' },
4322
+ { firstname: 'Louis', lastname: 'Denis', email: 'louis.denis@example.com' },
4323
+ { firstname: 'Édith', lastname: 'Cartier', email: 'edith.cartier@example.com' },
4324
+ { firstname: 'Alphonse', lastname: 'Bouvier', email: 'alphonse.bouvier@example.com' },
4325
+ { firstname: 'Eustache', lastname: 'Dubois', email: 'eustache.dubois@example.com' },
4326
+ { firstname: 'Rosemarie', lastname: 'Quessy', email: 'rosemarie.quessy@example.com' },
4327
+ { firstname: 'Serge', lastname: 'Rivard', email: 'serge.rivard@example.com' },
4328
+ { firstname: 'Jacques', lastname: 'Demers', email: 'jacques.demers@example.com' },
4329
+ { firstname: 'Aimée', lastname: 'Josseaume', email: 'aimee.josseaume@example.com' },
4330
+ { firstname: 'Delphine', lastname: 'Robillard', email: 'delphine.robillard@example.com' },
4331
+ { firstname: 'Alexandre', lastname: 'Lazure', email: 'alexandre.lazure@example.com' },
4332
+ ]
4333
+ }
4334
+
4335
+ fetchData()
4336
+
4337
+ return { args, users, state, fetchData, options, totalUsers, StateEnum }
4338
+ },
4339
+ template: `
4340
+ <div>
4341
+ <SyServerTable
4342
+ v-bind="args"
4343
+ v-model:options="options"
4344
+ :items="users"
4345
+ :server-items-length="totalUsers"
4346
+ :loading="state === StateEnum.PENDING"
4347
+ suffix="server-control-columns"
4348
+ @update:options="fetchData"
4349
+ />
4350
+ </div>
4351
+ `,
4352
+ }
4353
+ },
4354
+ }