@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
@@ -1,13 +1,96 @@
1
1
  <script server>
2
2
  export const value = $props.value
3
- export const onChange = $props.onChange
4
3
  export const className = $props.className || ''
5
4
  export const as = $props.as || 'div'
6
5
 
7
6
  const baseClasses = 'relative'
8
- const comboboxClasses = `${baseClasses} ${className}`.trim()
7
+ export const comboboxClasses = `${baseClasses} ${className}`.trim()
8
+ export const comboboxInstanceId = `stx-cb-${Math.random().toString(36).slice(2, 11)}`
9
9
  </script>
10
10
 
11
- <component :is="{{ as }}" class="{{ comboboxClasses }}">
12
- <slot :value="value" :onChange="onChange" />
11
+ <script client>
12
+ const emit = defineEmits()
13
+ const comboboxInstanceId = {{ comboboxInstanceId }}
14
+ const initialValue = {{ value === undefined ? 'null' : value }}
15
+
16
+ const isOpen = state(false)
17
+ const selected = state(initialValue)
18
+ const query = state('')
19
+ let rootEl = null
20
+ let outsideHandler = null
21
+
22
+ function isSelectedValue(v) {
23
+ return selected() === v
24
+ }
25
+
26
+ function selectValue(v) {
27
+ selected.set(v)
28
+ isOpen.set(false)
29
+ syncOptionStates()
30
+ emit('change', v)
31
+ }
32
+
33
+ function syncOptionStates() {
34
+ if (!rootEl) return
35
+ const options = rootEl.querySelectorAll('[data-stx-combobox-option]')
36
+ for (const opt of options) {
37
+ const raw = opt.dataset.optionValue
38
+ let v
39
+ try { v = JSON.parse(raw ?? 'null') }
40
+ catch { v = raw }
41
+ const sel = isSelectedValue(v)
42
+ opt.setAttribute('aria-selected', sel ? 'true' : 'false')
43
+ opt.setAttribute('data-selected', sel ? 'true' : 'false')
44
+ }
45
+ }
46
+
47
+ function open() { if (!isOpen()) { isOpen.set(true) } }
48
+ function close() { if (isOpen()) isOpen.set(false) }
49
+ function toggle() { isOpen() ? close() : open() }
50
+
51
+ function onToggle(event) { event.stopPropagation(); toggle() }
52
+ function onSelect(event) { event.stopPropagation(); selectValue(event.detail) }
53
+ function onQuery(event) {
54
+ event.stopPropagation()
55
+ query.set(event.detail)
56
+ if (!isOpen()) open()
57
+ emit('query', event.detail)
58
+ }
59
+
60
+ onMount(() => {
61
+ rootEl = document.querySelector(`[data-combobox-id="${comboboxInstanceId}"]`)
62
+ syncOptionStates()
63
+ outsideHandler = (event) => {
64
+ if (!isOpen()) return
65
+ if (rootEl && !rootEl.contains(event.target)) close()
66
+ }
67
+ document.addEventListener('click', outsideHandler, true)
68
+ })
69
+
70
+ onDestroy(() => {
71
+ if (outsideHandler) document.removeEventListener('click', outsideHandler, true)
72
+ })
73
+
74
+ const dataState = derived(() => isOpen() ? 'open' : 'closed')
75
+
76
+ defineExpose({ isOpen, selected, query, toggle, open, close, selectValue })
77
+ </script>
78
+
79
+ <component
80
+ :is="{{ as }}"
81
+ class="{{ comboboxClasses }}"
82
+ data-stx-combobox
83
+ data-combobox-id="{{ comboboxInstanceId }}"
84
+ :data-state="dataState()"
85
+ @stx:combobox-toggle="onToggle($event)"
86
+ @stx:combobox-select="onSelect($event)"
87
+ @stx:combobox-query="onQuery($event)"
88
+ >
89
+ <slot />
13
90
  </component>
