@stacksjs/components 0.2.92 → 0.2.94

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 (188) hide show
  1. package/dist/{Sidebar-yb115a2w.stx → Sidebar-4hn7vw4n.stx} +18 -0
  2. package/dist/index.js +5 -5
  3. package/package.json +5 -6
  4. package/src/components/CodeBlock.stx +0 -54
  5. package/src/components/Footer.stx +0 -66
  6. package/src/components/Hero.stx +0 -25
  7. package/src/components/Installation.stx +0 -37
  8. package/src/components/index.ts +0 -4
  9. package/src/composables/index.ts +0 -4
  10. package/src/composables/useCopyCode.ts +0 -73
  11. package/src/composables/useDarkMode.ts +0 -100
  12. package/src/composables/useSEO.ts +0 -230
  13. package/src/composables/utilities/index.ts +0 -7
  14. package/src/composables/utilities/useClickOutside.ts +0 -72
  15. package/src/composables/utilities/useFocusTrap.ts +0 -159
  16. package/src/composables/utilities/useKeyboard.ts +0 -124
  17. package/src/composables/utilities/useLocalStorage.ts +0 -132
  18. package/src/composables/utilities/useMediaQuery.ts +0 -118
  19. package/src/index.ts +0 -99
  20. package/src/stx.d.ts +0 -4
  21. package/src/ui/accordion/Accordion.stx +0 -146
  22. package/src/ui/accordion/AccordionItem.stx +0 -37
  23. package/src/ui/accordion/index.ts +0 -47
  24. package/src/ui/audio/Audio.stx +0 -50
  25. package/src/ui/audio/README.md +0 -41
  26. package/src/ui/audio/index.ts +0 -13
  27. package/src/ui/auth/Login.stx +0 -161
  28. package/src/ui/auth/README.md +0 -142
  29. package/src/ui/auth/Signup.stx +0 -159
  30. package/src/ui/auth/TwoFactorChallenge.stx +0 -111
  31. package/src/ui/auth/index.ts +0 -32
  32. package/src/ui/avatar/Avatar.stx +0 -77
  33. package/src/ui/avatar/index.ts +0 -12
  34. package/src/ui/badge/Badge.stx +0 -62
  35. package/src/ui/badge/index.ts +0 -10
  36. package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
  37. package/src/ui/breadcrumb/index.ts +0 -15
  38. package/src/ui/button/Button.stx +0 -74
  39. package/src/ui/button/README.md +0 -80
  40. package/src/ui/button/index.ts +0 -85
  41. package/src/ui/calendar/Calendar.stx +0 -189
  42. package/src/ui/calendar/README.md +0 -254
  43. package/src/ui/calendar/index.ts +0 -14
  44. package/src/ui/card/Card.stx +0 -68
  45. package/src/ui/card/index.ts +0 -13
  46. package/src/ui/checkbox/Checkbox.stx +0 -86
  47. package/src/ui/checkbox/index.ts +0 -16
  48. package/src/ui/combobox/Combobox.stx +0 -96
  49. package/src/ui/combobox/ComboboxButton.stx +0 -25
  50. package/src/ui/combobox/ComboboxInput.stx +0 -32
  51. package/src/ui/combobox/ComboboxOption.stx +0 -49
  52. package/src/ui/combobox/ComboboxOptions.stx +0 -16
  53. package/src/ui/combobox/README.md +0 -150
  54. package/src/ui/combobox/index.ts +0 -40
  55. package/src/ui/command-palette/CommandPalette.stx +0 -81
  56. package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
  57. package/src/ui/command-palette/README.md +0 -42
  58. package/src/ui/command-palette/index.ts +0 -15
  59. package/src/ui/dialog/Dialog.stx +0 -41
  60. package/src/ui/dialog/DialogBackdrop.stx +0 -9
  61. package/src/ui/dialog/DialogDescription.stx +0 -11
  62. package/src/ui/dialog/DialogPanel.stx +0 -16
  63. package/src/ui/dialog/DialogTitle.stx +0 -11
  64. package/src/ui/dialog/README.md +0 -121
  65. package/src/ui/dialog/index.ts +0 -32
  66. package/src/ui/drawer/Drawer.stx +0 -120
  67. package/src/ui/drawer/README.md +0 -90
  68. package/src/ui/drawer/index.ts +0 -9
  69. package/src/ui/dropdown/Dropdown.stx +0 -82
  70. package/src/ui/dropdown/DropdownButton.stx +0 -29
  71. package/src/ui/dropdown/DropdownItem.stx +0 -35
  72. package/src/ui/dropdown/DropdownItems.stx +0 -18
  73. package/src/ui/dropdown/README.md +0 -114
  74. package/src/ui/dropdown/index.ts +0 -29
  75. package/src/ui/form/Form.stx +0 -95
  76. package/src/ui/form/index.ts +0 -67
  77. package/src/ui/heatmap/Heatmap.stx +0 -294
  78. package/src/ui/image/Image.stx +0 -165
  79. package/src/ui/image/README.md +0 -164
  80. package/src/ui/image/index.ts +0 -22
  81. package/src/ui/input/EmailInput.stx +0 -30
  82. package/src/ui/input/NumberInput.stx +0 -126
  83. package/src/ui/input/PasswordInput.stx +0 -143
  84. package/src/ui/input/SearchInput.stx +0 -44
  85. package/src/ui/input/TextInput.stx +0 -132
  86. package/src/ui/input/index.ts +0 -180
  87. package/src/ui/listbox/Listbox.stx +0 -101
  88. package/src/ui/listbox/ListboxButton.stx +0 -28
  89. package/src/ui/listbox/ListboxLabel.stx +0 -11
  90. package/src/ui/listbox/ListboxOption.stx +0 -49
  91. package/src/ui/listbox/ListboxOptions.stx +0 -16
  92. package/src/ui/listbox/README.md +0 -135
  93. package/src/ui/listbox/index.ts +0 -39
  94. package/src/ui/navigator/Navigator.stx +0 -113
  95. package/src/ui/navigator/README.md +0 -352
  96. package/src/ui/navigator/index.ts +0 -20
  97. package/src/ui/notification/Notification.stx +0 -117
  98. package/src/ui/notification/README.md +0 -114
  99. package/src/ui/notification/index.ts +0 -12
  100. package/src/ui/pagination/Pagination.stx +0 -150
  101. package/src/ui/pagination/index.ts +0 -11
  102. package/src/ui/payment/Checkout.stx +0 -31
  103. package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
  104. package/src/ui/payment/PaymentMethods.stx +0 -104
  105. package/src/ui/payment/README.md +0 -345
  106. package/src/ui/payment/SubscriptionCheckout.stx +0 -323
  107. package/src/ui/payment/index.ts +0 -59
  108. package/src/ui/popover/Popover.stx +0 -60
  109. package/src/ui/popover/PopoverButton.stx +0 -29
  110. package/src/ui/popover/PopoverPanel.stx +0 -23
  111. package/src/ui/popover/README.md +0 -91
  112. package/src/ui/popover/index.ts +0 -22
  113. package/src/ui/portal/Portal.stx +0 -57
  114. package/src/ui/portal/index.ts +0 -6
  115. package/src/ui/progress/Progress.stx +0 -131
  116. package/src/ui/progress/index.ts +0 -12
  117. package/src/ui/radio/Radio.stx +0 -77
  118. package/src/ui/radio/index.ts +0 -15
  119. package/src/ui/radio-group/README.md +0 -139
  120. package/src/ui/radio-group/RadioGroup.stx +0 -60
  121. package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
  122. package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
  123. package/src/ui/radio-group/RadioGroupOption.stx +0 -58
  124. package/src/ui/radio-group/index.ts +0 -30
  125. package/src/ui/select/README.md +0 -180
  126. package/src/ui/select/Select.stx +0 -82
  127. package/src/ui/select/index.ts +0 -21
  128. package/src/ui/sidebar/Sidebar.stx +0 -458
  129. package/src/ui/sidebar/SidebarFooter.stx +0 -71
  130. package/src/ui/sidebar/SidebarHeader.stx +0 -103
  131. package/src/ui/sidebar/SidebarItem.stx +0 -86
  132. package/src/ui/sidebar/SidebarSection.stx +0 -83
  133. package/src/ui/sidebar/index.ts +0 -153
  134. package/src/ui/sidebar/themes.ts +0 -240
  135. package/src/ui/skeleton/Skeleton.stx +0 -48
  136. package/src/ui/skeleton/index.ts +0 -11
  137. package/src/ui/spinner/Spinner.stx +0 -109
  138. package/src/ui/spinner/index.ts +0 -9
  139. package/src/ui/stepper/README.md +0 -36
  140. package/src/ui/stepper/Stepper.stx +0 -17
  141. package/src/ui/stepper/StepperStep.stx +0 -57
  142. package/src/ui/stepper/index.ts +0 -19
  143. package/src/ui/storage/README.md +0 -193
  144. package/src/ui/storage/index.ts +0 -347
  145. package/src/ui/switch/README.md +0 -114
  146. package/src/ui/switch/Switch.stx +0 -72
  147. package/src/ui/switch/index.ts +0 -10
  148. package/src/ui/table/README.md +0 -39
  149. package/src/ui/table/Table.stx +0 -14
  150. package/src/ui/table/TableBody.stx +0 -10
  151. package/src/ui/table/TableCell.stx +0 -10
  152. package/src/ui/table/TableHead.stx +0 -10
  153. package/src/ui/table/TableHeader.stx +0 -20
  154. package/src/ui/table/TableRow.stx +0 -11
  155. package/src/ui/table/index.ts +0 -35
  156. package/src/ui/tabs/TabPanel.stx +0 -25
  157. package/src/ui/tabs/Tabs.stx +0 -202
  158. package/src/ui/tabs/index.ts +0 -50
  159. package/src/ui/teleport/Teleport.stx +0 -65
  160. package/src/ui/teleport/index.ts +0 -7
  161. package/src/ui/textarea/Textarea.stx +0 -108
  162. package/src/ui/textarea/index.ts +0 -20
  163. package/src/ui/tooltip/Tooltip.stx +0 -71
  164. package/src/ui/tooltip/index.ts +0 -10
  165. package/src/ui/transition/README.md +0 -30
  166. package/src/ui/transition/Transition.stx +0 -102
  167. package/src/ui/transition/index.ts +0 -25
  168. package/src/ui/video/README.md +0 -47
  169. package/src/ui/video/Video.stx +0 -45
  170. package/src/ui/video/index.ts +0 -16
  171. package/src/ui/virtual-list/VirtualList.stx +0 -67
  172. package/src/ui/virtual-list/index.ts +0 -10
  173. package/src/ui/virtual-table/VirtualTable.stx +0 -103
  174. package/src/ui/virtual-table/index.ts +0 -21
  175. package/src/utils/PROP_VALIDATION.md +0 -458
  176. package/src/utils/THEME_SYSTEM.md +0 -702
  177. package/src/utils/accessibility.ts +0 -841
  178. package/src/utils/animation/easing.ts +0 -368
  179. package/src/utils/animation/helpers.ts +0 -223
  180. package/src/utils/animation/index.ts +0 -5
  181. package/src/utils/animation/keyframes.ts +0 -230
  182. package/src/utils/error-handling.ts +0 -475
  183. package/src/utils/highlighter.ts +0 -93
  184. package/src/utils/i18n.ts +0 -634
  185. package/src/utils/keep-alive.ts +0 -819
  186. package/src/utils/lazy-loading.ts +0 -530
  187. package/src/utils/prop-validation.ts +0 -478
  188. package/src/utils/theme.ts +0 -626
