sprintify-ui 0.1.15 → 0.1.17

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 +229 -254
  2. package/dist/sprintify-ui.es.js +10346 -10218
  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 +29 -29
  7. package/dist/types/src/components/BaseAddressForm.vue.d.ts +81 -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 +232 -232
  13. package/dist/types/src/components/BaseAutocompleteDrawer.vue.d.ts +90 -90
  14. package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +213 -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 +50 -50
  18. package/dist/types/src/components/BaseBelongsTo.vue.d.ts +219 -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 +57 -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 +665 -665
  37. package/dist/types/src/components/BaseDataIteratorSectionModal.vue.d.ts +29 -29
  38. package/dist/types/src/components/BaseDataTable.vue.d.ts +383 -383
  39. package/dist/types/src/components/BaseDataTableRowAction.vue.d.ts +18 -18
  40. package/dist/types/src/components/BaseDatePicker.vue.d.ts +84 -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 +62 -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 +96 -96
  51. package/dist/types/src/components/BaseFilePicker.vue.d.ts +59 -59
  52. package/dist/types/src/components/BaseFilePickerCrop.vue.d.ts +57 -57
  53. package/dist/types/src/components/BaseFileUploader.vue.d.ts +85 -85
  54. package/dist/types/src/components/BaseForm.vue.d.ts +131 -131
  55. package/dist/types/src/components/BaseHasMany.vue.d.ts +152 -147
  56. package/dist/types/src/components/BaseHeader.vue.d.ts +80 -80
  57. package/dist/types/src/components/BaseIconPicker.vue.d.ts +38 -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 +172 -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 +79 -79
  82. package/dist/types/src/components/BaseModalSide.vue.d.ts +61 -61
  83. package/dist/types/src/components/BaseNavbar.vue.d.ts +35 -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 +97 -97
  97. package/dist/types/src/components/BaseShortcut.vue.d.ts +86 -86
  98. package/dist/types/src/components/BaseSideNavigation.vue.d.ts +33 -33
  99. package/dist/types/src/components/BaseSideNavigationItem.vue.d.ts +43 -43
  100. package/dist/types/src/components/BaseSkeleton.vue.d.ts +30 -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 +43 -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 +33 -33
  110. package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +204 -190
  111. package/dist/types/src/components/BaseTagAutocompleteFetch.vue.d.ts +144 -139
  112. package/dist/types/src/components/BaseTextarea.vue.d.ts +98 -98
  113. package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +98 -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 -0
  128. package/dist/types/src/index.d.ts +220 -398
  129. package/dist/types/src/stores/dialogs.d.ts +9 -9
  130. package/dist/types/src/stores/i18n.d.ts +5 -0
  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 +139 -138
  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 -352
  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 -353
  176. package/src/components/BaseAutocompleteFetch.stories.js +224 -224
  177. package/src/components/BaseAutocompleteFetch.vue +277 -276
  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 +85 -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 -60
  199. package/src/components/BaseClickOutside.vue +37 -37
  200. package/src/components/BaseClipboard.stories.js +31 -31
  201. package/src/components/BaseClipboard.vue +96 -94
  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 -449
  210. package/src/components/BaseCropperModal.stories.js +54 -54
  211. package/src/components/BaseCropperModal.vue +140 -139
  212. package/src/components/BaseDataIterator.stories.js +197 -197
  213. package/src/components/BaseDataIterator.vue +777 -778
  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 -120
  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 +230 -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 -133
  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 +323 -323
  248. package/src/components/BaseHasMany.stories.js +189 -150
  249. package/src/components/BaseHasMany.vue +125 -119
  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 -214
  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 -143
  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 +94 -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 -316
  278. package/src/components/BaseMediaList.vue +68 -68
  279. package/src/components/BaseMediaListItem.vue +172 -171
  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 +129 -129
  286. package/src/components/BaseMenuItem.vue +107 -107
  287. package/src/components/BaseModalCenter.stories.js +68 -68
  288. package/src/components/BaseModalCenter.vue +125 -125
  289. package/src/components/BaseModalSide.stories.js +55 -55
  290. package/src/components/BaseModalSide.vue +116 -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 -346
  299. package/src/components/BasePagination.stories.js +35 -35
  300. package/src/components/BasePagination.vue +266 -265
  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 -72
  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 -219
  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 +24 -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 -179
  338. package/src/components/BaseTagAutocomplete.vue +428 -414
  339. package/src/components/BaseTagAutocompleteFetch.stories.js +185 -146
  340. package/src/components/BaseTagAutocompleteFetch.vue +206 -201
  341. package/src/components/BaseTextarea.stories.js +43 -43
  342. package/src/components/BaseTextarea.vue +87 -87
  343. package/src/components/BaseTextareaAutoresize.stories.js +58 -58
  344. package/src/components/BaseTextareaAutoresize.vue +140 -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 -0
  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 -0
  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,778 +1,777 @@
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
-
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 i18n = useI18n();
280
- const http = config.http;
281
-
282
- const emit = defineEmits([
283
- 'click',
284
- 'delete',
285
- 'checkAll',
286
- 'update:checked-rows',
287
- 'check',
288
- 'update:query',
289
- 'will-scroll-top',
290
- 'fetch',
291
- ]);
292
-
293
- const dataIteratorNode = ref<null | HTMLElement>(null);
294
- const searchInput = ref<null | HTMLInputElement>(null);
295
-
296
- const route = useRoute();
297
- const router = useRouter();
298
- const routeName = route.name;
299
-
300
- let willUnmount = false;
301
-
302
- onBeforeUnmount(() => {
303
- willUnmount = true;
304
- });
305
-
306
- const width = ref(800);
307
- useResizeObserver(dataIteratorNode, () => {
308
- width.value = dataIteratorNode.value?.clientWidth ?? 800;
309
- });
310
-
311
- /** Data table state */
312
-
313
- const firstLoad = ref(false);
314
- const loading = ref(true);
315
- const error = ref(false);
316
- const searchQuery = ref('');
317
-
318
- let lastUrl = '';
319
- let lastQueryHash = '';
320
- const query = ref<DataTableQuery>(cloneDeep(props.defaultQuery));
321
- const slots = useSlots();
322
-
323
- const compactLayout = computed(() => {
324
- if (props.layout === 'compact') {
325
- return true;
326
- }
327
- return width.value < 1024;
328
- });
329
-
330
- const hasFilters = computed((): boolean => {
331
- const numberOfFilterSlots = slots.filters;
332
- return numberOfFilterSlots !== undefined;
333
- });
334
-
335
- /*
336
- |--------------------------------------------------------------------------
337
- | Has sidebar observer
338
- |--------------------------------------------------------------------------
339
- */
340
-
341
- const hasSidebar = ref(false);
342
- const sidebar = ref<null | HTMLElement>(null);
343
-
344
- function checkIfSidebarIsEmpty() {
345
- hasSidebar.value = (sidebar?.value?.childElementCount ?? 0) > 0;
346
- }
347
-
348
- const checkIfSidebarIsEmptyDebounce = debounce(checkIfSidebarIsEmpty, 100);
349
-
350
- useMutationObserver(sidebar, checkIfSidebarIsEmptyDebounce, {
351
- attributes: false,
352
- childList: true,
353
- });
354
-
355
- onMounted(() => {
356
- checkIfSidebarIsEmpty();
357
- });
358
-
359
- watch(
360
- () => compactLayout.value,
361
- () => {
362
- // After the sidebar appears...
363
- nextTick(() => {
364
- checkIfSidebarIsEmpty();
365
- });
366
- }
367
- );
368
-
369
- /*
370
- |--------------------------------------------------------------------------
371
- | Query params
372
- |--------------------------------------------------------------------------
373
- */
374
-
375
- function updateFilterQueryValue(key: string, value: any) {
376
- let newQuery = cloneDeep(query.value);
377
- newQuery = set(newQuery, key, value);
378
- newQuery = set(newQuery, 'page', 1);
379
- updateQuery(newQuery);
380
- }
381
-
382
- function updateFilterQuery(newQuery: DataTableQuery) {
383
- newQuery = set(newQuery, 'page', 1);
384
- updateQuery(newQuery);
385
- }
386
-
387
- function updateQuery(newQuery: DataTableQuery) {
388
- if (!props.historyMode) {
389
- query.value = newQuery;
390
- fetch();
391
- return;
392
- }
393
-
394
- if (route.name === null) {
395
- throw new Error('Route name is required for history mode');
396
- }
397
-
398
- const newRoute = router.resolve({
399
- name: route.name,
400
- params: route.params,
401
- });
402
-
403
- const newParams = config.formatQueryString(newQuery);
404
- const newRoutePath = newRoute.fullPath + '?' + newParams;
405
-
406
- const oldParamString = getRouteQuery();
407
- const oldParams = config.formatQueryString(oldParamString);
408
-
409
- // Push new route if different
410
- if (oldParams != newParams) {
411
- if (!firstLoad.value) {
412
- router.replace(newRoutePath);
413
- return;
414
- }
415
- router.push(newRoutePath);
416
- return;
417
- }
418
-
419
- // If the URL is unchanged, we must manually trigger the fetch() method
420
- // on first load.
421
-
422
- if (!firstLoad.value) {
423
- query.value = newQuery;
424
- fetch();
425
- }
426
- }
427
-
428
- function queryHash(query: DataTableQuery): string {
429
- return hash(query);
430
- }
431
-
432
- /*
433
- |--------------------------------------------------------------------------
434
- | Data fetching
435
- |--------------------------------------------------------------------------
436
- */
437
-
438
- const url = computed<string>(() => {
439
- return props.url;
440
- });
441
-
442
- /*
443
- |--------------------------------------------------------------------------
444
- | Handlers
445
- |--------------------------------------------------------------------------
446
- */
447
-
448
- function onPageChange(p: number) {
449
- const newQuery = cloneDeep(query.value);
450
-
451
- newQuery.page = p;
452
-
453
- updateQuery(newQuery);
454
-
455
- scrollIntoView();
456
- }
457
-
458
- function onSortChange(field: string, direction: Direction) {
459
- let newSort = field;
460
-
461
- if (newSort && direction == 'desc') {
462
- newSort = '-' + newSort;
463
- }
464
-
465
- const newQuery = cloneDeep(query.value);
466
-
467
- newQuery.page = 1;
468
- newQuery.sort = newSort;
469
-
470
- updateQuery(newQuery);
471
- }
472
-
473
- const onSearch = debounce((event: any) => {
474
- const newQuery = cloneDeep(query.value);
475
-
476
- newQuery.page = 1;
477
- newQuery.search = searchQuery.value;
478
-
479
- updateQuery(newQuery);
480
- }, 350);
481
-
482
- /*
483
- |--------------------------------------------------------------------------
484
- | Route watcher
485
- |--------------------------------------------------------------------------
486
- */
487
-
488
- watch(
489
- () => route.query,
490
- () => {
491
- // Wait for the willUnmount flag to be set
492
- nextTick(() => {
493
- onRouteChange();
494
- });
495
- }
496
- );
497
-
498
- watch(
499
- () => props.urlQuery,
500
- () => {
501
- fetch();
502
- }
503
- );
504
-
505
- function getRouteQuery() {
506
- return config.parseQueryString(window.location.search.replace(/^(\?)/, ''));
507
- }
508
-
509
- function onRouteChange() {
510
- if (!props.historyMode) {
511
- return;
512
- }
513
-
514
- if (willUnmount) {
515
- return;
516
- }
517
-
518
- // Stop if route was changed
519
- if (route.name != routeName) {
520
- return;
521
- }
522
-
523
- const routeQuery = getRouteQuery();
524
- const newQuery = routeQuery as DataTableQuery;
525
-
526
- const newQueryHash = queryHash(newQuery);
527
-
528
- if (newQueryHash == lastQueryHash) {
529
- return;
530
- }
531
-
532
- lastQueryHash = newQueryHash;
533
-
534
- query.value = newQuery;
535
-
536
- // Update search input if not in focus
537
- if (searchInput.value && searchInput.value !== document.activeElement) {
538
- updateSearchInput();
539
- }
540
-
541
- fetch();
542
- }
543
-
544
- /*
545
- |--------------------------------------------------------------------------
546
- | Fetch
547
- |--------------------------------------------------------------------------
548
- */
549
-
550
- function fetchWithLoading(force = false) {
551
- fetch(force, true);
552
- }
553
-
554
- function fetchWithoutLoading(force = false) {
555
- fetch(force, false);
556
- }
557
-
558
- function fetch(force = false, showLoading = true) {
559
- if (willUnmount) {
560
- return;
561
- }
562
-
563
- const urlSplit = url.value.split(/[?#]/);
564
-
565
- const baseUrl = urlSplit[0];
566
- const urlQueryString = urlSplit[1] ?? null;
567
- const urlQuery = config.parseQueryString(urlQueryString);
568
-
569
- // Ordered by priority
570
- const allParams = merge(
571
- cloneDeep(query.value),
572
- cloneDeep(props.urlQuery),
573
- cloneDeep(urlQuery)
574
- );
575
-
576
- const queryString = config.formatQueryString(allParams);
577
-
578
- const fullUrl = baseUrl + '?' + queryString;
579
-
580
- if (lastUrl == fullUrl && !force) {
581
- return;
582
- }
583
-
584
- if (showLoading) {
585
- loading.value = true;
586
- }
587
-
588
- lastUrl = fullUrl;
589
-
590
- http
591
- .get(fullUrl)
592
- .then((response) => {
593
- data.value = response.data;
594
- loading.value = false;
595
- error.value = false;
596
- firstLoad.value = true;
597
- emit('fetch', data.value);
598
- })
599
- .catch(() => {
600
- data.value = null;
601
- loading.value = false;
602
- error.value = true;
603
- })
604
- .finally(() => {
605
- loading.value = false;
606
- });
607
- }
608
-
609
- /*
610
- |--------------------------------------------------------------------------
611
- | Data parsing
612
- |--------------------------------------------------------------------------
613
- */
614
-
615
- const data = ref<null | ResourceCollection | PaginatedCollection | Collection>(
616
- null
617
- );
618
-
619
- const { items, paginationMetadata, lastPage } = useHasPaginatedData(data);
620
-
621
- const page = computed((): number => {
622
- if (query.value.page && parseInt(query.value.page + '')) {
623
- return parseInt(query.value.page + '');
624
- }
625
- return 1;
626
- });
627
-
628
- const sortField = computed((): string => {
629
- return query.value.sort?.trim().replace(/^(-)/, '') ?? '';
630
- });
631
-
632
- const sortDirection = computed((): Direction => {
633
- if (query.value.sort && query.value.sort.length) {
634
- if (query.value.sort[0] == '-') {
635
- return 'desc';
636
- }
637
- }
638
- return 'asc';
639
- });
640
-
641
- const searchKeywords = computed((): string => {
642
- return query.value.search ?? '';
643
- });
644
-
645
- /*
646
- |--------------------------------------------------------------------------
647
- | Helpers
648
- |--------------------------------------------------------------------------
649
- */
650
-
651
- /** Scroll into view */
652
-
653
- const scrollIntoView = () => {
654
- emit('will-scroll-top');
655
-
656
- if (!props.scrollTopOnFetch) {
657
- return;
658
- }
659
-
660
- scrollIntoViewAction();
661
- };
662
-
663
- const scrollIntoViewAction = () => {
664
- if (dataIteratorNode.value == null) {
665
- return;
666
- }
667
-
668
- dataIteratorNode.value.scrollIntoView({
669
- behavior: 'smooth',
670
- });
671
- };
672
-
673
- function updateSearchInput() {
674
- searchQuery.value = searchKeywords.value;
675
- }
676
-
677
- const paginationStart = computed(() => {
678
- if (paginationMetadata.value == null) {
679
- return '';
680
- }
681
-
682
- return (
683
- paginationMetadata.value.per_page *
684
- (paginationMetadata.value.current_page - 1) +
685
- 1
686
- );
687
- });
688
-
689
- const paginationEnd = computed(() => {
690
- if (paginationMetadata.value == null) {
691
- return '';
692
- }
693
-
694
- return Math.min(
695
- paginationMetadata.value.current_page * paginationMetadata.value.per_page,
696
- paginationMetadata.value.total
697
- );
698
- });
699
-
700
- /*
701
- |--------------------------------------------------------------------------
702
- | Created
703
- |--------------------------------------------------------------------------
704
- */
705
-
706
- let newQuery = cloneDeep(query.value);
707
- const routeQuery = getRouteQuery();
708
-
709
- newQuery = merge(newQuery, routeQuery);
710
-
711
- updateQuery(newQuery);
712
-
713
- // Update search input on first load
714
- onMounted(() => {
715
- updateSearchInput();
716
- });
717
-
718
- /*
719
- |--------------------------------------------------------------------------
720
- | Sections
721
- |--------------------------------------------------------------------------
722
- */
723
-
724
- const sectionModalActive = ref<null | number>(null);
725
-
726
- const sectionsInternal = computed<DataIteratorSection[]>(() => {
727
- const sections = props.sections ?? [];
728
-
729
- if (hasFilters.value) {
730
- return [
731
- {
732
- name: 'filters',
733
- title: i18n.t('sui.filters'),
734
- closeText: i18n.t('sui.apply_filters'),
735
- icon: 'heroicons:adjustments-horizontal-solid',
736
- opened: true,
737
- },
738
- ...sections,
739
- ];
740
- }
741
-
742
- return sections;
743
- });
744
-
745
- function openSection(index: number) {
746
- if (sectionsInternal.value[index]) {
747
- sectionModalActive.value = index;
748
- } else {
749
- sectionModalActive.value = null;
750
- }
751
- }
752
-
753
- function closeSection() {
754
- sectionModalActive.value = null;
755
- }
756
-
757
- /*
758
- |--------------------------------------------------------------------------
759
- | Provide
760
- |--------------------------------------------------------------------------
761
- */
762
-
763
- provide('dataIterator:width', width);
764
-
765
- /*
766
- |--------------------------------------------------------------------------
767
- | Exposed API
768
- |--------------------------------------------------------------------------
769
- */
770
-
771
- defineExpose({
772
- fetch: () => fetch(true),
773
- fetchWithLoading: () => fetchWithLoading(true),
774
- fetchWithoutLoading: () => fetchWithoutLoading(true),
775
- scrollIntoView: scrollIntoViewAction,
776
- query,
777
- });
778
- </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 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>