@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,139 +0,0 @@
1
- # Radio Group Component
2
-
3
- A set of checkable buttons (radio buttons) where only one can be checked at a time.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- bun add @stacksjs/components
9
- ```
10
-
11
- ## Usage
12
-
13
- ```stx
14
- <script server>
15
- let selected = 'option1'
16
-
17
- function handleChange(value) {
18
- selected = value
19
- }
20
- </script>
21
-
22
- <RadioGroup :value="selected" :onChange="handleChange">
23
- <RadioGroupLabel>Choose an option</RadioGroupLabel>
24
-
25
- <RadioGroupOption value="option1" :currentValue="selected" :onChange="handleChange">
26
- Option 1
27
- </RadioGroupOption>
28
-
29
- <RadioGroupOption value="option2" :currentValue="selected" :onChange="handleChange">
30
- Option 2
31
- </RadioGroupOption>
32
- </RadioGroup>
33
- ```
34
-
35
- ## Props
36
-
37
- ### RadioGroup
38
- | Prop | Type | Default | Description |
39
- |------|------|---------|-------------|
40
- | `value` | `any` | - | Currently selected value |
41
- | `onChange` | `(value: any) => void` | - | Change handler |
42
- | `className` | `string` | `''` | Additional CSS classes |
43
- | `as` | `string` | `'div'` | HTML element to render |
44
-
45
- ### RadioGroupLabel
46
- | Prop | Type | Default | Description |
47
- |------|------|---------|-------------|
48
- | `className` | `string` | `''` | Additional CSS classes |
49
- | `as` | `string` | `'label'` | HTML element to render |
50
-
51
- ### RadioGroupOption
52
- | Prop | Type | Default | Description |
53
- |------|------|---------|-------------|
54
- | `value` | `any` | - | Option value |
55
- | `disabled` | `boolean` | `false` | Disable the option |
56
- | `className` | `string` | `''` | Additional CSS classes |
57
- | `as` | `string` | `'div'` | HTML element to render |
58
- | `currentValue` | `any` | - | Current selected value |
59
- | `onChange` | `(value: any) => void` | - | Change handler |
60
-
61
- ### RadioGroupDescription
62
- | Prop | Type | Default | Description |
63
- |------|------|---------|-------------|
64
- | `className` | `string` | `''` | Additional CSS classes |
65
- | `as` | `string` | `'span'` | HTML element to render |
66
-
67
- ## Examples
68
-
69
- ### Server Plans
70
-
71
- ```stx
72
- <script server>
73
- const plans = [
74
- { name: 'Startup', ram: '12GB', cpus: '6 CPUs', disk: '160 GB SSD' },
75
- { name: 'Business', ram: '16GB', cpus: '8 CPUs', disk: '512 GB SSD' },
76
- { name: 'Enterprise', ram: '32GB', cpus: '12 CPUs', disk: '1024 GB SSD' }
77
- ]
78
-
79
- let selected = plans[0]
80
-
81
- function handleChange(plan) {
82
- selected = plan
83
- }
84
- </script>
85
-
86
- <RadioGroup :value="selected" :onChange="handleChange">
87
- <RadioGroupLabel className="sr-only">Server size</RadioGroupLabel>
88
-
89
- <div class="space-y-2">
90
- @foreach(plan in plans)
91
- <RadioGroupOption
92
- :value="plan"
93
- :currentValue="selected"
94
- :onChange="handleChange"
95
- >
96
- <div class="w-full flex items-center justify-between">
97
- <div class="flex items-center">
98
- <div class="text-sm">
99
- <RadioGroupLabel className="font-medium">
100
- {{ plan.name }}
101
- </RadioGroupLabel>
102
-
103
- <RadioGroupDescription>
104
- {{ plan.ram }}/{{ plan.cpus }} · {{ plan.disk }}
105
- </RadioGroupDescription>
106
- </div>
107
- </div>
108
-
109
- @if(selected === plan)
110
- <svg class="h-6 w-6 shrink-0 text-white" viewBox="0 0 24 24" fill="none">
111
- <circle cx="12" cy="12" r="12" fill="#fff" fill-opacity="0.2" />
112
- <path d="M7 13l3 3 7-7" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
113
- </svg>
114
- @endif
115
- </div>
116
- </RadioGroupOption>
117
- @endforeach
118
- </div>
119
- </RadioGroup>
120
- ```
121
-
122
- ### With Disabled Option
123
-
124
- ```stx
125
- <RadioGroupOption value="disabled" disabled>
126
- This option is disabled
127
- </RadioGroupOption>
128
- ```
129
-
130
- ## Features
131
-
132
- - Single selection from multiple options
133
- - Keyboard navigation
134
- - Focus management
135
- - Dark mode support
136
- - Accessible (WAI-ARIA)
137
- - Disabled state support
138
- - Active state on focus
139
- - Customizable styling
@@ -1,60 +0,0 @@
1
- <script server>
2
- export const value = $props.value
3
- export const className = $props.className || ''
4
- export const as = $props.as || 'div'
5
-
6
- const baseClasses = 'w-full'
7
- export const groupClasses = `${baseClasses} ${className}`.trim()
8
- export const radioGroupInstanceId = `stx-rg-${Math.random().toString(36).slice(2, 11)}`
9
- </script>
10
-
11
- <script client>
12
- const emit = defineEmits()
13
- const radioGroupInstanceId = {{ radioGroupInstanceId }}
14
- const initialValue = {{ value === undefined ? 'null' : value }}
15
- const selected = state(initialValue)
16
- let rootEl = null
17
-
18
- function selectValue(v) {
19
- selected.set(v)
20
- syncOptionStates()
21
- emit('change', v)
22
- }
23
-
24
- function syncOptionStates() {
25
- if (!rootEl) return
26
- const options = rootEl.querySelectorAll('[data-stx-radio-option]')
27
- for (const opt of options) {
28
- const raw = opt.dataset.optionValue
29
- let v
30
- try { v = JSON.parse(raw ?? 'null') }
31
- catch { v = raw }
32
- const checked = selected() === v
33
- opt.setAttribute('aria-checked', checked ? 'true' : 'false')
34
- opt.setAttribute('data-checked', checked ? 'true' : 'false')
35
- }
36
- }
37
-
38
- function onSelect(event) {
39
- event.stopPropagation()
40
- selectValue(event.detail)
41
- }
42
-
43
- onMount(() => {
44
- rootEl = document.querySelector(`[data-radio-group-id="${radioGroupInstanceId}"]`)
45
- syncOptionStates()
46
- })
47
-
48
- defineExpose({ selected, selectValue })
49
- </script>
50
-
51
- <component
52
- :is="{{ as }}"
53
- class="{{ groupClasses }}"
54
- data-stx-radio-group
55
- data-radio-group-id="{{ radioGroupInstanceId }}"
56
- role="radiogroup"
57
- @stx:radio-select="onSelect($event)"
58
- >
59
- <slot />
60
- </component>
@@ -1,11 +0,0 @@
1
- <script server>
2
- export const className = $props.className || ''
3
- export const as = $props.as || 'span'
4
-
5
- const baseClasses = 'inline text-sm text-neutral-500 dark:text-neutral-400'
6
- const descriptionClasses = `${baseClasses} ${className}`.trim()
7
- </script>
8
-
9
- <component :is="{{ as }}" class="{{ descriptionClasses }}">
10
- <slot />
11
- </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,58 +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 || 'div'
6
-
7
- const baseClasses = 'relative flex cursor-pointer rounded-lg px-5 py-4 shadow-md focus:outline-none transition-all bg-white dark:bg-neutral-800 text-neutral-900 dark:text-neutral-100 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-60 focus-visible:ring-offset-2 focus-visible:ring-offset-blue-300 dark:focus-visible:ring-offset-blue-700'
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:radio-select', { bubbles: true, detail: optionValue }),
21
- )
22
- }
23
-
24
- function onKey(event) {
25
- if (isDisabled) return
26
- if (event.key === ' ' || event.key === 'Enter') {
27
- event.preventDefault()
28
- event.currentTarget.dispatchEvent(
29
- new CustomEvent('stx:radio-select', { bubbles: true, detail: optionValue }),
30
- )
31
- }
32
- }
33
- </script>
34
-
35
- <component
36
- :is="{{ as }}"
37
- class="{{ optionClasses }}"
38
- data-stx-radio-option
39
- data-option-value="{{ valueJson }}"
40
- role="radio"
41
- aria-checked="false"
42
- @if(disabled)aria-disabled="true"@endif
43
- tabindex="{{ disabled ? '-1' : '0' }}"
44
- @click="onClick($event)"
45
- @keydown="onKey($event)"
46
- >
47
- <slot :disabled="disabled" />
48
- </component>
49
-
50
- <style>
51
- [data-stx-radio-option][data-checked="true"] {
52
- background-color: rgb(30 58 138 / 0.75);
53
- color: white;
54
- }
55
- .dark [data-stx-radio-option][data-checked="true"] {
56
- background-color: rgb(30 64 175 / 1);
57
- }
58
- </style>
@@ -1,30 +0,0 @@
1
- export { default as RadioGroup } from './RadioGroup.stx'
2
- export { default as RadioGroupDescription } from './RadioGroupDescription.stx'
3
- export { default as RadioGroupLabel } from './RadioGroupLabel.stx'
4
- export { default as RadioGroupOption } from './RadioGroupOption.stx'
5
-
6
- export interface RadioGroupProps {
7
- value?: any
8
- onChange?: (value: any) => void
9
- className?: string
10
- as?: string
11
- }
12
-
13
- export interface RadioGroupLabelProps {
14
- className?: string
15
- as?: string
16
- }
17
-
18
- export interface RadioGroupOptionProps {
19
- value: any
20
- disabled?: boolean
21
- className?: string
22
- as?: string
23
- currentValue?: any
24
- onChange?: (value: any) => void
25
- }
26
-
27
- export interface RadioGroupDescriptionProps {
28
- className?: string
29
- as?: string
30
- }
@@ -1,180 +0,0 @@
1
- # Select Component
2
-
3
- A styled native select dropdown with support for labels, validation, and dark mode.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- bun add @stacksjs/components
9
- ```
10
-
11
- ## Usage
12
-
13
- ### Basic Select
14
-
15
- ```stx
16
- <script server>
17
- const options = [
18
- { value: 'option1', label: 'Option 1' },
19
- { value: 'option2', label: 'Option 2' },
20
- { value: 'option3', label: 'Option 3' },
21
- ]
22
-
23
- const selected = 'option1'
24
-
25
- const handleChange = (value) => {
26
- console.log('Selected:', value)
27
- selected = value
28
- }
29
- </script>
30
-
31
- <Select
32
- :value="selected"
33
- :onChange="handleChange"
34
- :options="options"
35
- placeholder="Choose an option"
36
- />
37
- ```
38
-
39
- ### With Label and Helper Text
40
-
41
- ```stx
42
- <Select
43
- label="Country"
44
- helperText="Select your country of residence"
45
- :value="country"
46
- :onChange="(val) => country = val"
47
- :options="countries"
48
- required
49
- />
50
- ```
51
-
52
- ### With Error State
53
-
54
- ```stx
55
- <Select
56
- label="Category"
57
- :value="category"
58
- :onChange="(val) => category = val"
59
- :options="categories"
60
- error
61
- helperText="Please select a valid category"
62
- required
63
- />
64
- ```
65
-
66
- ### Different Sizes
67
-
68
- ```stx
69
- <!-- Small -->
70
- <Select size="sm" :options="options" />
71
-
72
- <!-- Medium (default) -->
73
- <Select size="md" :options="options" />
74
-
75
- <!-- Large -->
76
- <Select size="lg" :options="options" />
77
- ```
78
-
79
- ### Disabled State
80
-
81
- ```stx
82
- <Select disabled value="locked" :options="options" />
83
- ```
84
-
85
- ### With Disabled Options
86
-
87
- ```stx
88
- <script server>
89
- const options = [
90
- { value: '1', label: 'Available Option' },
91
- { value: '2', label: 'Disabled Option', disabled: true },
92
- { value: '3', label: 'Another Available' },
93
- ]
94
- </script>
95
-
96
- <Select :options="options" />
97
- ```
98
-
99
- ## Props
100
-
101
- | Prop | Type | Default | Description |
102
- |------|------|---------|-------------|
103
- | `value` | `string \| number` | `''` | Selected value |
104
- | `onChange` | `function` | - | Callback when selection changes |
105
- | `options` | `SelectOption[]` | `[]` | Array of options |
106
- | `placeholder` | `string` | `'Select an option'` | Placeholder text |
107
- | `disabled` | `boolean` | `false` | Disable the select |
108
- | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Select size |
109
- | `error` | `boolean` | `false` | Show error state |
110
- | `label` | `string` | `''` | Label text |
111
- | `helperText` | `string` | `''` | Helper/error text below select |
112
- | `required` | `boolean` | `false` | Mark as required |
113
- | `className` | `string` | `''` | Additional CSS classes |
114
-
115
- ## SelectOption Interface
116
-
117
- ```typescript
118
- interface SelectOption {
119
- value: string | number
120
- label: string
121
- disabled?: boolean
122
- }
123
- ```
124
-
125
- ## Features
126
-
127
- - **Native select** - Uses native `<select>` element for best UX
128
- - **3 sizes** - Small, medium, and large
129
- - **Dark mode** - Full dark mode support
130
- - **Validation states** - Error and required states
131
- - **Disabled options** - Individual options can be disabled
132
- - **Labels and helpers** - Support for labels and helper text
133
- - **Accessible** - Proper ARIA attributes and labels
134
- - **Keyboard navigation** - Full keyboard support
135
- - **Modern ES modules** - Clean export syntax
136
- - **Headwind styling** - Utility-first CSS classes
137
-
138
- ## Accessibility
139
-
140
- - Proper `label` associations with `for` attribute
141
- - `aria-invalid` for error states
142
- - `aria-describedby` for helper text
143
- - Required field indicator
144
- - Disabled state properly communicated
145
- - Keyboard navigable
146
-
147
- ## Example: Form Select
148
-
149
- ```stx
150
- <script server>
151
- const countries = [
152
- { value: 'us', label: 'United States' },
153
- { value: 'ca', label: 'Canada' },
154
- { value: 'mx', label: 'Mexico' },
155
- { value: 'uk', label: 'United Kingdom' },
156
- { value: 'de', label: 'Germany' },
157
- ]
158
-
159
- let selectedCountry = ''
160
- let hasError = false
161
-
162
- const handleCountryChange = (value) => {
163
- selectedCountry = value
164
- hasError = !value
165
- }
166
- </script>
167
-
168
- <form>
169
- <Select
170
- label="Country"
171
- :value="selectedCountry"
172
- :onChange="handleCountryChange"
173
- :options="countries"
174
- placeholder="Select your country"
175
- required
176
- :error="hasError"
177
- :helperText="hasError ? 'Country is required' : 'Choose your country'"
178
- />
179
- </form>
180
- ```
@@ -1,82 +0,0 @@
1
- <script server>
2
- export const value = $props.value || ''
3
- export const options = $props.options || []
4
- export const placeholder = $props.placeholder || 'Select an option'
5
- export const disabled = $props.disabled || false
6
- export const size = $props.size || 'md'
7
- export const error = $props.error || false
8
- export const label = $props.label || ''
9
- export const helperText = $props.helperText || ''
10
- export const required = $props.required || false
11
- export const className = $props.className || ''
12
-
13
- const sizeClasses = { sm: 'px-3 py-1.5 text-sm h-8', md: 'px-4 py-2 text-base h-10', lg: 'px-5 py-3 text-lg h-12' }
14
- const baseClasses = 'block w-full border-0 rounded-md shadow-sm ring-1 ring-inset transition-colors duration-200 focus:ring-2 focus:ring-inset dark:bg-blue-gray-700 dark:text-gray-100'
15
- const stateClasses = error
16
- ? 'ring-red-300 dark:ring-red-600 text-red-900 dark:text-red-300 placeholder-red-300 dark:placeholder-red-500 focus:ring-red-500 dark:focus:ring-red-400'
17
- : 'ring-gray-300 dark:ring-gray-600 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:ring-indigo-600 dark:focus:ring-indigo-400'
18
- const disabledClasses = disabled
19
- ? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
20
- : 'bg-white dark:bg-blue-gray-700 cursor-pointer hover:ring-gray-400 dark:hover:ring-gray-500'
21
-
22
- export const selectClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses} ${className}`.trim()
23
- export const labelClasses = `block text-sm font-medium leading-6 ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100'}`.trim()
24
- export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
25
- export const selectId = `select-${value}`
26
- </script>
27
-
28
- <script client>
29
- const emit = defineEmits()
30
- // Reactive prop binding — parent can drive `value` via signals. See #1704.
31
- const selectedValue = useReactiveProp('value', {{ value }}, { parse: (v) => v == null ? '' : String(v) })
32
-
33
- function onChange(event) {
34
- selectedValue.set(event.target.value)
35
- emit('change', selectedValue())
36
- }
37
-
38
- defineExpose({ value: selectedValue })
39
- </script>
40
-
41
- <div class="w-full">
42
- @if(label)
43
- <label for="{{ selectId }}" class="{{ labelClasses }}">
44
- {{ label }}
45
- @if(required)
46
- <span class="text-red-500 ml-1">*</span>
47
- @endif
48
- </label>
49
- @endif
50
-
51
- <div class="{{ label ? 'mt-2' : '' }}">
52
- <select
53
- id="{{ selectId }}"
54
- class="{{ selectClasses }}"
55
- :value="selectedValue()"
56
- @if(disabled)disabled@endif
57
- @if(required)required@endif
58
- @change="onChange($event)"
59
- :aria-invalid="{{ error ? 'true' : 'false' }}"
60
- aria-describedby="{{ helperText ? 'helper-text' : '' }}"
61
- >
62
- @if(placeholder)
63
- <option value="" disabled>{{ placeholder }}</option>
64
- @endif
65
-
66
- @foreach(option in options)
67
- <option
68
- value="{{ option.value }}"
69
- @if(option.disabled)disabled@endif
70
- >
71
- {{ option.label }}
72
- </option>
73
- @endforeach
74
- </select>
75
- </div>
76
-
77
- @if(helperText)
78
- <p id="helper-text" class="{{ helperClasses }}">
79
- {{ helperText }}
80
- </p>
81
- @endif
82
- </div>
@@ -1,21 +0,0 @@
1
- export { default as Select } from './Select.stx'
2
-
3
- export interface SelectOption {
4
- value: string | number
5
- label: string
6
- disabled?: boolean
7
- }
8
-
9
- export interface SelectProps {
10
- value?: string | number
11
- onChange?: (value: string | number) => void
12
- options?: SelectOption[]
13
- placeholder?: string
14
- disabled?: boolean
15
- size?: 'sm' | 'md' | 'lg'
16
- error?: boolean
17
- label?: string
18
- helperText?: string
19
- required?: boolean
20
- className?: string
21
- }