@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,843 @@
1
+ <script setup lang="ts">
2
+ defineOptions({ inheritAttrs: false })
3
+
4
+ import { ref, computed, watch, nextTick, onMounted, onUnmounted, provide, toRef } from 'vue'
5
+ import { cn } from '../../../lib/utils'
6
+ import type { SOTPVariant, SOTPSize, SOTPRounded, SOTPGap, SOTPMode, SOTPAnimation, SOTPEntryAnimation, SOTPInputAnimation, SOTPSuccessAnimation, SOTPErrorAnimation, SOTPVisualDefaults } from './types'
7
+ import { SOTP_INJECTION_KEY } from './useOTPContext'
8
+ import type { SOTPContext } from './useOTPContext'
9
+
10
+ export interface Props {
11
+ // Core
12
+ modelValue?: string
13
+ maxlength?: number
14
+ mode?: SOTPMode
15
+
16
+ // Visual Design
17
+ variant?: SOTPVariant
18
+ size?: SOTPSize
19
+ color?: string
20
+ rounded?: SOTPRounded
21
+ gap?: SOTPGap
22
+
23
+ // Animations
24
+ animation?: SOTPAnimation
25
+ entryAnimation?: SOTPEntryAnimation
26
+ inputAnimation?: SOTPInputAnimation
27
+ successAnimation?: SOTPSuccessAnimation
28
+ errorAnimation?: SOTPErrorAnimation
29
+
30
+ // Text Morphing
31
+ morphText?: boolean
32
+ morphDuration?: number
33
+ showPlaceholder?: boolean
34
+ placeholderChar?: string
35
+ maskChar?: string
36
+
37
+ // States
38
+ disabled?: boolean
39
+ readonly?: boolean
40
+ loading?: boolean
41
+ error?: string | boolean
42
+ success?: string | boolean
43
+ autoFocus?: boolean
44
+
45
+ // Behavior
46
+ autoSubmit?: boolean
47
+ masked?: boolean
48
+ clearOnError?: boolean
49
+ allowPaste?: boolean
50
+ separator?: string
51
+ separatorPosition?: number[] // e.g., [3] for XXX-XXX format
52
+
53
+ // Label & Messages
54
+ label?: string
55
+ hint?: string
56
+ errorMessage?: string
57
+ successMessage?: string
58
+
59
+ // Advanced
60
+ countdown?: number // seconds countdown for resend
61
+ resendText?: string
62
+ ariaLabel?: string
63
+ }
64
+
65
+ const props = withDefaults(defineProps<Props>(), {
66
+ modelValue: '',
67
+ maxlength: 6,
68
+ mode: 'numeric',
69
+ variant: 'outlined',
70
+ size: 'medium',
71
+ color: 'var(--s-primary)',
72
+ rounded: 'md',
73
+ gap: 'normal',
74
+ animation: 'bounce',
75
+ entryAnimation: 'scale',
76
+ inputAnimation: 'pop',
77
+ successAnimation: 'celebrate',
78
+ errorAnimation: 'shake',
79
+ morphText: true,
80
+ morphDuration: 150,
81
+ showPlaceholder: true,
82
+ placeholderChar: '○',
83
+ maskChar: '●',
84
+ disabled: false,
85
+ readonly: false,
86
+ loading: false,
87
+ autoFocus: true,
88
+ autoSubmit: true,
89
+ masked: false,
90
+ clearOnError: false,
91
+ allowPaste: true,
92
+ separator: '',
93
+ resendText: 'Resend code',
94
+ countdown: 0
95
+ })
96
+
97
+ const emit = defineEmits<{
98
+ 'update:modelValue': [value: string]
99
+ 'complete': [value: string]
100
+ 'change': [value: string]
101
+ 'input': [index: number, value: string]
102
+ 'focus': [index: number]
103
+ 'blur': [index: number]
104
+ 'paste': [value: string]
105
+ 'resend': []
106
+ 'error': [message: string]
107
+ 'success': []
108
+ }>()
109
+
110
+ // Refs
111
+ const inputRefs = ref<(HTMLInputElement | null)[]>([])
112
+ const containerRef = ref<HTMLElement | null>(null)
113
+ const digits = ref<string[]>(Array(props.maxlength).fill(''))
114
+ const activeIndex = ref(-1)
115
+ const isComplete = ref(false)
116
+ const showSuccess = ref(false)
117
+ const showError = ref(false)
118
+ const animatingIndices = ref<Set<number>>(new Set())
119
+ const morphingIndices = ref<Map<number, { from: string; to: string; progress: number; phase: 'out' | 'in' }>>(new Map())
120
+
121
+ // Countdown
122
+ const countdownValue = ref(props.countdown)
123
+ const countdownInterval = ref<ReturnType<typeof setInterval> | null>(null)
124
+
125
+ // Computed
126
+ const isNumericMode = computed(() => props.mode === 'numeric')
127
+ const inputPattern = computed(() => {
128
+ switch (props.mode) {
129
+ case 'numeric': return '[0-9]'
130
+ case 'alphabetic': return '[a-zA-Z]'
131
+ case 'alphanumeric': return '[a-zA-Z0-9]'
132
+ default: return '.'
133
+ }
134
+ })
135
+
136
+ const inputMode = computed(() => {
137
+ return props.mode === 'numeric' ? 'numeric' : 'text'
138
+ })
139
+
140
+ // Size configurations
141
+ const sizeConfig = computed(() => {
142
+ const sizes = {
143
+ small: {
144
+ box: 'w-9 h-10',
145
+ text: 'text-lg',
146
+ label: 'text-xs',
147
+ hint: 'text-xs',
148
+ icon: 'text-sm'
149
+ },
150
+ medium: {
151
+ box: 'w-12 h-14',
152
+ text: 'text-2xl',
153
+ label: 'text-sm',
154
+ hint: 'text-xs',
155
+ icon: 'text-base'
156
+ },
157
+ large: {
158
+ box: 'w-14 h-16',
159
+ text: 'text-3xl',
160
+ label: 'text-base',
161
+ hint: 'text-sm',
162
+ icon: 'text-lg'
163
+ },
164
+ xl: {
165
+ box: 'w-18 h-20',
166
+ text: 'text-4xl',
167
+ label: 'text-lg',
168
+ hint: 'text-base',
169
+ icon: 'text-xl'
170
+ }
171
+ }
172
+ return sizes[props.size]
173
+ })
174
+
175
+ // Gap configurations
176
+ const gapConfig = computed(() => {
177
+ const gaps = {
178
+ tight: 'gap-1',
179
+ normal: 'gap-2',
180
+ wide: 'gap-4'
181
+ }
182
+ return gaps[props.gap]
183
+ })
184
+
185
+ // Rounded configurations
186
+ const roundedConfig = computed(() => {
187
+ const radii = {
188
+ none: 'rounded-none',
189
+ sm: 'rounded',
190
+ md: 'rounded-lg',
191
+ lg: 'rounded-xl',
192
+ full: 'rounded-full'
193
+ }
194
+ return radii[props.rounded]
195
+ })
196
+
197
+ // Variant classes
198
+ const variantClasses = computed(() => {
199
+ const base = {
200
+ outlined: 'border-2 bg-background border-border',
201
+ filled: 'border-2 border-transparent bg-accent',
202
+ underlined: 'border-b-2 border-t-0 border-l-0 border-r-0 rounded-none! bg-transparent border-border',
203
+ ghost: 'border-2 border-transparent bg-transparent',
204
+ morphing: 'border-2 bg-muted border-border shadow-inner'
205
+ }
206
+ return base[props.variant]
207
+ })
208
+
209
+ // Check if value at index is valid
210
+ const isValidChar = (char: string): boolean => {
211
+ if (!char) return true
212
+ const regex = new RegExp(`^${inputPattern.value}$`)
213
+ return regex.test(char)
214
+ }
215
+
216
+ // Get display character - handles morphing animation phases
217
+ const getDisplayChar = (index: number): string => {
218
+ const morphData = morphingIndices.value.get(index)
219
+
220
+ if (morphData) {
221
+ // During morph animation, show old char during 'out' phase, new char during 'in' phase
222
+ const char = morphData.phase === 'out' ? morphData.from : morphData.to
223
+ if (!char) return props.showPlaceholder ? props.placeholderChar : ''
224
+ if (props.masked && char) return props.maskChar
225
+ return char.toUpperCase()
226
+ }
227
+
228
+ const char = digits.value[index]
229
+ if (!char) return props.showPlaceholder ? props.placeholderChar : ''
230
+ if (props.masked) return props.maskChar
231
+ return char.toUpperCase()
232
+ }
233
+
234
+ // Get morph transform style for enhanced animation
235
+ const getMorphStyle = (index: number) => {
236
+ const morphData = morphingIndices.value.get(index)
237
+ if (!morphData) return {}
238
+
239
+ const { progress, phase } = morphData
240
+
241
+ if (phase === 'out') {
242
+ // Old character: scale down, rotate, blur, fade out
243
+ const scale = 1 - progress * 0.5
244
+ const rotateY = progress * 90
245
+ const blur = progress * 4
246
+ const opacity = 1 - progress
247
+ return {
248
+ transform: `scale(${scale}) rotateY(${rotateY}deg) translateZ(0)`,
249
+ filter: `blur(${blur}px)`,
250
+ opacity
251
+ }
252
+ } else {
253
+ // New character: scale up, rotate in, unblur, fade in
254
+ const scale = 0.5 + progress * 0.5
255
+ const rotateY = -90 + progress * 90
256
+ const blur = (1 - progress) * 4
257
+ const opacity = progress
258
+ return {
259
+ transform: `scale(${scale}) rotateY(${rotateY}deg) translateZ(0)`,
260
+ filter: `blur(${blur}px)`,
261
+ opacity
262
+ }
263
+ }
264
+ }
265
+
266
+ // Check if OTP is complete
267
+ const checkComplete = () => {
268
+ const value = digits.value.join('')
269
+ isComplete.value = value.length === props.maxlength && digits.value.every(d => d !== '')
270
+
271
+ if (isComplete.value && props.autoSubmit) {
272
+ showSuccess.value = true
273
+ emit('complete', value)
274
+ emit('success')
275
+
276
+ // Reset success after animation
277
+ setTimeout(() => {
278
+ showSuccess.value = false
279
+ }, 1500)
280
+ }
281
+ }
282
+
283
+ // Text morphing animation - two phase: out (old char fades/flips out) then in (new char fades/flips in)
284
+ const startMorphAnimation = (index: number, from: string, to: string) => {
285
+ if (!props.morphText) return
286
+
287
+ // If no actual change, just trigger input animation
288
+ if (from === to) {
289
+ animatingIndices.value.add(index)
290
+ setTimeout(() => animatingIndices.value.delete(index), 300)
291
+ return
292
+ }
293
+
294
+ const halfDuration = props.morphDuration
295
+ const totalDuration = halfDuration * 2
296
+
297
+ // Start with 'out' phase
298
+ morphingIndices.value.set(index, { from, to, progress: 0, phase: 'out' })
299
+ animatingIndices.value.add(index)
300
+
301
+ const startTime = performance.now()
302
+ const animate = (currentTime: number) => {
303
+ const elapsed = currentTime - startTime
304
+ const totalProgress = Math.min(elapsed / totalDuration, 1)
305
+
306
+ if (totalProgress < 0.5) {
307
+ // Phase 1: "out" - old character morphs away
308
+ const outProgress = totalProgress * 2 // 0 to 1 during first half
309
+ morphingIndices.value.set(index, { from, to, progress: outProgress, phase: 'out' })
310
+ } else {
311
+ // Phase 2: "in" - new character morphs in
312
+ const inProgress = (totalProgress - 0.5) * 2 // 0 to 1 during second half
313
+ morphingIndices.value.set(index, { from, to, progress: inProgress, phase: 'in' })
314
+ }
315
+
316
+ if (totalProgress < 1) {
317
+ requestAnimationFrame(animate)
318
+ } else {
319
+ morphingIndices.value.delete(index)
320
+ animatingIndices.value.delete(index)
321
+ }
322
+ }
323
+
324
+ requestAnimationFrame(animate)
325
+ }
326
+
327
+ // Handle input
328
+ const handleInput = (event: Event, index: number) => {
329
+ const target = event.target as HTMLInputElement
330
+ let value = target.value.slice(-1) // Take only the last character
331
+
332
+ // Validate character
333
+ if (value && !isValidChar(value)) {
334
+ target.value = digits.value[index]
335
+ return
336
+ }
337
+
338
+ // Format for mode
339
+ if (props.mode === 'alphabetic' || props.mode === 'alphanumeric') {
340
+ value = value.toUpperCase()
341
+ }
342
+
343
+ const oldValue = digits.value[index]
344
+ digits.value[index] = value
345
+
346
+ // Trigger morph animation
347
+ startMorphAnimation(index, oldValue, value)
348
+
349
+ // Emit events
350
+ const fullValue = digits.value.join('')
351
+ emit('update:modelValue', fullValue)
352
+ emit('input', index, value)
353
+ emit('change', fullValue)
354
+
355
+ // Move to next input
356
+ if (value && index < props.maxlength - 1) {
357
+ nextTick(() => {
358
+ focusInput(index + 1)
359
+ })
360
+ }
361
+
362
+ checkComplete()
363
+ }
364
+
365
+ // Handle keydown
366
+ const handleKeydown = (event: KeyboardEvent, index: number) => {
367
+ const key = event.key
368
+
369
+ if (key === 'Backspace') {
370
+ event.preventDefault()
371
+
372
+ if (digits.value[index]) {
373
+ // Clear current digit
374
+ const oldValue = digits.value[index]
375
+ digits.value[index] = ''
376
+ startMorphAnimation(index, oldValue, '')
377
+ } else if (index > 0) {
378
+ // Move to previous and clear
379
+ focusInput(index - 1)
380
+ const oldValue = digits.value[index - 1]
381
+ digits.value[index - 1] = ''
382
+ startMorphAnimation(index - 1, oldValue, '')
383
+ }
384
+
385
+ const fullValue = digits.value.join('')
386
+ emit('update:modelValue', fullValue)
387
+ emit('change', fullValue)
388
+ isComplete.value = false
389
+
390
+ } else if (key === 'Delete') {
391
+ event.preventDefault()
392
+ if (digits.value[index]) {
393
+ const oldValue = digits.value[index]
394
+ digits.value[index] = ''
395
+ startMorphAnimation(index, oldValue, '')
396
+
397
+ const fullValue = digits.value.join('')
398
+ emit('update:modelValue', fullValue)
399
+ emit('change', fullValue)
400
+ isComplete.value = false
401
+ }
402
+
403
+ } else if (key === 'ArrowLeft') {
404
+ event.preventDefault()
405
+ if (index > 0) {
406
+ focusInput(index - 1)
407
+ }
408
+
409
+ } else if (key === 'ArrowRight') {
410
+ event.preventDefault()
411
+ if (index < props.maxlength - 1) {
412
+ focusInput(index + 1)
413
+ }
414
+
415
+ } else if (key === 'Home') {
416
+ event.preventDefault()
417
+ focusInput(0)
418
+
419
+ } else if (key === 'End') {
420
+ event.preventDefault()
421
+ focusInput(props.maxlength - 1)
422
+ }
423
+ }
424
+
425
+ // Handle paste
426
+ const handlePaste = (event: ClipboardEvent, index: number) => {
427
+ if (!props.allowPaste) {
428
+ event.preventDefault()
429
+ return
430
+ }
431
+
432
+ event.preventDefault()
433
+ const pastedData = event.clipboardData?.getData('text') || ''
434
+ const cleanedData = pastedData.replace(new RegExp(`[^${inputPattern.value.slice(1, -1)}]`, 'gi'), '')
435
+
436
+ if (!cleanedData) return
437
+
438
+ emit('paste', cleanedData)
439
+
440
+ // Fill digits starting from current index
441
+ const chars = cleanedData.split('').slice(0, props.maxlength - index)
442
+ chars.forEach((char, i) => {
443
+ if (index + i < props.maxlength) {
444
+ const oldValue = digits.value[index + i]
445
+ const newValue = props.mode !== 'numeric' ? char.toUpperCase() : char
446
+ digits.value[index + i] = newValue
447
+ startMorphAnimation(index + i, oldValue, newValue)
448
+ }
449
+ })
450
+
451
+ const fullValue = digits.value.join('')
452
+ emit('update:modelValue', fullValue)
453
+ emit('change', fullValue)
454
+
455
+ // Focus the next empty input or last filled
456
+ const nextEmptyIndex = digits.value.findIndex((d, i) => i >= index && !d)
457
+ if (nextEmptyIndex !== -1) {
458
+ focusInput(nextEmptyIndex)
459
+ } else {
460
+ focusInput(Math.min(index + chars.length, props.maxlength - 1))
461
+ }
462
+
463
+ checkComplete()
464
+ }
465
+
466
+ // Handle focus
467
+ const handleFocus = (index: number) => {
468
+ activeIndex.value = index
469
+ emit('focus', index)
470
+
471
+ // Select the input content
472
+ const input = inputRefs.value[index]
473
+ if (input) {
474
+ input.select()
475
+ }
476
+ }
477
+
478
+ // Handle blur
479
+ const handleBlur = (index: number) => {
480
+ activeIndex.value = -1
481
+ emit('blur', index)
482
+ }
483
+
484
+ // Focus specific input
485
+ const focusInput = (index: number) => {
486
+ const input = inputRefs.value[index]
487
+ if (input && !props.disabled && !props.readonly) {
488
+ input.focus()
489
+ input.select()
490
+ }
491
+ }
492
+
493
+ // Clear all
494
+ const clear = () => {
495
+ digits.value = Array(props.maxlength).fill('')
496
+ emit('update:modelValue', '')
497
+ emit('change', '')
498
+ isComplete.value = false
499
+ focusInput(0)
500
+ }
501
+
502
+ // Trigger error state
503
+ const triggerError = (message?: string) => {
504
+ showError.value = true
505
+ emit('error', message || props.errorMessage || 'Invalid code')
506
+
507
+ if (props.clearOnError) {
508
+ setTimeout(() => {
509
+ clear()
510
+ }, 500)
511
+ }
512
+
513
+ setTimeout(() => {
514
+ showError.value = false
515
+ }, 600)
516
+ }
517
+
518
+ // Start countdown
519
+ const startCountdown = () => {
520
+ if (countdownInterval.value) {
521
+ clearInterval(countdownInterval.value)
522
+ }
523
+
524
+ countdownValue.value = props.countdown
525
+
526
+ countdownInterval.value = setInterval(() => {
527
+ if (countdownValue.value > 0) {
528
+ countdownValue.value--
529
+ } else if (countdownInterval.value) {
530
+ clearInterval(countdownInterval.value)
531
+ countdownInterval.value = null
532
+ }
533
+ }, 1000)
534
+ }
535
+
536
+ // Handle resend
537
+ const handleResend = () => {
538
+ if (countdownValue.value > 0) return
539
+
540
+ emit('resend')
541
+ clear()
542
+
543
+ if (props.countdown > 0) {
544
+ startCountdown()
545
+ }
546
+ }
547
+
548
+ // Format countdown
549
+ const formattedCountdown = computed(() => {
550
+ const minutes = Math.floor(countdownValue.value / 60)
551
+ const seconds = countdownValue.value % 60
552
+ return `${minutes}:${seconds.toString().padStart(2, '0')}`
553
+ })
554
+
555
+ // Should show separator after index
556
+ const shouldShowSeparator = (index: number): boolean => {
557
+ if (!props.separator || !props.separatorPosition?.length) return false
558
+ return props.separatorPosition.includes(index + 1) && index < props.maxlength - 1
559
+ }
560
+
561
+ // Register input from SOTPSlot
562
+ const registerInput = (index: number, el: HTMLInputElement | null) => {
563
+ inputRefs.value[index] = el
564
+ }
565
+
566
+ // Visual defaults computed
567
+ const defaults = computed<SOTPVisualDefaults>(() => ({
568
+ variant: props.variant,
569
+ size: props.size,
570
+ color: props.color,
571
+ rounded: props.rounded,
572
+ animation: props.animation,
573
+ entryAnimation: props.entryAnimation,
574
+ inputAnimation: props.inputAnimation,
575
+ successAnimation: props.successAnimation,
576
+ errorAnimation: props.errorAnimation,
577
+ morphText: props.morphText,
578
+ morphDuration: props.morphDuration,
579
+ showPlaceholder: props.showPlaceholder,
580
+ placeholderChar: props.placeholderChar,
581
+ maskChar: props.maskChar,
582
+ }))
583
+
584
+ // Expose methods
585
+ defineExpose({
586
+ clear,
587
+ focusInput,
588
+ focus: () => focusInput(0),
589
+ blur: () => inputRefs.value[activeIndex.value]?.blur(),
590
+ triggerError,
591
+ getValue: () => digits.value.join(''),
592
+ isComplete: () => isComplete.value
593
+ })
594
+
595
+ // Provide context for SOTPSlot children
596
+ provide(SOTP_INJECTION_KEY, {
597
+ digits,
598
+ activeIndex,
599
+ isComplete: isComplete as any,
600
+ showSuccess,
601
+ showError,
602
+ animatingIndices,
603
+ morphingIndices,
604
+ maxlength: toRef(props, 'maxlength'),
605
+ mode: toRef(props, 'mode'),
606
+ masked: toRef(props, 'masked'),
607
+ disabled: toRef(props, 'disabled'),
608
+ readonly: toRef(props, 'readonly'),
609
+ loading: toRef(props, 'loading'),
610
+ allowPaste: toRef(props, 'allowPaste'),
611
+ error: toRef(props, 'error') as any,
612
+ success: toRef(props, 'success') as any,
613
+ ariaLabel: toRef(props, 'ariaLabel') as any,
614
+ defaults,
615
+ inputPattern,
616
+ inputMode,
617
+ registerInput,
618
+ handleInput,
619
+ handleKeydown,
620
+ handlePaste,
621
+ handleFocus,
622
+ handleBlur,
623
+ focusInput,
624
+ startMorphAnimation,
625
+ getMorphStyle,
626
+ getDisplayChar,
627
+ } satisfies SOTPContext)
628
+
629
+ // Lifecycle
630
+ onMounted(() => {
631
+ if (props.autoFocus) {
632
+ nextTick(() => {
633
+ // Find first empty input
634
+ const firstEmptyIndex = digits.value.findIndex(d => !d)
635
+ focusInput(firstEmptyIndex !== -1 ? firstEmptyIndex : 0)
636
+ })
637
+ }
638
+
639
+ if (props.countdown > 0) {
640
+ startCountdown()
641
+ }
642
+ })
643
+
644
+ onUnmounted(() => {
645
+ if (countdownInterval.value) {
646
+ clearInterval(countdownInterval.value)
647
+ }
648
+ })
649
+
650
+ // Initialize digits from modelValue
651
+ watch(() => props.modelValue, (newValue) => {
652
+ const chars = newValue.split('').slice(0, props.maxlength)
653
+ digits.value = [...chars, ...Array(props.maxlength - chars.length).fill('')]
654
+ checkComplete()
655
+ }, { immediate: true })
656
+
657
+ // Watch for maxlength prop changes
658
+ watch(() => props.maxlength, (newLength, oldLength) => {
659
+ if (newLength !== oldLength) {
660
+ const currentValue = digits.value.join('')
661
+ const chars = currentValue.split('').slice(0, newLength)
662
+ digits.value = [...chars, ...Array(newLength - chars.length).fill('')]
663
+ emit('update:modelValue', digits.value.join(''))
664
+ checkComplete()
665
+ }
666
+ })
667
+
668
+ // Watch for error prop changes
669
+ watch(() => props.error, (hasError) => {
670
+ if (hasError) {
671
+ showError.value = true
672
+ setTimeout(() => {
673
+ showError.value = false
674
+ }, 600)
675
+ }
676
+ })
677
+ </script>
678
+
679
+ <template>
680
+ <div
681
+ ref="containerRef"
682
+ v-bind="$attrs"
683
+ :class="cn(
684
+ 's-otp-wrapper flex flex-col items-center w-fit',
685
+ disabled && 'opacity-50 pointer-events-none',
686
+ $attrs.class ?? ''
687
+ )"
688
+ >
689
+ <!-- Label -->
690
+ <label
691
+ v-if="label"
692
+ class="s-otp-label font-medium text-muted-foreground mb-3 text-center"
693
+ :class="sizeConfig.label"
694
+ >
695
+ {{ label }}
696
+ </label>
697
+
698
+ <!-- OTP Container — children (SOTPGroup > SOTPSlot) go here -->
699
+ <div
700
+ class="s-otp-container relative flex items-center"
701
+ :class="gapConfig"
702
+ >
703
+ <slot />
704
+
705
+ <!-- Loading indicator -->
706
+ <div v-if="loading" class="ml-3 flex items-center">
707
+ <span class="mdi mdi-loading animate-spin text-primary" :class="sizeConfig.icon" />
708
+ </div>
709
+
710
+ <!-- Success checkmark overlay (global effect) -->
711
+ <Transition
712
+ enter-active-class="transition-all duration-400 ease-out"
713
+ enter-from-class="opacity-0 scale-50"
714
+ enter-to-class="opacity-100 scale-100"
715
+ leave-active-class="transition-all duration-200 ease-in"
716
+ leave-from-class="opacity-100 scale-100"
717
+ leave-to-class="opacity-0 scale-50"
718
+ >
719
+ <div
720
+ v-if="showSuccess && defaults.successAnimation === 'check'"
721
+ class="absolute inset-0 flex items-center justify-center pointer-events-none z-20"
722
+ >
723
+ <div
724
+ class="flex items-center justify-center bg-emerald-500 text-white rounded-full shadow-lg shadow-emerald-500/50 animate-check-bounce"
725
+ :class="{
726
+ 'w-12 h-12 text-2xl': defaults.size === 'small',
727
+ 'w-16 h-16 text-3xl': defaults.size === 'medium',
728
+ 'w-20 h-20 text-4xl': defaults.size === 'large',
729
+ 'w-24 h-24 text-5xl': defaults.size === 'xl'
730
+ }"
731
+ >
732
+ <span class="mdi mdi-check-bold animate-check-draw" />
733
+ </div>
734
+ </div>
735
+ </Transition>
736
+ </div>
737
+
738
+ <!-- Messages -->
739
+ <div class="s-otp-messages mt-3 text-center min-h-5">
740
+ <Transition
741
+ enter-active-class="transition-all duration-200 ease-out"
742
+ enter-from-class="opacity-0 -translate-y-2"
743
+ enter-to-class="opacity-100 translate-y-0"
744
+ leave-active-class="transition-all duration-150 ease-in"
745
+ leave-from-class="opacity-100 translate-y-0"
746
+ leave-to-class="opacity-0 -translate-y-2"
747
+ mode="out-in"
748
+ >
749
+ <!-- Error message -->
750
+ <p
751
+ v-if="error || showError"
752
+ key="error"
753
+ class="text-red-500 flex items-center justify-center gap-1"
754
+ :class="sizeConfig.hint"
755
+ >
756
+ <span class="mdi mdi-alert-circle" />
757
+ {{ typeof error === 'string' ? error : errorMessage || 'Invalid code' }}
758
+ </p>
759
+
760
+ <!-- Success message -->
761
+ <p
762
+ v-else-if="success || showSuccess"
763
+ key="success"
764
+ class="text-emerald-500 flex items-center justify-center gap-1"
765
+ :class="sizeConfig.hint"
766
+ >
767
+ <span class="mdi mdi-check-circle" />
768
+ {{ typeof success === 'string' ? success : successMessage || 'Code verified!' }}
769
+ </p>
770
+
771
+ <!-- Hint -->
772
+ <p
773
+ v-else-if="hint"
774
+ key="hint"
775
+ class="text-muted-foreground"
776
+ :class="sizeConfig.hint"
777
+ >
778
+ {{ hint }}
779
+ </p>
780
+
781
+ <span v-else key="empty" />
782
+ </Transition>
783
+ </div>
784
+
785
+ <!-- Resend section -->
786
+ <div
787
+ v-if="countdown > 0 || $slots.resend"
788
+ class="s-otp-resend mt-4 text-center"
789
+ >
790
+ <slot name="resend">
791
+ <button
792
+ type="button"
793
+ class="text-sm transition-all duration-200"
794
+ :class="[
795
+ countdownValue > 0
796
+ ? 'text-muted-foreground cursor-not-allowed'
797
+ : 'text-primary hover:underline cursor-pointer'
798
+ ]"
799
+ :disabled="countdownValue > 0"
800
+ @click="handleResend"
801
+ >
802
+ <template v-if="countdownValue > 0">
803
+ {{ resendText }} in {{ formattedCountdown }}
804
+ </template>
805
+ <template v-else>
806
+ {{ resendText }}
807
+ </template>
808
+ </button>
809
+ </slot>
810
+ </div>
811
+
812
+ </div>
813
+ </template>
814
+
815
+ <style scoped>
816
+ /* Success check animations */
817
+ @keyframes check-bounce {
818
+ 0% { transform: scale(0) rotate(-45deg); }
819
+ 50% { transform: scale(1.2) rotate(10deg); }
820
+ 70% { transform: scale(0.9) rotate(-5deg); }
821
+ 100% { transform: scale(1) rotate(0deg); }
822
+ }
823
+ .animate-check-bounce {
824
+ animation: check-bounce 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
825
+ }
826
+
827
+ @keyframes check-draw {
828
+ 0% {
829
+ clip-path: inset(0 100% 0 0);
830
+ opacity: 0;
831
+ }
832
+ 50% {
833
+ opacity: 1;
834
+ }
835
+ 100% {
836
+ clip-path: inset(0 0 0 0);
837
+ opacity: 1;
838
+ }
839
+ }
840
+ .animate-check-draw {
841
+ animation: check-draw 0.4s ease-out 0.1s both;
842
+ }
843
+ </style>