sprintify-ui 0.1.17 → 0.2.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 (395) hide show
  1. package/README.md +228 -229
  2. package/dist/sprintify-ui.es.js +15738 -13373
  3. package/dist/style.css +1 -1
  4. package/dist/tailwindcss/index.js +306 -306
  5. package/dist/types/src/components/BaseActionItem.vue.d.ts +33 -33
  6. package/dist/types/src/components/BaseActionItemButton.vue.d.ts +32 -29
  7. package/dist/types/src/components/BaseAddressForm.vue.d.ts +71 -81
  8. package/dist/types/src/components/BaseAlert.vue.d.ts +51 -51
  9. package/dist/types/src/components/BaseApp.vue.d.ts +9 -9
  10. package/dist/types/src/components/BaseAppDialogs.vue.d.ts +14 -14
  11. package/dist/types/src/components/BaseAppNotifications.vue.d.ts +2 -2
  12. package/dist/types/src/components/BaseAutocomplete.vue.d.ts +237 -232
  13. package/dist/types/src/components/BaseAutocompleteDrawer.vue.d.ts +91 -90
  14. package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +210 -213
  15. package/dist/types/src/components/BaseAvatar.vue.d.ts +52 -52
  16. package/dist/types/src/components/BaseAvatarGroup.vue.d.ts +43 -43
  17. package/dist/types/src/components/BaseBadge.vue.d.ts +53 -50
  18. package/dist/types/src/components/BaseBelongsTo.vue.d.ts +216 -219
  19. package/dist/types/src/components/BaseBoolean.vue.d.ts +15 -15
  20. package/dist/types/src/components/BaseBreadcrumbs.vue.d.ts +14 -14
  21. package/dist/types/src/components/BaseButton.vue.d.ts +23 -23
  22. package/dist/types/src/components/BaseButtonGroup.vue.d.ts +143 -143
  23. package/dist/types/src/components/BaseCard.vue.d.ts +21 -21
  24. package/dist/types/src/components/BaseCardRow.vue.d.ts +16 -16
  25. package/dist/types/src/components/BaseCharacterCounter.vue.d.ts +49 -49
  26. package/dist/types/src/components/BaseClickOutside.vue.d.ts +26 -26
  27. package/dist/types/src/components/BaseClipboard.vue.d.ts +21 -21
  28. package/dist/types/src/components/BaseColor.vue.d.ts +80 -80
  29. package/dist/types/src/components/BaseContainer.vue.d.ts +34 -34
  30. package/dist/types/src/components/BaseCounter.vue.d.ts +42 -42
  31. package/dist/types/src/components/BaseCropper.vue.d.ts +59 -57
  32. package/dist/types/src/components/BaseCropperModal.vue.d.ts +27 -27
  33. package/dist/types/src/components/BaseDataIterator.vue.d.ts +212 -212
  34. package/dist/types/src/components/BaseDataIteratorSectionBox.vue.d.ts +23 -23
  35. package/dist/types/src/components/BaseDataIteratorSectionButton.vue.d.ts +20 -20
  36. package/dist/types/src/components/BaseDataIteratorSectionColumns.vue.d.ts +637 -665
  37. package/dist/types/src/components/BaseDataIteratorSectionModal.vue.d.ts +29 -29
  38. package/dist/types/src/components/BaseDataTable.vue.d.ts +385 -383
  39. package/dist/types/src/components/BaseDataTableRowAction.vue.d.ts +18 -18
  40. package/dist/types/src/components/BaseDatePicker.vue.d.ts +87 -84
  41. package/dist/types/src/components/BaseDateSelect.vue.d.ts +70 -70
  42. package/dist/types/src/components/BaseDescriptionList.vue.d.ts +9 -9
  43. package/dist/types/src/components/BaseDescriptionListItem.vue.d.ts +10 -10
  44. package/dist/types/src/components/BaseDialog.vue.d.ts +60 -60
  45. package/dist/types/src/components/BaseDisplayRelativeTime.vue.d.ts +68 -68
  46. package/dist/types/src/components/BaseDraggable.vue.d.ts +34 -34
  47. package/dist/types/src/components/BaseDropdown.vue.d.ts +65 -62
  48. package/dist/types/src/components/BaseDropdownAutocomplete.vue.d.ts +132 -132
  49. package/dist/types/src/components/BaseField.vue.d.ts +58 -58
  50. package/dist/types/src/components/BaseFieldI18n.vue.d.ts +93 -96
  51. package/dist/types/src/components/BaseFilePicker.vue.d.ts +62 -59
  52. package/dist/types/src/components/BaseFilePickerCrop.vue.d.ts +60 -57
  53. package/dist/types/src/components/BaseFileUploader.vue.d.ts +112 -85
  54. package/dist/types/src/components/BaseForm.vue.d.ts +134 -131
  55. package/dist/types/src/components/BaseHasMany.vue.d.ts +149 -152
  56. package/dist/types/src/components/BaseHeader.vue.d.ts +83 -80
  57. package/dist/types/src/components/BaseIconPicker.vue.d.ts +41 -38
  58. package/dist/types/src/components/BaseInput.vue.d.ts +169 -169
  59. package/dist/types/src/components/BaseInputError.vue.d.ts +9 -9
  60. package/dist/types/src/components/BaseInputLabel.vue.d.ts +43 -43
  61. package/dist/types/src/components/BaseInputPercent.vue.d.ts +133 -133
  62. package/dist/types/src/components/BaseLayoutNotificationDropdown.vue.d.ts +36 -36
  63. package/dist/types/src/components/BaseLayoutNotificationItem.vue.d.ts +16 -16
  64. package/dist/types/src/components/BaseLayoutNotificationItemContent.vue.d.ts +18 -18
  65. package/dist/types/src/components/BaseLayoutSidebar.vue.d.ts +51 -51
  66. package/dist/types/src/components/BaseLayoutSidebarConfigurable.vue.d.ts +87 -87
  67. package/dist/types/src/components/BaseLayoutStacked.vue.d.ts +23 -23
  68. package/dist/types/src/components/BaseLayoutStackedConfigurable.vue.d.ts +78 -78
  69. package/dist/types/src/components/BaseLoadingCover.vue.d.ts +96 -96
  70. package/dist/types/src/components/BaseMediaGallery.vue.d.ts +64 -64
  71. package/dist/types/src/components/BaseMediaGalleryItem.vue.d.ts +45 -45
  72. package/dist/types/src/components/BaseMediaItem.vue.d.ts +27 -27
  73. package/dist/types/src/components/BaseMediaLibrary.vue.d.ts +190 -172
  74. package/dist/types/src/components/BaseMediaList.vue.d.ts +47 -47
  75. package/dist/types/src/components/BaseMediaListItem.vue.d.ts +47 -47
  76. package/dist/types/src/components/BaseMediaPictures.vue.d.ts +55 -55
  77. package/dist/types/src/components/BaseMediaPicturesItem.vue.d.ts +54 -54
  78. package/dist/types/src/components/BaseMediaPreview.vue.d.ts +36 -36
  79. package/dist/types/src/components/BaseMenu.vue.d.ts +68 -68
  80. package/dist/types/src/components/BaseMenuItem.vue.d.ts +61 -61
  81. package/dist/types/src/components/BaseModalCenter.vue.d.ts +80 -79
  82. package/dist/types/src/components/BaseModalSide.vue.d.ts +62 -61
  83. package/dist/types/src/components/BaseNavbar.vue.d.ts +38 -35
  84. package/dist/types/src/components/BaseNavbarItem.vue.d.ts +26 -26
  85. package/dist/types/src/components/BaseNavbarItemContent.vue.d.ts +60 -60
  86. package/dist/types/src/components/BaseNavbarSideItem.vue.d.ts +44 -44
  87. package/dist/types/src/components/BaseNavbarSideItemContent.vue.d.ts +60 -60
  88. package/dist/types/src/components/BaseNumber.vue.d.ts +125 -125
  89. package/dist/types/src/components/BasePagination.vue.d.ts +35 -35
  90. package/dist/types/src/components/BasePanel.vue.d.ts +31 -31
  91. package/dist/types/src/components/BasePassword.vue.d.ts +62 -62
  92. package/dist/types/src/components/BaseProgressCircle.vue.d.ts +37 -37
  93. package/dist/types/src/components/BaseRadioGroup.vue.d.ts +105 -105
  94. package/dist/types/src/components/BaseReadMore.vue.d.ts +21 -21
  95. package/dist/types/src/components/BaseRichText.vue.d.ts +92 -92
  96. package/dist/types/src/components/BaseSelect.vue.d.ts +98 -97
  97. package/dist/types/src/components/BaseShortcut.vue.d.ts +86 -86
  98. package/dist/types/src/components/BaseSideNavigation.vue.d.ts +36 -33
  99. package/dist/types/src/components/BaseSideNavigationItem.vue.d.ts +46 -43
  100. package/dist/types/src/components/BaseSkeleton.vue.d.ts +31 -30
  101. package/dist/types/src/components/BaseStatistic.vue.d.ts +56 -56
  102. package/dist/types/src/components/BaseStepper.vue.d.ts +16 -16
  103. package/dist/types/src/components/BaseStepperItem.vue.d.ts +51 -51
  104. package/dist/types/src/components/BaseSwitch.vue.d.ts +87 -87
  105. package/dist/types/src/components/BaseSystemAlert.vue.d.ts +52 -52
  106. package/dist/types/src/components/BaseTabItem.vue.d.ts +46 -43
  107. package/dist/types/src/components/BaseTable.vue.d.ts +212 -212
  108. package/dist/types/src/components/BaseTableColumn.vue.d.ts +174 -174
  109. package/dist/types/src/components/BaseTabs.vue.d.ts +36 -33
  110. package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +209 -204
  111. package/dist/types/src/components/BaseTagAutocompleteFetch.vue.d.ts +141 -144
  112. package/dist/types/src/components/BaseTextarea.vue.d.ts +98 -98
  113. package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +108 -98
  114. package/dist/types/src/components/BaseTimeline.vue.d.ts +14 -14
  115. package/dist/types/src/components/BaseTimelineItem.vue.d.ts +14 -14
  116. package/dist/types/src/components/SlotComponent.d.ts +43 -43
  117. package/dist/types/src/components/index.d.ts +93 -93
  118. package/dist/types/src/composables/breakpoints.d.ts +27 -27
  119. package/dist/types/src/composables/clickOutside.d.ts +8 -8
  120. package/dist/types/src/composables/field.d.ts +19 -19
  121. package/dist/types/src/composables/hasOptions.d.ts +7 -7
  122. package/dist/types/src/composables/mediaQuery.d.ts +2 -2
  123. package/dist/types/src/composables/modal.d.ts +6 -6
  124. package/dist/types/src/composables/paginatedData.d.ts +7 -7
  125. package/dist/types/src/constants/MyConstants.d.ts +1 -1
  126. package/dist/types/src/constants/index.d.ts +2 -2
  127. package/dist/types/src/i18n/index.d.ts +1 -1
  128. package/dist/types/src/index.d.ts +220 -220
  129. package/dist/types/src/stores/dialogs.d.ts +9 -9
  130. package/dist/types/src/stores/i18n.d.ts +5 -5
  131. package/dist/types/src/stores/notifications.d.ts +10 -10
  132. package/dist/types/src/stores/systemAlerts.d.ts +9 -9
  133. package/dist/types/src/svg/BaseEmptyState.vue.d.ts +2 -2
  134. package/dist/types/src/svg/BaseSpinnerLarge.vue.d.ts +2 -2
  135. package/dist/types/src/svg/BaseSpinnerSmall.vue.d.ts +2 -2
  136. package/dist/types/src/types/Color.d.ts +9 -9
  137. package/dist/types/src/types/Country.d.ts +4 -4
  138. package/dist/types/src/types/ImagePickerResult.d.ts +5 -5
  139. package/dist/types/src/types/Media.d.ts +9 -9
  140. package/dist/types/src/types/Notification.d.ts +8 -8
  141. package/dist/types/src/types/Region.d.ts +5 -5
  142. package/dist/types/src/types/Status.d.ts +5 -5
  143. package/dist/types/src/types/StepperItem.d.ts +7 -7
  144. package/dist/types/src/types/TimelineItem.d.ts +8 -8
  145. package/dist/types/src/types/UploadedFile.d.ts +10 -10
  146. package/dist/types/src/types/User.d.ts +6 -6
  147. package/dist/types/src/types/index.d.ts +218 -218
  148. package/dist/types/src/utils/blob.d.ts +3 -3
  149. package/dist/types/src/utils/colors.d.ts +13 -13
  150. package/dist/types/src/utils/cropper/avatar.d.ts +5 -5
  151. package/dist/types/src/utils/cropper/cover.d.ts +5 -5
  152. package/dist/types/src/utils/cropper/presetInterface.d.ts +7 -7
  153. package/dist/types/src/utils/cropper/presets.d.ts +6 -6
  154. package/dist/types/src/utils/fileSizeFormat.d.ts +1 -1
  155. package/dist/types/src/utils/fileValidations.d.ts +2 -2
  156. package/dist/types/src/utils/index.d.ts +6 -6
  157. package/dist/types/src/utils/resizeImageFromURI.d.ts +1 -1
  158. package/dist/types/src/utils/scrollPreventer.d.ts +3 -3
  159. package/dist/types/src/utils/toHumanList.d.ts +1 -1
  160. package/package.json +138 -139
  161. package/src/assets/form.css +6 -6
  162. package/src/assets/main.css +35 -35
  163. package/src/assets/tailwind.css +2 -2
  164. package/src/components/BaseActionItem.vue +63 -63
  165. package/src/components/BaseActionItemButton.vue +75 -75
  166. package/src/components/BaseAddressForm.stories.js +103 -103
  167. package/src/components/BaseAddressForm.vue +353 -353
  168. package/src/components/BaseAlert.stories.js +52 -52
  169. package/src/components/BaseAlert.vue +152 -152
  170. package/src/components/BaseApp.vue +16 -16
  171. package/src/components/BaseAppDialogs.vue +124 -124
  172. package/src/components/BaseAppNotifications.vue +73 -73
  173. package/src/components/BaseAutocomplete.stories.js +236 -236
  174. package/src/components/BaseAutocomplete.vue +514 -514
  175. package/src/components/BaseAutocompleteDrawer.vue +354 -354
  176. package/src/components/BaseAutocompleteFetch.stories.js +224 -224
  177. package/src/components/BaseAutocompleteFetch.vue +277 -277
  178. package/src/components/BaseAvatar.stories.js +39 -39
  179. package/src/components/BaseAvatar.vue +120 -120
  180. package/src/components/BaseAvatarGroup.stories.js +71 -71
  181. package/src/components/BaseAvatarGroup.vue +148 -148
  182. package/src/components/BaseBadge.stories.js +124 -124
  183. package/src/components/BaseBadge.vue +79 -79
  184. package/src/components/BaseBelongsTo.stories.js +223 -223
  185. package/src/components/BaseBelongsTo.vue +184 -184
  186. package/src/components/BaseBoolean.stories.js +35 -35
  187. package/src/components/BaseBoolean.vue +26 -26
  188. package/src/components/BaseBreadcrumbs.stories.js +45 -45
  189. package/src/components/BaseBreadcrumbs.vue +98 -98
  190. package/src/components/BaseButton.stories.js +88 -88
  191. package/src/components/BaseButton.vue +39 -39
  192. package/src/components/BaseButtonGroup.stories.js +86 -85
  193. package/src/components/BaseButtonGroup.vue +147 -147
  194. package/src/components/BaseCard.stories.js +61 -61
  195. package/src/components/BaseCard.vue +49 -49
  196. package/src/components/BaseCardRow.vue +34 -34
  197. package/src/components/BaseCharacterCounter.stories.js +30 -30
  198. package/src/components/BaseCharacterCounter.vue +61 -61
  199. package/src/components/BaseClickOutside.vue +37 -37
  200. package/src/components/BaseClipboard.stories.js +31 -31
  201. package/src/components/BaseClipboard.vue +96 -96
  202. package/src/components/BaseColor.stories.js +46 -46
  203. package/src/components/BaseColor.vue +151 -151
  204. package/src/components/BaseContainer.stories.js +34 -34
  205. package/src/components/BaseContainer.vue +50 -50
  206. package/src/components/BaseCounter.stories.js +47 -47
  207. package/src/components/BaseCounter.vue +82 -82
  208. package/src/components/BaseCropper.stories.js +113 -113
  209. package/src/components/BaseCropper.vue +446 -446
  210. package/src/components/BaseCropperModal.stories.js +54 -54
  211. package/src/components/BaseCropperModal.vue +140 -140
  212. package/src/components/BaseDataIterator.stories.js +197 -197
  213. package/src/components/BaseDataIterator.vue +784 -777
  214. package/src/components/BaseDataIteratorSectionBox.vue +33 -33
  215. package/src/components/BaseDataIteratorSectionButton.vue +40 -40
  216. package/src/components/BaseDataIteratorSectionColumns.vue +67 -67
  217. package/src/components/BaseDataIteratorSectionModal.vue +41 -41
  218. package/src/components/BaseDataTable.stories.js +341 -341
  219. package/src/components/BaseDataTable.vue +724 -724
  220. package/src/components/BaseDataTableRowAction.vue +28 -28
  221. package/src/components/BaseDatePicker.stories.js +130 -130
  222. package/src/components/BaseDatePicker.vue +374 -374
  223. package/src/components/BaseDateSelect.stories.js +47 -47
  224. package/src/components/BaseDateSelect.vue +209 -209
  225. package/src/components/BaseDescriptionList.stories.js +35 -35
  226. package/src/components/BaseDescriptionList.vue +13 -13
  227. package/src/components/BaseDescriptionListItem.vue +47 -47
  228. package/src/components/BaseDialog.stories.js +51 -51
  229. package/src/components/BaseDialog.vue +119 -119
  230. package/src/components/BaseDisplayRelativeTime.stories.js +59 -59
  231. package/src/components/BaseDisplayRelativeTime.vue +120 -120
  232. package/src/components/BaseDraggable.vue +71 -71
  233. package/src/components/BaseDropdown.stories.js +210 -210
  234. package/src/components/BaseDropdown.vue +269 -269
  235. package/src/components/BaseDropdownAutocomplete.stories.js +187 -187
  236. package/src/components/BaseDropdownAutocomplete.vue +232 -230
  237. package/src/components/BaseField.vue +109 -109
  238. package/src/components/BaseFieldI18n.stories.js +38 -38
  239. package/src/components/BaseFieldI18n.vue +162 -162
  240. package/src/components/BaseFilePicker.stories.js +78 -78
  241. package/src/components/BaseFilePicker.vue +132 -132
  242. package/src/components/BaseFilePickerCrop.stories.js +134 -134
  243. package/src/components/BaseFilePickerCrop.vue +127 -127
  244. package/src/components/BaseFileUploader.stories.js +84 -84
  245. package/src/components/BaseFileUploader.vue +163 -163
  246. package/src/components/BaseForm.stories.js +48 -48
  247. package/src/components/BaseForm.vue +324 -323
  248. package/src/components/BaseHasMany.stories.js +189 -189
  249. package/src/components/BaseHasMany.vue +125 -125
  250. package/src/components/BaseHeader.stories.js +127 -127
  251. package/src/components/BaseHeader.vue +188 -188
  252. package/src/components/BaseIconPicker.stories.js +22 -22
  253. package/src/components/BaseIconPicker.vue +215 -215
  254. package/src/components/BaseInput.stories.js +151 -151
  255. package/src/components/BaseInput.vue +266 -266
  256. package/src/components/BaseInputError.vue +7 -7
  257. package/src/components/BaseInputLabel.stories.js +36 -36
  258. package/src/components/BaseInputLabel.vue +72 -72
  259. package/src/components/BaseInputPercent.stories.js +50 -50
  260. package/src/components/BaseInputPercent.vue +123 -123
  261. package/src/components/BaseLayoutNotificationDropdown.vue +144 -144
  262. package/src/components/BaseLayoutNotificationItem.vue +43 -43
  263. package/src/components/BaseLayoutNotificationItemContent.vue +27 -27
  264. package/src/components/BaseLayoutSidebar.vue +219 -219
  265. package/src/components/BaseLayoutSidebarConfigurable.stories.js +166 -166
  266. package/src/components/BaseLayoutSidebarConfigurable.vue +167 -167
  267. package/src/components/BaseLayoutStacked.vue +52 -52
  268. package/src/components/BaseLayoutStackedConfigurable.stories.js +109 -109
  269. package/src/components/BaseLayoutStackedConfigurable.vue +141 -141
  270. package/src/components/BaseLoadingCover.stories.js +55 -55
  271. package/src/components/BaseLoadingCover.vue +101 -94
  272. package/src/components/BaseMediaGallery.vue +93 -93
  273. package/src/components/BaseMediaGalleryItem.vue +92 -92
  274. package/src/components/BaseMediaItem.stories.js +41 -41
  275. package/src/components/BaseMediaItem.vue +71 -71
  276. package/src/components/BaseMediaLibrary.stories.js +262 -262
  277. package/src/components/BaseMediaLibrary.vue +313 -313
  278. package/src/components/BaseMediaList.vue +68 -68
  279. package/src/components/BaseMediaListItem.vue +172 -172
  280. package/src/components/BaseMediaPictures.vue +64 -64
  281. package/src/components/BaseMediaPicturesItem.vue +94 -94
  282. package/src/components/BaseMediaPreview.stories.js +72 -72
  283. package/src/components/BaseMediaPreview.vue +106 -106
  284. package/src/components/BaseMenu.stories.js +125 -125
  285. package/src/components/BaseMenu.vue +137 -129
  286. package/src/components/BaseMenuItem.vue +107 -107
  287. package/src/components/BaseModalCenter.stories.js +68 -68
  288. package/src/components/BaseModalCenter.vue +131 -125
  289. package/src/components/BaseModalSide.stories.js +55 -55
  290. package/src/components/BaseModalSide.vue +122 -116
  291. package/src/components/BaseNavbar.stories.js +151 -151
  292. package/src/components/BaseNavbar.vue +91 -91
  293. package/src/components/BaseNavbarItem.vue +49 -49
  294. package/src/components/BaseNavbarItemContent.vue +86 -86
  295. package/src/components/BaseNavbarSideItem.vue +111 -111
  296. package/src/components/BaseNavbarSideItemContent.vue +104 -104
  297. package/src/components/BaseNumber.stories.js +66 -66
  298. package/src/components/BaseNumber.vue +366 -366
  299. package/src/components/BasePagination.stories.js +35 -35
  300. package/src/components/BasePagination.vue +266 -266
  301. package/src/components/BasePanel.stories.js +56 -56
  302. package/src/components/BasePanel.vue +39 -39
  303. package/src/components/BasePassword.stories.js +41 -41
  304. package/src/components/BasePassword.vue +88 -88
  305. package/src/components/BaseProgressCircle.stories.js +27 -27
  306. package/src/components/BaseProgressCircle.vue +77 -77
  307. package/src/components/BaseRadioGroup.stories.js +88 -88
  308. package/src/components/BaseRadioGroup.vue +122 -122
  309. package/src/components/BaseReadMore.stories.js +30 -30
  310. package/src/components/BaseReadMore.vue +73 -73
  311. package/src/components/BaseRichText.stories.js +102 -102
  312. package/src/components/BaseRichText.vue +183 -183
  313. package/src/components/BaseSelect.stories.js +118 -118
  314. package/src/components/BaseSelect.vue +220 -220
  315. package/src/components/BaseShortcut.stories.js +102 -102
  316. package/src/components/BaseShortcut.vue +105 -105
  317. package/src/components/BaseSideNavigation.stories.js +80 -80
  318. package/src/components/BaseSideNavigation.vue +29 -29
  319. package/src/components/BaseSideNavigationItem.vue +92 -92
  320. package/src/components/BaseSkeleton.stories.js +36 -36
  321. package/src/components/BaseSkeleton.vue +40 -24
  322. package/src/components/BaseStatistic.stories.js +51 -51
  323. package/src/components/BaseStatistic.vue +98 -98
  324. package/src/components/BaseStepper.stories.js +94 -94
  325. package/src/components/BaseStepper.vue +69 -69
  326. package/src/components/BaseStepperItem.stories.js +65 -65
  327. package/src/components/BaseStepperItem.vue +149 -149
  328. package/src/components/BaseSwitch.stories.js +131 -131
  329. package/src/components/BaseSwitch.vue +209 -209
  330. package/src/components/BaseSystemAlert.stories.js +63 -63
  331. package/src/components/BaseSystemAlert.vue +86 -86
  332. package/src/components/BaseTabItem.vue +93 -93
  333. package/src/components/BaseTable.vue +880 -880
  334. package/src/components/BaseTableColumn.vue +124 -124
  335. package/src/components/BaseTabs.stories.js +85 -85
  336. package/src/components/BaseTabs.vue +73 -73
  337. package/src/components/BaseTagAutocomplete.stories.js +258 -258
  338. package/src/components/BaseTagAutocomplete.vue +429 -428
  339. package/src/components/BaseTagAutocompleteFetch.stories.js +185 -185
  340. package/src/components/BaseTagAutocompleteFetch.vue +206 -206
  341. package/src/components/BaseTextarea.stories.js +43 -43
  342. package/src/components/BaseTextarea.vue +87 -87
  343. package/src/components/BaseTextareaAutoresize.stories.js +87 -58
  344. package/src/components/BaseTextareaAutoresize.vue +146 -140
  345. package/src/components/BaseTimeline.stories.js +53 -53
  346. package/src/components/BaseTimeline.vue +29 -29
  347. package/src/components/BaseTimelineItem.stories.js +78 -78
  348. package/src/components/BaseTimelineItem.vue +79 -79
  349. package/src/components/SlotComponent.ts +37 -37
  350. package/src/components/index.ts +188 -188
  351. package/src/composables/breakpoints.ts +94 -94
  352. package/src/composables/clickOutside.ts +80 -80
  353. package/src/composables/field.ts +117 -117
  354. package/src/composables/hasOptions.ts +68 -68
  355. package/src/composables/mediaQuery.ts +42 -42
  356. package/src/composables/modal.ts +73 -73
  357. package/src/composables/paginatedData.ts +65 -65
  358. package/src/constants/MyConstants.ts +1 -1
  359. package/src/constants/index.ts +5 -5
  360. package/src/env.d.ts +15 -15
  361. package/src/i18n/index.ts +60 -60
  362. package/src/index.ts +111 -111
  363. package/src/lang/en.json +87 -87
  364. package/src/lang/fr.json +87 -87
  365. package/src/stores/dialogs.ts +45 -45
  366. package/src/stores/i18n.ts +14 -14
  367. package/src/stores/notifications.ts +47 -47
  368. package/src/stores/systemAlerts.ts +33 -33
  369. package/src/svg/BaseEmptyState.vue +34 -34
  370. package/src/svg/BaseSpinnerLarge.vue +47 -47
  371. package/src/svg/BaseSpinnerSmall.vue +9 -9
  372. package/src/types/Color.ts +9 -9
  373. package/src/types/Country.ts +4 -4
  374. package/src/types/ImagePickerResult.ts +5 -5
  375. package/src/types/Media.ts +10 -10
  376. package/src/types/Notification.ts +10 -10
  377. package/src/types/Region.ts +5 -5
  378. package/src/types/Status.ts +5 -5
  379. package/src/types/StepperItem.ts +8 -8
  380. package/src/types/TimelineItem.ts +8 -8
  381. package/src/types/UploadedFile.ts +11 -11
  382. package/src/types/User.ts +7 -7
  383. package/src/types/index.ts +267 -267
  384. package/src/utils/blob.ts +30 -30
  385. package/src/utils/colors.ts +200 -200
  386. package/src/utils/cropper/avatar.ts +33 -33
  387. package/src/utils/cropper/cover.ts +41 -41
  388. package/src/utils/cropper/presetInterface.ts +16 -16
  389. package/src/utils/cropper/presets.ts +7 -7
  390. package/src/utils/fileSizeFormat.ts +15 -15
  391. package/src/utils/fileValidations.ts +26 -26
  392. package/src/utils/index.ts +16 -16
  393. package/src/utils/resizeImageFromURI.ts +118 -118
  394. package/src/utils/scrollPreventer.ts +11 -11
  395. package/src/utils/toHumanList.ts +20 -20
