@stacksjs/components 0.2.35 → 0.2.54

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 (237) hide show
  1. package/dist/Accordion-19x8q4rj.stx +88 -0
  2. package/dist/{Audio-vdmg1258.stx → Audio-hjywk3qc.stx} +4 -4
  3. package/dist/{Avatar-365cvrh4.stx → Avatar-h3jmwcq0.stx} +23 -34
  4. package/dist/{Badge-8m3qete4.stx → Badge-k8dy3d53.stx} +11 -19
  5. package/dist/{Button-3tt4vsqt.stx → Button-1vknasy3.stx} +18 -33
  6. package/dist/Calendar-reny0et7.stx +189 -0
  7. package/dist/{Card-6wdbexqr.stx → Card-c1g1fd1w.stx} +27 -33
  8. package/dist/{Checkbox-p3q3e3nf.stx → Checkbox-zgdmy3p5.stx} +36 -44
  9. package/dist/{Checkout-e4037tyy.stx → Checkout-agt0ngpw.stx} +8 -16
  10. package/dist/Combobox-fdkr6cdq.stx +96 -0
  11. package/dist/{ComboboxButton-8gxwkhdx.stx → ComboboxButton-g76stp1e.stx} +14 -2
  12. package/dist/{ComboboxInput-mk99z65n.stx → ComboboxInput-z1293bbh.stx} +12 -5
  13. package/dist/ComboboxOption-hmxt3gte.stx +49 -0
  14. package/dist/{ListboxOptions-8kqvfp7z.stx → ComboboxOptions-sq5360ae.stx} +2 -2
  15. package/dist/CommandPalette-36mxf05w.stx +81 -0
  16. package/dist/CommandPaletteItem-mvk6q7a2.stx +27 -0
  17. package/dist/Dialog-hczpexs4.stx +37 -0
  18. package/dist/{Drawer-p7m351bn.stx → Drawer-xhevp5cw.stx} +29 -49
  19. package/dist/Dropdown-vvhemtc0.stx +82 -0
  20. package/dist/{DropdownButton-9w8zkfwj.stx → DropdownButton-wcz67dxy.stx} +12 -6
  21. package/dist/DropdownItem-c7rz08e5.stx +35 -0
  22. package/dist/{DropdownItems-aygjpxkx.stx → DropdownItems-1sxnd5qn.stx} +2 -2
  23. package/dist/EmailInput-vak3kkmk.stx +30 -0
  24. package/dist/Form-mas630hy.stx +95 -0
  25. package/dist/{Image-jhkm6w1f.stx → Image-yhxcvgzs.stx} +68 -100
  26. package/dist/Listbox-q49tnr90.stx +101 -0
  27. package/dist/{ListboxButton-spwx7513.stx → ListboxButton-keb8gm0q.stx} +12 -1
  28. package/dist/ListboxOption-e0dtv1gc.stx +49 -0
  29. package/dist/{ComboboxOptions-6b5ywc5x.stx → ListboxOptions-ejkatcfg.stx} +7 -3
  30. package/dist/{Login-rx9gyqhx.stx → Login-gepemsth.stx} +17 -19
  31. package/dist/Navigator-rk2a5wvk.stx +113 -0
  32. package/dist/Notification-tb9b525s.stx +78 -0
  33. package/dist/{NumberInput-nzpkx1ry.stx → NumberInput-drnvtqej.stx} +40 -53
  34. package/dist/{Pagination-h61eq390.stx → Pagination-n3d31cns.stx} +73 -108
  35. package/dist/PasswordInput-n02x6053.stx +142 -0
  36. package/dist/{PaymentMethods-j42jsk5x.stx → PaymentMethods-x4vrx9yb.stx} +21 -30
  37. package/dist/Popover-et2q44m7.stx +60 -0
  38. package/dist/{PopoverButton-fnaqc77g.stx → PopoverButton-mffzc9pr.stx} +12 -6
  39. package/dist/{PopoverPanel-y0vtf5w9.stx → PopoverPanel-054yh38h.stx} +6 -6
  40. package/dist/{Portal-s2q37yp0.stx → Portal-1vf14s4x.stx} +20 -37
  41. package/dist/{Progress-vmdkm06p.stx → Progress-yxcntr4x.stx} +36 -48
  42. package/dist/{Radio-04bs3d21.stx → Radio-cgjv2hb2.stx} +29 -34
  43. package/dist/RadioGroup-qxmnm3xm.stx +60 -0
  44. package/dist/RadioGroupOption-s1d6r221.stx +58 -0
  45. package/dist/SearchInput-pdx7agn0.stx +44 -0
  46. package/dist/{Select-g4mmvfv8.stx → Select-28y0pg83.stx} +23 -31
  47. package/dist/Sidebar-73vc78as.stx +236 -0
  48. package/dist/SidebarFooter-zsfsgcxb.stx +109 -0
  49. package/dist/SidebarHeader-7v174a9c.stx +122 -0
  50. package/dist/SidebarItem-3y3e8g2t.stx +92 -0
  51. package/dist/SidebarSection-z5ey6awe.stx +113 -0
  52. package/dist/{Signup-c4dyv6f3.stx → Signup-hxm5hwkr.stx} +26 -28
  53. package/dist/{StepperStep-en4r00zv.stx → StepperStep-wxgembat.stx} +9 -5
  54. package/dist/{SubscriptionCheckout-gawjzf3q.stx → SubscriptionCheckout-zy2ycrmw.stx} +99 -129
  55. package/dist/Switch-r5t5w04w.stx +70 -0
  56. package/dist/{TableHeader-ydys9rzz.stx → TableHeader-0eyzbqpz.stx} +10 -3
  57. package/dist/Tabs-bzvvn4ke.stx +126 -0
  58. package/dist/Teleport-tsx4bt4n.stx +65 -0
  59. package/dist/{TextInput-k2kfkgy1.stx → TextInput-kf084763.stx} +42 -59
  60. package/dist/{Textarea-szad57dz.stx → Textarea-xg4rr2xz.stx} +42 -54
  61. package/dist/{Tooltip-em7t551n.stx → Tooltip-m0tqd72e.stx} +34 -40
  62. package/dist/Transition-7smenaz8.stx +102 -0
  63. package/dist/{TwoFactorChallenge-an9yvxvq.stx → TwoFactorChallenge-y5m3nk79.stx} +49 -63
  64. package/dist/{Video-crxva65p.stx → Video-84j4pv6h.stx} +4 -4
  65. package/dist/VirtualList-fa7v5a3h.stx +67 -0
  66. package/dist/VirtualTable-f9crbfjr.stx +103 -0
  67. package/dist/components/CodeBlock.stx +1 -6
  68. package/dist/index.js +13 -13
  69. package/dist/ui/accordion/Accordion.stx +1 -3
  70. package/dist/ui/auth/Login.stx +0 -1
  71. package/dist/ui/auth/Signup.stx +0 -1
  72. package/dist/ui/auth/TwoFactorChallenge.stx +1 -3
  73. package/dist/ui/avatar/Avatar.stx +0 -2
  74. package/dist/ui/badge/Badge.stx +0 -1
  75. package/dist/ui/button/Button.stx +1 -2
  76. package/dist/ui/calendar/Calendar.stx +12 -2
  77. package/dist/ui/card/Card.stx +0 -2
  78. package/dist/ui/checkbox/Checkbox.stx +2 -2
  79. package/dist/ui/combobox/Combobox.stx +2 -1
  80. package/dist/ui/combobox/ComboboxButton.stx +1 -0
  81. package/dist/ui/combobox/ComboboxInput.stx +1 -4
  82. package/dist/ui/combobox/ComboboxOption.stx +2 -9
  83. package/dist/ui/combobox/ComboboxOptions.stx +1 -1
  84. package/dist/ui/command-palette/CommandPalette.stx +3 -6
  85. package/dist/ui/command-palette/CommandPaletteItem.stx +2 -7
  86. package/dist/ui/dialog/Dialog.stx +1 -3
  87. package/dist/ui/drawer/Drawer.stx +1 -3
  88. package/dist/ui/dropdown/Dropdown.stx +2 -5
  89. package/dist/ui/dropdown/DropdownButton.stx +1 -3
  90. package/dist/ui/dropdown/DropdownItem.stx +2 -7
  91. package/dist/ui/dropdown/DropdownItems.stx +1 -1
  92. package/dist/ui/form/Form.stx +0 -5
  93. package/dist/ui/image/Image.stx +1 -6
  94. package/dist/ui/input/EmailInput.stx +1 -1
  95. package/dist/ui/input/NumberInput.stx +0 -1
  96. package/dist/ui/input/PasswordInput.stx +1 -4
  97. package/dist/ui/input/SearchInput.stx +2 -2
  98. package/dist/ui/input/TextInput.stx +1 -2
  99. package/dist/ui/listbox/Listbox.stx +2 -1
  100. package/dist/ui/listbox/ListboxButton.stx +1 -0
  101. package/dist/ui/listbox/ListboxOption.stx +2 -9
  102. package/dist/ui/listbox/ListboxOptions.stx +1 -1
  103. package/dist/ui/navigator/Navigator.stx +4 -4
  104. package/dist/ui/notification/Notification.stx +1 -5
  105. package/dist/ui/pagination/Pagination.stx +1 -2
  106. package/dist/ui/payment/Checkout.stx +0 -4
  107. package/dist/ui/payment/PaymentMethods.stx +15 -2
  108. package/dist/ui/payment/SubscriptionCheckout.stx +3 -184
  109. package/dist/ui/popover/Popover.stx +2 -5
  110. package/dist/ui/popover/PopoverButton.stx +1 -3
  111. package/dist/ui/popover/PopoverPanel.stx +1 -1
  112. package/dist/ui/portal/Portal.stx +1 -0
  113. package/dist/ui/progress/Progress.stx +6 -1
  114. package/dist/ui/radio/Radio.stx +1 -1
  115. package/dist/ui/radio-group/RadioGroup.stx +2 -1
  116. package/dist/ui/radio-group/RadioGroupOption.stx +2 -9
  117. package/dist/ui/select/Select.stx +1 -1
  118. package/dist/ui/sidebar/Sidebar.stx +9 -26
  119. package/dist/ui/sidebar/SidebarFooter.stx +6 -21
  120. package/dist/ui/sidebar/SidebarHeader.stx +7 -18
  121. package/dist/ui/sidebar/SidebarItem.stx +4 -20
  122. package/dist/ui/sidebar/SidebarSection.stx +5 -18
  123. package/dist/ui/sidebar/index.d.ts +13 -1
  124. package/dist/ui/stepper/StepperStep.stx +1 -5
  125. package/dist/ui/switch/Switch.stx +4 -10
  126. package/dist/ui/table/TableHeader.stx +1 -1
  127. package/dist/ui/tabs/Tabs.stx +0 -1
  128. package/dist/ui/teleport/Teleport.stx +1 -0
  129. package/dist/ui/textarea/Textarea.stx +1 -1
  130. package/dist/ui/tooltip/Tooltip.stx +1 -2
  131. package/dist/ui/transition/Transition.stx +9 -16
  132. package/dist/ui/virtual-list/VirtualList.stx +2 -4
  133. package/dist/ui/virtual-table/VirtualTable.stx +1 -1
  134. package/package.json +5 -5
  135. package/src/components/CodeBlock.stx +26 -29
  136. package/src/components/Footer.stx +1 -1
  137. package/src/components/Hero.stx +1 -1
  138. package/src/components/Installation.stx +5 -7
  139. package/src/ui/accordion/Accordion.stx +48 -68
  140. package/src/ui/audio/Audio.stx +4 -4
  141. package/src/ui/auth/Login.stx +17 -19
  142. package/src/ui/auth/Signup.stx +26 -28
  143. package/src/ui/auth/TwoFactorChallenge.stx +49 -63
  144. package/src/ui/avatar/Avatar.stx +23 -34
  145. package/src/ui/badge/Badge.stx +11 -19
  146. package/src/ui/button/Button.stx +18 -33
  147. package/src/ui/calendar/Calendar.stx +93 -136
  148. package/src/ui/card/Card.stx +27 -33
  149. package/src/ui/checkbox/Checkbox.stx +36 -44
  150. package/src/ui/combobox/Combobox.stx +87 -4
  151. package/src/ui/combobox/ComboboxButton.stx +14 -2
  152. package/src/ui/combobox/ComboboxInput.stx +12 -5
  153. package/src/ui/combobox/ComboboxOption.stx +29 -32
  154. package/src/ui/combobox/ComboboxOptions.stx +7 -3
  155. package/src/ui/command-palette/CommandPalette.stx +70 -28
  156. package/src/ui/command-palette/CommandPaletteItem.stx +15 -20
  157. package/src/ui/dialog/Dialog.stx +28 -10
  158. package/src/ui/drawer/Drawer.stx +29 -49
  159. package/src/ui/dropdown/Dropdown.stx +69 -11
  160. package/src/ui/dropdown/DropdownButton.stx +12 -6
  161. package/src/ui/dropdown/DropdownItem.stx +18 -26
  162. package/src/ui/dropdown/DropdownItems.stx +2 -2
  163. package/src/ui/form/Form.stx +50 -164
  164. package/src/ui/heatmap/Heatmap.stx +16 -37
  165. package/src/ui/image/Image.stx +68 -100
  166. package/src/ui/input/EmailInput.stx +16 -27
  167. package/src/ui/input/NumberInput.stx +40 -53
  168. package/src/ui/input/PasswordInput.stx +64 -77
  169. package/src/ui/input/SearchInput.stx +27 -46
  170. package/src/ui/input/TextInput.stx +42 -59
  171. package/src/ui/listbox/Listbox.stx +91 -4
  172. package/src/ui/listbox/ListboxButton.stx +12 -1
  173. package/src/ui/listbox/ListboxOption.stx +30 -38
  174. package/src/ui/listbox/ListboxOptions.stx +2 -2
  175. package/src/ui/navigator/Navigator.stx +52 -77
  176. package/src/ui/notification/Notification.stx +46 -39
  177. package/src/ui/pagination/Pagination.stx +73 -108
  178. package/src/ui/payment/Checkout.stx +8 -16
  179. package/src/ui/payment/PaymentMethods.stx +21 -30
  180. package/src/ui/payment/SubscriptionCheckout.stx +99 -129
  181. package/src/ui/popover/Popover.stx +50 -14
  182. package/src/ui/popover/PopoverButton.stx +12 -6
  183. package/src/ui/popover/PopoverPanel.stx +6 -6
  184. package/src/ui/portal/Portal.stx +20 -37
  185. package/src/ui/progress/Progress.stx +36 -48
  186. package/src/ui/radio/Radio.stx +29 -34
  187. package/src/ui/radio-group/RadioGroup.stx +51 -4
  188. package/src/ui/radio-group/RadioGroupOption.stx +37 -30
  189. package/src/ui/select/Select.stx +23 -31
  190. package/src/ui/sidebar/Sidebar.stx +136 -188
  191. package/src/ui/sidebar/SidebarFooter.stx +49 -35
  192. package/src/ui/sidebar/SidebarHeader.stx +52 -30
  193. package/src/ui/sidebar/SidebarItem.stx +52 -78
  194. package/src/ui/sidebar/SidebarSection.stx +56 -51
  195. package/src/ui/sidebar/index.ts +21 -1
  196. package/src/ui/stepper/StepperStep.stx +9 -5
  197. package/src/ui/switch/Switch.stx +38 -42
  198. package/src/ui/table/TableHeader.stx +10 -3
  199. package/src/ui/tabs/Tabs.stx +68 -87
  200. package/src/ui/teleport/Teleport.stx +23 -59
  201. package/src/ui/textarea/Textarea.stx +42 -54
  202. package/src/ui/tooltip/Tooltip.stx +34 -40
  203. package/src/ui/transition/Transition.stx +76 -148
  204. package/src/ui/video/Video.stx +4 -4
  205. package/src/ui/virtual-list/VirtualList.stx +45 -61
  206. package/src/ui/virtual-table/VirtualTable.stx +62 -72
  207. package/dist/Accordion-sbfra0mz.stx +0 -108
  208. package/dist/Calendar-58nnkdh8.stx +0 -232
  209. package/dist/Combobox-th5ebw62.stx +0 -13
  210. package/dist/ComboboxOption-an9zyan6.stx +0 -52
  211. package/dist/CommandPalette-kwmdf2np.stx +0 -39
  212. package/dist/CommandPaletteItem-zx2fbpts.stx +0 -32
  213. package/dist/Dialog-rgmj8q2j.stx +0 -19
  214. package/dist/Dropdown-jz4jy0n3.stx +0 -24
  215. package/dist/DropdownItem-wagdspb8.stx +0 -43
  216. package/dist/EmailInput-ajsp9vw6.stx +0 -41
  217. package/dist/Form-1mdmx9b6.stx +0 -209
  218. package/dist/Listbox-pqqh72g8.stx +0 -14
  219. package/dist/ListboxOption-7nxzaexq.stx +0 -57
  220. package/dist/Navigator-aay8bjab.stx +0 -138
  221. package/dist/Notification-gk33gv7b.stx +0 -71
  222. package/dist/PasswordInput-zv4hm43j.stx +0 -155
  223. package/dist/Popover-469gbt6r.stx +0 -24
  224. package/dist/RadioGroup-bvb1mrhd.stx +0 -13
  225. package/dist/RadioGroupOption-cpdwgnhw.stx +0 -51
  226. package/dist/SearchInput-2521m475.stx +0 -63
  227. package/dist/Sidebar-40ckbzh8.stx +0 -288
  228. package/dist/SidebarFooter-hnth56mg.stx +0 -95
  229. package/dist/SidebarHeader-3wrte2p2.stx +0 -100
  230. package/dist/SidebarItem-zqtvvzpc.stx +0 -118
  231. package/dist/SidebarSection-3etwnbjr.stx +0 -108
  232. package/dist/Switch-036j9vm1.stx +0 -74
  233. package/dist/Tabs-svh721m0.stx +0 -145
  234. package/dist/Teleport-qfch3w0b.stx +0 -101
  235. package/dist/Transition-0hmr6zxb.stx +0 -174
  236. package/dist/VirtualList-cndhjejh.stx +0 -83
  237. package/dist/VirtualTable-ng0b48yp.stx +0 -113
