@stacksjs/components 0.2.3

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 (342) hide show
  1. package/README.md +477 -0
  2. package/dist/Accordion-x2gjedap.stx +108 -0
  3. package/dist/Audio-0fk6p9tn.stx +50 -0
  4. package/dist/Avatar-5m0v0s4x.stx +88 -0
  5. package/dist/Badge-ynes82vv.stx +70 -0
  6. package/dist/Breadcrumb-h9n427sk.stx +79 -0
  7. package/dist/Button-7xtrzn14.stx +89 -0
  8. package/dist/Calendar-egvx1pma.stx +232 -0
  9. package/dist/Card-r9w7vy1a.stx +74 -0
  10. package/dist/Checkbox-m8v46pmx.stx +92 -0
  11. package/dist/Checkout-vcnnr176.stx +39 -0
  12. package/dist/CodeBlock-mwex82cm.stx +57 -0
  13. package/dist/Combobox-npb08vkp.stx +13 -0
  14. package/dist/ComboboxButton-vcdyq4zw.stx +13 -0
  15. package/dist/ComboboxInput-c9d2p02m.stx +25 -0
  16. package/dist/ComboboxOption-7d0h9ash.stx +52 -0
  17. package/dist/ComboboxOptions-4dtqc0qa.stx +12 -0
  18. package/dist/CommandPalette-ah00bf8v.stx +39 -0
  19. package/dist/CommandPaletteItem-2yx0gvtj.stx +32 -0
  20. package/dist/DefaultPaymentMethod-emw5fg8s.stx +62 -0
  21. package/dist/Dialog-1w4rz7x0.stx +19 -0
  22. package/dist/DialogBackdrop-qbm3y5ab.stx +9 -0
  23. package/dist/DialogDescription-01w5j74b.stx +11 -0
  24. package/dist/DialogPanel-brsszrrs.stx +16 -0
  25. package/dist/DialogTitle-3rxhwg21.stx +11 -0
  26. package/dist/Drawer-yfaz8q7f.stx +137 -0
  27. package/dist/Dropdown-ng7nx556.stx +24 -0
  28. package/dist/DropdownButton-vc4zpyrb.stx +23 -0
  29. package/dist/DropdownItem-z491y23c.stx +43 -0
  30. package/dist/DropdownItems-7ykf29sj.stx +18 -0
  31. package/dist/EmailInput-bfshpj92.stx +41 -0
  32. package/dist/Footer-xdaf02nr.stx +66 -0
  33. package/dist/Form-z4f7w6r1.stx +209 -0
  34. package/dist/Hero-9vvp7pxf.stx +25 -0
  35. package/dist/Image-ktk0451c.stx +197 -0
  36. package/dist/Installation-33bj4rm7.stx +39 -0
  37. package/dist/Listbox-xcejcmyc.stx +14 -0
  38. package/dist/ListboxButton-nx5kmw19.stx +17 -0
  39. package/dist/ListboxLabel-aky8a73n.stx +11 -0
  40. package/dist/ListboxOption-6h4hsdz5.stx +57 -0
  41. package/dist/ListboxOptions-2sacdtm5.stx +16 -0
  42. package/dist/Login-s2jkvd9y.stx +163 -0
  43. package/dist/Navigator-tqqdgdkw.stx +138 -0
  44. package/dist/Notification-sry8egea.stx +71 -0
  45. package/dist/NumberInput-zm93ddfw.stx +138 -0
  46. package/dist/Pagination-yza9v4qw.stx +183 -0
  47. package/dist/PasswordInput-4xnndp5y.stx +155 -0
  48. package/dist/PaymentMethods-jxba6kek.stx +113 -0
  49. package/dist/Popover-scbqatjq.stx +24 -0
  50. package/dist/PopoverButton-rbxhzj4k.stx +23 -0
  51. package/dist/PopoverPanel-9ng1rm6e.stx +23 -0
  52. package/dist/Portal-j111yvqp.stx +74 -0
  53. package/dist/Progress-gbmw3akr.stx +142 -0
  54. package/dist/Radio-1n9qamv1.stx +81 -0
  55. package/dist/RadioGroup-12dw2vhk.stx +13 -0
  56. package/dist/RadioGroupDescription-bjm3mdht.stx +11 -0
  57. package/dist/RadioGroupLabel-aky8a73n.stx +11 -0
  58. package/dist/RadioGroupOption-9pcxgwmd.stx +51 -0
  59. package/dist/SearchInput-g240e2nq.stx +63 -0
  60. package/dist/Select-p9f94b20.stx +89 -0
  61. package/dist/Sidebar-gwxw8q38.stx +288 -0
  62. package/dist/SidebarFooter-887kxret.stx +95 -0
  63. package/dist/SidebarHeader-qjeyx0hv.stx +100 -0
  64. package/dist/SidebarItem-4dpwtmb4.stx +118 -0
  65. package/dist/SidebarSection-kh92fk4m.stx +108 -0
  66. package/dist/Signup-3nxq5zag.stx +161 -0
  67. package/dist/Skeleton-g0nx1njm.stx +48 -0
  68. package/dist/Spinner-w4zj58xp.stx +109 -0
  69. package/dist/Stepper-rwc5actp.stx +17 -0
  70. package/dist/StepperStep-h0k657nm.stx +53 -0
  71. package/dist/SubscriptionCheckout-4cbxdg7w.stx +353 -0
  72. package/dist/Switch-kjq8qd2p.stx +74 -0
  73. package/dist/Table-59j9qwp0.stx +14 -0
  74. package/dist/TableBody-reka2gwv.stx +10 -0
  75. package/dist/TableCell-j7nh4sgr.stx +10 -0
  76. package/dist/TableHead-ecbrwpqm.stx +10 -0
  77. package/dist/TableHeader-jn2shwsv.stx +13 -0
  78. package/dist/TableRow-xz8rrxhc.stx +11 -0
  79. package/dist/Tabs-kb6ex6m7.stx +145 -0
  80. package/dist/Teleport-819cg9zm.stx +101 -0
  81. package/dist/TextInput-zx9kgr79.stx +146 -0
  82. package/dist/Textarea-5xvmrb0z.stx +119 -0
  83. package/dist/Tooltip-5ehqsy49.stx +76 -0
  84. package/dist/Transition-2j39s044.stx +174 -0
  85. package/dist/TwoFactorChallenge-2q4gg89m.stx +125 -0
  86. package/dist/Video-6aq4shh4.stx +45 -0
  87. package/dist/VirtualList-weh5qtm1.stx +83 -0
  88. package/dist/VirtualTable-59a1eskc.stx +113 -0
  89. package/dist/components/index.d.ts +4 -0
  90. package/dist/composables/index.d.ts +4 -0
  91. package/dist/composables/index.js +3 -0
  92. package/dist/composables/useCopyCode.d.ts +19 -0
  93. package/dist/composables/useDarkMode.d.ts +25 -0
  94. package/dist/composables/useSEO.d.ts +36 -0
  95. package/dist/composables/utilities/index.d.ts +5 -0
  96. package/dist/composables/utilities/useClickOutside.d.ts +28 -0
  97. package/dist/composables/utilities/useFocusTrap.d.ts +34 -0
  98. package/dist/composables/utilities/useKeyboard.d.ts +55 -0
  99. package/dist/composables/utilities/useLocalStorage.d.ts +33 -0
  100. package/dist/composables/utilities/useMediaQuery.d.ts +64 -0
  101. package/dist/index.d.ts +86 -0
  102. package/dist/index.js +19 -0
  103. package/dist/ui/accordion/index.d.ts +12 -0
  104. package/dist/ui/audio/index.d.ts +12 -0
  105. package/dist/ui/auth/index.d.ts +29 -0
  106. package/dist/ui/avatar/index.d.ts +11 -0
  107. package/dist/ui/badge/index.d.ts +9 -0
  108. package/dist/ui/breadcrumb/index.d.ts +13 -0
  109. package/dist/ui/button/index.d.ts +45 -0
  110. package/dist/ui/calendar/index.d.ts +13 -0
  111. package/dist/ui/card/index.d.ts +12 -0
  112. package/dist/ui/checkbox/index.d.ts +15 -0
  113. package/dist/ui/combobox/index.d.ts +35 -0
  114. package/dist/ui/command-palette/index.d.ts +13 -0
  115. package/dist/ui/dialog/index.d.ts +27 -0
  116. package/dist/ui/drawer/index.d.ts +8 -0
  117. package/dist/ui/dropdown/index.d.ts +25 -0
  118. package/dist/ui/form/index.d.ts +48 -0
  119. package/dist/ui/image/index.d.ts +21 -0
  120. package/dist/ui/input/index.d.ts +73 -0
  121. package/dist/ui/listbox/index.d.ts +34 -0
  122. package/dist/ui/navigator/index.d.ts +18 -0
  123. package/dist/ui/notification/index.d.ts +11 -0
  124. package/dist/ui/pagination/index.d.ts +10 -0
  125. package/dist/ui/payment/index.d.ts +53 -0
  126. package/dist/ui/popover/index.d.ts +19 -0
  127. package/dist/ui/portal/index.d.ts +5 -0
  128. package/dist/ui/progress/index.d.ts +11 -0
  129. package/dist/ui/radio/index.d.ts +14 -0
  130. package/dist/ui/radio-group/index.d.ts +26 -0
  131. package/dist/ui/select/index.d.ts +19 -0
  132. package/dist/ui/sidebar/index.d.ts +129 -0
  133. package/dist/ui/skeleton/index.d.ts +10 -0
  134. package/dist/ui/spinner/index.d.ts +8 -0
  135. package/dist/ui/stepper/index.d.ts +17 -0
  136. package/dist/ui/storage/index.d.ts +40 -0
  137. package/dist/ui/switch/index.d.ts +9 -0
  138. package/dist/ui/table/index.d.ts +29 -0
  139. package/dist/ui/tabs/index.d.ts +14 -0
  140. package/dist/ui/teleport/index.d.ts +6 -0
  141. package/dist/ui/textarea/index.d.ts +19 -0
  142. package/dist/ui/tooltip/index.d.ts +9 -0
  143. package/dist/ui/transition/index.d.ts +24 -0
  144. package/dist/ui/video/index.d.ts +15 -0
  145. package/dist/ui/virtual-list/index.d.ts +9 -0
  146. package/dist/ui/virtual-table/index.d.ts +19 -0
  147. package/dist/utils/accessibility.d.ts +285 -0
  148. package/dist/utils/animation/easing.d.ts +112 -0
  149. package/dist/utils/animation/helpers.d.ts +119 -0
  150. package/dist/utils/animation/index.d.ts +3 -0
  151. package/dist/utils/animation/keyframes.d.ts +334 -0
  152. package/dist/utils/error-handling.d.ts +182 -0
  153. package/dist/utils/highlighter.d.ts +27 -0
  154. package/dist/utils/highlighter.js +2 -0
  155. package/dist/utils/i18n.d.ts +245 -0
  156. package/dist/utils/keep-alive.d.ts +171 -0
  157. package/dist/utils/lazy-loading.d.ts +174 -0
  158. package/dist/utils/prop-validation.d.ts +106 -0
  159. package/dist/utils/theme.d.ts +234 -0
  160. package/package.json +69 -0
  161. package/src/components/CodeBlock.stx +57 -0
  162. package/src/components/Footer.stx +66 -0
  163. package/src/components/Hero.stx +25 -0
  164. package/src/components/Installation.stx +39 -0
  165. package/src/components/index.ts +4 -0
  166. package/src/composables/index.ts +4 -0
  167. package/src/composables/useCopyCode.ts +73 -0
  168. package/src/composables/useDarkMode.ts +100 -0
  169. package/src/composables/useSEO.ts +229 -0
  170. package/src/composables/utilities/index.ts +7 -0
  171. package/src/composables/utilities/useClickOutside.ts +72 -0
  172. package/src/composables/utilities/useFocusTrap.ts +159 -0
  173. package/src/composables/utilities/useKeyboard.ts +124 -0
  174. package/src/composables/utilities/useLocalStorage.ts +132 -0
  175. package/src/composables/utilities/useMediaQuery.ts +118 -0
  176. package/src/index.ts +99 -0
  177. package/src/stx.d.ts +4 -0
  178. package/src/ui/accordion/Accordion.stx +108 -0
  179. package/src/ui/accordion/index.ts +14 -0
  180. package/src/ui/audio/Audio.stx +50 -0
  181. package/src/ui/audio/README.md +42 -0
  182. package/src/ui/audio/index.ts +13 -0
  183. package/src/ui/auth/Login.stx +163 -0
  184. package/src/ui/auth/README.md +145 -0
  185. package/src/ui/auth/Signup.stx +161 -0
  186. package/src/ui/auth/TwoFactorChallenge.stx +125 -0
  187. package/src/ui/auth/index.ts +32 -0
  188. package/src/ui/avatar/Avatar.stx +88 -0
  189. package/src/ui/avatar/index.ts +12 -0
  190. package/src/ui/badge/Badge.stx +70 -0
  191. package/src/ui/badge/index.ts +10 -0
  192. package/src/ui/breadcrumb/Breadcrumb.stx +79 -0
  193. package/src/ui/breadcrumb/index.ts +15 -0
  194. package/src/ui/button/Button.stx +89 -0
  195. package/src/ui/button/README.md +100 -0
  196. package/src/ui/button/index.ts +85 -0
  197. package/src/ui/calendar/Calendar.stx +232 -0
  198. package/src/ui/calendar/README.md +254 -0
  199. package/src/ui/calendar/index.ts +14 -0
  200. package/src/ui/card/Card.stx +74 -0
  201. package/src/ui/card/index.ts +13 -0
  202. package/src/ui/checkbox/Checkbox.stx +92 -0
  203. package/src/ui/checkbox/index.ts +16 -0
  204. package/src/ui/combobox/Combobox.stx +13 -0
  205. package/src/ui/combobox/ComboboxButton.stx +13 -0
  206. package/src/ui/combobox/ComboboxInput.stx +25 -0
  207. package/src/ui/combobox/ComboboxOption.stx +52 -0
  208. package/src/ui/combobox/ComboboxOptions.stx +12 -0
  209. package/src/ui/combobox/README.md +154 -0
  210. package/src/ui/combobox/index.ts +40 -0
  211. package/src/ui/command-palette/CommandPalette.stx +39 -0
  212. package/src/ui/command-palette/CommandPaletteItem.stx +32 -0
  213. package/src/ui/command-palette/README.md +42 -0
  214. package/src/ui/command-palette/index.ts +15 -0
  215. package/src/ui/dialog/Dialog.stx +19 -0
  216. package/src/ui/dialog/DialogBackdrop.stx +9 -0
  217. package/src/ui/dialog/DialogDescription.stx +11 -0
  218. package/src/ui/dialog/DialogPanel.stx +16 -0
  219. package/src/ui/dialog/DialogTitle.stx +11 -0
  220. package/src/ui/dialog/README.md +133 -0
  221. package/src/ui/dialog/index.ts +32 -0
  222. package/src/ui/drawer/Drawer.stx +137 -0
  223. package/src/ui/drawer/README.md +110 -0
  224. package/src/ui/drawer/index.ts +9 -0
  225. package/src/ui/dropdown/Dropdown.stx +24 -0
  226. package/src/ui/dropdown/DropdownButton.stx +23 -0
  227. package/src/ui/dropdown/DropdownItem.stx +43 -0
  228. package/src/ui/dropdown/DropdownItems.stx +18 -0
  229. package/src/ui/dropdown/README.md +142 -0
  230. package/src/ui/dropdown/index.ts +29 -0
  231. package/src/ui/form/Form.stx +209 -0
  232. package/src/ui/form/index.ts +67 -0
  233. package/src/ui/heatmap/Heatmap.stx +315 -0
  234. package/src/ui/image/Image.stx +197 -0
  235. package/src/ui/image/README.md +171 -0
  236. package/src/ui/image/index.ts +22 -0
  237. package/src/ui/input/EmailInput.stx +41 -0
  238. package/src/ui/input/NumberInput.stx +138 -0
  239. package/src/ui/input/PasswordInput.stx +155 -0
  240. package/src/ui/input/SearchInput.stx +63 -0
  241. package/src/ui/input/TextInput.stx +146 -0
  242. package/src/ui/input/index.ts +180 -0
  243. package/src/ui/listbox/Listbox.stx +14 -0
  244. package/src/ui/listbox/ListboxButton.stx +17 -0
  245. package/src/ui/listbox/ListboxLabel.stx +11 -0
  246. package/src/ui/listbox/ListboxOption.stx +57 -0
  247. package/src/ui/listbox/ListboxOptions.stx +16 -0
  248. package/src/ui/listbox/README.md +143 -0
  249. package/src/ui/listbox/index.ts +39 -0
  250. package/src/ui/navigator/Navigator.stx +138 -0
  251. package/src/ui/navigator/README.md +352 -0
  252. package/src/ui/navigator/index.ts +20 -0
  253. package/src/ui/notification/Notification.stx +71 -0
  254. package/src/ui/notification/README.md +118 -0
  255. package/src/ui/notification/index.ts +12 -0
  256. package/src/ui/pagination/Pagination.stx +183 -0
  257. package/src/ui/pagination/index.ts +11 -0
  258. package/src/ui/payment/Checkout.stx +39 -0
  259. package/src/ui/payment/DefaultPaymentMethod.stx +62 -0
  260. package/src/ui/payment/PaymentMethods.stx +113 -0
  261. package/src/ui/payment/README.md +427 -0
  262. package/src/ui/payment/SubscriptionCheckout.stx +353 -0
  263. package/src/ui/payment/index.ts +59 -0
  264. package/src/ui/popover/Popover.stx +24 -0
  265. package/src/ui/popover/PopoverButton.stx +23 -0
  266. package/src/ui/popover/PopoverPanel.stx +23 -0
  267. package/src/ui/popover/README.md +95 -0
  268. package/src/ui/popover/index.ts +22 -0
  269. package/src/ui/portal/Portal.stx +74 -0
  270. package/src/ui/portal/index.ts +6 -0
  271. package/src/ui/progress/Progress.stx +142 -0
  272. package/src/ui/progress/index.ts +12 -0
  273. package/src/ui/radio/Radio.stx +81 -0
  274. package/src/ui/radio/index.ts +15 -0
  275. package/src/ui/radio-group/README.md +145 -0
  276. package/src/ui/radio-group/RadioGroup.stx +13 -0
  277. package/src/ui/radio-group/RadioGroupDescription.stx +11 -0
  278. package/src/ui/radio-group/RadioGroupLabel.stx +11 -0
  279. package/src/ui/radio-group/RadioGroupOption.stx +51 -0
  280. package/src/ui/radio-group/index.ts +30 -0
  281. package/src/ui/select/README.md +204 -0
  282. package/src/ui/select/Select.stx +89 -0
  283. package/src/ui/select/index.ts +21 -0
  284. package/src/ui/sidebar/Sidebar.stx +288 -0
  285. package/src/ui/sidebar/SidebarFooter.stx +95 -0
  286. package/src/ui/sidebar/SidebarHeader.stx +100 -0
  287. package/src/ui/sidebar/SidebarItem.stx +118 -0
  288. package/src/ui/sidebar/SidebarSection.stx +108 -0
  289. package/src/ui/sidebar/index.ts +175 -0
  290. package/src/ui/skeleton/Skeleton.stx +48 -0
  291. package/src/ui/skeleton/index.ts +11 -0
  292. package/src/ui/spinner/Spinner.stx +109 -0
  293. package/src/ui/spinner/index.ts +9 -0
  294. package/src/ui/stepper/README.md +36 -0
  295. package/src/ui/stepper/Stepper.stx +17 -0
  296. package/src/ui/stepper/StepperStep.stx +53 -0
  297. package/src/ui/stepper/index.ts +19 -0
  298. package/src/ui/storage/README.md +193 -0
  299. package/src/ui/storage/index.ts +347 -0
  300. package/src/ui/switch/README.md +133 -0
  301. package/src/ui/switch/Switch.stx +74 -0
  302. package/src/ui/switch/index.ts +10 -0
  303. package/src/ui/table/README.md +39 -0
  304. package/src/ui/table/Table.stx +14 -0
  305. package/src/ui/table/TableBody.stx +10 -0
  306. package/src/ui/table/TableCell.stx +10 -0
  307. package/src/ui/table/TableHead.stx +10 -0
  308. package/src/ui/table/TableHeader.stx +13 -0
  309. package/src/ui/table/TableRow.stx +11 -0
  310. package/src/ui/table/index.ts +35 -0
  311. package/src/ui/tabs/Tabs.stx +145 -0
  312. package/src/ui/tabs/index.ts +16 -0
  313. package/src/ui/teleport/Teleport.stx +101 -0
  314. package/src/ui/teleport/index.ts +7 -0
  315. package/src/ui/textarea/Textarea.stx +119 -0
  316. package/src/ui/textarea/index.ts +20 -0
  317. package/src/ui/tooltip/Tooltip.stx +76 -0
  318. package/src/ui/tooltip/index.ts +10 -0
  319. package/src/ui/transition/README.md +30 -0
  320. package/src/ui/transition/Transition.stx +174 -0
  321. package/src/ui/transition/index.ts +25 -0
  322. package/src/ui/video/README.md +48 -0
  323. package/src/ui/video/Video.stx +45 -0
  324. package/src/ui/video/index.ts +16 -0
  325. package/src/ui/virtual-list/VirtualList.stx +83 -0
  326. package/src/ui/virtual-list/index.ts +10 -0
  327. package/src/ui/virtual-table/VirtualTable.stx +113 -0
  328. package/src/ui/virtual-table/index.ts +21 -0
  329. package/src/utils/PROP_VALIDATION.md +458 -0
  330. package/src/utils/THEME_SYSTEM.md +702 -0
  331. package/src/utils/accessibility.ts +841 -0
  332. package/src/utils/animation/easing.ts +368 -0
  333. package/src/utils/animation/helpers.ts +223 -0
  334. package/src/utils/animation/index.ts +5 -0
  335. package/src/utils/animation/keyframes.ts +230 -0
  336. package/src/utils/error-handling.ts +473 -0
  337. package/src/utils/highlighter.ts +93 -0
  338. package/src/utils/i18n.ts +634 -0
  339. package/src/utils/keep-alive.ts +818 -0
  340. package/src/utils/lazy-loading.ts +530 -0
  341. package/src/utils/prop-validation.ts +478 -0
  342. package/src/utils/theme.ts +626 -0
