@aleph-alpha/ui-library 1.9.0 → 1.11.0

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 (432) hide show
  1. package/README.md +14 -0
  2. package/dist/system/index-CkH7HQaa.js +7 -0
  3. package/dist/system/index-CuHwEAQ_.js +7 -0
  4. package/dist/system/index.d.ts +1322 -318
  5. package/dist/system/lib.js +8839 -6993
  6. package/package.json +2 -1
  7. package/src/@types/shims-vue.d.ts +5 -0
  8. package/src/__tests__/placeholder.test.ts +7 -0
  9. package/src/compositions/UiCompositionPlaceholder/UiCompositionPlaceholder.vue +9 -0
  10. package/src/compositions/UiCompositionPlaceholder/index.ts +1 -0
  11. package/src/compositions/UiCompositionPlaceholder/types.ts +8 -0
  12. package/src/compositions/UiDataTable/UiDataTable.mock.ts +104 -0
  13. package/src/compositions/UiDataTable/UiDataTable.stories.ts +1575 -0
  14. package/src/compositions/UiDataTable/UiDataTable.vue +129 -0
  15. package/src/compositions/UiDataTable/UiDataTableColumnHeader.vue +97 -0
  16. package/src/compositions/UiDataTable/UiDataTablePagination.vue +147 -0
  17. package/src/compositions/UiDataTable/UiDataTableToolbar.vue +85 -0
  18. package/src/compositions/UiDataTable/__tests__/UiDataTable.test.ts +372 -0
  19. package/src/compositions/UiDataTable/__tests__/UiDataTableColumnHeader.test.ts +217 -0
  20. package/src/compositions/UiDataTable/__tests__/UiDataTablePagination.test.ts +274 -0
  21. package/src/compositions/UiDataTable/__tests__/UiDataTableToolbar.test.ts +198 -0
  22. package/src/compositions/UiDataTable/constants.ts +77 -0
  23. package/src/compositions/UiDataTable/index.ts +6 -0
  24. package/src/compositions/UiDataTable/types.ts +39 -0
  25. package/src/compositions/UiDatePicker/UiDatePicker.stories.ts +976 -0
  26. package/src/compositions/UiDatePicker/UiDatePicker.vue +193 -0
  27. package/src/compositions/UiDatePicker/__tests__/UiDatePicker.test.ts +325 -0
  28. package/src/compositions/UiDatePicker/index.ts +14 -0
  29. package/src/compositions/UiDatePicker/types.ts +220 -0
  30. package/src/compositions/index.ts +8 -0
  31. package/src/foundations/UiPlaceholder/UiPlaceholder.vue +9 -0
  32. package/src/foundations/UiPlaceholder/index.ts +1 -0
  33. package/src/foundations/UiPlaceholder/types.ts +8 -0
  34. package/src/foundations/index.ts +6 -0
  35. package/src/index.ts +27 -0
  36. package/src/lib/utils.ts +6 -0
  37. package/src/primitives/UiAccordion/UiAccordion.stories.ts +476 -0
  38. package/src/primitives/UiAccordion/UiAccordion.vue +31 -0
  39. package/src/primitives/UiAccordion/UiAccordionContent.vue +16 -0
  40. package/src/primitives/UiAccordion/UiAccordionItem.vue +16 -0
  41. package/src/primitives/UiAccordion/UiAccordionTrigger.vue +23 -0
  42. package/src/primitives/UiAccordion/__tests__/UiAccordion.test.ts +198 -0
  43. package/src/primitives/UiAccordion/index.ts +6 -0
  44. package/src/primitives/UiAccordion/types.ts +95 -0
  45. package/src/primitives/UiAlert/UiAlert.stories.ts +199 -0
  46. package/src/primitives/UiAlert/UiAlert.vue +27 -0
  47. package/src/primitives/UiAlert/UiAlertDescription.vue +13 -0
  48. package/src/primitives/UiAlert/UiAlertTitle.vue +13 -0
  49. package/src/primitives/UiAlert/__tests__/UiAlert.test.ts +20 -0
  50. package/src/primitives/UiAlert/constants.ts +3 -0
  51. package/src/primitives/UiAlert/index.ts +5 -0
  52. package/src/primitives/UiAlert/types.ts +14 -0
  53. package/src/primitives/UiAlertDialog/UiAlertDialog.stories.ts +186 -0
  54. package/src/primitives/UiAlertDialog/UiAlertDialog.vue +18 -0
  55. package/src/primitives/UiAlertDialog/UiAlertDialogAction.vue +16 -0
  56. package/src/primitives/UiAlertDialog/UiAlertDialogCancel.vue +16 -0
  57. package/src/primitives/UiAlertDialog/UiAlertDialogContent.vue +26 -0
  58. package/src/primitives/UiAlertDialog/UiAlertDialogDescription.vue +16 -0
  59. package/src/primitives/UiAlertDialog/UiAlertDialogFooter.vue +13 -0
  60. package/src/primitives/UiAlertDialog/UiAlertDialogHeader.vue +16 -0
  61. package/src/primitives/UiAlertDialog/UiAlertDialogTitle.vue +16 -0
  62. package/src/primitives/UiAlertDialog/UiAlertDialogTrigger.vue +17 -0
  63. package/src/primitives/UiAlertDialog/__tests__/UiAlertDialog.test.ts +184 -0
  64. package/src/primitives/UiAlertDialog/index.ts +9 -0
  65. package/src/primitives/UiAlertDialog/types.ts +83 -0
  66. package/src/primitives/UiAvatar/UiAvatar.stories.ts +194 -0
  67. package/src/primitives/UiAvatar/UiAvatar.vue +13 -0
  68. package/src/primitives/UiAvatar/UiAvatarFallback.vue +13 -0
  69. package/src/primitives/UiAvatar/UiAvatarImage.vue +14 -0
  70. package/src/primitives/UiAvatar/__tests__/UiAvatar.test.ts +36 -0
  71. package/src/primitives/UiAvatar/index.ts +3 -0
  72. package/src/primitives/UiAvatar/types.ts +17 -0
  73. package/src/primitives/UiBadge/UiBadge.stories.ts +373 -0
  74. package/src/primitives/UiBadge/UiBadge.vue +21 -0
  75. package/src/primitives/UiBadge/__tests__/UiBadge.test.ts +44 -0
  76. package/src/primitives/UiBadge/constants.ts +3 -0
  77. package/src/primitives/UiBadge/index.ts +2 -0
  78. package/src/primitives/UiBadge/types.ts +48 -0
  79. package/src/primitives/UiButton/UiButton.stories.ts +537 -0
  80. package/src/primitives/UiButton/UiButton.vue +72 -0
  81. package/src/primitives/UiButton/__tests__/UiButton.test.ts +133 -0
  82. package/src/primitives/UiButton/index.ts +2 -0
  83. package/src/primitives/UiButton/types.ts +87 -0
  84. package/src/primitives/UiCalendar/UiCalendar.stories.ts +797 -0
  85. package/src/primitives/UiCalendar/UiCalendar.vue +67 -0
  86. package/src/primitives/UiCalendar/__tests__/UiCalendar.test.ts +45 -0
  87. package/src/primitives/UiCalendar/index.ts +15 -0
  88. package/src/primitives/UiCalendar/types.ts +236 -0
  89. package/src/primitives/UiCard/UiCard.stories.ts +197 -0
  90. package/src/primitives/UiCard/UiCard.vue +13 -0
  91. package/src/primitives/UiCard/UiCardAction.vue +13 -0
  92. package/src/primitives/UiCard/UiCardContent.vue +13 -0
  93. package/src/primitives/UiCard/UiCardDescription.vue +13 -0
  94. package/src/primitives/UiCard/UiCardFooter.vue +13 -0
  95. package/src/primitives/UiCard/UiCardHeader.vue +13 -0
  96. package/src/primitives/UiCard/UiCardTitle.vue +13 -0
  97. package/src/primitives/UiCard/__tests__/UiCard.test.ts +19 -0
  98. package/src/primitives/UiCard/__tests__/UiCardAction.test.ts +19 -0
  99. package/src/primitives/UiCard/__tests__/UiCardContent.test.ts +19 -0
  100. package/src/primitives/UiCard/__tests__/UiCardDescription.test.ts +19 -0
  101. package/src/primitives/UiCard/__tests__/UiCardFooter.test.ts +19 -0
  102. package/src/primitives/UiCard/__tests__/UiCardHeader.test.ts +19 -0
  103. package/src/primitives/UiCard/__tests__/UiCardTitle.test.ts +19 -0
  104. package/src/primitives/UiCard/index.ts +7 -0
  105. package/src/primitives/UiCard/types.ts +10 -0
  106. package/src/primitives/UiCheckbox/UiCheckbox.stories.ts +231 -0
  107. package/src/primitives/UiCheckbox/UiCheckbox.vue +19 -0
  108. package/src/primitives/UiCheckbox/__tests__/UiCheckbox.test.ts +29 -0
  109. package/src/primitives/UiCheckbox/index.ts +2 -0
  110. package/src/primitives/UiCheckbox/types.ts +30 -0
  111. package/src/primitives/UiDrawer/UiDrawer.stories.ts +602 -0
  112. package/src/primitives/UiDrawer/UiDrawer.vue +19 -0
  113. package/src/primitives/UiDrawer/UiDrawerClose.vue +16 -0
  114. package/src/primitives/UiDrawer/UiDrawerContent.vue +29 -0
  115. package/src/primitives/UiDrawer/UiDrawerDescription.vue +16 -0
  116. package/src/primitives/UiDrawer/UiDrawerFooter.vue +16 -0
  117. package/src/primitives/UiDrawer/UiDrawerHeader.vue +16 -0
  118. package/src/primitives/UiDrawer/UiDrawerTitle.vue +16 -0
  119. package/src/primitives/UiDrawer/UiDrawerTrigger.vue +16 -0
  120. package/src/primitives/UiDrawer/__tests__/UiDrawer.test.ts +229 -0
  121. package/src/primitives/UiDrawer/index.ts +8 -0
  122. package/src/primitives/UiDrawer/types.ts +96 -0
  123. package/src/primitives/UiDropdownMenu/UiDropdownMenu.stories.ts +760 -0
  124. package/src/primitives/UiDropdownMenu/UiDropdownMenu.vue +25 -0
  125. package/src/primitives/UiDropdownMenu/UiDropdownMenuCheckboxItem.vue +29 -0
  126. package/src/primitives/UiDropdownMenu/UiDropdownMenuContent.vue +27 -0
  127. package/src/primitives/UiDropdownMenu/UiDropdownMenuGroup.vue +13 -0
  128. package/src/primitives/UiDropdownMenu/UiDropdownMenuItem.vue +26 -0
  129. package/src/primitives/UiDropdownMenu/UiDropdownMenuLabel.vue +18 -0
  130. package/src/primitives/UiDropdownMenu/UiDropdownMenuRadioGroup.vue +20 -0
  131. package/src/primitives/UiDropdownMenu/UiDropdownMenuRadioItem.vue +26 -0
  132. package/src/primitives/UiDropdownMenu/UiDropdownMenuSeparator.vue +11 -0
  133. package/src/primitives/UiDropdownMenu/UiDropdownMenuShortcut.vue +13 -0
  134. package/src/primitives/UiDropdownMenu/UiDropdownMenuSub.vue +26 -0
  135. package/src/primitives/UiDropdownMenu/UiDropdownMenuSubContent.vue +23 -0
  136. package/src/primitives/UiDropdownMenu/UiDropdownMenuSubTrigger.vue +24 -0
  137. package/src/primitives/UiDropdownMenu/UiDropdownMenuTrigger.vue +24 -0
  138. package/src/primitives/UiDropdownMenu/__tests__/UiDropdownMenu.test.ts +557 -0
  139. package/src/primitives/UiDropdownMenu/index.ts +16 -0
  140. package/src/primitives/UiDropdownMenu/types.ts +219 -0
  141. package/src/primitives/UiField/UiField.stories.ts +1496 -0
  142. package/src/primitives/UiField/UiField.vue +18 -0
  143. package/src/primitives/UiField/UiFieldContent.vue +13 -0
  144. package/src/primitives/UiField/UiFieldDescription.vue +13 -0
  145. package/src/primitives/UiField/UiFieldError.vue +20 -0
  146. package/src/primitives/UiField/UiFieldGroup.vue +13 -0
  147. package/src/primitives/UiField/UiFieldLabel.vue +16 -0
  148. package/src/primitives/UiField/UiFieldLegend.vue +13 -0
  149. package/src/primitives/UiField/UiFieldSeparator.vue +13 -0
  150. package/src/primitives/UiField/UiFieldSet.vue +13 -0
  151. package/src/primitives/UiField/UiFieldTitle.vue +13 -0
  152. package/src/primitives/UiField/__tests__/UiFieldError.test.ts +35 -0
  153. package/src/primitives/UiField/index.ts +10 -0
  154. package/src/primitives/UiField/types.ts +47 -0
  155. package/src/primitives/UiIcon/UiIcon.stories.ts +95 -0
  156. package/src/primitives/UiIcon/UiIcon.vue +14 -0
  157. package/src/primitives/UiIcon/__tests__/UiIcon.test.ts +24 -0
  158. package/src/primitives/UiIcon/index.ts +1 -0
  159. package/src/primitives/UiIcon/types.ts +23 -0
  160. package/src/primitives/UiIconButton/UiIconButton.stories.ts +446 -0
  161. package/src/primitives/UiIconButton/UiIconButton.vue +63 -0
  162. package/src/primitives/UiIconButton/__tests__/UiIconButton.test.ts +102 -0
  163. package/src/primitives/UiIconButton/index.ts +2 -0
  164. package/src/primitives/UiIconButton/types.ts +67 -0
  165. package/src/primitives/UiInput/UiInput.stories.ts +193 -0
  166. package/src/primitives/UiInput/UiInput.vue +19 -0
  167. package/src/primitives/UiInput/__tests__/UiInput.test.ts +38 -0
  168. package/src/primitives/UiInput/index.ts +2 -0
  169. package/src/primitives/UiInput/types.ts +31 -0
  170. package/src/primitives/UiPopover/UiPopover.stories.ts +394 -0
  171. package/src/primitives/UiPopover/UiPopover.vue +17 -0
  172. package/src/primitives/UiPopover/UiPopoverContent.vue +27 -0
  173. package/src/primitives/UiPopover/UiPopoverTrigger.vue +16 -0
  174. package/src/primitives/UiPopover/__tests__/UiPopover.test.ts +87 -0
  175. package/src/primitives/UiPopover/index.ts +5 -0
  176. package/src/primitives/UiPopover/types.ts +86 -0
  177. package/src/primitives/UiProgress/UiProgress.stories.ts +92 -0
  178. package/src/primitives/UiProgress/UiProgress.vue +25 -0
  179. package/src/primitives/UiProgress/__tests__/UiProgress.test.ts +46 -0
  180. package/src/primitives/UiProgress/index.ts +2 -0
  181. package/src/primitives/UiProgress/types.ts +16 -0
  182. package/src/primitives/UiRadioGroup/UiRadioGroup.stories.ts +291 -0
  183. package/src/primitives/UiRadioGroup/UiRadioGroup.vue +43 -0
  184. package/src/primitives/UiRadioGroup/UiRadioGroupItem.vue +18 -0
  185. package/src/primitives/UiRadioGroup/__tests__/UiRadioGroup.test.ts +404 -0
  186. package/src/primitives/UiRadioGroup/index.ts +4 -0
  187. package/src/primitives/UiRadioGroup/types.ts +66 -0
  188. package/src/primitives/UiRangeCalendar/UiRangeCalendar.stories.ts +609 -0
  189. package/src/primitives/UiRangeCalendar/UiRangeCalendar.vue +50 -0
  190. package/src/primitives/UiRangeCalendar/__tests__/UiRangeCalendar.test.ts +35 -0
  191. package/src/primitives/UiRangeCalendar/index.ts +13 -0
  192. package/src/primitives/UiRangeCalendar/types.ts +184 -0
  193. package/src/primitives/UiSelect/UiSelect.stories.ts +425 -0
  194. package/src/primitives/UiSelect/UiSelect.vue +47 -0
  195. package/src/primitives/UiSelect/UiSelectContent.vue +30 -0
  196. package/src/primitives/UiSelect/UiSelectGroup.vue +13 -0
  197. package/src/primitives/UiSelect/UiSelectItem.vue +19 -0
  198. package/src/primitives/UiSelect/UiSelectLabel.vue +13 -0
  199. package/src/primitives/UiSelect/UiSelectSeparator.vue +11 -0
  200. package/src/primitives/UiSelect/UiSelectTrigger.vue +30 -0
  201. package/src/primitives/UiSelect/UiSelectValue.vue +18 -0
  202. package/src/primitives/UiSelect/__tests__/UiSelect.test.ts +211 -0
  203. package/src/primitives/UiSelect/__tests__/UiSelectContent.test.ts +30 -0
  204. package/src/primitives/UiSelect/__tests__/UiSelectGroup.test.ts +85 -0
  205. package/src/primitives/UiSelect/__tests__/UiSelectItem.test.ts +79 -0
  206. package/src/primitives/UiSelect/__tests__/UiSelectLabel.test.ts +83 -0
  207. package/src/primitives/UiSelect/__tests__/UiSelectSeparator.test.ts +82 -0
  208. package/src/primitives/UiSelect/__tests__/UiSelectTrigger.test.ts +54 -0
  209. package/src/primitives/UiSelect/__tests__/UiSelectValue.test.ts +39 -0
  210. package/src/primitives/UiSelect/index.ts +10 -0
  211. package/src/primitives/UiSelect/types.ts +93 -0
  212. package/src/primitives/UiSlider/UiSlider.stories.ts +226 -0
  213. package/src/primitives/UiSlider/UiSlider.vue +44 -0
  214. package/src/primitives/UiSlider/__tests__/UiSlider.test.ts +76 -0
  215. package/src/primitives/UiSlider/index.ts +1 -0
  216. package/src/primitives/UiSlider/types.ts +101 -0
  217. package/src/primitives/UiSpinner/UiSpinner.stories.ts +143 -0
  218. package/src/primitives/UiSpinner/UiSpinner.vue +16 -0
  219. package/src/primitives/UiSpinner/__tests__/UiSpinner.test.ts +19 -0
  220. package/src/primitives/UiSpinner/index.ts +2 -0
  221. package/src/primitives/UiSpinner/types.ts +16 -0
  222. package/src/primitives/UiSwitch/UiSwitch.stories.ts +120 -0
  223. package/src/primitives/UiSwitch/UiSwitch.vue +21 -0
  224. package/src/primitives/UiSwitch/__tests__/UiSwitch.test.ts +47 -0
  225. package/src/primitives/UiSwitch/index.ts +2 -0
  226. package/src/primitives/UiSwitch/types.ts +25 -0
  227. package/src/primitives/UiTable/UiTable.stories.ts +505 -0
  228. package/src/primitives/UiTable/UiTable.vue +13 -0
  229. package/src/primitives/UiTable/UiTableBody.vue +13 -0
  230. package/src/primitives/UiTable/UiTableCaption.vue +13 -0
  231. package/src/primitives/UiTable/UiTableCell.vue +16 -0
  232. package/src/primitives/UiTable/UiTableEmpty.vue +18 -0
  233. package/src/primitives/UiTable/UiTableFooter.vue +13 -0
  234. package/src/primitives/UiTable/UiTableHead.vue +18 -0
  235. package/src/primitives/UiTable/UiTableHeader.vue +13 -0
  236. package/src/primitives/UiTable/UiTableRow.vue +18 -0
  237. package/src/primitives/UiTable/__tests__/UiTable.test.ts +19 -0
  238. package/src/primitives/UiTable/__tests__/UiTableBody.test.ts +19 -0
  239. package/src/primitives/UiTable/__tests__/UiTableCaption.test.ts +19 -0
  240. package/src/primitives/UiTable/__tests__/UiTableCell.test.ts +26 -0
  241. package/src/primitives/UiTable/__tests__/UiTableEmpty.test.ts +32 -0
  242. package/src/primitives/UiTable/__tests__/UiTableFooter.test.ts +19 -0
  243. package/src/primitives/UiTable/__tests__/UiTableHead.test.ts +43 -0
  244. package/src/primitives/UiTable/__tests__/UiTableHeader.test.ts +19 -0
  245. package/src/primitives/UiTable/__tests__/UiTableRow.test.ts +32 -0
  246. package/src/primitives/UiTable/index.ts +16 -0
  247. package/src/primitives/UiTable/types.ts +68 -0
  248. package/src/primitives/UiTabs/UiTabs.stories.ts +456 -0
  249. package/src/primitives/UiTabs/UiTabs.vue +31 -0
  250. package/src/primitives/UiTabs/UiTabsContent.vue +16 -0
  251. package/src/primitives/UiTabs/UiTabsList.vue +16 -0
  252. package/src/primitives/UiTabs/UiTabsTrigger.vue +16 -0
  253. package/src/primitives/UiTabs/__tests__/UiTabs.test.ts +122 -0
  254. package/src/primitives/UiTabs/index.ts +6 -0
  255. package/src/primitives/UiTabs/types.ts +68 -0
  256. package/src/primitives/UiTextarea/UiTextarea.stories.ts +107 -0
  257. package/src/primitives/UiTextarea/UiTextarea.vue +19 -0
  258. package/src/primitives/UiTextarea/__tests__/UiTextarea.test.ts +40 -0
  259. package/src/primitives/UiTextarea/index.ts +2 -0
  260. package/src/primitives/UiTextarea/types.ts +30 -0
  261. package/src/primitives/UiTooltip/UiTooltip.stories.ts +550 -0
  262. package/src/primitives/UiTooltip/UiTooltip.vue +42 -0
  263. package/src/primitives/UiTooltip/__tests__/UiTooltip.test.ts +78 -0
  264. package/src/primitives/UiTooltip/index.ts +2 -0
  265. package/src/primitives/UiTooltip/types.ts +53 -0
  266. package/src/primitives/index.ts +33 -0
  267. package/src/primitives/shadcn/accordion/Accordion.vue +15 -0
  268. package/src/primitives/shadcn/accordion/AccordionContent.vue +23 -0
  269. package/src/primitives/shadcn/accordion/AccordionItem.vue +24 -0
  270. package/src/primitives/shadcn/accordion/AccordionTrigger.vue +35 -0
  271. package/src/primitives/shadcn/accordion/index.ts +4 -0
  272. package/src/primitives/shadcn/alert/Alert.vue +17 -0
  273. package/src/primitives/shadcn/alert/AlertDescription.vue +22 -0
  274. package/src/primitives/shadcn/alert/AlertTitle.vue +17 -0
  275. package/src/primitives/shadcn/alert/index.ts +24 -0
  276. package/src/primitives/shadcn/alert-dialog/AlertDialog.vue +15 -0
  277. package/src/primitives/shadcn/alert-dialog/AlertDialogAction.vue +18 -0
  278. package/src/primitives/shadcn/alert-dialog/AlertDialogCancel.vue +21 -0
  279. package/src/primitives/shadcn/alert-dialog/AlertDialogContent.vue +44 -0
  280. package/src/primitives/shadcn/alert-dialog/AlertDialogDescription.vue +21 -0
  281. package/src/primitives/shadcn/alert-dialog/AlertDialogFooter.vue +17 -0
  282. package/src/primitives/shadcn/alert-dialog/AlertDialogHeader.vue +17 -0
  283. package/src/primitives/shadcn/alert-dialog/AlertDialogTitle.vue +21 -0
  284. package/src/primitives/shadcn/alert-dialog/AlertDialogTrigger.vue +12 -0
  285. package/src/primitives/shadcn/alert-dialog/index.ts +9 -0
  286. package/src/primitives/shadcn/avatar/Avatar.vue +18 -0
  287. package/src/primitives/shadcn/avatar/AvatarFallback.vue +21 -0
  288. package/src/primitives/shadcn/avatar/AvatarImage.vue +12 -0
  289. package/src/primitives/shadcn/avatar/index.ts +3 -0
  290. package/src/primitives/shadcn/badge/Badge.vue +28 -0
  291. package/src/primitives/shadcn/badge/index.ts +24 -0
  292. package/src/primitives/shadcn/button/Button.vue +29 -0
  293. package/src/primitives/shadcn/button/index.ts +36 -0
  294. package/src/primitives/shadcn/calendar/Calendar.vue +206 -0
  295. package/src/primitives/shadcn/calendar/CalendarCell.vue +28 -0
  296. package/src/primitives/shadcn/calendar/CalendarCellTrigger.vue +44 -0
  297. package/src/primitives/shadcn/calendar/CalendarGrid.vue +23 -0
  298. package/src/primitives/shadcn/calendar/CalendarGridBody.vue +12 -0
  299. package/src/primitives/shadcn/calendar/CalendarGridHead.vue +13 -0
  300. package/src/primitives/shadcn/calendar/CalendarGridRow.vue +23 -0
  301. package/src/primitives/shadcn/calendar/CalendarHeadCell.vue +23 -0
  302. package/src/primitives/shadcn/calendar/CalendarHeader.vue +23 -0
  303. package/src/primitives/shadcn/calendar/CalendarHeading.vue +30 -0
  304. package/src/primitives/shadcn/calendar/CalendarNextButton.vue +33 -0
  305. package/src/primitives/shadcn/calendar/CalendarPrevButton.vue +33 -0
  306. package/src/primitives/shadcn/calendar/index.ts +14 -0
  307. package/src/primitives/shadcn/card/Card.vue +22 -0
  308. package/src/primitives/shadcn/card/CardAction.vue +17 -0
  309. package/src/primitives/shadcn/card/CardContent.vue +14 -0
  310. package/src/primitives/shadcn/card/CardDescription.vue +14 -0
  311. package/src/primitives/shadcn/card/CardFooter.vue +14 -0
  312. package/src/primitives/shadcn/card/CardHeader.vue +22 -0
  313. package/src/primitives/shadcn/card/CardTitle.vue +14 -0
  314. package/src/primitives/shadcn/card/index.ts +7 -0
  315. package/src/primitives/shadcn/checkbox/Checkbox.vue +38 -0
  316. package/src/primitives/shadcn/checkbox/index.ts +1 -0
  317. package/src/primitives/shadcn/drawer/Drawer.vue +15 -0
  318. package/src/primitives/shadcn/drawer/DrawerClose.vue +12 -0
  319. package/src/primitives/shadcn/drawer/DrawerContent.vue +52 -0
  320. package/src/primitives/shadcn/drawer/DrawerDescription.vue +20 -0
  321. package/src/primitives/shadcn/drawer/DrawerFooter.vue +17 -0
  322. package/src/primitives/shadcn/drawer/DrawerHeader.vue +17 -0
  323. package/src/primitives/shadcn/drawer/DrawerTitle.vue +20 -0
  324. package/src/primitives/shadcn/drawer/DrawerTrigger.vue +12 -0
  325. package/src/primitives/shadcn/drawer/index.ts +8 -0
  326. package/src/primitives/shadcn/dropdown-menu/DropdownMenu.vue +15 -0
  327. package/src/primitives/shadcn/dropdown-menu/DropdownMenuCheckboxItem.vue +41 -0
  328. package/src/primitives/shadcn/dropdown-menu/DropdownMenuContent.vue +40 -0
  329. package/src/primitives/shadcn/dropdown-menu/DropdownMenuGroup.vue +12 -0
  330. package/src/primitives/shadcn/dropdown-menu/DropdownMenuItem.vue +41 -0
  331. package/src/primitives/shadcn/dropdown-menu/DropdownMenuLabel.vue +25 -0
  332. package/src/primitives/shadcn/dropdown-menu/DropdownMenuRadioGroup.vue +15 -0
  333. package/src/primitives/shadcn/dropdown-menu/DropdownMenuRadioItem.vue +38 -0
  334. package/src/primitives/shadcn/dropdown-menu/DropdownMenuSeparator.vue +23 -0
  335. package/src/primitives/shadcn/dropdown-menu/DropdownMenuShortcut.vue +17 -0
  336. package/src/primitives/shadcn/dropdown-menu/DropdownMenuSub.vue +15 -0
  337. package/src/primitives/shadcn/dropdown-menu/DropdownMenuSubContent.vue +29 -0
  338. package/src/primitives/shadcn/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
  339. package/src/primitives/shadcn/dropdown-menu/DropdownMenuTrigger.vue +14 -0
  340. package/src/primitives/shadcn/dropdown-menu/index.ts +16 -0
  341. package/src/primitives/shadcn/field/Field.vue +22 -0
  342. package/src/primitives/shadcn/field/FieldContent.vue +17 -0
  343. package/src/primitives/shadcn/field/FieldDescription.vue +24 -0
  344. package/src/primitives/shadcn/field/FieldError.vue +69 -0
  345. package/src/primitives/shadcn/field/FieldGroup.vue +22 -0
  346. package/src/primitives/shadcn/field/FieldLabel.vue +28 -0
  347. package/src/primitives/shadcn/field/FieldLegend.vue +26 -0
  348. package/src/primitives/shadcn/field/FieldSeparator.vue +28 -0
  349. package/src/primitives/shadcn/field/FieldSet.vue +23 -0
  350. package/src/primitives/shadcn/field/FieldTitle.vue +22 -0
  351. package/src/primitives/shadcn/field/index.ts +39 -0
  352. package/src/primitives/shadcn/icon/Icon.vue +38 -0
  353. package/src/primitives/shadcn/icon/index.ts +1 -0
  354. package/src/primitives/shadcn/index.ts +3 -0
  355. package/src/primitives/shadcn/input/Input.vue +35 -0
  356. package/src/primitives/shadcn/input/index.ts +1 -0
  357. package/src/primitives/shadcn/label/Label.vue +28 -0
  358. package/src/primitives/shadcn/label/index.ts +1 -0
  359. package/src/primitives/shadcn/native-select/NativeSelect.vue +56 -0
  360. package/src/primitives/shadcn/native-select/NativeSelectOptGroup.vue +18 -0
  361. package/src/primitives/shadcn/native-select/NativeSelectOption.vue +18 -0
  362. package/src/primitives/shadcn/native-select/index.ts +3 -0
  363. package/src/primitives/shadcn/popover/Popover.vue +19 -0
  364. package/src/primitives/shadcn/popover/PopoverContent.vue +41 -0
  365. package/src/primitives/shadcn/popover/PopoverTrigger.vue +11 -0
  366. package/src/primitives/shadcn/popover/index.ts +4 -0
  367. package/src/primitives/shadcn/progress/Progress.vue +30 -0
  368. package/src/primitives/shadcn/progress/index.ts +1 -0
  369. package/src/primitives/shadcn/radio-group/RadioGroup.vue +25 -0
  370. package/src/primitives/shadcn/radio-group/RadioGroupItem.vue +38 -0
  371. package/src/primitives/shadcn/radio-group/index.ts +2 -0
  372. package/src/primitives/shadcn/range-calendar/RangeCalendar.vue +73 -0
  373. package/src/primitives/shadcn/range-calendar/RangeCalendarCell.vue +28 -0
  374. package/src/primitives/shadcn/range-calendar/RangeCalendarCellTrigger.vue +46 -0
  375. package/src/primitives/shadcn/range-calendar/RangeCalendarGrid.vue +23 -0
  376. package/src/primitives/shadcn/range-calendar/RangeCalendarGridBody.vue +12 -0
  377. package/src/primitives/shadcn/range-calendar/RangeCalendarGridHead.vue +12 -0
  378. package/src/primitives/shadcn/range-calendar/RangeCalendarGridRow.vue +23 -0
  379. package/src/primitives/shadcn/range-calendar/RangeCalendarHeadCell.vue +23 -0
  380. package/src/primitives/shadcn/range-calendar/RangeCalendarHeader.vue +23 -0
  381. package/src/primitives/shadcn/range-calendar/RangeCalendarHeading.vue +30 -0
  382. package/src/primitives/shadcn/range-calendar/RangeCalendarNextButton.vue +34 -0
  383. package/src/primitives/shadcn/range-calendar/RangeCalendarPrevButton.vue +34 -0
  384. package/src/primitives/shadcn/range-calendar/index.ts +12 -0
  385. package/src/primitives/shadcn/select/Select.vue +15 -0
  386. package/src/primitives/shadcn/select/SelectContent.vue +55 -0
  387. package/src/primitives/shadcn/select/SelectGroup.vue +12 -0
  388. package/src/primitives/shadcn/select/SelectItem.vue +39 -0
  389. package/src/primitives/shadcn/select/SelectItemText.vue +12 -0
  390. package/src/primitives/shadcn/select/SelectLabel.vue +17 -0
  391. package/src/primitives/shadcn/select/SelectScrollDownButton.vue +26 -0
  392. package/src/primitives/shadcn/select/SelectScrollUpButton.vue +26 -0
  393. package/src/primitives/shadcn/select/SelectSeparator.vue +19 -0
  394. package/src/primitives/shadcn/select/SelectTrigger.vue +37 -0
  395. package/src/primitives/shadcn/select/SelectValue.vue +12 -0
  396. package/src/primitives/shadcn/select/index.ts +11 -0
  397. package/src/primitives/shadcn/separator/Separator.vue +27 -0
  398. package/src/primitives/shadcn/separator/index.ts +1 -0
  399. package/src/primitives/shadcn/slider/Slider.vue +45 -0
  400. package/src/primitives/shadcn/slider/index.ts +1 -0
  401. package/src/primitives/shadcn/spinner/Spinner.vue +18 -0
  402. package/src/primitives/shadcn/spinner/index.ts +1 -0
  403. package/src/primitives/shadcn/switch/Switch.vue +40 -0
  404. package/src/primitives/shadcn/switch/index.ts +1 -0
  405. package/src/primitives/shadcn/table/Table.vue +16 -0
  406. package/src/primitives/shadcn/table/TableBody.vue +14 -0
  407. package/src/primitives/shadcn/table/TableCaption.vue +14 -0
  408. package/src/primitives/shadcn/table/TableCell.vue +26 -0
  409. package/src/primitives/shadcn/table/TableEmpty.vue +29 -0
  410. package/src/primitives/shadcn/table/TableFooter.vue +17 -0
  411. package/src/primitives/shadcn/table/TableHead.vue +28 -0
  412. package/src/primitives/shadcn/table/TableHeader.vue +14 -0
  413. package/src/primitives/shadcn/table/TableRow.vue +21 -0
  414. package/src/primitives/shadcn/table/index.ts +9 -0
  415. package/src/primitives/shadcn/table/utils.ts +8 -0
  416. package/src/primitives/shadcn/tabs/Tabs.vue +24 -0
  417. package/src/primitives/shadcn/tabs/TabsContent.vue +21 -0
  418. package/src/primitives/shadcn/tabs/TabsList.vue +26 -0
  419. package/src/primitives/shadcn/tabs/TabsTrigger.vue +28 -0
  420. package/src/primitives/shadcn/tabs/index.ts +4 -0
  421. package/src/primitives/shadcn/textarea/Textarea.vue +33 -0
  422. package/src/primitives/shadcn/textarea/index.ts +1 -0
  423. package/src/primitives/shadcn/tooltip/Tooltip.vue +15 -0
  424. package/src/primitives/shadcn/tooltip/TooltipContent.vue +40 -0
  425. package/src/primitives/shadcn/tooltip/TooltipProvider.vue +12 -0
  426. package/src/primitives/shadcn/tooltip/TooltipTrigger.vue +12 -0
  427. package/src/primitives/shadcn/tooltip/index.ts +4 -0
  428. package/src/styles/global.css +1 -0
  429. package/src/templates/UiTemplatePlaceholder/UiTemplatePlaceholder.vue +9 -0
  430. package/src/templates/UiTemplatePlaceholder/index.ts +1 -0
  431. package/src/templates/UiTemplatePlaceholder/types.ts +8 -0
  432. package/src/templates/index.ts +6 -0
