@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,797 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SDrawer - Advanced Drawer/Sheet Component
4
+ * A fully accessible, animated drawer with swipe gestures, snap points, and keyboard navigation
5
+ */
6
+ import { type InjectionKey, type Ref } from 'vue'
7
+
8
+ // Types
9
+ export type DrawerSide = 'left' | 'right' | 'top' | 'bottom'
10
+ export type DrawerSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full'
11
+ export type DrawerVariant = 'default' | 'glass' | 'bordered' | 'elevated' | 'minimal'
12
+
13
+ export interface SDrawerContext {
14
+ close: () => void
15
+ isOpen: Ref<boolean>
16
+ side: DrawerSide
17
+ hasHeader: Ref<boolean>
18
+ hasFooter: Ref<boolean>
19
+ setHasHeader: (value: boolean) => void
20
+ setHasFooter: (value: boolean) => void
21
+ titleId: string
22
+ descriptionId: string
23
+ dragProgress: Ref<number>
24
+ isDragging: Ref<boolean>
25
+ }
26
+
27
+ export const SDrawerContextKey: InjectionKey<SDrawerContext> = Symbol('SDrawerContext')
28
+ </script>
29
+
30
+ <script setup lang="ts">
31
+ defineOptions({ inheritAttrs: false })
32
+
33
+ import { ref, computed, provide, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
34
+ import { cn } from '../../../lib/utils'
35
+
36
+ export interface Props {
37
+ /** Control drawer visibility */
38
+ modelValue?: boolean
39
+ /** Which side the drawer slides from */
40
+ side?: DrawerSide
41
+ /** Drawer size */
42
+ size?: DrawerSize
43
+ /** Visual variant */
44
+ variant?: DrawerVariant
45
+ /** Show close button */
46
+ closable?: boolean
47
+ /** Close on backdrop click */
48
+ closeOnBackdrop?: boolean
49
+ /** Close on escape key */
50
+ closeOnEscape?: boolean
51
+ /** Lock body scroll when open */
52
+ lockScroll?: boolean
53
+ /** Trap focus within drawer */
54
+ trapFocus?: boolean
55
+ /** Show backdrop overlay */
56
+ backdrop?: boolean
57
+ /** Backdrop blur effect */
58
+ backdropBlur?: boolean
59
+ /** Custom backdrop class */
60
+ backdropClass?: string
61
+ /** Custom drawer panel class */
62
+ panelClass?: string
63
+ /** Teleport target */
64
+ teleport?: boolean | string
65
+ /** Z-index for the drawer */
66
+ zIndex?: number
67
+ /** Drawer title (alternative to header slot) */
68
+ title?: string
69
+ /** Drawer description */
70
+ description?: string
71
+ /** Hide the default header */
72
+ hideHeader?: boolean
73
+ /** Initial focus selector */
74
+ initialFocus?: string
75
+ /** Auto-focus first interactive element */
76
+ autoFocus?: boolean
77
+ /** Enable swipe/drag to close */
78
+ swipeable?: boolean
79
+ /** Swipe threshold to close (0-1) */
80
+ swipeThreshold?: number
81
+ /** Show drag handle indicator */
82
+ showHandle?: boolean
83
+ /** Snap points (percentages from 0-100) for bottom/top drawers */
84
+ snapPoints?: number[]
85
+ /** Default snap point index */
86
+ defaultSnapPoint?: number
87
+ /** Enable modal mode (adds rounded corners, handle) */
88
+ modal?: boolean
89
+ /** Custom rounded corners */
90
+ rounded?: boolean
91
+ /** Animation duration in ms */
92
+ duration?: number
93
+ /** Overlay opacity (0-1) */
94
+ overlayOpacity?: number
95
+ }
96
+
97
+ const props = withDefaults(defineProps<Props>(), {
98
+ modelValue: false,
99
+ side: 'bottom',
100
+ size: 'md',
101
+ variant: 'default',
102
+ closable: true,
103
+ closeOnBackdrop: true,
104
+ closeOnEscape: true,
105
+ lockScroll: true,
106
+ trapFocus: true,
107
+ backdrop: true,
108
+ backdropBlur: false,
109
+ backdropClass: '',
110
+ panelClass: '',
111
+ teleport: true,
112
+ zIndex: 1000,
113
+ title: undefined,
114
+ description: undefined,
115
+ hideHeader: false,
116
+ initialFocus: undefined,
117
+ autoFocus: false,
118
+ swipeable: true,
119
+ swipeThreshold: 0.3,
120
+ showHandle: true,
121
+ snapPoints: () => [],
122
+ defaultSnapPoint: 0,
123
+ modal: false,
124
+ rounded: true,
125
+ duration: 300,
126
+ overlayOpacity: 0.5
127
+ })
128
+
129
+ const emit = defineEmits<{
130
+ 'update:modelValue': [value: boolean]
131
+ 'open': []
132
+ 'close': []
133
+ 'opened': []
134
+ 'closed': []
135
+ 'before-open': []
136
+ 'before-close': []
137
+ 'snap': [snapIndex: number, snapValue: number]
138
+ 'drag-start': []
139
+ 'drag-end': [progress: number]
140
+ }>()
141
+
142
+ // Generate unique IDs for ARIA
143
+ const uid = Math.random().toString(36).slice(2, 9)
144
+ const titleId = `drawer-title-${uid}`
145
+ const descriptionId = `drawer-desc-${uid}`
146
+
147
+ // Refs
148
+ const drawerRef = ref<HTMLElement | null>(null)
149
+ const backdropRef = ref<HTMLElement | null>(null)
150
+ const panelRef = ref<HTMLElement | null>(null)
151
+ const isOpen = ref(props.modelValue)
152
+ const isAnimating = ref(false)
153
+ const hasHeader = ref(false)
154
+ const hasFooter = ref(false)
155
+ const previousActiveElement = ref<HTMLElement | null>(null)
156
+
157
+ // Drag/Swipe state
158
+ const isDragging = ref(false)
159
+ const dragProgress = ref(0)
160
+ const dragStartPos = ref(0)
161
+ const dragCurrentPos = ref(0)
162
+ const currentSnapIndex = ref(props.defaultSnapPoint)
163
+
164
+ // Focus trap elements
165
+ const focusableSelector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
166
+ let scrollbarWidth = 0
167
+
168
+ // Computed
169
+ const teleportTarget = computed(() => {
170
+ if (props.teleport === true) return 'body'
171
+ if (typeof props.teleport === 'string') return props.teleport
172
+ return undefined
173
+ })
174
+
175
+ const isHorizontal = computed(() => props.side === 'left' || props.side === 'right')
176
+ const isVertical = computed(() => props.side === 'top' || props.side === 'bottom')
177
+
178
+ const sizeValue = computed(() => {
179
+ const sizes = {
180
+ xs: isHorizontal.value ? '280px' : '25vh',
181
+ sm: isHorizontal.value ? '320px' : '35vh',
182
+ md: isHorizontal.value ? '400px' : '50vh',
183
+ lg: isHorizontal.value ? '540px' : '70vh',
184
+ xl: isHorizontal.value ? '720px' : '85vh',
185
+ full: '100%'
186
+ }
187
+ return sizes[props.size]
188
+ })
189
+
190
+ const sizeClasses = computed(() => {
191
+ if (isHorizontal.value) {
192
+ return `h-full ${props.size === 'full' ? 'w-full' : ''}`
193
+ }
194
+ return `w-full ${props.size === 'full' ? 'h-full' : ''}`
195
+ })
196
+
197
+ const positionClasses = computed(() => ({
198
+ left: 'inset-y-0 left-0',
199
+ right: 'inset-y-0 right-0',
200
+ top: 'inset-x-0 top-0',
201
+ bottom: 'inset-x-0 bottom-0'
202
+ }[props.side]))
203
+
204
+ const roundedClasses = computed(() => {
205
+ if (!props.rounded || props.size === 'full') return ''
206
+
207
+ const roundedMap = {
208
+ left: 'rounded-r-3xl',
209
+ right: 'rounded-l-3xl',
210
+ top: 'rounded-b-3xl',
211
+ bottom: 'rounded-t-3xl'
212
+ }
213
+ return props.modal ? roundedMap[props.side] : roundedMap[props.side]
214
+ })
215
+
216
+ const variantClasses = computed(() => ({
217
+ default: 'bg-background border-0 shadow-2xl',
218
+ glass: 'bg-background/90 backdrop-blur-2xl border border-border/50 shadow-2xl',
219
+ bordered: 'bg-background border border-border shadow-xl',
220
+ elevated: 'bg-background shadow-[0_-25px_80px_-12px_rgba(0,0,0,0.3)] dark:shadow-[0_-25px_80px_-12px_rgba(0,0,0,0.7)]',
221
+ minimal: 'bg-background'
222
+ }[props.variant]))
223
+
224
+ const borderClasses = computed(() => {
225
+ if (props.variant === 'default' || props.variant === 'minimal') {
226
+ const borderMap = {
227
+ left: 'border-r border-border',
228
+ right: 'border-l border-border',
229
+ top: 'border-b border-border',
230
+ bottom: 'border-t border-border'
231
+ }
232
+ return props.rounded ? '' : borderMap[props.side]
233
+ }
234
+ return ''
235
+ })
236
+
237
+ // Animation transform values
238
+ const getTransformValue = (progress: number = 0) => {
239
+ const transforms = {
240
+ left: `translateX(${-100 + progress * 100}%)`,
241
+ right: `translateX(${100 - progress * 100}%)`,
242
+ top: `translateY(${-100 + progress * 100}%)`,
243
+ bottom: `translateY(${100 - progress * 100}%)`
244
+ }
245
+ return transforms[props.side]
246
+ }
247
+
248
+ const panelStyle = computed(() => {
249
+ const style: Record<string, string> = {}
250
+
251
+ // Set size
252
+ if (isHorizontal.value && props.size !== 'full') {
253
+ style.width = sizeValue.value
254
+ style.maxWidth = '100vw'
255
+ } else if (isVertical.value && props.size !== 'full') {
256
+ style.height = sizeValue.value
257
+ style.maxHeight = '100vh'
258
+ }
259
+
260
+ // Set transform during drag
261
+ if (isDragging.value) {
262
+ style.transform = getTransformValue(1 - dragProgress.value)
263
+ style.transition = 'none'
264
+ }
265
+
266
+ return style
267
+ })
268
+
269
+ const backdropStyle = computed(() => {
270
+ const opacity = isDragging.value
271
+ ? props.overlayOpacity * (1 - dragProgress.value)
272
+ : props.overlayOpacity
273
+
274
+ return {
275
+ backgroundColor: `rgba(0, 0, 0, ${opacity})`
276
+ }
277
+ })
278
+
279
+ // Methods
280
+ const getScrollbarWidth = () => {
281
+ const outer = document.createElement('div')
282
+ outer.style.visibility = 'hidden'
283
+ outer.style.overflow = 'scroll'
284
+ document.body.appendChild(outer)
285
+ const inner = document.createElement('div')
286
+ outer.appendChild(inner)
287
+ const scrollbarWidth = outer.offsetWidth - inner.offsetWidth
288
+ outer.parentNode?.removeChild(outer)
289
+ return scrollbarWidth
290
+ }
291
+
292
+ const lockBodyScroll = () => {
293
+ if (!props.lockScroll) return
294
+
295
+ scrollbarWidth = getScrollbarWidth()
296
+ const hasScrollbar = window.innerWidth > document.documentElement.clientWidth
297
+
298
+ document.body.style.overflow = 'hidden'
299
+ if (hasScrollbar) {
300
+ document.body.style.paddingRight = `${scrollbarWidth}px`
301
+ }
302
+ }
303
+
304
+ const unlockBodyScroll = () => {
305
+ if (!props.lockScroll) return
306
+
307
+ document.body.style.overflow = ''
308
+ document.body.style.paddingRight = ''
309
+ }
310
+
311
+ const getFocusableElements = (): HTMLElement[] => {
312
+ if (!panelRef.value) return []
313
+ return Array.from(panelRef.value.querySelectorAll(focusableSelector))
314
+ .filter(el => !el.hasAttribute('disabled') && el.getAttribute('tabindex') !== '-1') as HTMLElement[]
315
+ }
316
+
317
+ const focusFirstElement = () => {
318
+ if (!props.trapFocus) return
319
+
320
+ nextTick(() => {
321
+ if (props.initialFocus && panelRef.value) {
322
+ const initialElement = panelRef.value.querySelector(props.initialFocus) as HTMLElement
323
+ if (initialElement) {
324
+ initialElement.focus()
325
+ return
326
+ }
327
+ }
328
+
329
+ if (props.autoFocus) {
330
+ const focusable = getFocusableElements()
331
+ const targetIndex = focusable.length > 1 ? 1 : 0
332
+ if (focusable.length > 0) {
333
+ focusable[targetIndex].focus()
334
+ return
335
+ }
336
+ }
337
+
338
+ panelRef.value?.focus()
339
+ })
340
+ }
341
+
342
+ const handleKeydown = (event: KeyboardEvent) => {
343
+ if (!isOpen.value) return
344
+
345
+ if (event.key === 'Escape' && props.closeOnEscape) {
346
+ event.preventDefault()
347
+ event.stopPropagation()
348
+ close()
349
+ return
350
+ }
351
+
352
+ if (event.key === 'Tab' && props.trapFocus) {
353
+ const focusable = getFocusableElements()
354
+ if (focusable.length === 0) return
355
+
356
+ const first = focusable[0]
357
+ const last = focusable[focusable.length - 1]
358
+
359
+ if (event.shiftKey) {
360
+ if (document.activeElement === first) {
361
+ event.preventDefault()
362
+ last.focus()
363
+ }
364
+ } else {
365
+ if (document.activeElement === last) {
366
+ event.preventDefault()
367
+ first.focus()
368
+ }
369
+ }
370
+ }
371
+ }
372
+
373
+ const handleBackdropClick = (event: MouseEvent) => {
374
+ const target = event.target as HTMLElement
375
+ const isBackdropClick = target === backdropRef.value || target === drawerRef.value
376
+
377
+ if (isBackdropClick && props.closeOnBackdrop) {
378
+ close()
379
+ }
380
+ }
381
+
382
+ // Drag/Swipe handling
383
+ const handleDragStart = (event: TouchEvent | MouseEvent) => {
384
+ if (!props.swipeable || !isOpen.value) return
385
+
386
+ isDragging.value = true
387
+ dragProgress.value = 0
388
+
389
+ if ('touches' in event) {
390
+ dragStartPos.value = isHorizontal.value ? event.touches[0].clientX : event.touches[0].clientY
391
+ } else {
392
+ dragStartPos.value = isHorizontal.value ? event.clientX : event.clientY
393
+ }
394
+
395
+ dragCurrentPos.value = dragStartPos.value
396
+ emit('drag-start')
397
+ }
398
+
399
+ const handleDragMove = (event: TouchEvent | MouseEvent) => {
400
+ if (!isDragging.value || !panelRef.value) return
401
+
402
+ let currentPos: number
403
+ if ('touches' in event) {
404
+ currentPos = isHorizontal.value ? event.touches[0].clientX : event.touches[0].clientY
405
+ } else {
406
+ currentPos = isHorizontal.value ? event.clientX : event.clientY
407
+ }
408
+
409
+ dragCurrentPos.value = currentPos
410
+ const delta = currentPos - dragStartPos.value
411
+
412
+ // Calculate progress based on side
413
+ const panelSize = isHorizontal.value
414
+ ? panelRef.value.offsetWidth
415
+ : panelRef.value.offsetHeight
416
+
417
+ let progress = 0
418
+
419
+ if (props.side === 'left') {
420
+ progress = Math.max(0, Math.min(1, -delta / panelSize))
421
+ } else if (props.side === 'right') {
422
+ progress = Math.max(0, Math.min(1, delta / panelSize))
423
+ } else if (props.side === 'top') {
424
+ progress = Math.max(0, Math.min(1, -delta / panelSize))
425
+ } else if (props.side === 'bottom') {
426
+ progress = Math.max(0, Math.min(1, delta / panelSize))
427
+ }
428
+
429
+ dragProgress.value = progress
430
+ }
431
+
432
+ const handleDragEnd = () => {
433
+ if (!isDragging.value) return
434
+
435
+ const shouldClose = dragProgress.value >= props.swipeThreshold
436
+
437
+ emit('drag-end', dragProgress.value)
438
+
439
+ isDragging.value = false
440
+
441
+ if (shouldClose) {
442
+ close()
443
+ } else {
444
+ // Animate back to open position
445
+ dragProgress.value = 0
446
+ }
447
+ }
448
+
449
+ const snapTo = (index: number) => {
450
+ if (props.snapPoints.length === 0) return
451
+
452
+ const snapValue = props.snapPoints[index]
453
+ if (snapValue !== undefined) {
454
+ currentSnapIndex.value = index
455
+ emit('snap', index, snapValue)
456
+ }
457
+ }
458
+
459
+ const open = () => {
460
+ if (isOpen.value || isAnimating.value) return
461
+
462
+ emit('before-open')
463
+ previousActiveElement.value = document.activeElement as HTMLElement
464
+ isOpen.value = true
465
+ isAnimating.value = true
466
+
467
+ lockBodyScroll()
468
+ emit('update:modelValue', true)
469
+ emit('open')
470
+
471
+ nextTick(() => {
472
+ focusFirstElement()
473
+ setTimeout(() => {
474
+ isAnimating.value = false
475
+ emit('opened')
476
+ }, props.duration)
477
+ })
478
+ }
479
+
480
+ const close = () => {
481
+ if (!isOpen.value || isAnimating.value) return
482
+
483
+ emit('before-close')
484
+ isAnimating.value = true
485
+ isOpen.value = false
486
+
487
+ unlockBodyScroll()
488
+ emit('update:modelValue', false)
489
+ emit('close')
490
+
491
+ setTimeout(() => {
492
+ isAnimating.value = false
493
+ dragProgress.value = 0
494
+ if (previousActiveElement.value) {
495
+ previousActiveElement.value.focus()
496
+ }
497
+ emit('closed')
498
+ }, props.duration)
499
+ }
500
+
501
+ // Watch modelValue
502
+ watch(() => props.modelValue, (value) => {
503
+ if (value) {
504
+ open()
505
+ } else {
506
+ close()
507
+ }
508
+ }, { immediate: true })
509
+
510
+ // Lifecycle
511
+ onMounted(() => {
512
+ document.addEventListener('keydown', handleKeydown)
513
+ })
514
+
515
+ onBeforeUnmount(() => {
516
+ document.removeEventListener('keydown', handleKeydown)
517
+ unlockBodyScroll()
518
+ })
519
+
520
+ // Provide context
521
+ provide(SDrawerContextKey, {
522
+ close,
523
+ isOpen,
524
+ side: props.side,
525
+ hasHeader,
526
+ hasFooter,
527
+ setHasHeader: (value: boolean) => hasHeader.value = value,
528
+ setHasFooter: (value: boolean) => hasFooter.value = value,
529
+ titleId,
530
+ descriptionId,
531
+ dragProgress,
532
+ isDragging
533
+ })
534
+
535
+ // Expose for external control
536
+ defineExpose({
537
+ open,
538
+ close,
539
+ isOpen,
540
+ snapTo
541
+ })
542
+ </script>
543
+
544
+ <template>
545
+ <Teleport v-if="teleportTarget" :to="teleportTarget" :disabled="!teleportTarget">
546
+ <Transition
547
+ enter-active-class="transition-opacity ease-out"
548
+ :enter-active-class="`transition-opacity duration-${duration} ease-out`"
549
+ enter-from-class="opacity-0"
550
+ enter-to-class="opacity-100"
551
+ leave-active-class="transition-opacity ease-in"
552
+ :leave-active-class="`transition-opacity duration-${Math.round(duration * 0.7)} ease-in`"
553
+ leave-from-class="opacity-100"
554
+ leave-to-class="opacity-0"
555
+ >
556
+ <div
557
+ v-if="isOpen"
558
+ ref="drawerRef"
559
+ v-bind="$attrs"
560
+ :class="cn('s-drawer fixed inset-0', $attrs.class ?? '')"
561
+ :style="{ zIndex }"
562
+ role="dialog"
563
+ aria-modal="true"
564
+ :aria-labelledby="title ? titleId : undefined"
565
+ :aria-describedby="description ? descriptionId : undefined"
566
+ @mousedown="handleBackdropClick"
567
+ >
568
+ <!-- Backdrop -->
569
+ <div
570
+ v-if="backdrop"
571
+ ref="backdropRef"
572
+ class="s-drawer-backdrop absolute inset-0 -z-1 transition-colors"
573
+ :class="[
574
+ backdropBlur ? 'backdrop-blur-sm' : '',
575
+ backdropClass
576
+ ]"
577
+ :style="backdropStyle"
578
+ aria-hidden="true"
579
+ />
580
+
581
+ <!-- Drawer Panel -->
582
+ <Transition
583
+ appear
584
+ :enter-active-class="`transition-transform duration-[${duration}ms] ease-[cubic-bezier(0.32,0.72,0,1)]`"
585
+ :enter-from-class="side === 'left' ? '-translate-x-full' : side === 'right' ? 'translate-x-full' : side === 'top' ? '-translate-y-full' : 'translate-y-full'"
586
+ enter-to-class="translate-x-0 translate-y-0"
587
+ :leave-active-class="`transition-transform duration-[${Math.round(duration * 0.7)}ms] ease-[cubic-bezier(0.32,0.72,0,1)]`"
588
+ leave-from-class="translate-x-0 translate-y-0"
589
+ :leave-to-class="side === 'left' ? '-translate-x-full' : side === 'right' ? 'translate-x-full' : side === 'top' ? '-translate-y-full' : 'translate-y-full'"
590
+ >
591
+ <div
592
+ v-show="isOpen"
593
+ ref="panelRef"
594
+ class="s-drawer-panel absolute flex flex-col outline-none overflow-hidden"
595
+ :class="[
596
+ sizeClasses,
597
+ positionClasses,
598
+ roundedClasses,
599
+ variantClasses,
600
+ borderClasses,
601
+ panelClass
602
+ ]"
603
+ :style="panelStyle"
604
+ tabindex="-1"
605
+ @mousedown.stop
606
+ @touchstart.passive="handleDragStart"
607
+ @touchmove.passive="handleDragMove"
608
+ @touchend.passive="handleDragEnd"
609
+ >
610
+ <!-- Drag Handle Zone (for bottom/top drawers) - Large touch target for easy swiping -->
611
+ <div
612
+ v-if="showHandle && (side === 'bottom' || side === 'top' || modal)"
613
+ class="s-drawer-handle-zone relative shrink-0 cursor-grab active:cursor-grabbing select-none touch-none"
614
+ :class="side === 'top' ? 'order-last' : ''"
615
+ @mousedown="handleDragStart"
616
+ @mousemove="handleDragMove"
617
+ @mouseup="handleDragEnd"
618
+ @mouseleave="handleDragEnd"
619
+ >
620
+ <!-- Extended touch target area - invisible but interactive -->
621
+ <div class="absolute inset-x-0 -top-3 h-[60px] z-10" />
622
+
623
+ <!-- Visual handle container -->
624
+ <div class="s-drawer-handle flex items-center justify-center py-4 pt-5">
625
+ <div class="w-12 h-1.5 rounded-full bg-muted-foreground/40 transition-all duration-200 hover:bg-muted-foreground/60 hover:w-16 hover:scale-105" />
626
+ </div>
627
+ </div>
628
+
629
+ <!-- Default Header (if title is provided and hideHeader is false) -->
630
+ <!-- Also serves as a drag target for swiping -->
631
+ <div
632
+ v-if="(title || closable) && !hideHeader && !$slots.header"
633
+ class="s-drawer-header flex items-start justify-between gap-4 px-6 py-5 border-b border-border shrink-0"
634
+ :class="swipeable && isVertical ? 'cursor-grab active:cursor-grabbing touch-none' : ''"
635
+ @mousedown="swipeable && isVertical ? handleDragStart($event) : undefined"
636
+ @mousemove="swipeable && isVertical ? handleDragMove($event) : undefined"
637
+ @mouseup="swipeable && isVertical ? handleDragEnd() : undefined"
638
+ @mouseleave="swipeable && isVertical ? handleDragEnd() : undefined"
639
+ >
640
+ <div class="flex-1 min-w-0">
641
+ <h2
642
+ v-if="title"
643
+ :id="titleId"
644
+ class="text-lg font-semibold text-foreground tracking-tight"
645
+ >
646
+ {{ title }}
647
+ </h2>
648
+ <p
649
+ v-if="description"
650
+ :id="descriptionId"
651
+ class="mt-1.5 text-sm text-muted-foreground"
652
+ >
653
+ {{ description }}
654
+ </p>
655
+ </div>
656
+
657
+ <button
658
+ v-if="closable"
659
+ type="button"
660
+ class="s-drawer-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"
661
+ aria-label="Close drawer"
662
+ @click="close"
663
+ >
664
+ <span class="mdi mdi-close text-xl" />
665
+ </button>
666
+ </div>
667
+
668
+ <!-- Header Slot -->
669
+ <slot name="header" />
670
+
671
+ <!-- Content -->
672
+ <div class="s-drawer-body flex-1 overflow-y-auto overscroll-contain">
673
+ <slot />
674
+ </div>
675
+
676
+ <!-- Footer Slot -->
677
+ <slot name="footer" />
678
+ </div>
679
+ </Transition>
680
+ </div>
681
+ </Transition>
682
+ </Teleport>
683
+
684
+ <!-- Non-teleported fallback -->
685
+ <template v-else>
686
+ <Transition
687
+ enter-active-class="transition-opacity duration-300 ease-out"
688
+ enter-from-class="opacity-0"
689
+ enter-to-class="opacity-100"
690
+ leave-active-class="transition-opacity duration-200 ease-in"
691
+ leave-from-class="opacity-100"
692
+ leave-to-class="opacity-0"
693
+ >
694
+ <div
695
+ v-if="isOpen"
696
+ ref="drawerRef"
697
+ v-bind="$attrs"
698
+ :class="cn('s-drawer fixed inset-0', $attrs.class ?? '')"
699
+ :style="{ zIndex }"
700
+ role="dialog"
701
+ aria-modal="true"
702
+ @mousedown="handleBackdropClick"
703
+ >
704
+ <div
705
+ v-if="backdrop"
706
+ ref="backdropRef"
707
+ class="s-drawer-backdrop absolute inset-0 -z-1"
708
+ :class="[backdropBlur ? 'backdrop-blur-sm' : '', backdropClass]"
709
+ :style="backdropStyle"
710
+ aria-hidden="true"
711
+ />
712
+
713
+ <div
714
+ v-show="isOpen"
715
+ ref="panelRef"
716
+ class="s-drawer-panel absolute flex flex-col outline-none overflow-hidden"
717
+ :class="[sizeClasses, positionClasses, roundedClasses, variantClasses, borderClasses, panelClass]"
718
+ :style="panelStyle"
719
+ tabindex="-1"
720
+ @mousedown.stop
721
+ >
722
+ <slot />
723
+ </div>
724
+ </div>
725
+ </Transition>
726
+ </template>
727
+ </template>
728
+
729
+ <style scoped>
730
+ .s-drawer-panel {
731
+ scrollbar-width: thin;
732
+ scrollbar-color: var(--s-border) transparent;
733
+ will-change: transform;
734
+ }
735
+
736
+ .s-drawer-panel::-webkit-scrollbar {
737
+ width: 6px;
738
+ }
739
+
740
+ .s-drawer-panel::-webkit-scrollbar-track {
741
+ background: transparent;
742
+ }
743
+
744
+ .s-drawer-panel::-webkit-scrollbar-thumb {
745
+ background: var(--s-border);
746
+ border-radius: 3px;
747
+ }
748
+
749
+ .s-drawer-body {
750
+ scrollbar-width: thin;
751
+ scrollbar-color: var(--s-border) transparent;
752
+ }
753
+
754
+ .s-drawer-body::-webkit-scrollbar {
755
+ width: 6px;
756
+ }
757
+
758
+ .s-drawer-body::-webkit-scrollbar-track {
759
+ background: transparent;
760
+ }
761
+
762
+ .s-drawer-body::-webkit-scrollbar-thumb {
763
+ background: var(--s-border);
764
+ border-radius: 3px;
765
+ }
766
+
767
+ /* Smooth spring-like animation */
768
+ .s-drawer-panel {
769
+ transition-timing-function: cubic-bezier(0.32, 0.72, 0, 1);
770
+ }
771
+
772
+ /* Handle zone - large touch target */
773
+ .s-drawer-handle-zone {
774
+ min-height: 44px; /* iOS recommended touch target */
775
+ }
776
+
777
+ /* Handle visual indicator */
778
+ .s-drawer-handle > div {
779
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
780
+ }
781
+
782
+ /* Handle pulse animation on hover */
783
+ .s-drawer-handle-zone:hover .s-drawer-handle > div {
784
+ animation: handle-pulse 1.5s ease-in-out infinite;
785
+ }
786
+
787
+ /* Active state when dragging */
788
+ .s-drawer-handle-zone:active .s-drawer-handle > div {
789
+ transform: scaleY(1.3);
790
+ background-color: var(--s-muted-foreground) !important;
791
+ }
792
+
793
+ @keyframes handle-pulse {
794
+ 0%, 100% { transform: scaleX(1); }
795
+ 50% { transform: scaleX(1.15); }
796
+ }
797
+ </style>