@stacksjs/components 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/README.md +477 -0
  2. package/dist/Accordion-x2gjedap.stx +108 -0
  3. package/dist/Audio-0fk6p9tn.stx +50 -0
  4. package/dist/Avatar-5m0v0s4x.stx +88 -0
  5. package/dist/Badge-ynes82vv.stx +70 -0
  6. package/dist/Breadcrumb-h9n427sk.stx +79 -0
  7. package/dist/Button-7xtrzn14.stx +89 -0
  8. package/dist/Calendar-egvx1pma.stx +232 -0
  9. package/dist/Card-r9w7vy1a.stx +74 -0
  10. package/dist/Checkbox-m8v46pmx.stx +92 -0
  11. package/dist/Checkout-vcnnr176.stx +39 -0
  12. package/dist/CodeBlock-mwex82cm.stx +57 -0
  13. package/dist/Combobox-npb08vkp.stx +13 -0
  14. package/dist/ComboboxButton-vcdyq4zw.stx +13 -0
  15. package/dist/ComboboxInput-c9d2p02m.stx +25 -0
  16. package/dist/ComboboxOption-7d0h9ash.stx +52 -0
  17. package/dist/ComboboxOptions-4dtqc0qa.stx +12 -0
  18. package/dist/CommandPalette-ah00bf8v.stx +39 -0
  19. package/dist/CommandPaletteItem-2yx0gvtj.stx +32 -0
  20. package/dist/DefaultPaymentMethod-emw5fg8s.stx +62 -0
  21. package/dist/Dialog-1w4rz7x0.stx +19 -0
  22. package/dist/DialogBackdrop-qbm3y5ab.stx +9 -0
  23. package/dist/DialogDescription-01w5j74b.stx +11 -0
  24. package/dist/DialogPanel-brsszrrs.stx +16 -0
  25. package/dist/DialogTitle-3rxhwg21.stx +11 -0
  26. package/dist/Drawer-yfaz8q7f.stx +137 -0
  27. package/dist/Dropdown-ng7nx556.stx +24 -0
  28. package/dist/DropdownButton-vc4zpyrb.stx +23 -0
  29. package/dist/DropdownItem-z491y23c.stx +43 -0
  30. package/dist/DropdownItems-7ykf29sj.stx +18 -0
  31. package/dist/EmailInput-bfshpj92.stx +41 -0
  32. package/dist/Footer-xdaf02nr.stx +66 -0
  33. package/dist/Form-z4f7w6r1.stx +209 -0
  34. package/dist/Hero-9vvp7pxf.stx +25 -0
  35. package/dist/Image-ktk0451c.stx +197 -0
  36. package/dist/Installation-33bj4rm7.stx +39 -0
  37. package/dist/Listbox-xcejcmyc.stx +14 -0
  38. package/dist/ListboxButton-nx5kmw19.stx +17 -0
  39. package/dist/ListboxLabel-aky8a73n.stx +11 -0
  40. package/dist/ListboxOption-6h4hsdz5.stx +57 -0
  41. package/dist/ListboxOptions-2sacdtm5.stx +16 -0
  42. package/dist/Login-s2jkvd9y.stx +163 -0
  43. package/dist/Navigator-tqqdgdkw.stx +138 -0
  44. package/dist/Notification-sry8egea.stx +71 -0
  45. package/dist/NumberInput-zm93ddfw.stx +138 -0
  46. package/dist/Pagination-yza9v4qw.stx +183 -0
  47. package/dist/PasswordInput-4xnndp5y.stx +155 -0
  48. package/dist/PaymentMethods-jxba6kek.stx +113 -0
  49. package/dist/Popover-scbqatjq.stx +24 -0
  50. package/dist/PopoverButton-rbxhzj4k.stx +23 -0
  51. package/dist/PopoverPanel-9ng1rm6e.stx +23 -0
  52. package/dist/Portal-j111yvqp.stx +74 -0
  53. package/dist/Progress-gbmw3akr.stx +142 -0
  54. package/dist/Radio-1n9qamv1.stx +81 -0
  55. package/dist/RadioGroup-12dw2vhk.stx +13 -0
  56. package/dist/RadioGroupDescription-bjm3mdht.stx +11 -0
  57. package/dist/RadioGroupLabel-aky8a73n.stx +11 -0
  58. package/dist/RadioGroupOption-9pcxgwmd.stx +51 -0
  59. package/dist/SearchInput-g240e2nq.stx +63 -0
  60. package/dist/Select-p9f94b20.stx +89 -0
  61. package/dist/Sidebar-gwxw8q38.stx +288 -0
  62. package/dist/SidebarFooter-887kxret.stx +95 -0
  63. package/dist/SidebarHeader-qjeyx0hv.stx +100 -0
  64. package/dist/SidebarItem-4dpwtmb4.stx +118 -0
  65. package/dist/SidebarSection-kh92fk4m.stx +108 -0
  66. package/dist/Signup-3nxq5zag.stx +161 -0
  67. package/dist/Skeleton-g0nx1njm.stx +48 -0
  68. package/dist/Spinner-w4zj58xp.stx +109 -0
  69. package/dist/Stepper-rwc5actp.stx +17 -0
  70. package/dist/StepperStep-h0k657nm.stx +53 -0
  71. package/dist/SubscriptionCheckout-4cbxdg7w.stx +353 -0
  72. package/dist/Switch-kjq8qd2p.stx +74 -0
  73. package/dist/Table-59j9qwp0.stx +14 -0
  74. package/dist/TableBody-reka2gwv.stx +10 -0
  75. package/dist/TableCell-j7nh4sgr.stx +10 -0
  76. package/dist/TableHead-ecbrwpqm.stx +10 -0
  77. package/dist/TableHeader-jn2shwsv.stx +13 -0
  78. package/dist/TableRow-xz8rrxhc.stx +11 -0
  79. package/dist/Tabs-kb6ex6m7.stx +145 -0
  80. package/dist/Teleport-819cg9zm.stx +101 -0
  81. package/dist/TextInput-zx9kgr79.stx +146 -0
  82. package/dist/Textarea-5xvmrb0z.stx +119 -0
  83. package/dist/Tooltip-5ehqsy49.stx +76 -0
  84. package/dist/Transition-2j39s044.stx +174 -0
  85. package/dist/TwoFactorChallenge-2q4gg89m.stx +125 -0
  86. package/dist/Video-6aq4shh4.stx +45 -0
  87. package/dist/VirtualList-weh5qtm1.stx +83 -0
  88. package/dist/VirtualTable-59a1eskc.stx +113 -0
  89. package/dist/components/index.d.ts +4 -0
  90. package/dist/composables/index.d.ts +4 -0
  91. package/dist/composables/index.js +3 -0
  92. package/dist/composables/useCopyCode.d.ts +19 -0
  93. package/dist/composables/useDarkMode.d.ts +25 -0
  94. package/dist/composables/useSEO.d.ts +36 -0
  95. package/dist/composables/utilities/index.d.ts +5 -0
  96. package/dist/composables/utilities/useClickOutside.d.ts +28 -0
  97. package/dist/composables/utilities/useFocusTrap.d.ts +34 -0
  98. package/dist/composables/utilities/useKeyboard.d.ts +55 -0
  99. package/dist/composables/utilities/useLocalStorage.d.ts +33 -0
  100. package/dist/composables/utilities/useMediaQuery.d.ts +64 -0
  101. package/dist/index.d.ts +86 -0
  102. package/dist/index.js +19 -0
  103. package/dist/ui/accordion/index.d.ts +12 -0
  104. package/dist/ui/audio/index.d.ts +12 -0
  105. package/dist/ui/auth/index.d.ts +29 -0
  106. package/dist/ui/avatar/index.d.ts +11 -0
  107. package/dist/ui/badge/index.d.ts +9 -0
  108. package/dist/ui/breadcrumb/index.d.ts +13 -0
  109. package/dist/ui/button/index.d.ts +45 -0
  110. package/dist/ui/calendar/index.d.ts +13 -0
  111. package/dist/ui/card/index.d.ts +12 -0
  112. package/dist/ui/checkbox/index.d.ts +15 -0
  113. package/dist/ui/combobox/index.d.ts +35 -0
  114. package/dist/ui/command-palette/index.d.ts +13 -0
  115. package/dist/ui/dialog/index.d.ts +27 -0
  116. package/dist/ui/drawer/index.d.ts +8 -0
  117. package/dist/ui/dropdown/index.d.ts +25 -0
  118. package/dist/ui/form/index.d.ts +48 -0
  119. package/dist/ui/image/index.d.ts +21 -0
  120. package/dist/ui/input/index.d.ts +73 -0
  121. package/dist/ui/listbox/index.d.ts +34 -0
  122. package/dist/ui/navigator/index.d.ts +18 -0
  123. package/dist/ui/notification/index.d.ts +11 -0
  124. package/dist/ui/pagination/index.d.ts +10 -0
  125. package/dist/ui/payment/index.d.ts +53 -0
  126. package/dist/ui/popover/index.d.ts +19 -0
  127. package/dist/ui/portal/index.d.ts +5 -0
  128. package/dist/ui/progress/index.d.ts +11 -0
  129. package/dist/ui/radio/index.d.ts +14 -0
  130. package/dist/ui/radio-group/index.d.ts +26 -0
  131. package/dist/ui/select/index.d.ts +19 -0
  132. package/dist/ui/sidebar/index.d.ts +129 -0
  133. package/dist/ui/skeleton/index.d.ts +10 -0
  134. package/dist/ui/spinner/index.d.ts +8 -0
  135. package/dist/ui/stepper/index.d.ts +17 -0
  136. package/dist/ui/storage/index.d.ts +40 -0
  137. package/dist/ui/switch/index.d.ts +9 -0
  138. package/dist/ui/table/index.d.ts +29 -0
  139. package/dist/ui/tabs/index.d.ts +14 -0
  140. package/dist/ui/teleport/index.d.ts +6 -0
  141. package/dist/ui/textarea/index.d.ts +19 -0
  142. package/dist/ui/tooltip/index.d.ts +9 -0
  143. package/dist/ui/transition/index.d.ts +24 -0
  144. package/dist/ui/video/index.d.ts +15 -0
  145. package/dist/ui/virtual-list/index.d.ts +9 -0
  146. package/dist/ui/virtual-table/index.d.ts +19 -0
  147. package/dist/utils/accessibility.d.ts +285 -0
  148. package/dist/utils/animation/easing.d.ts +112 -0
  149. package/dist/utils/animation/helpers.d.ts +119 -0
  150. package/dist/utils/animation/index.d.ts +3 -0
  151. package/dist/utils/animation/keyframes.d.ts +334 -0
  152. package/dist/utils/error-handling.d.ts +182 -0
  153. package/dist/utils/highlighter.d.ts +27 -0
  154. package/dist/utils/highlighter.js +2 -0
  155. package/dist/utils/i18n.d.ts +245 -0
  156. package/dist/utils/keep-alive.d.ts +171 -0
  157. package/dist/utils/lazy-loading.d.ts +174 -0
  158. package/dist/utils/prop-validation.d.ts +106 -0
  159. package/dist/utils/theme.d.ts +234 -0
  160. package/package.json +69 -0
  161. package/src/components/CodeBlock.stx +57 -0
  162. package/src/components/Footer.stx +66 -0
  163. package/src/components/Hero.stx +25 -0
  164. package/src/components/Installation.stx +39 -0
  165. package/src/components/index.ts +4 -0
  166. package/src/composables/index.ts +4 -0
  167. package/src/composables/useCopyCode.ts +73 -0
  168. package/src/composables/useDarkMode.ts +100 -0
  169. package/src/composables/useSEO.ts +229 -0
  170. package/src/composables/utilities/index.ts +7 -0
  171. package/src/composables/utilities/useClickOutside.ts +72 -0
  172. package/src/composables/utilities/useFocusTrap.ts +159 -0
  173. package/src/composables/utilities/useKeyboard.ts +124 -0
  174. package/src/composables/utilities/useLocalStorage.ts +132 -0
  175. package/src/composables/utilities/useMediaQuery.ts +118 -0
  176. package/src/index.ts +99 -0
  177. package/src/stx.d.ts +4 -0
  178. package/src/ui/accordion/Accordion.stx +108 -0
  179. package/src/ui/accordion/index.ts +14 -0
  180. package/src/ui/audio/Audio.stx +50 -0
  181. package/src/ui/audio/README.md +42 -0
  182. package/src/ui/audio/index.ts +13 -0
  183. package/src/ui/auth/Login.stx +163 -0
  184. package/src/ui/auth/README.md +145 -0
  185. package/src/ui/auth/Signup.stx +161 -0
  186. package/src/ui/auth/TwoFactorChallenge.stx +125 -0
  187. package/src/ui/auth/index.ts +32 -0
  188. package/src/ui/avatar/Avatar.stx +88 -0
  189. package/src/ui/avatar/index.ts +12 -0
  190. package/src/ui/badge/Badge.stx +70 -0
  191. package/src/ui/badge/index.ts +10 -0
  192. package/src/ui/breadcrumb/Breadcrumb.stx +79 -0
  193. package/src/ui/breadcrumb/index.ts +15 -0
  194. package/src/ui/button/Button.stx +89 -0
  195. package/src/ui/button/README.md +100 -0
  196. package/src/ui/button/index.ts +85 -0
  197. package/src/ui/calendar/Calendar.stx +232 -0
  198. package/src/ui/calendar/README.md +254 -0
  199. package/src/ui/calendar/index.ts +14 -0
  200. package/src/ui/card/Card.stx +74 -0
  201. package/src/ui/card/index.ts +13 -0
  202. package/src/ui/checkbox/Checkbox.stx +92 -0
  203. package/src/ui/checkbox/index.ts +16 -0
  204. package/src/ui/combobox/Combobox.stx +13 -0
  205. package/src/ui/combobox/ComboboxButton.stx +13 -0
  206. package/src/ui/combobox/ComboboxInput.stx +25 -0
  207. package/src/ui/combobox/ComboboxOption.stx +52 -0
  208. package/src/ui/combobox/ComboboxOptions.stx +12 -0
  209. package/src/ui/combobox/README.md +154 -0
  210. package/src/ui/combobox/index.ts +40 -0
  211. package/src/ui/command-palette/CommandPalette.stx +39 -0
  212. package/src/ui/command-palette/CommandPaletteItem.stx +32 -0
  213. package/src/ui/command-palette/README.md +42 -0
  214. package/src/ui/command-palette/index.ts +15 -0
  215. package/src/ui/dialog/Dialog.stx +19 -0
  216. package/src/ui/dialog/DialogBackdrop.stx +9 -0
  217. package/src/ui/dialog/DialogDescription.stx +11 -0
  218. package/src/ui/dialog/DialogPanel.stx +16 -0
  219. package/src/ui/dialog/DialogTitle.stx +11 -0
  220. package/src/ui/dialog/README.md +133 -0
  221. package/src/ui/dialog/index.ts +32 -0
  222. package/src/ui/drawer/Drawer.stx +137 -0
  223. package/src/ui/drawer/README.md +110 -0
  224. package/src/ui/drawer/index.ts +9 -0
  225. package/src/ui/dropdown/Dropdown.stx +24 -0
  226. package/src/ui/dropdown/DropdownButton.stx +23 -0
  227. package/src/ui/dropdown/DropdownItem.stx +43 -0
  228. package/src/ui/dropdown/DropdownItems.stx +18 -0
  229. package/src/ui/dropdown/README.md +142 -0
  230. package/src/ui/dropdown/index.ts +29 -0
  231. package/src/ui/form/Form.stx +209 -0
  232. package/src/ui/form/index.ts +67 -0
  233. package/src/ui/heatmap/Heatmap.stx +315 -0
  234. package/src/ui/image/Image.stx +197 -0
  235. package/src/ui/image/README.md +171 -0
  236. package/src/ui/image/index.ts +22 -0
  237. package/src/ui/input/EmailInput.stx +41 -0
  238. package/src/ui/input/NumberInput.stx +138 -0
  239. package/src/ui/input/PasswordInput.stx +155 -0
  240. package/src/ui/input/SearchInput.stx +63 -0
  241. package/src/ui/input/TextInput.stx +146 -0
  242. package/src/ui/input/index.ts +180 -0
  243. package/src/ui/listbox/Listbox.stx +14 -0
  244. package/src/ui/listbox/ListboxButton.stx +17 -0
  245. package/src/ui/listbox/ListboxLabel.stx +11 -0
  246. package/src/ui/listbox/ListboxOption.stx +57 -0
  247. package/src/ui/listbox/ListboxOptions.stx +16 -0
  248. package/src/ui/listbox/README.md +143 -0
  249. package/src/ui/listbox/index.ts +39 -0
  250. package/src/ui/navigator/Navigator.stx +138 -0
  251. package/src/ui/navigator/README.md +352 -0
  252. package/src/ui/navigator/index.ts +20 -0
  253. package/src/ui/notification/Notification.stx +71 -0
  254. package/src/ui/notification/README.md +118 -0
  255. package/src/ui/notification/index.ts +12 -0
  256. package/src/ui/pagination/Pagination.stx +183 -0
  257. package/src/ui/pagination/index.ts +11 -0
  258. package/src/ui/payment/Checkout.stx +39 -0
  259. package/src/ui/payment/DefaultPaymentMethod.stx +62 -0
  260. package/src/ui/payment/PaymentMethods.stx +113 -0
  261. package/src/ui/payment/README.md +427 -0
  262. package/src/ui/payment/SubscriptionCheckout.stx +353 -0
  263. package/src/ui/payment/index.ts +59 -0
  264. package/src/ui/popover/Popover.stx +24 -0
  265. package/src/ui/popover/PopoverButton.stx +23 -0
  266. package/src/ui/popover/PopoverPanel.stx +23 -0
  267. package/src/ui/popover/README.md +95 -0
  268. package/src/ui/popover/index.ts +22 -0
  269. package/src/ui/portal/Portal.stx +74 -0
  270. package/src/ui/portal/index.ts +6 -0
  271. package/src/ui/progress/Progress.stx +142 -0
  272. package/src/ui/progress/index.ts +12 -0
  273. package/src/ui/radio/Radio.stx +81 -0
  274. package/src/ui/radio/index.ts +15 -0
  275. package/src/ui/radio-group/README.md +145 -0
  276. package/src/ui/radio-group/RadioGroup.stx +13 -0
  277. package/src/ui/radio-group/RadioGroupDescription.stx +11 -0
  278. package/src/ui/radio-group/RadioGroupLabel.stx +11 -0
  279. package/src/ui/radio-group/RadioGroupOption.stx +51 -0
  280. package/src/ui/radio-group/index.ts +30 -0
  281. package/src/ui/select/README.md +204 -0
  282. package/src/ui/select/Select.stx +89 -0
  283. package/src/ui/select/index.ts +21 -0
  284. package/src/ui/sidebar/Sidebar.stx +288 -0
  285. package/src/ui/sidebar/SidebarFooter.stx +95 -0
  286. package/src/ui/sidebar/SidebarHeader.stx +100 -0
  287. package/src/ui/sidebar/SidebarItem.stx +118 -0
  288. package/src/ui/sidebar/SidebarSection.stx +108 -0
  289. package/src/ui/sidebar/index.ts +175 -0
  290. package/src/ui/skeleton/Skeleton.stx +48 -0
  291. package/src/ui/skeleton/index.ts +11 -0
  292. package/src/ui/spinner/Spinner.stx +109 -0
  293. package/src/ui/spinner/index.ts +9 -0
  294. package/src/ui/stepper/README.md +36 -0
  295. package/src/ui/stepper/Stepper.stx +17 -0
  296. package/src/ui/stepper/StepperStep.stx +53 -0
  297. package/src/ui/stepper/index.ts +19 -0
  298. package/src/ui/storage/README.md +193 -0
  299. package/src/ui/storage/index.ts +347 -0
  300. package/src/ui/switch/README.md +133 -0
  301. package/src/ui/switch/Switch.stx +74 -0
  302. package/src/ui/switch/index.ts +10 -0
  303. package/src/ui/table/README.md +39 -0
  304. package/src/ui/table/Table.stx +14 -0
  305. package/src/ui/table/TableBody.stx +10 -0
  306. package/src/ui/table/TableCell.stx +10 -0
  307. package/src/ui/table/TableHead.stx +10 -0
  308. package/src/ui/table/TableHeader.stx +13 -0
  309. package/src/ui/table/TableRow.stx +11 -0
  310. package/src/ui/table/index.ts +35 -0
  311. package/src/ui/tabs/Tabs.stx +145 -0
  312. package/src/ui/tabs/index.ts +16 -0
  313. package/src/ui/teleport/Teleport.stx +101 -0
  314. package/src/ui/teleport/index.ts +7 -0
  315. package/src/ui/textarea/Textarea.stx +119 -0
  316. package/src/ui/textarea/index.ts +20 -0
  317. package/src/ui/tooltip/Tooltip.stx +76 -0
  318. package/src/ui/tooltip/index.ts +10 -0
  319. package/src/ui/transition/README.md +30 -0
  320. package/src/ui/transition/Transition.stx +174 -0
  321. package/src/ui/transition/index.ts +25 -0
  322. package/src/ui/video/README.md +48 -0
  323. package/src/ui/video/Video.stx +45 -0
  324. package/src/ui/video/index.ts +16 -0
  325. package/src/ui/virtual-list/VirtualList.stx +83 -0
  326. package/src/ui/virtual-list/index.ts +10 -0
  327. package/src/ui/virtual-table/VirtualTable.stx +113 -0
  328. package/src/ui/virtual-table/index.ts +21 -0
  329. package/src/utils/PROP_VALIDATION.md +458 -0
  330. package/src/utils/THEME_SYSTEM.md +702 -0
  331. package/src/utils/accessibility.ts +841 -0
  332. package/src/utils/animation/easing.ts +368 -0
  333. package/src/utils/animation/helpers.ts +223 -0
  334. package/src/utils/animation/index.ts +5 -0
  335. package/src/utils/animation/keyframes.ts +230 -0
  336. package/src/utils/error-handling.ts +473 -0
  337. package/src/utils/highlighter.ts +93 -0
  338. package/src/utils/i18n.ts +634 -0
  339. package/src/utils/keep-alive.ts +818 -0
  340. package/src/utils/lazy-loading.ts +530 -0
  341. package/src/utils/prop-validation.ts +478 -0
  342. package/src/utils/theme.ts +626 -0