@@ -1,777 +1,784 @@
1
- <template>
2
- <div ref="dataIteratorNode">
3
- <div
4
- class="grid w-full max-w-full grid-flow-row gap-4"
5
- :class="{
6
- 'grid-cols-[1fr_300px]': hasSidebar,
7
- }"
8
- >
9
- <div
10
- class="min-w-0"
11
- :class="{ 'col-span-1': !compactLayout, 'col-span-2': compactLayout }"
12
- >
13
- <!-- Header -->
14
- <div class="mb-4 flex space-x-2 empty:mb-0">
15
- <!-- Search bar -->
16
- <div v-if="searchable" class="grow">
17
- <div class="relative h-11">
18
- <div
19
- class="pointer-events-none absolute top-0 left-0 flex h-full items-center justify-center pl-2.5"
20
- >
21
- <BaseIcon
22
- class="h-6 w-6 text-slate-400"
23
- icon="heroicons:magnifying-glass"
24
- />
25
- </div>
26
- <input
27
- ref="searchInput"
28
- v-model="searchQuery"
29
- type="text"
30
- class="h-11 w-full overflow-hidden rounded-md border border-slate-300 bg-white pl-10 pr-9 shadow-sm"
31
- :placeholder="t('sui.autocomplete_placeholder')"
32
- @input="onSearch"
33
- />
34
- <div
35
- v-if="searchQuery"
36
- class="absolute top-0 right-0 flex h-full items-center justify-center p-1"
37
- >
38
- <button
39
- type="button"
40
- class="flex appearance-none items-center rounded p-1 hover:bg-slate-100"
41
- @click="
42
- searchQuery = '';
43
- onSearch('');
44
- "
45
- >
46
- <BaseIcon
47
- class="h-6 w-6 text-slate-500"
48
- icon="heroicons:x-mark"
49
- />
50
- </button>
51
- </div>
52
- </div>
53
- </div>
54
-
55
- <template v-if="compactLayout">
56
- <template
57
- v-for="(section, i) in sectionsInternal"
58
- :key="section.name"
59
- >
60
- <BaseDataIteratorSectionButton
61
- :section="section"
62
- @open="openSection(i)"
63
- >
64
- </BaseDataIteratorSectionButton>
65
- </template>
66
- </template>
67
-
68
- <!-- Menu -->
69
- <BaseMenu
70
- v-if="actions && actions.length"
71
- menu-class="w-52"
72
- :items="actions"
73
- >
74
- <template #button="{ open }">
75
- <div
76
- class="flex h-11 w-11 items-center justify-center rounded-md border border-slate-300 bg-white shadow-sm duration-150 hover:bg-slate-50"
77
- :class="[
78
- open ? 'ring-2 ring-primary-500 ring-offset-2' : false,
79
- ]"
80
- >
81
- <BaseIcon icon="heroicons-solid:dots-vertical" />
82
- </div>
83
- </template>
84
- </BaseMenu>
85
- </div>
86
-
87
- <slot
88
- :items="items"
89
- :loading="loading"
90
- :error="error"
91
- :first-load="firstLoad"
92
- :page="page"
93
- :sort-field="sortField"
94
- :sort-direction="sortDirection"
95
- :on-sort-change="onSortChange"
96
- :on-page-change="onPageChange"
97
- />
98
-
99
- <!-- Pagination -->
100
-
101
- <div v-if="paginationMetadata" class="mt-4">
102
- <p
103
- class="text-center text-sm text-slate-500 sm:text-right [&>b]:font-medium [&>b]:text-slate-600"
104
- >
105
- {{ t('sui.pagination_detail_1') }}
106
-
107
- <b>{{ paginationStart }}</b> - <b>{{ paginationEnd }}</b>
108
-
109
- {{ t('sui.pagination_detail_2') }}
110
-
111
- <b>{{ paginationMetadata.total }}</b>
112
- </p>
113
- </div>
114
-
115
- <div v-if="paginationMetadata" class="mt-4">
116
- <BasePagination
117
- :model-value="page"
118
- :last-page="lastPage"
119
- @update:model-value="onPageChange"
120
- />
121
- </div>
122
- </div>
123
-
124
- <div v-if="!compactLayout" ref="sidebar" class="space-y-3">
125
- <slot
126
- name="sidebarTop"
127
- :pagination-metadata="paginationMetadata"
128
- ></slot>
129
-
130
- <template v-for="section in sectionsInternal" :key="section.name">
131
- <BaseDataIteratorSectionBox :section="section">
132
- <slot
133
- :name="section.name"
134
- :query="query"
135
- :update-query="updateFilterQuery"
136
- :update-query-value="updateFilterQueryValue"
137
- />
138
- </BaseDataIteratorSectionBox>
139
- </template>
140
-
141
- <slot
142
- name="sidebarBottom"
143
- :pagination-metadata="paginationMetadata"
144
- ></slot>
145
- </div>
146
- </div>
147
-
148
- <template v-for="(section, i) in sectionsInternal" :key="section.name">
149
- <BaseDataIteratorSectionModal
150
- :section="section"
151
- :model-value="sectionModalActive == i"
152
- @update:model-value="closeSection"
153
- >
154
- <slot
155
- :name="section.name"
156
- :query="query"
157
- :update-query="updateFilterQuery"
158
- :update-query-value="updateFilterQueryValue"
159
- />
160
- </BaseDataIteratorSectionModal>
161
- </template>
162
- </div>
163
- </template>
164
-
165
- <script lang="ts">
166
- /* eslint-disable @typescript-eslint/no-explicit-any */
167
- import { t } from '@/i18n';
168
- type Direction = 'asc' | 'desc';
169
-
170
- const DEFAULT_QUERY = {
171
- page: 1,
172
- search: '',
173
- sort: '',
174
- filter: {},
175
- } as DataTableQuery;
176
- </script>
177
-
178
- <script lang="ts" setup>
179
- import { cloneDeep, debounce, merge, set } from 'lodash';
180
- import hash from 'object-hash';
181
- import { PropType } from 'vue';
182
- import {
183
- Collection,
184
- DataIteratorSection,
185
- DataTableQuery,
186
- MenuItemInterface,
187
- PaginatedCollection,
188
- ResourceCollection,
189
- } from '@/types';
190
-
191
- import BaseMenu from './BaseMenu.vue';
192
- import BasePagination from './BasePagination.vue';
193
- import { config } from '@/index';
194
- import { useMutationObserver, useResizeObserver } from '@vueuse/core';
195
- import { useHasPaginatedData } from '@/composables/paginatedData';
196
- import BaseDataIteratorSectionButton from './BaseDataIteratorSectionButton.vue';
197
- import BaseDataIteratorSectionModal from './BaseDataIteratorSectionModal.vue';
198
- import BaseDataIteratorSectionBox from './BaseDataIteratorSectionBox.vue';
199
-
200
- const props = defineProps({
201
- /**
202
- * Base URL from which to make requests
203
- */
204
- url: {
205
- required: true,
206
- type: String,
207
- },
208
-
209
- /**
210
- * Query params that always get applied.
211
- * To add overwrite-able query params, use defaultQuery.
212
- */
213
- urlQuery: {
214
- default: undefined,
215
- type: Object as PropType<Record<string, any>>,
216
- },
217
-
218
- /**
219
- * Query params that gets applied by default.
220
- * These may be overwritten by URL params generated by the data-table or filters
221
- * To add query params that are always active, use urlQuery.
222
- */
223
- defaultQuery: {
224
- default: function () {
225
- return DEFAULT_QUERY;
226
- },
227
- type: Object as PropType<DataTableQuery>,
228
- },
229
-
230
- /**
231
- * Add a search bar.
232
- */
233
- searchable: {
234
- default: true,
235
- type: Boolean,
236
- },
237
-
238
- /**
239
- * Configure contextual actions.
240
- */
241
- actions: {
242
- default: undefined,
243
- type: Array as PropType<MenuItemInterface[]>,
244
- },
245
-
246
- /**
247
- * Save data table state in URL.
248
- */
249
- historyMode: {
250
- default: false,
251
- type: Boolean,
252
- },
253
-
254
- /**
255
- * Layout type
256
- */
257
- layout: {
258
- default: 'default',
259
- type: String as PropType<'default' | 'compact'>,
260
- },
261
-
262
- /**
263
- * Sections
264
- */
265
- sections: {
266
- default: undefined,
267
- type: Array as PropType<DataIteratorSection[]>,
268
- },
269
-
270
- /**
271
- * Scroll to top when fetching new data
272
- */
273
- scrollTopOnFetch: {
274
- default: true,
275
- type: Boolean,
276
- },
277
- });
278
-
279
- const http = config.http;
280
-
281
- const emit = defineEmits([
282
- 'click',
283
- 'delete',
284
- 'checkAll',
285
- 'update:checked-rows',
286
- 'check',
287
- 'update:query',
288
- 'will-scroll-top',
289
- 'fetch',
290
- ]);
291
-
292
- const dataIteratorNode = ref<null | HTMLElement>(null);
293
- const searchInput = ref<null | HTMLInputElement>(null);
294
-
295
- const route = useRoute();
296
- const router = useRouter();
297
- const routeName = route.name;
298
-
299
- let willUnmount = false;
300
-
301
- onBeforeUnmount(() => {
302
- willUnmount = true;
303
- });
304
-
305
- const width = ref(800);
306
- useResizeObserver(dataIteratorNode, () => {
307
- width.value = dataIteratorNode.value?.clientWidth ?? 800;
308
- });
309
-
310
- /** Data table state */
311
-
312
- const firstLoad = ref(false);
313
- const loading = ref(true);
314
- const error = ref(false);
315
- const searchQuery = ref('');
316
-
317
- let lastUrl = '';
318
- let lastQueryHash = '';
319
- const query = ref<DataTableQuery>(cloneDeep(props.defaultQuery));
320
- const slots = useSlots();
321
-
322
- const compactLayout = computed(() => {
323
- if (props.layout === 'compact') {
324
- return true;
325
- }
326
- return width.value < 1024;
327
- });
328
-
329
- const hasFilters = computed((): boolean => {
330
- const numberOfFilterSlots = slots.filters;
331
- return numberOfFilterSlots !== undefined;
332
- });
333
-
334
- /*
335
- |--------------------------------------------------------------------------
336
- | Has sidebar observer
337
- |--------------------------------------------------------------------------
338
- */
339
-
340
- const hasSidebar = ref(false);
341
- const sidebar = ref<null | HTMLElement>(null);
342
-
343
- function checkIfSidebarIsEmpty() {
344
- hasSidebar.value = (sidebar?.value?.childElementCount ?? 0) > 0;
345
- }
346
-
347
- const checkIfSidebarIsEmptyDebounce = debounce(checkIfSidebarIsEmpty, 100);
348
-
349
- useMutationObserver(sidebar, checkIfSidebarIsEmptyDebounce, {
350
- attributes: false,
351
- childList: true,
352
- });
353
-
354
- onMounted(() => {
355
- checkIfSidebarIsEmpty();
356
- });
357
-
358
- watch(
359
- () => compactLayout.value,
360
- () => {
361
- // After the sidebar appears...
362
- nextTick(() => {
363
- checkIfSidebarIsEmpty();
364
- });
365
- }
366
- );
367
-
368
- /*
369
- |--------------------------------------------------------------------------
370
- | Query params
371
- |--------------------------------------------------------------------------
372
- */
373
-
374
- function updateFilterQueryValue(key: string, value: any) {
375
- let newQuery = cloneDeep(query.value);
376
- newQuery = set(newQuery, key, value);
377
- newQuery = set(newQuery, 'page', 1);
378
- updateQuery(newQuery);
379
- }
380
-
381
- function updateFilterQuery(newQuery: DataTableQuery) {
382
- newQuery = set(newQuery, 'page', 1);
383
- updateQuery(newQuery);
384
- }
385
-
386
- function updateQuery(newQuery: DataTableQuery) {
387
- if (!props.historyMode) {
388
- query.value = newQuery;
389
- fetch();
390
- return;
391
- }
392
-
393
- if (route.name === null) {
394
- throw new Error('Route name is required for history mode');
395
- }
396
-
397
- const newRoute = router.resolve({
398
- name: route.name,
399
- params: route.params,
400
- });
401
-
402
- const newParams = config.formatQueryString(newQuery);
403
- const newRoutePath = newRoute.fullPath + '?' + newParams;
404
-
405
- const oldParamString = getRouteQuery();
406
- const oldParams = config.formatQueryString(oldParamString);
407
-
408
- // Push new route if different
409
- if (oldParams != newParams) {
410
- if (!firstLoad.value) {
411
- router.replace(newRoutePath);
412
- return;
413
- }
414
- router.push(newRoutePath);
415
- return;
416
- }
417
-
418
- // If the URL is unchanged, we must manually trigger the fetch() method
419
- // on first load.
420
-
421
- if (!firstLoad.value) {
422
- query.value = newQuery;
423
- fetch();
424
- }
425
- }
426
-
427
- function queryHash(query: DataTableQuery): string {
428
- return hash(query);
429
- }
430
-
431
- /*
432
- |--------------------------------------------------------------------------
433
- | Data fetching
434
- |--------------------------------------------------------------------------
435
- */
436
-
437
- const url = computed<string>(() => {
438
- return props.url;
439
- });
440
-
441
- /*
442
- |--------------------------------------------------------------------------
443
- | Handlers
444
- |--------------------------------------------------------------------------
445
- */
446
-
447
- function onPageChange(p: number) {
448
- const newQuery = cloneDeep(query.value);
449
-
450
- newQuery.page = p;
451
-
452
- updateQuery(newQuery);
453
-
454
- scrollIntoView();
455
- }
456
-
457
- function onSortChange(field: string, direction: Direction) {
458
- let newSort = field;
459
-
460
- if (newSort && direction == 'desc') {
461
- newSort = '-' + newSort;
462
- }
463
-
464
- const newQuery = cloneDeep(query.value);
465
-
466
- newQuery.page = 1;
467
- newQuery.sort = newSort;
468
-
469
- updateQuery(newQuery);
470
- }
471
-
472
- const onSearch = debounce((event: any) => {
473
- const newQuery = cloneDeep(query.value);
474
-
475
- newQuery.page = 1;
476
- newQuery.search = searchQuery.value;
477
-
478
- updateQuery(newQuery);
479
- }, 350);
480
-
481
- /*
482
- |--------------------------------------------------------------------------
483
- | Route watcher
484
- |--------------------------------------------------------------------------
485
- */
486
-
487
- watch(
488
- () => route.query,
489
- () => {
490
- // Wait for the willUnmount flag to be set
491
- nextTick(() => {
492
- onRouteChange();
493
- });
494
- }
495
- );
496
-
497
- watch(
498
- () => props.urlQuery,
499
- () => {
500
- fetch();
501
- }
502
- );
503
-
504
- function getRouteQuery() {
505
- return config.parseQueryString(window.location.search.replace(/^(\?)/, ''));
506
- }
507
-
508
- function onRouteChange() {
509
- if (!props.historyMode) {
510
- return;
511
- }
512
-
513
- if (willUnmount) {
514
- return;
515
- }
516
-
517
- // Stop if route was changed
518
- if (route.name != routeName) {
519
- return;
520
- }
521
-
522
- const routeQuery = getRouteQuery();
523
- const newQuery = routeQuery as DataTableQuery;
524
-
525
- const newQueryHash = queryHash(newQuery);
526
-
527
- if (newQueryHash == lastQueryHash) {
528
- return;
529
- }
530
-
531
- lastQueryHash = newQueryHash;
532
-
533
- query.value = newQuery;
534
-
535
- // Update search input if not in focus
536
- if (searchInput.value && searchInput.value !== document.activeElement) {
537
- updateSearchInput();
538
- }
539
-
540
- fetch();
541
- }
542
-
543
- /*
544
- |--------------------------------------------------------------------------
545
- | Fetch
546
- |--------------------------------------------------------------------------
547
- */
548
-
549
- function fetchWithLoading(force = false) {
550
- fetch(force, true);
551
- }
552
-
553
- function fetchWithoutLoading(force = false) {
554
- fetch(force, false);
555
- }
556
-
557
- function fetch(force = false, showLoading = true) {
558
- if (willUnmount) {
559
- return;
560
- }
561
-
562
- const urlSplit = url.value.split(/[?#]/);
563
-
564
- const baseUrl = urlSplit[0];
565
- const urlQueryString = urlSplit[1] ?? null;
566
- const urlQuery = config.parseQueryString(urlQueryString);
567
-
568
- // Ordered by priority
569
- const allParams = merge(
570
- cloneDeep(query.value),
571
- cloneDeep(props.urlQuery),
572
- cloneDeep(urlQuery)
573
- );
574
-
575
- const queryString = config.formatQueryString(allParams);
576
-
577
- const fullUrl = baseUrl + '?' + queryString;
578
-
579
- if (lastUrl == fullUrl && !force) {
580
- return;
581
- }
582
-
583
- if (showLoading) {
584
- loading.value = true;
585
- }
586
-
587
- lastUrl = fullUrl;
588
-
589
- http
590
- .get(fullUrl)
591
- .then((response) => {
592
- data.value = response.data;
593
- loading.value = false;
594
- error.value = false;
595
- firstLoad.value = true;
596
- emit('fetch', data.value);
597
- })
598
- .catch(() => {
599
- data.value = null;
600
- loading.value = false;
601
- error.value = true;
602
- })
603
- .finally(() => {
604
- loading.value = false;
605
- });
606
- }
607
-
608
- /*
609
- |--------------------------------------------------------------------------
610
- | Data parsing
611
- |--------------------------------------------------------------------------
612
- */
613
-
614
- const data = ref<null | ResourceCollection | PaginatedCollection | Collection>(
615
- null
616
- );
617
-
618
- const { items, paginationMetadata, lastPage } = useHasPaginatedData(data);
619
-
620
- const page = computed((): number => {
621
- if (query.value.page && parseInt(query.value.page + '')) {
622
- return parseInt(query.value.page + '');
623
- }
624
- return 1;
625
- });
626
-
627
- const sortField = computed((): string => {
628
- return query.value.sort?.trim().replace(/^(-)/, '') ?? '';
629
- });
630
-
631
- const sortDirection = computed((): Direction => {
632
- if (query.value.sort && query.value.sort.length) {
633
- if (query.value.sort[0] == '-') {
634
- return 'desc';
635
- }
636
- }
637
- return 'asc';
638
- });
639
-
640
- const searchKeywords = computed((): string => {
641
- return query.value.search ?? '';
642
- });
643
-
644
- /*
645
- |--------------------------------------------------------------------------
646
- | Helpers
647
- |--------------------------------------------------------------------------
648
- */
649
-
650
- /** Scroll into view */
651
-
652
- const scrollIntoView = () => {
653
- emit('will-scroll-top');
654
-
655
- if (!props.scrollTopOnFetch) {
656
- return;
657
- }
658
-
659
- scrollIntoViewAction();
660
- };
661
-
662
- const scrollIntoViewAction = () => {
663
- if (dataIteratorNode.value == null) {
664
- return;
665
- }
666
-
667
- dataIteratorNode.value.scrollIntoView({
668
- behavior: 'smooth',
669
- });
670
- };
671
-
672
- function updateSearchInput() {
673
- searchQuery.value = searchKeywords.value;
674
- }
675
-
676
- const paginationStart = computed(() => {
677
- if (paginationMetadata.value == null) {
678
- return '';
679
- }
680
-
681
- return (
682
- paginationMetadata.value.per_page *
683
- (paginationMetadata.value.current_page - 1) +
684
- 1
685
- );
686
- });
687
-
688
- const paginationEnd = computed(() => {
689
- if (paginationMetadata.value == null) {
690
- return '';
691
- }
692
-
693
- return Math.min(
694
- paginationMetadata.value.current_page * paginationMetadata.value.per_page,
695
- paginationMetadata.value.total
696
- );
697
- });
698
-
699
- /*
700
- |--------------------------------------------------------------------------
701
- | Created
702
- |--------------------------------------------------------------------------
703
- */
704
-
705
- let newQuery = cloneDeep(query.value);
706
- const routeQuery = getRouteQuery();
707
-
708
- newQuery = merge(newQuery, routeQuery);
709
-
710
- updateQuery(newQuery);
711
-
712
- // Update search input on first load
713
- onMounted(() => {
714
- updateSearchInput();
715
- });
716
-
717
- /*
718
- |--------------------------------------------------------------------------
719
- | Sections
720
- |--------------------------------------------------------------------------
721
- */
722
-
723
- const sectionModalActive = ref<null | number>(null);
724
-
725
- const sectionsInternal = computed<DataIteratorSection[]>(() => {
726
- const sections = props.sections ?? [];
727
-
728
- if (hasFilters.value) {
729
- return [
730
- {
731
- name: 'filters',
732
- title: t('sui.filters'),
733
- closeText: t('sui.apply_filters'),
734
- icon: 'heroicons:adjustments-horizontal-solid',
735
- opened: true,
736
- },
737
- ...sections,
738
- ];
739
- }
740
-
741
- return sections;
742
- });
743
-
744
- function openSection(index: number) {
745
- if (sectionsInternal.value[index]) {
746
- sectionModalActive.value = index;
747
- } else {
748
- sectionModalActive.value = null;
749
- }
750
- }
751
-
752
- function closeSection() {
753
- sectionModalActive.value = null;
754
- }
755
-
756
- /*
757
- |--------------------------------------------------------------------------
758
- | Provide
759
- |--------------------------------------------------------------------------
760
- */
761
-
762
- provide('dataIterator:width', width);
763
-
764
- /*
765
- |--------------------------------------------------------------------------
766
- | Exposed API
767
- |--------------------------------------------------------------------------
768
- */
769
-
770
- defineExpose({
771
- fetch: () => fetch(true),
772
- fetchWithLoading: () => fetchWithLoading(true),
773
- fetchWithoutLoading: () => fetchWithoutLoading(true),
774
- scrollIntoView: scrollIntoViewAction,
775
- query,
776
- });
777
- </script>
1
+ <template>
2
+ <div ref="dataIteratorNode">
3
+ <div
4
+ class="grid w-full max-w-full grid-flow-row gap-4"
5
+ :class="{
6
+ 'grid-cols-[1fr_300px]': hasSidebar,
7
+ }"
8
+ >
9
+ <div
10
+ class="min-w-0"
11
+ :class="{ 'col-span-1': !compactLayout, 'col-span-2': compactLayout }"
12
+ >
13
+ <!-- Header -->
14
+ <div class="mb-4 flex space-x-2 empty:mb-0">
15
+ <!-- Search bar -->
16
+ <div v-if="searchable" class="grow">
17
+ <div class="relative h-11">
18
+ <div
19
+ class="pointer-events-none absolute left-0 top-0 flex h-full items-center justify-center pl-2.5"
20
+ >
21
+ <BaseIcon
22
+ class="h-6 w-6 text-slate-400"
23
+ icon="heroicons:magnifying-glass"
24
+ />
25
+ </div>
26
+ <input
27
+ ref="searchInput"
28
+ v-model="searchQuery"
29
+ type="text"
30
+ class="h-11 w-full overflow-hidden rounded-md border border-slate-300 bg-white pl-10 pr-9 shadow-sm"
31
+ :placeholder="t('sui.autocomplete_placeholder')"
32
+ @input="onSearch"
33
+ />
34
+ <div
35
+ v-if="searchQuery"
36
+ class="absolute right-0 top-0 flex h-full items-center justify-center p-1"
37
+ >
38
+ <button
39
+ type="button"
40
+ class="flex appearance-none items-center rounded p-1 hover:bg-slate-100"
41
+ @click="
42
+ searchQuery = '';
43
+ onSearch('');
44
+ "
45
+ >
46
+ <BaseIcon
47
+ class="h-6 w-6 text-slate-500"
48
+ icon="heroicons:x-mark"
49
+ />
50
+ </button>
51
+ </div>
52
+ </div>
53
+ </div>
54
+
55
+ <template v-if="compactLayout">
56
+ <template
57
+ v-for="(section, i) in sectionsInternal"
58
+ :key="section.name"
59
+ >
60
+ <BaseDataIteratorSectionButton
61
+ :section="section"
62
+ @open="openSection(i)"
63
+ >
64
+ </BaseDataIteratorSectionButton>
65
+ </template>
66
+ </template>
67
+
68
+ <!-- Menu -->
69
+ <BaseMenu
70
+ v-if="actions && actions.length"
71
+ tw-menu="w-52"
72
+ :items="actions"
73
+ >
74
+ <template #button="{ open }">
75
+ <div
76
+ class="flex h-11 w-11 items-center justify-center rounded-md border border-slate-300 bg-white shadow-sm duration-150 hover:bg-slate-50"
77
+ :class="[
78
+ open ? 'ring-2 ring-primary-500 ring-offset-2' : false,
79
+ ]"
80
+ >
81
+ <BaseIcon icon="heroicons-solid:dots-vertical" />
82
+ </div>
83
+ </template>
84
+ </BaseMenu>
85
+ </div>
86
+
87
+ <slot
88
+ :items="items"
89
+ :loading="loading"
90
+ :error="error"
91
+ :first-load="firstLoad"
92
+ :page="page"
93
+ :sort-field="sortField"
94
+ :sort-direction="sortDirection"
95
+ :on-sort-change="onSortChange"
96
+ :on-page-change="onPageChange"
97
+ />
98
+
99
+ <!-- Pagination -->
100
+
101
+ <div v-if="paginationMetadata" class="mt-4">
102
+ <p
103
+ class="text-center text-sm text-slate-500 sm:text-right [&>b]:font-medium [&>b]:text-slate-600"
104
+ >
105
+ {{ t('sui.pagination_detail_1') }}
106
+
107
+ <b>{{ paginationStart }}</b> - <b>{{ paginationEnd }}</b>
108
+
109
+ {{ t('sui.pagination_detail_2') }}
110
+
111
+ <b>{{ paginationMetadata.total }}</b>
112
+ </p>
113
+ </div>
114
+
115
+ <div v-if="paginationMetadata" class="mt-4">
116
+ <BasePagination
117
+ :model-value="page"
118
+ :last-page="lastPage"
119
+ @update:model-value="onPageChange"
120
+ />
121
+ </div>
122
+ </div>
123
+
124
+ <div v-if="!compactLayout" ref="sidebar" class="space-y-3">
125
+ <slot
126
+ name="sidebarTop"
127
+ :pagination-metadata="paginationMetadata"
128
+ ></slot>
129
+
130
+ <template v-for="section in sectionsInternal" :key="section.name">
131
+ <BaseDataIteratorSectionBox :section="section">
132
+ <slot
133
+ :name="section.name"
134
+ :query="query"
135
+ :update-query="updateFilterQuery"
136
+ :update-query-value="updateFilterQueryValue"
137
+ />
138
+ </BaseDataIteratorSectionBox>
139
+ </template>
140
+
141
+ <slot
142
+ name="sidebarBottom"
143
+ :pagination-metadata="paginationMetadata"
144
+ ></slot>
145
+ </div>
146
+ </div>
147
+
148
+ <template v-for="(section, i) in sectionsInternal" :key="section.name">
149
+ <BaseDataIteratorSectionModal
150
+ :section="section"
151
+ :model-value="sectionModalActive == i"
152
+ @update:model-value="closeSection"
153
+ >
154
+ <slot
155
+ :name="section.name"
156
+ :query="query"
157
+ :update-query="updateFilterQuery"
158
+ :update-query-value="updateFilterQueryValue"
159
+ />
160
+ </BaseDataIteratorSectionModal>
161
+ </template>
162
+ </div>
163
+ </template>
164
+
165
+ <script lang="ts">
166
+ /* eslint-disable @typescript-eslint/no-explicit-any */
167
+ import { t } from '@/i18n';
168
+ type Direction = 'asc' | 'desc';
169
+
170
+ const DEFAULT_QUERY = {
171
+ page: 1,
172
+ search: '',
173
+ sort: '',
174
+ filter: {},
175
+ } as DataTableQuery;
176
+ </script>
177
+
178
+ <script lang="ts" setup>
179
+ import { cloneDeep, debounce, merge, set } from 'lodash';
180
+ import hash from 'object-hash';
181
+ import { PropType } from 'vue';
182
+ import {
183
+ Collection,
184
+ DataIteratorSection,
185
+ DataTableQuery,
186
+ MenuItemInterface,
187
+ PaginatedCollection,
188
+ ResourceCollection,
189
+ } from '@/types';
190
+
191
+ import BaseMenu from './BaseMenu.vue';
192
+ import BasePagination from './BasePagination.vue';
193
+ import { config } from '@/index';
194
+ import { useMutationObserver, useResizeObserver } from '@vueuse/core';
195
+ import { useHasPaginatedData } from '@/composables/paginatedData';
196
+ import BaseDataIteratorSectionButton from './BaseDataIteratorSectionButton.vue';
197
+ import BaseDataIteratorSectionModal from './BaseDataIteratorSectionModal.vue';
198
+ import BaseDataIteratorSectionBox from './BaseDataIteratorSectionBox.vue';
199
+
200
+ const props = defineProps({
201
+ /**
202
+ * Base URL from which to make requests
203
+ */
204
+ url: {
205
+ required: true,
206
+ type: String,
207
+ },
208
+
209
+ /**
210
+ * Query params that always get applied.
211
+ * To add overwrite-able query params, use defaultQuery.
212
+ */
213
+ urlQuery: {
214
+ default: undefined,
215
+ type: Object as PropType<Record<string, any>>,
216
+ },
217
+
218
+ /**
219
+ * Query params that gets applied by default.
220
+ * These may be overwritten by URL params generated by the data-table or filters
221
+ * To add query params that are always active, use urlQuery.
222
+ */
223
+ defaultQuery: {
224
+ default: function () {
225
+ return DEFAULT_QUERY;
226
+ },
227
+ type: Object as PropType<DataTableQuery>,
228
+ },
229
+
230
+ /**
231
+ * Add a search bar.
232
+ */
233
+ searchable: {
234
+ default: true,
235
+ type: Boolean,
236
+ },
237
+
238
+ /**
239
+ * Configure contextual actions.
240
+ */
241
+ actions: {
242
+ default: undefined,
243
+ type: Array as PropType<MenuItemInterface[]>,
244
+ },
245
+
246
+ /**
247
+ * Save data table state in URL.
248
+ */
249
+ historyMode: {
250
+ default: false,
251
+ type: Boolean,
252
+ },
253
+
254
+ /**
255
+ * Layout type
256
+ */
257
+ layout: {
258
+ default: 'default',
259
+ type: String as PropType<'default' | 'compact'>,
260
+ },
261
+
262
+ /**
263
+ * Sections
264
+ */
265
+ sections: {
266
+ default: undefined,
267
+ type: Array as PropType<DataIteratorSection[]>,
268
+ },
269
+
270
+ /**
271
+ * Scroll to top when fetching new data
272
+ */
273
+ scrollTopOnFetch: {
274
+ default: true,
275
+ type: Boolean,
276
+ },
277
+ });
278
+
279
+ const http = config.http;
280
+
281
+ const emit = defineEmits([
282
+ 'click',
283
+ 'delete',
284
+ 'checkAll',
285
+ 'update:checked-rows',
286
+ 'check',
287
+ 'update:query',
288
+ 'will-scroll-top',
289
+ 'fetch',
290
+ ]);
291
+
292
+ const dataIteratorNode = ref<null | HTMLElement>(null);
293
+ const searchInput = ref<null | HTMLInputElement>(null);
294
+
295
+ const route = useRoute();
296
+ const router = useRouter();
297
+ const routeName = route.name;
298
+
299
+ let willUnmount = false;
300
+
301
+ onBeforeUnmount(() => {
302
+ willUnmount = true;
303
+ });
304
+
305
+ const width = ref(800);
306
+ useResizeObserver(dataIteratorNode, () => {
307
+ width.value = dataIteratorNode.value?.clientWidth ?? 800;
308
+ });
309
+
310
+ /** Data table state */
311
+
312
+ const firstLoad = ref(false);
313
+ const loading = ref(true);
314
+ const error = ref(false);
315
+ const searchQuery = ref('');
316
+
317
+ let lastUrl = '';
318
+ let lastQueryHash = '';
319
+ const query = ref<DataTableQuery>(cloneDeep(props.defaultQuery));
320
+ const slots = useSlots();
321
+
322
+ const compactLayout = computed(() => {
323
+ if (props.layout === 'compact') {
324
+ return true;
325
+ }
326
+ return width.value < 1024;
327
+ });
328
+
329
+ const hasFilters = computed((): boolean => {
330
+ const numberOfFilterSlots = slots.filters;
331
+ return numberOfFilterSlots !== undefined;
332
+ });
333
+
334
+ /*
335
+ |--------------------------------------------------------------------------
336
+ | Has sidebar observer
337
+ |--------------------------------------------------------------------------
338
+ */
339
+
340
+ const hasSidebar = ref(false);
341
+ const sidebar = ref<null | HTMLElement>(null);
342
+
343
+ function checkIfSidebarIsEmpty() {
344
+ hasSidebar.value = (sidebar?.value?.childElementCount ?? 0) > 0;
345
+ }
346
+
347
+ const checkIfSidebarIsEmptyDebounce = debounce(checkIfSidebarIsEmpty, 100);
348
+
349
+ useMutationObserver(sidebar, checkIfSidebarIsEmptyDebounce, {
350
+ attributes: false,
351
+ childList: true,
352
+ });
353
+
354
+ onMounted(() => {
355
+ checkIfSidebarIsEmpty();
356
+ });
357
+
358
+ watch(
359
+ () => compactLayout.value,
360
+ () => {
361
+ // After the sidebar appears...
362
+ nextTick(() => {
363
+ checkIfSidebarIsEmpty();
364
+ });
365
+ }
366
+ );
367
+
368
+ /*
369
+ |--------------------------------------------------------------------------
370
+ | Query params
371
+ |--------------------------------------------------------------------------
372
+ */
373
+
374
+ function updateFilterQueryValue(key: string, value: any) {
375
+ let newQuery = cloneDeep(query.value);
376
+ newQuery = set(newQuery, key, value);
377
+ newQuery = set(newQuery, 'page', 1);
378
+ updateQuery(newQuery);
379
+ }
380
+
381
+ function updateFilterQuery(newQuery: DataTableQuery) {
382
+ newQuery = set(newQuery, 'page', 1);
383
+ updateQuery(newQuery);
384
+ }
385
+
386
+ function updateQuery(newQuery: DataTableQuery) {
387
+ if (!props.historyMode) {
388
+ query.value = newQuery;
389
+ fetch();
390
+ return;
391
+ }
392
+
393
+ if (route.name === null) {
394
+ throw new Error('Route name is required for history mode');
395
+ }
396
+
397
+ const newRoute = router.resolve({
398
+ name: route.name,
399
+ params: route.params,
400
+ });
401
+
402
+ const newParams = config.formatQueryString(newQuery);
403
+ const newRoutePath = newRoute.fullPath + '?' + newParams;
404
+
405
+ const oldParamString = getRouteQuery();
406
+ const oldParams = config.formatQueryString(oldParamString);
407
+
408
+ // Push new route if different
409
+ if (oldParams != newParams) {
410
+ if (!firstLoad.value) {
411
+ router.replace(newRoutePath);
412
+ return;
413
+ }
414
+ router.push(newRoutePath);
415
+ return;
416
+ }
417
+
418
+ // If the URL is unchanged, we must manually trigger the fetch() method
419
+ // on first load.
420
+
421
+ if (!firstLoad.value) {
422
+ query.value = newQuery;
423
+ fetch();
424
+ }
425
+ }
426
+
427
+ function queryHash(query: DataTableQuery): string {
428
+ return hash(query);
429
+ }
430
+
431
+ /*
432
+ |--------------------------------------------------------------------------
433
+ | Data fetching
434
+ |--------------------------------------------------------------------------
435
+ */
436
+
437
+ const url = computed<string>(() => {
438
+ return props.url;
439
+ });
440
+
441
+ /*
442
+ |--------------------------------------------------------------------------
443
+ | Handlers
444
+ |--------------------------------------------------------------------------
445
+ */
446
+
447
+ function onPageChange(p: number) {
448
+ const newQuery = cloneDeep(query.value);
449
+
450
+ newQuery.page = p;
451
+
452
+ updateQuery(newQuery);
453
+
454
+ scrollIntoView();
455
+ }
456
+
457
+ function onSortChange(field: string, direction: Direction) {
458
+ let newSort = field;
459
+
460
+ if (newSort && direction == 'desc') {
461
+ newSort = '-' + newSort;
462
+ }
463
+
464
+ const newQuery = cloneDeep(query.value);
465
+
466
+ newQuery.page = 1;
467
+ newQuery.sort = newSort;
468
+
469
+ updateQuery(newQuery);
470
+ }
471
+
472
+ const onSearch = debounce((event: any) => {
473
+ const newQuery = cloneDeep(query.value);
474
+
475
+ newQuery.page = 1;
476
+ newQuery.search = searchQuery.value;
477
+
478
+ updateQuery(newQuery);
479
+ }, 350);
480
+
481
+ /*
482
+ |--------------------------------------------------------------------------
483
+ | Route watcher
484
+ |--------------------------------------------------------------------------
485
+ */
486
+
487
+ watch(
488
+ () => route.query,
489
+ () => {
490
+ // Wait for the willUnmount flag to be set
491
+ nextTick(() => {
492
+ onRouteChange();
493
+ });
494
+ }
495
+ );
496
+
497
+ watch(
498
+ () => props.url,
499
+ () => {
500
+ fetch();
501
+ }
502
+ );
503
+
504
+ watch(
505
+ () => props.urlQuery,
506
+ () => {
507
+ fetch();
508
+ }
509
+ );
510
+
511
+ function getRouteQuery() {
512
+ return config.parseQueryString(window.location.search.replace(/^(\?)/, ''));
513
+ }
514
+
515
+ function onRouteChange() {
516
+ if (!props.historyMode) {
517
+ return;
518
+ }
519
+
520
+ if (willUnmount) {
521
+ return;
522
+ }
523
+
524
+ // Stop if route was changed
525
+ if (route.name != routeName) {
526
+ return;
527
+ }
528
+
529
+ const routeQuery = getRouteQuery();
530
+ const newQuery = routeQuery as DataTableQuery;
531
+
532
+ const newQueryHash = queryHash(newQuery);
533
+
534
+ if (newQueryHash == lastQueryHash) {
535
+ return;
536
+ }
537
+
538
+ lastQueryHash = newQueryHash;
539
+
540
+ query.value = newQuery;
541
+
542
+ // Update search input if not in focus
543
+ if (searchInput.value && searchInput.value !== document.activeElement) {
544
+ updateSearchInput();
545
+ }
546
+
547
+ fetch();
548
+ }
549
+
550
+ /*
551
+ |--------------------------------------------------------------------------
552
+ | Fetch
553
+ |--------------------------------------------------------------------------
554
+ */
555
+
556
+ function fetchWithLoading(force = false) {
557
+ fetch(force, true);
558
+ }
559
+
560
+ function fetchWithoutLoading(force = false) {
561
+ fetch(force, false);
562
+ }
563
+
564
+ function fetch(force = false, showLoading = true) {
565
+ if (willUnmount) {
566
+ return;
567
+ }
568
+
569
+ const urlSplit = url.value.split(/[?#]/);
570
+
571
+ const baseUrl = urlSplit[0];
572
+ const urlQueryString = urlSplit[1] ?? null;
573
+ const urlQuery = config.parseQueryString(urlQueryString);
574
+
575
+ // Ordered by priority
576
+ const allParams = merge(
577
+ cloneDeep(query.value),
578
+ cloneDeep(props.urlQuery),
579
+ cloneDeep(urlQuery)
580
+ );
581
+
582
+ const queryString = config.formatQueryString(allParams);
583
+
584
+ const fullUrl = baseUrl + '?' + queryString;
585
+
586
+ if (lastUrl == fullUrl && !force) {
587
+ return;
588
+ }
589
+
590
+ if (showLoading) {
591
+ loading.value = true;
592
+ }
593
+
594
+ lastUrl = fullUrl;
595
+
596
+ http
597
+ .get(fullUrl)
598
+ .then((response) => {
599
+ data.value = response.data;
600
+ loading.value = false;
601
+ error.value = false;
602
+ firstLoad.value = true;
603
+ emit('fetch', data.value);
604
+ })
605
+ .catch(() => {
606
+ data.value = null;
607
+ loading.value = false;
608
+ error.value = true;
609
+ })
610
+ .finally(() => {
611
+ loading.value = false;
612
+ });
613
+ }
614
+
615
+ /*
616
+ |--------------------------------------------------------------------------
617
+ | Data parsing
618
+ |--------------------------------------------------------------------------
619
+ */
620
+
621
+ const data = ref<null | ResourceCollection | PaginatedCollection | Collection>(
622
+ null
623
+ );
624
+
625
+ const { items, paginationMetadata, lastPage } = useHasPaginatedData(data);
626
+
627
+ const page = computed((): number => {
628
+ if (query.value.page && parseInt(query.value.page + '')) {
629
+ return parseInt(query.value.page + '');
630
+ }
631
+ return 1;
632
+ });
633
+
634
+ const sortField = computed((): string => {
635
+ return query.value.sort?.trim().replace(/^(-)/, '') ?? '';
636
+ });
637
+
638
+ const sortDirection = computed((): Direction => {
639
+ if (query.value.sort && query.value.sort.length) {
640
+ if (query.value.sort[0] == '-') {
641
+ return 'desc';
642
+ }
643
+ }
644
+ return 'asc';
645
+ });
646
+
647
+ const searchKeywords = computed((): string => {
648
+ return query.value.search ?? '';
649
+ });
650
+
651
+ /*
652
+ |--------------------------------------------------------------------------
653
+ | Helpers
654
+ |--------------------------------------------------------------------------
655
+ */
656
+
657
+ /** Scroll into view */
658
+
659
+ const scrollIntoView = () => {
660
+ emit('will-scroll-top');
661
+
662
+ if (!props.scrollTopOnFetch) {
663
+ return;
664
+ }
665
+
666
+ scrollIntoViewAction();
667
+ };
668
+
669
+ const scrollIntoViewAction = () => {
670
+ if (dataIteratorNode.value == null) {
671
+ return;
672
+ }
673
+
674
+ dataIteratorNode.value.scrollIntoView({
675
+ behavior: 'smooth',
676
+ });
677
+ };
678
+
679
+ function updateSearchInput() {
680
+ searchQuery.value = searchKeywords.value;
681
+ }
682
+
683
+ const paginationStart = computed(() => {
684
+ if (paginationMetadata.value == null) {
685
+ return '';
686
+ }
687
+
688
+ return (
689
+ paginationMetadata.value.per_page *
690
+ (paginationMetadata.value.current_page - 1) +
691
+ 1
692
+ );
693
+ });
694
+
695
+ const paginationEnd = computed(() => {
696
+ if (paginationMetadata.value == null) {
697
+ return '';
698
+ }
699
+
700
+ return Math.min(
701
+ paginationMetadata.value.current_page * paginationMetadata.value.per_page,
702
+ paginationMetadata.value.total
703
+ );
704
+ });
705
+
706
+ /*
707
+ |--------------------------------------------------------------------------
708
+ | Created
709
+ |--------------------------------------------------------------------------
710
+ */
711
+
712
+ let newQuery = cloneDeep(query.value);
713
+ const routeQuery = getRouteQuery();
714
+
715
+ newQuery = merge(newQuery, routeQuery);
716
+
717
+ updateQuery(newQuery);
718
+
719
+ // Update search input on first load
720
+ onMounted(() => {
721
+ updateSearchInput();
722
+ });
723
+
724
+ /*
725
+ |--------------------------------------------------------------------------
726
+ | Sections
727
+ |--------------------------------------------------------------------------
728
+ */
729
+
730
+ const sectionModalActive = ref<null | number>(null);
731
+
732
+ const sectionsInternal = computed<DataIteratorSection[]>(() => {
733
+ const sections = props.sections ?? [];
734
+
735
+ if (hasFilters.value) {
736
+ return [
737
+ {
738
+ name: 'filters',
739
+ title: t('sui.filters'),
740
+ closeText: t('sui.apply_filters'),
741
+ icon: 'heroicons:adjustments-horizontal-solid',
742
+ opened: true,
743
+ },
744
+ ...sections,
745
+ ];
746
+ }
747
+
748
+ return sections;
749
+ });
750
+
751
+ function openSection(index: number) {
752
+ if (sectionsInternal.value[index]) {
753
+ sectionModalActive.value = index;
754
+ } else {
755
+ sectionModalActive.value = null;
756
+ }
757
+ }
758
+
759
+ function closeSection() {
760
+ sectionModalActive.value = null;
761
+ }
762
+
763
+ /*
764
+ |--------------------------------------------------------------------------
765
+ | Provide
766
+ |--------------------------------------------------------------------------
767
+ */
768
+
769
+ provide('dataIterator:width', width);
770
+
771
+ /*
772
+ |--------------------------------------------------------------------------
773
+ | Exposed API
774
+ |--------------------------------------------------------------------------
775
+ */
776
+
777
+ defineExpose({
778
+ fetch: () => fetch(true),
779
+ fetchWithLoading: () => fetchWithLoading(true),
780
+ fetchWithoutLoading: () => fetchWithoutLoading(true),
781
+ scrollIntoView: scrollIntoViewAction,
782
+ query,
783
+ });
784
+ </script>