@@ -3,60 +3,70 @@ export const items = $props.items || []
3
3
  export const allowMultiple = $props.allowMultiple || false
4
4
  export const defaultOpen = $props.defaultOpen || []
5
5
  export const className = $props.className || ''
6
- export const onChange = $props.onChange
7
6
 
8
- // Track which items are open
9
- let openItems = defaultOpen
7
+ export const accordionClasses = `divide-y divide-gray-200 dark:divide-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg ${className}`.trim()
8
+ export const headerClasses = `w-full flex items-center justify-between px-4 py-3 text-left font-medium text-gray-900 dark:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900`.trim()
9
+ export const iconClasses = `w-5 h-5 text-gray-500 dark:text-gray-400 transition-transform duration-200`.trim()
10
+ export const contentClasses = `px-4 py-3 text-gray-700 dark:text-gray-300`.trim()
11
+ </script>
12
+
13
+ <script client>
14
+ const emit = defineEmits()
15
+ const allowMultiple = {{ allowMultiple }}
16
+ const openItems = state({{ defaultOpen }})
10
17
 
11
- // Toggle an item
12
18
  function toggleItem(index) {
19
+ const cur = openItems()
13
20
  if (allowMultiple) {
14
- // Multiple items can be open
15
- if (openItems.includes(index)) {
16
- openItems = openItems.filter(i => i !== index)
17
- } else {
18
- openItems = [...openItems, index]
19
- }
20
- } else {
21
- // Only one item can be open
22
- if (openItems.includes(index)) {
23
- openItems = []
24
- } else {
25
- openItems = [index]
26
- }
21
+ openItems.set(cur.includes(index) ? cur.filter(i => i !== index) : [...cur, index])
27
22
  }
28
-
29
- if (onChange) {
30
- onChange(openItems)
23
+ else {
24
+ openItems.set(cur.includes(index) ? [] : [index])
31
25
  }
26
+ emit('change', openItems())
32
27
  }
33
28
 
34
- // Check if item is open
35
29
  function isOpen(index) {
36
- return openItems.includes(index)
30
+ return openItems().includes(index)
37
31
  }
38
32
 
39
- export const accordionClasses = `divide-y divide-gray-200 dark:divide-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg ${className}`.trim()
40
- export const itemClasses = ``.trim()
41
- export const headerClasses = `w-full flex items-center justify-between px-4 py-3 text-left font-medium text-gray-900 dark:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900`.trim()
42
- export const iconClasses = `w-5 h-5 text-gray-500 dark:text-gray-400 transition-transform duration-200`.trim()
43
- export const contentClasses = `px-4 py-3 text-gray-700 dark:text-gray-300`.trim()
33
+ function onHeaderKey(event, index) {
34
+ const buttons = event.currentTarget.closest('[role="region"]').querySelectorAll('button[data-accordion-header]')
35
+ if (event.key === 'ArrowDown') {
36
+ event.preventDefault()
37
+ buttons[index + 1]?.focus()
38
+ }
39
+ else if (event.key === 'ArrowUp') {
40
+ event.preventDefault()
41
+ buttons[index - 1]?.focus()
42
+ }
43
+ else if (event.key === 'Home') {
44
+ event.preventDefault()
45
+ buttons[0]?.focus()
46
+ }
47
+ else if (event.key === 'End') {
48
+ event.preventDefault()
49
+ buttons[buttons.length - 1]?.focus()
50
+ }
51
+ }
44
52
  </script>
45
53
 
46
54
  <div class="{{ accordionClasses }}" role="region">
47
55
  @foreach(item in items)
48
- <div class="{{ itemClasses }}">
56
+ <div>
49
57
  <button
50
58
  type="button"
59
+ data-accordion-header
51
60
  class="{{ headerClasses }}"
52
61
  @click="toggleItem({{ $loop.index }})"
53
- aria-expanded="{{ isOpen($loop.index) ? 'true' : 'false' }}"
62
+ @keydown="onHeaderKey($event, {{ $loop.index }})"
63
+ :aria-expanded="isOpen({{ $loop.index }}) ? 'true' : 'false'"
54
64
  aria-controls="accordion-content-{{ $loop.index }}"
55
65
  >
56
66
  <span>{{ item.title }}</span>
57
67
  <svg
58
68
  class="{{ iconClasses }}"
59
- style="transform: rotate({{ isOpen($loop.index) ? '180' : '0' }}deg)"
69
+ :style="`transform: rotate(${isOpen({{ $loop.index }}) ? 180 : 0}deg)`"
60
70
  fill="none"
61
71
  viewBox="0 0 24 24"
62
72
  stroke="currentColor"
@@ -65,44 +75,14 @@ export const contentClasses = `px-4 py-3 text-gray-700 dark:text-gray-300`.trim(
65
75
  </svg>
66
76
  </button>
67
77
 
68
- @if(isOpen($loop.index))
69
- <div
70
- id="accordion-content-{{ $loop.index }}"
71
- class="{{ contentClasses }}"
72
- role="region"
73
- >
74
- {!! item.content !!}
75
- </div>
76
- @endif
78
+ <div
79
+ :show="isOpen({{ $loop.index }})"
80
+ id="accordion-content-{{ $loop.index }}"
81
+ class="{{ contentClasses }}"
82
+ role="region"
83
+ >
84
+ {!! item.content !!}
85
+ </div>
77
86
  </div>
78
87
  @endforeach
79
88
  </div>
80
-
81
- <script>
82
- // Keyboard navigation
83
- if (typeof window !== 'undefined') {
84
- window.addEventListener('DOMContentLoaded', () => {
85
- const buttons = document.querySelectorAll('[aria-expanded]')
86
-
87
- buttons.forEach((button, index) => {
88
- button.addEventListener('keydown', (event) => {
89
- if (event.key === 'ArrowDown') {
90
- event.preventDefault()
91
- const nextButton = buttons[index + 1]
92
- if (nextButton) nextButton.focus()
93
- } else if (event.key === 'ArrowUp') {
94
- event.preventDefault()
95
- const prevButton = buttons[index - 1]
96
- if (prevButton) prevButton.focus()
97
- } else if (event.key === 'Home') {
98
- event.preventDefault()
99
- buttons[0].focus()
100
- } else if (event.key === 'End') {
101
- event.preventDefault()
102
- buttons[buttons.length - 1].focus()
103
- }
104
- })
105
- })
106
- })
107
- }
108
- </script>
@@ -25,10 +25,10 @@ export const audioClasses = 'w-full rounded-md bg-gray-100 dark:bg-gray-800'
25
25
  <audio
26
26
  class="{{ audioClasses }}"
27
27
  src="{{ src }}"
28
- controls="{{ controls }}"
29
- autoplay="{{ autoplay }}"
30
- loop="{{ loop }}"
31
- muted="{{ muted }}"
28
+ @if(controls)controls@endif
29
+ @if(autoplay)autoplay@endif
30
+ @if(loop)loop@endif
31
+ @if(muted)muted@endif
32
32
  preload="{{ preload }}"
33
33
  >
34
34
  Your browser does not support the audio element.
@@ -6,12 +6,27 @@ export const showRememberMe = $props.showRememberMe ?? true
6
6
  export const showForgotPassword = $props.showForgotPassword ?? true
7
7
  export const showSignup = $props.showSignup ?? true
8
8
  export const signupText = $props.signupText || 'Start a 14 day free trial'
9
- export const onSubmit = $props.onSubmit || (() => {})
10
9
  export const className = $props.className || ''
11
10
 
12
11
  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
12
  </script>
14
13
 
14
+ <script client>
15
+ const emit = defineEmits()
16
+
17
+ function onSubmit(event) {
18
+ event.preventDefault()
19
+ const formData = new FormData(event.target)
20
+ const email = formData.get('email')
21
+ const password = formData.get('password')
22
+ emit('submit', { email, password })
23
+ setTimeout(() => {
24
+ const pw = event.target.querySelector('#password')
25
+ if (pw) pw.value = ''
26
+ }, 1000)
27
+ }
28
+ </script>
29
+
15
30
  <div class="{{ containerClasses }}">
16
31
  <div class="sm:mx-auto sm:max-w-md sm:w-full">
17
32
  @if(showLogo)
@@ -22,7 +37,7 @@ export const containerClasses = `min-h-full flex flex-col justify-center py-12 l
22
37
  </h2>
23
38
  </div>
24
39
 
25
- <form onsubmit="event.preventDefault(); handleSubmit(event)">
40
+ <form @submit="onSubmit($event)">
26
41
  <div class="mt-10 sm:mx-auto sm:max-w-[480px] sm:w-full">
27
42
  <div class="bg-white dark:bg-blue-gray-800 px-6 py-12 shadow sm:rounded-lg sm:px-12">
28
43
  <div class="space-y-6">
@@ -144,20 +159,3 @@ export const containerClasses = `min-h-full flex flex-col justify-center py-12 l
144
159
  </div>
145
160
  </form>
146
161
  </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>
@@ -1,12 +1,36 @@
1
1
  <script server>
2
2
  export const headingText = $props.headingText || 'Sign up for Stacks'
3
3
  export const showSocialSignup = $props.showSocialSignup ?? true
4
- export const onSubmit = $props.onSubmit || (() => {})
5
4
  export const className = $props.className || ''
6
5
 
7
6
  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
7
  </script>
9
8
 
9
+ <script client>
10
+ const emit = defineEmits()
11
+
12
+ function onSubmit(event) {
13
+ event.preventDefault()
14
+ const formData = new FormData(event.target)
15
+ const name = formData.get('name')
16
+ const email = formData.get('email')
17
+ const confirmEmail = formData.get('confirm_email')
18
+ const password = formData.get('password')
19
+
20
+ if (email !== confirmEmail) {
21
+ emit('error', { message: 'Emails do not match' })
22
+ return
23
+ }
24
+
25
+ emit('submit', { name, email, password })
26
+
27
+ setTimeout(() => {
28
+ event.target.querySelector('#password')?.setAttribute('value', '')
29
+ event.target.querySelector('#confirm_email')?.setAttribute('value', '')
30
+ }, 1000)
31
+ }
32
+ </script>
33
+
10
34
  <div class="{{ containerClasses }}">
11
35
  <div class="sm:mx-auto sm:max-w-md sm:w-full">
12
36
  <h2 class="mt-6 text-center text-2xl text-gray-900 dark:text-gray-100 font-bold leading-9 tracking-tight">
@@ -16,7 +40,7 @@ export const containerClasses = `min-h-full flex flex-col justify-center py-12 l
16
40
 
17
41
  <div class="mt-10 sm:mx-auto sm:max-w-[480px] sm:w-full">
18
42
  <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)">
43
+ <form class="space-y-6" @submit="onSubmit($event)">
20
44
  <div>
21
45
  <label for="name" class="block text-sm text-gray-900 dark:text-gray-100 font-medium leading-6">
22
46
  Full Name
@@ -133,29 +157,3 @@ export const containerClasses = `min-h-full flex flex-col justify-center py-12 l
133
157
  </div>
134
158
  </div>
135
159
  </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>
@@ -3,12 +3,53 @@ export const headingText = $props.headingText || 'Two Factor Authentication'
3
3
  export const labelText = $props.labelText || 'Authentication code'
4
4
  export const instructionText = $props.instructionText || 'Open your Symantec 2FA app to view your authentication code.'
5
5
  export const codeLength = $props.codeLength || 6
6
- export const onSubmit = $props.onSubmit || (() => {})
7
- export const onUseRecoveryCode = $props.onUseRecoveryCode
6
+ export const showRecoveryCode = $props.showRecoveryCode ?? true
8
7
  export const className = $props.className || ''
9
8
 
10
9
  export const containerClasses = `py-32 dark:bg-blue-gray-900 ${className}`.trim()
11
- export const codes = Array(codeLength).fill('')
10
+ </script>
11
+
12
+ <script client>
13
+ const emit = defineEmits()
14
+ const codeLength = {{ codeLength }}
15
+
16
+ function onCodeInput(event, index) {
17
+ const input = event.target
18
+ const value = input.value
19
+ if (!/^\d*$/.test(value)) {
20
+ input.value = ''
21
+ return
22
+ }
23
+ if (value.length === 1 && index < codeLength - 1) {
24
+ document.getElementById(`code-${index + 1}`)?.focus()
25
+ }
26
+ }
27
+
28
+ function onKeyDown(event, index) {
29
+ if (event.key === 'Backspace' && !event.target.value && index > 0) {
30
+ document.getElementById(`code-${index - 1}`)?.focus()
31
+ }
32
+ }
33
+
34
+ function onSubmit(event) {
35
+ event.preventDefault()
36
+ const code = []
37
+ for (let i = 0; i < codeLength; i++) {
38
+ const input = document.getElementById(`code-${i}`)
39
+ if (input?.value) code.push(input.value)
40
+ }
41
+ if (code.length === codeLength) {
42
+ emit('submit', code.join(''))
43
+ }
44
+ else {
45
+ emit('error', { message: 'Please enter all digits' })
46
+ }
47
+ }
48
+
49
+ function onRecoveryCode(event) {
50
+ event.preventDefault()
51
+ emit('useRecoveryCode')
52
+ }
12
53
  </script>
13
54
 
14
55
  <div class="{{ containerClasses }}">
@@ -21,7 +62,7 @@ export const codes = Array(codeLength).fill('')
21
62
  <label for="code-0" class="mt-4 block text-sm text-gray-900 dark:text-gray-100 font-medium leading-6">
22
63
  {{ labelText }}
23
64
  </label>
24
- <form onsubmit="event.preventDefault(); handleSubmit(event)">
65
+ <form @submit="onSubmit($event)">
25
66
  <div class="mt-2 flex rounded-md shadow-sm space-x-2">
26
67
  @for(i in Array(codeLength).keys())
27
68
  <input
@@ -31,8 +72,8 @@ export const codes = Array(codeLength).fill('')
31
72
  name="code-{{ i }}"
32
73
  placeholder="-"
33
74
  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 }})"