91
+
92
+ <style>
93
+ [data-stx-combobox][data-state="closed"] [data-stx-combobox-options] {
94
+ display: none;
95
+ }
96
+ </style>
@@ -3,10 +3,22 @@ export const className = $props.className || ''
3
3
  export const as = $props.as || 'button'
4
4
 
5
5
  const baseClasses = 'absolute inset-y-0 right-0 flex items-center pr-2'
6
- const buttonClasses = `${baseClasses} ${className}`.trim()
6
+ export const buttonClasses = `${baseClasses} ${className}`.trim()
7
7
  </script>
8
8
 
9
- <component :is="{{ as }}" class="{{ buttonClasses }}" tabindex="-1">
9
+ <script client>
10
+ function onClick(event) {
11
+ event.currentTarget.dispatchEvent(new CustomEvent('stx:combobox-toggle', { bubbles: true }))
12
+ }
13
+ </script>
14
+
15
+ <component
16
+ :is="{{ as }}"
17
+ class="{{ buttonClasses }}"
18
+ data-stx-combobox-button
19
+ tabindex="-1"
20
+ @click="onClick($event)"
21
+ >
10
22
  <svg class="h-5 w-5 text-neutral-400" viewBox="0 0 20 20" fill="none" stroke="currentColor">
11
23
  <path d="M7 7l3-3 3 3m0 6l-3 3-3-3" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
12
24
  </svg>
@@ -1,25 +1,32 @@
1
1
  <script server>
2
2
  export const displayValue = $props.displayValue
3
- export const onChange = $props.onChange
4
3
  export const className = $props.className || ''
5
4
  export const placeholder = $props.placeholder || ''
6
5
  export const disabled = $props.disabled || false
7
6
 
8
7
  const baseClasses = 'w-full rounded-lg border-none bg-white dark:bg-neutral-800 py-2 pl-3 pr-10 text-sm leading-5 text-neutral-900 dark:text-white focus:ring-0 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'
9
8
  const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : ''
10
- const inputClasses = `${baseClasses} ${disabledClasses} ${className}`.trim()
9
+ export const inputClasses = `${baseClasses} ${disabledClasses} ${className}`.trim()
10
+ </script>
11
+
12
+ <script client>
13
+ const isDisabled = {{ disabled }}
11
14
 
