sprintify-ui 0.10.65 → 0.10.67

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 (416) hide show
  1. package/README.md +266 -266
  2. package/dist/{BaseCkeditor-D6D4FPEb.js → BaseCkeditor-B4PbYw6a.js} +2 -2
  3. package/dist/sprintify-ui.es.js +4435 -4557
  4. package/dist/style.css +3 -3
  5. package/dist/tailwindcss/button.js +260 -260
  6. package/dist/tailwindcss/index.js +21 -21
  7. package/dist/tailwindcss/input.js +22 -22
  8. package/dist/tailwindcss/overlay.js +12 -12
  9. package/dist/tailwindcss/table.js +91 -91
  10. package/dist/tailwindcss/theme.js +52 -52
  11. package/dist/types/components/BaseActionButtons.vue.d.ts +1 -1
  12. package/dist/types/components/BaseActionItem.vue.d.ts +7 -10
  13. package/dist/types/components/BaseAddressForm.vue.d.ts +1 -1
  14. package/dist/types/components/BaseAlert.vue.d.ts +24 -11
  15. package/dist/types/components/BaseApp.vue.d.ts +14 -12
  16. package/dist/types/components/BaseAssign.vue.d.ts +1 -1
  17. package/dist/types/components/BaseAutocomplete.vue.d.ts +665 -455
  18. package/dist/types/components/BaseAutocompleteDrawer.vue.d.ts +119 -24
  19. package/dist/types/components/BaseAutocompleteFetch.vue.d.ts +1033 -412
  20. package/dist/types/components/BaseAvatarGroup.vue.d.ts +1 -1
  21. package/dist/types/components/BaseBadge.vue.d.ts +23 -11
  22. package/dist/types/components/BaseBelongsTo.vue.d.ts +998 -411
  23. package/dist/types/components/BaseBelongsToFetch.vue.d.ts +754 -371
  24. package/dist/types/components/BaseBoolean.vue.d.ts +1 -1
  25. package/dist/types/components/BaseBreadcrumbs.vue.d.ts +1 -1
  26. package/dist/types/components/BaseButton.vue.d.ts +78 -16
  27. package/dist/types/components/BaseButtonGroup.vue.d.ts +166 -15
  28. package/dist/types/components/BaseCard.vue.d.ts +26 -12
  29. package/dist/types/components/BaseCardRow.vue.d.ts +20 -11
  30. package/dist/types/components/BaseCharacterCounter.vue.d.ts +1 -1
  31. package/dist/types/components/BaseClipboard.vue.d.ts +45 -13
  32. package/dist/types/components/BaseCollapse.vue.d.ts +41 -20
  33. package/dist/types/components/BaseContainer.vue.d.ts +16 -11
  34. package/dist/types/components/BaseCounter.vue.d.ts +1 -1
  35. package/dist/types/components/BaseCropper.vue.d.ts +55 -30
  36. package/dist/types/components/BaseDataIterator.vue.d.ts +43 -42
  37. package/dist/types/components/BaseDataIteratorSectionBox.vue.d.ts +15 -12
  38. package/dist/types/components/BaseDataIteratorSectionColumns.vue.d.ts +1 -1
  39. package/dist/types/components/BaseDataIteratorSectionModal.vue.d.ts +19 -10
  40. package/dist/types/components/BaseDataTable.vue.d.ts +2090 -738
  41. package/dist/types/components/BaseDataTableTemplate.vue.d.ts +576 -100
  42. package/dist/types/components/BaseDatePicker.vue.d.ts +1 -1
  43. package/dist/types/components/BaseDateSelect.vue.d.ts +1 -1
  44. package/dist/types/components/BaseDescriptionList.vue.d.ts +12 -12
  45. package/dist/types/components/BaseDescriptionListItem.vue.d.ts +16 -15
  46. package/dist/types/components/BaseDialog.vue.d.ts +114 -413
  47. package/dist/types/components/BaseDisplayRelativeTime.vue.d.ts +55 -13
  48. package/dist/types/components/BaseDraggable.vue.d.ts +25 -16
  49. package/dist/types/components/BaseDropdown.vue.d.ts +155 -17
  50. package/dist/types/components/BaseDropdownAutocomplete.vue.d.ts +131 -16
  51. package/dist/types/components/BaseField.vue.d.ts +103 -12
  52. package/dist/types/components/BaseFieldI18n.vue.d.ts +1 -1
  53. package/dist/types/components/BaseFilePicker.vue.d.ts +35 -16
  54. package/dist/types/components/BaseFilePickerCrop.vue.d.ts +87 -42
  55. package/dist/types/components/BaseFileUploader.vue.d.ts +195 -29
  56. package/dist/types/components/BaseForm.vue.d.ts +161 -20
  57. package/dist/types/components/BaseGantt.vue.d.ts +1130 -409
  58. package/dist/types/components/BaseHasMany.vue.d.ts +590 -347
  59. package/dist/types/components/BaseHasManyFetch.vue.d.ts +602 -251
  60. package/dist/types/components/BaseHeader.vue.d.ts +1 -1
  61. package/dist/types/components/BaseIconPicker.vue.d.ts +1 -1
  62. package/dist/types/components/BaseInputError.vue.d.ts +23 -11
  63. package/dist/types/components/BaseJsonReaderItem.vue.d.ts +1 -1
  64. package/dist/types/components/BaseLayoutNotificationItemContent.vue.d.ts +1 -1
  65. package/dist/types/components/BaseLayoutSidebar.vue.d.ts +124 -16
  66. package/dist/types/components/BaseLayoutSidebarConfigurable.vue.d.ts +115 -13
  67. package/dist/types/components/BaseLayoutStacked.vue.d.ts +69 -22
  68. package/dist/types/components/BaseLayoutStackedConfigurable.vue.d.ts +120 -10
  69. package/dist/types/components/BaseLazy.vue.d.ts +20 -15
  70. package/dist/types/components/BaseMediaGallery.vue.d.ts +1 -1
  71. package/dist/types/components/BaseMediaGalleryItem.vue.d.ts +1 -1
  72. package/dist/types/components/BaseMediaItem.vue.d.ts +1 -1
  73. package/dist/types/components/BaseMediaLibrary.vue.d.ts +234 -34
  74. package/dist/types/components/BaseMediaListItem.vue.d.ts +1 -1
  75. package/dist/types/components/BaseMediaPicturesItem.vue.d.ts +1 -1
  76. package/dist/types/components/BaseMenu.vue.d.ts +233 -30
  77. package/dist/types/components/BaseMenuItem.vue.d.ts +1 -1
  78. package/dist/types/components/BaseModalCenter.vue.d.ts +88 -12
  79. package/dist/types/components/BaseModalSide.vue.d.ts +85 -12
  80. package/dist/types/components/BaseNavbar.vue.d.ts +88 -22
  81. package/dist/types/components/BaseNavbarItem.vue.d.ts +1 -1
  82. package/dist/types/components/BaseNavbarItemContent.vue.d.ts +1 -1
  83. package/dist/types/components/BaseNavbarSideItem.vue.d.ts +1 -1
  84. package/dist/types/components/BaseNavbarSideItemContent.vue.d.ts +1 -1
  85. package/dist/types/components/BasePagination.vue.d.ts +1 -1
  86. package/dist/types/components/BaseRadioGroup.vue.d.ts +113 -13
  87. package/dist/types/components/BaseReadMore.vue.d.ts +31 -12
  88. package/dist/types/components/BaseRichText.vue.d.ts +1 -1
  89. package/dist/types/components/BaseSelect.vue.d.ts +149 -12
  90. package/dist/types/components/BaseShortcut.vue.d.ts +1 -1
  91. package/dist/types/components/BaseSideNavigation.vue.d.ts +11 -12
  92. package/dist/types/components/BaseSideNavigationItem.vue.d.ts +22 -13
  93. package/dist/types/components/BaseSkeleton.vue.d.ts +3 -3
  94. package/dist/types/components/BaseStepper.vue.d.ts +1 -1
  95. package/dist/types/components/BaseSwitch.vue.d.ts +122 -13
  96. package/dist/types/components/BaseSystemAlert.vue.d.ts +58 -11
  97. package/dist/types/components/BaseTabItem.vue.d.ts +35 -18
  98. package/dist/types/components/BaseTable.vue.d.ts +29 -14
  99. package/dist/types/components/BaseTableBody.vue.d.ts +9 -12
  100. package/dist/types/components/BaseTableCell.vue.d.ts +40 -15
  101. package/dist/types/components/BaseTableColumn.vue.d.ts +3 -3
  102. package/dist/types/components/BaseTableHead.vue.d.ts +14 -12
  103. package/dist/types/components/BaseTableHeader.vue.d.ts +46 -17
  104. package/dist/types/components/BaseTableRow.vue.d.ts +40 -13
  105. package/dist/types/components/BaseTabs.vue.d.ts +21 -15
  106. package/dist/types/components/BaseTagAutocomplete.vue.d.ts +602 -203
  107. package/dist/types/components/BaseTagAutocompleteFetch.vue.d.ts +807 -348
  108. package/dist/types/components/BaseTextareaAutoresize.vue.d.ts +1 -1
  109. package/dist/types/components/BaseTimeline.vue.d.ts +1 -1
  110. package/dist/types/components/BaseTimelineItem.vue.d.ts +1 -1
  111. package/dist/types/components/BaseToast.vue.d.ts +1 -1
  112. package/dist/types/components/BaseTooltip.vue.d.ts +41 -15
  113. package/dist/types/components/BaseUniqueCode.vue.d.ts +1 -1
  114. package/dist/types/stories/PageInputSizes.vue.d.ts +1 -1
  115. package/dist/types/stories/PageShow.vue.d.ts +1 -1
  116. package/dist/types/svg/BaseEmptyState.vue.d.ts +1 -1
  117. package/dist/types/svg/BaseSpinnerSmall.vue.d.ts +1 -1
  118. package/package.json +135 -135
  119. package/src/assets/base-cropper.css +61 -61
  120. package/src/assets/base-date-picker.css +4 -4
  121. package/src/assets/base-rich-text.css +270 -270
  122. package/src/assets/base-spinner.css +18 -18
  123. package/src/assets/base-tabs.css +4 -4
  124. package/src/assets/base-time-picker.css +9 -9
  125. package/src/assets/flatpickr.css +247 -247
  126. package/src/assets/form.css +6 -6
  127. package/src/assets/google-pac.css +25 -25
  128. package/src/assets/main.css +56 -56
  129. package/src/assets/tailwind.css +2 -2
  130. package/src/changelog.mdx +6 -6
  131. package/src/components/BaseActionButtons.vue +76 -76
  132. package/src/components/BaseActionItem.vue +80 -80
  133. package/src/components/BaseActionItemButton.vue +75 -75
  134. package/src/components/BaseAddressForm.stories.js +114 -114
  135. package/src/components/BaseAddressForm.vue +382 -382
  136. package/src/components/BaseAlert.stories.js +75 -75
  137. package/src/components/BaseAlert.vue +101 -101
  138. package/src/components/BaseApp.vue +16 -16
  139. package/src/components/BaseAppDialogs.vue +126 -126
  140. package/src/components/BaseAppSnackbars.vue +40 -40
  141. package/src/components/BaseAssign.mdx +98 -98
  142. package/src/components/BaseAssign.stories.js +78 -78
  143. package/src/components/BaseAssign.vue +366 -366
  144. package/src/components/BaseAutocomplete.stories.js +243 -243
  145. package/src/components/BaseAutocomplete.vue +603 -603
  146. package/src/components/BaseAutocompleteDrawer.vue +386 -386
  147. package/src/components/BaseAutocompleteFetch.stories.js +224 -224
  148. package/src/components/BaseAutocompleteFetch.vue +314 -314
  149. package/src/components/BaseAvatar.stories.js +39 -39
  150. package/src/components/BaseAvatar.vue +164 -164
  151. package/src/components/BaseAvatarGroup.stories.js +71 -71
  152. package/src/components/BaseAvatarGroup.vue +148 -148
  153. package/src/components/BaseBadge.stories.js +132 -130
  154. package/src/components/BaseBadge.vue +118 -97
  155. package/src/components/BaseBelongsTo.stories.js +220 -220
  156. package/src/components/BaseBelongsTo.vue +164 -164
  157. package/src/components/BaseBelongsToFetch.stories.js +223 -223
  158. package/src/components/BaseBelongsToFetch.vue +213 -213
  159. package/src/components/BaseBoolean.stories.js +35 -35
  160. package/src/components/BaseBoolean.vue +26 -26
  161. package/src/components/BaseBreadcrumbs.stories.js +50 -50
  162. package/src/components/BaseBreadcrumbs.vue +109 -109
  163. package/src/components/BaseButton.stories.js +126 -126
  164. package/src/components/BaseButton.vue +279 -279
  165. package/src/components/BaseButtonGroup.stories.js +114 -114
  166. package/src/components/BaseButtonGroup.vue +193 -193
  167. package/src/components/BaseCard.stories.js +63 -63
  168. package/src/components/BaseCard.vue +49 -49
  169. package/src/components/BaseCardRow.vue +53 -53
  170. package/src/components/BaseCharacterCounter.stories.js +30 -30
  171. package/src/components/BaseCharacterCounter.vue +64 -64
  172. package/src/components/BaseCkeditor.vue +154 -154
  173. package/src/components/BaseClipboard.stories.js +55 -55
  174. package/src/components/BaseClipboard.vue +105 -105
  175. package/src/components/BaseCollapse.stories.js +168 -168
  176. package/src/components/BaseCollapse.vue +98 -98
  177. package/src/components/BaseColor.stories.js +66 -66
  178. package/src/components/BaseColor.vue +155 -155
  179. package/src/components/BaseContainer.stories.js +34 -34
  180. package/src/components/BaseContainer.vue +64 -64
  181. package/src/components/BaseCounter.stories.js +47 -47
  182. package/src/components/BaseCounter.vue +83 -83
  183. package/src/components/BaseCropper.stories.js +113 -113
  184. package/src/components/BaseCropper.vue +390 -390
  185. package/src/components/BaseCropperModal.stories.js +54 -54
  186. package/src/components/BaseCropperModal.vue +143 -143
  187. package/src/components/BaseDataIterator.stories.js +292 -292
  188. package/src/components/BaseDataIterator.vue +986 -986
  189. package/src/components/BaseDataIteratorSectionBox.vue +36 -36
  190. package/src/components/BaseDataIteratorSectionButton.vue +42 -42
  191. package/src/components/BaseDataIteratorSectionColumns.vue +150 -150
  192. package/src/components/BaseDataIteratorSectionModal.vue +41 -41
  193. package/src/components/BaseDataTable.stories.js +393 -393
  194. package/src/components/BaseDataTable.vue +966 -966
  195. package/src/components/BaseDataTableRowAction.vue +70 -70
  196. package/src/components/BaseDataTableTemplate.vue +831 -838
  197. package/src/components/BaseDatePicker.stories.js +166 -166
  198. package/src/components/BaseDatePicker.vue +372 -372
  199. package/src/components/BaseDateSelect.stories.js +68 -68
  200. package/src/components/BaseDateSelect.vue +222 -222
  201. package/src/components/BaseDescriptionList.stories.js +35 -35
  202. package/src/components/BaseDescriptionList.vue +13 -13
  203. package/src/components/BaseDescriptionListItem.vue +47 -47
  204. package/src/components/BaseDialog.stories.js +95 -95
  205. package/src/components/BaseDialog.vue +221 -221
  206. package/src/components/BaseDisplayRelativeTime.stories.js +47 -47
  207. package/src/components/BaseDisplayRelativeTime.vue +126 -126
  208. package/src/components/BaseDraggable.stories.js +34 -34
  209. package/src/components/BaseDraggable.vue +111 -111
  210. package/src/components/BaseDropdown.stories.js +164 -164
  211. package/src/components/BaseDropdown.vue +74 -74
  212. package/src/components/BaseDropdownAutocomplete.stories.js +208 -208
  213. package/src/components/BaseDropdownAutocomplete.vue +203 -203
  214. package/src/components/BaseField.vue +151 -151
  215. package/src/components/BaseFieldI18n.stories.js +37 -37
  216. package/src/components/BaseFieldI18n.vue +170 -170
  217. package/src/components/BaseFilePicker.stories.js +84 -84
  218. package/src/components/BaseFilePicker.vue +163 -163
  219. package/src/components/BaseFilePickerCrop.stories.js +135 -135
  220. package/src/components/BaseFilePickerCrop.vue +130 -130
  221. package/src/components/BaseFileUploader.stories.js +101 -101
  222. package/src/components/BaseFileUploader.vue +185 -185
  223. package/src/components/BaseForm.mdx +87 -87
  224. package/src/components/BaseForm.stories.js +133 -133
  225. package/src/components/BaseForm.vue +372 -372
  226. package/src/components/BaseGantt.stories.js +145 -145
  227. package/src/components/BaseGantt.vue +384 -384
  228. package/src/components/BaseHasMany.stories.js +211 -211
  229. package/src/components/BaseHasMany.vue +135 -135
  230. package/src/components/BaseHasManyFetch.stories.js +278 -278
  231. package/src/components/BaseHasManyFetch.vue +222 -222
  232. package/src/components/BaseHeader.stories.js +137 -137
  233. package/src/components/BaseHeader.vue +141 -141
  234. package/src/components/BaseIconPicker.stories.js +22 -22
  235. package/src/components/BaseIconPicker.vue +225 -225
  236. package/src/components/BaseInput.stories.js +202 -202
  237. package/src/components/BaseInput.vue +402 -402
  238. package/src/components/BaseInputError.vue +39 -39
  239. package/src/components/BaseInputLabel.stories.js +36 -36
  240. package/src/components/BaseInputLabel.vue +83 -83
  241. package/src/components/BaseInputPercent.stories.js +66 -66
  242. package/src/components/BaseInputPercent.vue +139 -139
  243. package/src/components/BaseJsonReader.stories.js +120 -120
  244. package/src/components/BaseJsonReader.vue +51 -51
  245. package/src/components/BaseJsonReaderItem.vue +119 -119
  246. package/src/components/BaseLayoutNotificationDropdown.vue +153 -153
  247. package/src/components/BaseLayoutNotificationItem.vue +53 -53
  248. package/src/components/BaseLayoutNotificationItemContent.vue +41 -41
  249. package/src/components/BaseLayoutSidebar.vue +300 -300
  250. package/src/components/BaseLayoutSidebarConfigurable.stories.js +217 -217
  251. package/src/components/BaseLayoutSidebarConfigurable.vue +202 -202
  252. package/src/components/BaseLayoutStacked.vue +78 -78
  253. package/src/components/BaseLayoutStackedConfigurable.stories.js +181 -181
  254. package/src/components/BaseLayoutStackedConfigurable.vue +196 -196
  255. package/src/components/BaseLazy.stories.js +59 -59
  256. package/src/components/BaseLazy.vue +80 -80
  257. package/src/components/BaseLoadingCover.stories.js +55 -55
  258. package/src/components/BaseLoadingCover.vue +101 -101
  259. package/src/components/BaseMediaGallery.vue +96 -96
  260. package/src/components/BaseMediaGalleryItem.vue +101 -101
  261. package/src/components/BaseMediaItem.stories.js +41 -41
  262. package/src/components/BaseMediaItem.vue +80 -80
  263. package/src/components/BaseMediaLibrary.stories.js +267 -267
  264. package/src/components/BaseMediaLibrary.vue +357 -357
  265. package/src/components/BaseMediaList.vue +67 -67
  266. package/src/components/BaseMediaListItem.vue +213 -213
  267. package/src/components/BaseMediaPictures.vue +64 -64
  268. package/src/components/BaseMediaPicturesItem.vue +100 -100
  269. package/src/components/BaseMediaPreview.stories.js +72 -72
  270. package/src/components/BaseMediaPreview.vue +106 -106
  271. package/src/components/BaseMenu.stories.js +134 -134
  272. package/src/components/BaseMenu.vue +187 -187
  273. package/src/components/BaseMenuItem.vue +177 -177
  274. package/src/components/BaseModalCenter.stories.js +68 -68
  275. package/src/components/BaseModalCenter.vue +128 -128
  276. package/src/components/BaseModalSide.stories.js +61 -61
  277. package/src/components/BaseModalSide.vue +130 -130
  278. package/src/components/BaseNavbar.stories.js +152 -152
  279. package/src/components/BaseNavbar.vue +191 -191
  280. package/src/components/BaseNavbarItem.vue +108 -108
  281. package/src/components/BaseNavbarItemContent.vue +124 -124
  282. package/src/components/BaseNavbarSideItem.vue +187 -187
  283. package/src/components/BaseNavbarSideItemContent.vue +126 -126
  284. package/src/components/BasePagination.stories.js +35 -35
  285. package/src/components/BasePagination.vue +266 -266
  286. package/src/components/BasePanel.stories.js +56 -56
  287. package/src/components/BasePanel.vue +42 -42
  288. package/src/components/BasePassword.stories.js +80 -80
  289. package/src/components/BasePassword.vue +87 -87
  290. package/src/components/BaseProgressCircle.stories.js +27 -27
  291. package/src/components/BaseProgressCircle.vue +80 -80
  292. package/src/components/BaseRadioGroup.stories.js +87 -87
  293. package/src/components/BaseRadioGroup.vue +124 -124
  294. package/src/components/BaseReadMore.stories.js +30 -30
  295. package/src/components/BaseReadMore.vue +73 -73
  296. package/src/components/BaseRichText.stories.js +90 -90
  297. package/src/components/BaseRichText.vue +87 -87
  298. package/src/components/BaseScrollColumn.vue +128 -128
  299. package/src/components/BaseSelect.stories.js +151 -151
  300. package/src/components/BaseSelect.vue +241 -241
  301. package/src/components/BaseShortcut.stories.js +100 -100
  302. package/src/components/BaseShortcut.vue +114 -114
  303. package/src/components/BaseSideNavigation.stories.js +85 -85
  304. package/src/components/BaseSideNavigation.vue +32 -32
  305. package/src/components/BaseSideNavigationItem.vue +99 -99
  306. package/src/components/BaseSkeleton.stories.js +36 -36
  307. package/src/components/BaseSkeleton.vue +40 -40
  308. package/src/components/BaseStatistic.stories.js +51 -51
  309. package/src/components/BaseStatistic.vue +98 -98
  310. package/src/components/BaseStepper.stories.js +94 -94
  311. package/src/components/BaseStepper.vue +72 -72
  312. package/src/components/BaseStepperItem.stories.js +65 -65
  313. package/src/components/BaseStepperItem.vue +149 -149
  314. package/src/components/BaseSwitch.stories.js +133 -133
  315. package/src/components/BaseSwitch.vue +228 -226
  316. package/src/components/BaseSystemAlert.stories.js +63 -63
  317. package/src/components/BaseSystemAlert.vue +89 -89
  318. package/src/components/BaseTabItem.vue +192 -192
  319. package/src/components/BaseTable.stories.js +214 -214
  320. package/src/components/BaseTable.vue +111 -111
  321. package/src/components/BaseTableBody.vue +14 -14
  322. package/src/components/BaseTableCell.vue +204 -204
  323. package/src/components/BaseTableColumn.vue +140 -140
  324. package/src/components/BaseTableHead.vue +38 -38
  325. package/src/components/BaseTableHeader.vue +139 -139
  326. package/src/components/BaseTableRow.vue +197 -197
  327. package/src/components/BaseTabs.stories.js +165 -165
  328. package/src/components/BaseTabs.vue +203 -203
  329. package/src/components/BaseTagAutocomplete.stories.js +271 -271
  330. package/src/components/BaseTagAutocomplete.vue +565 -565
  331. package/src/components/BaseTagAutocompleteFetch.stories.js +211 -211
  332. package/src/components/BaseTagAutocompleteFetch.vue +237 -237
  333. package/src/components/BaseTextarea.stories.js +81 -81
  334. package/src/components/BaseTextarea.vue +138 -138
  335. package/src/components/BaseTextareaAutoresize.stories.js +125 -125
  336. package/src/components/BaseTextareaAutoresize.vue +187 -187
  337. package/src/components/BaseTimePicker.stories.js +68 -68
  338. package/src/components/BaseTimePicker.vue +379 -379
  339. package/src/components/BaseTimeline.stories.js +55 -55
  340. package/src/components/BaseTimeline.vue +38 -38
  341. package/src/components/BaseTimelineItem.stories.js +77 -77
  342. package/src/components/BaseTimelineItem.vue +90 -90
  343. package/src/components/BaseToast.stories.js +50 -50
  344. package/src/components/BaseToast.vue +43 -43
  345. package/src/components/BaseTooltip.stories.js +65 -65
  346. package/src/components/BaseTooltip.vue +93 -93
  347. package/src/components/BaseUniqueCode.stories.js +36 -36
  348. package/src/components/BaseUniqueCode.vue +183 -183
  349. package/src/components/SlotComponent.ts +37 -37
  350. package/src/components/index.ts +222 -222
  351. package/src/composables/breakpoints.ts +94 -94
  352. package/src/composables/clickOutside.ts +80 -80
  353. package/src/composables/field.ts +156 -156
  354. package/src/composables/hasOptions.ts +86 -86
  355. package/src/composables/inputSize.ts +35 -35
  356. package/src/composables/isLastColumn.ts +30 -30
  357. package/src/composables/mediaQuery.ts +42 -42
  358. package/src/composables/modal.ts +73 -73
  359. package/src/composables/paginatedData.ts +76 -76
  360. package/src/composables/tooltip.ts +84 -84
  361. package/src/constants/MyConstants.ts +1 -1
  362. package/src/constants/index.ts +5 -5
  363. package/src/env.d.ts +15 -15
  364. package/src/i18n/index.ts +60 -60
  365. package/src/index.ts +138 -138
  366. package/src/lang/en.json +101 -101
  367. package/src/lang/fr.json +101 -101
  368. package/src/services/gantt/format.ts +133 -133
  369. package/src/services/gantt/timescale.ts +250 -250
  370. package/src/services/gantt/types.ts +81 -81
  371. package/src/services/table/classes.ts +37 -37
  372. package/src/services/table/customKeyActions.ts +2 -2
  373. package/src/services/table/types.ts +27 -27
  374. package/src/stores/dialogs.ts +48 -48
  375. package/src/stores/i18n.ts +14 -14
  376. package/src/stores/snackbars.ts +47 -47
  377. package/src/stores/systemAlerts.ts +32 -32
  378. package/src/stories/InputSizes.stories.js +21 -21
  379. package/src/stories/List.stories.js +136 -136
  380. package/src/stories/PageInputSizes.vue +228 -228
  381. package/src/stories/PageShow.vue +423 -423
  382. package/src/stories/Show.stories.js +21 -21
  383. package/src/svg/BaseEmptyState.vue +38 -38
  384. package/src/svg/BaseSpinnerLarge.vue +40 -40
  385. package/src/svg/BaseSpinnerSmall.vue +13 -13
  386. package/src/types/Color.ts +9 -9
  387. package/src/types/Country.ts +4 -4
  388. package/src/types/ImagePickerResult.ts +5 -5
  389. package/src/types/Media.ts +10 -10
  390. package/src/types/Notification.ts +11 -11
  391. package/src/types/Region.ts +5 -5
  392. package/src/types/Status.ts +5 -5
  393. package/src/types/StepperItem.ts +8 -8
  394. package/src/types/ToolbarOption.ts +1 -1
  395. package/src/types/UploadedFile.ts +11 -11
  396. package/src/types/User.ts +7 -7
  397. package/src/types/index.ts +302 -302
  398. package/src/utils/blob.ts +30 -30
  399. package/src/utils/colors.ts +239 -239
  400. package/src/utils/cropper/avatar.ts +33 -33
  401. package/src/utils/cropper/cover.ts +41 -41
  402. package/src/utils/cropper/presetInterface.ts +16 -16
  403. package/src/utils/cropper/presets.ts +7 -7
  404. package/src/utils/deepIncludes.ts +76 -76
  405. package/src/utils/fileSizeFormat.ts +15 -15
  406. package/src/utils/fileValidations.ts +26 -26
  407. package/src/utils/getApiData.ts +11 -11
  408. package/src/utils/index.ts +18 -18
  409. package/src/utils/resizeImageFromURI.ts +118 -118
  410. package/src/utils/scrollPreventer.ts +11 -11
  411. package/src/utils/sizeBehaviors.ts +3 -3
  412. package/src/utils/sizes.ts +38 -38
  413. package/src/utils/slots.ts +12 -12
  414. package/src/utils/storage.ts +36 -36
  415. package/src/utils/toHumanList.ts +20 -20
  416. package/src/utils/uuid.ts +7 -7
