@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/tabs/TabPanel.stx
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
// Slot wrapper that the parent <Tabs> discovers via DOM walk. The parent
|
|
3
|
-
// reads `data-label` (and optional `data-icon`) to build the tab list and
|
|
4
|
-
// toggles the `hidden` attribute as the active tab changes. Hidden by
|
|
5
|
-
// default — the parent flips the active one visible after mount. See
|
|
6
|
-
// stacksjs/stx#1703.
|
|
7
|
-
export const label = $props.label || ''
|
|
8
|
-
export const icon = $props.icon || ''
|
|
9
|
-
export const className = $props.className || ''
|
|
10
|
-
|
|
11
|
-
const baseClasses = 'p-4 focus:outline-none'
|
|
12
|
-
export const panelClasses = `${baseClasses} ${className}`.trim()
|
|
13
|
-
</script>
|
|
14
|
-
|
|
15
|
-
<div
|
|
16
|
-
data-stx-tab-panel
|
|
17
|
-
data-label="{{ label }}"
|
|
18
|
-
data-icon="{{ icon }}"
|
|
19
|
-
class="{{ panelClasses }}"
|
|
20
|
-
role="tabpanel"
|
|
21
|
-
tabindex="0"
|
|
22
|
-
hidden
|
|
23
|
-
>
|
|
24
|
-
<slot />
|
|
25
|
-
</div>
|
package/src/ui/tabs/Tabs.stx
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
// Two modes (see stacksjs/stx#1703):
|
|
3
|
-
// 1. Legacy prop API — pass `tabs={[{label, content, icon}, …]}` for
|
|
4
|
-
// simple string-content tabs (kept for backward compatibility with
|
|
5
|
-
// existing visual snapshots and consumers).
|
|
6
|
-
// 2. Slot API — drop `<TabPanel label="…">` children for arbitrary
|
|
7
|
-
// content (components, forms, tables, etc.). The parent enumerates
|
|
8
|
-
// [data-stx-tab-panel] descendants on mount and builds the tab list
|
|
9
|
-
// dynamically.
|
|
10
|
-
export const tabs = $props.tabs || []
|
|
11
|
-
export const defaultTab = $props.defaultTab || 0
|
|
12
|
-
export const orientation = $props.orientation || 'horizontal'
|
|
13
|
-
export const variant = $props.variant || 'line'
|
|
14
|
-
export const className = $props.className || ''
|
|
15
|
-
|
|
16
|
-
const orientationClasses = {
|
|
17
|
-
horizontal: 'flex-col',
|
|
18
|
-
vertical: 'flex-row',
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const tabListOrientationClasses = {
|
|
22
|
-
horizontal: 'flex-row border-b',
|
|
23
|
-
vertical: 'flex-col border-r',
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const variantClasses = {
|
|
27
|
-
line: 'border-gray-200 dark:border-gray-700',
|
|
28
|
-
pills: 'gap-2 border-0 bg-gray-100 dark:bg-gray-800 p-1 rounded-lg',
|
|
29
|
-
enclosed: 'border-gray-200 dark:border-gray-700',
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const containerClasses = `flex ${orientationClasses[orientation]} ${className}`.trim()
|
|
33
|
-
export const tabListClasses = `flex ${tabListOrientationClasses[orientation]} ${variantClasses[variant]}`.trim()
|
|
34
|
-
export const tabPanelClasses = `p-4 focus:outline-none`.trim()
|
|
35
|
-
// True when the parent passes a non-empty `tabs` array — switches to the
|
|
36
|
-
// legacy render path so existing snapshots stay byte-identical.
|
|
37
|
-
export const hasPropTabs = Array.isArray(tabs) && tabs.length > 0
|
|
38
|
-
</script>
|
|
39
|
-
|
|
40
|
-
<script client>
|
|
41
|
-
const emit = defineEmits()
|
|
42
|
-
const tabs = {{ tabs }}
|
|
43
|
-
const variant = {{ variant }}
|
|
44
|
-
const orientation = {{ orientation }}
|
|
45
|
-
const hasPropTabs = {{ hasPropTabs }}
|
|
46
|
-
const containerRef = useRef()
|
|
47
|
-
// In slot mode, `discoveredTabs` is populated from child <TabPanel> data
|
|
48
|
-
// attributes on mount. In prop mode, it mirrors the `tabs` prop so the
|
|
49
|
-
// rendering loop works the same in both modes.
|
|
50
|
-
const discoveredTabs = state(hasPropTabs ? tabs : [])
|
|
51
|
-
const activeTab = state({{ defaultTab }})
|
|
52
|
-
|
|
53
|
-
// Walk only direct descendants of THIS container (not nested Tabs').
|
|
54
|
-
function findOwnPanels() {
|
|
55
|
-
const root = containerRef.value
|
|
56
|
-
if (!root) return []
|
|
57
|
-
// Closest ancestor with data-stx-tabs must be this container's root —
|
|
58
|
-
// protects against nested <Tabs> contamination.
|
|
59
|
-
return Array.from(root.querySelectorAll('[data-stx-tab-panel]'))
|
|
60
|
-
.filter(p => p.closest('[data-stx-tabs]') === root)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
onMount(() => {
|
|
64
|
-
if (hasPropTabs) return // legacy mode: prop array already in discoveredTabs
|
|
65
|
-
const panels = findOwnPanels()
|
|
66
|
-
const list = panels.map(p => ({
|
|
67
|
-
label: p.dataset.label || '',
|
|
68
|
-
icon: p.dataset.icon || '',
|
|
69
|
-
}))
|
|
70
|
-
discoveredTabs.set(list)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
// In slot mode, toggle each panel's `hidden` attribute as activeTab changes.
|
|
74
|
-
// Prop mode is handled inline by the @foreach loop's :show binding below.
|
|
75
|
-
effect(() => {
|
|
76
|
-
if (hasPropTabs) return
|
|
77
|
-
const panels = findOwnPanels()
|
|
78
|
-
const idx = activeTab()
|
|
79
|
-
panels.forEach((p, i) => {
|
|
80
|
-
if (i === idx) p.removeAttribute('hidden')
|
|
81
|
-
else p.setAttribute('hidden', '')
|
|
82
|
-
})
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
function selectTab(index) {
|
|
86
|
-
activeTab.set(index)
|
|
87
|
-
const list = discoveredTabs()
|
|
88
|
-
emit('change', { index, tab: list[index] })
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function isActive(index) {
|
|
92
|
-
return activeTab() === index
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function getTabClasses(index) {
|
|
96
|
-
const isActiveTab = isActive(index)
|
|
97
|
-
const base = 'px-4 py-2 font-medium text-sm transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900'
|
|
98
|
-
if (variant === 'line') {
|
|
99
|
-
const border = orientation === 'horizontal' ? 'border-b-2' : 'border-r-2'
|
|
100
|
-
return `${base} ${border} ${isActiveTab ? 'border-blue-500 text-blue-600 dark:text-blue-400' : 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 hover:border-gray-300 dark:hover:border-gray-600'}`
|
|
101
|
-
}
|
|
102
|
-
if (variant === 'pills') {
|
|
103
|
-
return `${base} rounded-md ${isActiveTab ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'}`
|
|
104
|
-
}
|
|
105
|
-
if (variant === 'enclosed') {
|
|
106
|
-
const borders = orientation === 'horizontal' ? 'border-t border-l border-r rounded-t-md' : 'border-t border-l border-b rounded-l-md'
|
|
107
|
-
return `${base} ${borders} ${isActiveTab ? 'bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 -mb-px' : 'bg-gray-50 dark:bg-gray-900 border-transparent hover:border-gray-200 dark:hover:border-gray-700'}`
|
|
108
|
-
}
|
|
109
|
-
return base
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function onTabKey(event, index) {
|
|
113
|
-
const tabCount = discoveredTabs().length
|
|
114
|
-
let next = index
|
|
115
|
-
if (orientation === 'horizontal') {
|
|
116
|
-
if (event.key === 'ArrowRight') { event.preventDefault(); next = (index + 1) % tabCount }
|
|
117
|
-
else if (event.key === 'ArrowLeft') { event.preventDefault(); next = (index - 1 + tabCount) % tabCount }
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
if (event.key === 'ArrowDown') { event.preventDefault(); next = (index + 1) % tabCount }
|
|
121
|
-
else if (event.key === 'ArrowUp') { event.preventDefault(); next = (index - 1 + tabCount) % tabCount }
|
|
122
|
-
}
|
|
123
|
-
if (event.key === 'Home') { event.preventDefault(); next = 0 }
|
|
124
|
-
else if (event.key === 'End') { event.preventDefault(); next = tabCount - 1 }
|
|
125
|
-
if (next !== index) {
|
|
126
|
-
selectTab(next)
|
|
127
|
-
document.getElementById(`tab-${next}`)?.focus()
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
defineExpose({ activeTab, selectTab })
|
|
132
|
-
</script>
|
|
133
|
-
|
|
134
|
-
<div x-ref="containerRef" data-stx-tabs class="{{ containerClasses }}">
|
|
135
|
-
<div
|
|
136
|
-
class="{{ tabListClasses }}"
|
|
137
|
-
role="tablist"
|
|
138
|
-
aria-orientation="{{ orientation }}"
|
|
139
|
-
>
|
|
140
|
-
@if(hasPropTabs)
|
|
141
|
-
{{-- Legacy mode: server-side iterate the prop tabs array. Buttons --}}
|
|
142
|
-
{{-- get static class names so existing visual snapshots stay --}}
|
|
143
|
-
{{-- byte-identical. --}}
|
|
144
|
-
@foreach(tab in tabs)
|
|
145
|
-
<button
|
|
146
|
-
type="button"
|
|
147
|
-
:class="getTabClasses({{ $loop.index }})"
|
|
148
|
-
role="tab"
|
|
149
|
-
:aria-selected="isActive({{ $loop.index }}) ? 'true' : 'false'"
|
|
150
|
-
aria-controls="tab-panel-{{ $loop.index }}"
|
|
151
|
-
id="tab-{{ $loop.index }}"
|
|
152
|
-
:tabindex="isActive({{ $loop.index }}) ? '0' : '-1'"
|
|
153
|
-
@click="selectTab({{ $loop.index }})"
|
|
154
|
-
@keydown="onTabKey($event, {{ $loop.index }})"
|
|
155
|
-
>
|
|
156
|
-
@if(tab.icon)
|
|
157
|
-
<span class="inline-flex items-center gap-2">
|
|
158
|
-
<span x-html="{!! JSON.stringify(tab.icon) !!}"></span>
|
|
159
|
-
<span>{{ tab.label }}</span>
|
|
160
|
-
</span>
|
|
161
|
-
@else
|
|
162
|
-
{{ tab.label }}
|
|
163
|
-
@endif
|
|
164
|
-
</button>
|
|
165
|
-
@endforeach
|
|
166
|
-
@else
|
|
167
|
-
{{-- Slot mode: render the tab list dynamically from --}}
|
|
168
|
-
{{-- discoveredTabs() which is populated in onMount. --}}
|
|
169
|
-
<button
|
|
170
|
-
:for="(tab, idx) in discoveredTabs()"
|
|
171
|
-
:key="idx"
|
|
172
|
-
type="button"
|
|
173
|
-
x-class="getTabClasses(idx)"
|
|
174
|
-
role="tab"
|
|
175
|
-
:aria-selected="isActive(idx) ? 'true' : 'false'"
|
|
176
|
-
:tabindex="isActive(idx) ? 0 : -1"
|
|
177
|
-
@click="selectTab(idx)"
|
|
178
|
-
@keydown="onTabKey($event, idx)"
|
|
179
|
-
>
|
|
180
|
-
<span :if="tab.icon" class="inline-flex items-center gap-2" x-html="tab.icon"></span>
|
|
181
|
-
<span :text="tab.label"></span>
|
|
182
|
-
</button>
|
|
183
|
-
@endif
|
|
184
|
-
</div>
|
|
185
|
-
|
|
186
|
-
@if(hasPropTabs)
|
|
187
|
-
@foreach(tab in tabs)
|
|
188
|
-
<div
|
|
189
|
-
:show="isActive({{ $loop.index }})"
|
|
190
|
-
id="tab-panel-{{ $loop.index }}"
|
|
191
|
-
class="{{ tabPanelClasses }}"
|
|
192
|
-
role="tabpanel"
|
|
193
|
-
aria-labelledby="tab-{{ $loop.index }}"
|
|
194
|
-
tabindex="0"
|
|
195
|
-
>
|
|
196
|
-
{!! tab.content !!}
|
|
197
|
-
</div>
|
|
198
|
-
@endforeach
|
|
199
|
-
@else
|
|
200
|
-
<slot />
|
|
201
|
-
@endif
|
|
202
|
-
</div>
|
package/src/ui/tabs/index.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
export { default as TabPanel } from './TabPanel.stx'
|
|
2
|
-
export { default as Tabs } from './Tabs.stx'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Legacy prop API — pass `tabs` as an array of `{ label, content, icon? }`
|
|
6
|
-
* where `content` is a HTML string. Kept for backward compatibility; prefer
|
|
7
|
-
* the slot API below for any tab whose content needs to be a component tree.
|
|
8
|
-
*
|
|
9
|
-
* See stacksjs/stx#1703.
|
|
10
|
-
*/
|
|
11
|
-
export interface Tab {
|
|
12
|
-
label: string
|
|
13
|
-
content: string
|
|
14
|
-
icon?: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface TabsProps {
|
|
18
|
-
/** Legacy: array of tab definitions with string content. */
|
|
19
|
-
tabs?: Tab[]
|
|
20
|
-
defaultTab?: number
|
|
21
|
-
orientation?: 'horizontal' | 'vertical'
|
|
22
|
-
variant?: 'line' | 'pills' | 'enclosed'
|
|
23
|
-
onChange?: (index: number, tab: Tab) => void
|
|
24
|
-
className?: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Slot API — wrap each tab's content in a `<TabPanel label="…">`. The parent
|
|
29
|
-
* `<Tabs>` discovers panels on mount via `[data-stx-tab-panel]` data
|
|
30
|
-
* attributes, builds the tab list dynamically, and toggles each panel's
|
|
31
|
-
* `hidden` attribute as the active tab changes.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```html
|
|
35
|
-
* <Tabs defaultTab="0" variant="pills">
|
|
36
|
-
* <TabPanel label="Drivers">
|
|
37
|
-
* <DriversTable :drivers="drivers()" />
|
|
38
|
-
* </TabPanel>
|
|
39
|
-
* <TabPanel label="Notifications" icon="bell">
|
|
40
|
-
* <NotificationsTable />
|
|
41
|
-
* </TabPanel>
|
|
42
|
-
* </Tabs>
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
export interface TabPanelProps {
|
|
46
|
-
label: string
|
|
47
|
-
/** Optional icon HTML (rendered via `x-html`) for the tab button. */
|
|
48
|
-
icon?: string
|
|
49
|
-
className?: string
|
|
50
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
export const to = $props.to || 'body'
|
|
3
|
-
export const disabled = $props.disabled || false
|
|
4
|
-
export const defer = $props.defer || false
|
|
5
|
-
|
|
6
|
-
export const teleportId = `teleport-${Math.random().toString(36).slice(2, 11)}`
|
|
7
|
-
</script>
|
|
8
|
-
|
|
9
|
-
<script client>
|
|
10
|
-
const to = {{ to }}
|
|
11
|
-
const isDisabled = {{ disabled }}
|
|
12
|
-
const shouldDefer = {{ defer }}
|
|
13
|
-
const teleportId = {{ teleportId }}
|
|
14
|
-
|
|
15
|
-
let teleportContainer = null
|
|
16
|
-
|
|
17
|
-
function teleport() {
|
|
18
|
-
if (isDisabled) return
|
|
19
|
-
const targetElement = typeof to === 'string' ? document.querySelector(to) : to
|
|
20
|
-
if (!targetElement) {
|
|
21
|
-
console.warn(`Teleport target "${to}" not found`)
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
const sourceElement = document.getElementById(teleportId)
|
|
25
|
-
if (!sourceElement) return
|
|
26
|
-
if (!teleportContainer) {
|
|
27
|
-
teleportContainer = document.createElement('div')
|
|
28
|
-
teleportContainer.setAttribute('data-teleport-id', teleportId)
|
|
29
|
-
}
|
|
30
|
-
while (sourceElement.firstChild) {
|
|
31
|
-
teleportContainer.appendChild(sourceElement.firstChild)
|
|
32
|
-
}
|
|
33
|
-
targetElement.appendChild(teleportContainer)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function returnContent() {
|
|
37
|
-
if (!teleportContainer || !teleportContainer.parentNode) return
|
|
38
|
-
const sourceElement = document.getElementById(teleportId)
|
|
39
|
-
if (!sourceElement) return
|
|
40
|
-
while (teleportContainer.firstChild) {
|
|
41
|
-
sourceElement.appendChild(teleportContainer.firstChild)
|
|
42
|
-
}
|
|
43
|
-
teleportContainer.parentNode.removeChild(teleportContainer)
|
|
44
|
-
teleportContainer = null
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
onMount(() => {
|
|
48
|
-
if (shouldDefer) setTimeout(teleport, 0)
|
|
49
|
-
else teleport()
|
|
50
|
-
window.addEventListener('beforeunload', returnContent)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
onDestroy(() => {
|
|
54
|
-
returnContent()
|
|
55
|
-
window.removeEventListener('beforeunload', returnContent)
|
|
56
|
-
})
|
|
57
|
-
</script>
|
|
58
|
-
|
|
59
|
-
@if(disabled)
|
|
60
|
-
<slot />
|
|
61
|
-
@else
|
|
62
|
-
<div id="{{ teleportId }}" data-teleport-source="true">
|
|
63
|
-
<slot />
|
|
64
|
-
</div>
|
|
65
|
-
@endif
|
package/src/ui/teleport/index.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
export const value = $props.value || ''
|
|
3
|
-
export const placeholder = $props.placeholder || ''
|
|
4
|
-
export const disabled = $props.disabled || false
|
|
5
|
-
export const readonly = $props.readonly || false
|
|
6
|
-
export const rows = $props.rows || 4
|
|
7
|
-
export const autoResize = $props.autoResize || false
|
|
8
|
-
export const maxRows = $props.maxRows || 10
|
|
9
|
-
export const error = $props.error || false
|
|
10
|
-
export const label = $props.label || ''
|
|
11
|
-
export const helperText = $props.helperText || ''
|
|
12
|
-
export const required = $props.required || false
|
|
13
|
-
export const maxLength = $props.maxLength
|
|
14
|
-
export const showCount = $props.showCount || false
|
|
15
|
-
export const resize = $props.resize || 'vertical'
|
|
16
|
-
export const className = $props.className || ''
|
|
17
|
-
|
|
18
|
-
const resizeClasses = { none: 'resize-none', vertical: 'resize-y', horizontal: 'resize-x', both: 'resize' }
|
|
19
|
-
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 px-4 py-2 text-base dark:bg-blue-gray-700 dark:text-gray-100'
|
|
20
|
-
const stateClasses = error
|
|
21
|
-
? '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'
|
|
22
|
-
: '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'
|
|
23
|
-
const disabledClasses = disabled
|
|
24
|
-
? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
|
|
25
|
-
: readonly
|
|
26
|
-
? 'bg-gray-50 dark:bg-gray-800 cursor-default'
|
|
27
|
-
: 'bg-white dark:bg-blue-gray-700'
|
|
28
|
-
|
|
29
|
-
export const textareaClasses = `${baseClasses} ${stateClasses} ${disabledClasses} ${resizeClasses[resize]} ${className}`.trim()
|
|
30
|
-
export const labelClasses = `block text-sm font-medium leading-6 mb-2 ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100'}`.trim()
|
|
31
|
-
export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
|
|
32
|
-
</script>
|
|
33
|
-
|
|
34
|
-
<script client>
|
|
35
|
-
const emit = defineEmits()
|
|
36
|
-
const autoResize = {{ autoResize }}
|
|
37
|
-
const maxRows = {{ maxRows }}
|
|
38
|
-
// Reactive prop binding — parent can drive `value` via signals. See #1704.
|
|
39
|
-
const inputValue = useReactiveProp('value', {{ value }}, { parse: (v) => v == null ? '' : String(v) })
|
|
40
|
-
|
|
41
|
-
function resizeTextarea(el) {
|
|
42
|
-
if (!autoResize) return
|
|
43
|
-
el.style.height = 'auto'
|
|
44
|
-
const scrollHeight = el.scrollHeight
|
|
45
|
-
const lineHeight = Number.parseInt(window.getComputedStyle(el).lineHeight) || 20
|
|
46
|
-
const maxHeight = lineHeight * maxRows
|
|
47
|
-
el.style.height = `${Math.min(scrollHeight, maxHeight)}px`
|
|
48
|
-
el.style.overflowY = scrollHeight > maxHeight ? 'auto' : 'hidden'
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function onInput(event) {
|
|
52
|
-
inputValue.set(event.target.value)
|
|
53
|
-
resizeTextarea(event.target)
|
|
54
|
-
emit('input', inputValue())
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
onMount(() => {
|
|
58
|
-
if (autoResize) {
|
|
59
|
-
const el = document.getElementById('textarea-input')
|
|
60
|
-
if (el) resizeTextarea(el)
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const charCount = derived(() => `${inputValue().length} / {{ maxLength || 0 }}`)
|
|
65
|
-
|
|
66
|
-
defineExpose({ value: inputValue })
|
|
67
|
-
</script>
|
|
68
|
-
|
|
69
|
-
<div class="w-full">
|
|
70
|
-
@if(label)
|
|
71
|
-
<label for="textarea-input" class="{{ labelClasses }}">
|
|
72
|
-
{{ label }}
|
|
73
|
-
@if(required)
|
|
74
|
-
<span class="text-red-500 ml-1">*</span>
|
|
75
|
-
@endif
|
|
76
|
-
</label>
|
|
77
|
-
@endif
|
|
78
|
-
|
|
79
|
-
<textarea
|
|
80
|
-
id="textarea-input"
|
|
81
|
-
class="{{ textareaClasses }}"
|
|
82
|
-
rows="{{ rows }}"
|
|
83
|
-
placeholder="{{ placeholder }}"
|
|
84
|
-
@if(disabled)disabled@endif
|
|
85
|
-
@if(readonly)readonly@endif
|
|
86
|
-
@if(required)required@endif
|
|
87
|
-
@if(maxLength)maxlength="{{ maxLength }}"@endif
|
|
88
|
-
@input="onInput($event)"
|
|
89
|
-
:aria-invalid="{{ error ? 'true' : 'false' }}"
|
|
90
|
-
@if(helperText)aria-describedby="helper-text"@endif
|
|
91
|
-
>{{ value }}</textarea>
|
|
92
|
-
|
|
93
|
-
@if(helperText || (showCount && maxLength))
|
|
94
|
-
<div class="flex items-center justify-between mt-2">
|
|
95
|
-
@if(helperText)
|
|
96
|
-
<p id="helper-text" class="{{ helperClasses }}">
|
|
97
|
-
{{ helperText }}
|
|
98
|
-
</p>
|
|
99
|
-
@else
|
|
100
|
-
<span></span>
|
|
101
|
-
@endif
|
|
102
|
-
|
|
103
|
-
@if(showCount && maxLength)
|
|
104
|
-
<p class="text-sm text-gray-500 dark:text-gray-400" x-text="charCount()"></p>
|
|
105
|
-
@endif
|
|
106
|
-
</div>
|
|
107
|
-
@endif
|
|
108
|
-
</div>
|
package/src/ui/textarea/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export { default as Textarea } from './Textarea.stx'
|
|
2
|
-
|
|
3
|
-
export interface TextareaProps {
|
|
4
|
-
value?: string
|
|
5
|
-
onChange?: (value: string) => void
|
|
6
|
-
placeholder?: string
|
|
7
|
-
disabled?: boolean
|
|
8
|
-
readonly?: boolean
|
|
9
|
-
rows?: number
|
|
10
|
-
autoResize?: boolean
|
|
11
|
-
maxRows?: number
|
|
12
|
-
error?: boolean
|
|
13
|
-
label?: string
|
|
14
|
-
helperText?: string
|
|
15
|
-
required?: boolean
|
|
16
|
-
maxLength?: number
|
|
17
|
-
showCount?: boolean
|
|
18
|
-
resize?: 'none' | 'vertical' | 'horizontal' | 'both'
|
|
19
|
-
className?: string
|
|
20
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
<script server>
|
|
2
|
-
export const content = $props.content || ''
|
|
3
|
-
export const position = $props.position || 'top'
|
|
4
|
-
export const delay = $props.delay || 0
|
|
5
|
-
export const className = $props.className || ''
|
|
6
|
-
export const show = $props.show || false
|
|
7
|
-
export const disabled = $props.disabled || false
|
|
8
|
-
|
|
9
|
-
const positionClasses = {
|
|
10
|
-
top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',
|
|
11
|
-
bottom: 'top-full left-1/2 -translate-x-1/2 mt-2',
|
|
12
|
-
left: 'right-full top-1/2 -translate-y-1/2 mr-2',
|
|
13
|
-
right: 'left-full top-1/2 -translate-y-1/2 ml-2',
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const arrowClasses = {
|
|
17
|
-
top: 'top-full left-1/2 -translate-x-1/2 border-t-gray-900 dark:border-t-gray-700',
|
|
18
|
-
bottom: 'bottom-full left-1/2 -translate-x-1/2 border-b-gray-900 dark:border-b-gray-700',
|
|
19
|
-
left: 'left-full top-1/2 -translate-y-1/2 border-l-gray-900 dark:border-l-gray-700',
|
|
20
|
-
right: 'right-full top-1/2 -translate-y-1/2 border-r-gray-900 dark:border-r-gray-700',
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const tooltipClasses = `absolute z-50 px-3 py-2 text-sm font-medium text-white bg-gray-900 dark:bg-gray-700 rounded-lg shadow-lg whitespace-nowrap pointer-events-none transition-opacity duration-200 ${positionClasses[position]} ${className}`.trim()
|
|
24
|
-
export const arrowClass = `absolute w-0 h-0 border-4 border-transparent ${arrowClasses[position]}`.trim()
|
|
25
|
-
</script>
|
|
26
|
-
|
|
27
|
-
<script client>
|
|
28
|
-
const delay = {{ delay }}
|
|
29
|
-
const isDisabled = {{ disabled }}
|
|
30
|
-
// Reactive prop binding — parent can drive `show` via signals. See #1704.
|
|
31
|
-
const isVisible = useReactiveProp('show', {{ show }})
|
|
32
|
-
let timeoutId = null
|
|
33
|
-
|
|
34
|
-
function showTooltip() {
|
|
35
|
-
if (isDisabled) return
|
|
36
|
-
if (delay > 0) {
|
|
37
|
-
timeoutId = setTimeout(() => isVisible.set(true), delay)
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
isVisible.set(true)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function hideTooltip() {
|
|
45
|
-
if (timeoutId) {
|
|
46
|
-
clearTimeout(timeoutId)
|
|
47
|
-
timeoutId = null
|
|
48
|
-
}
|
|
49
|
-
isVisible.set(false)
|
|
50
|
-
}
|
|
51
|
-
</script>
|
|
52
|
-
|
|
53
|
-
<div class="relative inline-block {{ disabled ? 'cursor-not-allowed' : '' }}">
|
|
54
|
-
<div
|
|
55
|
-
@mouseenter="showTooltip()"
|
|
56
|
-
@mouseleave="hideTooltip()"
|
|
57
|
-
@focusin="showTooltip()"
|
|
58
|
-
@focusout="hideTooltip()"
|
|
59
|
-
>
|
|
60
|
-
<slot />
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
<div
|
|
64
|
-
:show="isVisible() && !{{ disabled }}"
|
|
65
|
-
class="{{ tooltipClasses }}"
|
|
66
|
-
role="tooltip"
|
|
67
|
-
>
|
|
68
|
-
{{ content }}
|
|
69
|
-
<div class="{{ arrowClass }}"></div>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
package/src/ui/tooltip/index.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# Transition Component
|
|
2
|
-
|
|
3
|
-
Smooth enter/leave transitions for showing and hiding content.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
bun add @stacksjs/components
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
```stx
|
|
14
|
-
<script server>
|
|
15
|
-
let isOpen = true
|
|
16
|
-
</script>
|
|
17
|
-
|
|
18
|
-
<Transition :show="isOpen">
|
|
19
|
-
<div class="p-4 bg-white rounded-lg shadow-lg">
|
|
20
|
-
Content with fade transition
|
|
21
|
-
</div>
|
|
22
|
-
</Transition>
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Features
|
|
26
|
-
|
|
27
|
-
- Enter/leave animations
|
|
28
|
-
- Customizable transition classes
|
|
29
|
-
- Dark mode support
|
|
30
|
-
- Headwind utility classes
|