@@ -0,0 +1,163 @@
1
+ <script>
2
+ export const showLogo = $props.showLogo ?? true
3
+ export const headingText = $props.headingText || 'Sign in to your account'
4
+ export const showSocialLogin = $props.showSocialLogin ?? true
5
+ export const showRememberMe = $props.showRememberMe ?? true
6
+ export const showForgotPassword = $props.showForgotPassword ?? true
7
+ export const showSignup = $props.showSignup ?? true
8
+ export const signupText = $props.signupText || 'Start a 14 day free trial'
9
+ export const onSubmit = $props.onSubmit || (() => {})
10
+ export const className = $props.className || ''
11
+
12
+ export const containerClasses = `min-h-full flex flex-col justify-center py-12 lg:px-8 sm:px-6 dark:bg-blue-gray-900 ${className}`.trim()
13
+ </script>
14
+
15
+ <div class="{{ containerClasses }}">
16
+ <div class="sm:mx-auto sm:max-w-md sm:w-full">
17
+ @if(showLogo)
18
+ <img src="/logo.svg" alt="Logo" class="mx-auto h-11 w-auto" />
19
+ @endif
20
+ <h2 class="mt-6 text-center text-2xl text-gray-900 dark:text-gray-100 font-bold leading-9 tracking-tight">
21
+ {{ headingText }}
22
+ </h2>
23
+ </div>
24
+
25
+ <form onsubmit="event.preventDefault(); handleSubmit(event)">
26
+ <div class="mt-10 sm:mx-auto sm:max-w-[480px] sm:w-full">
27
+ <div class="bg-white dark:bg-blue-gray-800 px-6 py-12 shadow sm:rounded-lg sm:px-12">
28
+ <div class="space-y-6">
29
+ <div>
30
+ <label for="email" class="block text-sm text-gray-900 dark:text-gray-100 font-medium leading-6">
31
+ Email address
32
+ </label>
33
+ <div class="mt-2">
34
+ <input
35
+ id="email"
36
+ name="email"
37
+ type="email"
38
+ autocomplete="email"
39
+ required
40
+ class="block w-full border-0 rounded-md py-1.5 text-gray-900 dark:text-gray-100 shadow-sm ring-1 ring-gray-300 dark:ring-gray-600 ring-inset sm:text-sm placeholder:text-gray-400 dark:placeholder:text-gray-500 sm:leading-6 focus:ring-2 focus:ring-indigo-600 focus:ring-inset dark:bg-blue-gray-700"
41
+ />
42
+ </div>
43
+ </div>
44
+
45
+ <div>
46
+ <label for="password" class="block text-sm text-gray-900 dark:text-gray-100 font-medium leading-6">
47
+ Password
48
+ </label>
49
+ <div class="mt-2">
50
+ <input
51
+ id="password"
52
+ name="password"
53
+ type="password"
54
+ autocomplete="current-password"
55
+ required
56
+ class="block w-full border-0 rounded-md py-1.5 text-gray-900 dark:text-gray-100 shadow-sm ring-1 ring-gray-300 dark:ring-gray-600 ring-inset sm:text-sm placeholder:text-gray-400 dark:placeholder:text-gray-500 sm:leading-6 focus:ring-2 focus:ring-indigo-600 focus:ring-inset dark:bg-blue-gray-700"
57
+ />
58
+ </div>
59
+ </div>
60
+
61
+ <div class="flex items-center justify-between">
62
+ @if(showRememberMe)
63
+ <div class="flex items-center">
64
+ <input
65
+ id="remember-me"
66
+ name="remember-me"
67
+ type="checkbox"
68
+ class="h-4 w-4 border-gray-300 dark:border-gray-600 rounded text-indigo-600 focus:ring-indigo-600 dark:bg-blue-gray-700"
69
+ />
70
+ <label for="remember-me" class="ml-3 block text-sm text-gray-900 dark:text-gray-100 leading-6">
71
+ Remember me
72
+ </label>
73
+ </div>
74
+ @endif
75
+
76
+ @if(showForgotPassword)
77
+ <div class="text-sm leading-6">
78
+ <a href="#" class="text-indigo-600 dark:text-indigo-400 font-semibold hover:text-indigo-500 dark:hover:text-indigo-300">
79
+ Forgot password?
80
+ </a>
81
+ </div>
82
+ @endif
83
+ </div>
84
+
85
+ <div>
86
+ <button
87
+ type="submit"
88
+ class="w-full flex justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm text-white font-semibold leading-6 shadow-sm hover:bg-indigo-500 focus-visible:outline-2 focus-visible:outline-indigo-600 focus-visible:outline-offset-2 focus-visible:outline"
89
+ >
90
+ Sign in
91
+ </button>
92
+ </div>
93
+ </div>
94
+
95
+ @if(showSocialLogin)
96
+ <div>
97
+ <div class="relative mt-10">
98
+ <div class="absolute inset-0 flex items-center" aria-hidden="true">
99
+ <div class="w-full border-t border-gray-200 dark:border-gray-600"></div>
100
+ </div>
101
+ <div class="relative flex justify-center text-sm font-medium leading-6">
102
+ <span class="bg-white dark:bg-blue-gray-800 px-6 text-gray-900 dark:text-gray-100">
103
+ Or continue with
104
+ </span>
105
+ </div>
106
+ </div>
107
+
108
+ <div class="grid grid-cols-2 mt-6 gap-4">
109
+ <a
110
+ href="#"
111
+ class="w-full flex items-center justify-center gap-3 rounded-md bg-white dark:bg-blue-gray-700 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 font-semibold shadow-sm ring-1 ring-gray-300 dark:ring-gray-600 ring-inset hover:bg-gray-50 dark:hover:bg-blue-gray-600 focus-visible:ring-transparent"
112
+ >
113
+ <svg class="h-5 w-5" viewBox="0 0 24 24" aria-hidden="true">
114
+ <path d="M12.0003 4.75C13.7703 4.75 15.3553 5.36002 16.6053 6.54998L20.0303 3.125C17.9502 1.19 15.2353 0 12.0003 0C7.31028 0 3.25527 2.69 1.28027 6.60998L5.27028 9.70498C6.21525 6.86002 8.87028 4.75 12.0003 4.75Z" fill="#EA4335" />
115
+ <path d="M23.49 12.275C23.49 11.49 23.415 10.73 23.3 10H12V14.51H18.47C18.18 15.99 17.34 17.25 16.08 18.1L19.945 21.1C22.2 19.01 23.49 15.92 23.49 12.275Z" fill="#4285F4" />
116
+ <path d="M5.26498 14.2949C5.02498 13.5699 4.88501 12.7999 4.88501 11.9999C4.88501 11.1999 5.01998 10.4299 5.26498 9.7049L1.275 6.60986C0.46 8.22986 0 10.0599 0 11.9999C0 13.9399 0.46 15.7699 1.28 17.3899L5.26498 14.2949Z" fill="#FBBC05" />
117
+ <path d="M12.0004 24.0001C15.2404 24.0001 17.9654 22.935 19.9454 21.095L16.0804 18.095C15.0054 18.82 13.6204 19.245 12.0004 19.245C8.8704 19.245 6.21537 17.135 5.2654 14.29L1.27539 17.385C3.25539 21.31 7.3104 24.0001 12.0004 24.0001Z" fill="#34A853" />
118
+ </svg>
119
+ <span class="text-sm font-semibold leading-6">Google</span>
120
+ </a>
121
+
122
+ <a
123
+ href="#"
124
+ class="w-full flex items-center justify-center gap-3 rounded-md bg-white dark:bg-blue-gray-700 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 font-semibold shadow-sm ring-1 ring-gray-300 dark:ring-gray-600 ring-inset hover:bg-gray-50 dark:hover:bg-blue-gray-600 focus-visible:ring-transparent"
125
+ >
126
+ <svg class="h-5 w-5 fill-[#24292F]" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
127
+ <path fill-rule="evenodd" d="M10 0C4.477 0 0 4.484 0 10.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0110 4.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.203 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.942.359.31.678.921.678 1.856 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0020 10.017C20 4.484 15.522 0 10 0z" clip-rule="evenodd" />
128
+ </svg>
129
+ <span class="text-sm font-semibold leading-6">GitHub</span>
130
+ </a>
131
+ </div>
132
+ </div>
133
+ @endif
134
+ </div>
135
+
136
+ @if(showSignup)
137
+ <p class="mt-10 text-center text-sm text-gray-500 dark:text-gray-400">
138
+ Not a member?
139
+ <a href="#" class="text-indigo-600 dark:text-indigo-400 font-semibold leading-6 hover:text-indigo-500 dark:hover:text-indigo-300">
140
+ {{ signupText }}
141
+ </a>
142
+ </p>
143
+ @endif
144
+ </div>
145
+ </form>
146
+ </div>
147
+
148
+ <script>
149
+ function handleSubmit(event) {
150
+ const formData = new FormData(event.target)
151
+ const email = formData.get('email')
152
+ const password = formData.get('password')
153
+
154
+ if (onSubmit) {
155
+ onSubmit({ email, password })
156
+ }
157
+
158
+ // Clear password after submission
159
+ setTimeout(() => {
160
+ event.target.querySelector('#password').value = ''
161
+ }, 1000)
162
+ }
163
+ </script>
@@ -0,0 +1,145 @@
1
+ # Auth Components
2
+
3
+ Authentication UI components including Login, Signup, and Two-Factor Authentication.
4
+
5
+ ## Components
6
+
7
+ ### Login
8
+ A full-featured login form with email/password and optional social login.
9
+
10
+ ### Signup
11
+ A registration form with name, email, email confirmation, and password fields.
12
+
13
+ ### TwoFactorChallenge
14
+ A 2FA code entry component with auto-focus and recovery code option.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ bun add @stacksjs/components
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### Login Component
25
+
26
+ ```stx
27
+ <script>
28
+ export const handleLogin = (data) => {
29
+ console.log('Login:', data.email, data.password)
30
+ }
31
+ </script>
32
+
33
+ @component('Login', {
34
+ showLogo: true,
35
+ headingText: 'Sign in to your account',
36
+ showSocialLogin: true,
37
+ showRememberMe: true,
38
+ showForgotPassword: true,
39
+ showSignup: true,
40
+ signupText: 'Start a 14 day free trial',
41
+ onSubmit: handleLogin
42
+ })
43
+ @endcomponent
44
+ ```
45
+
46
+ ### Signup Component
47
+
48
+ ```stx
49
+ <script>
50
+ export const handleSignup = (data) => {
51
+ console.log('Signup:', data.name, data.email, data.password)
52
+ }
53
+ </script>
54
+
55
+ @component('Signup', {
56
+ headingText: 'Sign up for Stacks',
57
+ showSocialSignup: true,
58
+ onSubmit: handleSignup
59
+ })
60
+ @endcomponent
61
+ ```
62
+
63
+ ### TwoFactorChallenge Component
64
+
65
+ ```stx
66
+ <script>
67
+ export const handleVerify = (code) => {
68
+ console.log('2FA Code:', code)
69
+ }
70
+
71
+ export const handleRecoveryCode = () => {
72
+ console.log('Using recovery code')
73
+ }
74
+ </script>
75
+
76
+ @component('TwoFactorChallenge', {
77
+ headingText: 'Two Factor Authentication',
78
+ labelText: 'Authentication code',
79
+ instructionText: 'Open your 2FA app to view your authentication code.',
80
+ codeLength: 6,
81
+ onSubmit: handleVerify,
82
+ onUseRecoveryCode: handleRecoveryCode
83
+ })
84
+ @endcomponent
85
+ ```
86
+
87
+ ## Props
88
+
89
+ ### Login Props
90
+
91
+ | Prop | Type | Default | Description |
92
+ |------|------|---------|-------------|
93
+ | `showLogo` | `boolean` | `true` | Show logo at top |
94
+ | `headingText` | `string` | `'Sign in to your account'` | Heading text |
95
+ | `showSocialLogin` | `boolean` | `true` | Show social login buttons |
96
+ | `showRememberMe` | `boolean` | `true` | Show remember me checkbox |
97
+ | `showForgotPassword` | `boolean` | `true` | Show forgot password link |
98
+ | `showSignup` | `boolean` | `true` | Show signup link at bottom |
99
+ | `signupText` | `string` | `'Start a 14 day free trial'` | Signup link text |
100
+ | `onSubmit` | `function` | - | Callback when form submitted |
101
+ | `className` | `string` | `''` | Additional CSS classes |
102
+
103
+ ### Signup Props
104
+
105
+ | Prop | Type | Default | Description |
106
+ |------|------|---------|-------------|
107
+ | `headingText` | `string` | `'Sign up for Stacks'` | Heading text |
108
+ | `showSocialSignup` | `boolean` | `true` | Show social signup buttons |
109
+ | `onSubmit` | `function` | - | Callback when form submitted |
110
+ | `className` | `string` | `''` | Additional CSS classes |
111
+
112
+ ### TwoFactorChallenge Props
113
+
114
+ | Prop | Type | Default | Description |
115
+ |------|------|---------|-------------|
116
+ | `headingText` | `string` | `'Two Factor Authentication'` | Heading text |
117
+ | `labelText` | `string` | `'Authentication code'` | Input label |
118
+ | `instructionText` | `string` | `'Open your Symantec 2FA app...'` | Instructions |
119
+ | `codeLength` | `number` | `6` | Number of code digits |
120
+ | `onSubmit` | `function` | - | Callback with complete code |
121
+ | `onUseRecoveryCode` | `function` | - | Callback for recovery code |
122
+ | `className` | `string` | `''` | Additional CSS classes |
123
+
124
+ ## Features
125
+
126
+ - **Modern ES module exports** - Clean, modern JavaScript
127
+ - **Dark mode support** - All components support dark mode
128
+ - **Fully accessible** - Proper labels, ARIA attributes
129
+ - **Social login** - Google and GitHub integration
130
+ - **Auto-focus** - 2FA inputs auto-focus next field
131
+ - **Validation** - Email confirmation validation in Signup
132
+ - **TypeScript** - Full type definitions
133
+ - **Headwind styling** - Utility-first CSS classes
134
+
135
+ ## Accessibility
136
+
137
+ - Proper label associations
138
+ - ARIA attributes where needed
139
+ - Keyboard navigation support
140
+ - Focus management in 2FA component
141
+ - Required field indicators
142
+
143
+ ## Customization
144
+
145
+ All components accept a `className` prop for additional styling and support full customization through props.
@@ -0,0 +1,161 @@
1
+ <script>
2
+ export const headingText = $props.headingText || 'Sign up for Stacks'
3
+ export const showSocialSignup = $props.showSocialSignup ?? true
4
+ export const onSubmit = $props.onSubmit || (() => {})
5
+ export const className = $props.className || ''
6
+
7
+ export const containerClasses = `min-h-full flex flex-col justify-center py-12 lg:px-8 sm:px-6 dark:bg-blue-gray-900 ${className}`.trim()
8
+ </script>
9
+
10
+ <div class="{{ containerClasses }}">
11
+ <div class="sm:mx-auto sm:max-w-md sm:w-full">
12
+ <h2 class="mt-6 text-center text-2xl text-gray-900 dark:text-gray-100 font-bold leading-9 tracking-tight">
13
+ {{ headingText }}
14
+ </h2>
15
+ </div>
16
+
17
+ <div class="mt-10 sm:mx-auto sm:max-w-[480px] sm:w-full">
18
+ <div class="bg-white dark:bg-blue-gray-800 px-6 py-12 shadow sm:rounded-lg sm:px-12">
19
+ <form class="space-y-6" onsubmit="event.preventDefault(); handleSubmit(event)">
20
+ <div>
21
+ <label for="name" class="block text-sm text-gray-900 dark:text-gray-100 font-medium leading-6">
22
+ Full Name
23
+ </label>
24
+ <div class="mt-2">
25
+ <input
26
+ id="name"
27
+ name="name"
28
+ type="text"
29
+ autocomplete="name"
30
+ required
31
+ class="block w-full border-0 rounded-md p-2 py-1.5 text-gray-900 dark:text-gray-100 shadow-sm ring-1 ring-gray-300 dark:ring-gray-600 ring-inset sm:text-sm placeholder:text-gray-400 dark:placeholder:text-gray-500 sm:leading-6 focus:ring-2 focus:ring-teal-600 focus:ring-inset dark:bg-blue-gray-700"
32
+ />
33
+ </div>
34
+ </div>
35
+
36
+ <div>
37
+ <label for="email" class="block text-sm text-gray-900 dark:text-gray-100 font-medium leading-6">
38
+ Email address
39
+ </label>
40
+ <div class="mt-2">
41
+ <input
42
+ id="email"
43
+ name="email"
44
+ type="email"
45
+ autocomplete="email"
46
+ required
47
+ class="block w-full border-0 rounded-md p-2 py-1.5 text-gray-900 dark:text-gray-100 shadow-sm ring-1 ring-gray-300 dark:ring-gray-600 ring-inset sm:text-sm placeholder:text-gray-400 dark:placeholder:text-gray-500 sm:leading-6 focus:ring-2 focus:ring-teal-600 focus:ring-inset dark:bg-blue-gray-700"
48
+ />
49
+ </div>
50
+ </div>
51
+
52
+ <div>
53
+ <label for="confirm_email" class="block text-sm text-gray-900 dark:text-gray-100 font-medium leading-6">
54
+ Confirm Email
55
+ </label>
56
+ <div class="mt-2">
57
+ <input
58
+ id="confirm_email"
59
+ name="confirm_email"
60
+ type="email"
61
+ autocomplete="email"
62
+ required
63
+ class="block w-full border-0 rounded-md p-2 py-1.5 text-gray-900 dark:text-gray-100 shadow-sm ring-1 ring-gray-300 dark:ring-gray-600 ring-inset sm:text-sm placeholder:text-gray-400 dark:placeholder:text-gray-500 sm:leading-6 focus:ring-2 focus:ring-teal-600 focus:ring-inset dark:bg-blue-gray-700"
64
+ />
65
+ </div>
66
+ </div>
67
+
68
+ <div>
69
+ <label for="password" class="block text-sm text-gray-900 dark:text-gray-100 font-medium leading-6">
70
+ Password
71
+ </label>
72
+ <div class="mt-2">
73
+ <input
74
+ id="password"
75
+ name="password"
76
+ type="password"
77
+ autocomplete="new-password"
78
+ required
79
+ class="block w-full border-0 rounded-md p-2 py-1.5 text-gray-900 dark:text-gray-100 shadow-sm ring-1 ring-gray-300 dark:ring-gray-600 ring-inset sm:text-sm placeholder:text-gray-400 dark:placeholder:text-gray-500 sm:leading-6 focus:ring-2 focus:ring-teal-600 focus:ring-inset dark:bg-blue-gray-700"
80
+ />
81
+ </div>
82
+ </div>
83
+
84
+ <div>
85
+ <button
86
+ type="submit"
87
+ class="w-full flex justify-center rounded-md bg-teal-600 px-3 py-1.5 text-sm text-white font-semibold leading-6 shadow-sm hover:bg-teal-500 focus-visible:outline-2 focus-visible:outline-teal-600 focus-visible:outline-offset-2 focus-visible:outline"
88
+ >
89
+ Sign up
90
+ </button>
91
+ </div>
92
+ </form>
93
+
94
+ @if(showSocialSignup)
95
+ <div>
96
+ <div class="relative mt-10">
97
+ <div class="absolute inset-0 flex items-center" aria-hidden="true">
98
+ <div class="w-full border-t border-gray-200 dark:border-gray-600"></div>
99
+ </div>
100
+ <div class="relative flex justify-center text-sm font-medium leading-6">
101
+ <span class="bg-white dark:bg-blue-gray-800 px-6 text-gray-900 dark:text-gray-100">
102
+ Or signup with
103
+ </span>
104
+ </div>
105
+ </div>
106
+
107
+ <div class="grid grid-cols-2 mt-6 gap-4">
108
+ <a
109
+ href="#"
110
+ class="w-full flex items-center justify-center gap-3 rounded-md bg-white dark:bg-blue-gray-700 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 font-semibold shadow-sm ring-1 ring-gray-300 dark:ring-gray-600 ring-inset hover:bg-gray-50 dark:hover:bg-blue-gray-600 focus-visible:ring-transparent"
111
+ >
112
+ <svg class="h-5 w-5" viewBox="0 0 24 24" aria-hidden="true">
113
+ <path d="M12.0003 4.75C13.7703 4.75 15.3553 5.36002 16.6053 6.54998L20.0303 3.125C17.9502 1.19 15.2353 0 12.0003 0C7.31028 0 3.25527 2.69 1.28027 6.60998L5.27028 9.70498C6.21525 6.86002 8.87028 4.75 12.0003 4.75Z" fill="#EA4335" />
114
+ <path d="M23.49 12.275C23.49 11.49 23.415 10.73 23.3 10H12V14.51H18.47C18.18 15.99 17.34 17.25 16.08 18.1L19.945 21.1C22.2 19.01 23.49 15.92 23.49 12.275Z" fill="#4285F4" />
115
+ <path d="M5.26498 14.2949C5.02498 13.5699 4.88501 12.7999 4.88501 11.9999C4.88501 11.1999 5.01998 10.4299 5.26498 9.7049L1.275 6.60986C0.46 8.22986 0 10.0599 0 11.9999C0 13.9399 0.46 15.7699 1.28 17.3899L5.26498 14.2949Z" fill="#FBBC05" />
116
+ <path d="M12.0004 24.0001C15.2404 24.0001 17.9654 22.935 19.9454 21.095L16.0804 18.095C15.0054 18.82 13.6204 19.245 12.0004 19.245C8.8704 19.245 6.21537 17.135 5.2654 14.29L1.27539 17.385C3.25539 21.31 7.3104 24.0001 12.0004 24.0001Z" fill="#34A853" />
117
+ </svg>
118
+ <span class="text-sm font-semibold leading-6">Google</span>
119
+ </a>
120
+
121
+ <a
122
+ href="#"
123
+ class="w-full flex items-center justify-center gap-3 rounded-md bg-white dark:bg-blue-gray-700 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 font-semibold shadow-sm ring-1 ring-gray-300 dark:ring-gray-600 ring-inset hover:bg-gray-50 dark:hover:bg-blue-gray-600 focus-visible:ring-transparent"
124
+ >
125
+ <svg class="h-5 w-5 fill-[#24292F] dark:fill-white" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
126
+ <path fill-rule="evenodd" d="M10 0C4.477 0 0 4.484 0 10.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0110 4.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.203 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.942.359.31.678.921.678 1.856 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0020 10.017C20 4.484 15.522 0 10 0z" clip-rule="evenodd" />
127
+ </svg>
128
+ <span class="text-sm font-semibold leading-6">GitHub</span>
129
+ </a>
130
+ </div>
131
+ </div>
132
+ @endif
133
+ </div>
134
+ </div>
135
+ </div>
136
+
137
+ <script>
138
+ function handleSubmit(event) {
139
+ const formData = new FormData(event.target)
140
+ const name = formData.get('name')
141
+ const email = formData.get('email')
142
+ const confirmEmail = formData.get('confirm_email')
143
+ const password = formData.get('password')
144
+
145
+ // Basic email confirmation validation
146
+ if (email !== confirmEmail) {
147
+ alert('Emails do not match!')
148
+ return
149
+ }
150
+
151
+ if (onSubmit) {
152
+ onSubmit({ name, email, password })
153
+ }
154
+
155
+ // Clear sensitive fields after submission
156
+ setTimeout(() => {
157
+ event.target.querySelector('#password').value = ''
158
+ event.target.querySelector('#confirm_email').value = ''
159
+ }, 1000)
160
+ }
161
+ </script>
@@ -0,0 +1,125 @@
1
+ <script>
2
+ export const headingText = $props.headingText || 'Two Factor Authentication'
3
+ export const labelText = $props.labelText || 'Authentication code'
4
+ export const instructionText = $props.instructionText || 'Open your Symantec 2FA app to view your authentication code.'
5
+ export const codeLength = $props.codeLength || 6
6
+ export const onSubmit = $props.onSubmit || (() => {})
7
+ export const onUseRecoveryCode = $props.onUseRecoveryCode
8
+ export const className = $props.className || ''
9
+
10
+ export const containerClasses = `py-32 dark:bg-blue-gray-900 ${className}`.trim()
11
+ export const codes = Array(codeLength).fill('')
12
+ </script>
13
+
14
+ <div class="{{ containerClasses }}">
15
+ <div class="mx-auto max-w-sm rounded-md px-8 py-4 shadow-md bg-white dark:bg-blue-gray-800">
16
+ <div class="card-body">
17
+ <div>
18
+ <h3 class="text-center text-sm text-gray-900 dark:text-gray-100 font-semibold">
19
+ {{ headingText }}
20
+ </h3>
21
+ <label for="code-0" class="mt-4 block text-sm text-gray-900 dark:text-gray-100 font-medium leading-6">
22
+ {{ labelText }}
23
+ </label>
24
+ <form onsubmit="event.preventDefault(); handleSubmit(event)">
25
+ <div class="mt-2 flex rounded-md shadow-sm space-x-2">
26
+ @for(i in Array(codeLength).keys())
27
+ <input
28
+ id="code-{{ i }}"
29
+ type="text"
30
+ maxlength="1"
31
+ name="code-{{ i }}"
32
+ placeholder="-"
33
+ class="block w-full border-0 rounded-md px-2 py-2 text-center text-gray-900 dark:text-gray-100 ring-1 ring-gray-300 dark:ring-gray-600 ring-inset sm:text-sm placeholder:text-gray-400 dark:placeholder:text-gray-500 sm:leading-6 focus:ring-2 focus:ring-indigo-600 focus:ring-inset dark:bg-blue-gray-700"
34
+ oninput="handleCodeInput(event, {{ i }})"
35
+ onkeydown="handleKeyDown(event, {{ i }})"
36
+ />
37
+ @endfor
38
+ </div>
39
+ <div class="py-4">
40
+ <button
41
+ type="submit"
42
+ class="relative h-8 w-full flex items-center justify-center rounded-full bg-teal-600 px-4 text-white font-semibold hover:bg-teal-500 focus:outline-none focus:ring-2 focus:ring-teal-600 focus:ring-offset-2"
43
+ >
44
+ Verify
45
+ </button>
46
+ </div>
47
+ </form>
48
+ <div>
49
+ <p class="text-xs text-gray-700 dark:text-gray-300 font-semibold">
50
+ {{ instructionText }}
51
+ </p>
52
+ </div>
53
+ @if(onUseRecoveryCode)
54
+ <div class="mt-4">
55
+ <p class="text-xs text-gray-700 dark:text-gray-300 font-semibold">
56
+ Having Problems?
57
+ </p>
58
+ <a
59
+ href="#"
60
+ onclick="event.preventDefault(); handleRecoveryCode()"
61
+ class="text-xs text-blue-500 hover:underline"
62
+ >
63
+ Use recovery code instead
64
+ </a>
65
+ </div>
66
+ @endif
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+
72
+ <script>
73
+ function handleCodeInput(event, index) {
74
+ const input = event.target
75
+ const value = input.value
76
+
77
+ // Only allow digits
78
+ if (!/^\d*$/.test(value)) {
79
+ input.value = ''
80
+ return
81
+ }
82
+
83
+ // Auto-focus next input
84
+ if (value.length === 1 && index < codeLength - 1) {
85
+ const nextInput = document.getElementById(`code-${index + 1}`)
86
+ if (nextInput) {
87
+ nextInput.focus()
88
+ }
89
+ }
90
+ }
91
+
92
+ function handleKeyDown(event, index) {
93
+ // Handle backspace to move to previous input
94
+ if (event.key === 'Backspace' && !event.target.value && index > 0) {
95
+ const prevInput = document.getElementById(`code-${index - 1}`)
96
+ if (prevInput) {
97
+ prevInput.focus()
98
+ }
99
+ }
100
+ }
101
+
102
+ function handleSubmit(event) {
103
+ const code = []
104
+ for (let i = 0; i < codeLength; i++) {
105
+ const input = document.getElementById(`code-${i}`)
106
+ if (input && input.value) {
107
+ code.push(input.value)
108
+ }
109
+ }
110
+
111
+ if (code.length === codeLength) {
112
+ if (onSubmit) {
113
+ onSubmit(code.join(''))
114
+ }
115
+ } else {
116
+ alert('Please enter all digits')
117
+ }
118
+ }
119
+
120
+ function handleRecoveryCode() {
121
+ if (onUseRecoveryCode) {
122
+ onUseRecoveryCode()
123
+ }
124
+ }
125
+ </script>
@@ -0,0 +1,32 @@
1
+ export { default as Login } from './Login.stx'
2
+ export { default as Signup } from './Signup.stx'
3
+ export { default as TwoFactorChallenge } from './TwoFactorChallenge.stx'
4
+
5
+ export interface LoginProps {
6
+ showLogo?: boolean
7
+ headingText?: string
8
+ showSocialLogin?: boolean
9
+ showRememberMe?: boolean
10
+ showForgotPassword?: boolean
11
+ showSignup?: boolean
12
+ signupText?: string
13
+ onSubmit?: (data: { email: string, password: string }) => void
14
+ className?: string
15
+ }
16
+
17
+ export interface SignupProps {
18
+ headingText?: string
19
+ showSocialSignup?: boolean
20
+ onSubmit?: (data: { name: string, email: string, password: string }) => void
21
+ className?: string
22
+ }
23
+
24
+ export interface TwoFactorChallengeProps {
25
+ headingText?: string
26
+ labelText?: string
27
+ instructionText?: string
28
+ codeLength?: number
29
+ onSubmit?: (code: string) => void
30
+ onUseRecoveryCode?: () => void
31
+ className?: string
32
+ }