@@ -1,986 +1,986 @@
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
15
- class="flex space-x-2 empty:mb-0"
16
- :class="{
17
- 'mb-2.5': sizeInternal.size.value == 'sm',
18
- 'mb-4': sizeInternal.size.value == 'md',
19
- }"
20
- >
21
- <!-- Search bar -->
22
- <div
23
- v-if="searchable"
24
- class="grow min-w-0"
25
- >
26
- <BaseInput
27
- v-model="searchQuery"
28
- :placeholder="t('sui.autocomplete_placeholder')"
29
- :size="sizeInternal.size.value"
30
- class="w-full"
31
- :required="false"
32
- icon-left="heroicons:magnifying-glass"
33
- :icon-right="searchQuery ? 'heroicons:x-mark' : undefined"
34
- @icon-right-click="searchQuery = ''; onSearch('');"
35
- @update:model-value="onSearch"
36
- @focus="searchInputFocus = true"
37
- @blur="searchInputFocus = false"
38
- />
39
- </div>
40
-
41
- <template v-if="compactLayout">
42
- <BaseDataIteratorSectionButton
43
- v-for="(section, i) in sectionsInternal"
44
- :key="section.name"
45
- :section="section"
46
- :size="sizeInternal.size.value"
47
- @open="openSection(i)"
48
- />
49
- </template>
50
-
51
- <!-- Menu -->
52
- <BaseMenu
53
- v-if="actions && actions.length"
54
- :size="sizeInternal.size.value == 'sm' ? 'xs' : 'sm'"
55
- :items="actions"
56
- >
57
- <template #button="{ open }">
58
- <BaseButton
59
- as="div"
60
- :size="sizeInternal.size.value"
61
- :class="[
62
- open ? 'input-focus' : '',
63
- ]"
64
- icon="heroicons-solid:dots-vertical"
65
- />
66
- </template>
67
- </BaseMenu>
68
- </div>
69
-
70
- <div
71
- v-if="filtersPosition == 'top' && hasFilters"
72
- :class="{
73
- 'mb-2.5': sizeInternal.size.value == 'sm',
74
- 'mb-4': sizeInternal.size.value == 'md',
75
- }"
76
- >
77
- <BaseDataIteratorSectionBox :section="sectionFilter">
78
- <slot
79
- :name="sectionFilter.name"
80
- :query="query"
81
- :update-query="updateFilterQuery"
82
- :update-query-value="updateFilterQueryValue"
83
- :update-query-value-debounce="updateFilterQueryValueDebounce"
84
- />
85
- </BaseDataIteratorSectionBox>
86
- </div>
87
- <slot
88
- :data="data"
89
- :items="itemsInternal"
90
- :loading="loading"
91
- :error="error"
92
- :first-load="firstLoad"
93
- :page="page"
94
- :sort-field="sortField"
95
- :sort-direction="sortDirection"
96
- :on-sort-change="onSortChange"
97
- :on-page-change="onPageChange"
98
- />
99
-
100
- <!-- Pagination -->
101
-
102
- <div
103
- v-if="paginationMetadata"
104
- class="mt-4"
105
- >
106
- <p
107
- class="text-center text-sm text-slate-500 sm:text-right [&>b]:font-medium [&>b]:text-slate-600"
108
- >
109
- {{ t('sui.pagination_detail_1') }}
110
-
111
- <b>{{ paginationStart }}</b> - <b>{{ paginationEnd }}</b>
112
-
113
- {{ t('sui.pagination_detail_2') }}
114
-
115
- <b>{{ paginationMetadata.total }}</b>
116
- </p>
117
- </div>
118
-
119
- <div
120
- v-if="paginationMetadata"
121
- class="mt-4"
122
- >
123
- <BasePagination
124
- :model-value="page"
125
- :last-page="lastPage"
126
- @update:model-value="onPageChange"
127
- />
128
- </div>
129
- </div>
130
-
131
- <div
132
- v-if="!compactLayout"
133
- ref="sidebar"
134
- class="space-y-3"
135
- >
136
- <slot
137
- name="sidebarTop"
138
- :pagination-metadata="paginationMetadata"
139
- />
140
-
141
- <template
142
- v-for="section in sectionsInternal"
143
- :key="section.name"
144
- >
145
- <BaseDataIteratorSectionBox :section="section">
146
- <slot
147
- :name="section.name"
148
- :query="query"
149
- :update-query="updateFilterQuery"
150
- :update-query-value="updateFilterQueryValue"
151
- :update-query-value-debounce="updateFilterQueryValueDebounce"
152
- />
153
- </BaseDataIteratorSectionBox>
154
- </template>
155
-
156
- <slot
157
- name="sidebarBottom"
158
- :pagination-metadata="paginationMetadata"
159
- />
160
- </div>
161
- </div>
162
-
163
- <template
164
- v-for="(section, i) in sectionsInternal"
165
- :key="section.name"
166
- >
167
- <BaseDataIteratorSectionModal
168
- :section="section"
169
- :model-value="sectionModalActive == i"
170
- @update:model-value="closeSection"
171
- >
172
- <slot
173
- :name="section.name"
174
- :query="query"
175
- :update-query="updateFilterQuery"
176
- :update-query-value="updateFilterQueryValue"
177
- :update-query-value-debounce="updateFilterQueryValueDebounce"
178
- />
179
- </BaseDataIteratorSectionModal>
180
- </template>
181
- </div>
182
- </template>
183
-
184
- <script lang="ts">
185
- /* eslint-disable @typescript-eslint/no-explicit-any */
186
- import { t } from '@/i18n';
187
- type Direction = 'asc' | 'desc';
188
- import { Size } from '@/utils/sizes';
189
- import BaseInput from './BaseInput.vue';
190
- import { useInputSize } from '@/composables/inputSize';
191
- import { deepIncludes } from '@/utils/deepIncludes';
192
-
193
- const DEFAULT_QUERY = {
194
- page: 1,
195
- search: '',
196
- sort: '',
197
- filter: {},
198
- } as DataTableQuery;
199
- </script>
200
-
201
- <script lang="ts" setup>
202
- import { cloneDeep, debounce, merge, set, sortBy, uniqueId } from 'lodash';
203
- import hash from 'object-hash';
204
- import { PropType } from 'vue';
205
- import {
206
- Collection,
207
- DataIteratorSection,
208
- DataTableQuery,
209
- PaginatedCollection,
210
- ResourceCollection,
211
- } from '@/types';
212
- import { ActionItem } from '@/types';
213
-
214
- import BaseMenu from './BaseMenu.vue';
215
- import BaseButton from './BaseButton.vue';
216
- import BasePagination from './BasePagination.vue';
217
- import { config } from '@/index';
218
- import { useMutationObserver, useResizeObserver } from '@vueuse/core';
219
- import { useHasPaginatedData } from '@/composables/paginatedData';
220
- import BaseDataIteratorSectionButton from './BaseDataIteratorSectionButton.vue';
221
- import BaseDataIteratorSectionModal from './BaseDataIteratorSectionModal.vue';
222
- import BaseDataIteratorSectionBox from './BaseDataIteratorSectionBox.vue';
223
-
224
- const props = defineProps({
225
-
226
- /**
227
- * Data to display
228
- */
229
- items: {
230
- default: undefined,
231
- type: Array as PropType<Collection | undefined>,
232
- },
233
-
234
- /**
235
- * Base URL from which to make requests
236
- */
237
- url: {
238
- default: undefined,
239
- type: String as PropType<string | undefined>,
240
- },
241
-
242
- /**
243
- * Query params that always get applied.
244
- * To add overwrite-able query params, use defaultQuery.
245
- */
246
- urlQuery: {
247
- default: undefined,
248
- type: Object as PropType<Record<string, any>>,
249
- },
250
-
251
- /**
252
- * Query params that gets applied by default.
253
- * These may be overwritten by URL params generated by the data-table or filters
254
- * To add query params that are always active, use urlQuery.
255
- */
256
- defaultQuery: {
257
- default: function () {
258
- return DEFAULT_QUERY;
259
- },
260
- type: Object as PropType<DataTableQuery>,
261
- },
262
-
263
- /**
264
- * Add a search bar.
265
- */
266
- searchable: {
267
- default: true,
268
- type: Boolean,
269
- },
270
-
271
- /**
272
- * Configure contextual actions.
273
- */
274
- actions: {
275
- default: undefined,
276
- type: Array as PropType<ActionItem[]>,
277
- },
278
-
279
- /**
280
- * Save data table state in URL.
281
- */
282
- historyMode: {
283
- default: false,
284
- type: Boolean,
285
- },
286
-
287
- /**
288
- * Layout type
289
- */
290
- layout: {
291
- default: 'default',
292
- type: String as PropType<'default' | 'compact'>,
293
- },
294
-
295
- /**
296
- * Overall size and spacing of the component
297
- */
298
- size: {
299
- default: 'md',
300
- type: String as PropType<Size>,
301
- },
302
-
303
- /**
304
- * Sections
305
- */
306
- sections: {
307
- default: undefined,
308
- type: Array as PropType<DataIteratorSection[]>,
309
- },
310
-
311
- /**
312
- * Scroll to top when fetching new data
313
- */
314
- scrollTopOnFetch: {
315
- default: true,
316
- type: Boolean,
317
- },
318
-
319
- /**
320
- * Filters position
321
- */
322
- filtersPosition: {
323
- default: 'section',
324
- type: String as PropType<'section' | 'top'>,
325
- },
326
-
327
- /**
328
- * Per page (only when using local data)
329
- */
330
- perPage: {
331
- default: undefined,
332
- type: Number,
333
- },
334
-
335
- /**
336
- * Function to search local data
337
- */
338
- search: {
339
- default: undefined,
340
- type: Function as PropType<(items: Collection, search: string | null) => Collection>,
341
- }
342
- });
343
-
344
- const LOCAL = 'local';
345
- const REMOTE = 'remote';
346
-
347
- const http = config.http;
348
-
349
- const emit = defineEmits([
350
- 'click',
351
- 'delete',
352
- 'checkAll',
353
- 'update:checked-rows',
354
- 'check',
355
- 'update:query',
356
- 'will-scroll-top',
357
- 'fetch',
358
- ]);
359
-
360
- const dataMode = computed(() => {
361
- if (props.url) {
362
- return REMOTE;
363
- }
364
-
365
- return LOCAL;
366
- });
367
-
368
- const dataIteratorNode = ref<null | HTMLElement>(null);
369
- const searchInputFocus = ref(false);
370
-
371
- const route = useRoute();
372
- const router = useRouter();
373
- const routeName = route.name;
374
-
375
- const sizeInternal = useInputSize(props.size);
376
-
377
- let willUnmount = false;
378
-
379
- onBeforeUnmount(() => {
380
- willUnmount = true;
381
- });
382
-
383
- const width = ref(800);
384
- useResizeObserver(dataIteratorNode, () => {
385
- width.value = dataIteratorNode.value?.clientWidth ?? 800;
386
- });
387
-
388
- /** Data table state */
389
-
390
- const firstLoad = ref(false);
391
- const loading = ref(false);
392
- const error = ref(false);
393
- const searchQuery = ref('');
394
-
395
- let lastUrl = '';
396
- let lastQueryHash = '';
397
-
398
- // eslint-disable-next-line vue/no-setup-props-destructure
399
- const query = ref<DataTableQuery>(cloneDeep(props.defaultQuery));
400
- const slots = useSlots();
401
-
402
- const compactLayout = computed(() => {
403
- if (props.layout === 'compact') {
404
- return true;
405
- }
406
- return width.value < 1024;
407
- });
408
-
409
- const hasFilters = computed((): boolean => {
410
- const numberOfFilterSlots = slots.filters;
411
- return numberOfFilterSlots !== undefined;
412
- });
413
-
414
- /*
415
- |--------------------------------------------------------------------------
416
- | Has sidebar observer
417
- |--------------------------------------------------------------------------
418
- */
419
-
420
- const hasSidebar = ref(false);
421
- const sidebar = ref<null | HTMLElement>(null);
422
-
423
- function checkIfSidebarIsEmpty() {
424
- hasSidebar.value = (sidebar?.value?.childElementCount ?? 0) > 0;
425
- }
426
-
427
- const checkIfSidebarIsEmptyDebounce = debounce(checkIfSidebarIsEmpty, 100);
428
-
429
- useMutationObserver(sidebar, checkIfSidebarIsEmptyDebounce, {
430
- attributes: false,
431
- childList: true,
432
- });
433
-
434
- onMounted(() => {
435
- checkIfSidebarIsEmpty();
436
- });
437
-
438
- watch(
439
- () => compactLayout.value,
440
- () => {
441
- // After the sidebar appears...
442
- nextTick(() => {
443
- checkIfSidebarIsEmpty();
444
- });
445
- }
446
- );
447
-
448
- /*
449
- |--------------------------------------------------------------------------
450
- | Query params
451
- |--------------------------------------------------------------------------
452
- */
453
-
454
- function updateFilterQueryValue(key: string | DataTableQuery, value?: any) {
455
-
456
- if (typeof key === 'object') {
457
- // If the key is an object, merge it into the query
458
- let newQuery = cloneDeep(query.value);
459
- newQuery = merge(newQuery, key);
460
- newQuery = set(newQuery, 'page', 1);
461
-
462
- updateQuery(newQuery);
463
- return;
464
- }
465
-
466
- let newQuery = cloneDeep(query.value);
467
- newQuery = set(newQuery, key, value);
468
- newQuery = set(newQuery, 'page', 1);
469
- updateQuery(newQuery);
470
- }
471
-
472
- const updateFilterQueryValueDebounce = debounce((key: string, value: any) => {
473
- updateFilterQueryValue(key, value);
474
- }, 350);
475
-
476
- function updateFilterQuery(newQuery: DataTableQuery) {
477
- newQuery = set(newQuery, 'page', 1);
478
- updateQuery(newQuery);
479
- }
480
-
481
- function updateQuery(newQuery: DataTableQuery) {
482
- if (!props.historyMode) {
483
- query.value = newQuery;
484
- fetch();
485
- return;
486
- }
487
-
488
- if (route.name === null) {
489
- throw new Error('Route name is required for history mode');
490
- }
491
-
492
- const newRoute = router.resolve({
493
- name: route.name,
494
- params: route.params,
495
- });
496
-
497
- const newParams = config.formatQueryString(newQuery);
498
- const newRoutePath = newRoute.fullPath + '?' + newParams;
499
-
500
- const oldParamString = getRouteQuery();
501
- const oldParams = config.formatQueryString(oldParamString);
502
-
503
- // Push new route if different
504
- if (oldParams != newParams) {
505
- if (!firstLoad.value) {
506
- router.replace(newRoutePath);
507
- return;
508
- }
509
- router.push(newRoutePath);
510
- return;
511
- }
512
-
513
- // If the URL is unchanged, we must manually trigger the fetch() method
514
- // on first load.
515
-
516
- if (!firstLoad.value) {
517
- query.value = newQuery;
518
- fetch();
519
- }
520
- }
521
-
522
- function queryHash(query: DataTableQuery): string {
523
- return hash(query);
524
- }
525
-
526
- /*
527
- |--------------------------------------------------------------------------
528
- | Data fetching
529
- |--------------------------------------------------------------------------
530
- */
531
-
532
- const url = computed<string | undefined>(() => {
533
- return props.url;
534
- });
535
-
536
- /*
537
- |--------------------------------------------------------------------------
538
- | Handlers
539
- |--------------------------------------------------------------------------
540
- */
541
-
542
- function onPageChange(p: number) {
543
- const newQuery = cloneDeep(query.value);
544
-
545
- newQuery.page = p;
546
-
547
- updateQuery(newQuery);
548
-
549
- scrollIntoView();
550
- }
551
-
552
- function onSortChange(field: string, direction: Direction) {
553
- let newSort = field;
554
-
555
- if (newSort && direction == 'desc') {
556
- newSort = '-' + newSort;
557
- }
558
-
559
- const newQuery = cloneDeep(query.value);
560
-
561
- newQuery.page = 1;
562
- newQuery.sort = newSort;
563
-
564
- updateQuery(newQuery);
565
- }
566
-
567
- const onSearch = debounce((event: any) => {
568
- const newQuery = cloneDeep(query.value);
569
-
570
- newQuery.page = 1;
571
- newQuery.search = searchQuery.value;
572
-
573
- updateQuery(newQuery);
574
- }, dataMode.value == REMOTE ? 350 : 0);
575
-
576
- /*
577
- |--------------------------------------------------------------------------
578
- | Route watcher
579
- |--------------------------------------------------------------------------
580
- */
581
-
582
- watch(
583
- () => route.query,
584
- () => {
585
- // Wait for the willUnmount flag to be set
586
- nextTick(() => {
587
- onRouteChange();
588
- });
589
- }
590
- );
591
-
592
- watch(
593
- () => props.url,
594
- () => {
595
- fetch();
596
- }
597
- );
598
-
599
- watch(
600
- () => props.urlQuery,
601
- () => {
602
- fetch();
603
- }
604
- );
605
-
606
- function getRouteQuery() {
607
- if (!props.historyMode) {
608
- return {};
609
- }
610
- return config.parseQueryString(window.location.search.replace(/^(\?)/, ''));
611
- }
612
-
613
- function onRouteChange() {
614
- if (!props.historyMode) {
615
- return;
616
- }
617
-
618
- if (willUnmount) {
619
- return;
620
- }
621
-
622
- // Stop if route was changed
623
- if (route.name != routeName) {
624
- return;
625
- }
626
-
627
- const routeQuery = getRouteQuery();
628
- const newQuery = routeQuery as DataTableQuery;
629
-
630
- const newQueryHash = queryHash(newQuery);
631
-
632
- if (newQueryHash == lastQueryHash) {
633
- return;
634
- }
635
-
636
- lastQueryHash = newQueryHash;
637
-
638
- query.value = newQuery;
639
-
640
- // Update search input if not in focus
641
- if (!searchInputFocus.value) {
642
- updateSearchInput();
643
- }
644
-
645
- fetch();
646
- }
647
-
648
- /*
649
- |--------------------------------------------------------------------------
650
- | Fetch
651
- |--------------------------------------------------------------------------
652
- */
653
-
654
- const fullQuery = computed(() => {
655
-
656
- const urlSplit = (url.value + '').split(/[?#]/);
657
- const urlQueryString = urlSplit[1] ?? null;
658
- const urlQuery = config.parseQueryString(urlQueryString);
659
-
660
- // Ordered by priority
661
- const allParams = merge(
662
- cloneDeep(query.value),
663
- cloneDeep(props.urlQuery),
664
- cloneDeep(urlQuery)
665
- );
666
-
667
- return allParams;
668
- });
669
-
670
- function fetchWithLoading(force = false) {
671
- fetch(force, true);
672
- }
673
-
674
- function fetchWithoutLoading(force = false) {
675
- fetch(force, false);
676
- }
677
-
678
- let requestId = '';
679
-
680
- function fetch(force = false, showLoading = true) {
681
- if (willUnmount) {
682
- return;
683
- }
684
-
685
- if (url.value == null) {
686
- firstLoad.value = true;
687
- return;
688
- }
689
-
690
- const fullQueryInternal = fullQuery.value;
691
- const fullQueryString = config.formatQueryString(fullQueryInternal);
692
-
693
- const urlSplit = url.value.split(/[?#]/);
694
- const baseUrl = urlSplit[0];
695
- const fullUrl = baseUrl + '?' + fullQueryString;
696
-
697
- if (lastUrl == fullUrl && !force) {
698
- return;
699
- }
700
-
701
- const requestIdInternal = uniqueId();
702
- requestId = requestIdInternal;
703
-
704
- if (showLoading) {
705
- loading.value = true;
706
- }
707
-
708
- lastUrl = fullUrl;
709
-
710
- http
711
- .get(fullUrl)
712
- .then((response) => {
713
- if (requestIdInternal !== requestId) {
714
- return;
715
- }
716
-
717
- data.value = response.data;
718
- error.value = false;
719
- firstLoad.value = true;
720
- emit('fetch', data.value);
721
- })
722
- .catch(() => {
723
- data.value = null;
724
- error.value = true;
725
- })
726
- .finally(() => {
727
- loading.value = false;
728
- });
729
- }
730
-
731
- /*
732
- |--------------------------------------------------------------------------
733
- | Data parsing
734
- |--------------------------------------------------------------------------
735
- */
736
-
737
- const data = ref<null | ResourceCollection | PaginatedCollection | Collection>(
738
- null
739
- );
740
-
741
- const page = computed((): number => {
742
- if (query.value.page && parseInt(query.value.page + '')) {
743
- return parseInt(query.value.page + '');
744
- }
745
-
746
- return 1;
747
- });
748
-
749
- const dataInternal = computed<ResourceCollection | PaginatedCollection | Collection | null | undefined>(() => {
750
- if (dataMode.value == REMOTE) {
751
- return data.value;
752
- }
753
-
754
- return searchItems(props.items);
755
- });
756
-
757
- const { items, paginationMetadata, lastPage } = useHasPaginatedData(
758
- dataInternal,
759
- page,
760
- computed(() => {
761
- if (dataMode.value == REMOTE) {
762
- return undefined;
763
- }
764
-
765
- return props.perPage;
766
- }),
767
- );
768
-
769
- const itemsInternal = computed(() => {
770
- if (dataMode.value == REMOTE) {
771
- return items.value;
772
- }
773
-
774
- return paginateItems(sortItems(items.value));
775
- });
776
-
777
- /*
778
- |--------------------------------------------------------------------------
779
- | Local data filtering, sorting and pagination
780
- |--------------------------------------------------------------------------
781
- */
782
-
783
- function searchItems(items: Collection | undefined) {
784
-
785
- if (!items) {
786
- return [];
787
- }
788
-
789
- if (props.search) {
790
- return props.search(items, searchKeywords.value);
791
- }
792
-
793
- return items.filter((it) => deepIncludes(it, searchKeywords.value));
794
- }
795
-
796
- function sortItems(items: Collection | undefined) {
797
- if (!items) {
798
- return [];
799
- }
800
-
801
- if (!sortField.value) {
802
- return items;
803
- }
804
-
805
- if (!sortDirection.value) {
806
- return items;
807
- }
808
-
809
- if (sortDirection.value == 'asc') {
810
- return sortBy(items, sortField.value);
811
- }
812
-
813
- return sortBy(items, sortField.value).reverse();
814
- }
815
-
816
- function paginateItems(items: Collection | undefined) {
817
- if (!items) {
818
- return [];
819
- }
820
-
821
- if (!props.perPage) {
822
- return items;
823
- }
824
-
825
- return items.slice(
826
- (page.value - 1) * props.perPage,
827
- page.value * props.perPage
828
- );
829
- }
830
-
831
- const sortField = computed((): string => {
832
- return query.value.sort?.trim().replace(/^(-)/, '') ?? '';
833
- });
834
-
835
- const sortDirection = computed((): Direction => {
836
- if (query.value.sort && query.value.sort.length) {
837
- if (query.value.sort[0] == '-') {
838
- return 'desc';
839
- }
840
- }
841
- return 'asc';
842
- });
843
-
844
- const searchKeywords = computed((): string => {
845
- return query.value.search ?? '';
846
- });
847
-
848
- /*
849
- |--------------------------------------------------------------------------
850
- | Helpers
851
- |--------------------------------------------------------------------------
852
- */
853
-
854
- /** Scroll into view */
855
-
856
- const scrollIntoView = () => {
857
- emit('will-scroll-top');
858
-
859
- if (!props.scrollTopOnFetch) {
860
- return;
861
- }
862
-
863
- scrollIntoViewAction();
864
- };
865
-
866
- const scrollIntoViewAction = () => {
867
- if (dataIteratorNode.value == null) {
868
- return;
869
- }
870
-
871
- dataIteratorNode.value.scrollIntoView({
872
- behavior: 'smooth',
873
- });
874
- };
875
-
876
- function updateSearchInput() {
877
- searchQuery.value = searchKeywords.value;
878
- }
879
-
880
- const paginationStart = computed(() => {
881
- if (paginationMetadata.value == null) {
882
- return '';
883
- }
884
-
885
- return (
886
- paginationMetadata.value.per_page *
887
- (paginationMetadata.value.current_page - 1) +
888
- 1
889
- );
890
- });
891
-
892
- const paginationEnd = computed(() => {
893
- if (paginationMetadata.value == null) {
894
- return '';
895
- }
896
-
897
- return Math.min(
898
- paginationMetadata.value.current_page * paginationMetadata.value.per_page,
899
- paginationMetadata.value.total
900
- );
901
- });
902
-
903
- /*
904
- |--------------------------------------------------------------------------
905
- | Created
906
- |--------------------------------------------------------------------------
907
- */
908
-
909
- let newQuery = cloneDeep(query.value);
910
- const routeQuery = getRouteQuery();
911
-
912
- newQuery = merge(newQuery, routeQuery);
913
-
914
- updateQuery(newQuery);
915
-
916
- // Update search input on first load
917
- onMounted(() => {
918
- updateSearchInput();
919
- });
920
-
921
- /*
922
- |--------------------------------------------------------------------------
923
- | Sections
924
- |--------------------------------------------------------------------------
925
- */
926
-
927
- const sectionModalActive = ref<null | number>(null);
928
-
929
- const sectionFilter = computed(() => {
930
- return {
931
- name: 'filters',
932
- title: t('sui.filters'),
933
- closeText: t('sui.apply_filters'),
934
- icon: 'heroicons:funnel',
935
- opened: true,
936
- };
937
- });
938
-
939
- const sectionsInternal = computed<DataIteratorSection[]>(() => {
940
- const sections = props.sections ?? [];
941
-
942
- if (hasFilters.value && props.filtersPosition == 'section') {
943
- return [
944
- sectionFilter.value,
945
- ...sections,
946
- ];
947
- }
948
-
949
- return sections;
950
- });
951
-
952
- function openSection(index: number) {
953
- if (sectionsInternal.value[index]) {
954
- sectionModalActive.value = index;
955
- } else {
956
- sectionModalActive.value = null;
957
- }
958
- }
959
-
960
- function closeSection() {
961
- sectionModalActive.value = null;
962
- }
963
-
964
- /*
965
- |--------------------------------------------------------------------------
966
- | Provide
967
- |--------------------------------------------------------------------------
968
- */
969
-
970
- provide('dataIterator:width', width);
971
-
972
- /*
973
- |--------------------------------------------------------------------------
974
- | Exposed API
975
- |--------------------------------------------------------------------------
976
- */
977
-
978
- defineExpose({
979
- fetch: () => fetch(true),
980
- fetchWithLoading: () => fetchWithLoading(true),
981
- fetchWithoutLoading: () => fetchWithoutLoading(true),
982
- scrollIntoView: scrollIntoViewAction,
983
- query: computed(() => fullQuery.value),
984
- data: computed(() => dataInternal.value),
985
- });
986
- </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
15
+ class="flex space-x-2 empty:mb-0"
16
+ :class="{
17
+ 'mb-2.5': sizeInternal.size.value == 'sm',
18
+ 'mb-4': sizeInternal.size.value == 'md',
19
+ }"
20
+ >
21
+ <!-- Search bar -->
22
+ <div
23
+ v-if="searchable"
24
+ class="grow min-w-0"
25
+ >
26
+ <BaseInput
27
+ v-model="searchQuery"
28
+ :placeholder="t('sui.autocomplete_placeholder')"
29
+ :size="sizeInternal.size.value"
30
+ class="w-full"
31
+ :required="false"
32
+ icon-left="heroicons:magnifying-glass"
33
+ :icon-right="searchQuery ? 'heroicons:x-mark' : undefined"
34
+ @icon-right-click="searchQuery = ''; onSearch('');"
35
+ @update:model-value="onSearch"
36
+ @focus="searchInputFocus = true"
37
+ @blur="searchInputFocus = false"
38
+ />
39
+ </div>
40
+
41
+ <template v-if="compactLayout">
42
+ <BaseDataIteratorSectionButton
43
+ v-for="(section, i) in sectionsInternal"
44
+ :key="section.name"
45
+ :section="section"
46
+ :size="sizeInternal.size.value"
47
+ @open="openSection(i)"
48
+ />
49
+ </template>
50
+
51
+ <!-- Menu -->
52
+ <BaseMenu
53
+ v-if="actions && actions.length"
54
+ :size="sizeInternal.size.value == 'sm' ? 'xs' : 'sm'"
55
+ :items="actions"
56
+ >
57
+ <template #button="{ open }">
58
+ <BaseButton
59
+ as="div"
60
+ :size="sizeInternal.size.value"
61
+ :class="[
62
+ open ? 'input-focus' : '',
63
+ ]"
64
+ icon="heroicons-solid:dots-vertical"
65
+ />
66
+ </template>
67
+ </BaseMenu>
68
+ </div>
69
+
70
+ <div
71
+ v-if="filtersPosition == 'top' && hasFilters"
72
+ :class="{
73
+ 'mb-2.5': sizeInternal.size.value == 'sm',
74
+ 'mb-4': sizeInternal.size.value == 'md',
75
+ }"
76
+ >
77
+ <BaseDataIteratorSectionBox :section="sectionFilter">
78
+ <slot
79
+ :name="sectionFilter.name"
80
+ :query="query"
81
+ :update-query="updateFilterQuery"
82
+ :update-query-value="updateFilterQueryValue"
83
+ :update-query-value-debounce="updateFilterQueryValueDebounce"
84
+ />
85
+ </BaseDataIteratorSectionBox>
86
+ </div>
87
+ <slot
88
+ :data="data"
89
+ :items="itemsInternal"
90
+ :loading="loading"
91
+ :error="error"
92
+ :first-load="firstLoad"
93
+ :page="page"
94
+ :sort-field="sortField"
95
+ :sort-direction="sortDirection"
96
+ :on-sort-change="onSortChange"
97
+ :on-page-change="onPageChange"
98
+ />
99
+
100
+ <!-- Pagination -->
101
+
102
+ <div
103
+ v-if="paginationMetadata"
104
+ class="mt-4"
105
+ >
106
+ <p
107
+ class="text-center text-sm text-slate-500 sm:text-right [&>b]:font-medium [&>b]:text-slate-600"
108
+ >
109
+ {{ t('sui.pagination_detail_1') }}
110
+
111
+ <b>{{ paginationStart }}</b> - <b>{{ paginationEnd }}</b>
112
+
113
+ {{ t('sui.pagination_detail_2') }}
114
+
115
+ <b>{{ paginationMetadata.total }}</b>
116
+ </p>
117
+ </div>
118
+
119
+ <div
120
+ v-if="paginationMetadata"
121
+ class="mt-4"
122
+ >
123
+ <BasePagination
124
+ :model-value="page"
125
+ :last-page="lastPage"
126
+ @update:model-value="onPageChange"
127
+ />
128
+ </div>
129
+ </div>
130
+
131
+ <div
132
+ v-if="!compactLayout"
133
+ ref="sidebar"
134
+ class="space-y-3"
135
+ >
136
+ <slot
137
+ name="sidebarTop"
138
+ :pagination-metadata="paginationMetadata"
139
+ />
140
+
141
+ <template
142
+ v-for="section in sectionsInternal"
143
+ :key="section.name"
144
+ >
145
+ <BaseDataIteratorSectionBox :section="section">
146
+ <slot
147
+ :name="section.name"
148
+ :query="query"
149
+ :update-query="updateFilterQuery"
150
+ :update-query-value="updateFilterQueryValue"
151
+ :update-query-value-debounce="updateFilterQueryValueDebounce"
152
+ />
153
+ </BaseDataIteratorSectionBox>
154
+ </template>
155
+
156
+ <slot
157
+ name="sidebarBottom"
158
+ :pagination-metadata="paginationMetadata"
159
+ />
160
+ </div>
161
+ </div>
162
+
163
+ <template
164
+ v-for="(section, i) in sectionsInternal"
165
+ :key="section.name"
166
+ >
167
+ <BaseDataIteratorSectionModal
168
+ :section="section"
169
+ :model-value="sectionModalActive == i"
170
+ @update:model-value="closeSection"
171
+ >
172
+ <slot
173
+ :name="section.name"
174
+ :query="query"
175
+ :update-query="updateFilterQuery"
176
+ :update-query-value="updateFilterQueryValue"
177
+ :update-query-value-debounce="updateFilterQueryValueDebounce"
178
+ />
179
+ </BaseDataIteratorSectionModal>
180
+ </template>
181
+ </div>
182
+ </template>
183
+
184
+ <script lang="ts">
185
+ /* eslint-disable @typescript-eslint/no-explicit-any */
186
+ import { t } from '@/i18n';
187
+ type Direction = 'asc' | 'desc';
188
+ import { Size } from '@/utils/sizes';
189
+ import BaseInput from './BaseInput.vue';
190
+ import { useInputSize } from '@/composables/inputSize';
191
+ import { deepIncludes } from '@/utils/deepIncludes';
192
+
193
+ const DEFAULT_QUERY = {
194
+ page: 1,
195
+ search: '',
196
+ sort: '',
197
+ filter: {},
198
+ } as DataTableQuery;
199
+ </script>
200
+
201
+ <script lang="ts" setup>
202
+ import { cloneDeep, debounce, merge, set, sortBy, uniqueId } from 'lodash';
203
+ import hash from 'object-hash';
204
+ import { PropType } from 'vue';
205
+ import {
206
+ Collection,
207
+ DataIteratorSection,
208
+ DataTableQuery,
209
+ PaginatedCollection,
210
+ ResourceCollection,
211
+ } from '@/types';
212
+ import { ActionItem } from '@/types';
213
+
214
+ import BaseMenu from './BaseMenu.vue';
215
+ import BaseButton from './BaseButton.vue';
216
+ import BasePagination from './BasePagination.vue';
217
+ import { config } from '@/index';
218
+ import { useMutationObserver, useResizeObserver } from '@vueuse/core';
219
+ import { useHasPaginatedData } from '@/composables/paginatedData';
220
+ import BaseDataIteratorSectionButton from './BaseDataIteratorSectionButton.vue';
221
+ import BaseDataIteratorSectionModal from './BaseDataIteratorSectionModal.vue';
222
+ import BaseDataIteratorSectionBox from './BaseDataIteratorSectionBox.vue';
223
+
224
+ const props = defineProps({
225
+
226
+ /**
227
+ * Data to display
228
+ */
229
+ items: {
230
+ default: undefined,
231
+ type: Array as PropType<Collection | undefined>,
232
+ },
233
+
234
+ /**
235
+ * Base URL from which to make requests
236
+ */
237
+ url: {
238
+ default: undefined,
239
+ type: String as PropType<string | undefined>,
240
+ },
241
+
242
+ /**
243
+ * Query params that always get applied.
244
+ * To add overwrite-able query params, use defaultQuery.
245
+ */
246
+ urlQuery: {
247
+ default: undefined,
248
+ type: Object as PropType<Record<string, any>>,
249
+ },
250
+
251
+ /**
252
+ * Query params that gets applied by default.
253
+ * These may be overwritten by URL params generated by the data-table or filters
254
+ * To add query params that are always active, use urlQuery.
255
+ */
256
+ defaultQuery: {
257
+ default: function () {
258
+ return DEFAULT_QUERY;
259
+ },
260
+ type: Object as PropType<DataTableQuery>,
261
+ },
262
+
263
+ /**
264
+ * Add a search bar.
265
+ */
266
+ searchable: {
267
+ default: true,
268
+ type: Boolean,
269
+ },
270
+
271
+ /**
272
+ * Configure contextual actions.
273
+ */
274
+ actions: {
275
+ default: undefined,
276
+ type: Array as PropType<ActionItem[]>,
277
+ },
278
+
279
+ /**
280
+ * Save data table state in URL.
281
+ */
282
+ historyMode: {
283
+ default: false,
284
+ type: Boolean,
285
+ },
286
+
287
+ /**
288
+ * Layout type
289
+ */
290
+ layout: {
291
+ default: 'default',
292
+ type: String as PropType<'default' | 'compact'>,
293
+ },
294
+
295
+ /**
296
+ * Overall size and spacing of the component
297
+ */
298
+ size: {
299
+ default: 'md',
300
+ type: String as PropType<Size>,
301
+ },
302
+
303
+ /**
304
+ * Sections
305
+ */
306
+ sections: {
307
+ default: undefined,
308
+ type: Array as PropType<DataIteratorSection[]>,
309
+ },
310
+
311
+ /**
312
+ * Scroll to top when fetching new data
313
+ */
314
+ scrollTopOnFetch: {
315
+ default: true,
316
+ type: Boolean,
317
+ },
318
+
319
+ /**
320
+ * Filters position
321
+ */
322
+ filtersPosition: {
323
+ default: 'section',
324
+ type: String as PropType<'section' | 'top'>,
325
+ },
326
+
327
+ /**
328
+ * Per page (only when using local data)
329
+ */
330
+ perPage: {
331
+ default: undefined,
332
+ type: Number,
333
+ },
334
+
335
+ /**
336
+ * Function to search local data
337
+ */
338
+ search: {
339
+ default: undefined,
340
+ type: Function as PropType<(items: Collection, search: string | null) => Collection>,
341
+ }
342
+ });
343
+
344
+ const LOCAL = 'local';
345
+ const REMOTE = 'remote';
346
+
347
+ const http = config.http;
348
+
349
+ const emit = defineEmits([
350
+ 'click',
351
+ 'delete',
352
+ 'checkAll',
353
+ 'update:checked-rows',
354
+ 'check',
355
+ 'update:query',
356
+ 'will-scroll-top',
357
+ 'fetch',
358
+ ]);
359
+
360
+ const dataMode = computed(() => {
361
+ if (props.url) {
362
+ return REMOTE;
363
+ }
364
+
365
+ return LOCAL;
366
+ });
367
+
368
+ const dataIteratorNode = ref<null | HTMLElement>(null);
369
+ const searchInputFocus = ref(false);
370
+
371
+ const route = useRoute();
372
+ const router = useRouter();
373
+ const routeName = route.name;
374
+
375
+ const sizeInternal = useInputSize(props.size);
376
+
377
+ let willUnmount = false;
378
+
379
+ onBeforeUnmount(() => {
380
+ willUnmount = true;
381
+ });
382
+
383
+ const width = ref(800);
384
+ useResizeObserver(dataIteratorNode, () => {
385
+ width.value = dataIteratorNode.value?.clientWidth ?? 800;
386
+ });
387
+
388
+ /** Data table state */
389
+
390
+ const firstLoad = ref(false);
391
+ const loading = ref(false);
392
+ const error = ref(false);
393
+ const searchQuery = ref('');
394
+
395
+ let lastUrl = '';
396
+ let lastQueryHash = '';
397
+
398
+ // eslint-disable-next-line vue/no-setup-props-destructure
399
+ const query = ref<DataTableQuery>(cloneDeep(props.defaultQuery));
400
+ const slots = useSlots();
401
+
402
+ const compactLayout = computed(() => {
403
+ if (props.layout === 'compact') {
404
+ return true;
405
+ }
406
+ return width.value < 1024;
407
+ });
408
+
409
+ const hasFilters = computed((): boolean => {
410
+ const numberOfFilterSlots = slots.filters;
411
+ return numberOfFilterSlots !== undefined;
412
+ });
413
+
414
+ /*
415
+ |--------------------------------------------------------------------------
416
+ | Has sidebar observer
417
+ |--------------------------------------------------------------------------
418
+ */
419
+
420
+ const hasSidebar = ref(false);
421
+ const sidebar = ref<null | HTMLElement>(null);
422
+
423
+ function checkIfSidebarIsEmpty() {
424
+ hasSidebar.value = (sidebar?.value?.childElementCount ?? 0) > 0;
425
+ }
426
+
427
+ const checkIfSidebarIsEmptyDebounce = debounce(checkIfSidebarIsEmpty, 100);
428
+
429
+ useMutationObserver(sidebar, checkIfSidebarIsEmptyDebounce, {
430
+ attributes: false,
431
+ childList: true,
432
+ });
433
+
434
+ onMounted(() => {
435
+ checkIfSidebarIsEmpty();
436
+ });
437
+
438
+ watch(
439
+ () => compactLayout.value,
440
+ () => {
441
+ // After the sidebar appears...
442
+ nextTick(() => {
443
+ checkIfSidebarIsEmpty();
444
+ });
445
+ }
446
+ );
447
+
448
+ /*
449
+ |--------------------------------------------------------------------------
450
+ | Query params
451
+ |--------------------------------------------------------------------------
452
+ */
453
+
454
+ function updateFilterQueryValue(key: string | DataTableQuery, value?: any) {
455
+
456
+ if (typeof key === 'object') {
457
+ // If the key is an object, merge it into the query
458
+ let newQuery = cloneDeep(query.value);
459
+ newQuery = merge(newQuery, key);
460
+ newQuery = set(newQuery, 'page', 1);
461
+
462
+ updateQuery(newQuery);
463
+ return;
464
+ }
465
+
466
+ let newQuery = cloneDeep(query.value);
467
+ newQuery = set(newQuery, key, value);
468
+ newQuery = set(newQuery, 'page', 1);
469
+ updateQuery(newQuery);
470
+ }
471
+
472
+ const updateFilterQueryValueDebounce = debounce((key: string, value: any) => {
473
+ updateFilterQueryValue(key, value);
474
+ }, 350);
475
+
476
+ function updateFilterQuery(newQuery: DataTableQuery) {
477
+ newQuery = set(newQuery, 'page', 1);
478
+ updateQuery(newQuery);
479
+ }
480
+
481
+ function updateQuery(newQuery: DataTableQuery) {
482
+ if (!props.historyMode) {
483
+ query.value = newQuery;
484
+ fetch();
485
+ return;
486
+ }
487
+
488
+ if (route.name === null) {
489
+ throw new Error('Route name is required for history mode');
490
+ }
491
+
492
+ const newRoute = router.resolve({
493
+ name: route.name,
494
+ params: route.params,
495
+ });
496
+
497
+ const newParams = config.formatQueryString(newQuery);
498
+ const newRoutePath = newRoute.fullPath + '?' + newParams;
499
+
500
+ const oldParamString = getRouteQuery();
501
+ const oldParams = config.formatQueryString(oldParamString);
502
+
503
+ // Push new route if different
504
+ if (oldParams != newParams) {
505
+ if (!firstLoad.value) {
506
+ router.replace(newRoutePath);
507
+ return;
508
+ }
509
+ router.push(newRoutePath);
510
+ return;
511
+ }
512
+
513
+ // If the URL is unchanged, we must manually trigger the fetch() method
514
+ // on first load.
515
+
516
+ if (!firstLoad.value) {
517
+ query.value = newQuery;
518
+ fetch();
519
+ }
520
+ }
521
+
522
+ function queryHash(query: DataTableQuery): string {
523
+ return hash(query);
524
+ }
525
+
526
+ /*
527
+ |--------------------------------------------------------------------------
528
+ | Data fetching
529
+ |--------------------------------------------------------------------------
530
+ */
531
+
532
+ const url = computed<string | undefined>(() => {
533
+ return props.url;
534
+ });
535
+
536
+ /*
537
+ |--------------------------------------------------------------------------
538
+ | Handlers
539
+ |--------------------------------------------------------------------------
540
+ */
541
+
542
+ function onPageChange(p: number) {
543
+ const newQuery = cloneDeep(query.value);
544
+
545
+ newQuery.page = p;
546
+
547
+ updateQuery(newQuery);
548
+
549
+ scrollIntoView();
550
+ }
551
+
552
+ function onSortChange(field: string, direction: Direction) {
553
+ let newSort = field;
554
+
555
+ if (newSort && direction == 'desc') {
556
+ newSort = '-' + newSort;
557
+ }
558
+
559
+ const newQuery = cloneDeep(query.value);
560
+
561
+ newQuery.page = 1;
562
+ newQuery.sort = newSort;
563
+
564
+ updateQuery(newQuery);
565
+ }
566
+
567
+ const onSearch = debounce((event: any) => {
568
+ const newQuery = cloneDeep(query.value);
569
+
570
+ newQuery.page = 1;
571
+ newQuery.search = searchQuery.value;
572
+
573
+ updateQuery(newQuery);
574
+ }, dataMode.value == REMOTE ? 350 : 0);
575
+
576
+ /*
577
+ |--------------------------------------------------------------------------
578
+ | Route watcher
579
+ |--------------------------------------------------------------------------
580
+ */
581
+
582
+ watch(
583
+ () => route.query,
584
+ () => {
585
+ // Wait for the willUnmount flag to be set
586
+ nextTick(() => {
587
+ onRouteChange();
588
+ });
589
+ }
590
+ );
591
+
592
+ watch(
593
+ () => props.url,
594
+ () => {
595
+ fetch();
596
+ }
597
+ );
598
+
599
+ watch(
600
+ () => props.urlQuery,
601
+ () => {
602
+ fetch();
603
+ }
604
+ );
605
+
606
+ function getRouteQuery() {
607
+ if (!props.historyMode) {
608
+ return {};
609
+ }
610
+ return config.parseQueryString(window.location.search.replace(/^(\?)/, ''));
611
+ }
612
+
613
+ function onRouteChange() {
614
+ if (!props.historyMode) {
615
+ return;
616
+ }
617
+
618
+ if (willUnmount) {
619
+ return;
620
+ }
621
+
622
+ // Stop if route was changed
623
+ if (route.name != routeName) {
624
+ return;
625
+ }
626
+
627
+ const routeQuery = getRouteQuery();
628
+ const newQuery = routeQuery as DataTableQuery;
629
+
630
+ const newQueryHash = queryHash(newQuery);
631
+
632
+ if (newQueryHash == lastQueryHash) {
633
+ return;
634
+ }
635
+
636
+ lastQueryHash = newQueryHash;
637
+
638
+ query.value = newQuery;
639
+
640
+ // Update search input if not in focus
641
+ if (!searchInputFocus.value) {
642
+ updateSearchInput();
643
+ }
644
+
645
+ fetch();
646
+ }
647
+
648
+ /*
649
+ |--------------------------------------------------------------------------
650
+ | Fetch
651
+ |--------------------------------------------------------------------------
652
+ */
653
+
654
+ const fullQuery = computed(() => {
655
+
656
+ const urlSplit = (url.value + '').split(/[?#]/);
657
+ const urlQueryString = urlSplit[1] ?? null;
658
+ const urlQuery = config.parseQueryString(urlQueryString);
659
+
660
+ // Ordered by priority
661
+ const allParams = merge(
662
+ cloneDeep(query.value),
663
+ cloneDeep(props.urlQuery),
664
+ cloneDeep(urlQuery)
665
+ );
666
+
667
+ return allParams;
668
+ });
669
+
670
+ function fetchWithLoading(force = false) {
671
+ fetch(force, true);
672
+ }
673
+
674
+ function fetchWithoutLoading(force = false) {
675
+ fetch(force, false);
676
+ }
677
+
678
+ let requestId = '';
679
+
680
+ function fetch(force = false, showLoading = true) {
681
+ if (willUnmount) {
682
+ return;
683
+ }
684
+
685
+ if (url.value == null) {
686
+ firstLoad.value = true;
687
+ return;
688
+ }
689
+
690
+ const fullQueryInternal = fullQuery.value;
691
+ const fullQueryString = config.formatQueryString(fullQueryInternal);
692
+
693
+ const urlSplit = url.value.split(/[?#]/);
694
+ const baseUrl = urlSplit[0];
695
+ const fullUrl = baseUrl + '?' + fullQueryString;
696
+
697
+ if (lastUrl == fullUrl && !force) {
698
+ return;
699
+ }
700
+
701
+ const requestIdInternal = uniqueId();
702
+ requestId = requestIdInternal;
703
+
704
+ if (showLoading) {
705
+ loading.value = true;
706
+ }
707
+
708
+ lastUrl = fullUrl;
709
+
710
+ http
711
+ .get(fullUrl)
712
+ .then((response) => {
713
+ if (requestIdInternal !== requestId) {
714
+ return;
715
+ }
716
+
717
+ data.value = response.data;
718
+ error.value = false;
719
+ firstLoad.value = true;
720
+ emit('fetch', data.value);
721
+ })
722
+ .catch(() => {
723
+ data.value = null;
724
+ error.value = true;
725
+ })
726
+ .finally(() => {
727
+ loading.value = false;
728
+ });
729
+ }
730
+
731
+ /*
732
+ |--------------------------------------------------------------------------
733
+ | Data parsing
734
+ |--------------------------------------------------------------------------
735
+ */
736
+
737
+ const data = ref<null | ResourceCollection | PaginatedCollection | Collection>(
738
+ null
739
+ );
740
+
741
+ const page = computed((): number => {
742
+ if (query.value.page && parseInt(query.value.page + '')) {
743
+ return parseInt(query.value.page + '');
744
+ }
745
+
746
+ return 1;
747
+ });
748
+
749
+ const dataInternal = computed<ResourceCollection | PaginatedCollection | Collection | null | undefined>(() => {
750
+ if (dataMode.value == REMOTE) {
751
+ return data.value;
752
+ }
753
+
754
+ return searchItems(props.items);
755
+ });
756
+
757
+ const { items, paginationMetadata, lastPage } = useHasPaginatedData(
758
+ dataInternal,
759
+ page,
760
+ computed(() => {
761
+ if (dataMode.value == REMOTE) {
762
+ return undefined;
763
+ }
764
+
765
+ return props.perPage;
766
+ }),
767
+ );
768
+
769
+ const itemsInternal = computed(() => {
770
+ if (dataMode.value == REMOTE) {
771
+ return items.value;
772
+ }
773
+
774
+ return paginateItems(sortItems(items.value));
775
+ });
776
+
777
+ /*
778
+ |--------------------------------------------------------------------------
779
+ | Local data filtering, sorting and pagination
780
+ |--------------------------------------------------------------------------
781
+ */
782
+
783
+ function searchItems(items: Collection | undefined) {
784
+
785
+ if (!items) {
786
+ return [];
787
+ }
788
+
789
+ if (props.search) {
790
+ return props.search(items, searchKeywords.value);
791
+ }
792
+
793
+ return items.filter((it) => deepIncludes(it, searchKeywords.value));
794
+ }
795
+
796
+ function sortItems(items: Collection | undefined) {
797
+ if (!items) {
798
+ return [];
799
+ }
800
+
801
+ if (!sortField.value) {
802
+ return items;
803
+ }
804
+
805
+ if (!sortDirection.value) {
806
+ return items;
807
+ }
808
+
809
+ if (sortDirection.value == 'asc') {
810
+ return sortBy(items, sortField.value);
811
+ }
812
+
813
+ return sortBy(items, sortField.value).reverse();
814
+ }
815
+
816
+ function paginateItems(items: Collection | undefined) {
817
+ if (!items) {
818
+ return [];
819
+ }
820
+
821
+ if (!props.perPage) {
822
+ return items;
823
+ }
824
+
825
+ return items.slice(
826
+ (page.value - 1) * props.perPage,
827
+ page.value * props.perPage
828
+ );
829
+ }
830
+
831
+ const sortField = computed((): string => {
832
+ return query.value.sort?.trim().replace(/^(-)/, '') ?? '';
833
+ });
834
+
835
+ const sortDirection = computed((): Direction => {
836
+ if (query.value.sort && query.value.sort.length) {
837
+ if (query.value.sort[0] == '-') {
838
+ return 'desc';
839
+ }
840
+ }
841
+ return 'asc';
842
+ });
843
+
844
+ const searchKeywords = computed((): string => {
845
+ return query.value.search ?? '';
846
+ });
847
+
848
+ /*
849
+ |--------------------------------------------------------------------------
850
+ | Helpers
851
+ |--------------------------------------------------------------------------
852
+ */
853
+
854
+ /** Scroll into view */
855
+
856
+ const scrollIntoView = () => {
857
+ emit('will-scroll-top');
858
+
859
+ if (!props.scrollTopOnFetch) {
860
+ return;
861
+ }
862
+
863
+ scrollIntoViewAction();
864
+ };
865
+
866
+ const scrollIntoViewAction = () => {
867
+ if (dataIteratorNode.value == null) {
868
+ return;
869
+ }
870
+
871
+ dataIteratorNode.value.scrollIntoView({
872
+ behavior: 'smooth',
873
+ });
874
+ };
875
+
876
+ function updateSearchInput() {
877
+ searchQuery.value = searchKeywords.value;
878
+ }
879
+
880
+ const paginationStart = computed(() => {
881
+ if (paginationMetadata.value == null) {
882
+ return '';
883
+ }
884
+
885
+ return (
886
+ paginationMetadata.value.per_page *
887
+ (paginationMetadata.value.current_page - 1) +
888
+ 1
889
+ );
890
+ });
891
+
892
+ const paginationEnd = computed(() => {
893
+ if (paginationMetadata.value == null) {
894
+ return '';
895
+ }
896
+
897
+ return Math.min(
898
+ paginationMetadata.value.current_page * paginationMetadata.value.per_page,
899
+ paginationMetadata.value.total
900
+ );
901
+ });
902
+
903
+ /*
904
+ |--------------------------------------------------------------------------
905
+ | Created
906
+ |--------------------------------------------------------------------------
907
+ */
908
+
909
+ let newQuery = cloneDeep(query.value);
910
+ const routeQuery = getRouteQuery();
911
+
912
+ newQuery = merge(newQuery, routeQuery);
913
+
914
+ updateQuery(newQuery);
915
+
916
+ // Update search input on first load
917
+ onMounted(() => {
918
+ updateSearchInput();
919
+ });
920
+
921
+ /*
922
+ |--------------------------------------------------------------------------
923
+ | Sections
924
+ |--------------------------------------------------------------------------
925
+ */
926
+
927
+ const sectionModalActive = ref<null | number>(null);
928
+
929
+ const sectionFilter = computed(() => {
930
+ return {
931
+ name: 'filters',
932
+ title: t('sui.filters'),
933
+ closeText: t('sui.apply_filters'),
934
+ icon: 'heroicons:funnel',
935
+ opened: true,
936
+ };
937
+ });
938
+
939
+ const sectionsInternal = computed<DataIteratorSection[]>(() => {
940
+ const sections = props.sections ?? [];
941
+
942
+ if (hasFilters.value && props.filtersPosition == 'section') {
943
+ return [
944
+ sectionFilter.value,
945
+ ...sections,
946
+ ];
947
+ }
948
+
949
+ return sections;
950
+ });
951
+
952
+ function openSection(index: number) {
953
+ if (sectionsInternal.value[index]) {
954
+ sectionModalActive.value = index;
955
+ } else {
956
+ sectionModalActive.value = null;
957
+ }
958
+ }
959
+
960
+ function closeSection() {
961
+ sectionModalActive.value = null;
962
+ }
963
+
964
+ /*
965
+ |--------------------------------------------------------------------------
966
+ | Provide
967
+ |--------------------------------------------------------------------------
968
+ */
969
+
970
+ provide('dataIterator:width', width);
971
+
972
+ /*
973
+ |--------------------------------------------------------------------------
974
+ | Exposed API
975
+ |--------------------------------------------------------------------------
976
+ */
977
+
978
+ defineExpose({
979
+ fetch: () => fetch(true),
980
+ fetchWithLoading: () => fetchWithLoading(true),
981
+ fetchWithoutLoading: () => fetchWithoutLoading(true),
982
+ scrollIntoView: scrollIntoViewAction,
983
+ query: computed(() => fullQuery.value),
984
+ data: computed(() => dataInternal.value),
985
+ });
986
+ </script>