@stacksjs/components 0.2.27 → 0.2.53

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 +3 -3
  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,51 +3,50 @@ export const checked = $props.checked || false
3
3
  export const disabled = $props.disabled || false
4
4
  export const size = $props.size || 'md'
5
5
  export const className = $props.className || ''
6
- export const onChange = $props.onChange
7
6
  export const label = $props.label || ''
8
7
 
9
- // Size configurations using headwind
10
8
  const sizeConfig = {
11
- sm: {
12
- track: 'h-5 w-9',
13
- thumb: 'h-4 w-4',
14
- translate: 'translate-x-4',
15
- },
16
- md: {
17
- track: 'h-[38px] w-[74px]',
18
- thumb: 'h-[34px] w-[34px]',
19
- translate: 'translate-x-9',
20
- },
21
- lg: {
22
- track: 'h-12 w-24',
23
- thumb: 'h-11 w-11',
24
- translate: 'translate-x-12',
25
- },
9
+ sm: { track: 'h-5 w-9', thumb: 'h-4 w-4', translate: 'translate-x-4' },
10
+ md: { track: 'h-[38px] w-[74px]', thumb: 'h-[34px] w-[34px]', translate: 'translate-x-9' },
11
+ lg: { track: 'h-12 w-24', thumb: 'h-11 w-11', translate: 'translate-x-12' },
26
12
  }
27
13
 
28
- const config = sizeConfig[size]
14
+ export const config = sizeConfig[size]
29
15
 
30
- // Track classes
31
- const trackBaseClasses = 'relative inline-flex shrink-0 cursor-pointer border-2 border-transparent rounded-full transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-neutral-300 dark:focus-visible:ring-offset-neutral-700'
32
- const trackStateClasses = checked
33
- ? 'bg-blue-500 dark:bg-blue-600'
34
- : 'bg-neutral-400 dark:bg-neutral-600'
35
- const trackDisabledClasses = disabled
36
- ? 'opacity-50 cursor-not-allowed pointer-events-none'
37
- : ''
16
+ export const trackBaseClasses = 'relative inline-flex shrink-0 cursor-pointer border-2 border-transparent rounded-full transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-neutral-300 dark:focus-visible:ring-offset-neutral-700'
17
+ export const trackDisabledClasses = disabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : ''
18
+ export const thumbBaseClasses = 'pointer-events-none inline-block transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out'
19
+ </script>
38
20
 
39
- export const trackClasses = `${trackBaseClasses} ${trackStateClasses} ${trackDisabledClasses} ${config.track} ${className}`.trim()
21
+ <script client>
22
+ const emit = defineEmits()
23
+ const isDisabled = {{ disabled }}
24
+ const config = {{ config }}
25
+ const trackBase = {{ trackBaseClasses }}
26
+ const trackDisabled = {{ trackDisabledClasses }}
27
+ const thumbBase = {{ thumbBaseClasses }}
28
+ const extraClass = {{ className }}
29
+ const isChecked = state({{ checked }})
40
30
 
41
- // Thumb classes
42
- const thumbBaseClasses = 'pointer-events-none inline-block transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out'
43
- const thumbPositionClasses = checked ? config.translate : 'translate-x-0'
44
- export const thumbClasses = `${thumbBaseClasses} ${thumbPositionClasses} ${config.thumb}`.trim()
31
+ function toggle() {
32
+ if (isDisabled) return
33
+ isChecked.set(!isChecked())
34
+ emit('change', isChecked())
35
+ }
45
36
 