75
+ @input="onCodeInput($event, {{ i }})"
76
+ @keydown="onKeyDown($event, {{ i }})"
36
77
  />
37
78
  @endfor
38
79
  </div>
@@ -50,14 +91,14 @@ export const codes = Array(codeLength).fill('')
50
91
  {{ instructionText }}
51
92
  </p>
52
93
  </div>
53
- @if(onUseRecoveryCode)
94
+ @if(showRecoveryCode)
54
95
  <div class="mt-4">
55
96
  <p class="text-xs text-gray-700 dark:text-gray-300 font-semibold">
56
97
  Having Problems?
57
98
  </p>
58
99
  <a
59
100
  href="#"
60
- @click="event.preventDefault(); handleRecoveryCode()"
101
+ @click="onRecoveryCode($event)"
61
102
  class="text-xs text-blue-500 hover:underline"
62
103
  >
63
104
  Use recovery code instead
@@ -68,58 +109,3 @@ export const codes = Array(codeLength).fill('')
68
109
  </div>
69
110
  </div>
70
111
  </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>
@@ -6,11 +6,7 @@ export const size = $props.size || 'md'
6
6
  export const shape = $props.shape || 'circle'
7
7
  export const status = $props.status || ''
8
8
  export const className = $props.className || ''
9
- export const onError = $props.onError
10
9
 