@@ -1,180 +0,0 @@
1
- import type { PropSchema, ValidationResult } from '../../utils/prop-validation'
2
- import { createPropValidator, PropTypes } from '../../utils/prop-validation'
3
-
4
- export { default as EmailInput } from './EmailInput.stx'
5
- export { default as NumberInput } from './NumberInput.stx'
6
- export { default as PasswordInput } from './PasswordInput.stx'
7
- export { default as SearchInput } from './SearchInput.stx'
8
- export { default as TextInput } from './TextInput.stx'
9
-
10
- export interface BaseInputProps {
11
- value?: string
12
- onChange?: (value: string) => void
13
- placeholder?: string
14
- disabled?: boolean
15
- readonly?: boolean
16
- size?: 'sm' | 'md' | 'lg'
17
- error?: boolean
18
- label?: string
19
- helperText?: string
20
- required?: boolean
21
- className?: string
22
- inputClassName?: string
23
- }
24
-
25
- export interface TextInputProps extends BaseInputProps {
26
- leftIcon?: string
27
- rightIcon?: string
28
- clearable?: boolean
29
- maxLength?: number
30
- showCount?: boolean
31
- }
32
-
33
- export interface EmailInputProps extends Omit<BaseInputProps, 'placeholder'> {
34
- placeholder?: string
35
- }
36
-
37
- export interface PasswordInputProps extends Omit<BaseInputProps, 'placeholder'> {
38
- placeholder?: string
39
- showStrength?: boolean
40
- }
41
-
42
- export interface NumberInputProps extends BaseInputProps {
43
- min?: number
44
- max?: number
45
- step?: number
46
- showControls?: boolean
47
- }
48
-
49
- export interface SearchInputProps extends Omit<BaseInputProps, 'placeholder'> {
50
- placeholder?: string
51
- onSearch?: (value: string) => void
52
- loading?: boolean
53
- }
54
-
55
- /**
56
- * Base input prop validation schema
57
- */
58
- const baseInputPropSchema: PropSchema = {
59
- value: PropTypes.string,
60
- onChange: PropTypes.func,
61
- placeholder: PropTypes.string,
62
- disabled: PropTypes.boolean,
63
- readonly: PropTypes.boolean,
64
- size: PropTypes.oneOf(['sm', 'md', 'lg']),
65
- error: PropTypes.boolean,
66
- label: PropTypes.string,
67
- helperText: PropTypes.string,
68
- required: PropTypes.boolean,
69
- className: PropTypes.string,
70
- inputClassName: PropTypes.string,
71
- }
72
-
73
- /**
74
- * TextInput prop validation schema
75
- */
76
- export const textInputPropSchema: PropSchema = {
77
- value: PropTypes.string,
78
- onChange: PropTypes.func,
79
- placeholder: PropTypes.string,
80
- disabled: PropTypes.boolean,
81
- readonly: PropTypes.boolean,
82
- size: PropTypes.oneOf(['sm', 'md', 'lg']),
83
- error: PropTypes.boolean,
84
- label: PropTypes.string,
85
- helperText: PropTypes.string,
86
- required: PropTypes.boolean,
87
- className: PropTypes.string,
88
- inputClassName: PropTypes.string,
89
- leftIcon: PropTypes.string,
90
- rightIcon: PropTypes.string,
91
- clearable: PropTypes.boolean,
92
- maxLength: PropTypes.number,
93
- showCount: PropTypes.boolean,
94
- }
95
-
96
- /**
97
- * NumberInput prop validation schema
98
- */
99
- export const numberInputPropSchema: PropSchema = {
100
- value: PropTypes.string,
101
- onChange: PropTypes.func,
102
- placeholder: PropTypes.string,
103
- disabled: PropTypes.boolean,
104
- readonly: PropTypes.boolean,
105
- size: PropTypes.oneOf(['sm', 'md', 'lg']),
106
- error: PropTypes.boolean,
107
- label: PropTypes.string,
108
- helperText: PropTypes.string,
109
- required: PropTypes.boolean,
110
- className: PropTypes.string,
111
- inputClassName: PropTypes.string,
112
- min: PropTypes.number,
113
- max: PropTypes.number,
114
- step: PropTypes.number,
115
- showControls: PropTypes.boolean,
116
- }
117
-
118
- /**
119
- * PasswordInput prop validation schema
120
- */
121
- const passwordInputPropSchema: PropSchema = {
122
- value: PropTypes.string,
123
- onChange: PropTypes.func,
124
- placeholder: PropTypes.string,
125
- disabled: PropTypes.boolean,
126
- readonly: PropTypes.boolean,
127
- size: PropTypes.oneOf(['sm', 'md', 'lg']),
128
- error: PropTypes.boolean,
129
- label: PropTypes.string,
130
- helperText: PropTypes.string,
131
- required: PropTypes.boolean,
132
- className: PropTypes.string,
133
- inputClassName: PropTypes.string,
134
- showStrength: PropTypes.boolean,
135
- }
136
-
137
- /**
138
- * SearchInput prop validation schema
139
- */
140
- const searchInputPropSchema: PropSchema = {
141
- value: PropTypes.string,
142
- onChange: PropTypes.func,
143
- placeholder: PropTypes.string,
144
- disabled: PropTypes.boolean,
145
- readonly: PropTypes.boolean,
146
- size: PropTypes.oneOf(['sm', 'md', 'lg']),
147
- error: PropTypes.boolean,
148
- label: PropTypes.string,
149
- helperText: PropTypes.string,
150
- required: PropTypes.boolean,
151
- className: PropTypes.string,
152
- inputClassName: PropTypes.string,
153
- onSearch: PropTypes.func,
154
- loading: PropTypes.boolean,
155
- }
156
-
157
- /**
158
- * Validate TextInput component props
159
- */
160
- export const validateTextInputProps: (props: Record<string, any>) => ValidationResult = createPropValidator('TextInput', textInputPropSchema)
161
-
162
- /**
163
- * Validate EmailInput component props
164
- */
165
- export const validateEmailInputProps: (props: Record<string, any>) => ValidationResult = createPropValidator('EmailInput', baseInputPropSchema)
166
-
167
- /**
168
- * Validate PasswordInput component props
169
- */
170
- export const validatePasswordInputProps: (props: Record<string, any>) => ValidationResult = createPropValidator('PasswordInput', passwordInputPropSchema)
171
-
172
- /**
173
- * Validate NumberInput component props
174
- */
175
- export const validateNumberInputProps: (props: Record<string, any>) => ValidationResult = createPropValidator('NumberInput', numberInputPropSchema)
176
-
177
- /**
178
- * Validate SearchInput component props
179
- */
180
- export const validateSearchInputProps: (props: Record<string, any>) => ValidationResult = createPropValidator('SearchInput', searchInputPropSchema)
@@ -1,101 +0,0 @@
1
- <script server>
2
- export const value = $props.value
3
- export const multiple = $props.multiple || false
4
- export const className = $props.className || ''
5
- export const as = $props.as || 'div'
6
-
7
- const baseClasses = 'relative'
8
- export const listboxClasses = `${baseClasses} ${className}`.trim()
9
- export const listboxInstanceId = `stx-lb-${Math.random().toString(36).slice(2, 11)}`
10
- </script>
11
-
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 />
95
- </component>
96
-
97
- <style>
98
- [data-stx-listbox][data-state="closed"] [data-stx-listbox-options] {
99
- display: none;
100
- }
101
- </style>
@@ -1,28 +0,0 @@
1
- <script server>
2
- export const className = $props.className || ''
3
- export const as = $props.as || 'button'
4
- export const disabled = $props.disabled || false
5
-
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
- const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : ''
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
- }
18
- </script>
19
-
20
- <component
21
- :is="{{ as }}"
22
- class="{{ buttonClasses }}"
23
- data-stx-listbox-button
24
- @if(disabled)disabled aria-disabled="true"@endif
25
- @click="onClick($event)"
26
- >
27
- <slot />
28
- </component>
@@ -1,11 +0,0 @@
1
- <script server>
2
- export const className = $props.className || ''
3
- export const as = $props.as || 'label'
4
-
5
- const baseClasses = 'block text-sm font-medium text-neutral-900 dark:text-white'
6
- const labelClasses = `${baseClasses} ${className}`.trim()
7
- </script>
8
-
9
- <component :is="{{ as }}" class="{{ labelClasses }}">
10
- <slot />
11
- </component>
@@ -1,49 +0,0 @@
1
- <script server>
2
- export const value = $props.value
3
- export const disabled = $props.disabled || false
4
- export const className = $props.className || ''
5
- export const as = $props.as || 'li'
6
-
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>
12
-
13
- <script client>
14
- const isDisabled = {{ disabled }}
15
- const optionValue = {{ value === undefined ? 'null' : value }}
16
-
17
- function onClick(event) {
18
- if (isDisabled) return
19
- event.currentTarget.dispatchEvent(
20
- new CustomEvent('stx:listbox-select', { bubbles: true, detail: optionValue }),
21
- )
22
- }
23
- </script>
24
-
25
- <component
26
- :is="{{ as }}"
27
- class="{{ optionClasses }}"
28
- data-stx-listbox-option
29
- data-option-value="{{ valueJson }}"
30
- role="option"
31
- aria-selected="false"
32
- @if(disabled)aria-disabled="true"@endif
33
- @click="onClick($event)"
34
- >
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>
40
-
41
- <slot :disabled="disabled" />
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,16 +0,0 @@
1
- <script server>
2
- export const className = $props.className || ''
3
- export const as = $props.as || 'ul'
4
-
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'
6
- export const optionsClasses = `${baseClasses} ${className}`.trim()
7
- </script>
8
-
9
- <component
10
- :is="{{ as }}"
11
- class="{{ optionsClasses }}"
12
- data-stx-listbox-options
13
- role="listbox"
14
- >
15
- <slot />
16
- </component>
@@ -1,135 +0,0 @@
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 server>
15
- const people = [
16
- { id: 1, name: 'Wade Cooper' },
17
- { id: 2, name: 'Arlene Mccoy' },
18
- { id: 3, name: 'Devon Webb' }
19
- ]
20
-
21
- let selected = people[0]
22
-
23
- function handleChange(person) {
24
- selected = person
25
- }
26
- </script>
27
-
28
- <Listbox :value="selected" :onChange="handleChange">
29
- <ListboxLabel>Assign to</ListboxLabel>
30
-
31
- <ListboxButton>{{ selected.name }}</ListboxButton>
32
-
33
- <ListboxOptions>
34
- @foreach(person in people)
35
- <ListboxOption :value="person" :currentValue="selected" :onChange="handleChange">
36
- {{ person.name }}
37
- </ListboxOption>
38
- @endforeach
39
- </ListboxOptions>
40
- </Listbox>
41
- ```
42
-
43
- ## Props
44
-
45
- ### Listbox
46
- | Prop | Type | Default | Description |
47
- |------|------|---------|-------------|
48
- | `value` | `any` | - | Currently selected value(s) |
49
- | `onChange` | `(value: any) => void` | - | Change handler |
50
- | `multiple` | `boolean` | `false` | Allow multiple selection |
51
- | `className` | `string` | `''` | Additional CSS classes |
52
- | `as` | `string` | `'div'` | HTML element to render |
53
-
54
- ### ListboxButton
55
- | Prop | Type | Default | Description |
56
- |------|------|---------|-------------|
57
- | `className` | `string` | `''` | Additional CSS classes |
58
- | `as` | `string` | `'button'` | HTML element to render |
59
- | `disabled` | `boolean` | `false` | Disable the button |
60
-
61
- ### ListboxOptions
62
- | Prop | Type | Default | Description |
63
- |------|------|---------|-------------|
64
- | `className` | `string` | `''` | Additional CSS classes |
65
- | `as` | `string` | `'ul'` | HTML element to render |
66
- | `static` | `boolean` | `false` | Keep visible regardless of state |
67
-
68
- ### ListboxOption
69
- | Prop | Type | Default | Description |
70
- |------|------|---------|-------------|
71
- | `value` | `any` | - | Option value |
72
- | `disabled` | `boolean` | `false` | Disable the option |
73
- | `className` | `string` | `''` | Additional CSS classes |
74
- | `as` | `string` | `'li'` | HTML element to render |
75
- | `currentValue` | `any` | - | Current selected value(s) |
76
- | `onChange` | `(value: any) => void` | - | Change handler |
77
-
78
- ### ListboxLabel
79
- | Prop | Type | Default | Description |
80
- |------|------|---------|-------------|
81
- | `className` | `string` | `''` | Additional CSS classes |
82
- | `as` | `string` | `'label'` | HTML element to render |
83
-
84
- ## Examples
85
-
86
- ### Multiple Selection
87
-
88
- ```stx
89
- <script server>
90
- let selectedPeople = [people[0], people[1]]
91
-
92
- function handleChange(person) {
93
- if (selectedPeople.includes(person)) {
94
- selectedPeople = selectedPeople.filter(p => p !== person)
95
- } else {
96
- selectedPeople = [...selectedPeople, person]
97
- }
98
- }
99
- </script>
100
-
101
- <Listbox :value="selectedPeople" :onChange="handleChange" multiple>
102
- <!-- ... -->
103
- </Listbox>
104
- ```
105
-
106
- ### With Avatars
107
-
108
- ```stx
109
- <ListboxOption :value="person">
110
- <div class="flex items-center">
111
- <img src="{{ person.avatar }}" alt="" class="h-6 w-6 rounded-full mr-2" />
112
- {{ person.name }}
113
- </div>
114
- </ListboxOption>
115
- ```
116
-
117
- ### Disabled Option
118
-
119
- ```stx
120
- <ListboxOption :value="person" disabled>
121
- {{ person.name }} (unavailable)
122
- </ListboxOption>
123
- ```
124
-
125
- ## Features
126
-
127
- - Single or multiple selection
128
- - Keyboard navigation
129
- - Search/filter support
130
- - Dark mode support
131
- - Accessible (WAI-ARIA)
132
- - Disabled state
133
- - Active state on hover
134
- - Check icon for selected items
135
- - Customizable styling
@@ -1,39 +0,0 @@
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
- }
@@ -1,113 +0,0 @@
1
- <script server>
2
- export const items = $props.items || []
3
- export const active = $props.active || ''
4
- export const orientation = $props.orientation || 'horizontal'
5
- export const variant = $props.variant || 'default'
6
- export const size = $props.size || 'md'
7
- export const className = $props.className || ''
8
-
9
- const containerBaseClasses = orientation === 'horizontal' ? 'flex items-center' : 'flex flex-col'
10
- export const containerClasses = `${containerBaseClasses} ${className}`.trim()
11
- export const gapClasses = orientation === 'horizontal' ? 'gap-1' : 'gap-0.5'
12
- </script>
13
-
14
- <script client>
15
- const emit = defineEmits()
16
- const items = {{ items }}
17
- const variant = {{ variant }}
18
- const size = {{ size }}
19
- const initialActive = {{ active }}
20
- const activeKey = state(initialActive)
21
-
22
- function isActive(item) {
23
- const a = activeKey()
24
- if (typeof item.href === 'string' && typeof a === 'string') {
25
- if (item.href === a) return true
26
- if (a.startsWith(item.href)) return true
27
- }
28
- if (item.id && item.id === a) return true
29
- return false
30
- }
31
-
32
- function getItemClasses(item) {
33
- const isActiveItem = isActive(item)
34
- const sizeClasses = { sm: 'px-2 py-1 text-sm', md: 'px-3 py-2 text-base', lg: 'px-4 py-3 text-lg' }
35
- let cls = sizeClasses[size] || sizeClasses.md
36
- if (variant === 'default') {
37
- cls += isActiveItem
38
- ? ' text-gray-900 dark:text-gray-100 font-medium'
39
- : ' text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100'
40
- }
41
- else if (variant === 'pills') {
42
- cls += isActiveItem
43
- ? ' bg-blue-600 text-white font-medium rounded-full'
44
- : ' text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full'
45
- }
46
- else if (variant === 'underline') {
47
- cls += isActiveItem
48
- ? ' text-blue-600 dark:text-blue-400 font-medium border-b-2 border-blue-600 dark:border-blue-400'
49
- : ' text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 border-b-2 border-transparent'
50
- }
51
- else if (variant === 'sidebar') {
52
- cls += isActiveItem
53
- ? ' 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'
54
- : ' text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 border-l-4 border-transparent'
55
- }
56
- cls += ' transition-all duration-200 cursor-pointer flex items-center gap-2'
57
- if (item.disabled) cls += ' opacity-50 cursor-not-allowed pointer-events-none'
58
- return cls.trim()
59
- }
60
-
61
- function onClickItem(item, event) {
62
- if (item.disabled) {
63
- event.preventDefault()
64
- return
65
- }
66
- event.preventDefault()
67
- activeKey.set(item.href || item.id || activeKey())
68
- emit('navigate', item)
69
- }
70
-
71
- defineExpose({ activeKey })
72
- </script>
73
-
74
- <nav class="{{ containerClasses }} {{ gapClasses }}" role="navigation" aria-label="Main navigation">
75
- @foreach(item in items)
76
- @if(item.href)
77
- <a
78
- href="{{ item.href }}"
79
- :class="getItemClasses({{ JSON.stringify(item) }})"
80
- @click="onClickItem({{ JSON.stringify(item) }}, $event)"
81
- :aria-current="isActive({{ JSON.stringify(item) }}) ? 'page' : null"
82
- @if(item.disabled)aria-disabled="true" tabindex="-1"@endif
83
- >
84
- @if(item.icon)
85
- <span class="flex-shrink-0">{!! item.icon !!}</span>
86
- @endif
87
- <span>{{ item.label }}</span>
88
- @if(item.badge)
89
- <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">
90
- {{ item.badge }}
91
- </span>
92
- @endif
93
- </a>
94
- @else
95
- <button
96
- type="button"
97
- :class="getItemClasses({{ JSON.stringify(item) }})"
98
- @click="onClickItem({{ JSON.stringify(item) }}, $event)"
99
- @if(item.disabled)disabled aria-disabled="true"@endif
100
- >
101
- @if(item.icon)
102
- <span class="flex-shrink-0">{!! item.icon !!}</span>
103
- @endif
104
- <span>{{ item.label }}</span>
105
- @if(item.badge)
106
- <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">
107
- {{ item.badge }}
108
- </span>
109
- @endif
110
- </button>
111
- @endif
112
- @endforeach
113
- </nav>