@@ -0,0 +1,143 @@
1
+ # Listbox Component
2
+
3
+ A custom select component for choosing one or multiple values from a list.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @stacksjs/components
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```stx
14
+ <script>
15
+ export const people = [
16
+ { id: 1, name: 'Wade Cooper' },
17
+ { id: 2, name: 'Arlene Mccoy' },
18
+ { id: 3, name: 'Devon Webb' }
19
+ ]
20
+
21
+ export let selected = people[0]
22
+
23
+ export function handleChange(person) {
24
+ selected = person
25
+ }
26
+ </script>
27
+
28
+ @component('Listbox', { value: selected, onChange: handleChange })
29
+ @component('ListboxLabel')
30
+ Assign to
31
+ @endcomponent
32
+
33
+ @component('ListboxButton')
34
+ {{ selected.name }}
35
+ @endcomponent
36
+
37
+ @component('ListboxOptions')
38
+ @foreach(person in people)
39
+ @component('ListboxOption', {
40
+ value: person,
41
+ currentValue: selected,
42
+ onChange: handleChange
43
+ })
44
+ {{ person.name }}
45
+ @endcomponent
46
+ @endforeach
47
+ @endcomponent
48
+ @endcomponent
49
+ ```
50
+
51
+ ## Props
52
+
53
+ ### Listbox
54
+ | Prop | Type | Default | Description |
55
+ |------|------|---------|-------------|
56
+ | `value` | `any` | - | Currently selected value(s) |
57
+ | `onChange` | `(value: any) => void` | - | Change handler |
58
+ | `multiple` | `boolean` | `false` | Allow multiple selection |
59
+ | `className` | `string` | `''` | Additional CSS classes |
60
+ | `as` | `string` | `'div'` | HTML element to render |
61
+
62
+ ### ListboxButton
63
+ | Prop | Type | Default | Description |
64
+ |------|------|---------|-------------|
65
+ | `className` | `string` | `''` | Additional CSS classes |
66
+ | `as` | `string` | `'button'` | HTML element to render |
67
+ | `disabled` | `boolean` | `false` | Disable the button |
68
+
69
+ ### ListboxOptions
70
+ | Prop | Type | Default | Description |
71
+ |------|------|---------|-------------|
72
+ | `className` | `string` | `''` | Additional CSS classes |
73
+ | `as` | `string` | `'ul'` | HTML element to render |
74
+ | `static` | `boolean` | `false` | Keep visible regardless of state |
75
+
76
+ ### ListboxOption
77
+ | Prop | Type | Default | Description |
78
+ |------|------|---------|-------------|
79
+ | `value` | `any` | - | Option value |
80
+ | `disabled` | `boolean` | `false` | Disable the option |
81
+ | `className` | `string` | `''` | Additional CSS classes |
82
+ | `as` | `string` | `'li'` | HTML element to render |
83
+ | `currentValue` | `any` | - | Current selected value(s) |
84
+ | `onChange` | `(value: any) => void` | - | Change handler |
85
+
86
+ ### ListboxLabel
87
+ | Prop | Type | Default | Description |
88
+ |------|------|---------|-------------|
89
+ | `className` | `string` | `''` | Additional CSS classes |
90
+ | `as` | `string` | `'label'` | HTML element to render |
91
+
92
+ ## Examples
93
+
94
+ ### Multiple Selection
95
+
96
+ ```stx
97
+ <script>
98
+ export let selectedPeople = [people[0], people[1]]
99
+
100
+ export function handleChange(person) {
101
+ if (selectedPeople.includes(person)) {
102
+ selectedPeople = selectedPeople.filter(p => p !== person)
103
+ } else {
104
+ selectedPeople = [...selectedPeople, person]
105
+ }
106
+ }
107
+ </script>
108
+
109
+ @component('Listbox', { value: selectedPeople, onChange: handleChange, multiple: true })
110
+ <!-- ... -->
111
+ @endcomponent
112
+ ```
113
+
114
+ ### With Avatars
115
+
116
+ ```stx
117
+ @component('ListboxOption', { value: person })
118
+ <div class="flex items-center">
119
+ <img src="{{ person.avatar }}" alt="" class="h-6 w-6 rounded-full mr-2" />
120
+ {{ person.name }}
121
+ </div>
122
+ @endcomponent
123
+ ```
124
+
125
+ ### Disabled Option
126
+
127
+ ```stx
128
+ @component('ListboxOption', { value: person, disabled: true })
129
+ {{ person.name }} (unavailable)
130
+ @endcomponent
131
+ ```
132
+
133
+ ## Features
134
+
135
+ - Single or multiple selection
136
+ - Keyboard navigation
137
+ - Search/filter support
138
+ - Dark mode support
139
+ - Accessible (WAI-ARIA)
140
+ - Disabled state
141
+ - Active state on hover
142
+ - Check icon for selected items
143
+ - Customizable styling
@@ -0,0 +1,39 @@
1
+ export { default as Listbox } from './Listbox.stx'
2
+ export { default as ListboxButton } from './ListboxButton.stx'
3
+ export { default as ListboxLabel } from './ListboxLabel.stx'
4
+ export { default as ListboxOption } from './ListboxOption.stx'
5
+ export { default as ListboxOptions } from './ListboxOptions.stx'
6
+
7
+ export interface ListboxProps {
8
+ value?: any
9
+ onChange?: (value: any) => void
10
+ multiple?: boolean
11
+ className?: string
12
+ as?: string
13
+ }
14
+
15
+ export interface ListboxButtonProps {
16
+ className?: string
17
+ as?: string
18
+ disabled?: boolean
19
+ }
20
+
21
+ export interface ListboxOptionsProps {
22
+ className?: string
23
+ as?: string
24
+ static?: boolean
25
+ }
26
+
27
+ export interface ListboxOptionProps {
28
+ value: any
29
+ disabled?: boolean
30
+ className?: string
31
+ as?: string
32
+ currentValue?: any
33
+ onChange?: (value: any) => void
34
+ }
35
+
36
+ export interface ListboxLabelProps {
37
+ className?: string
38
+ as?: string
39
+ }
@@ -0,0 +1,138 @@
1
+ <script>
2
+ // Navigator Component Props
3
+ export const items = $props.items || []
4
+ export const active = $props.active || ''
5
+ export const orientation = $props.orientation || 'horizontal' // 'horizontal' | 'vertical'
6
+ export const variant = $props.variant || 'default' // 'default' | 'pills' | 'underline' | 'sidebar'
7
+ export const size = $props.size || 'md' // 'sm' | 'md' | 'lg'
8
+ export const onNavigate = $props.onNavigate
9
+ export const className = $props.className || ''
10
+
11
+ // Helper function to check if item is active
12
+ function isActive(item) {
13
+ if (typeof item.href === 'string' && typeof active === 'string') {
14
+ // Exact match
15
+ if (item.href === active) return true
16
+ // Match with query params/hash
17
+ if (active.startsWith(item.href)) return true
18
+ }
19
+ if (item.id && item.id === active) return true
20
+ return false
21
+ }
22
+
23
+ // Base classes
24
+ const containerBaseClasses = orientation === 'horizontal'
25
+ ? 'flex items-center'
26
+ : 'flex flex-col'
27
+
28
+ const containerClasses = `${containerBaseClasses} ${className}`.trim()
29
+
30
+ // Item classes based on variant and size
31
+ function getItemClasses(item) {
32
+ const active = isActive(item)
33
+ let classes = ''
34
+
35
+ // Size classes
36
+ const sizeClasses = {
37
+ sm: 'px-2 py-1 text-sm',
38
+ md: 'px-3 py-2 text-base',
39
+ lg: 'px-4 py-3 text-lg',
40
+ }
41
+
42
+ classes += sizeClasses[size] || sizeClasses.md
43
+
44
+ // Variant classes
45
+ if (variant === 'default') {
46
+ classes += active
47
+ ? ' text-gray-900 dark:text-gray-100 font-medium'
48
+ : ' text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100'
49
+ } else if (variant === 'pills') {
50
+ classes += active
51
+ ? ' bg-blue-600 text-white font-medium rounded-full'
52
+ : ' text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full'
53
+ } else if (variant === 'underline') {
54
+ classes += active
55
+ ? ' text-blue-600 dark:text-blue-400 font-medium border-b-2 border-blue-600 dark:border-blue-400'
56
+ : ' text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 border-b-2 border-transparent'
57
+ } else if (variant === 'sidebar') {
58
+ classes += active
59
+ ? ' bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 font-medium border-l-4 border-blue-600 dark:border-blue-400'
60
+ : ' text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 border-l-4 border-transparent'
61
+ }
62
+
63
+ // Common classes
64
+ classes += ' transition-all duration-200 cursor-pointer flex items-center gap-2'
65
+
66
+ if (item.disabled) {
67
+ classes += ' opacity-50 cursor-not-allowed pointer-events-none'
68
+ }
69
+
70
+ return classes.trim()
71
+ }
72
+
73
+ // Gap classes between items
74
+ const gapClasses = orientation === 'horizontal'
75
+ ? 'gap-1'
76
+ : 'gap-0.5'
77
+
78
+ function handleClick(item, event) {
79
+ if (item.disabled) {
80
+ event.preventDefault()
81
+ return
82
+ }
83
+
84
+ if (onNavigate) {
85
+ event.preventDefault()
86
+ onNavigate(item)
87
+ }
88
+ }
89
+ </script>
90
+
91
+ <nav class="{{ containerClasses }} {{ gapClasses }}" role="navigation" aria-label="Main navigation">
92
+ @foreach(item in items)
93
+ @if(item.href)
94
+ <a
95
+ href="{{ item.href }}"
96
+ class="{{ getItemClasses(item) }}"
97
+ onclick="handleClick(item, event)"
98
+ @if(isActive(item))
99
+ aria-current="page"
100
+ @endif
101
+ @if(item.disabled)
102
+ aria-disabled="true"
103
+ tabindex="-1"
104
+ @endif
105
+ >
106
+ @if(item.icon)
107
+ <span class="flex-shrink-0" innerHTML="{!! item.icon !!}"></span>
108
+ @endif
109
+ <span>{{ item.label }}</span>
110
+ @if(item.badge)
111
+ <span class="ml-auto px-2 py-0.5 text-xs font-semibold rounded-full bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300">
112
+ {{ item.badge }}
113
+ </span>
114
+ @endif
115
+ </a>
116
+ @else
117
+ <button
118
+ type="button"
119
+ class="{{ getItemClasses(item) }}"
120
+ onclick="handleClick(item, event)"
121
+ @if(item.disabled)
122
+ disabled
123
+ aria-disabled="true"
124
+ @endif
125
+ >
126
+ @if(item.icon)
127
+ <span class="flex-shrink-0" innerHTML="{!! item.icon !!}"></span>
128
+ @endif
129
+ <span>{{ item.label }}</span>
130
+ @if(item.badge)
131
+ <span class="ml-auto px-2 py-0.5 text-xs font-semibold rounded-full bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300">
132
+ {{ item.badge }}
133
+ </span>
134
+ @endif
135
+ </button>
136
+ @endif
137
+ @endforeach
138
+ </nav>
@@ -0,0 +1,352 @@
1
+ # Navigator Component
2
+
3
+ A versatile navigation component that supports multiple orientations, variants, and styles. Perfect for main navigation, sidebars, tabs, and more.
4
+
5
+ ## Features
6
+
7
+ - ✅ Multiple variants (default, pills, underline, sidebar)
8
+ - ✅ Horizontal and vertical orientations
9
+ - ✅ Active state handling
10
+ - ✅ Icon support
11
+ - ✅ Badge support
12
+ - ✅ Disabled states
13
+ - ✅ Multiple sizes (sm, md, lg)
14
+ - ✅ Custom navigation handlers
15
+ - ✅ Dark mode support
16
+ - ✅ Accessible with ARIA labels
17
+ - ✅ Keyboard navigation support
18
+
19
+ ## Usage
20
+
21
+ ### Basic Horizontal Navigation
22
+
23
+ ```stx
24
+ <script>
25
+ import { Navigator } from '@stacksjs/components'
26
+
27
+ const items = [
28
+ { id: 'home', label: 'Home', href: '/' },
29
+ { id: 'about', label: 'About', href: '/about' },
30
+ { id: 'contact', label: 'Contact', href: '/contact' },
31
+ ]
32
+
33
+ let activeItem = 'home'
34
+ </script>
35
+
36
+ <Navigator items={items} active={activeItem} />
37
+ ```
38
+
39
+ ### Vertical Sidebar Navigation
40
+
41
+ ```stx
42
+ <script>
43
+ import { Navigator } from '@stacksjs/components'
44
+
45
+ const items = [
46
+ { id: 'dashboard', label: 'Dashboard', href: '/dashboard' },
47
+ { id: 'projects', label: 'Projects', href: '/projects' },
48
+ { id: 'team', label: 'Team', href: '/team' },
49
+ { id: 'settings', label: 'Settings', href: '/settings' },
50
+ ]
51
+ </script>
52
+
53
+ <Navigator
54
+ items={items}
55
+ active="dashboard"
56
+ orientation="vertical"
57
+ variant="sidebar"
58
+ />
59
+ ```
60
+
61
+ ### Pills Navigation
62
+
63
+ ```stx
64
+ <script>
65
+ import { Navigator } from '@stacksjs/components'
66
+
67
+ const items = [
68
+ { id: 'overview', label: 'Overview', href: '/overview' },
69
+ { id: 'analytics', label: 'Analytics', href: '/analytics' },
70
+ { id: 'reports', label: 'Reports', href: '/reports' },
71
+ ]
72
+ </script>
73
+
74
+ <Navigator
75
+ items={items}
76
+ active="overview"
77
+ variant="pills"
78
+ />
79
+ ```
80
+
81
+ ### Underline Tabs
82
+
83
+ ```stx
84
+ <script>
85
+ import { Navigator } from '@stacksjs/components'
86
+
87
+ const tabs = [
88
+ { id: 'profile', label: 'Profile', href: '#profile' },
89
+ { id: 'security', label: 'Security', href: '#security' },
90
+ { id: 'notifications', label: 'Notifications', href: '#notifications' },
91
+ ]
92
+ </script>
93
+
94
+ <Navigator
95
+ items={tabs}
96
+ active="profile"
97
+ variant="underline"
98
+ />
99
+ ```
100
+
101
+ ### With Icons
102
+
103
+ ```stx
104
+ <script>
105
+ import { Navigator } from '@stacksjs/components'
106
+
107
+ const items = [
108
+ {
109
+ id: 'home',
110
+ label: 'Home',
111
+ href: '/',
112
+ icon: '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" /></svg>',
113
+ },
114
+ {
115
+ id: 'search',
116
+ label: 'Search',
117
+ href: '/search',
118
+ icon: '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><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>',
119
+ },
120
+ ]
121
+ </script>
122
+
123
+ <Navigator items={items} active="home" />
124
+ ```
125
+
126
+ ### With Badges
127
+
128
+ ```stx
129
+ <script>
130
+ import { Navigator } from '@stacksjs/components'
131
+
132
+ const items = [
133
+ { id: 'inbox', label: 'Inbox', href: '/inbox', badge: '12' },
134
+ { id: 'drafts', label: 'Drafts', href: '/drafts', badge: '3' },
135
+ { id: 'sent', label: 'Sent', href: '/sent' },
136
+ ]
137
+ </script>
138
+
139
+ <Navigator items={items} active="inbox" variant="sidebar" orientation="vertical" />
140
+ ```
141
+
142
+ ### With Disabled Items
143
+
144
+ ```stx
145
+ <script>
146
+ import { Navigator } from '@stacksjs/components'
147
+
148
+ const items = [
149
+ { id: 'enabled', label: 'Enabled', href: '/enabled' },
150
+ { id: 'disabled', label: 'Disabled (Coming Soon)', href: '/disabled', disabled: true },
151
+ { id: 'another', label: 'Another Link', href: '/another' },
152
+ ]
153
+ </script>
154
+
155
+ <Navigator items={items} active="enabled" />
156
+ ```
157
+
158
+ ### With Custom Navigation Handler
159
+
160
+ ```stx
161
+ <script>
162
+ import { Navigator } from '@stacksjs/components'
163
+
164
+ const items = [
165
+ { id: 'tab1', label: 'Tab 1' },
166
+ { id: 'tab2', label: 'Tab 2' },
167
+ { id: 'tab3', label: 'Tab 3' },
168
+ ]
169
+
170
+ let activeTab = 'tab1'
171
+
172
+ function handleNavigate(item) {
173
+ activeTab = item.id
174
+ console.log('Navigated to:', item.label)
175
+ // Custom logic here (e.g., switch content, fetch data, etc.)
176
+ }
177
+ </script>
178
+
179
+ <Navigator
180
+ items={items}
181
+ active={activeTab}
182
+ onNavigate={handleNavigate}
183
+ variant="underline"
184
+ />
185
+ ```
186
+
187
+ ## Props
188
+
189
+ | Prop | Type | Default | Description |
190
+ |------|------|---------|-------------|
191
+ | `items` | `NavigatorItem[]` | `[]` | Array of navigation items |
192
+ | `active` | `string` | `''` | ID or href of the currently active item |
193
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout orientation |
194
+ | `variant` | `'default' \| 'pills' \| 'underline' \| 'sidebar'` | `'default'` | Visual style variant |
195
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of navigation items |
196
+ | `onNavigate` | `(item: NavigatorItem) => void` | - | Custom navigation handler (prevents default link behavior) |
197
+ | `className` | `string` | `''` | Additional CSS classes |
198
+
199
+ ### NavigatorItem Interface
200
+
201
+ ```typescript
202
+ interface NavigatorItem {
203
+ id?: string // Unique identifier
204
+ label: string // Display text
205
+ href?: string // Link URL (renders as <a> if provided, <button> otherwise)
206
+ icon?: string // SVG icon HTML
207
+ badge?: string | number // Badge text/number
208
+ disabled?: boolean // Whether the item is disabled
209
+ }
210
+ ```
211
+
212
+ ## Variants
213
+
214
+ ### Default
215
+ Clean, simple navigation with subtle hover effects.
216
+
217
+ ### Pills
218
+ Rounded pill-style navigation with filled active state.
219
+
220
+ ### Underline
221
+ Tab-style navigation with bottom border indicator.
222
+
223
+ ### Sidebar
224
+ Left-bordered navigation optimized for vertical sidebars.
225
+
226
+ ## Sizes
227
+
228
+ - **sm**: Compact padding and text
229
+ - **md**: Default comfortable size
230
+ - **lg**: Large padding and text
231
+
232
+ ## Examples
233
+
234
+ ### App Header Navigation
235
+
236
+ ```stx
237
+ <script>
238
+ import { Navigator } from '@stacksjs/components'
239
+
240
+ const mainNav = [
241
+ { id: 'products', label: 'Products', href: '/products' },
242
+ { id: 'pricing', label: 'Pricing', href: '/pricing' },
243
+ { id: 'docs', label: 'Docs', href: '/docs' },
244
+ { id: 'blog', label: 'Blog', href: '/blog' },
245
+ ]
246
+ </script>
247
+
248
+ <header class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
249
+ <div class="max-w-7xl mx-auto px-4 py-4">
250
+ <Navigator items={mainNav} active="/products" />
251
+ </div>
252
+ </header>
253
+ ```
254
+
255
+ ### Dashboard Sidebar
256
+
257
+ ```stx
258
+ <script>
259
+ import { Navigator } from '@stacksjs/components'
260
+
261
+ const sidebarNav = [
262
+ {
263
+ id: 'dashboard',
264
+ label: 'Dashboard',
265
+ href: '/dashboard',
266
+ icon: '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" /></svg>',
267
+ },
268
+ {
269
+ id: 'projects',
270
+ label: 'Projects',
271
+ href: '/projects',
272
+ badge: '5',
273
+ icon: '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" /></svg>',
274
+ },
275
+ {
276
+ id: 'team',
277
+ label: 'Team',
278
+ href: '/team',
279
+ icon: '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" /></svg>',
280
+ },
281
+ ]
282
+ </script>
283
+
284
+ <aside class="w-64 bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 h-full">
285
+ <Navigator
286
+ items={sidebarNav}
287
+ active="/dashboard"
288
+ orientation="vertical"
289
+ variant="sidebar"
290
+ />
291
+ </aside>
292
+ ```
293
+
294
+ ### Settings Tabs
295
+
296
+ ```stx
297
+ <script>
298
+ import { Navigator } from '@stacksjs/components'
299
+
300
+ const settingsTabs = [
301
+ { id: 'general', label: 'General', href: '#general' },
302
+ { id: 'security', label: 'Security', href: '#security' },
303
+ { id: 'notifications', label: 'Notifications', href: '#notifications' },
304
+ { id: 'billing', label: 'Billing', href: '#billing' },
305
+ ]
306
+
307
+ let activeTab = 'general'
308
+ </script>
309
+
310
+ <div class="border-b border-gray-200 dark:border-gray-700 mb-6">
311
+ <Navigator
312
+ items={settingsTabs}
313
+ active={activeTab}
314
+ variant="underline"
315
+ />
316
+ </div>
317
+ ```
318
+
319
+ ## Styling
320
+
321
+ The navigator uses utility-first CSS classes and supports dark mode. Customize appearance by passing additional classes:
322
+
323
+ ```stx
324
+ <Navigator
325
+ className="bg-gray-50 dark:bg-gray-900 rounded-lg p-2"
326
+ items={items}
327
+ active="home"
328
+ />
329
+ ```
330
+
331
+ ## Accessibility
332
+
333
+ - Semantic HTML (`<nav>`, `<a>`, `<button>`)
334
+ - ARIA labels for navigation role
335
+ - `aria-current="page"` for active items
336
+ - `aria-disabled` for disabled items
337
+ - Keyboard navigation support
338
+ - Focus indicators
339
+
340
+ ## Browser Support
341
+
342
+ Works in all modern browsers that support:
343
+ - CSS Flexbox
344
+ - CSS custom properties
345
+ - ES6+ JavaScript
346
+
347
+ ## Related Components
348
+
349
+ - **Breadcrumb**: Hierarchical navigation trail
350
+ - **Tabs**: Content switching interface
351
+ - **Sidebar**: Full sidebar layout with navigation
352
+ - **Menu**: Dropdown menu navigation
@@ -0,0 +1,20 @@
1
+ export { default as Navigator } from './Navigator.stx'
2
+
3
+ export interface NavigatorItem {
4
+ id?: string
5
+ label: string
6
+ href?: string
7
+ icon?: string
8
+ badge?: string | number
9
+ disabled?: boolean
10
+ }
11
+
12
+ export interface NavigatorProps {
13
+ items: NavigatorItem[]
14
+ active?: string // ID or href of active item
15
+ orientation?: 'horizontal' | 'vertical'
16
+ variant?: 'default' | 'pills' | 'underline' | 'sidebar'
17
+ size?: 'sm' | 'md' | 'lg'
18
+ onNavigate?: (item: NavigatorItem) => void
19
+ className?: string
20
+ }