@stacksjs/components 0.2.35 → 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
@@ -1,7 +1,5 @@
1
1
  <script server>
2
2
  export const value = $props.value || ''
3
- export const onChange = $props.onChange || (() => {})
4
- export const onSearch = $props.onSearch || (() => {})
5
3
  export const placeholder = $props.placeholder || 'Search...'
6
4
  export const disabled = $props.disabled || false
7
5
  export const size = $props.size || 'md'
@@ -11,53 +9,36 @@ export const helperText = $props.helperText || ''
11
9
  export const loading = $props.loading || false
12
10
  export const className = $props.className || ''
13
11
 
14
- // Search icon SVG
15
- const searchIcon = '<svg fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>'
16
-
17
- // Loading spinner SVG
18
- const spinnerIcon = '<svg class="animate-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>'
19
-
20
- export const rightIcon = loading ? spinnerIcon : undefined
12
+ export const searchIcon = '<svg fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>'
13
+ export const spinnerIcon = '<svg class="animate-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>'
14
+ export const rightIcon = loading ? spinnerIcon : ''
21
15
  </script>
22
16
 
23
- @component('TextInput', {
24
- value,
25
- onChange,
26
- placeholder,
27
- disabled,
28
- size,
29
- error,
30
- label,
31
- helperText,
32
- leftIcon: searchIcon,
33
- rightIcon,
34
- clearable: !loading,
35
- className,
36
- inputClassName: 'pr-20'
37
- })
38
- @endcomponent
39
-
40
- <script>
41
- // Debounce search to avoid excessive calls
17
+ <script client>
18
+ const emit = defineEmits()
42
19
  let searchTimeout = null
43
20
 