12
- export function handleChange(e) {
13
- onChange?.(e.target.value)
15
+ function onInput(event) {
16
+ if (isDisabled) return
17
+ event.currentTarget.dispatchEvent(
18
+ new CustomEvent('stx:combobox-query', { bubbles: true, detail: event.target.value }),
19
+ )
14
20
  }
15
21
  </script>
16
22
 
17
23
  <input
18
24
  type="text"
19
25
  class="{{ inputClasses }}"
26
+ data-stx-combobox-input
20
27
  placeholder="{{ placeholder }}"
21
28
  value="{{ displayValue }}"
22
29
  @if(disabled)disabled@endif
23
- @input="handleChange"
30
+ @input="onInput($event)"
24
31
  autocomplete="off"
25
32
  />
@@ -3,50 +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 = currentValue === value
11
-
12
- const baseClasses = 'relative cursor-pointer select-none py-2 pl-10 pr-4'
13
- const stateClasses = active
14
- ? 'bg-blue-100 text-blue-900 dark:bg-blue-900 dark:text-blue-100'
15
- : 'text-neutral-900 dark:text-neutral-100'
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'
16
8
  const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : ''
17
- const optionClasses = `${baseClasses} ${stateClasses} ${disabledClasses} ${className}`.trim()
9
+ export const optionClasses = `${baseClasses} ${disabledClasses} ${className}`.trim()
10
+ export const valueJson = JSON.stringify(value === undefined ? null : value)
11
+ </script>
18
12
 
19
- export function handleClick() {
20
- if (disabled) return
21
- onChange?.(value)
22
- }
13
+ <script client>
14
+ const isDisabled = {{ disabled }}
15
+ const optionValue = {{ value === undefined ? 'null' : value }}
23
16
 
24
- export function handleMouseEnter() {
25
- if (!disabled) active = true
26
- }
27
-
28
- export function handleMouseLeave() {
29
- active = false
17
+ function onClick(event) {
18
+ if (isDisabled) return
19
+ event.currentTarget.dispatchEvent(
20
+ new CustomEvent('stx:combobox-select', { bubbles: true, detail: optionValue }),
21
+ )
30
22
  }
31
23
  </script>
32
24
 
33
25
  <component
34
26
  :is="{{ as }}"
35
27
  class="{{ optionClasses }}"
28
+ data-stx-combobox-option
29
+ data-option-value="{{ valueJson }}"
36
30
  role="option"
37
- aria-selected="{{ selected ? 'true' : 'false' }}"
31
+ aria-selected="false"
38
32
  @if(disabled)aria-disabled="true"@endif
39
- @click="handleClick"
40
- @mouseenter="handleMouseEnter"
41
- @mouseleave="handleMouseLeave"
33
+ @click="onClick($event)"
42
34
  >
43
- @if(selected)
44
- <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-blue-600 dark:text-blue-400">
45
- <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
46
- <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" />
47
- </svg>
48
- </span>
49
- @endif
35
+ <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-blue-600 dark:text-blue-400" data-stx-combobox-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>
50
40
 
51
- <slot :active="active" :selected="selected" :disabled="disabled" />
41
+ <slot :disabled="disabled" />
52
42
  </component>
43
+
44
+ <style>
45
+ [data-stx-combobox-option][data-selected="false"] [data-stx-combobox-check],
46
+ [data-stx-combobox-option]:not([data-selected]) [data-stx-combobox-check] {
47
+ display: none;
48
+ }
49
+ </style>
@@ -1,12 +1,16 @@
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
- <component :is="{{ as }}" class="{{ optionsClasses }}" role="listbox">
9
+ <component
10
+ :is="{{ as }}"
11
+ class="{{ optionsClasses }}"
12
+ data-stx-combobox-options
13
+ role="listbox"
14
+ >
11
15
  <slot />
12
16
  </component>
@@ -1,39 +1,81 @@
1
1
  <script server>
2
2
  export const open = $props.open || false
3
- export const onClose = $props.onClose
4
- export const query = $props.query || ''
5
- export const onQueryChange = $props.onQueryChange
3
+ export const initialQuery = $props.query || ''
6
4
  export const className = $props.className || ''
7
5
 
8
6
  const baseClasses = 'fixed inset-0 z-50 overflow-y-auto p-4 sm:p-6 md:p-20'
9
- const paletteClasses = `${baseClasses} ${className}`.trim()
7
+ export const paletteClasses = `${baseClasses} ${className}`.trim()
8
+ export const paletteInstanceId = `stx-cmd-${Math.random().toString(36).slice(2, 11)}`
9
+ </script>
10
+
11
+ <script client>
12
+ const emit = defineEmits()
13
+ const paletteInstanceId = {{ paletteInstanceId }}
14
+ const isOpen = state({{ open }})
15
+ const query = state({{ initialQuery }})
16
+ let escHandler = null
17
+
18
+ function close() {
19
+ if (!isOpen()) return
20
+ isOpen.set(false)
21
+ emit('close')
22
+ }
23
+
24
+ function show() {
25
+ if (isOpen()) return
26
+ isOpen.set(true)
27
+ }
28
+
29
+ function onQueryInput(event) {
30
+ query.set(event.target.value)
31
+ emit('query', event.target.value)
32
+ }
10
33
 
11
- export function handleClose() {
12
- onClose?.()
34
+ function onItemSelect(event) {
35
+ event.stopPropagation()
36
+ emit('select', event.detail)
37
+ close()
13
38
  }
39
+
40
+ onMount(() => {
41
+ escHandler = (event) => {
42
+ if (event.key === 'Escape' && isOpen()) close()
43
+ }
44
+ document.addEventListener('keydown', escHandler)
45
+ })
46
+
47
+ onDestroy(() => {
48
+ if (escHandler) document.removeEventListener('keydown', escHandler)
49
+ })
50
+
51
+ defineExpose({ isOpen, query, show, close })
14
52
  </script>
15
53
 
16
- @if(open)
17
- <div class="{{ paletteClasses }}">
18
- <div class="fixed inset-0 bg-neutral-500 bg-opacity-25 dark:bg-opacity-50 transition-opacity" @click="handleClose" />
19
-
20
- <div class="mx-auto max-w-xl transform divide-y divide-neutral-100 dark:divide-neutral-800 overflow-hidden rounded-xl bg-white dark:bg-neutral-900 shadow-2xl ring-1 ring-black ring-opacity-5 transition-all">
21
- <div class="relative">
22
- <svg class="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-neutral-400" viewBox="0 0 20 20" fill="currentColor">
23
- <path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
24
- </svg>
25
- <input
26
- type="text"
27
- class="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-neutral-900 dark:text-white placeholder-neutral-400 focus:ring-0 sm:text-sm"
28
- placeholder="Search..."
29
- value="{{ query }}"
30
- @input="(e) => onQueryChange?.(e.target.value)"
31
- />
32
- </div>
33
-
34
- <div class="max-h-96 overflow-y-auto py-2">
35
- <slot :query="query" />
36
- </div>
54
+ <div
55
+ :show="isOpen()"
56
+ class="{{ paletteClasses }}"
57
+ data-stx-command-palette
58
+ data-palette-id="{{ paletteInstanceId }}"
59
+ @stx:command-select="onItemSelect($event)"
60
+ >
61
+ <div class="fixed inset-0 bg-neutral-500 bg-opacity-25 dark:bg-opacity-50 transition-opacity" @click="close()"></div>
62
+
63
+ <div class="mx-auto max-w-xl transform divide-y divide-neutral-100 dark:divide-neutral-800 overflow-hidden rounded-xl bg-white dark:bg-neutral-900 shadow-2xl ring-1 ring-black ring-opacity-5 transition-all relative">
64
+ <div class="relative">
65
+ <svg class="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-neutral-400" viewBox="0 0 20 20" fill="currentColor">
66
+ <path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
67
+ </svg>
68
+ <input
69
+ type="text"
70
+ class="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-neutral-900 dark:text-white placeholder-neutral-400 focus:ring-0 sm:text-sm"
71
+ placeholder="Search..."
72
+ :value="query()"
73
+ @input="onQueryInput($event)"
74
+ />
75
+ </div>
76
+
77
+ <div class="max-h-96 overflow-y-auto py-2">
78
+ <slot />
37
79
  </div>
38
80
  </div>
39
- @endif
81
+ </div>
@@ -1,32 +1,27 @@
1
1
  <script server>
2
2
  export const className = $props.className || ''
3
- export const onClick = $props.onClick
4
- export let active = false
3
+ export const value = $props.value
5
4
 
6
- const baseClasses = 'flex cursor-pointer select-none items-center px-4 py-2'
7
- const activeClasses = active
8
- ? 'bg-blue-600 text-white'
9
- : 'text-neutral-900 dark:text-neutral-100'
10
- const itemClasses = `${baseClasses} ${activeClasses} ${className}`.trim()
11
-
12
- export function handleClick() {
13
- onClick?.()
14
- }
5
+ const baseClasses = 'flex cursor-pointer select-none items-center px-4 py-2 text-neutral-900 dark:text-neutral-100 hover:bg-blue-600 hover:text-white'
6
+ export const itemClasses = `${baseClasses} ${className}`.trim()
7
+ </script>
15
8
 
16
- export function handleMouseEnter() {
17
- active = true
18
- }
9
+ <script client>
10
+ const emit = defineEmits()
11
+ const itemValue = {{ value === undefined ? 'null' : value }}
19
12
 
20
- export function handleMouseLeave() {
21
- active = false
13
+ function onClick(event) {
14
+ emit('click', event)
15
+ event.currentTarget.dispatchEvent(
16
+ new CustomEvent('stx:command-select', { bubbles: true, detail: itemValue }),
17
+ )
22
18
  }
23
19
  </script>
24
20
 
25
21
  <div
26
22
  class="{{ itemClasses }}"
27
- @click="handleClick"
28
- @mouseenter="handleMouseEnter"
29
- @mouseleave="handleMouseLeave"
23
+ data-stx-command-item
24
+ @click="onClick($event)"
30
25
  >
31
- <slot :active="active" />
26
+ <slot />
32
27
  </div>
@@ -1,19 +1,37 @@
1
1
  <script server>
2
2
  export const open = $props.open || false
3
- export const onClose = $props.onClose
4
3
  export const className = $props.className || ''
5
- export const as = $props.as || 'div'
6
4
 
7
5
  const baseClasses = 'relative z-50'
8
- const dialogClasses = `${baseClasses} ${className}`.trim()
6
+ export const dialogClasses = `${baseClasses} ${className}`.trim()
7
+ </script>
8
+
9
+ <script client>
10
+ const emit = defineEmits()
11
+ const isOpen = state({{ open }})
9
12
 
10
- export function handleClose() {
11
- onClose?.(false)
13
+ function close() {
14
+ isOpen.set(false)
15
+ emit('close')
12
16
  }
17
+
18
+ function onBackdropKey(event) {
19
+ if (event.key === 'Escape') close()
20
+ }
21
+
22
+ defineExpose({
23
+ open: () => isOpen.set(true),
24
+ close,
25
+ isOpen,
26
+ })
13
27
  </script>
14
28
 
15
- @if(open)
16
- <div class="{{ dialogClasses }}" role="dialog" aria-modal="true">
17
- <slot />
18
- </div>
19
- @endif
29
+ <div
30
+ :show="isOpen()"
31
+ class="{{ dialogClasses }}"
32
+ role="dialog"
33
+ aria-modal="true"
34
+ @keydown="onBackdropKey($event)"
35
+ >
36
+ <slot />
37
+ </div>
@@ -1,11 +1,9 @@
1
1
  <script server>
2
2
  export const open = $props.open ?? false
3
- export const onClose = $props.onClose || (() => {})
4
3
  export const position = $props.position || 'right'
5
4
  export const title = $props.title || ''
6
5
  export const className = $props.className || ''
7
6
 
8
- // Position classes
9
7
  const positionClasses = {
10
8
  right: 'inset-y-0 right-0 pl-10 sm:pl-16',
11
9
  left: 'inset-y-0 left-0 pr-10 sm:pr-16',
@@ -13,27 +11,6 @@ const positionClasses = {
13
11
  bottom: 'inset-x-0 bottom-0 pt-10',
14
12
  }
15
13
 
16
- // Slide animation classes
17
- const slideClasses = {
18
- right: {
19
- enter: 'translate-x-full',
20
- leave: 'translate-x-0',
21
- },
22
- left: {
23
- enter: '-translate-x-full',
24
- leave: 'translate-x-0',
25
- },
26
- top: {
27
- enter: '-translate-y-full',
28
- leave: 'translate-y-0',
29
- },
30
- bottom: {
31
- enter: 'translate-y-full',
32
- leave: 'translate-y-0',
33
- },
34
- }
35
-
36
- // Size classes based on position
37
14
  const sizeClasses = {
38
15
  right: 'max-w-md w-screen',
39
16
  left: 'max-w-md w-screen',
@@ -41,7 +18,6 @@ const sizeClasses = {
41
18
  bottom: 'max-h-[90vh] w-full',
42
19
  }
43
20
 
44
- // Close button position
45
21
  const closeButtonPosition = {
46
22
  right: 'left-0 -ml-8 sm:-ml-10',
47
23
  left: 'right-0 -mr-8 sm:-mr-10',
@@ -51,33 +27,52 @@ const closeButtonPosition = {
51
27
 
52
28
  export const containerClasses = `pointer-events-none fixed ${positionClasses[position]} flex max-w-full`.trim()
53
29
  export const panelClasses = `pointer-events-auto relative ${sizeClasses[position]} ${className}`.trim()
54
- export const slideAnimation = slideClasses[position]
55
30
  export const closeButtonClasses = `absolute top-0 ${closeButtonPosition[position]} flex pt-4 pr-2 sm:pr-4`.trim()
56
31
  </script>
57
32
 
58
- @if(open)
59
- <!-- Backdrop -->
33
+ <script client>
34
+ const emit = defineEmits()
35
+ const isOpen = state({{ open }})
36
+
37
+ function close() {
38
+ isOpen.set(false)
39
+ emit('close')
40
+ }
41
+
42
+ function onBackdropClick() {
43
+ close()
44
+ }
45
+
46
+ function onPanelKey(event) {
47
+ if (event.key === 'Escape') close()
48
+ }
49
+
50
+ defineExpose({
51
+ open: () => isOpen.set(true),
52
+ close,
53
+ isOpen,
54
+ })
55
+ </script>
56
+
57
+ <div :show="isOpen()">
60
58
  <div
61
59
  class="fixed inset-0 z-50 bg-gray-500/75 dark:bg-gray-900/75 transition-opacity duration-500"
62
60
  style="animation: fadeIn 0.5s ease-in-out"
63
- @click="handleBackdropClick()"
61
+ @click="onBackdropClick()"
64
62
  ></div>
65
63
 
66
- <!-- Drawer Container -->
67
- <div class="fixed inset-0 z-50 overflow-hidden">
64
+ <div class="fixed inset-0 z-50 overflow-hidden" @keydown="onPanelKey($event)">
68
65
  <div class="absolute inset-0 overflow-hidden">
69
66
  <div class="{{ containerClasses }}">
70
- <!-- Drawer Panel -->
71
67
  <div
72
68
  class="{{ panelClasses }}"
73
69
  style="animation: slideIn 0.5s ease-in-out"
74
70
  >
75
- <!-- Close Button -->
76
71
  <div class="{{ closeButtonClasses }}">
77
72
  <button
78
73
  type="button"
79
74
  class="relative rounded-md text-gray-300 dark:text-gray-400 hover:text-white dark:hover:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
80
- @click="handleClose()"
75
+ @click="close()"
81
76
  >
82
77
  <span class="absolute -inset-2.5"></span>
83
78
  <span class="sr-only">Close panel</span>
@@ -87,7 +82,6 @@ export const closeButtonClasses = `absolute top-0 ${closeButtonPosition[position
87
82
  </button>
88
83
  </div>
89
84
 
90
- <!-- Drawer Content -->
91
85
  <div class="relative flex h-full flex-col overflow-y-auto bg-white dark:bg-blue-gray-800 py-6 shadow-xl">
92
86
  @if(title)
93
87
  <div class="px-4 sm:px-6">
@@ -104,7 +98,7 @@ export const closeButtonClasses = `absolute top-0 ${closeButtonPosition[position
104
98
  </div>
105
99
  </div>
106
100
  </div>
107
- @endif
101
+ </div>
108
102
 
109
103
  <style>
110
104
  @keyframes fadeIn {
@@ -121,17 +115,3 @@ export const closeButtonClasses = `absolute top-0 ${closeButtonPosition[position
121
115
  }
122
116
  }
123
117
  </style>
124
-
125
- <script>
126
- function handleClose() {
127
- if (onClose) {
128
- onClose()
129
- }
130
- }
131
-
132
- function handleBackdropClick() {
133
- if (onClose) {
134
- onClose()
135
- }
136
- }
137
- </script>