@sakoa/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/README.md +171 -0
  2. package/dist/App.d.ts +2 -0
  3. package/dist/cli/index.js +9243 -0
  4. package/dist/components/DemoSection.d.ts +30 -0
  5. package/dist/components/SApiKeyboard.d.ts +22 -0
  6. package/dist/components/SApiSection.d.ts +21 -0
  7. package/dist/components/SApiTable.d.ts +46 -0
  8. package/dist/components/STableOfContents.d.ts +2 -0
  9. package/dist/components/ui/SAlert.d.ts +76 -0
  10. package/dist/components/ui/SBadge.d.ts +56 -0
  11. package/dist/components/ui/SButton.d.ts +67 -0
  12. package/dist/components/ui/SCheckbox.d.ts +64 -0
  13. package/dist/components/ui/SChip.d.ts +43 -0
  14. package/dist/components/ui/SDatePicker.d.ts +77 -0
  15. package/dist/components/ui/SGlassButton.d.ts +70 -0
  16. package/dist/components/ui/SIcon.d.ts +29 -0
  17. package/dist/components/ui/SInput.d.ts +129 -0
  18. package/dist/components/ui/SKbd.d.ts +24 -0
  19. package/dist/components/ui/SKbdShortcut.d.ts +14 -0
  20. package/dist/components/ui/SSelect.d.ts +148 -0
  21. package/dist/components/ui/SSkeleton.d.ts +37 -0
  22. package/dist/components/ui/SSwitch.d.ts +61 -0
  23. package/dist/components/ui/STooltip.d.ts +82 -0
  24. package/dist/components/ui/accordion/SAccordionContent.d.ts +23 -0
  25. package/dist/components/ui/accordion/SAccordionItem.d.ts +70 -0
  26. package/dist/components/ui/accordion/SAccordionTrigger.d.ts +37 -0
  27. package/dist/components/ui/accordion/index.d.ts +4 -0
  28. package/dist/components/ui/avatar/SAvatar.d.ts +36 -0
  29. package/dist/components/ui/avatar/SAvatarFallback.d.ts +26 -0
  30. package/dist/components/ui/avatar/SAvatarGroup.d.ts +30 -0
  31. package/dist/components/ui/avatar/SAvatarImage.d.ts +23 -0
  32. package/dist/components/ui/avatar/index.d.ts +4 -0
  33. package/dist/components/ui/breadcrumb/SBreadcrumb.d.ts +22 -0
  34. package/dist/components/ui/breadcrumb/SBreadcrumbEllipsis.d.ts +17 -0
  35. package/dist/components/ui/breadcrumb/SBreadcrumbItem.d.ts +17 -0
  36. package/dist/components/ui/breadcrumb/SBreadcrumbLink.d.ts +26 -0
  37. package/dist/components/ui/breadcrumb/SBreadcrumbList.d.ts +17 -0
  38. package/dist/components/ui/breadcrumb/SBreadcrumbPage.d.ts +17 -0
  39. package/dist/components/ui/breadcrumb/SBreadcrumbSeparator.d.ts +17 -0
  40. package/dist/components/ui/breadcrumb/index.d.ts +7 -0
  41. package/dist/components/ui/card/SCard.d.ts +103 -0
  42. package/dist/components/ui/card/SCardActions.d.ts +44 -0
  43. package/dist/components/ui/card/SCardContent.d.ts +35 -0
  44. package/dist/components/ui/card/SCardFooter.d.ts +38 -0
  45. package/dist/components/ui/card/SCardHeader.d.ts +53 -0
  46. package/dist/components/ui/card/SCardMedia.d.ts +83 -0
  47. package/dist/components/ui/card/SGlassCard.d.ts +103 -0
  48. package/dist/components/ui/card/SMorphingCardContent.d.ts +18 -0
  49. package/dist/components/ui/card/index.d.ts +24 -0
  50. package/dist/components/ui/carousel/SCarousel.d.ts +166 -0
  51. package/dist/components/ui/carousel/index.d.ts +2 -0
  52. package/dist/components/ui/color-picker/SColorPickerAlphaSlider.d.ts +4 -0
  53. package/dist/components/ui/color-picker/SColorPickerCopy.d.ts +19 -0
  54. package/dist/components/ui/color-picker/SColorPickerEyeDropper.d.ts +17 -0
  55. package/dist/components/ui/color-picker/SColorPickerHueSlider.d.ts +4 -0
  56. package/dist/components/ui/color-picker/SColorPickerInputs.d.ts +2 -0
  57. package/dist/components/ui/color-picker/SColorPickerPresets.d.ts +9 -0
  58. package/dist/components/ui/color-picker/SColorPickerPreview.d.ts +2 -0
  59. package/dist/components/ui/color-picker/SColorPickerRecent.d.ts +7 -0
  60. package/dist/components/ui/color-picker/SColorPickerSpectrum.d.ts +4 -0
  61. package/dist/components/ui/color-picker/index.d.ts +11 -0
  62. package/dist/components/ui/drawer/index.d.ts +11 -0
  63. package/dist/components/ui/dropdown/SDropdownDivider.d.ts +8 -0
  64. package/dist/components/ui/dropdown/SDropdownGroup.d.ts +25 -0
  65. package/dist/components/ui/dropdown/SDropdownItem.d.ts +56 -0
  66. package/dist/components/ui/dropdown/index.d.ts +4 -0
  67. package/dist/components/ui/form/SForm.d.ts +38 -0
  68. package/dist/components/ui/form/SFormField.d.ts +31 -0
  69. package/dist/components/ui/form/index.d.ts +5 -0
  70. package/dist/components/ui/modal/index.d.ts +19 -0
  71. package/dist/components/ui/option/SOption.d.ts +32 -0
  72. package/dist/components/ui/option/SOptionGroup.d.ts +28 -0
  73. package/dist/components/ui/option/index.d.ts +2 -0
  74. package/dist/components/ui/otp/SOTP.d.ts +122 -0
  75. package/dist/components/ui/otp/SOTPGroup.d.ts +23 -0
  76. package/dist/components/ui/otp/SOTPSeparator.d.ts +17 -0
  77. package/dist/components/ui/otp/SOTPSlot.d.ts +49 -0
  78. package/dist/components/ui/otp/index.d.ts +7 -0
  79. package/dist/components/ui/otp/types.d.ts +26 -0
  80. package/dist/components/ui/otp/useOTPContext.d.ts +42 -0
  81. package/dist/components/ui/pagination/SPagination.d.ts +151 -0
  82. package/dist/components/ui/pagination/index.d.ts +2 -0
  83. package/dist/components/ui/progress/SProgress.d.ts +62 -0
  84. package/dist/components/ui/progress/SProgressRange.d.ts +91 -0
  85. package/dist/components/ui/progress/index.d.ts +4 -0
  86. package/dist/components/ui/radio/SRadio.d.ts +58 -0
  87. package/dist/components/ui/radio/SRadioGroup.d.ts +52 -0
  88. package/dist/components/ui/radio/index.d.ts +2 -0
  89. package/dist/components/ui/stepper/SStepper.d.ts +83 -0
  90. package/dist/components/ui/stepper/SStepperContent.d.ts +24 -0
  91. package/dist/components/ui/stepper/SStepperDescription.d.ts +20 -0
  92. package/dist/components/ui/stepper/SStepperIndicator.d.ts +37 -0
  93. package/dist/components/ui/stepper/SStepperItem.d.ts +37 -0
  94. package/dist/components/ui/stepper/SStepperSeparator.d.ts +5 -0
  95. package/dist/components/ui/stepper/SStepperTitle.d.ts +20 -0
  96. package/dist/components/ui/stepper/SStepperTrigger.d.ts +22 -0
  97. package/dist/components/ui/stepper/index.d.ts +11 -0
  98. package/dist/components/ui/table/STableBody.d.ts +27 -0
  99. package/dist/components/ui/table/STableCell.d.ts +55 -0
  100. package/dist/components/ui/table/STableColumn.d.ts +87 -0
  101. package/dist/components/ui/table/STableEmpty.d.ts +54 -0
  102. package/dist/components/ui/table/STableHeader.d.ts +25 -0
  103. package/dist/components/ui/table/STableRow.d.ts +38 -0
  104. package/dist/components/ui/table/STableSkeleton.d.ts +29 -0
  105. package/dist/components/ui/table/index.d.ts +98 -0
  106. package/dist/components/ui/table/useDataTable.d.ts +80 -0
  107. package/dist/components/ui/tabs/STabPane.d.ts +31 -0
  108. package/dist/components/ui/tabs/STabsContent.d.ts +21 -0
  109. package/dist/components/ui/tabs/STabsIndicator.d.ts +9 -0
  110. package/dist/components/ui/tabs/STabsTrigger.d.ts +28 -0
  111. package/dist/components/ui/tabs/index.d.ts +6 -0
  112. package/dist/components/ui/toast/SToast.d.ts +49 -0
  113. package/dist/components/ui/toast/SToastContainer.d.ts +21 -0
  114. package/dist/components/ui/toast/index.d.ts +2 -0
  115. package/dist/composables/useAsync.d.ts +134 -0
  116. package/dist/composables/useClickOutside.d.ts +69 -0
  117. package/dist/composables/useClipboard.d.ts +46 -0
  118. package/dist/composables/useDebounce.d.ts +150 -0
  119. package/dist/composables/useDialog.d.ts +118 -0
  120. package/dist/composables/useForm.d.ts +204 -0
  121. package/dist/composables/useHotkey.d.ts +128 -0
  122. package/dist/composables/useIntersectionObserver.d.ts +156 -0
  123. package/dist/composables/useLocalStorage.d.ts +120 -0
  124. package/dist/composables/useMediaQuery.d.ts +115 -0
  125. package/dist/composables/useTheme.d.ts +8 -0
  126. package/dist/composables/useToast.d.ts +1619 -0
  127. package/dist/index.d.ts +71 -0
  128. package/dist/layouts/UILayout.d.ts +2 -0
  129. package/dist/lib/utils.d.ts +2 -0
  130. package/dist/main.d.ts +0 -0
  131. package/dist/router.d.ts +2 -0
  132. package/dist/saka-ui.css +1 -0
  133. package/dist/saka-ui.js +18513 -0
  134. package/dist/saka-ui.umd.cjs +38 -0
  135. package/dist/views/docs/CustomizationView.d.ts +2 -0
  136. package/dist/views/docs/FormValidationView.d.ts +2 -0
  137. package/dist/views/docs/StylingGuideView.d.ts +2 -0
  138. package/dist/views/docs/UseAsyncView.d.ts +2 -0
  139. package/dist/views/docs/UseClickOutsideView.d.ts +124 -0
  140. package/dist/views/docs/UseClipboardView.d.ts +4 -0
  141. package/dist/views/docs/UseDebounceView.d.ts +2 -0
  142. package/dist/views/docs/UseHotkeyView.d.ts +205 -0
  143. package/dist/views/docs/UseIntersectionObserverView.d.ts +5 -0
  144. package/dist/views/docs/UseLocalStorageView.d.ts +2 -0
  145. package/dist/views/docs/UseMediaQueryView.d.ts +2 -0
  146. package/dist/views/docs/UseThemeView.d.ts +2 -0
  147. package/dist/views/examples/AuthFormView.d.ts +2 -0
  148. package/dist/views/examples/CreditCardFormView.d.ts +6 -0
  149. package/dist/views/examples/FormFieldExampleView.d.ts +2 -0
  150. package/dist/views/examples/ProjectFormView.d.ts +2 -0
  151. package/dist/views/ui/AccordionView.d.ts +2 -0
  152. package/dist/views/ui/AlertView.d.ts +2 -0
  153. package/dist/views/ui/AvatarView.d.ts +2 -0
  154. package/dist/views/ui/BadgeView.d.ts +2 -0
  155. package/dist/views/ui/BreadcrumbView.d.ts +2 -0
  156. package/dist/views/ui/ButtonView.d.ts +2 -0
  157. package/dist/views/ui/CardView.d.ts +2 -0
  158. package/dist/views/ui/CarouselView.d.ts +274 -0
  159. package/dist/views/ui/CheckboxView.d.ts +2 -0
  160. package/dist/views/ui/ChipView.d.ts +2 -0
  161. package/dist/views/ui/ColorPickerView.d.ts +2 -0
  162. package/dist/views/ui/DatePickerView.d.ts +2 -0
  163. package/dist/views/ui/DialogView.d.ts +2 -0
  164. package/dist/views/ui/DrawerView.d.ts +2 -0
  165. package/dist/views/ui/DropdownView.d.ts +2 -0
  166. package/dist/views/ui/GlassButtonView.d.ts +2 -0
  167. package/dist/views/ui/GlassCardView.d.ts +2 -0
  168. package/dist/views/ui/HomeView.d.ts +2 -0
  169. package/dist/views/ui/IconsView.d.ts +2 -0
  170. package/dist/views/ui/InputView.d.ts +2 -0
  171. package/dist/views/ui/KbdView.d.ts +2 -0
  172. package/dist/views/ui/ModalView.d.ts +2 -0
  173. package/dist/views/ui/MorphingCardView.d.ts +2 -0
  174. package/dist/views/ui/MorphingModalView.d.ts +2 -0
  175. package/dist/views/ui/OTPView.d.ts +206 -0
  176. package/dist/views/ui/PaginationView.d.ts +2 -0
  177. package/dist/views/ui/ProgressView.d.ts +2 -0
  178. package/dist/views/ui/RadioView.d.ts +2 -0
  179. package/dist/views/ui/SelectView.d.ts +2 -0
  180. package/dist/views/ui/SkeletonView.d.ts +2 -0
  181. package/dist/views/ui/StepperView.d.ts +2 -0
  182. package/dist/views/ui/SwitchView.d.ts +2 -0
  183. package/dist/views/ui/TableView.d.ts +2 -0
  184. package/dist/views/ui/TabsView.d.ts +2 -0
  185. package/dist/views/ui/ToastView.d.ts +2 -0
  186. package/dist/views/ui/TooltipView.d.ts +2 -0
  187. package/dist/vite.svg +1 -0
  188. package/package.json +64 -0
  189. package/registry/components/accordion.json +19 -0
  190. package/registry/components/alert.json +17 -0
  191. package/registry/components/avatar.json +18 -0
  192. package/registry/components/badge.json +14 -0
  193. package/registry/components/breadcrumb.json +24 -0
  194. package/registry/components/button.json +17 -0
  195. package/registry/components/card.json +23 -0
  196. package/registry/components/carousel.json +19 -0
  197. package/registry/components/checkbox.json +17 -0
  198. package/registry/components/chip.json +17 -0
  199. package/registry/components/color-picker.json +24 -0
  200. package/registry/components/date-picker.json +17 -0
  201. package/registry/components/drawer.json +26 -0
  202. package/registry/components/dropdown.json +21 -0
  203. package/registry/components/form.json +16 -0
  204. package/registry/components/glass-button.json +17 -0
  205. package/registry/components/icon.json +17 -0
  206. package/registry/components/input.json +17 -0
  207. package/registry/components/kbd.json +16 -0
  208. package/registry/components/modal.json +32 -0
  209. package/registry/components/option.json +16 -0
  210. package/registry/components/otp.json +23 -0
  211. package/registry/components/pagination.json +18 -0
  212. package/registry/components/progress.json +16 -0
  213. package/registry/components/radio.json +19 -0
  214. package/registry/components/select.json +17 -0
  215. package/registry/components/skeleton.json +14 -0
  216. package/registry/components/switch.json +17 -0
  217. package/registry/components/table.json +26 -0
  218. package/registry/components/tabs.json +19 -0
  219. package/registry/components/toast.json +19 -0
  220. package/registry/components/tooltip.json +14 -0
  221. package/registry/index.json +4 -0
  222. package/registry/source/components/ui/SAlert.vue +388 -0
  223. package/registry/source/components/ui/SBadge.vue +243 -0
  224. package/registry/source/components/ui/SButton.vue +387 -0
  225. package/registry/source/components/ui/SCheckbox.vue +310 -0
  226. package/registry/source/components/ui/SChip.vue +130 -0
  227. package/registry/source/components/ui/SDatePicker.vue +1290 -0
  228. package/registry/source/components/ui/SGlassButton.vue +547 -0
  229. package/registry/source/components/ui/SIcon.vue +78 -0
  230. package/registry/source/components/ui/SInput.vue +1054 -0
  231. package/registry/source/components/ui/SKbd.vue +96 -0
  232. package/registry/source/components/ui/SKbdShortcut.vue +36 -0
  233. package/registry/source/components/ui/SSelect.vue +1290 -0
  234. package/registry/source/components/ui/SSkeleton.vue +185 -0
  235. package/registry/source/components/ui/SSwitch.vue +275 -0
  236. package/registry/source/components/ui/STooltip.vue +491 -0
  237. package/registry/source/components/ui/accordion/SAccordion.vue +248 -0
  238. package/registry/source/components/ui/accordion/SAccordionItem.vue +353 -0
  239. package/registry/source/components/ui/accordion/index.ts +5 -0
  240. package/registry/source/components/ui/avatar/SAvatar.vue +169 -0
  241. package/registry/source/components/ui/avatar/SAvatarFallback.vue +66 -0
  242. package/registry/source/components/ui/avatar/SAvatarGroup.vue +69 -0
  243. package/registry/source/components/ui/avatar/SAvatarImage.vue +92 -0
  244. package/registry/source/components/ui/avatar/index.ts +5 -0
  245. package/registry/source/components/ui/breadcrumb/SBreadcrumb.vue +23 -0
  246. package/registry/source/components/ui/breadcrumb/SBreadcrumbEllipsis.vue +17 -0
  247. package/registry/source/components/ui/breadcrumb/SBreadcrumbItem.vue +14 -0
  248. package/registry/source/components/ui/breadcrumb/SBreadcrumbLink.vue +46 -0
  249. package/registry/source/components/ui/breadcrumb/SBreadcrumbList.vue +17 -0
  250. package/registry/source/components/ui/breadcrumb/SBreadcrumbPage.vue +15 -0
  251. package/registry/source/components/ui/breadcrumb/SBreadcrumbSeparator.vue +18 -0
  252. package/registry/source/components/ui/breadcrumb/index.ts +7 -0
  253. package/registry/source/components/ui/card/SCard.vue +517 -0
  254. package/registry/source/components/ui/card/SCardActions.vue +129 -0
  255. package/registry/source/components/ui/card/SCardContent.vue +117 -0
  256. package/registry/source/components/ui/card/SCardFooter.vue +103 -0
  257. package/registry/source/components/ui/card/SCardHeader.vue +163 -0
  258. package/registry/source/components/ui/card/SCardMedia.vue +312 -0
  259. package/registry/source/components/ui/card/index.ts +34 -0
  260. package/registry/source/components/ui/carousel/SCarousel.vue +1069 -0
  261. package/registry/source/components/ui/carousel/SCarouselSlide.vue +107 -0
  262. package/registry/source/components/ui/carousel/index.ts +3 -0
  263. package/registry/source/components/ui/color-picker/SColorPicker.vue +772 -0
  264. package/registry/source/components/ui/color-picker/SColorPickerAlphaSlider.vue +158 -0
  265. package/registry/source/components/ui/color-picker/SColorPickerCopy.vue +76 -0
  266. package/registry/source/components/ui/color-picker/SColorPickerEyeDropper.vue +68 -0
  267. package/registry/source/components/ui/color-picker/SColorPickerHueSlider.vue +138 -0
  268. package/registry/source/components/ui/color-picker/SColorPickerInputs.vue +227 -0
  269. package/registry/source/components/ui/color-picker/SColorPickerPresets.vue +87 -0
  270. package/registry/source/components/ui/color-picker/SColorPickerPreview.vue +46 -0
  271. package/registry/source/components/ui/color-picker/SColorPickerRecent.vue +74 -0
  272. package/registry/source/components/ui/color-picker/SColorPickerSpectrum.vue +149 -0
  273. package/registry/source/components/ui/color-picker/index.ts +11 -0
  274. package/registry/source/components/ui/drawer/SDrawer.vue +797 -0
  275. package/registry/source/components/ui/drawer/SDrawerClose.vue +64 -0
  276. package/registry/source/components/ui/drawer/SDrawerContent.vue +81 -0
  277. package/registry/source/components/ui/drawer/SDrawerDescription.vue +40 -0
  278. package/registry/source/components/ui/drawer/SDrawerFooter.vue +97 -0
  279. package/registry/source/components/ui/drawer/SDrawerHandle.vue +79 -0
  280. package/registry/source/components/ui/drawer/SDrawerHeader.vue +117 -0
  281. package/registry/source/components/ui/drawer/SDrawerTitle.vue +40 -0
  282. package/registry/source/components/ui/drawer/SDrawerTrigger.vue +51 -0
  283. package/registry/source/components/ui/drawer/index.ts +20 -0
  284. package/registry/source/components/ui/dropdown/SDropdown.vue +843 -0
  285. package/registry/source/components/ui/dropdown/SDropdownDivider.vue +23 -0
  286. package/registry/source/components/ui/dropdown/SDropdownGroup.vue +53 -0
  287. package/registry/source/components/ui/dropdown/SDropdownItem.vue +179 -0
  288. package/registry/source/components/ui/dropdown/index.ts +5 -0
  289. package/registry/source/components/ui/form/SForm.vue +84 -0
  290. package/registry/source/components/ui/form/SFormField.vue +78 -0
  291. package/registry/source/components/ui/form/index.ts +8 -0
  292. package/registry/source/components/ui/modal/SModal.vue +648 -0
  293. package/registry/source/components/ui/modal/SModalClose.vue +49 -0
  294. package/registry/source/components/ui/modal/SModalContent.vue +74 -0
  295. package/registry/source/components/ui/modal/SModalDescription.vue +39 -0
  296. package/registry/source/components/ui/modal/SModalFooter.vue +84 -0
  297. package/registry/source/components/ui/modal/SModalHeader.vue +107 -0
  298. package/registry/source/components/ui/modal/SModalTitle.vue +39 -0
  299. package/registry/source/components/ui/modal/SModalTrigger.vue +61 -0
  300. package/registry/source/components/ui/modal/SMorphingModal.vue +429 -0
  301. package/registry/source/components/ui/modal/SMorphingModalClose.vue +42 -0
  302. package/registry/source/components/ui/modal/SMorphingModalDescription.vue +49 -0
  303. package/registry/source/components/ui/modal/SMorphingModalImage.vue +44 -0
  304. package/registry/source/components/ui/modal/SMorphingModalSubtitle.vue +29 -0
  305. package/registry/source/components/ui/modal/SMorphingModalTitle.vue +34 -0
  306. package/registry/source/components/ui/modal/SMorphingModalTrigger.vue +95 -0
  307. package/registry/source/components/ui/modal/index.ts +32 -0
  308. package/registry/source/components/ui/option/SOption.vue +180 -0
  309. package/registry/source/components/ui/option/SOptionGroup.vue +77 -0
  310. package/registry/source/components/ui/option/index.ts +3 -0
  311. package/registry/source/components/ui/otp/SOTP.vue +843 -0
  312. package/registry/source/components/ui/otp/SOTPGroup.vue +29 -0
  313. package/registry/source/components/ui/otp/SOTPSeparator.vue +15 -0
  314. package/registry/source/components/ui/otp/SOTPSlot.vue +462 -0
  315. package/registry/source/components/ui/otp/index.ts +7 -0
  316. package/registry/source/components/ui/otp/types.ts +27 -0
  317. package/registry/source/components/ui/otp/useOTPContext.ts +62 -0
  318. package/registry/source/components/ui/pagination/SPagination.vue +923 -0
  319. package/registry/source/components/ui/pagination/index.ts +8 -0
  320. package/registry/source/components/ui/progress/SProgress.vue +635 -0
  321. package/registry/source/components/ui/progress/SProgressRange.vue +715 -0
  322. package/registry/source/components/ui/progress/index.ts +4 -0
  323. package/registry/source/components/ui/radio/SRadio.vue +407 -0
  324. package/registry/source/components/ui/radio/SRadioGroup.vue +200 -0
  325. package/registry/source/components/ui/radio/index.ts +3 -0
  326. package/registry/source/components/ui/table/SDataTable.vue +828 -0
  327. package/registry/source/components/ui/table/STableBody.vue +70 -0
  328. package/registry/source/components/ui/table/STableCell.vue +147 -0
  329. package/registry/source/components/ui/table/STableColumn.vue +120 -0
  330. package/registry/source/components/ui/table/STableEmpty.vue +159 -0
  331. package/registry/source/components/ui/table/STableHeader.vue +132 -0
  332. package/registry/source/components/ui/table/STableRow.vue +106 -0
  333. package/registry/source/components/ui/table/STableSkeleton.vue +208 -0
  334. package/registry/source/components/ui/table/index.ts +126 -0
  335. package/registry/source/components/ui/table/useDataTable.ts +519 -0
  336. package/registry/source/components/ui/tabs/STabPane.vue +130 -0
  337. package/registry/source/components/ui/tabs/STabs.vue +467 -0
  338. package/registry/source/components/ui/tabs/index.ts +7 -0
  339. package/registry/source/components/ui/toast/SToast.vue +261 -0
  340. package/registry/source/components/ui/toast/SToastContainer.vue +209 -0
  341. package/registry/source/components/ui/toast/index.ts +2 -0
  342. package/registry/source/composables/useForm.ts +960 -0
  343. package/registry/source/composables/useTheme.ts +86 -0
  344. package/registry/source/composables/useToast.ts +440 -0
  345. package/registry/source/lib/utils.ts +6 -0
