@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.
- package/dist/{Sidebar-yb115a2w.stx → Sidebar-4hn7vw4n.stx} +18 -0
- package/dist/index.js +5 -5
- package/package.json +5 -6
- package/src/components/CodeBlock.stx +0 -54
- package/src/components/Footer.stx +0 -66
- package/src/components/Hero.stx +0 -25
- package/src/components/Installation.stx +0 -37
- package/src/components/index.ts +0 -4
- package/src/composables/index.ts +0 -4
- package/src/composables/useCopyCode.ts +0 -73
- package/src/composables/useDarkMode.ts +0 -100
- package/src/composables/useSEO.ts +0 -230
- package/src/composables/utilities/index.ts +0 -7
- package/src/composables/utilities/useClickOutside.ts +0 -72
- package/src/composables/utilities/useFocusTrap.ts +0 -159
- package/src/composables/utilities/useKeyboard.ts +0 -124
- package/src/composables/utilities/useLocalStorage.ts +0 -132
- package/src/composables/utilities/useMediaQuery.ts +0 -118
- package/src/index.ts +0 -99
- package/src/stx.d.ts +0 -4
- package/src/ui/accordion/Accordion.stx +0 -146
- package/src/ui/accordion/AccordionItem.stx +0 -37
- package/src/ui/accordion/index.ts +0 -47
- package/src/ui/audio/Audio.stx +0 -50
- package/src/ui/audio/README.md +0 -41
- package/src/ui/audio/index.ts +0 -13
- package/src/ui/auth/Login.stx +0 -161
- package/src/ui/auth/README.md +0 -142
- package/src/ui/auth/Signup.stx +0 -159
- package/src/ui/auth/TwoFactorChallenge.stx +0 -111
- package/src/ui/auth/index.ts +0 -32
- package/src/ui/avatar/Avatar.stx +0 -77
- package/src/ui/avatar/index.ts +0 -12
- package/src/ui/badge/Badge.stx +0 -62
- package/src/ui/badge/index.ts +0 -10
- package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
- package/src/ui/breadcrumb/index.ts +0 -15
- package/src/ui/button/Button.stx +0 -74
- package/src/ui/button/README.md +0 -80
- package/src/ui/button/index.ts +0 -85
- package/src/ui/calendar/Calendar.stx +0 -189
- package/src/ui/calendar/README.md +0 -254
- package/src/ui/calendar/index.ts +0 -14
- package/src/ui/card/Card.stx +0 -68
- package/src/ui/card/index.ts +0 -13
- package/src/ui/checkbox/Checkbox.stx +0 -86
- package/src/ui/checkbox/index.ts +0 -16
- package/src/ui/combobox/Combobox.stx +0 -96
- package/src/ui/combobox/ComboboxButton.stx +0 -25
- package/src/ui/combobox/ComboboxInput.stx +0 -32
- package/src/ui/combobox/ComboboxOption.stx +0 -49
- package/src/ui/combobox/ComboboxOptions.stx +0 -16
- package/src/ui/combobox/README.md +0 -150
- package/src/ui/combobox/index.ts +0 -40
- package/src/ui/command-palette/CommandPalette.stx +0 -81
- package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
- package/src/ui/command-palette/README.md +0 -42
- package/src/ui/command-palette/index.ts +0 -15
- package/src/ui/dialog/Dialog.stx +0 -41
- package/src/ui/dialog/DialogBackdrop.stx +0 -9
- package/src/ui/dialog/DialogDescription.stx +0 -11
- package/src/ui/dialog/DialogPanel.stx +0 -16
- package/src/ui/dialog/DialogTitle.stx +0 -11
- package/src/ui/dialog/README.md +0 -121
- package/src/ui/dialog/index.ts +0 -32
- package/src/ui/drawer/Drawer.stx +0 -120
- package/src/ui/drawer/README.md +0 -90
- package/src/ui/drawer/index.ts +0 -9
- package/src/ui/dropdown/Dropdown.stx +0 -82
- package/src/ui/dropdown/DropdownButton.stx +0 -29
- package/src/ui/dropdown/DropdownItem.stx +0 -35
- package/src/ui/dropdown/DropdownItems.stx +0 -18
- package/src/ui/dropdown/README.md +0 -114
- package/src/ui/dropdown/index.ts +0 -29
- package/src/ui/form/Form.stx +0 -95
- package/src/ui/form/index.ts +0 -67
- package/src/ui/heatmap/Heatmap.stx +0 -294
- package/src/ui/image/Image.stx +0 -165
- package/src/ui/image/README.md +0 -164
- package/src/ui/image/index.ts +0 -22
- package/src/ui/input/EmailInput.stx +0 -30
- package/src/ui/input/NumberInput.stx +0 -126
- package/src/ui/input/PasswordInput.stx +0 -143
- package/src/ui/input/SearchInput.stx +0 -44
- package/src/ui/input/TextInput.stx +0 -132
- package/src/ui/input/index.ts +0 -180
- package/src/ui/listbox/Listbox.stx +0 -101
- package/src/ui/listbox/ListboxButton.stx +0 -28
- package/src/ui/listbox/ListboxLabel.stx +0 -11
- package/src/ui/listbox/ListboxOption.stx +0 -49
- package/src/ui/listbox/ListboxOptions.stx +0 -16
- package/src/ui/listbox/README.md +0 -135
- package/src/ui/listbox/index.ts +0 -39
- package/src/ui/navigator/Navigator.stx +0 -113
- package/src/ui/navigator/README.md +0 -352
- package/src/ui/navigator/index.ts +0 -20
- package/src/ui/notification/Notification.stx +0 -117
- package/src/ui/notification/README.md +0 -114
- package/src/ui/notification/index.ts +0 -12
- package/src/ui/pagination/Pagination.stx +0 -150
- package/src/ui/pagination/index.ts +0 -11
- package/src/ui/payment/Checkout.stx +0 -31
- package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
- package/src/ui/payment/PaymentMethods.stx +0 -104
- package/src/ui/payment/README.md +0 -345
- package/src/ui/payment/SubscriptionCheckout.stx +0 -323
- package/src/ui/payment/index.ts +0 -59
- package/src/ui/popover/Popover.stx +0 -60
- package/src/ui/popover/PopoverButton.stx +0 -29
- package/src/ui/popover/PopoverPanel.stx +0 -23
- package/src/ui/popover/README.md +0 -91
- package/src/ui/popover/index.ts +0 -22
- package/src/ui/portal/Portal.stx +0 -57
- package/src/ui/portal/index.ts +0 -6
- package/src/ui/progress/Progress.stx +0 -131
- package/src/ui/progress/index.ts +0 -12
- package/src/ui/radio/Radio.stx +0 -77
- package/src/ui/radio/index.ts +0 -15
- package/src/ui/radio-group/README.md +0 -139
- package/src/ui/radio-group/RadioGroup.stx +0 -60
- package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
- package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
- package/src/ui/radio-group/RadioGroupOption.stx +0 -58
- package/src/ui/radio-group/index.ts +0 -30
- package/src/ui/select/README.md +0 -180
- package/src/ui/select/Select.stx +0 -82
- package/src/ui/select/index.ts +0 -21
- package/src/ui/sidebar/Sidebar.stx +0 -458
- package/src/ui/sidebar/SidebarFooter.stx +0 -71
- package/src/ui/sidebar/SidebarHeader.stx +0 -103
- package/src/ui/sidebar/SidebarItem.stx +0 -86
- package/src/ui/sidebar/SidebarSection.stx +0 -83
- package/src/ui/sidebar/index.ts +0 -153
- package/src/ui/sidebar/themes.ts +0 -240
- package/src/ui/skeleton/Skeleton.stx +0 -48
- package/src/ui/skeleton/index.ts +0 -11
- package/src/ui/spinner/Spinner.stx +0 -109
- package/src/ui/spinner/index.ts +0 -9
- package/src/ui/stepper/README.md +0 -36
- package/src/ui/stepper/Stepper.stx +0 -17
- package/src/ui/stepper/StepperStep.stx +0 -57
- package/src/ui/stepper/index.ts +0 -19
- package/src/ui/storage/README.md +0 -193
- package/src/ui/storage/index.ts +0 -347
- package/src/ui/switch/README.md +0 -114
- package/src/ui/switch/Switch.stx +0 -72
- package/src/ui/switch/index.ts +0 -10
- package/src/ui/table/README.md +0 -39
- package/src/ui/table/Table.stx +0 -14
- package/src/ui/table/TableBody.stx +0 -10
- package/src/ui/table/TableCell.stx +0 -10
- package/src/ui/table/TableHead.stx +0 -10
- package/src/ui/table/TableHeader.stx +0 -20
- package/src/ui/table/TableRow.stx +0 -11
- package/src/ui/table/index.ts +0 -35
- package/src/ui/tabs/TabPanel.stx +0 -25
- package/src/ui/tabs/Tabs.stx +0 -202
- package/src/ui/tabs/index.ts +0 -50
- package/src/ui/teleport/Teleport.stx +0 -65
- package/src/ui/teleport/index.ts +0 -7
- package/src/ui/textarea/Textarea.stx +0 -108
- package/src/ui/textarea/index.ts +0 -20
- package/src/ui/tooltip/Tooltip.stx +0 -71
- package/src/ui/tooltip/index.ts +0 -10
- package/src/ui/transition/README.md +0 -30
- package/src/ui/transition/Transition.stx +0 -102
- package/src/ui/transition/index.ts +0 -25
- package/src/ui/video/README.md +0 -47
- package/src/ui/video/Video.stx +0 -45
- package/src/ui/video/index.ts +0 -16
- package/src/ui/virtual-list/VirtualList.stx +0 -67
- package/src/ui/virtual-list/index.ts +0 -10
- package/src/ui/virtual-table/VirtualTable.stx +0 -103
- package/src/ui/virtual-table/index.ts +0 -21
- package/src/utils/PROP_VALIDATION.md +0 -458
- package/src/utils/THEME_SYSTEM.md +0 -702
- package/src/utils/accessibility.ts +0 -841
- package/src/utils/animation/easing.ts +0 -368
- package/src/utils/animation/helpers.ts +0 -223
- package/src/utils/animation/index.ts +0 -5
- package/src/utils/animation/keyframes.ts +0 -230
- package/src/utils/error-handling.ts +0 -475
- package/src/utils/highlighter.ts +0 -93
- package/src/utils/i18n.ts +0 -634
- package/src/utils/keep-alive.ts +0 -819
- package/src/utils/lazy-loading.ts +0 -530
- package/src/utils/prop-validation.ts +0 -478
- package/src/utils/theme.ts +0 -626
package/src/ui/input/index.ts
DELETED
|
@@ -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>
|
package/src/ui/listbox/README.md
DELETED
|
@@ -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
|
package/src/ui/listbox/index.ts
DELETED
|
@@ -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>
|