46
- // Toggle function
47
- export function toggle() {
48
- if (disabled) return
49
- onChange?.(!checked)
37
+ function trackClasses() {
38
+ const stateClass = isChecked()
39
+ ? 'bg-blue-500 dark:bg-blue-600'
40
+ : 'bg-neutral-400 dark:bg-neutral-600'
41
+ return `${trackBase} ${stateClass} ${trackDisabled} ${config.track} ${extraClass}`.trim()
50
42
  }
43
+
44
+ function thumbClasses() {
45
+ const pos = isChecked() ? config.translate : 'translate-x-0'
46
+ return `${thumbBase} ${pos} ${config.thumb}`.trim()
47
+ }
48
+
49
+ defineExpose({ isChecked, toggle })
51
50
  </script>
52
51
 
53
52
  <label class="inline-flex items-center gap-3 cursor-pointer">
@@ -60,15 +59,12 @@ export function toggle() {
60
59
  <button
61
60
  type="button"
62
61
  role="switch"
63
- aria-checked="{{ checked ? 'true' : 'false' }}"
64
- class="{{ trackClasses }}"
62
+ :aria-checked="isChecked() ? 'true' : 'false'"
63
+ :class="trackClasses()"
65
64
  @if(disabled)disabled aria-disabled="true"@endif
66
- @click="toggle"
65
+ @click="toggle()"
67
66
  >
68
67
  <span class="sr-only">@if(label){{ label }}@else Toggle switch@endif</span>
69
- <span
70
- aria-hidden="true"
71
- class="{{ thumbClasses }}"
72
- />
68
+ <span aria-hidden="true" :class="thumbClasses()"></span>
73
69
  </button>
74
70
  </label>
@@ -1,13 +1,20 @@
1
1
  <script server>
2
2
  export const className = $props.className || ''
3
3
  export const sortable = $props.sortable || false
4
- export const onClick = $props.onClick
5
4
 
6
5
  const baseClasses = 'px-6 py-3 text-left text-xs font-medium text-neutral-500 uppercase tracking-wider dark:text-neutral-400'
7
6
  const sortableClasses = sortable ? 'cursor-pointer hover:text-neutral-700 dark:hover:text-neutral-200' : ''
8
- const headerClasses = `${baseClasses} ${sortableClasses} ${className}`.trim()
7
+ export const headerClasses = `${baseClasses} ${sortableClasses} ${className}`.trim()
9
8
  </script>
10
9
 
11
- <th class="{{ headerClasses }}" @if(sortable)@click="onClick"@endif>
10
+ <script client>
11
+ const emit = defineEmits()
12
+
13
+ function onClick() {
14
+ emit('click')
15
+ }
16
+ </script>
17
+
18
+ <th class="{{ headerClasses }}" @if(sortable)@click="onClick()"@endif>
12
19
  <slot />
13
20
  </th>
@@ -4,59 +4,84 @@ export const defaultTab = $props.defaultTab || 0
4
4
  export const orientation = $props.orientation || 'horizontal'
5
5
  export const variant = $props.variant || 'line'
6
6
  export const className = $props.className || ''
7
- export const onChange = $props.onChange
8
7
 
9
- let activeTab = defaultTab
10
-
11
- function selectTab(index) {
12
- activeTab = index
13
- if (onChange) {
14
- onChange(index, tabs[index])
15
- }
16
- }
17
-
18
- // Orientation classes
19
8
  const orientationClasses = {
20
9
  horizontal: 'flex-col',
21
10
  vertical: 'flex-row',
22
11
  }
23
12
 
24
- // Tab list orientation classes
25
13
  const tabListOrientationClasses = {
26
14
  horizontal: 'flex-row border-b',
27
15
  vertical: 'flex-col border-r',
28
16
  }
29
17
 
30
- // Variant classes for tab list
31
18
  const variantClasses = {
32
19
  line: 'border-gray-200 dark:border-gray-700',
33
20
  pills: 'gap-2 border-0 bg-gray-100 dark:bg-gray-800 p-1 rounded-lg',
34
21
  enclosed: 'border-gray-200 dark:border-gray-700',
35
22
  }
36
23
 
37
- // Tab button classes based on variant
38
- function getTabClasses(index, variant, orientation) {
39
- const isActive = index === activeTab
40
- const baseClasses = 'px-4 py-2 font-medium text-sm transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900'
24
+ export const containerClasses = `flex ${orientationClasses[orientation]} ${className}`.trim()
25
+ export const tabListClasses = `flex ${tabListOrientationClasses[orientation]} ${variantClasses[variant]}`.trim()
26
+ export const tabPanelClasses = `p-4 focus:outline-none`.trim()
27
+ </script>
28
+
29
+ <script client>
30
+ const emit = defineEmits()
31
+ const tabs = {{ tabs }}
32
+ const variant = {{ variant }}
33
+ const orientation = {{ orientation }}
34
+ const activeTab = state({{ defaultTab }})
35
+
36
+ function selectTab(index) {
37
+ activeTab.set(index)
38
+ emit('change', { index, tab: tabs[index] })
39
+ }
40
+
41
+ function isActive(index) {
42
+ return activeTab() === index
43
+ }
41
44
 
45
+ function getTabClasses(index) {
46
+ const isActiveTab = isActive(index)
47
+ const base = 'px-4 py-2 font-medium text-sm transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900'
42
48
  if (variant === 'line') {
43
- const borderPosition = orientation === 'horizontal' ? 'border-b-2' : 'border-r-2'
44
- return `${baseClasses} ${borderPosition} ${isActive ? 'border-blue-500 text-blue-600 dark:text-blue-400' : 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 hover:border-gray-300 dark:hover:border-gray-600'}`
45
- } else if (variant === 'pills') {
46
- return `${baseClasses} rounded-md ${isActive ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'}`
47
- } else if (variant === 'enclosed') {
48
- const borderClasses = orientation === 'horizontal' ? 'border-t border-l border-r rounded-t-md' : 'border-t border-l border-b rounded-l-md'
49
- return `${baseClasses} ${borderClasses} ${isActive ? 'bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 -mb-px' : 'bg-gray-50 dark:bg-gray-900 border-transparent hover:border-gray-200 dark:hover:border-gray-700'}`
49
+ const border = orientation === 'horizontal' ? 'border-b-2' : 'border-r-2'
50
+ return `${base} ${border} ${isActiveTab ? 'border-blue-500 text-blue-600 dark:text-blue-400' : 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 hover:border-gray-300 dark:hover:border-gray-600'}`
50
51
  }
52
+ if (variant === 'pills') {
53
+ return `${base} rounded-md ${isActiveTab ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'}`
54
+ }
55
+ if (variant === 'enclosed') {
56
+ const borders = orientation === 'horizontal' ? 'border-t border-l border-r rounded-t-md' : 'border-t border-l border-b rounded-l-md'
57
+ return `${base} ${borders} ${isActiveTab ? 'bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 -mb-px' : 'bg-gray-50 dark:bg-gray-900 border-transparent hover:border-gray-200 dark:hover:border-gray-700'}`
58
+ }
59
+ return base
51
60
  }
52
61
 
53
- export const containerClasses = `flex ${orientationClasses[orientation]} ${className}`.trim()
54
- export const tabListClasses = `flex ${tabListOrientationClasses[orientation]} ${variantClasses[variant]}`.trim()
55
- export const tabPanelClasses = `p-4 focus:outline-none`.trim()
62
+ function onTabKey(event, index) {
63
+ const tabCount = tabs.length
64
+ let next = index
65
+ if (orientation === 'horizontal') {
66
+ if (event.key === 'ArrowRight') { event.preventDefault(); next = (index + 1) % tabCount }
67
+ else if (event.key === 'ArrowLeft') { event.preventDefault(); next = (index - 1 + tabCount) % tabCount }
68
+ }
69
+ else {
70
+ if (event.key === 'ArrowDown') { event.preventDefault(); next = (index + 1) % tabCount }
71
+ else if (event.key === 'ArrowUp') { event.preventDefault(); next = (index - 1 + tabCount) % tabCount }
72
+ }
73
+ if (event.key === 'Home') { event.preventDefault(); next = 0 }
74
+ else if (event.key === 'End') { event.preventDefault(); next = tabCount - 1 }
75
+ if (next !== index) {
76
+ selectTab(next)
77
+ document.getElementById(`tab-${next}`)?.focus()
78
+ }
79
+ }
80
+
81
+ defineExpose({ activeTab, selectTab })
56
82
  </script>
57
83
 
58
84
  <div class="{{ containerClasses }}">
59
- <!-- Tab List -->
60
85
  <div
61
86
  class="{{ tabListClasses }}"
62
87
  role="tablist"
@@ -65,18 +90,18 @@ export const tabPanelClasses = `p-4 focus:outline-none`.trim()
65
90
  @foreach(tab in tabs)
66
91
  <button
67
92
  type="button"
68
- class="{{ getTabClasses($loop.index, variant, orientation) }}"
93
+ :class="getTabClasses({{ $loop.index }})"
69
94
  role="tab"
70
- aria-selected="{{ activeTab === $loop.index ? 'true' : 'false' }}"
95
+ :aria-selected="isActive({{ $loop.index }}) ? 'true' : 'false'"
71
96
  aria-controls="tab-panel-{{ $loop.index }}"
72
97
  id="tab-{{ $loop.index }}"
73
- tabindex="{{ activeTab === $loop.index ? '0' : '-1' }}"
98
+ :tabindex="isActive({{ $loop.index }}) ? '0' : '-1'"
74
99
  @click="selectTab({{ $loop.index }})"
75
- onkeydown="handleKeyDown(event, {{ $loop.index }})"
100
+ @keydown="onTabKey($event, {{ $loop.index }})"
76
101
  >
77
102
  @if(tab.icon)
78
103
  <span class="inline-flex items-center gap-2">
79
- <span innerHTML="{!! tab.icon !!}"></span>
104
+ <span x-html="{!! JSON.stringify(tab.icon) !!}"></span>
80
105
  <span>{{ tab.label }}</span>
81
106
  </span>
82
107
  @else
@@ -86,60 +111,16 @@ export const tabPanelClasses = `p-4 focus:outline-none`.trim()
86
111
  @endforeach
87
112
  </div>
88
113
 
89
- <!-- Tab Panels -->
90
114
  @foreach(tab in tabs)
91
- @if(activeTab === $loop.index)
92
- <div
93
- id="tab-panel-{{ $loop.index }}"
94
- class="{{ tabPanelClasses }}"
95
- role="tabpanel"
96
- aria-labelledby="tab-{{ $loop.index }}"
97
- tabindex="0"
98
- >
99
- {!! tab.content !!}
100
- </div>
101
- @endif
115
+ <div
116
+ :show="isActive({{ $loop.index }})"
117
+ id="tab-panel-{{ $loop.index }}"
118
+ class="{{ tabPanelClasses }}"
119
+ role="tabpanel"
120
+ aria-labelledby="tab-{{ $loop.index }}"
121
+ tabindex="0"
122
+ >
123
+ {!! tab.content !!}
124
+ </div>
102
125
  @endforeach
103
126
  </div>
104
-
105
- <script>
106
- function handleKeyDown(event, currentIndex) {
107
- const tabCount = tabs.length
108
- let newIndex = currentIndex
109
-
110
- if (orientation === 'horizontal') {
111
- if (event.key === 'ArrowRight') {
112
- event.preventDefault()
113
- newIndex = (currentIndex + 1) % tabCount
114
- } else if (event.key === 'ArrowLeft') {
115
- event.preventDefault()
116
- newIndex = (currentIndex - 1 + tabCount) % tabCount
117
- }
118
- } else {
119
- if (event.key === 'ArrowDown') {
120
- event.preventDefault()
121
- newIndex = (currentIndex + 1) % tabCount
122
- } else if (event.key === 'ArrowUp') {
123
- event.preventDefault()
124
- newIndex = (currentIndex - 1 + tabCount) % tabCount
125
- }
126
- }
127
-
128
- if (event.key === 'Home') {
129
- event.preventDefault()
130
- newIndex = 0
131
- } else if (event.key === 'End') {
132
- event.preventDefault()
133
- newIndex = tabCount - 1
134
- }
135
-
136
- if (newIndex !== currentIndex) {
137
- selectTab(newIndex)
138
- // Focus the new tab
139
- if (typeof window !== 'undefined') {
140
- const newTab = document.getElementById(`tab-${newIndex}`)
141
- if (newTab) newTab.focus()
142
- }
143
- }
144
- }
145
- </script>
@@ -3,99 +3,63 @@ export const to = $props.to || 'body'
3
3
  export const disabled = $props.disabled || false
4
4
  export const defer = $props.defer || false
5
5
 
6
- let teleportContainer = null
7
- let teleportId = `teleport-${Math.random().toString(36).substr(2, 9)}`
6
+ export const teleportId = `teleport-${Math.random().toString(36).slice(2, 11)}`
7
+ </script>
8
8
 
9
- // Teleport content to target
10
- function teleport() {
11
- if (typeof window === 'undefined' || disabled)
12
- return
9
+ <script client>
10
+ const to = {{ to }}
11
+ const isDisabled = {{ disabled }}
12
+ const shouldDefer = {{ defer }}
13
+ const teleportId = {{ teleportId }}
13
14
 
14
- // Get target element
15
- const targetElement = typeof to === 'string'
16
- ? document.querySelector(to)
17
- : to
15
+ let teleportContainer = null
18
16
 
17
+ function teleport() {
18
+ if (isDisabled) return
19
+ const targetElement = typeof to === 'string' ? document.querySelector(to) : to
19
20
  if (!targetElement) {
20
21
  console.warn(`Teleport target "${to}" not found`)
21
22
  return
22
23
  }
23
-
24
- // Get source element
25
24
  const sourceElement = document.getElementById(teleportId)
26
- if (!sourceElement)
27
- return
28
-
29
- // Create teleport container if needed
25
+ if (!sourceElement) return
30
26
  if (!teleportContainer) {
31
27
  teleportContainer = document.createElement('div')
32
28
  teleportContainer.setAttribute('data-teleport-id', teleportId)
33
29
  }
34
-
35
- // Move all child nodes to container
36
30
  while (sourceElement.firstChild) {
37
31
  teleportContainer.appendChild(sourceElement.firstChild)
38
32
  }
39
-
40
- // Append to target
41
33
  targetElement.appendChild(teleportContainer)
42
34
  }
43
35
 
44
- // Return content back to original location
45
36
  function returnContent() {
46
- if (!teleportContainer || !teleportContainer.parentNode)
47
- return
48
-
37
+ if (!teleportContainer || !teleportContainer.parentNode) return
49
38
  const sourceElement = document.getElementById(teleportId)
50
- if (!sourceElement)
51
- return
52
-
53
- // Move content back
39
+ if (!sourceElement) return
54
40
  while (teleportContainer.firstChild) {
55
41
  sourceElement.appendChild(teleportContainer.firstChild)
56
42
  }
57
-
58
- // Remove container
59
43
  teleportContainer.parentNode.removeChild(teleportContainer)
60
44
  teleportContainer = null
61
45
  }
62
46
 
63
- // Initialize teleport
64
- if (typeof window !== 'undefined') {
65
- if (defer) {
66
- // Defer until next tick
67
- window.addEventListener('DOMContentLoaded', () => {
68
- setTimeout(teleport, 0)
69
- })
70
- }
71
- else {
72
- // Immediate
73
- window.addEventListener('DOMContentLoaded', teleport)
74
- }
75
-
76
- // Cleanup on page unload
47
+ onMount(() => {
48
+ if (shouldDefer) setTimeout(teleport, 0)
49
+ else teleport()
77
50
  window.addEventListener('beforeunload', returnContent)
78
- }
51
+ })
52
+
53
+ onDestroy(() => {
54
+ returnContent()
55
+ window.removeEventListener('beforeunload', returnContent)
56
+ })
79
57
  </script>
80
58
 
81
59
  @if(disabled)
82
- <!-- Render in place if disabled -->
83
60
  <slot />
84
61
  @else
85
- <!-- Source container -->
86
62
  <div id="{{ teleportId }}" data-teleport-source="true">
87
63
  <slot />
88
64
  </div>
89
65
  @endif
90
-
91
- <script>
92
- // Trigger teleport after render
93
- if (typeof window !== 'undefined' && document.readyState === 'complete') {
94
- if (defer) {
95
- setTimeout(teleport, 0)
96
- }
97
- else {
98
- teleport()
99
- }
100
- }
101
- </script>
@@ -1,6 +1,5 @@
1
1
  <script server>
2
2
  export const value = $props.value || ''
3
- export const onChange = $props.onChange || (() => {})
4
3
  export const placeholder = $props.placeholder || ''
5
4
  export const disabled = $props.disabled || false
6
5
  export const readonly = $props.readonly || false
@@ -13,24 +12,14 @@ export const helperText = $props.helperText || ''
13
12
  export const required = $props.required || false
14
13
  export const maxLength = $props.maxLength
15
14
  export const showCount = $props.showCount || false
16
- export const resize = $props.resize || 'vertical' // 'none' | 'vertical' | 'horizontal' | 'both'
15
+ export const resize = $props.resize || 'vertical'
17
16
  export const className = $props.className || ''
18
17
 
19
- // Resize classes
20
- const resizeClasses = {
21
- none: 'resize-none',
22
- vertical: 'resize-y',
23
- horizontal: 'resize-x',
24
- both: 'resize',
25
- }
26
-
27
- // Base and state classes
18
+ const resizeClasses = { none: 'resize-none', vertical: 'resize-y', horizontal: 'resize-x', both: 'resize' }
28
19
  const baseClasses = 'block w-full border-0 rounded-md shadow-sm ring-1 ring-inset transition-colors duration-200 focus:ring-2 focus:ring-inset px-4 py-2 text-base dark:bg-blue-gray-700 dark:text-gray-100'
29
-
30
20
  const stateClasses = error
31
21
  ? 'ring-red-300 dark:ring-red-600 text-red-900 dark:text-red-300 placeholder-red-300 dark:placeholder-red-500 focus:ring-red-500 dark:focus:ring-red-400'
32
22
  : 'ring-gray-300 dark:ring-gray-600 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:ring-indigo-600 dark:focus:ring-indigo-400'
33
-
34
23
  const disabledClasses = disabled
35
24
  ? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
36
25
  : readonly
@@ -42,6 +31,40 @@ export const labelClasses = `block text-sm font-medium leading-6 mb-2 ${error ?
42
31
  export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
43
32
  </script>
44
33
 
34
+ <script client>
35
+ const emit = defineEmits()
36
+ const autoResize = {{ autoResize }}
37
+ const maxRows = {{ maxRows }}
38
+ const inputValue = state({{ value }})
39
+
40
+ function resizeTextarea(el) {
41
+ if (!autoResize) return
42
+ el.style.height = 'auto'
43
+ const scrollHeight = el.scrollHeight
44
+ const lineHeight = Number.parseInt(window.getComputedStyle(el).lineHeight) || 20
45
+ const maxHeight = lineHeight * maxRows
46
+ el.style.height = `${Math.min(scrollHeight, maxHeight)}px`
47
+ el.style.overflowY = scrollHeight > maxHeight ? 'auto' : 'hidden'
48
+ }
49
+
50
+ function onInput(event) {
51
+ inputValue.set(event.target.value)
52
+ resizeTextarea(event.target)
53
+ emit('input', inputValue())
54
+ }
55
+
56
+ onMount(() => {
57
+ if (autoResize) {
58
+ const el = document.getElementById('textarea-input')
59
+ if (el) resizeTextarea(el)
60
+ }
61
+ })
62
+
63
+ const charCount = derived(() => `${inputValue().length} / {{ maxLength || 0 }}`)
64
+
65
+ defineExpose({ value: inputValue })
66
+ </script>
67
+
45
68
  <div class="w-full">
46
69
  @if(label)
47
70
  <label for="textarea-input" class="{{ labelClasses }}">
@@ -57,12 +80,12 @@ export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red
57
80
  class="{{ textareaClasses }}"
58
81
  rows="{{ rows }}"
59
82
  placeholder="{{ placeholder }}"
60
- disabled="{{ disabled }}"
61
- readonly="{{ readonly }}"
62
- required="{{ required }}"
83
+ @if(disabled)disabled@endif
84
+ @if(readonly)readonly@endif
85
+ @if(required)required@endif
63
86
  @if(maxLength)maxlength="{{ maxLength }}"@endif
64
- oninput="handleInput(event)"
65
- aria-invalid="{{ error ? 'true' : 'false' }}"
87
+ @input="onInput($event)"
88
+ :aria-invalid="{{ error ? 'true' : 'false' }}"
66
89
  @if(helperText)aria-describedby="helper-text"@endif
67
90
  >{{ value }}</textarea>
68
91
 
@@ -77,43 +100,8 @@ export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red
77
100
  @endif
78
101
 
79
102
  @if(showCount && maxLength)
80
- <p class="text-sm text-gray-500 dark:text-gray-400">
81
- {{ value.length }} / {{ maxLength }}
82
- </p>
103
+ <p class="text-sm text-gray-500 dark:text-gray-400" x-text="charCount()"></p>
83
104
  @endif
84
105
  </div>
85
106
  @endif
86
107
  </div>
87
-
88
- <script>
89
- function handleInput(event) {
90
- const textarea = event.target
91
- const newValue = textarea.value
92
-
93
- if (autoResize && typeof window !== 'undefined') {
94
- // Auto-resize functionality
95
- textarea.style.height = 'auto'
96
- const scrollHeight = textarea.scrollHeight
97
- const lineHeight = parseInt(window.getComputedStyle(textarea).lineHeight)
98
- const maxHeight = lineHeight * maxRows
99
-
100
- textarea.style.height = Math.min(scrollHeight, maxHeight) + 'px'
101
- textarea.style.overflowY = scrollHeight > maxHeight ? 'auto' : 'hidden'
102
- }
103
-
104
- if (onChange) {
105
- onChange(newValue)
106
- }
107
- }
108
-
109
- // Initialize auto-resize on mount
110
- if (autoResize && typeof window !== 'undefined') {
111
- window.addEventListener('DOMContentLoaded', () => {
112
- const textarea = document.getElementById('textarea-input')
113
- if (textarea && value) {
114
- // Trigger initial resize
115
- textarea.dispatchEvent(new Event('input'))
116
- }
117
- })
118
- }
119
- </script>