44
- function handleSearchInput(newValue) {
45
- if (onChange) {
46
- onChange(newValue)
47
- }
48
-
49
- // Debounce the onSearch callback
50
- if (searchTimeout) {
51
- clearTimeout(searchTimeout)
52
- }
53
-
54
- searchTimeout = setTimeout(() => {
55
- if (onSearch) {
56
- onSearch(newValue)
57
- }
58
- }, 300) // 300ms debounce
21
+ function onSearchInput(event) {
22
+ const target = event.target
23
+ const value = target?.value ?? event.detail
24
+ if (searchTimeout) clearTimeout(searchTimeout)
25
+ searchTimeout = setTimeout(() => emit('search', value), 300)
59
26
  }
60
-
61
- // Expose enhanced onChange handler
62
- export const enhancedOnChange = handleSearchInput
63
27
  </script>
28
+
29
+ <div @input="onSearchInput($event)">
30
+ <TextInput
31
+ :value="value"
32
+ :placeholder="placeholder"
33
+ :disabled="disabled"
34
+ :size="size"
35
+ :error="error"
36
+ :label="label"
37
+ :helperText="helperText"
38
+ :leftIcon="searchIcon"
39
+ :rightIcon="rightIcon"
40
+ :clearable="!loading"
41
+ :className="className"
42
+ inputClassName="pr-20"
43
+ />
44
+ </div>
@@ -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
@@ -17,45 +16,51 @@ export const showCount = $props.showCount || false
17
16
  export const className = $props.className || ''
18
17
  export const inputClassName = $props.inputClassName || ''
19
18
 
20
- // Size classes
21
- const sizeClasses = {
22
- sm: 'px-3 py-1.5 text-sm h-8',
23
- md: 'px-4 py-2 text-base h-10',
24
- lg: 'px-5 py-3 text-lg h-12',
25
- }
26
-
27
- // Icon size classes
28
- const iconSizeClasses = {
29
- sm: 'h-4 w-4',
30
- md: 'h-5 w-5',
31
- lg: 'h-6 w-6',
32
- }
19
+ const sizeClasses = { sm: 'px-3 py-1.5 text-sm h-8', md: 'px-4 py-2 text-base h-10', lg: 'px-5 py-3 text-lg h-12' }
20
+ const iconSizeClasses = { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6' }
33
21
 
34
- // Base and state classes
35
22
  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 dark:bg-blue-gray-700 dark:text-gray-100'
36
-
37
23
  const stateClasses = error
38
24
  ? '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'
39
25
  : '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'
40
-
41
26
  const disabledClasses = disabled
42
27
  ? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
43
28
  : readonly
44
29
  ? 'bg-gray-50 dark:bg-gray-800 cursor-default'
45
30
  : 'bg-white dark:bg-blue-gray-700'
46
-
47
31
  const paddingClasses = leftIcon ? 'pl-10' : rightIcon || clearable ? 'pr-10' : ''
48
32
 
49
33
  export const inputClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses} ${paddingClasses} ${inputClassName}`.trim()
50
34
  export const labelClasses = `block text-sm font-medium leading-6 mb-2 ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100'}`.trim()
51
35
  export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
52
36
  export const iconClasses = iconSizeClasses[size]
53
- export const hasValue = value && value.length > 0
37
+ export const inputId = `input-${label}`
38
+ </script>
39
+
40
+ <script client>
41
+ const emit = defineEmits()
42
+ const inputValue = state({{ value }})
43
+
44
+ function onInput(event) {
45
+ inputValue.set(event.target.value)
46
+ emit('input', inputValue())
47
+ }
48
+
49
+ function onClear() {
50
+ inputValue.set('')
51
+ emit('input', '')
52
+ emit('clear')
53
+ }
54
+
55
+ const hasValue = derived(() => inputValue().length > 0)
56
+ const charCount = derived(() => `${inputValue().length} / {{ maxLength || 0 }}`)
57
+
58
+ defineExpose({ value: inputValue, clear: onClear })
54
59
  </script>
55
60
 
56
61
  <div class="w-full {{ className }}">
57
62
  @if(label)
58
- <label for="input-{{ label }}" class="{{ labelClasses }}">
63
+ <label for="{{ inputId }}" class="{{ labelClasses }}">
59
64
  {{ label }}
60
65
  @if(required)
61
66
  <span class="text-red-500 ml-1">*</span>
@@ -65,33 +70,31 @@ export const hasValue = value && value.length > 0
65
70
 
66
71
  <div class="relative">
67
72
  @if(leftIcon)
68
- <!-- Left icon -->
69
73
  <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
70
- <span class="{{ iconClasses }} text-gray-400 dark:text-gray-500" innerHTML="{!! leftIcon !!}"></span>
74
+ <span class="{{ iconClasses }} text-gray-400 dark:text-gray-500">{!! leftIcon !!}</span>
71
75
  </div>
72
76
  @endif
73
77
 
74
78
  <input
75
79
  type="text"
76
- id="input-{{ label }}"
80
+ id="{{ inputId }}"
77
81
  class="{{ inputClasses }}"
78
- value="{{ value }}"
82
+ :value="inputValue()"
79
83
  placeholder="{{ placeholder }}"
80
- disabled="{{ disabled }}"
81
- readonly="{{ readonly }}"
82
- required="{{ required }}"
84
+ @if(disabled)disabled@endif
85
+ @if(readonly)readonly@endif
86
+ @if(required)required@endif
83
87
  @if(maxLength)maxlength="{{ maxLength }}"@endif
84
- oninput="handleInput(event)"
85
- aria-invalid="{{ error ? 'true' : 'false' }}"
88
+ @input="onInput($event)"
89
+ :aria-invalid="{{ error ? 'true' : 'false' }}"
86
90
  @if(helperText)aria-describedby="helper-text"@endif
87
91
  />
88
92
 
89
- @if(clearable && hasValue && !disabled && !readonly)
90
- <!-- Clear button -->
91
- <div class="absolute inset-y-0 right-0 flex items-center pr-3">
93
+ @if(clearable)
94
+ <div :show="hasValue() && !{{ disabled }} && !{{ readonly }}" class="absolute inset-y-0 right-0 flex items-center pr-3">
92
95
  <button
93
96
  type="button"
94
- @click="handleClear()"
97
+ @click="onClear()"
95
98
  class="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 focus:outline-none"
96
99
  aria-label="Clear input"
97
100
  >
@@ -100,13 +103,11 @@ export const hasValue = value && value.length > 0
100
103
  </svg>
101
104
  </button>
102
105
  </div>
103
- @else
104
- @if(rightIcon && !clearable)
105
- <!-- Right icon -->
106
- <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
107
- <span class="{{ iconClasses }} text-gray-400 dark:text-gray-500" innerHTML="{!! rightIcon !!}"></span>
108
- </div>
109
- @endif
106
+ @endif
107
+ @if(rightIcon && !clearable)
108
+ <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
109
+ <span class="{{ iconClasses }} text-gray-400 dark:text-gray-500">{!! rightIcon !!}</span>
110
+ </div>
110
111
  @endif
111
112
  </div>
112
113
 
@@ -121,26 +122,8 @@ export const hasValue = value && value.length > 0
121
122
  @endif
122
123
 
123
124
  @if(showCount && maxLength)
124
- <p class="text-sm text-gray-500 dark:text-gray-400">
125
- {{ value.length }} / {{ maxLength }}
126
- </p>
125
+ <p class="text-sm text-gray-500 dark:text-gray-400" x-text="charCount()"></p>
127
126
  @endif
128
127
  </div>
129
128
  @endif
130
129
  </div>
131
-
132
- <script>
133
- function handleInput(event) {
134
- const newValue = event.target.value
135
-
136
- if (onChange) {
137
- onChange(newValue)
138
- }
139
- }
140
-
141
- function handleClear() {
142
- if (onChange) {
143
- onChange('')
144
- }
145
- }
146
- </script>
@@ -1,14 +1,101 @@
1
1
  <script server>
2
2
  export const value = $props.value
3
- export const onChange = $props.onChange
4
3
  export const multiple = $props.multiple || false
5
4
  export const className = $props.className || ''
6
5
  export const as = $props.as || 'div'
7
6
 
8
7
  const baseClasses = 'relative'
9
- const listboxClasses = `${baseClasses} ${className}`.trim()
8
+ export const listboxClasses = `${baseClasses} ${className}`.trim()
9
+ export const listboxInstanceId = `stx-lb-${Math.random().toString(36).slice(2, 11)}`
10
10
  </script>
11
11
 
12
- <component :is="{{ as }}" class="{{ listboxClasses }}">
13
- <slot :value="value" :onChange="onChange" :multiple="multiple" />
12
+ <script client>
13
+ const emit = defineEmits()
14
+ const listboxInstanceId = {{ listboxInstanceId }}
15
+ const multiple = {{ multiple }}
16
+ const initialValue = {{ value === undefined ? 'null' : value }}
17
+
18
+ const isOpen = state(false)
19
+ const selected = state(initialValue)
20
+ let rootEl = null
21
+ let outsideHandler = null
22
+
23
+ function isSelectedValue(v) {
24
+ const cur = selected()
25
+ if (multiple && Array.isArray(cur)) return cur.includes(v)
26
+ return cur === v
27
+ }
28
+
29
+ function selectValue(v) {
30
+ if (multiple) {
31
+ const cur = Array.isArray(selected()) ? selected() : []
32
+ selected.set(cur.includes(v) ? cur.filter(x => x !== v) : [...cur, v])
33
+ }
34
+ else {
35
+ selected.set(v)
36
+ isOpen.set(false)
37
+ }
38
+ syncOptionStates()
39
+ emit('change', selected())
40
+ }
41
+
42
+ function syncOptionStates() {
43
+ if (!rootEl) return
44
+ const options = rootEl.querySelectorAll('[data-stx-listbox-option]')
45
+ for (const opt of options) {
46
+ const raw = opt.dataset.optionValue
47
+ let v
48
+ try { v = JSON.parse(raw ?? 'null') }
49
+ catch { v = raw }
50
+ const sel = isSelectedValue(v)
51
+ opt.setAttribute('aria-selected', sel ? 'true' : 'false')
52
+ opt.setAttribute('data-selected', sel ? 'true' : 'false')
53
+ }
54
+ }
55
+
56
+ function open() { if (!isOpen()) { isOpen.set(true); emit('change', selected()) } }
57
+ function close() { if (isOpen()) { isOpen.set(false) } }
58
+ function toggle() { isOpen() ? close() : open() }
59
+
60
+ function onToggle(event) { event.stopPropagation(); toggle() }
61
+ function onSelect(event) {
62
+ event.stopPropagation()
63
+ selectValue(event.detail)
64
+ }
65
+
66
+ onMount(() => {
67
+ rootEl = document.querySelector(`[data-listbox-id="${listboxInstanceId}"]`)
68
+ syncOptionStates()
69
+ outsideHandler = (event) => {
70
+ if (!isOpen()) return
71
+ if (rootEl && !rootEl.contains(event.target)) close()
72
+ }
73
+ document.addEventListener('click', outsideHandler, true)
74
+ })
75
+
76
+ onDestroy(() => {
77
+ if (outsideHandler) document.removeEventListener('click', outsideHandler, true)
78
+ })
79
+
80
+ const dataState = derived(() => isOpen() ? 'open' : 'closed')
81
+
82
+ defineExpose({ isOpen, selected, toggle, open, close, selectValue })
83
+ </script>
84
+
85
+ <component
86
+ :is="{{ as }}"
87
+ class="{{ listboxClasses }}"
88
+ data-stx-listbox
89
+ data-listbox-id="{{ listboxInstanceId }}"
90
+ :data-state="dataState()"
91
+ @stx:listbox-toggle="onToggle($event)"
92
+ @stx:listbox-select="onSelect($event)"
93
+ >
94
+ <slot />
14
95
  </component>
96
+
97
+ <style>
98
+ [data-stx-listbox][data-state="closed"] [data-stx-listbox-options] {
99
+ display: none;
100
+ }
101
+ </style>
@@ -5,13 +5,24 @@ export const disabled = $props.disabled || false
5
5
 
6
6
  const baseClasses = 'relative w-full cursor-pointer rounded-lg bg-white dark:bg-neutral-800 py-2 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-blue-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-blue-300'
7
7
  const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : ''
8
- const buttonClasses = `${baseClasses} ${disabledClasses} ${className}`.trim()
8
+ export const buttonClasses = `${baseClasses} ${disabledClasses} ${className}`.trim()
9
+ </script>
10
+
11
+ <script client>
12
+ const isDisabled = {{ disabled }}
13
+
14
+ function onClick(event) {
15
+ if (isDisabled) return
16
+ event.currentTarget.dispatchEvent(new CustomEvent('stx:listbox-toggle', { bubbles: true }))
17
+ }
9
18
  </script>
10
19
 
11
20
  <component
12
21
  :is="{{ as }}"
13
22
  class="{{ buttonClasses }}"
23
+ data-stx-listbox-button
14
24
  @if(disabled)disabled aria-disabled="true"@endif
25
+ @click="onClick($event)"
15
26
  >
16
27
  <slot />
17
28
  </component>
@@ -3,55 +3,47 @@ export const value = $props.value
3
3
  export const disabled = $props.disabled || false
4
4
  export const className = $props.className || ''
5
5
  export const as = $props.as || 'li'
6
- export const currentValue = $props.currentValue
7
- export const onChange = $props.onChange
8
6
 
9
- export let active = false
10
- export const selected = Array.isArray(currentValue)
11
- ? currentValue.includes(value)
12
- : currentValue === value
13
-
14
- const baseClasses = 'relative cursor-pointer select-none py-2 pl-10 pr-4'
15
- const stateClasses = active
16
- ? 'bg-blue-100 text-blue-900 dark:bg-blue-900 dark:text-blue-100'
17
- : 'text-neutral-900 dark:text-neutral-100'
18
- const disabledClasses = disabled
19
- ? 'opacity-50 cursor-not-allowed'
20
- : ''
21
-
22
- const optionClasses = `${baseClasses} ${stateClasses} ${disabledClasses} ${className}`.trim()
23
-
24
- export function handleClick() {
25
- if (disabled) return
26
- onChange?.(value)
27
- }
7
+ const baseClasses = 'relative cursor-pointer select-none py-2 pl-10 pr-4 text-neutral-900 dark:text-neutral-100 hover:bg-blue-100 dark:hover:bg-blue-900'
8
+ const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : ''
9
+ export const optionClasses = `${baseClasses} ${disabledClasses} ${className}`.trim()
10
+ export const valueJson = JSON.stringify(value === undefined ? null : value)
11
+ </script>
28
12
 
29
- export function handleMouseEnter() {
30
- if (!disabled) active = true
31
- }
13
+ <script client>
14
+ const isDisabled = {{ disabled }}
15
+ const optionValue = {{ value === undefined ? 'null' : value }}
32
16
 
33
- export function handleMouseLeave() {
34
- active = false
17
+ function onClick(event) {
18
+ if (isDisabled) return
19
+ event.currentTarget.dispatchEvent(
20
+ new CustomEvent('stx:listbox-select', { bubbles: true, detail: optionValue }),
21
+ )
35
22
  }
36
23
  </script>
37
24
 
38
25
  <component
39
26
  :is="{{ as }}"
40
27
  class="{{ optionClasses }}"
28
+ data-stx-listbox-option
29
+ data-option-value="{{ valueJson }}"
41
30
  role="option"
42
- aria-selected="{{ selected ? 'true' : 'false' }}"
31
+ aria-selected="false"
43
32
  @if(disabled)aria-disabled="true"@endif
44
- @click="handleClick"
45
- @mouseenter="handleMouseEnter"
46
- @mouseleave="handleMouseLeave"
33
+ @click="onClick($event)"
47
34
  >
48
- @if(selected)
49
- <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-blue-600 dark:text-blue-400">
50
- <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
51
- <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
52
- </svg>
53
- </span>
54
- @endif
35
+ <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-blue-600 dark:text-blue-400" data-stx-listbox-check>
36
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
37
+ <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
38
+ </svg>
39
+ </span>
55
40
 
56
- <slot :active="active" :selected="selected" :disabled="disabled" />
41
+ <slot :disabled="disabled" />
57
42
  </component>
43
+
44
+ <style>
45
+ [data-stx-listbox-option][data-selected="false"] [data-stx-listbox-check],
46
+ [data-stx-listbox-option]:not([data-selected]) [data-stx-listbox-check] {
47
+ display: none;
48
+ }
49
+ </style>
@@ -1,15 +1,15 @@
1
1
  <script server>
2
2
  export const className = $props.className || ''
3
3
  export const as = $props.as || 'ul'
4
- export const static_ = $props.static || false
5
4
 
6
5
  const baseClasses = 'absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white dark:bg-neutral-800 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none'
7
- const optionsClasses = `${baseClasses} ${className}`.trim()
6
+ export const optionsClasses = `${baseClasses} ${className}`.trim()
8
7
  </script>
9
8
 
10
9
  <component
11
10
  :is="{{ as }}"
12
11
  class="{{ optionsClasses }}"
12
+ data-stx-listbox-options
13
13
  role="listbox"
14
14
  >
15
15
  <slot />