11
- let imageError = false
12
-
13
- // Size classes
14
10
  const sizeClasses = {
15
11
  xs: 'w-6 h-6 text-xs',
16
12
  sm: 'w-8 h-8 text-sm',
@@ -20,13 +16,8 @@ const sizeClasses = {
20
16
  '2xl': 'w-20 h-20 text-2xl',
21
17
  }
22
18
 
23
- // Shape classes
24
- const shapeClasses = {
25
- circle: 'rounded-full',
26
- square: 'rounded-md',
27
- }
19
+ const shapeClasses = { circle: 'rounded-full', square: 'rounded-md' }
28
20
 
29
- // Status indicator size classes
30
21
  const statusSizeClasses = {
31
22
  xs: 'w-1.5 h-1.5 border',
32
23
  sm: 'w-2 h-2 border',
@@ -36,7 +27,6 @@ const statusSizeClasses = {
36
27
  '2xl': 'w-4 h-4 border-2',
37
28
  }
38
29
 
39
- // Status color classes
40
30
  const statusColorClasses = {
41
31
  online: 'bg-green-500 dark:bg-green-400',
42
32
  offline: 'bg-gray-400 dark:bg-gray-600',
@@ -48,24 +38,33 @@ export const containerClasses = `relative inline-block ${className}`.trim()
48
38
  export const avatarClasses = `flex items-center justify-center overflow-hidden bg-gray-200 dark:bg-gray-700 ${sizeClasses[size]} ${shapeClasses[shape]}`.trim()
49
39
  export const imageClasses = `w-full h-full object-cover`.trim()
50
40
  export const initialsClasses = `font-medium text-gray-600 dark:text-gray-300 uppercase select-none`.trim()
51
- export const statusClasses = `absolute bottom-0 right-0 ${statusSizeClasses[size]} ${statusColorClasses[status]} ${shapeClasses[shape]} border-white dark:border-gray-900`.trim()
52
-
53
- // Generate initials from name if not provided
41
+ export const statusClasses = status ? `absolute bottom-0 right-0 ${statusSizeClasses[size]} ${statusColorClasses[status]} ${shapeClasses[shape]} border-white dark:border-gray-900`.trim() : ''
54
42
  export const displayInitials = initials || (alt ? alt.split(' ').map(n => n[0]).join('').substring(0, 2) : '?')
55
43
  </script>
56
44
 
45
+ <script client>
46
+ const emit = defineEmits()
47
+ const imageError = state(false)
48
+ const hasSrc = {{ !!src }}
49
+
50
+ function onImageError(event) {
51
+ imageError.set(true)
52
+ emit('error', event)
53
+ }
54
+
55
+ const showImage = derived(() => hasSrc && !imageError())
56
+ </script>
57
+
57
58
  <div class="{{ containerClasses }}">
58
59
  <div class="{{ avatarClasses }}">
59
- @if(src && !imageError)
60
- <img
61
- src="{{ src }}"
62
- alt="{{ alt }}"
63
- class="{{ imageClasses }}"
64
- onerror="handleImageError(event)"
65
- />
66
- @else
67
- <span class="{{ initialsClasses }}">{{ displayInitials }}</span>
68
- @endif
60
+ <img
61
+ :show="showImage()"
62
+ src="{{ src }}"
63
+ alt="{{ alt }}"
64
+ class="{{ imageClasses }}"
65
+ @error="onImageError($event)"
66
+ />
67
+ <span :show="!showImage()" class="{{ initialsClasses }}">{{ displayInitials }}</span>
69
68
  </div>
70
69
 
71
70
  @if(status)
@@ -76,13 +75,3 @@ export const displayInitials = initials || (alt ? alt.split(' ').map(n => n[0]).
76
75
  ></span>
77
76
  @endif
78
77
  </div>
79
-
80
- <script>
81
- function handleImageError(event) {
82
- imageError = true
83
-
84
- if (onError) {
85
- onError(event)
86
- }
87
- }
88
- </script>
@@ -4,9 +4,7 @@ export const size = $props.size || 'md'
4
4
  export const dot = $props.dot || false
5
5
  export const removable = $props.removable || false
6
6
  export const className = $props.className || ''
7
- export const onRemove = $props.onRemove
8
7
 
9
- // Variant classes
10
8
  const variantClasses = {
11
9
  default: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300',
12
10
  primary: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200',
@@ -17,14 +15,8 @@ const variantClasses = {
17
15
  info: 'bg-cyan-100 text-cyan-800 dark:bg-cyan-900 dark:text-cyan-200',
18
16
  }
19
17
 
20
- // Size classes
21
- const sizeClasses = {
22
- sm: 'px-2 py-0.5 text-xs',
23
- md: 'px-2.5 py-0.5 text-sm',
24
- lg: 'px-3 py-1 text-base',
25
- }
18
+ const sizeClasses = { sm: 'px-2 py-0.5 text-xs', md: 'px-2.5 py-0.5 text-sm', lg: 'px-3 py-1 text-base' }
26
19
 
27
- // Dot color classes
28
20
  const dotColorClasses = {
29
21
  default: 'bg-gray-500 dark:bg-gray-400',
30
22
  primary: 'bg-blue-500 dark:bg-blue-400',
@@ -40,6 +32,14 @@ export const dotClasses = `w-2 h-2 rounded-full ${dotColorClasses[variant]}`.tri
40
32
  export const removeButtonClasses = `ml-0.5 -mr-1 text-current hover:bg-black hover:bg-opacity-10 dark:hover:bg-white dark:hover:bg-opacity-10 rounded-full p-0.5 transition-colors duration-150`.trim()
41
33
  </script>
42
34
 
35
+ <script client>
36
+ const emit = defineEmits()
37
+
38
+ function onRemove() {
39
+ emit('remove')
40
+ }
41
+ </script>
42
+
43
43
  <span class="{{ badgeClasses }}" role="status">
44
44
  @if(dot)
45
45
  <span class="{{ dotClasses }}" aria-hidden="true"></span>
@@ -47,10 +47,10 @@ export const removeButtonClasses = `ml-0.5 -mr-1 text-current hover:bg-black hov
47
47
 
48
48
  <slot />
49
49
 
50
- @if(removable && onRemove)
50
+ @if(removable)
51
51
  <button
52
52
  type="button"
53
- @click="handleRemove()"
53
+ @click="onRemove()"
54
54
  class="{{ removeButtonClasses }}"
55
55
  aria-label="Remove"
56
56
  >
@@ -60,11 +60,3 @@ export const removeButtonClasses = `ml-0.5 -mr-1 text-current hover:bg-black hov
60
60
  </button>
61
61
  @endif
62
62
  </span>
63
-
64
- <script>
65
- function handleRemove() {
66
- if (onRemove) {
67
- onRemove()
68
- }
69
- }
70
- </script>