@@ -0,0 +1,1575 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import type { ColumnDef } from '@tanstack/vue-table';
3
+ import { computed, h, ref } from 'vue';
4
+ import { Ban, MoreHorizontal } from 'lucide-vue-next';
5
+ import {
6
+ UiDataTable,
7
+ UiDataTableColumnHeader,
8
+ UiDataTablePagination,
9
+ UiDataTableToolbar,
10
+ } from './index';
11
+ import { UiCheckbox } from '@/primitives/UiCheckbox';
12
+ import { UiButton } from '@/primitives/UiButton';
13
+ import {
14
+ UiDropdownMenu,
15
+ UiDropdownMenuContent,
16
+ UiDropdownMenuItem,
17
+ UiDropdownMenuLabel,
18
+ UiDropdownMenuSeparator,
19
+ UiDropdownMenuTrigger,
20
+ } from '@/primitives/UiDropdownMenu';
21
+ import { payments, paymentsMedium, datasetOptions, type Payment } from './UiDataTable.mock';
22
+
23
+ // Basic columns
24
+ const basicColumns: ColumnDef<Payment>[] = [
25
+ {
26
+ accessorKey: 'status',
27
+ header: 'Status',
28
+ cell: ({ row }) => h('div', { class: 'capitalize' }, row.getValue('status')),
29
+ size: 120,
30
+ },
31
+ {
32
+ accessorKey: 'email',
33
+ header: 'Email',
34
+ cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
35
+ size: 250,
36
+ },
37
+ {
38
+ accessorKey: 'amount',
39
+ header: () => h('div', { class: 'text-right' }, 'Amount'),
40
+ cell: ({ row }) => {
41
+ const amount = Number.parseFloat(row.getValue('amount'));
42
+ const formatted = new Intl.NumberFormat('de-DE', {
43
+ style: 'currency',
44
+ currency: 'EUR',
45
+ }).format(amount);
46
+ return h('div', { class: 'text-right font-medium' }, formatted);
47
+ },
48
+ size: 120,
49
+ },
50
+ ];
51
+
52
+ // Sortable columns using UiDataTableColumnHeader
53
+ const sortableColumns: ColumnDef<Payment>[] = [
54
+ {
55
+ accessorKey: 'status',
56
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
57
+ cell: ({ row }) => h('div', { class: 'capitalize' }, row.getValue('status')),
58
+ size: 120,
59
+ },
60
+ {
61
+ accessorKey: 'email',
62
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
63
+ cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
64
+ size: 250,
65
+ },
66
+ {
67
+ accessorKey: 'amount',
68
+ header: ({ column }) =>
69
+ h(UiDataTableColumnHeader, { column, title: 'Amount', class: 'justify-end' }),
70
+ cell: ({ row }) => {
71
+ const amount = Number.parseFloat(row.getValue('amount'));
72
+ const formatted = new Intl.NumberFormat('de-DE', {
73
+ style: 'currency',
74
+ currency: 'EUR',
75
+ }).format(amount);
76
+ return h('div', { class: 'text-right font-medium' }, formatted);
77
+ },
78
+ size: 120,
79
+ },
80
+ ];
81
+
82
+ // Columns with selection using UiDataTableColumnHeader
83
+ const selectableColumns: ColumnDef<Payment>[] = [
84
+ {
85
+ id: 'select',
86
+ header: ({ table }) =>
87
+ h(UiCheckbox, {
88
+ modelValue:
89
+ table.getIsAllPageRowsSelected() ||
90
+ (table.getIsSomePageRowsSelected() && 'indeterminate'),
91
+ 'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
92
+ ariaLabel: 'Select all',
93
+ name: 'select-all',
94
+ }),
95
+ cell: ({ row }) =>
96
+ h(UiCheckbox, {
97
+ modelValue: row.getIsSelected(),
98
+ 'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
99
+ ariaLabel: 'Select row',
100
+ name: `select-${row.id}`,
101
+ }),
102
+ enableSorting: false,
103
+ enableHiding: false,
104
+ size: 40,
105
+ },
106
+ {
107
+ accessorKey: 'status',
108
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
109
+ cell: ({ row }) => h('div', { class: 'capitalize' }, row.getValue('status')),
110
+ size: 120,
111
+ },
112
+ {
113
+ accessorKey: 'email',
114
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
115
+ cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
116
+ size: 250,
117
+ },
118
+ {
119
+ accessorKey: 'amount',
120
+ header: ({ column }) =>
121
+ h(UiDataTableColumnHeader, { column, title: 'Amount', class: 'justify-end' }),
122
+ cell: ({ row }) => {
123
+ const amount = Number.parseFloat(row.getValue('amount'));
124
+ const formatted = new Intl.NumberFormat('de-DE', {
125
+ style: 'currency',
126
+ currency: 'EUR',
127
+ }).format(amount);
128
+ return h('div', { class: 'text-right font-medium' }, formatted);
129
+ },
130
+ size: 120,
131
+ },
132
+ ];
133
+
134
+ // Column options for controls
135
+ const columnOptions = {
136
+ Basic: basicColumns,
137
+ Sortable: sortableColumns,
138
+ 'With Selection': selectableColumns,
139
+ } as const;
140
+
141
+ const meta = {
142
+ title: 'Compositions/UiDataTable',
143
+ component: UiDataTable,
144
+ tags: ['autodocs'],
145
+ parameters: {
146
+ docs: {
147
+ description: {
148
+ component:
149
+ 'A powerful data table component built with TanStack Table and Vue 3. Supports sorting, pagination, filtering, and row selection. Use `UiDataTable` for the core table, `UiDataTablePagination` for pagination controls, and `UiDataTableToolbar` for search and filters.',
150
+ },
151
+ },
152
+ },
153
+ argTypes: {
154
+ columns: {
155
+ control: 'select',
156
+ options: Object.keys(columnOptions),
157
+ mapping: columnOptions,
158
+ description: 'The column definitions for the table. Select from predefined configurations.',
159
+ },
160
+ data: {
161
+ control: 'select',
162
+ options: Object.keys(datasetOptions),
163
+ mapping: datasetOptions,
164
+ description: 'The data to display in the table. Select from predefined datasets.',
165
+ },
166
+ emptyText: {
167
+ control: 'text',
168
+ description: 'Text to display when there are no results.',
169
+ },
170
+ tableMinHeight: {
171
+ control: 'text',
172
+ description:
173
+ 'Minimum height for the table container. Useful for preventing layout shifts during pagination. The last row will have a visible bottom border even when the container is taller than the content.',
174
+ },
175
+ },
176
+ args: {
177
+ data: 'Large (100 items)',
178
+ columns: 'Sortable',
179
+ },
180
+ } satisfies Meta<typeof UiDataTable>;
181
+
182
+ export default meta;
183
+ type Story = StoryObj<typeof meta>;
184
+
185
+ // Templates for source code display
186
+ const basicTemplateSource = `<script setup lang="ts">
187
+ import { UiDataTable } from '@aleph-alpha/ui-library';
188
+ import type { ColumnDef } from '@tanstack/vue-table';
189
+
190
+ interface Payment {
191
+ id: string;
192
+ amount: number;
193
+ status: string;
194
+ email: string;
195
+ }
196
+
197
+ const data: Payment[] = [
198
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
199
+ { id: '2', amount: 242, status: 'success', email: 'abe45@gmail.com' },
200
+ { id: '3', amount: 837, status: 'processing', email: 'monserrat44@gmail.com' },
201
+ ];
202
+
203
+ const columns: ColumnDef<Payment>[] = [
204
+ { accessorKey: 'status', header: 'Status' },
205
+ { accessorKey: 'email', header: 'Email' },
206
+ { accessorKey: 'amount', header: 'Amount' },
207
+ ];
208
+ </script>
209
+
210
+ <template>
211
+ <UiDataTable :data="data" :columns="columns" />
212
+ </template>`;
213
+
214
+ const withSortingTemplateSource = `<script setup lang="ts">
215
+ import { h } from 'vue';
216
+ import { UiDataTable, UiDataTableColumnHeader } from '@aleph-alpha/ui-library';
217
+ import type { ColumnDef } from '@tanstack/vue-table';
218
+
219
+ interface Payment {
220
+ id: string;
221
+ amount: number;
222
+ status: string;
223
+ email: string;
224
+ }
225
+
226
+ const data: Payment[] = [
227
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
228
+ { id: '2', amount: 242, status: 'success', email: 'abe45@gmail.com' },
229
+ { id: '3', amount: 837, status: 'processing', email: 'monserrat44@gmail.com' },
230
+ ];
231
+
232
+ // Use UiDataTableColumnHeader for sortable columns with dropdown menu
233
+ const columns: ColumnDef<Payment>[] = [
234
+ {
235
+ accessorKey: 'status',
236
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
237
+ size: 120,
238
+ },
239
+ {
240
+ accessorKey: 'email',
241
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
242
+ size: 250,
243
+ },
244
+ {
245
+ accessorKey: 'amount',
246
+ header: ({ column }) => h(UiDataTableColumnHeader, {
247
+ column,
248
+ title: 'Amount',
249
+ class: 'justify-end'
250
+ }),
251
+ cell: ({ row }) => {
252
+ const amount = Number.parseFloat(row.getValue('amount'));
253
+ return new Intl.NumberFormat('de-DE', {
254
+ style: 'currency',
255
+ currency: 'EUR',
256
+ }).format(amount);
257
+ },
258
+ size: 120,
259
+ },
260
+ ];
261
+ </script>
262
+
263
+ <template>
264
+ <UiDataTable :data="data" :columns="columns" />
265
+ </template>`;
266
+
267
+ const withPaginationTemplateSource = `<script setup lang="ts">
268
+ import { h, ref, computed } from 'vue';
269
+ import { UiDataTable, UiDataTableColumnHeader, UiDataTablePagination } from '@aleph-alpha/ui-library';
270
+ import type { ColumnDef } from '@tanstack/vue-table';
271
+
272
+ interface Payment {
273
+ id: string;
274
+ amount: number;
275
+ status: string;
276
+ email: string;
277
+ }
278
+
279
+ const data: Payment[] = [
280
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
281
+ { id: '2', amount: 242, status: 'success', email: 'abe45@gmail.com' },
282
+ // ... more data (pagination works best with 10+ items)
283
+ ];
284
+
285
+ const columns: ColumnDef<Payment>[] = [
286
+ {
287
+ accessorKey: 'status',
288
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
289
+ },
290
+ {
291
+ accessorKey: 'email',
292
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
293
+ },
294
+ {
295
+ accessorKey: 'amount',
296
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Amount' }),
297
+ },
298
+ ];
299
+
300
+ // Access the table instance via ref
301
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
302
+ const table = computed(() => tableRef.value?.table);
303
+ </script>
304
+
305
+ <template>
306
+ <div class="space-y-4">
307
+ <!-- tableMinHeight prevents layout shifts during pagination -->
308
+ <UiDataTable ref="tableRef" :data="data" :columns="columns" table-min-height="500px" />
309
+ <UiDataTablePagination v-if="table" :table="table" />
310
+ </div>
311
+ </template>`;
312
+
313
+ const withToolbarTemplateSource = `<script setup lang="ts">
314
+ import { h, ref, computed } from 'vue';
315
+ import { UiDataTable, UiDataTableColumnHeader, UiDataTableToolbar } from '@aleph-alpha/ui-library';
316
+ import type { ColumnDef } from '@tanstack/vue-table';
317
+
318
+ interface Payment {
319
+ id: string;
320
+ amount: number;
321
+ status: string;
322
+ email: string;
323
+ }
324
+
325
+ const data: Payment[] = [
326
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
327
+ { id: '2', amount: 242, status: 'success', email: 'abe45@gmail.com' },
328
+ { id: '3', amount: 837, status: 'processing', email: 'monserrat44@gmail.com' },
329
+ ];
330
+
331
+ const columns: ColumnDef<Payment>[] = [
332
+ {
333
+ accessorKey: 'status',
334
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
335
+ },
336
+ {
337
+ accessorKey: 'email',
338
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
339
+ },
340
+ {
341
+ accessorKey: 'amount',
342
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Amount' }),
343
+ },
344
+ ];
345
+
346
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
347
+ const table = computed(() => tableRef.value?.table);
348
+ </script>
349
+
350
+ <template>
351
+ <div class="space-y-4">
352
+ <!-- filter-column filters a specific column -->
353
+ <UiDataTableToolbar
354
+ v-if="table"
355
+ :table="table"
356
+ filter-column="email"
357
+ filter-placeholder="Filter emails..."
358
+ />
359
+ <UiDataTable ref="tableRef" :data="data" :columns="columns" />
360
+ </div>
361
+ </template>`;
362
+
363
+ const fullExampleTemplateSource = `<script setup lang="ts">
364
+ import { h, ref, computed } from 'vue';
365
+ import {
366
+ UiDataTable,
367
+ UiDataTableColumnHeader,
368
+ UiDataTableToolbar,
369
+ UiDataTablePagination
370
+ } from '@aleph-alpha/ui-library';
371
+ import { UiCheckbox } from '@aleph-alpha/ui-library';
372
+ import type { ColumnDef } from '@tanstack/vue-table';
373
+
374
+ interface Payment {
375
+ id: string;
376
+ amount: number;
377
+ status: string;
378
+ email: string;
379
+ }
380
+
381
+ const data: Payment[] = [
382
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
383
+ { id: '2', amount: 242, status: 'success', email: 'abe45@gmail.com' },
384
+ { id: '3', amount: 837, status: 'processing', email: 'monserrat44@gmail.com' },
385
+ // ... more data
386
+ ];
387
+
388
+ // Columns with selection checkbox and sortable headers
389
+ const columns: ColumnDef<Payment>[] = [
390
+ {
391
+ id: 'select',
392
+ header: ({ table }) => h(UiCheckbox, {
393
+ modelValue: table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && 'indeterminate'),
394
+ 'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
395
+ ariaLabel: 'Select all',
396
+ name: 'select-all',
397
+ }),
398
+ cell: ({ row }) => h(UiCheckbox, {
399
+ modelValue: row.getIsSelected(),
400
+ 'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
401
+ ariaLabel: 'Select row',
402
+ name: \`select-\${row.id}\`,
403
+ }),
404
+ enableSorting: false,
405
+ enableHiding: false,
406
+ size: 40,
407
+ },
408
+ {
409
+ accessorKey: 'status',
410
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
411
+ size: 120,
412
+ },
413
+ {
414
+ accessorKey: 'email',
415
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
416
+ size: 250,
417
+ },
418
+ {
419
+ accessorKey: 'amount',
420
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Amount' }),
421
+ size: 120,
422
+ },
423
+ ];
424
+
425
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
426
+ const table = computed(() => tableRef.value?.table);
427
+ </script>
428
+
429
+ <template>
430
+ <div class="space-y-4">
431
+ <UiDataTableToolbar
432
+ v-if="table"
433
+ :table="table"
434
+ global-filter
435
+ filter-placeholder="Search..."
436
+ />
437
+ <UiDataTable ref="tableRef" :data="data" :columns="columns" />
438
+ <UiDataTablePagination v-if="table" :table="table" />
439
+ </div>
440
+ </template>`;
441
+
442
+ const withGlobalFilterTemplateSource = `<script setup lang="ts">
443
+ import { h, ref, computed } from 'vue';
444
+ import { UiDataTable, UiDataTableColumnHeader, UiDataTableToolbar } from '@aleph-alpha/ui-library';
445
+ import type { ColumnDef } from '@tanstack/vue-table';
446
+
447
+ interface Payment {
448
+ id: string;
449
+ amount: number;
450
+ status: string;
451
+ email: string;
452
+ }
453
+
454
+ const data: Payment[] = [
455
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
456
+ { id: '2', amount: 242, status: 'success', email: 'abe45@gmail.com' },
457
+ { id: '3', amount: 837, status: 'processing', email: 'monserrat44@gmail.com' },
458
+ ];
459
+
460
+ const columns: ColumnDef<Payment>[] = [
461
+ {
462
+ accessorKey: 'status',
463
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
464
+ size: 120,
465
+ },
466
+ {
467
+ accessorKey: 'email',
468
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
469
+ size: 250,
470
+ },
471
+ {
472
+ accessorKey: 'amount',
473
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Amount' }),
474
+ size: 120,
475
+ },
476
+ ];
477
+
478
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
479
+ const table = computed(() => tableRef.value?.table);
480
+ </script>
481
+
482
+ <template>
483
+ <div class="space-y-4">
484
+ <!-- global-filter searches across ALL columns -->
485
+ <UiDataTableToolbar
486
+ v-if="table"
487
+ :table="table"
488
+ global-filter
489
+ filter-placeholder="Search all columns..."
490
+ />
491
+ <UiDataTable ref="tableRef" :data="data" :columns="columns" />
492
+ </div>
493
+ </template>`;
494
+
495
+ const withRowSelectionTemplateSource = `<script setup lang="ts">
496
+ import { h, ref, computed } from 'vue';
497
+ import { UiDataTable, UiDataTableColumnHeader, UiDataTablePagination } from '@aleph-alpha/ui-library';
498
+ import { UiCheckbox } from '@aleph-alpha/ui-library';
499
+ import type { ColumnDef } from '@tanstack/vue-table';
500
+
501
+ interface Payment {
502
+ id: string;
503
+ amount: number;
504
+ status: string;
505
+ email: string;
506
+ }
507
+
508
+ const data: Payment[] = [
509
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
510
+ { id: '2', amount: 242, status: 'success', email: 'abe45@gmail.com' },
511
+ { id: '3', amount: 837, status: 'processing', email: 'monserrat44@gmail.com' },
512
+ ];
513
+
514
+ // Add a selection column using UiCheckbox
515
+ const columns: ColumnDef<Payment>[] = [
516
+ {
517
+ id: 'select',
518
+ header: ({ table }) => h(UiCheckbox, {
519
+ modelValue: table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && 'indeterminate'),
520
+ 'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
521
+ ariaLabel: 'Select all',
522
+ name: 'select-all',
523
+ }),
524
+ cell: ({ row }) => h(UiCheckbox, {
525
+ modelValue: row.getIsSelected(),
526
+ 'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
527
+ ariaLabel: 'Select row',
528
+ name: \`select-\${row.id}\`,
529
+ }),
530
+ enableSorting: false,
531
+ enableHiding: false,
532
+ size: 40,
533
+ },
534
+ {
535
+ accessorKey: 'status',
536
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
537
+ size: 120,
538
+ },
539
+ {
540
+ accessorKey: 'email',
541
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
542
+ size: 250,
543
+ },
544
+ {
545
+ accessorKey: 'amount',
546
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Amount' }),
547
+ size: 120,
548
+ },
549
+ ];
550
+
551
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
552
+ const table = computed(() => tableRef.value?.table);
553
+ </script>
554
+
555
+ <template>
556
+ <div class="space-y-4">
557
+ <UiDataTable ref="tableRef" :data="data" :columns="columns" />
558
+ <!-- Pagination shows selection count -->
559
+ <UiDataTablePagination v-if="table" :table="table" />
560
+ </div>
561
+ </template>`;
562
+
563
+ const customEmptyStateTemplateSource = `<script setup lang="ts">
564
+ import { UiDataTable } from '@aleph-alpha/ui-library';
565
+ import type { ColumnDef } from '@tanstack/vue-table';
566
+
567
+ interface Payment {
568
+ id: string;
569
+ amount: number;
570
+ status: string;
571
+ email: string;
572
+ }
573
+
574
+ const data: Payment[] = []; // Empty data
575
+
576
+ const columns: ColumnDef<Payment>[] = [
577
+ { accessorKey: 'status', header: 'Status' },
578
+ { accessorKey: 'email', header: 'Email' },
579
+ { accessorKey: 'amount', header: 'Amount' },
580
+ ];
581
+ </script>
582
+
583
+ <template>
584
+ <!-- Use empty-text prop to customize the empty state message -->
585
+ <UiDataTable
586
+ :data="data"
587
+ :columns="columns"
588
+ empty-text="No payments found."
589
+ />
590
+ </template>`;
591
+
592
+ const withPageSizeTemplateSource = `<script setup lang="ts">
593
+ import { h, ref, computed } from 'vue';
594
+ import { UiDataTable, UiDataTableColumnHeader, UiDataTablePagination } from '@aleph-alpha/ui-library';
595
+ import type { ColumnDef } from '@tanstack/vue-table';
596
+
597
+ interface Payment {
598
+ id: string;
599
+ amount: number;
600
+ status: string;
601
+ email: string;
602
+ }
603
+
604
+ const data: Payment[] = [
605
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
606
+ { id: '2', amount: 242, status: 'success', email: 'abe45@gmail.com' },
607
+ // ... more data
608
+ ];
609
+
610
+ const columns: ColumnDef<Payment>[] = [
611
+ {
612
+ accessorKey: 'status',
613
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
614
+ size: 120,
615
+ },
616
+ {
617
+ accessorKey: 'email',
618
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
619
+ size: 250,
620
+ },
621
+ {
622
+ accessorKey: 'amount',
623
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Amount' }),
624
+ size: 120,
625
+ },
626
+ ];
627
+
628
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
629
+ const table = computed(() => tableRef.value?.table);
630
+ </script>
631
+
632
+ <template>
633
+ <div class="space-y-4">
634
+ <UiDataTable ref="tableRef" :data="data" :columns="columns" />
635
+ <!-- Customize page sizes and hide selection count -->
636
+ <UiDataTablePagination
637
+ v-if="table"
638
+ :table="table"
639
+ :page-size-options="[5, 10, 20, 50]"
640
+ :show-selection="false"
641
+ />
642
+ </div>
643
+ </template>`;
644
+
645
+ /**
646
+ * Basic table with minimal configuration.
647
+ *
648
+ * The simplest way to use UiDataTable - just pass `data` and `columns`.
649
+ * Column headers are plain text, no sorting or filtering.
650
+ */
651
+ export const Basic: Story = {
652
+ args: {
653
+ data: payments,
654
+ columns: basicColumns,
655
+ },
656
+ parameters: {
657
+ docs: {
658
+ source: {
659
+ code: basicTemplateSource,
660
+ },
661
+ },
662
+ },
663
+ render: (args) => ({
664
+ components: { UiDataTable },
665
+ setup() {
666
+ return { args };
667
+ },
668
+ template: '<UiDataTable v-bind="args" />',
669
+ }),
670
+ };
671
+
672
+ /**
673
+ * Table with sortable columns using UiDataTableColumnHeader.
674
+ *
675
+ * Use `UiDataTableColumnHeader` in your column definitions to enable:
676
+ * - Click to sort (ascending/descending)
677
+ * - Dropdown menu with sort options and hide column
678
+ *
679
+ * This is the recommended approach for sortable headers.
680
+ */
681
+ export const WithSorting: Story = {
682
+ args: {
683
+ data: payments,
684
+ columns: sortableColumns,
685
+ },
686
+ parameters: {
687
+ docs: {
688
+ source: {
689
+ code: withSortingTemplateSource,
690
+ },
691
+ },
692
+ },
693
+ render: (args) => ({
694
+ components: { UiDataTable },
695
+ setup() {
696
+ return { args };
697
+ },
698
+ template: '<UiDataTable v-bind="args" />',
699
+ }),
700
+ };
701
+
702
+ /**
703
+ * Table with pagination controls.
704
+ *
705
+ * Use `UiDataTablePagination` when displaying more than 10 rows.
706
+ * Shows navigation buttons (first, prev, next, last), page info, and rows per page selector.
707
+ *
708
+ * The `tableMinHeight` prop prevents layout shifts during pagination by ensuring
709
+ * the table container maintains a minimum height. The last row has a visible
710
+ * bottom border (Material UI-inspired behavior).
711
+ *
712
+ * Access the table instance via ref to pass to pagination component.
713
+ */
714
+ export const WithPagination: Story = {
715
+ args: {
716
+ data: payments,
717
+ columns: sortableColumns,
718
+ tableMinHeight: '500px',
719
+ },
720
+ parameters: {
721
+ docs: {
722
+ source: {
723
+ code: withPaginationTemplateSource,
724
+ },
725
+ },
726
+ },
727
+ render: (args) => ({
728
+ components: { UiDataTable, UiDataTablePagination },
729
+ setup() {
730
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
731
+ const table = computed(() => tableRef.value?.table);
732
+ return { args, tableRef, table };
733
+ },
734
+ template: `
735
+ <div class="space-y-4">
736
+ <UiDataTable ref="tableRef" v-bind="args" />
737
+ <UiDataTablePagination v-if="table" :table="table" />
738
+ </div>
739
+ `,
740
+ }),
741
+ };
742
+
743
+ /**
744
+ * Table with column-specific filtering.
745
+ *
746
+ * Use `UiDataTableToolbar` with `filter-column` prop to filter a specific column.
747
+ */
748
+ export const WithToolbar: Story = {
749
+ args: {
750
+ data: payments,
751
+ columns: sortableColumns,
752
+ },
753
+ parameters: {
754
+ docs: {
755
+ source: {
756
+ code: withToolbarTemplateSource,
757
+ },
758
+ },
759
+ },
760
+ render: (args) => ({
761
+ components: { UiDataTable, UiDataTableToolbar },
762
+ setup() {
763
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
764
+ const table = computed(() => tableRef.value?.table);
765
+ return { args, tableRef, table };
766
+ },
767
+ template: `
768
+ <div class="space-y-4">
769
+ <UiDataTableToolbar
770
+ v-if="table"
771
+ :table="table"
772
+ filter-column="email"
773
+ filter-placeholder="Filter emails..."
774
+ />
775
+ <UiDataTable ref="tableRef" v-bind="args" />
776
+ </div>
777
+ `,
778
+ }),
779
+ };
780
+
781
+ /**
782
+ * Table with global search across all columns.
783
+ *
784
+ * Use `global-filter` prop instead of `filter-column` to search across ALL columns.
785
+ * Matches any value in status, email, amount, or ID.
786
+ *
787
+ * Try typing "success", "gmail", or "316" to see it match different columns.
788
+ */
789
+ export const WithGlobalFilter: Story = {
790
+ args: {
791
+ data: payments,
792
+ columns: sortableColumns,
793
+ },
794
+ parameters: {
795
+ docs: {
796
+ source: {
797
+ code: withGlobalFilterTemplateSource,
798
+ },
799
+ },
800
+ },
801
+ render: (args) => ({
802
+ components: { UiDataTable, UiDataTableToolbar },
803
+ setup() {
804
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
805
+ const table = computed(() => tableRef.value?.table);
806
+ return { args, tableRef, table };
807
+ },
808
+ template: `
809
+ <div class="space-y-4">
810
+ <UiDataTableToolbar
811
+ v-if="table"
812
+ :table="table"
813
+ global-filter
814
+ filter-placeholder="Search all columns..."
815
+ />
816
+ <UiDataTable ref="tableRef" v-bind="args" />
817
+ </div>
818
+ `,
819
+ }),
820
+ };
821
+
822
+ /**
823
+ * Table with row selection checkboxes.
824
+ *
825
+ * Add a selection column using `UiCheckbox` to enable row selection.
826
+ * Use `table.getIsAllPageRowsSelected()` for the header checkbox and
827
+ * `row.getIsSelected()` for row checkboxes.
828
+ *
829
+ * The pagination component shows the selection count automatically.
830
+ */
831
+ export const WithRowSelection: Story = {
832
+ args: {
833
+ data: payments,
834
+ columns: selectableColumns,
835
+ },
836
+ parameters: {
837
+ docs: {
838
+ source: {
839
+ code: withRowSelectionTemplateSource,
840
+ },
841
+ },
842
+ },
843
+ render: (args) => ({
844
+ components: { UiDataTable, UiDataTablePagination },
845
+ setup() {
846
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
847
+ const table = computed(() => tableRef.value?.table);
848
+ return { args, tableRef, table };
849
+ },
850
+ template: `
851
+ <div class="space-y-4">
852
+ <UiDataTable ref="tableRef" v-bind="args" />
853
+ <UiDataTablePagination v-if="table" :table="table" />
854
+ </div>
855
+ `,
856
+ }),
857
+ };
858
+
859
+ /**
860
+ * Complete example combining all DataTable features.
861
+ *
862
+ * This example demonstrates the full power of UiDataTable with:
863
+ * - **Sortable columns** using `UiDataTableColumnHeader`
864
+ * - **Row selection** with checkboxes (header + row)
865
+ * - **Global search** across all columns
866
+ * - **Pagination** with page size selector
867
+ *
868
+ * Use this as a reference for building feature-rich data tables.
869
+ */
870
+ export const FullExample: Story = {
871
+ args: {
872
+ data: payments,
873
+ columns: selectableColumns,
874
+ },
875
+ parameters: {
876
+ docs: {
877
+ source: {
878
+ code: fullExampleTemplateSource,
879
+ },
880
+ },
881
+ },
882
+ render: (args) => ({
883
+ components: { UiDataTable, UiDataTableToolbar, UiDataTablePagination },
884
+ setup() {
885
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
886
+ const table = computed(() => tableRef.value?.table);
887
+ return { args, tableRef, table };
888
+ },
889
+ template: `
890
+ <div class="space-y-4">
891
+ <UiDataTableToolbar
892
+ v-if="table"
893
+ :table="table"
894
+ global-filter
895
+ filter-placeholder="Search..."
896
+ />
897
+ <UiDataTable ref="tableRef" v-bind="args" />
898
+ <UiDataTablePagination v-if="table" :table="table" />
899
+ </div>
900
+ `,
901
+ }),
902
+ };
903
+
904
+ /**
905
+ * Table with custom empty state message using the `empty-text` prop.
906
+ *
907
+ * Use the `empty-text` prop to customize the message shown when there's no data.
908
+ * Defaults to "No results." if not specified.
909
+ */
910
+ export const WithCustomEmptyText: Story = {
911
+ args: {
912
+ data: [],
913
+ columns: basicColumns,
914
+ emptyText: 'No payments found.',
915
+ },
916
+ parameters: {
917
+ docs: {
918
+ source: {
919
+ code: customEmptyStateTemplateSource,
920
+ },
921
+ },
922
+ },
923
+ render: (args) => ({
924
+ components: { UiDataTable },
925
+ setup() {
926
+ return { args };
927
+ },
928
+ template: '<UiDataTable v-bind="args" />',
929
+ }),
930
+ };
931
+
932
+ const withCustomEmptySlotTemplateSource = `<script setup lang="ts">
933
+ import { UiDataTable } from '@aleph-alpha/ui-library';
934
+ import { Ban } from 'lucide-vue-next';
935
+ import type { ColumnDef } from '@tanstack/vue-table';
936
+
937
+ interface Payment {
938
+ id: string;
939
+ amount: number;
940
+ status: string;
941
+ email: string;
942
+ }
943
+
944
+ const data: Payment[] = []; // Empty data
945
+
946
+ const columns: ColumnDef<Payment>[] = [
947
+ { accessorKey: 'status', header: 'Status' },
948
+ { accessorKey: 'email', header: 'Email' },
949
+ { accessorKey: 'amount', header: 'Amount' },
950
+ ];
951
+ </script>
952
+
953
+ <template>
954
+ <UiDataTable :data="data" :columns="columns">
955
+ <template #empty>
956
+ <div class="flex flex-col items-center gap-2 text-muted-foreground">
957
+ <Ban class="h-8 w-8" />
958
+ <p>No data available for this view.</p>
959
+ </div>
960
+ </template>
961
+ </UiDataTable>
962
+ </template>`;
963
+
964
+ export const WithCustomEmptySlot: Story = {
965
+ args: {
966
+ data: [],
967
+ columns: basicColumns,
968
+ },
969
+ parameters: {
970
+ docs: {
971
+ description: {
972
+ story:
973
+ 'Use the `#empty` slot to provide a fully custom empty state, such as an icon or formatted text.',
974
+ },
975
+ source: {
976
+ code: withCustomEmptySlotTemplateSource,
977
+ },
978
+ },
979
+ },
980
+ render: (args) => ({
981
+ components: { UiDataTable, Ban },
982
+ setup() {
983
+ return { args };
984
+ },
985
+ template: `
986
+ <UiDataTable v-bind="args">
987
+ <template #empty>
988
+ <div class="flex flex-col items-center gap-2 text-muted-foreground">
989
+ <Ban class="h-8 w-8" />
990
+ <p>No data available for this view.</p>
991
+ </div>
992
+ </template>
993
+ </UiDataTable>
994
+ `,
995
+ }),
996
+ };
997
+
998
+ // Columns using UiDataTableColumnHeader component
999
+ const columnHeaderColumns: ColumnDef<Payment>[] = [
1000
+ {
1001
+ accessorKey: 'status',
1002
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
1003
+ cell: ({ row }) => h('div', { class: 'capitalize' }, row.getValue('status')),
1004
+ size: 120,
1005
+ },
1006
+ {
1007
+ accessorKey: 'email',
1008
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
1009
+ cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
1010
+ size: 250,
1011
+ },
1012
+ {
1013
+ accessorKey: 'amount',
1014
+ header: ({ column }) =>
1015
+ h(UiDataTableColumnHeader, { column, title: 'Amount', class: 'justify-end' }),
1016
+ cell: ({ row }) => {
1017
+ const amount = Number.parseFloat(row.getValue('amount'));
1018
+ const formatted = new Intl.NumberFormat('de-DE', {
1019
+ style: 'currency',
1020
+ currency: 'EUR',
1021
+ }).format(amount);
1022
+ return h('div', { class: 'text-right font-medium' }, formatted);
1023
+ },
1024
+ size: 120,
1025
+ },
1026
+ ];
1027
+
1028
+ const withColumnHeaderTemplateSource = `<script setup lang="ts">
1029
+ import { h } from 'vue';
1030
+ import { UiDataTable, UiDataTableColumnHeader } from '@aleph-alpha/ui-library';
1031
+ import type { ColumnDef } from '@tanstack/vue-table';
1032
+
1033
+ interface Payment {
1034
+ id: string;
1035
+ amount: number;
1036
+ status: string;
1037
+ email: string;
1038
+ }
1039
+
1040
+ const data: Payment[] = [
1041
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
1042
+ // ... more data
1043
+ ];
1044
+
1045
+ // Using UiDataTableColumnHeader for sortable headers with hide option
1046
+ const columns: ColumnDef<Payment>[] = [
1047
+ {
1048
+ accessorKey: 'status',
1049
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
1050
+ cell: ({ row }) => h('div', { class: 'capitalize' }, row.getValue('status')),
1051
+ size: 120,
1052
+ },
1053
+ {
1054
+ accessorKey: 'email',
1055
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
1056
+ cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
1057
+ size: 250,
1058
+ },
1059
+ {
1060
+ accessorKey: 'amount',
1061
+ header: ({ column }) => h(UiDataTableColumnHeader, {
1062
+ column,
1063
+ title: 'Amount',
1064
+ class: 'justify-end'
1065
+ }),
1066
+ cell: ({ row }) => {
1067
+ const amount = Number.parseFloat(row.getValue('amount'));
1068
+ const formatted = new Intl.NumberFormat('de-DE', {
1069
+ style: 'currency',
1070
+ currency: 'EUR',
1071
+ }).format(amount);
1072
+ return h('div', { class: 'text-right font-medium' }, formatted);
1073
+ },
1074
+ size: 120,
1075
+ },
1076
+ ];
1077
+ </script>
1078
+
1079
+ <template>
1080
+ <UiDataTable :data="data" :columns="columns" />
1081
+ </template>`;
1082
+
1083
+ /**
1084
+ * Table demonstrating sortable columns with UiDataTableColumnHeader.
1085
+ *
1086
+ * The `UiDataTableColumnHeader` provides a dropdown with sort options.
1087
+ *
1088
+ * For right-aligned headers (like amounts), pass `class="justify-end"`.
1089
+ */
1090
+ export const WithColumnHeader: Story = {
1091
+ args: {
1092
+ data: payments,
1093
+ columns: columnHeaderColumns,
1094
+ },
1095
+ parameters: {
1096
+ docs: {
1097
+ source: {
1098
+ code: withColumnHeaderTemplateSource,
1099
+ },
1100
+ },
1101
+ },
1102
+ render: (args) => ({
1103
+ components: { UiDataTable, UiDataTableToolbar },
1104
+ setup() {
1105
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
1106
+ const table = computed(() => tableRef.value?.table);
1107
+ return { args, tableRef, table };
1108
+ },
1109
+ template: `
1110
+ <div class="space-y-4">
1111
+ <UiDataTableToolbar
1112
+ v-if="table"
1113
+ :table="table"
1114
+ global-filter
1115
+ filter-placeholder="Search..."
1116
+ />
1117
+ <UiDataTable ref="tableRef" v-bind="args" />
1118
+ </div>
1119
+ `,
1120
+ }),
1121
+ };
1122
+
1123
+ /**
1124
+ * Table with customized pagination options.
1125
+ *
1126
+ * Customize `UiDataTablePagination` with:
1127
+ * - `page-size-options`: Array of page size options (default: [10, 20, 30, 50])
1128
+ * - `show-page-size`: Show/hide the rows per page selector (default: true)
1129
+ * - `show-selection`: Show/hide the selection count (default: true)
1130
+ */
1131
+ export const WithPageSizeSelector: Story = {
1132
+ args: {
1133
+ data: payments,
1134
+ columns: sortableColumns,
1135
+ },
1136
+ parameters: {
1137
+ docs: {
1138
+ source: {
1139
+ code: withPageSizeTemplateSource,
1140
+ },
1141
+ },
1142
+ },
1143
+ render: (args) => ({
1144
+ components: { UiDataTable, UiDataTablePagination },
1145
+ setup() {
1146
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
1147
+ const table = computed(() => tableRef.value?.table);
1148
+ return { args, tableRef, table };
1149
+ },
1150
+ template: `
1151
+ <div class="space-y-4">
1152
+ <UiDataTable ref="tableRef" v-bind="args" />
1153
+ <UiDataTablePagination
1154
+ v-if="table"
1155
+ :table="table"
1156
+ :page-size-options="[5, 10, 20, 50]"
1157
+ :show-selection="false"
1158
+ />
1159
+ </div>
1160
+ `,
1161
+ }),
1162
+ };
1163
+
1164
+ /**
1165
+ * Table with 14 items, minimum height, and pagination.
1166
+ *
1167
+ * Demonstrates how `tableMinHeight` keeps stable layout when there's content
1168
+ * below the table (pagination). Page 1 shows 10 rows, page 2 shows 4 rows -
1169
+ * the table maintains consistent height regardless of how many rows are visible.
1170
+ */
1171
+ export const WithMinHeightSmallDataset: Story = {
1172
+ args: {
1173
+ // Use 14 items: page 1 = 10 rows, page 2 = 4 rows
1174
+ data: paymentsMedium.slice(0, 14),
1175
+ columns: sortableColumns,
1176
+ tableMinHeight: '500px',
1177
+ },
1178
+ parameters: {
1179
+ docs: {
1180
+ description: {
1181
+ story:
1182
+ 'Demonstrates `tableMinHeight` with 14 items and pagination. Page 1 shows 10 rows, page 2 shows only 4 rows. The table keeps a consistent height, preventing layout shifts when navigating between pages.',
1183
+ },
1184
+ source: {
1185
+ code: `<script setup lang="ts">
1186
+ import { h, ref, computed } from 'vue';
1187
+ import { UiDataTable, UiDataTableColumnHeader, UiDataTablePagination } from '@aleph-alpha/ui-library';
1188
+ import type { ColumnDef } from '@tanstack/vue-table';
1189
+
1190
+ interface Payment { id: string; amount: number; status: string; email: string; }
1191
+
1192
+ // 14 items: page 1 = 10 rows, page 2 = 4 rows
1193
+ const data: Payment[] = [
1194
+ { id: '1', amount: 316, status: 'success', email: 'user1@example.com' },
1195
+ { id: '2', amount: 242, status: 'pending', email: 'user2@example.com' },
1196
+ { id: '3', amount: 837, status: 'processing', email: 'user3@example.com' },
1197
+ { id: '4', amount: 154, status: 'failed', email: 'user4@example.com' },
1198
+ { id: '5', amount: 429, status: 'success', email: 'user5@example.com' },
1199
+ { id: '6', amount: 512, status: 'pending', email: 'user6@example.com' },
1200
+ { id: '7', amount: 623, status: 'processing', email: 'user7@example.com' },
1201
+ { id: '8', amount: 734, status: 'failed', email: 'user8@example.com' },
1202
+ { id: '9', amount: 845, status: 'success', email: 'user9@example.com' },
1203
+ { id: '10', amount: 956, status: 'pending', email: 'user10@example.com' },
1204
+ { id: '11', amount: 167, status: 'processing', email: 'user11@example.com' },
1205
+ { id: '12', amount: 278, status: 'failed', email: 'user12@example.com' },
1206
+ { id: '13', amount: 389, status: 'success', email: 'user13@example.com' },
1207
+ { id: '14', amount: 490, status: 'pending', email: 'user14@example.com' },
1208
+ ];
1209
+
1210
+ const columns: ColumnDef<Payment>[] = [
1211
+ { accessorKey: 'status', header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }) },
1212
+ { accessorKey: 'email', header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }) },
1213
+ { accessorKey: 'amount', header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Amount', class: 'justify-end' }) },
1214
+ ];
1215
+
1216
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
1217
+ const table = computed(() => tableRef.value?.table);
1218
+ </script>
1219
+
1220
+ <template>
1221
+ <div class="space-y-4">
1222
+ <UiDataTable ref="tableRef" :data="data" :columns="columns" table-min-height="500px" />
1223
+ <UiDataTablePagination v-if="table" :table="table" :show-selection="false" />
1224
+ </div>
1225
+ </template>`,
1226
+ },
1227
+ },
1228
+ },
1229
+ render: (args) => ({
1230
+ components: { UiDataTable, UiDataTablePagination },
1231
+ setup() {
1232
+ const tableRef = ref<InstanceType<typeof UiDataTable>>();
1233
+ const table = computed(() => tableRef.value?.table);
1234
+ return { args, tableRef, table };
1235
+ },
1236
+ template: `
1237
+ <div class="space-y-4">
1238
+ <UiDataTable ref="tableRef" v-bind="args" />
1239
+ <UiDataTablePagination v-if="table" :table="table" :show-selection="false" />
1240
+ </div>
1241
+ `,
1242
+ }),
1243
+ };
1244
+
1245
+ // Row Actions columns with dropdown menu
1246
+ const rowActionsColumns: ColumnDef<Payment>[] = [
1247
+ {
1248
+ accessorKey: 'status',
1249
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
1250
+ cell: ({ row }) => h('div', { class: 'capitalize' }, row.getValue('status')),
1251
+ size: 120,
1252
+ },
1253
+ {
1254
+ accessorKey: 'email',
1255
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
1256
+ cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
1257
+ size: 250,
1258
+ },
1259
+ {
1260
+ accessorKey: 'amount',
1261
+ header: ({ column }) =>
1262
+ h(UiDataTableColumnHeader, { column, title: 'Amount', class: 'justify-end' }),
1263
+ cell: ({ row }) => {
1264
+ const amount = Number.parseFloat(row.getValue('amount'));
1265
+ const formatted = new Intl.NumberFormat('de-DE', {
1266
+ style: 'currency',
1267
+ currency: 'EUR',
1268
+ }).format(amount);
1269
+ return h('div', { class: 'text-right font-medium' }, formatted);
1270
+ },
1271
+ size: 120,
1272
+ },
1273
+ {
1274
+ id: 'actions',
1275
+ enableHiding: false,
1276
+ enableSorting: false,
1277
+ header: () => h('span', { class: 'sr-only' }, 'Actions'),
1278
+ cell: ({ row }) => {
1279
+ const payment = row.original;
1280
+ return h(
1281
+ UiDropdownMenu,
1282
+ {},
1283
+ {
1284
+ default: () => [
1285
+ h(UiDropdownMenuTrigger, { asChild: true }, () =>
1286
+ h(UiButton, { variant: 'ghost', size: 'icon', class: 'h-8 w-8' }, () => [
1287
+ h('span', { class: 'sr-only' }, 'Open menu'),
1288
+ h(MoreHorizontal, { class: 'h-4 w-4' }),
1289
+ ]),
1290
+ ),
1291
+ h(UiDropdownMenuContent, { align: 'end' }, () => [
1292
+ h(UiDropdownMenuLabel, {}, () => 'Actions'),
1293
+ h(
1294
+ UiDropdownMenuItem,
1295
+ { onClick: () => navigator.clipboard.writeText(payment.id) },
1296
+ () => 'Copy payment ID',
1297
+ ),
1298
+ h(UiDropdownMenuSeparator),
1299
+ h(UiDropdownMenuItem, {}, () => 'View details'),
1300
+ h(UiDropdownMenuItem, {}, () => 'View customer'),
1301
+ ]),
1302
+ ],
1303
+ },
1304
+ );
1305
+ },
1306
+ size: 50,
1307
+ },
1308
+ ];
1309
+
1310
+ const withRowActionsTemplateSource = `<script setup lang="ts">
1311
+ import { h } from 'vue';
1312
+ import { UiDataTable, UiDataTableColumnHeader } from '@aleph-alpha/ui-library';
1313
+ import {
1314
+ UiDropdownMenu,
1315
+ UiDropdownMenuContent,
1316
+ UiDropdownMenuItem,
1317
+ UiDropdownMenuLabel,
1318
+ UiDropdownMenuSeparator,
1319
+ UiDropdownMenuTrigger,
1320
+ } from '@aleph-alpha/ui-library';
1321
+ import { UiButton } from '@aleph-alpha/ui-library';
1322
+ import type { ColumnDef } from '@tanstack/vue-table';
1323
+
1324
+ interface Payment {
1325
+ id: string;
1326
+ amount: number;
1327
+ status: string;
1328
+ email: string;
1329
+ }
1330
+
1331
+ const data: Payment[] = [
1332
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
1333
+ { id: '2', amount: 242, status: 'success', email: 'abe45@gmail.com' },
1334
+ { id: '3', amount: 837, status: 'processing', email: 'monserrat44@gmail.com' },
1335
+ ];
1336
+
1337
+ // Define a component for row actions (or use inline h() calls)
1338
+ const columns: ColumnDef<Payment>[] = [
1339
+ {
1340
+ accessorKey: 'status',
1341
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Status' }),
1342
+ cell: ({ row }) => h('div', { class: 'capitalize' }, row.getValue('status')),
1343
+ },
1344
+ {
1345
+ accessorKey: 'email',
1346
+ header: ({ column }) => h(UiDataTableColumnHeader, { column, title: 'Email' }),
1347
+ cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
1348
+ },
1349
+ {
1350
+ accessorKey: 'amount',
1351
+ header: ({ column }) => h(UiDataTableColumnHeader, {
1352
+ column,
1353
+ title: 'Amount',
1354
+ class: 'justify-end'
1355
+ }),
1356
+ cell: ({ row }) => {
1357
+ const amount = Number.parseFloat(row.getValue('amount'));
1358
+ const formatted = new Intl.NumberFormat('de-DE', {
1359
+ style: 'currency',
1360
+ currency: 'EUR',
1361
+ }).format(amount);
1362
+ return h('div', { class: 'text-right font-medium' }, formatted);
1363
+ },
1364
+ },
1365
+ {
1366
+ id: 'actions',
1367
+ enableHiding: false,
1368
+ cell: ({ row }) => {
1369
+ const payment = row.original;
1370
+
1371
+ // You can return a Vue component or use h() to render dropdown
1372
+ // For complex actions, create a separate component like DataTableRowActions.vue
1373
+ return h(UiDropdownMenu, {}, {
1374
+ default: () => [
1375
+ h(UiDropdownMenuTrigger, { asChild: true }, () =>
1376
+ h(UiButton, { variant: 'ghost', size: 'sm' }, () => '⋮')
1377
+ ),
1378
+ h(UiDropdownMenuContent, { align: 'end' }, () => [
1379
+ h(UiDropdownMenuLabel, {}, () => 'Actions'),
1380
+ h(UiDropdownMenuItem, {
1381
+ onClick: () => navigator.clipboard.writeText(payment.id)
1382
+ }, () => 'Copy payment ID'),
1383
+ h(UiDropdownMenuSeparator),
1384
+ h(UiDropdownMenuItem, {}, () => 'View details'),
1385
+ h(UiDropdownMenuItem, {}, () => 'View customer'),
1386
+ ]),
1387
+ ],
1388
+ });
1389
+ },
1390
+ },
1391
+ ];
1392
+ </script>
1393
+
1394
+ <template>
1395
+ <UiDataTable :data="data" :columns="columns" />
1396
+ </template>`;
1397
+
1398
+ /**
1399
+ * Table with row actions dropdown menu.
1400
+ *
1401
+ * Add an actions column with a dropdown menu for each row.
1402
+ * Use `row.original` to access the full row data for actions.
1403
+ *
1404
+ * For complex actions, create a separate component (e.g., `DataTableRowActions.vue`)
1405
+ * and import it in your column definition.
1406
+ */
1407
+ export const WithRowActions: Story = {
1408
+ args: {
1409
+ data: payments,
1410
+ columns: rowActionsColumns,
1411
+ },
1412
+ parameters: {
1413
+ docs: {
1414
+ source: {
1415
+ code: withRowActionsTemplateSource,
1416
+ },
1417
+ },
1418
+ },
1419
+ render: (args) => ({
1420
+ components: { UiDataTable },
1421
+ setup() {
1422
+ return { args };
1423
+ },
1424
+ template: '<UiDataTable v-bind="args" />',
1425
+ }),
1426
+ };
1427
+
1428
+ // Cell formatting columns
1429
+ const cellFormattingColumns: ColumnDef<Payment>[] = [
1430
+ {
1431
+ accessorKey: 'status',
1432
+ header: 'Status',
1433
+ cell: ({ row }) => {
1434
+ const status = row.getValue('status') as string;
1435
+ const statusStyles: Record<string, string> = {
1436
+ success: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300',
1437
+ processing: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300',
1438
+ pending: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300',
1439
+ failed: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300',
1440
+ };
1441
+ return h(
1442
+ 'span',
1443
+ {
1444
+ class: `inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize ${statusStyles[status] || ''}`,
1445
+ },
1446
+ status,
1447
+ );
1448
+ },
1449
+ size: 120,
1450
+ },
1451
+ {
1452
+ accessorKey: 'email',
1453
+ header: 'Email',
1454
+ cell: ({ row }) => h('div', { class: 'lowercase font-mono text-sm' }, row.getValue('email')),
1455
+ size: 250,
1456
+ },
1457
+ {
1458
+ accessorKey: 'amount',
1459
+ header: () => h('div', { class: 'text-right' }, 'Amount'),
1460
+ cell: ({ row }) => {
1461
+ const amount = Number.parseFloat(row.getValue('amount'));
1462
+ const formatted = new Intl.NumberFormat('de-DE', {
1463
+ style: 'currency',
1464
+ currency: 'EUR',
1465
+ }).format(amount);
1466
+ return h(
1467
+ 'div',
1468
+ {
1469
+ class: `text-right font-medium ${amount > 500 ? 'text-green-600' : 'text-muted-foreground'}`,
1470
+ },
1471
+ formatted,
1472
+ );
1473
+ },
1474
+ size: 120,
1475
+ },
1476
+ ];
1477
+
1478
+ const withCellFormattingTemplateSource = `<script setup lang="ts">
1479
+ import { h } from 'vue';
1480
+ import { UiDataTable } from '@aleph-alpha/ui-library';
1481
+ import type { ColumnDef } from '@tanstack/vue-table';
1482
+
1483
+ interface Payment {
1484
+ id: string;
1485
+ amount: number;
1486
+ status: 'pending' | 'processing' | 'success' | 'failed';
1487
+ email: string;
1488
+ }
1489
+
1490
+ const data: Payment[] = [
1491
+ { id: '1', amount: 316, status: 'success', email: 'ken99@yahoo.com' },
1492
+ { id: '2', amount: 242, status: 'pending', email: 'abe45@gmail.com' },
1493
+ { id: '3', amount: 837, status: 'processing', email: 'monserrat44@gmail.com' },
1494
+ { id: '4', amount: 721, status: 'failed', email: 'carmella@hotmail.com' },
1495
+ ];
1496
+
1497
+ const columns: ColumnDef<Payment>[] = [
1498
+ {
1499
+ accessorKey: 'status',
1500
+ header: 'Status',
1501
+ // Custom status badge with semantic colors
1502
+ cell: ({ row }) => {
1503
+ const status = row.getValue('status') as string;
1504
+ const statusStyles: Record<string, string> = {
1505
+ success: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300',
1506
+ processing: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300',
1507
+ pending: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300',
1508
+ failed: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300',
1509
+ };
1510
+ return h('span', {
1511
+ class: \`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize \${statusStyles[status] || ''}\`,
1512
+ }, status);
1513
+ },
1514
+ },
1515
+ {
1516
+ accessorKey: 'email',
1517
+ header: 'Email',
1518
+ // Monospace font for emails
1519
+ cell: ({ row }) => h('div', {
1520
+ class: 'lowercase font-mono text-sm'
1521
+ }, row.getValue('email')),
1522
+ },
1523
+ {
1524
+ accessorKey: 'amount',
1525
+ // Right-aligned header
1526
+ header: () => h('div', { class: 'text-right' }, 'Amount'),
1527
+ // Conditional formatting: green for high amounts
1528
+ cell: ({ row }) => {
1529
+ const amount = Number.parseFloat(row.getValue('amount'));
1530
+ const formatted = new Intl.NumberFormat('de-DE', {
1531
+ style: 'currency',
1532
+ currency: 'EUR',
1533
+ }).format(amount);
1534
+ return h('div', {
1535
+ class: \`text-right font-medium \${amount > 500 ? 'text-green-600' : 'text-muted-foreground'}\`,
1536
+ }, formatted);
1537
+ },
1538
+ },
1539
+ ];
1540
+ </script>
1541
+
1542
+ <template>
1543
+ <UiDataTable :data="data" :columns="columns" />
1544
+ </template>`;
1545
+
1546
+ /**
1547
+ * Table with custom cell formatting.
1548
+ *
1549
+ * Use the `cell` property in column definitions to customize how data is displayed:
1550
+ * - **Status badges** with semantic colors (green=success, yellow=processing, etc.)
1551
+ * - **Monospace font** for emails
1552
+ * - **Currency formatting** with conditional colors based on value
1553
+ *
1554
+ * Access cell value with `row.getValue('columnKey')` and return a VNode using `h()`.
1555
+ */
1556
+ export const WithCellFormatting: Story = {
1557
+ args: {
1558
+ data: payments,
1559
+ columns: cellFormattingColumns,
1560
+ },
1561
+ parameters: {
1562
+ docs: {
1563
+ source: {
1564
+ code: withCellFormattingTemplateSource,
1565
+ },
1566
+ },
1567
+ },
1568
+ render: (args) => ({
1569
+ components: { UiDataTable },
1570
+ setup() {
1571
+ return { args };
1572
+ },
1573
+ template: '<UiDataTable v-bind="args" />',
1574
+ }),
1575
+ };