@@ -0,0 +1,923 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref, watch, nextTick, onMounted, onBeforeUnmount, type VNode } from 'vue'
3
+ import { cn } from '../../../lib/utils'
4
+
5
+ defineOptions({ inheritAttrs: false })
6
+
7
+ // Types
8
+ export type PaginationVariant = 'default' | 'outlined' | 'ghost' | 'minimal' | 'dots'
9
+ export type PaginationSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
10
+ export type PaginationShape = 'rounded' | 'square' | 'pill'
11
+ export type AnimationType = 'none' | 'slide' | 'fade' | 'scale' | 'flip'
12
+
13
+ export interface Props {
14
+ /** Current page (v-model) */
15
+ modelValue?: number
16
+ /** Total number of items */
17
+ total: number
18
+ /** Items per page */
19
+ pageSize?: number
20
+ /** Maximum visible page buttons */
21
+ maxVisiblePages?: number
22
+ /** Visual variant */
23
+ variant?: PaginationVariant
24
+ /** Size of pagination buttons */
25
+ size?: PaginationSize
26
+ /** Button shape */
27
+ shape?: PaginationShape
28
+ /** Primary color */
29
+ color?: string
30
+ /** Show first/last page buttons */
31
+ showFirstLast?: boolean
32
+ /** Show prev/next buttons */
33
+ showPrevNext?: boolean
34
+ /** Show total items info */
35
+ showTotal?: boolean
36
+ /** Show quick jump input */
37
+ showQuickJump?: boolean
38
+ /** Show page size selector */
39
+ showPageSize?: boolean
40
+ /** Page size options for selector */
41
+ pageSizeOptions?: number[]
42
+ /** Disabled state */
43
+ disabled?: boolean
44
+ /** Loading state */
45
+ loading?: boolean
46
+ /** Animation type for page transitions */
47
+ animation?: AnimationType
48
+ /** Show progress bar */
49
+ showProgress?: boolean
50
+ /** Compact mode - combine prev/next into one button group */
51
+ compact?: boolean
52
+ /** Simple mode - only shows prev/next with page info */
53
+ simple?: boolean
54
+ /** Custom previous button text/icon */
55
+ prevText?: string
56
+ /** Custom next button text/icon */
57
+ nextText?: string
58
+ /** Custom first button text/icon */
59
+ firstText?: string
60
+ /** Custom last button text/icon */
61
+ lastText?: string
62
+ /** Enable keyboard navigation */
63
+ keyboard?: boolean
64
+ /** Background for the container */
65
+ background?: boolean
66
+ /** Hide single page */
67
+ hideSinglePage?: boolean
68
+ /** Format for total text */
69
+ totalFormat?: (total: number, range: [number, number]) => string
70
+ }
71
+
72
+ // Ellipsis hover state
73
+ const ellipsisStartHover = ref(false)
74
+ const ellipsisEndHover = ref(false)
75
+ const ellipsisStartRef = ref<HTMLElement | null>(null)
76
+ const ellipsisEndRef = ref<HTMLElement | null>(null)
77
+ let hoverTimeout: ReturnType<typeof setTimeout> | null = null
78
+
79
+ const props = withDefaults(defineProps<Props>(), {
80
+ modelValue: 1,
81
+ pageSize: 10,
82
+ maxVisiblePages: 7,
83
+ variant: 'default',
84
+ size: 'md',
85
+ shape: 'rounded',
86
+ color: 'var(--s-primary)',
87
+ showFirstLast: false,
88
+ showPrevNext: true,
89
+ showTotal: false,
90
+ showQuickJump: false,
91
+ showPageSize: false,
92
+ pageSizeOptions: () => [10, 20, 50, 100],
93
+ disabled: false,
94
+ loading: false,
95
+ animation: 'scale',
96
+ showProgress: false,
97
+ compact: false,
98
+ simple: false,
99
+ prevText: undefined,
100
+ nextText: undefined,
101
+ firstText: undefined,
102
+ lastText: undefined,
103
+ keyboard: true,
104
+ background: false,
105
+ hideSinglePage: false,
106
+ totalFormat: undefined
107
+ })
108
+
109
+ const emit = defineEmits<{
110
+ 'update:modelValue': [page: number]
111
+ 'update:pageSize': [size: number]
112
+ 'change': [page: number]
113
+ 'pageSizeChange': [size: number]
114
+ }>()
115
+
116
+ // Internal state
117
+ const internalPage = ref(props.modelValue)
118
+ const internalPageSize = ref(props.pageSize)
119
+ const jumpValue = ref('')
120
+ const isAnimating = ref(false)
121
+ const animationDirection = ref<'left' | 'right'>('right')
122
+
123
+ // Computed values
124
+ const totalPages = computed(() => Math.ceil(props.total / internalPageSize.value) || 1)
125
+
126
+ const currentPage = computed({
127
+ get: () => props.modelValue ?? internalPage.value,
128
+ set: (value) => {
129
+ const page = Math.max(1, Math.min(value, totalPages.value))
130
+ internalPage.value = page
131
+ emit('update:modelValue', page)
132
+ emit('change', page)
133
+ }
134
+ })
135
+
136
+ // Calculate current range
137
+ const currentRange = computed<[number, number]>(() => {
138
+ const start = (currentPage.value - 1) * internalPageSize.value + 1
139
+ const end = Math.min(currentPage.value * internalPageSize.value, props.total)
140
+ return [start, end]
141
+ })
142
+
143
+ // Format total text
144
+ const totalText = computed(() => {
145
+ if (props.totalFormat) {
146
+ return props.totalFormat(props.total, currentRange.value)
147
+ }
148
+ return `${currentRange.value[0]}-${currentRange.value[1]} of ${props.total}`
149
+ })
150
+
151
+ // Progress percentage
152
+ const progressPercent = computed(() => {
153
+ if (totalPages.value <= 1) return 100
154
+ return ((currentPage.value - 1) / (totalPages.value - 1)) * 100
155
+ })
156
+
157
+ // Whether to show pagination
158
+ const shouldShow = computed(() => {
159
+ if (props.hideSinglePage && totalPages.value <= 1) return false
160
+ return true
161
+ })
162
+
163
+ // Generate page numbers with ellipsis
164
+ const visiblePages = computed(() => {
165
+ const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = []
166
+ const total = totalPages.value
167
+ const current = currentPage.value
168
+ const max = props.maxVisiblePages
169
+
170
+ if (total <= max) {
171
+ // Show all pages
172
+ for (let i = 1; i <= total; i++) {
173
+ pages.push(i)
174
+ }
175
+ } else {
176
+ // Complex logic for ellipsis
177
+ const sideCount = Math.floor((max - 3) / 2) // pages on each side of current
178
+
179
+ // Always show first page
180
+ pages.push(1)
181
+
182
+ if (current <= sideCount + 2) {
183
+ // Near the start
184
+ for (let i = 2; i <= Math.max(max - 2, sideCount + 3); i++) {
185
+ pages.push(i)
186
+ }
187
+ pages.push('ellipsis-end')
188
+ pages.push(total)
189
+ } else if (current >= total - sideCount - 1) {
190
+ // Near the end
191
+ pages.push('ellipsis-start')
192
+ for (let i = Math.min(total - max + 3, total - sideCount - 2); i <= total; i++) {
193
+ pages.push(i)
194
+ }
195
+ } else {
196
+ // In the middle
197
+ pages.push('ellipsis-start')
198
+ for (let i = current - sideCount; i <= current + sideCount; i++) {
199
+ pages.push(i)
200
+ }
201
+ pages.push('ellipsis-end')
202
+ pages.push(total)
203
+ }
204
+ }
205
+
206
+ return pages
207
+ })
208
+
209
+ // Compute hidden pages for ellipsis dropdown
210
+ const hiddenPagesStart = computed(() => {
211
+ const pages: number[] = []
212
+ const visible = visiblePages.value.filter(p => typeof p === 'number') as number[]
213
+ const minVisible = Math.min(...visible)
214
+
215
+ for (let i = 2; i < minVisible; i++) {
216
+ pages.push(i)
217
+ }
218
+ return pages
219
+ })
220
+
221
+ const hiddenPagesEnd = computed(() => {
222
+ const pages: number[] = []
223
+ const visible = visiblePages.value.filter(p => typeof p === 'number') as number[]
224
+ const maxVisible = Math.max(...visible)
225
+
226
+ for (let i = maxVisible + 1; i < totalPages.value; i++) {
227
+ pages.push(i)
228
+ }
229
+ return pages
230
+ })
231
+
232
+ // Ellipsis hover handlers
233
+ const handleEllipsisEnter = (type: 'start' | 'end') => {
234
+ if (hoverTimeout) clearTimeout(hoverTimeout)
235
+ if (type === 'start') {
236
+ ellipsisStartHover.value = true
237
+ } else {
238
+ ellipsisEndHover.value = true
239
+ }
240
+ }
241
+
242
+ const handleEllipsisLeave = (type: 'start' | 'end') => {
243
+ hoverTimeout = setTimeout(() => {
244
+ if (type === 'start') {
245
+ ellipsisStartHover.value = false
246
+ } else {
247
+ ellipsisEndHover.value = false
248
+ }
249
+ }, 150)
250
+ }
251
+
252
+ const handleDropdownEnter = (type: 'start' | 'end') => {
253
+ if (hoverTimeout) clearTimeout(hoverTimeout)
254
+ }
255
+
256
+ const handleDropdownLeave = (type: 'start' | 'end') => {
257
+ if (type === 'start') {
258
+ ellipsisStartHover.value = false
259
+ } else {
260
+ ellipsisEndHover.value = false
261
+ }
262
+ }
263
+
264
+ const selectFromDropdown = (page: number) => {
265
+ goToPage(page)
266
+ ellipsisStartHover.value = false
267
+ ellipsisEndHover.value = false
268
+ }
269
+
270
+ // Navigation
271
+ const canGoPrev = computed(() => currentPage.value > 1 && !props.disabled && !props.loading)
272
+ const canGoNext = computed(() => currentPage.value < totalPages.value && !props.disabled && !props.loading)
273
+ const canGoFirst = computed(() => currentPage.value > 1 && !props.disabled && !props.loading)
274
+ const canGoLast = computed(() => currentPage.value < totalPages.value && !props.disabled && !props.loading)
275
+
276
+ const goToPage = async (page: number) => {
277
+ if (props.disabled || props.loading) return
278
+ if (page < 1 || page > totalPages.value) return
279
+ if (page === currentPage.value) return
280
+
281
+ animationDirection.value = page > currentPage.value ? 'right' : 'left'
282
+ isAnimating.value = true
283
+
284
+ await nextTick()
285
+ currentPage.value = page
286
+
287
+ setTimeout(() => {
288
+ isAnimating.value = false
289
+ }, 100)
290
+ }
291
+
292
+ const goFirst = () => goToPage(1)
293
+ const goLast = () => goToPage(totalPages.value)
294
+ const goPrev = () => goToPage(currentPage.value - 1)
295
+ const goNext = () => goToPage(currentPage.value + 1)
296
+
297
+ // Quick jump
298
+ const handleQuickJump = () => {
299
+ const page = parseInt(jumpValue.value)
300
+ if (!isNaN(page) && page >= 1 && page <= totalPages.value) {
301
+ goToPage(page)
302
+ }
303
+ jumpValue.value = ''
304
+ }
305
+
306
+ // Page size change
307
+ const handlePageSizeChange = (event: Event) => {
308
+ const target = event.target as HTMLSelectElement
309
+ const newSize = parseInt(target.value)
310
+ internalPageSize.value = newSize
311
+ emit('update:pageSize', newSize)
312
+ emit('pageSizeChange', newSize)
313
+
314
+ // Reset to page 1 when page size changes
315
+ goToPage(1)
316
+ }
317
+
318
+ // Keyboard navigation
319
+ const handleKeydown = (event: KeyboardEvent) => {
320
+ if (!props.keyboard || props.disabled || props.loading) return
321
+
322
+ switch (event.key) {
323
+ case 'ArrowLeft':
324
+ event.preventDefault()
325
+ goPrev()
326
+ break
327
+ case 'ArrowRight':
328
+ event.preventDefault()
329
+ goNext()
330
+ break
331
+ case 'Home':
332
+ event.preventDefault()
333
+ goFirst()
334
+ break
335
+ case 'End':
336
+ event.preventDefault()
337
+ goLast()
338
+ break
339
+ }
340
+ }
341
+
342
+ // Size classes
343
+ const sizeClasses = computed(() => {
344
+ const sizes = {
345
+ xs: 'h-6 min-w-6 text-xs px-1.5',
346
+ sm: 'h-8 min-w-8 text-sm px-2',
347
+ md: 'h-10 min-w-10 text-sm px-3',
348
+ lg: 'h-12 min-w-12 text-base px-4',
349
+ xl: 'h-14 min-w-14 text-lg px-5'
350
+ }
351
+ return sizes[props.size]
352
+ })
353
+
354
+ const iconSizes = computed(() => {
355
+ const sizes = {
356
+ xs: 'text-sm',
357
+ sm: 'text-base',
358
+ md: 'text-lg',
359
+ lg: 'text-xl',
360
+ xl: 'text-2xl'
361
+ }
362
+ return sizes[props.size]
363
+ })
364
+
365
+ const gapClasses = computed(() => {
366
+ const gaps = {
367
+ xs: 'gap-0.5',
368
+ sm: 'gap-1',
369
+ md: 'gap-1.5',
370
+ lg: 'gap-2',
371
+ xl: 'gap-2.5'
372
+ }
373
+ return gaps[props.size]
374
+ })
375
+
376
+ // Shape classes
377
+ const shapeClasses = computed(() => {
378
+ const shapes = {
379
+ rounded: 'rounded-lg',
380
+ square: 'rounded-none',
381
+ pill: 'rounded-full'
382
+ }
383
+ return shapes[props.shape]
384
+ })
385
+
386
+ // Active text color: use primary-foreground for default primary, white for custom colors
387
+ const activeTextColor = computed(() => props.color === 'var(--s-primary)' ? 'text-primary-foreground' : 'text-white')
388
+
389
+ // Button styles based on variant
390
+ const getButtonClasses = (isActive: boolean, isDisabled: boolean) => {
391
+ const base = `
392
+ relative flex items-center justify-center
393
+ font-medium transition-all duration-200 ease-out
394
+ select-none cursor-pointer
395
+ focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2
396
+ ${sizeClasses.value} ${shapeClasses.value}
397
+ `
398
+
399
+ if (isDisabled) {
400
+ return `${base} opacity-40 cursor-not-allowed pointer-events-none`
401
+ }
402
+
403
+ if (props.variant === 'default') {
404
+ return isActive
405
+ ? `${base} ${activeTextColor.value} shadow-md hover:shadow-lg`
406
+ : `${base} bg-muted text-foreground hover:bg-accent border border-border`
407
+ }
408
+
409
+ if (props.variant === 'outlined') {
410
+ return isActive
411
+ ? `${base} border-2 ${activeTextColor.value} shadow-md`
412
+ : `${base} border-2 border-border text-muted-foreground hover:border-muted-foreground hover:text-foreground`
413
+ }
414
+
415
+ if (props.variant === 'ghost') {
416
+ return isActive
417
+ ? `${base} ${activeTextColor.value}`
418
+ : `${base} text-muted-foreground hover:bg-muted hover:text-foreground`
419
+ }
420
+
421
+ if (props.variant === 'minimal') {
422
+ return isActive
423
+ ? `${base} font-bold`
424
+ : `${base} text-muted-foreground hover:text-foreground`
425
+ }
426
+
427
+ if (props.variant === 'dots') {
428
+ return isActive
429
+ ? `${base} !w-3 !h-3 !min-w-3 !p-0 rounded-full shadow-md`
430
+ : `${base} !w-2 !h-2 !min-w-2 !p-0 rounded-full bg-muted-foreground hover:bg-muted-foreground`
431
+ }
432
+
433
+ return base
434
+ }
435
+
436
+ // Active button style
437
+ const getActiveStyle = computed(() => {
438
+ const color = props.color
439
+ return {
440
+ backgroundColor: color,
441
+ '--tw-ring-color': `${color}50`,
442
+ borderColor: color
443
+ }
444
+ })
445
+
446
+ // Animation classes
447
+ const animationClass = computed(() => {
448
+ if (props.animation === 'none') return ''
449
+
450
+ return {
451
+ 'slide': isAnimating.value
452
+ ? `animate-pagination-slide-${animationDirection.value}`
453
+ : '',
454
+ 'fade': isAnimating.value ? 'animate-pagination-fade' : '',
455
+ 'scale': 'transition-transform hover:scale-110 active:scale-95',
456
+ 'flip': isAnimating.value ? 'animate-pagination-flip' : ''
457
+ }[props.animation] || ''
458
+ })
459
+
460
+ // Watch for external page changes
461
+ watch(() => props.modelValue, (newVal) => {
462
+ if (newVal !== undefined) {
463
+ internalPage.value = newVal
464
+ }
465
+ })
466
+
467
+ watch(() => props.pageSize, (newVal) => {
468
+ internalPageSize.value = newVal
469
+ })
470
+ </script>
471
+
472
+ <template>
473
+ <nav
474
+ v-if="shouldShow"
475
+ v-bind="$attrs"
476
+ :class="cn(
477
+ 's-pagination flex flex-wrap items-center',
478
+ gapClasses,
479
+ background && 'p-3 rounded-xl bg-muted border border-border',
480
+ (disabled || loading) && 'opacity-60 pointer-events-none',
481
+ $attrs.class ?? ''
482
+ )"
483
+ role="navigation"
484
+ aria-label="Pagination"
485
+ tabindex="0"
486
+ @keydown="handleKeydown"
487
+ >
488
+ <!-- Loading overlay -->
489
+ <Transition name="fade">
490
+ <div
491
+ v-if="loading"
492
+ class="absolute inset-0 flex items-center justify-center bg-background/50 rounded-xl z-10"
493
+ >
494
+ <span class="mdi mdi-loading animate-spin text-2xl" :style="{ color }"></span>
495
+ </div>
496
+ </Transition>
497
+
498
+ <!-- Total info -->
499
+ <div
500
+ v-if="showTotal && !simple"
501
+ class="flex items-center text-muted-foreground text-sm mr-2"
502
+ >
503
+ <slot name="total" :total="total" :range="currentRange">
504
+ <span>{{ totalText }}</span>
505
+ </slot>
506
+ </div>
507
+
508
+ <!-- Page size selector -->
509
+ <div v-if="showPageSize && !simple" class="flex items-center gap-1.5 mr-2">
510
+ <slot name="pageSize" :size="internalPageSize" :options="pageSizeOptions" :change="handlePageSizeChange">
511
+ <select
512
+ :value="internalPageSize"
513
+ class="h-9 px-2 rounded-lg bg-muted border border-border text-foreground text-sm cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary/50"
514
+ :disabled="disabled || loading"
515
+ @change="handlePageSizeChange"
516
+ >
517
+ <option
518
+ v-for="option in pageSizeOptions"
519
+ :key="option"
520
+ :value="option"
521
+ >
522
+ {{ option }} / page
523
+ </option>
524
+ </select>
525
+ </slot>
526
+ </div>
527
+
528
+ <!-- Simple mode -->
529
+ <template v-if="simple">
530
+ <button
531
+ type="button"
532
+ class="flex items-center gap-1.5"
533
+ :class="getButtonClasses(false, !canGoPrev)"
534
+ :disabled="!canGoPrev"
535
+ @click="goPrev"
536
+ >
537
+ <slot name="prev">
538
+ <span v-if="prevText">{{ prevText }}</span>
539
+ <span v-else class="mdi mdi-chevron-left" :class="iconSizes"></span>
540
+ </slot>
541
+ </button>
542
+
543
+ <div class="flex items-center gap-2 px-4 text-foreground font-medium">
544
+ <slot name="simple-content" :current="currentPage" :total="totalPages">
545
+ <span :style="{ color }">{{ currentPage }}</span>
546
+ <span class="text-muted-foreground">/</span>
547
+ <span class="text-muted-foreground">{{ totalPages }}</span>
548
+ </slot>
549
+ </div>
550
+
551
+ <button
552
+ type="button"
553
+ class="flex items-center gap-1.5"
554
+ :class="getButtonClasses(false, !canGoNext)"
555
+ :disabled="!canGoNext"
556
+ @click="goNext"
557
+ >
558
+ <slot name="next">
559
+ <span v-if="nextText">{{ nextText }}</span>
560
+ <span v-else class="mdi mdi-chevron-right" :class="iconSizes"></span>
561
+ </slot>
562
+ </button>
563
+ </template>
564
+
565
+ <!-- Full pagination -->
566
+ <template v-else>
567
+ <!-- First button -->
568
+ <button
569
+ v-if="showFirstLast"
570
+ type="button"
571
+ :class="getButtonClasses(false, !canGoFirst)"
572
+ :disabled="!canGoFirst"
573
+ aria-label="First page"
574
+ @click="goFirst"
575
+ >
576
+ <slot name="first">
577
+ <span v-if="firstText">{{ firstText }}</span>
578
+ <span v-else class="mdi mdi-chevron-double-left" :class="iconSizes"></span>
579
+ </slot>
580
+ </button>
581
+
582
+ <!-- Previous button -->
583
+ <button
584
+ v-if="showPrevNext"
585
+ type="button"
586
+ :class="getButtonClasses(false, !canGoPrev)"
587
+ :disabled="!canGoPrev"
588
+ aria-label="Previous page"
589
+ @click="goPrev"
590
+ >
591
+ <slot name="prev">
592
+ <span v-if="prevText">{{ prevText }}</span>
593
+ <span v-else class="mdi mdi-chevron-left" :class="iconSizes"></span>
594
+ </slot>
595
+ </button>
596
+
597
+ <!-- Page buttons -->
598
+ <div
599
+ class="flex items-center"
600
+ :class="gapClasses"
601
+ >
602
+ <template v-for="page in visiblePages" :key="page">
603
+ <!-- Ellipsis Start with Dropdown -->
604
+ <div
605
+ v-if="page === 'ellipsis-start'"
606
+ ref="ellipsisStartRef"
607
+ class="relative"
608
+ @mouseenter="handleEllipsisEnter('start')"
609
+ @mouseleave="handleEllipsisLeave('start')"
610
+ >
611
+ <span
612
+ class="flex items-center justify-center text-muted-foreground cursor-pointer hover:text-muted-foreground transition-colors"
613
+ :class="sizeClasses"
614
+ >
615
+ <slot name="ellipsis">
616
+ <span class="mdi mdi-dots-horizontal"></span>
617
+ </slot>
618
+ </span>
619
+
620
+ <!-- Dropdown for hidden pages -->
621
+ <Transition
622
+ enter-active-class="transition-all duration-100 ease-out"
623
+ enter-from-class="opacity-0 scale-95 -translate-y-1"
624
+ enter-to-class="opacity-100 scale-100 translate-y-0"
625
+ leave-active-class="transition-all duration-75 ease-in"
626
+ leave-from-class="opacity-100 scale-100"
627
+ leave-to-class="opacity-0 scale-95"
628
+ >
629
+ <div
630
+ v-if="ellipsisStartHover && hiddenPagesStart.length > 0"
631
+ class="absolute top-full left-1/2 -translate-x-1/2 mt-1 z-50 p-1.5 rounded-lg bg-background border border-border shadow-xl backdrop-blur-xl max-h-48 overflow-y-auto"
632
+ @mouseenter="handleDropdownEnter('start')"
633
+ @mouseleave="handleDropdownLeave('start')"
634
+ >
635
+ <div class="flex flex-wrap gap-1 min-w-max" :style="{ maxWidth: '200px' }">
636
+ <button
637
+ v-for="p in hiddenPagesStart"
638
+ :key="p"
639
+ type="button"
640
+ class="flex items-center justify-center text-sm min-w-8 h-8 px-2 rounded-sm text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
641
+ @click="selectFromDropdown(p)"
642
+ >
643
+ {{ p }}
644
+ </button>
645
+ </div>
646
+ </div>
647
+ </Transition>
648
+ </div>
649
+
650
+ <!-- Ellipsis End with Dropdown -->
651
+ <div
652
+ v-else-if="page === 'ellipsis-end'"
653
+ ref="ellipsisEndRef"
654
+ class="relative"
655
+ @mouseenter="handleEllipsisEnter('end')"
656
+ @mouseleave="handleEllipsisLeave('end')"
657
+ >
658
+ <span
659
+ class="flex items-center justify-center text-muted-foreground cursor-pointer hover:text-muted-foreground transition-colors"
660
+ :class="sizeClasses"
661
+ >
662
+ <slot name="ellipsis">
663
+ <span class="mdi mdi-dots-horizontal"></span>
664
+ </slot>
665
+ </span>
666
+
667
+ <!-- Dropdown for hidden pages -->
668
+ <Transition
669
+ enter-active-class="transition-all duration-100 ease-out"
670
+ enter-from-class="opacity-0 scale-95 -translate-y-1"
671
+ enter-to-class="opacity-100 scale-100 translate-y-0"
672
+ leave-active-class="transition-all duration-75 ease-in"
673
+ leave-from-class="opacity-100 scale-100"
674
+ leave-to-class="opacity-0 scale-95"
675
+ >
676
+ <div
677
+ v-if="ellipsisEndHover && hiddenPagesEnd.length > 0"
678
+ class="absolute top-full left-1/2 -translate-x-1/2 mt-1 z-50 p-1.5 rounded-lg bg-background border border-border shadow-xl backdrop-blur-xl max-h-48 overflow-y-auto"
679
+ @mouseenter="handleDropdownEnter('end')"
680
+ @mouseleave="handleDropdownLeave('end')"
681
+ >
682
+ <div class="flex flex-wrap gap-1 min-w-max" :style="{ maxWidth: '200px' }">
683
+ <button
684
+ v-for="p in hiddenPagesEnd"
685
+ :key="p"
686
+ type="button"
687
+ class="flex items-center justify-center text-sm min-w-8 h-8 px-2 rounded-sm text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
688
+ @click="selectFromDropdown(p)"
689
+ >
690
+ {{ p }}
691
+ </button>
692
+ </div>
693
+ </div>
694
+ </Transition>
695
+ </div>
696
+
697
+ <!-- Page button -->
698
+ <button
699
+ v-else
700
+ type="button"
701
+ :class="[
702
+ getButtonClasses(page === currentPage, false),
703
+ animationClass,
704
+ { 's-pagination__page--active': page === currentPage }
705
+ ]"
706
+ :style="page === currentPage ? getActiveStyle : undefined"
707
+ :aria-current="page === currentPage ? 'page' : undefined"
708
+ :aria-label="`Page ${page}`"
709
+ @click="goToPage(page)"
710
+ >
711
+ <slot name="page" :page="page" :active="page === currentPage">
712
+ <template v-if="variant === 'dots'">
713
+ <span class="sr-only">{{ page }}</span>
714
+ </template>
715
+ <template v-else>
716
+ {{ page }}
717
+ </template>
718
+ </slot>
719
+ </button>
720
+ </template>
721
+ </div>
722
+
723
+ <!-- Next button -->
724
+ <button
725
+ v-if="showPrevNext"
726
+ type="button"
727
+ :class="getButtonClasses(false, !canGoNext)"
728
+ :disabled="!canGoNext"
729
+ aria-label="Next page"
730
+ @click="goNext"
731
+ >
732
+ <slot name="next">
733
+ <span v-if="nextText">{{ nextText }}</span>
734
+ <span v-else class="mdi mdi-chevron-right" :class="iconSizes"></span>
735
+ </slot>
736
+ </button>
737
+
738
+ <!-- Last button -->
739
+ <button
740
+ v-if="showFirstLast"
741
+ type="button"
742
+ :class="getButtonClasses(false, !canGoLast)"
743
+ :disabled="!canGoLast"
744
+ aria-label="Last page"
745
+ @click="goLast"
746
+ >
747
+ <slot name="last">
748
+ <span v-if="lastText">{{ lastText }}</span>
749
+ <span v-else class="mdi mdi-chevron-double-right" :class="iconSizes"></span>
750
+ </slot>
751
+ </button>
752
+ </template>
753
+
754
+ <!-- Quick jump -->
755
+ <div v-if="showQuickJump && !simple" class="flex items-center gap-2 ml-2">
756
+ <slot name="quickJump" :value="jumpValue" :jump="handleQuickJump">
757
+ <span class="text-sm text-muted-foreground">Go to</span>
758
+ <input
759
+ v-model="jumpValue"
760
+ type="number"
761
+ :min="1"
762
+ :max="totalPages"
763
+ class="w-16 h-9 px-2 rounded-lg bg-muted border border-border text-foreground text-sm text-center focus:outline-none focus:ring-2 focus:ring-primary/50 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
764
+ :disabled="disabled || loading"
765
+ placeholder="#"
766
+ @keydown.enter="handleQuickJump"
767
+ />
768
+ </slot>
769
+ </div>
770
+
771
+ <!-- Progress bar -->
772
+ <div
773
+ v-if="showProgress"
774
+ class="w-full h-1 mt-2 rounded-full bg-accent overflow-hidden"
775
+ >
776
+ <div
777
+ class="h-full rounded-full transition-all duration-300 ease-out"
778
+ :style="{
779
+ width: `${progressPercent}%`,
780
+ backgroundColor: color
781
+ }"
782
+ ></div>
783
+ </div>
784
+ </nav>
785
+ </template>
786
+
787
+ <style scoped>
788
+ .s-pagination {
789
+ position: relative;
790
+ }
791
+
792
+ /* Focus styles */
793
+ .s-pagination:focus-visible {
794
+ outline: 2px solid var(--s-primary);
795
+ outline-offset: 2px;
796
+ border-radius: 0.75rem;
797
+ }
798
+
799
+ button:focus-visible {
800
+ outline: none;
801
+ box-shadow: 0 0 0 2px var(--s-background), 0 0 0 4px var(--s-primary);
802
+ }
803
+
804
+ /* Ripple effect for buttons */
805
+ .s-pagination button::after {
806
+ content: '';
807
+ position: absolute;
808
+ inset: 0;
809
+ border-radius: inherit;
810
+ background: radial-gradient(circle at center, rgba(255,255,255,0.3) 0%, transparent 70%);
811
+ opacity: 0;
812
+ transform: scale(0);
813
+ transition: opacity 0.2s, transform 0.3s;
814
+ }
815
+
816
+ .s-pagination button:active::after {
817
+ opacity: 1;
818
+ transform: scale(1);
819
+ transition: opacity 0s, transform 0s;
820
+ }
821
+
822
+ /* Animations - Fast and clean to avoid digit overlap */
823
+ .pagination-slide-enter-active,
824
+ .pagination-slide-leave-active {
825
+ transition: all 0.1s ease-out;
826
+ }
827
+
828
+ .pagination-slide-enter-from {
829
+ opacity: 0;
830
+ transform: translateX(-6px);
831
+ }
832
+
833
+ .pagination-slide-leave-to {
834
+ opacity: 0;
835
+ transform: translateX(6px);
836
+ }
837
+
838
+ .pagination-fade-enter-active,
839
+ .pagination-fade-leave-active {
840
+ transition: opacity 0.08s ease;
841
+ }
842
+
843
+ .pagination-fade-enter-from,
844
+ .pagination-fade-leave-to {
845
+ opacity: 0;
846
+ }
847
+
848
+ .pagination-scale-enter-active,
849
+ .pagination-scale-leave-active {
850
+ transition: all 0.1s ease-out;
851
+ }
852
+
853
+ .pagination-scale-enter-from,
854
+ .pagination-scale-leave-to {
855
+ opacity: 0;
856
+ transform: scale(0.9);
857
+ }
858
+
859
+ .pagination-flip-enter-active,
860
+ .pagination-flip-leave-active {
861
+ transition: all 0.12s ease-out;
862
+ }
863
+
864
+ .pagination-flip-enter-from {
865
+ opacity: 0;
866
+ transform: rotateY(-45deg);
867
+ }
868
+
869
+ .pagination-flip-leave-to {
870
+ opacity: 0;
871
+ transform: rotateY(45deg);
872
+ }
873
+
874
+ /* Active page pulse animation */
875
+ .s-pagination__page--active {
876
+ animation: pagination-pulse 0.15s ease-out;
877
+ }
878
+
879
+ @keyframes pagination-pulse {
880
+ 0% {
881
+ transform: scale(0.9);
882
+ }
883
+ 50% {
884
+ transform: scale(1.05);
885
+ }
886
+ 100% {
887
+ transform: scale(1);
888
+ }
889
+ }
890
+
891
+ /* Hover glow effect */
892
+ .s-pagination button:not(:disabled):hover {
893
+ box-shadow: 0 0 0 2px var(--s-border);
894
+ }
895
+
896
+ .s-pagination__page--active:hover {
897
+ box-shadow: 0 4px 12px -2px var(--btn-glow, rgba(0,0,0,0.2));
898
+ }
899
+
900
+ /* Fade transition */
901
+ .fade-enter-active,
902
+ .fade-leave-active {
903
+ transition: opacity 0.2s ease;
904
+ }
905
+
906
+ .fade-enter-from,
907
+ .fade-leave-to {
908
+ opacity: 0;
909
+ }
910
+
911
+ /* Screen reader only */
912
+ .sr-only {
913
+ position: absolute;
914
+ width: 1px;
915
+ height: 1px;
916
+ padding: 0;
917
+ margin: -1px;
918
+ overflow: hidden;
919
+ clip: rect(0, 0, 0, 0);
920
+ white-space: nowrap;
921
+ border: 0;
922
+ }
923
+ </style>