@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,648 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SModal - Advanced Modal/Dialog Component
4
+ * A fully accessible, animated modal with keyboard navigation and focus trapping
5
+ */
6
+ import { type InjectionKey, type Ref } from 'vue'
7
+
8
+ // Types
9
+ export type ModalSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full'
10
+ export type ModalVariant = 'default' | 'glass' | 'bordered' | 'elevated'
11
+ export type ModalAnimation = 'fade' | 'scale' | 'slide-up' | 'slide-down' | 'zoom' | 'flip'
12
+ export type ModalPosition = 'center' | 'top' | 'bottom' | 'left' | 'right'
13
+
14
+ export interface SModalContext {
15
+ close: () => void
16
+ isOpen: Ref<boolean>
17
+ size: ModalSize
18
+ hasHeader: Ref<boolean>
19
+ hasFooter: Ref<boolean>
20
+ setHasHeader: (value: boolean) => void
21
+ setHasFooter: (value: boolean) => void
22
+ titleId: string
23
+ descriptionId: string
24
+ }
25
+
26
+ export const SModalContextKey: InjectionKey<SModalContext> = Symbol('SModalContext')
27
+ </script>
28
+
29
+ <script setup lang="ts">
30
+ defineOptions({ inheritAttrs: false })
31
+
32
+ import { ref, computed, provide, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
33
+ import { cn } from '../../../lib/utils'
34
+
35
+ export interface Props {
36
+ /** Control modal visibility */
37
+ modelValue?: boolean
38
+ /** Modal size variant */
39
+ size?: ModalSize
40
+ /** Visual variant */
41
+ variant?: ModalVariant
42
+ /** Animation style */
43
+ animation?: ModalAnimation
44
+ /** Position on screen */
45
+ position?: ModalPosition
46
+ /** Show close button */
47
+ closable?: boolean
48
+ /** Close on backdrop click */
49
+ closeOnBackdrop?: boolean
50
+ /** Close on escape key */
51
+ closeOnEscape?: boolean
52
+ /** Lock body scroll when open */
53
+ lockScroll?: boolean
54
+ /** Trap focus within modal */
55
+ trapFocus?: boolean
56
+ /** Show backdrop overlay */
57
+ backdrop?: boolean
58
+ /** Backdrop blur effect */
59
+ backdropBlur?: boolean
60
+ /** Custom backdrop class */
61
+ backdropClass?: string
62
+ /** Custom modal panel class */
63
+ panelClass?: string
64
+ /** Teleport target */
65
+ teleport?: boolean | string
66
+ /** Z-index for the modal */
67
+ zIndex?: number
68
+ /** Persistent mode (can't be closed by backdrop/escape) */
69
+ persistent?: boolean
70
+ /** Fullscreen mode */
71
+ fullscreen?: boolean
72
+ /** Modal title (alternative to header slot) */
73
+ title?: string
74
+ /** Modal description */
75
+ description?: string
76
+ /** Hide the default header */
77
+ hideHeader?: boolean
78
+ /** Initial focus selector */
79
+ initialFocus?: string
80
+ /** Auto-focus first interactive element (default: false, focuses panel instead) */
81
+ autoFocus?: boolean
82
+ /** Stacked modal order (for nested modals) */
83
+ stackOrder?: number
84
+ }
85
+
86
+ const props = withDefaults(defineProps<Props>(), {
87
+ modelValue: false,
88
+ size: 'md',
89
+ variant: 'default',
90
+ animation: 'scale',
91
+ position: 'center',
92
+ closable: true,
93
+ closeOnBackdrop: true,
94
+ closeOnEscape: true,
95
+ lockScroll: true,
96
+ trapFocus: true,
97
+ backdrop: true,
98
+ backdropBlur: false,
99
+ backdropClass: '',
100
+ panelClass: '',
101
+ teleport: true,
102
+ zIndex: 1000,
103
+ persistent: false,
104
+ fullscreen: false,
105
+ title: undefined,
106
+ description: undefined,
107
+ hideHeader: false,
108
+ initialFocus: undefined,
109
+ autoFocus: false,
110
+ stackOrder: 0
111
+ })
112
+
113
+ const emit = defineEmits<{
114
+ 'update:modelValue': [value: boolean]
115
+ 'open': []
116
+ 'close': []
117
+ 'opened': []
118
+ 'closed': []
119
+ 'before-open': []
120
+ 'before-close': []
121
+ }>()
122
+
123
+ // Generate unique IDs for ARIA
124
+ const uid = Math.random().toString(36).slice(2, 9)
125
+ const titleId = `modal-title-${uid}`
126
+ const descriptionId = `modal-desc-${uid}`
127
+
128
+ // Refs
129
+ const modalRef = ref<HTMLElement | null>(null)
130
+ const backdropRef = ref<HTMLElement | null>(null)
131
+ const panelRef = ref<HTMLElement | null>(null)
132
+ const isOpen = ref(props.modelValue)
133
+ const isAnimating = ref(false)
134
+ const hasHeader = ref(false)
135
+ const hasFooter = ref(false)
136
+ const previousActiveElement = ref<HTMLElement | null>(null)
137
+
138
+ // Focus trap elements
139
+ const focusableSelector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
140
+ let scrollbarWidth = 0
141
+
142
+ // Computed
143
+ const teleportTarget = computed(() => {
144
+ if (props.teleport === true) return 'body'
145
+ if (typeof props.teleport === 'string') return props.teleport
146
+ return undefined
147
+ })
148
+
149
+ const effectiveZIndex = computed(() => props.zIndex + (props.stackOrder * 10))
150
+
151
+ const sizeClasses = computed(() => {
152
+ if (props.fullscreen) return 'w-full h-full max-w-full max-h-full rounded-none'
153
+
154
+ return {
155
+ xs: 'w-full max-w-xs',
156
+ sm: 'w-full max-w-sm',
157
+ md: 'w-full max-w-lg',
158
+ lg: 'w-full max-w-2xl',
159
+ xl: 'w-full max-w-4xl',
160
+ full: 'w-full max-w-[calc(100vw-2rem)] max-h-[calc(100vh-2rem)]'
161
+ }[props.size]
162
+ })
163
+
164
+ const positionClasses = computed(() => {
165
+ if (props.fullscreen) return 'items-stretch justify-center'
166
+
167
+ return {
168
+ center: 'items-center justify-center',
169
+ top: 'items-start justify-center pt-16',
170
+ bottom: 'items-end justify-center pb-16',
171
+ left: 'items-center justify-start pl-4',
172
+ right: 'items-center justify-end pr-4'
173
+ }[props.position]
174
+ })
175
+
176
+ const variantClasses = computed(() => ({
177
+ default: 'bg-background border border-border shadow-2xl',
178
+ glass: 'bg-background/80 backdrop-blur-2xl border border-border/50 shadow-2xl',
179
+ bordered: 'bg-background border-2 border-primary/30 shadow-xl',
180
+ elevated: 'bg-background shadow-[0_25px_80px_-12px_rgba(0,0,0,0.4)] dark:shadow-[0_25px_80px_-12px_rgba(0,0,0,0.8)]'
181
+ }[props.variant]))
182
+
183
+ // Animation configurations
184
+ const panelAnimations = computed(() => {
185
+ const animations = {
186
+ fade: {
187
+ enter: 'transition-all duration-300 ease-out',
188
+ enterFrom: 'opacity-0',
189
+ enterTo: 'opacity-100',
190
+ leave: 'transition-all duration-200 ease-in',
191
+ leaveFrom: 'opacity-100',
192
+ leaveTo: 'opacity-0'
193
+ },
194
+ scale: {
195
+ enter: 'transition-all duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)]',
196
+ enterFrom: 'opacity-0 scale-90',
197
+ enterTo: 'opacity-100 scale-100',
198
+ leave: 'transition-all duration-200 ease-in',
199
+ leaveFrom: 'opacity-100 scale-100',
200
+ leaveTo: 'opacity-0 scale-95'
201
+ },
202
+ 'slide-up': {
203
+ enter: 'transition-all duration-300 ease-out',
204
+ enterFrom: 'opacity-0 translate-y-12',
205
+ enterTo: 'opacity-100 translate-y-0',
206
+ leave: 'transition-all duration-200 ease-in',
207
+ leaveFrom: 'opacity-100 translate-y-0',
208
+ leaveTo: 'opacity-0 translate-y-8'
209
+ },
210
+ 'slide-down': {
211
+ enter: 'transition-all duration-300 ease-out',
212
+ enterFrom: 'opacity-0 -translate-y-12',
213
+ enterTo: 'opacity-100 translate-y-0',
214
+ leave: 'transition-all duration-200 ease-in',
215
+ leaveFrom: 'opacity-100 translate-y-0',
216
+ leaveTo: 'opacity-0 -translate-y-8'
217
+ },
218
+ zoom: {
219
+ enter: 'transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)]',
220
+ enterFrom: 'opacity-0 scale-50',
221
+ enterTo: 'opacity-100 scale-100',
222
+ leave: 'transition-all duration-200 ease-in',
223
+ leaveFrom: 'opacity-100 scale-100',
224
+ leaveTo: 'opacity-0 scale-75'
225
+ },
226
+ flip: {
227
+ enter: 'transition-all duration-400 ease-out [transform-style:preserve-3d]',
228
+ enterFrom: 'opacity-0 [transform:rotateX(-15deg)_scale(0.95)]',
229
+ enterTo: 'opacity-100 [transform:rotateX(0deg)_scale(1)]',
230
+ leave: 'transition-all duration-200 ease-in [transform-style:preserve-3d]',
231
+ leaveFrom: 'opacity-100 [transform:rotateX(0deg)_scale(1)]',
232
+ leaveTo: 'opacity-0 [transform:rotateX(15deg)_scale(0.95)]'
233
+ }
234
+ }
235
+ return animations[props.animation]
236
+ })
237
+
238
+ // Methods
239
+ const getScrollbarWidth = () => {
240
+ const outer = document.createElement('div')
241
+ outer.style.visibility = 'hidden'
242
+ outer.style.overflow = 'scroll'
243
+ document.body.appendChild(outer)
244
+ const inner = document.createElement('div')
245
+ outer.appendChild(inner)
246
+ const scrollbarWidth = outer.offsetWidth - inner.offsetWidth
247
+ outer.parentNode?.removeChild(outer)
248
+ return scrollbarWidth
249
+ }
250
+
251
+ const lockBodyScroll = () => {
252
+ if (!props.lockScroll) return
253
+
254
+ scrollbarWidth = getScrollbarWidth()
255
+ const hasScrollbar = window.innerWidth > document.documentElement.clientWidth
256
+
257
+ document.body.style.overflow = 'hidden'
258
+ if (hasScrollbar) {
259
+ document.body.style.paddingRight = `${scrollbarWidth}px`
260
+ }
261
+ }
262
+
263
+ const unlockBodyScroll = () => {
264
+ if (!props.lockScroll) return
265
+
266
+ document.body.style.overflow = ''
267
+ document.body.style.paddingRight = ''
268
+ }
269
+
270
+ const getFocusableElements = (): HTMLElement[] => {
271
+ if (!panelRef.value) return []
272
+ return Array.from(panelRef.value.querySelectorAll(focusableSelector))
273
+ .filter(el => !el.hasAttribute('disabled') && el.getAttribute('tabindex') !== '-1') as HTMLElement[]
274
+ }
275
+
276
+ const focusFirstElement = () => {
277
+ if (!props.trapFocus) return
278
+
279
+ nextTick(() => {
280
+ // If initialFocus is specified, focus that element
281
+ if (props.initialFocus && panelRef.value) {
282
+ const initialElement = panelRef.value.querySelector(props.initialFocus) as HTMLElement
283
+ if (initialElement) {
284
+ initialElement.focus()
285
+ return
286
+ }
287
+ }
288
+
289
+ // If autoFocus is enabled, focus the first interactive element
290
+ if (props.autoFocus) {
291
+ const focusable = getFocusableElements()
292
+ // Skip close button (first element) if there are other focusable elements
293
+ const targetIndex = focusable.length > 1 ? 1 : 0
294
+ if (focusable.length > 0) {
295
+ focusable[targetIndex].focus()
296
+ return
297
+ }
298
+ }
299
+
300
+ // Otherwise, focus the panel itself (not the close button)
301
+ // This prevents the focus ring on the close button while maintaining accessibility
302
+ // Users can Tab to navigate to interactive elements
303
+ panelRef.value?.focus()
304
+ })
305
+ }
306
+
307
+ const handleKeydown = (event: KeyboardEvent) => {
308
+ if (!isOpen.value) return
309
+
310
+ // Escape key
311
+ if (event.key === 'Escape' && props.closeOnEscape && !props.persistent) {
312
+ event.preventDefault()
313
+ event.stopPropagation()
314
+ close()
315
+ return
316
+ }
317
+
318
+ // Focus trap
319
+ if (event.key === 'Tab' && props.trapFocus) {
320
+ const focusable = getFocusableElements()
321
+ if (focusable.length === 0) return
322
+
323
+ const first = focusable[0]
324
+ const last = focusable[focusable.length - 1]
325
+
326
+ if (event.shiftKey) {
327
+ if (document.activeElement === first) {
328
+ event.preventDefault()
329
+ last.focus()
330
+ }
331
+ } else {
332
+ if (document.activeElement === last) {
333
+ event.preventDefault()
334
+ first.focus()
335
+ }
336
+ }
337
+ }
338
+ }
339
+
340
+ const handleBackdropClick = (event: MouseEvent) => {
341
+ // Only close if clicking directly on the backdrop or modal wrapper, not the panel
342
+ const target = event.target as HTMLElement
343
+ const isBackdropClick = target === backdropRef.value || target === modalRef.value
344
+
345
+ if (isBackdropClick && props.closeOnBackdrop && !props.persistent) {
346
+ close()
347
+ } else if (isBackdropClick && props.persistent) {
348
+ // Shake animation for persistent modals
349
+ panelRef.value?.classList.add('animate-shake')
350
+ setTimeout(() => panelRef.value?.classList.remove('animate-shake'), 300)
351
+ }
352
+ }
353
+
354
+ const open = () => {
355
+ if (isOpen.value || isAnimating.value) return
356
+
357
+ emit('before-open')
358
+ previousActiveElement.value = document.activeElement as HTMLElement
359
+ isOpen.value = true
360
+ isAnimating.value = true
361
+
362
+ lockBodyScroll()
363
+ emit('update:modelValue', true)
364
+ emit('open')
365
+
366
+ nextTick(() => {
367
+ focusFirstElement()
368
+ setTimeout(() => {
369
+ isAnimating.value = false
370
+ emit('opened')
371
+ }, 300)
372
+ })
373
+ }
374
+
375
+ const close = () => {
376
+ if (!isOpen.value || isAnimating.value) return
377
+
378
+ emit('before-close')
379
+ isAnimating.value = true
380
+ isOpen.value = false
381
+
382
+ unlockBodyScroll()
383
+ emit('update:modelValue', false)
384
+ emit('close')
385
+
386
+ setTimeout(() => {
387
+ isAnimating.value = false
388
+ if (previousActiveElement.value) {
389
+ previousActiveElement.value.focus()
390
+ }
391
+ emit('closed')
392
+ }, 200)
393
+ }
394
+
395
+ // Watch modelValue
396
+ watch(() => props.modelValue, (value) => {
397
+ if (value) {
398
+ open()
399
+ } else {
400
+ close()
401
+ }
402
+ }, { immediate: true })
403
+
404
+ // Lifecycle
405
+ onMounted(() => {
406
+ document.addEventListener('keydown', handleKeydown)
407
+ })
408
+
409
+ onBeforeUnmount(() => {
410
+ document.removeEventListener('keydown', handleKeydown)
411
+ unlockBodyScroll()
412
+ })
413
+
414
+ // Provide context
415
+ provide(SModalContextKey, {
416
+ close,
417
+ isOpen,
418
+ size: props.size,
419
+ hasHeader,
420
+ hasFooter,
421
+ setHasHeader: (value: boolean) => hasHeader.value = value,
422
+ setHasFooter: (value: boolean) => hasFooter.value = value,
423
+ titleId,
424
+ descriptionId
425
+ })
426
+
427
+ // Expose for external control
428
+ defineExpose({
429
+ open,
430
+ close,
431
+ isOpen
432
+ })
433
+ </script>
434
+
435
+ <template>
436
+ <Teleport v-if="teleportTarget" :to="teleportTarget" :disabled="!teleportTarget">
437
+ <Transition
438
+ enter-active-class="transition-opacity duration-300 ease-out"
439
+ enter-from-class="opacity-0"
440
+ enter-to-class="opacity-100"
441
+ leave-active-class="transition-opacity duration-200 ease-in"
442
+ leave-from-class="opacity-100"
443
+ leave-to-class="opacity-0"
444
+ >
445
+ <div
446
+ v-if="isOpen"
447
+ ref="modalRef"
448
+ v-bind="$attrs"
449
+ :class="cn('s-modal fixed inset-0 flex overflow-hidden', positionClasses, fullscreen ? 'p-0' : 'p-4', $attrs.class ?? '')"
450
+ :style="{ zIndex: effectiveZIndex }"
451
+ role="dialog"
452
+ aria-modal="true"
453
+ :aria-labelledby="title ? titleId : undefined"
454
+ :aria-describedby="description ? descriptionId : undefined"
455
+ @mousedown="handleBackdropClick"
456
+ >
457
+ <!-- Backdrop -->
458
+ <div
459
+ v-if="backdrop"
460
+ ref="backdropRef"
461
+ class="s-modal-backdrop absolute inset-0 -z-1"
462
+ :class="[
463
+ 'bg-black/60 dark:bg-black/70',
464
+ backdropBlur ? 'backdrop-blur-sm' : '',
465
+ backdropClass
466
+ ]"
467
+ aria-hidden="true"
468
+ />
469
+
470
+ <!-- Modal Panel -->
471
+ <Transition
472
+ appear
473
+ :enter-active-class="panelAnimations.enter"
474
+ :enter-from-class="panelAnimations.enterFrom"
475
+ :enter-to-class="panelAnimations.enterTo"
476
+ :leave-active-class="panelAnimations.leave"
477
+ :leave-from-class="panelAnimations.leaveFrom"
478
+ :leave-to-class="panelAnimations.leaveTo"
479
+ >
480
+ <div
481
+ v-show="isOpen"
482
+ ref="panelRef"
483
+ class="s-modal-panel relative flex flex-col overflow-hidden outline-none"
484
+ :class="[
485
+ sizeClasses,
486
+ variantClasses,
487
+ fullscreen ? '' : 'rounded-2xl',
488
+ 'max-h-[calc(100vh-2rem)]',
489
+ panelClass
490
+ ]"
491
+ tabindex="-1"
492
+ @mousedown.stop
493
+ >
494
+ <!-- Default Header (if title is provided and hideHeader is false) -->
495
+ <div
496
+ v-if="(title || closable) && !hideHeader && !$slots.header"
497
+ class="s-modal-header flex items-start justify-between gap-4 px-6 py-5 border-b border-border shrink-0"
498
+ >
499
+ <div class="flex-1 min-w-0">
500
+ <h2
501
+ v-if="title"
502
+ :id="titleId"
503
+ class="text-lg font-semibold text-foreground tracking-tight"
504
+ >
505
+ {{ title }}
506
+ </h2>
507
+ <p
508
+ v-if="description"
509
+ :id="descriptionId"
510
+ class="mt-1.5 text-sm text-muted-foreground"
511
+ >
512
+ {{ description }}
513
+ </p>
514
+ </div>
515
+
516
+ <button
517
+ v-if="closable"
518
+ type="button"
519
+ class="s-modal-close shrink-0 flex items-center justify-center w-8 h-8 -mt-1 -mr-2 rounded-lg text-muted-foreground hover:text-foreground hover:bg-accent transition-all duration-150 outline-none focus:ring-2 focus:ring-primary/30"
520
+ aria-label="Close modal"
521
+ @click="close"
522
+ >
523
+ <span class="mdi mdi-close text-xl" />
524
+ </button>
525
+ </div>
526
+
527
+ <!-- Header Slot -->
528
+ <slot name="header" />
529
+
530
+ <!-- Content -->
531
+ <div class="s-modal-body flex-1 overflow-y-auto overscroll-contain">
532
+ <slot />
533
+ </div>
534
+
535
+ <!-- Footer Slot -->
536
+ <slot name="footer" />
537
+ </div>
538
+ </Transition>
539
+ </div>
540
+ </Transition>
541
+ </Teleport>
542
+
543
+ <!-- Non-teleported fallback -->
544
+ <template v-else>
545
+ <Transition
546
+ enter-active-class="transition-opacity duration-300 ease-out"
547
+ enter-from-class="opacity-0"
548
+ enter-to-class="opacity-100"
549
+ leave-active-class="transition-opacity duration-200 ease-in"
550
+ leave-from-class="opacity-100"
551
+ leave-to-class="opacity-0"
552
+ >
553
+ <div
554
+ v-if="isOpen"
555
+ ref="modalRef"
556
+ v-bind="$attrs"
557
+ :class="cn('s-modal fixed inset-0 flex overflow-hidden', positionClasses, fullscreen ? 'p-0' : 'p-4', $attrs.class ?? '')"
558
+ :style="{ zIndex: effectiveZIndex }"
559
+ role="dialog"
560
+ aria-modal="true"
561
+ @mousedown="handleBackdropClick"
562
+ >
563
+ <div
564
+ v-if="backdrop"
565
+ ref="backdropRef"
566
+ class="s-modal-backdrop absolute inset-0 -z-1 bg-black/60 dark:bg-black/70"
567
+ :class="[backdropBlur ? 'backdrop-blur-sm' : '', backdropClass]"
568
+ aria-hidden="true"
569
+ />
570
+
571
+ <Transition
572
+ appear
573
+ :enter-active-class="panelAnimations.enter"
574
+ :enter-from-class="panelAnimations.enterFrom"
575
+ :enter-to-class="panelAnimations.enterTo"
576
+ :leave-active-class="panelAnimations.leave"
577
+ :leave-from-class="panelAnimations.leaveFrom"
578
+ :leave-to-class="panelAnimations.leaveTo"
579
+ >
580
+ <div
581
+ v-show="isOpen"
582
+ ref="panelRef"
583
+ class="s-modal-panel relative flex flex-col overflow-hidden rounded-2xl outline-none"
584
+ :class="[sizeClasses, variantClasses, 'max-h-[calc(100vh-2rem)]', panelClass]"
585
+ tabindex="-1"
586
+ @mousedown.stop
587
+ >
588
+ <slot />
589
+ </div>
590
+ </Transition>
591
+ </div>
592
+ </Transition>
593
+ </template>
594
+ </template>
595
+
596
+ <style scoped>
597
+ .s-modal {
598
+ perspective: 1000px;
599
+ }
600
+
601
+ .s-modal-panel {
602
+ transform-style: preserve-3d;
603
+ scrollbar-width: thin;
604
+ scrollbar-color: var(--s-border) transparent;
605
+ }
606
+
607
+ .s-modal-panel::-webkit-scrollbar {
608
+ width: 6px;
609
+ }
610
+
611
+ .s-modal-panel::-webkit-scrollbar-track {
612
+ background: transparent;
613
+ }
614
+
615
+ .s-modal-panel::-webkit-scrollbar-thumb {
616
+ background: var(--s-border);
617
+ border-radius: 3px;
618
+ }
619
+
620
+ .s-modal-body {
621
+ scrollbar-width: thin;
622
+ scrollbar-color: var(--s-border) transparent;
623
+ }
624
+
625
+ .s-modal-body::-webkit-scrollbar {
626
+ width: 6px;
627
+ }
628
+
629
+ .s-modal-body::-webkit-scrollbar-track {
630
+ background: transparent;
631
+ }
632
+
633
+ .s-modal-body::-webkit-scrollbar-thumb {
634
+ background: var(--s-border);
635
+ border-radius: 3px;
636
+ }
637
+
638
+ /* Shake animation for persistent modals */
639
+ @keyframes shake {
640
+ 0%, 100% { transform: translateX(0); }
641
+ 10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }
642
+ 20%, 40%, 60%, 80% { transform: translateX(4px); }
643
+ }
644
+
645
+ .animate-shake {
646
+ animation: shake 0.3s ease-in-out;
647
+ }
648
+ </style>
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SModalClose - Close button for modal
4
+ * Uses context to close the modal when clicked
5
+ */
6
+ </script>
7
+
8
+ <script setup lang="ts">
9
+ defineOptions({ inheritAttrs: false })
10
+
11
+ import { inject } from 'vue'
12
+ import { cn } from '../../../lib/utils'
13
+ import { SModalContextKey } from './SModal.vue'
14
+
15
+ export interface Props {
16
+ /** Additional class */
17
+ closeClass?: string
18
+ /** Disable the button */
19
+ disabled?: boolean
20
+ }
21
+
22
+ const props = withDefaults(defineProps<Props>(), {
23
+ closeClass: '',
24
+ disabled: false
25
+ })
26
+
27
+ const context = inject(SModalContextKey)
28
+
29
+ const handleClose = () => {
30
+ if (!props.disabled) {
31
+ context?.close()
32
+ }
33
+ }
34
+ </script>
35
+
36
+ <template>
37
+ <button
38
+ type="button"
39
+ v-bind="$attrs"
40
+ :class="cn('s-modal-close flex items-center justify-center w-8 h-8 rounded-lg text-muted-foreground hover:text-foreground hover:bg-accent transition-all duration-150 outline-none focus:ring-2 focus:ring-primary/30 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-muted-foreground', closeClass, $attrs.class ?? '')"
41
+ :disabled="disabled"
42
+ aria-label="Close modal"
43
+ @click="handleClose"
44
+ >
45
+ <slot>
46
+ <span class="mdi mdi-close text-xl" />
47
+ </slot>
48
+ </button>
49
+ </template>