@stacksjs/components 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +477 -0
- package/dist/Accordion-x2gjedap.stx +108 -0
- package/dist/Audio-0fk6p9tn.stx +50 -0
- package/dist/Avatar-5m0v0s4x.stx +88 -0
- package/dist/Badge-ynes82vv.stx +70 -0
- package/dist/Breadcrumb-h9n427sk.stx +79 -0
- package/dist/Button-7xtrzn14.stx +89 -0
- package/dist/Calendar-egvx1pma.stx +232 -0
- package/dist/Card-r9w7vy1a.stx +74 -0
- package/dist/Checkbox-m8v46pmx.stx +92 -0
- package/dist/Checkout-vcnnr176.stx +39 -0
- package/dist/CodeBlock-mwex82cm.stx +57 -0
- package/dist/Combobox-npb08vkp.stx +13 -0
- package/dist/ComboboxButton-vcdyq4zw.stx +13 -0
- package/dist/ComboboxInput-c9d2p02m.stx +25 -0
- package/dist/ComboboxOption-7d0h9ash.stx +52 -0
- package/dist/ComboboxOptions-4dtqc0qa.stx +12 -0
- package/dist/CommandPalette-ah00bf8v.stx +39 -0
- package/dist/CommandPaletteItem-2yx0gvtj.stx +32 -0
- package/dist/DefaultPaymentMethod-emw5fg8s.stx +62 -0
- package/dist/Dialog-1w4rz7x0.stx +19 -0
- package/dist/DialogBackdrop-qbm3y5ab.stx +9 -0
- package/dist/DialogDescription-01w5j74b.stx +11 -0
- package/dist/DialogPanel-brsszrrs.stx +16 -0
- package/dist/DialogTitle-3rxhwg21.stx +11 -0
- package/dist/Drawer-yfaz8q7f.stx +137 -0
- package/dist/Dropdown-ng7nx556.stx +24 -0
- package/dist/DropdownButton-vc4zpyrb.stx +23 -0
- package/dist/DropdownItem-z491y23c.stx +43 -0
- package/dist/DropdownItems-7ykf29sj.stx +18 -0
- package/dist/EmailInput-bfshpj92.stx +41 -0
- package/dist/Footer-xdaf02nr.stx +66 -0
- package/dist/Form-z4f7w6r1.stx +209 -0
- package/dist/Hero-9vvp7pxf.stx +25 -0
- package/dist/Image-ktk0451c.stx +197 -0
- package/dist/Installation-33bj4rm7.stx +39 -0
- package/dist/Listbox-xcejcmyc.stx +14 -0
- package/dist/ListboxButton-nx5kmw19.stx +17 -0
- package/dist/ListboxLabel-aky8a73n.stx +11 -0
- package/dist/ListboxOption-6h4hsdz5.stx +57 -0
- package/dist/ListboxOptions-2sacdtm5.stx +16 -0
- package/dist/Login-s2jkvd9y.stx +163 -0
- package/dist/Navigator-tqqdgdkw.stx +138 -0
- package/dist/Notification-sry8egea.stx +71 -0
- package/dist/NumberInput-zm93ddfw.stx +138 -0
- package/dist/Pagination-yza9v4qw.stx +183 -0
- package/dist/PasswordInput-4xnndp5y.stx +155 -0
- package/dist/PaymentMethods-jxba6kek.stx +113 -0
- package/dist/Popover-scbqatjq.stx +24 -0
- package/dist/PopoverButton-rbxhzj4k.stx +23 -0
- package/dist/PopoverPanel-9ng1rm6e.stx +23 -0
- package/dist/Portal-j111yvqp.stx +74 -0
- package/dist/Progress-gbmw3akr.stx +142 -0
- package/dist/Radio-1n9qamv1.stx +81 -0
- package/dist/RadioGroup-12dw2vhk.stx +13 -0
- package/dist/RadioGroupDescription-bjm3mdht.stx +11 -0
- package/dist/RadioGroupLabel-aky8a73n.stx +11 -0
- package/dist/RadioGroupOption-9pcxgwmd.stx +51 -0
- package/dist/SearchInput-g240e2nq.stx +63 -0
- package/dist/Select-p9f94b20.stx +89 -0
- package/dist/Sidebar-gwxw8q38.stx +288 -0
- package/dist/SidebarFooter-887kxret.stx +95 -0
- package/dist/SidebarHeader-qjeyx0hv.stx +100 -0
- package/dist/SidebarItem-4dpwtmb4.stx +118 -0
- package/dist/SidebarSection-kh92fk4m.stx +108 -0
- package/dist/Signup-3nxq5zag.stx +161 -0
- package/dist/Skeleton-g0nx1njm.stx +48 -0
- package/dist/Spinner-w4zj58xp.stx +109 -0
- package/dist/Stepper-rwc5actp.stx +17 -0
- package/dist/StepperStep-h0k657nm.stx +53 -0
- package/dist/SubscriptionCheckout-4cbxdg7w.stx +353 -0
- package/dist/Switch-kjq8qd2p.stx +74 -0
- package/dist/Table-59j9qwp0.stx +14 -0
- package/dist/TableBody-reka2gwv.stx +10 -0
- package/dist/TableCell-j7nh4sgr.stx +10 -0
- package/dist/TableHead-ecbrwpqm.stx +10 -0
- package/dist/TableHeader-jn2shwsv.stx +13 -0
- package/dist/TableRow-xz8rrxhc.stx +11 -0
- package/dist/Tabs-kb6ex6m7.stx +145 -0
- package/dist/Teleport-819cg9zm.stx +101 -0
- package/dist/TextInput-zx9kgr79.stx +146 -0
- package/dist/Textarea-5xvmrb0z.stx +119 -0
- package/dist/Tooltip-5ehqsy49.stx +76 -0
- package/dist/Transition-2j39s044.stx +174 -0
- package/dist/TwoFactorChallenge-2q4gg89m.stx +125 -0
- package/dist/Video-6aq4shh4.stx +45 -0
- package/dist/VirtualList-weh5qtm1.stx +83 -0
- package/dist/VirtualTable-59a1eskc.stx +113 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/composables/index.d.ts +4 -0
- package/dist/composables/index.js +3 -0
- package/dist/composables/useCopyCode.d.ts +19 -0
- package/dist/composables/useDarkMode.d.ts +25 -0
- package/dist/composables/useSEO.d.ts +36 -0
- package/dist/composables/utilities/index.d.ts +5 -0
- package/dist/composables/utilities/useClickOutside.d.ts +28 -0
- package/dist/composables/utilities/useFocusTrap.d.ts +34 -0
- package/dist/composables/utilities/useKeyboard.d.ts +55 -0
- package/dist/composables/utilities/useLocalStorage.d.ts +33 -0
- package/dist/composables/utilities/useMediaQuery.d.ts +64 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +19 -0
- package/dist/ui/accordion/index.d.ts +12 -0
- package/dist/ui/audio/index.d.ts +12 -0
- package/dist/ui/auth/index.d.ts +29 -0
- package/dist/ui/avatar/index.d.ts +11 -0
- package/dist/ui/badge/index.d.ts +9 -0
- package/dist/ui/breadcrumb/index.d.ts +13 -0
- package/dist/ui/button/index.d.ts +45 -0
- package/dist/ui/calendar/index.d.ts +13 -0
- package/dist/ui/card/index.d.ts +12 -0
- package/dist/ui/checkbox/index.d.ts +15 -0
- package/dist/ui/combobox/index.d.ts +35 -0
- package/dist/ui/command-palette/index.d.ts +13 -0
- package/dist/ui/dialog/index.d.ts +27 -0
- package/dist/ui/drawer/index.d.ts +8 -0
- package/dist/ui/dropdown/index.d.ts +25 -0
- package/dist/ui/form/index.d.ts +48 -0
- package/dist/ui/image/index.d.ts +21 -0
- package/dist/ui/input/index.d.ts +73 -0
- package/dist/ui/listbox/index.d.ts +34 -0
- package/dist/ui/navigator/index.d.ts +18 -0
- package/dist/ui/notification/index.d.ts +11 -0
- package/dist/ui/pagination/index.d.ts +10 -0
- package/dist/ui/payment/index.d.ts +53 -0
- package/dist/ui/popover/index.d.ts +19 -0
- package/dist/ui/portal/index.d.ts +5 -0
- package/dist/ui/progress/index.d.ts +11 -0
- package/dist/ui/radio/index.d.ts +14 -0
- package/dist/ui/radio-group/index.d.ts +26 -0
- package/dist/ui/select/index.d.ts +19 -0
- package/dist/ui/sidebar/index.d.ts +129 -0
- package/dist/ui/skeleton/index.d.ts +10 -0
- package/dist/ui/spinner/index.d.ts +8 -0
- package/dist/ui/stepper/index.d.ts +17 -0
- package/dist/ui/storage/index.d.ts +40 -0
- package/dist/ui/switch/index.d.ts +9 -0
- package/dist/ui/table/index.d.ts +29 -0
- package/dist/ui/tabs/index.d.ts +14 -0
- package/dist/ui/teleport/index.d.ts +6 -0
- package/dist/ui/textarea/index.d.ts +19 -0
- package/dist/ui/tooltip/index.d.ts +9 -0
- package/dist/ui/transition/index.d.ts +24 -0
- package/dist/ui/video/index.d.ts +15 -0
- package/dist/ui/virtual-list/index.d.ts +9 -0
- package/dist/ui/virtual-table/index.d.ts +19 -0
- package/dist/utils/accessibility.d.ts +285 -0
- package/dist/utils/animation/easing.d.ts +112 -0
- package/dist/utils/animation/helpers.d.ts +119 -0
- package/dist/utils/animation/index.d.ts +3 -0
- package/dist/utils/animation/keyframes.d.ts +334 -0
- package/dist/utils/error-handling.d.ts +182 -0
- package/dist/utils/highlighter.d.ts +27 -0
- package/dist/utils/highlighter.js +2 -0
- package/dist/utils/i18n.d.ts +245 -0
- package/dist/utils/keep-alive.d.ts +171 -0
- package/dist/utils/lazy-loading.d.ts +174 -0
- package/dist/utils/prop-validation.d.ts +106 -0
- package/dist/utils/theme.d.ts +234 -0
- package/package.json +69 -0
- package/src/components/CodeBlock.stx +57 -0
- package/src/components/Footer.stx +66 -0
- package/src/components/Hero.stx +25 -0
- package/src/components/Installation.stx +39 -0
- package/src/components/index.ts +4 -0
- package/src/composables/index.ts +4 -0
- package/src/composables/useCopyCode.ts +73 -0
- package/src/composables/useDarkMode.ts +100 -0
- package/src/composables/useSEO.ts +229 -0
- package/src/composables/utilities/index.ts +7 -0
- package/src/composables/utilities/useClickOutside.ts +72 -0
- package/src/composables/utilities/useFocusTrap.ts +159 -0
- package/src/composables/utilities/useKeyboard.ts +124 -0
- package/src/composables/utilities/useLocalStorage.ts +132 -0
- package/src/composables/utilities/useMediaQuery.ts +118 -0
- package/src/index.ts +99 -0
- package/src/stx.d.ts +4 -0
- package/src/ui/accordion/Accordion.stx +108 -0
- package/src/ui/accordion/index.ts +14 -0
- package/src/ui/audio/Audio.stx +50 -0
- package/src/ui/audio/README.md +42 -0
- package/src/ui/audio/index.ts +13 -0
- package/src/ui/auth/Login.stx +163 -0
- package/src/ui/auth/README.md +145 -0
- package/src/ui/auth/Signup.stx +161 -0
- package/src/ui/auth/TwoFactorChallenge.stx +125 -0
- package/src/ui/auth/index.ts +32 -0
- package/src/ui/avatar/Avatar.stx +88 -0
- package/src/ui/avatar/index.ts +12 -0
- package/src/ui/badge/Badge.stx +70 -0
- package/src/ui/badge/index.ts +10 -0
- package/src/ui/breadcrumb/Breadcrumb.stx +79 -0
- package/src/ui/breadcrumb/index.ts +15 -0
- package/src/ui/button/Button.stx +89 -0
- package/src/ui/button/README.md +100 -0
- package/src/ui/button/index.ts +85 -0
- package/src/ui/calendar/Calendar.stx +232 -0
- package/src/ui/calendar/README.md +254 -0
- package/src/ui/calendar/index.ts +14 -0
- package/src/ui/card/Card.stx +74 -0
- package/src/ui/card/index.ts +13 -0
- package/src/ui/checkbox/Checkbox.stx +92 -0
- package/src/ui/checkbox/index.ts +16 -0
- package/src/ui/combobox/Combobox.stx +13 -0
- package/src/ui/combobox/ComboboxButton.stx +13 -0
- package/src/ui/combobox/ComboboxInput.stx +25 -0
- package/src/ui/combobox/ComboboxOption.stx +52 -0
- package/src/ui/combobox/ComboboxOptions.stx +12 -0
- package/src/ui/combobox/README.md +154 -0
- package/src/ui/combobox/index.ts +40 -0
- package/src/ui/command-palette/CommandPalette.stx +39 -0
- package/src/ui/command-palette/CommandPaletteItem.stx +32 -0
- package/src/ui/command-palette/README.md +42 -0
- package/src/ui/command-palette/index.ts +15 -0
- package/src/ui/dialog/Dialog.stx +19 -0
- package/src/ui/dialog/DialogBackdrop.stx +9 -0
- package/src/ui/dialog/DialogDescription.stx +11 -0
- package/src/ui/dialog/DialogPanel.stx +16 -0
- package/src/ui/dialog/DialogTitle.stx +11 -0
- package/src/ui/dialog/README.md +133 -0
- package/src/ui/dialog/index.ts +32 -0
- package/src/ui/drawer/Drawer.stx +137 -0
- package/src/ui/drawer/README.md +110 -0
- package/src/ui/drawer/index.ts +9 -0
- package/src/ui/dropdown/Dropdown.stx +24 -0
- package/src/ui/dropdown/DropdownButton.stx +23 -0
- package/src/ui/dropdown/DropdownItem.stx +43 -0
- package/src/ui/dropdown/DropdownItems.stx +18 -0
- package/src/ui/dropdown/README.md +142 -0
- package/src/ui/dropdown/index.ts +29 -0
- package/src/ui/form/Form.stx +209 -0
- package/src/ui/form/index.ts +67 -0
- package/src/ui/heatmap/Heatmap.stx +315 -0
- package/src/ui/image/Image.stx +197 -0
- package/src/ui/image/README.md +171 -0
- package/src/ui/image/index.ts +22 -0
- package/src/ui/input/EmailInput.stx +41 -0
- package/src/ui/input/NumberInput.stx +138 -0
- package/src/ui/input/PasswordInput.stx +155 -0
- package/src/ui/input/SearchInput.stx +63 -0
- package/src/ui/input/TextInput.stx +146 -0
- package/src/ui/input/index.ts +180 -0
- package/src/ui/listbox/Listbox.stx +14 -0
- package/src/ui/listbox/ListboxButton.stx +17 -0
- package/src/ui/listbox/ListboxLabel.stx +11 -0
- package/src/ui/listbox/ListboxOption.stx +57 -0
- package/src/ui/listbox/ListboxOptions.stx +16 -0
- package/src/ui/listbox/README.md +143 -0
- package/src/ui/listbox/index.ts +39 -0
- package/src/ui/navigator/Navigator.stx +138 -0
- package/src/ui/navigator/README.md +352 -0
- package/src/ui/navigator/index.ts +20 -0
- package/src/ui/notification/Notification.stx +71 -0
- package/src/ui/notification/README.md +118 -0
- package/src/ui/notification/index.ts +12 -0
- package/src/ui/pagination/Pagination.stx +183 -0
- package/src/ui/pagination/index.ts +11 -0
- package/src/ui/payment/Checkout.stx +39 -0
- package/src/ui/payment/DefaultPaymentMethod.stx +62 -0
- package/src/ui/payment/PaymentMethods.stx +113 -0
- package/src/ui/payment/README.md +427 -0
- package/src/ui/payment/SubscriptionCheckout.stx +353 -0
- package/src/ui/payment/index.ts +59 -0
- package/src/ui/popover/Popover.stx +24 -0
- package/src/ui/popover/PopoverButton.stx +23 -0
- package/src/ui/popover/PopoverPanel.stx +23 -0
- package/src/ui/popover/README.md +95 -0
- package/src/ui/popover/index.ts +22 -0
- package/src/ui/portal/Portal.stx +74 -0
- package/src/ui/portal/index.ts +6 -0
- package/src/ui/progress/Progress.stx +142 -0
- package/src/ui/progress/index.ts +12 -0
- package/src/ui/radio/Radio.stx +81 -0
- package/src/ui/radio/index.ts +15 -0
- package/src/ui/radio-group/README.md +145 -0
- package/src/ui/radio-group/RadioGroup.stx +13 -0
- package/src/ui/radio-group/RadioGroupDescription.stx +11 -0
- package/src/ui/radio-group/RadioGroupLabel.stx +11 -0
- package/src/ui/radio-group/RadioGroupOption.stx +51 -0
- package/src/ui/radio-group/index.ts +30 -0
- package/src/ui/select/README.md +204 -0
- package/src/ui/select/Select.stx +89 -0
- package/src/ui/select/index.ts +21 -0
- package/src/ui/sidebar/Sidebar.stx +288 -0
- package/src/ui/sidebar/SidebarFooter.stx +95 -0
- package/src/ui/sidebar/SidebarHeader.stx +100 -0
- package/src/ui/sidebar/SidebarItem.stx +118 -0
- package/src/ui/sidebar/SidebarSection.stx +108 -0
- package/src/ui/sidebar/index.ts +175 -0
- package/src/ui/skeleton/Skeleton.stx +48 -0
- package/src/ui/skeleton/index.ts +11 -0
- package/src/ui/spinner/Spinner.stx +109 -0
- package/src/ui/spinner/index.ts +9 -0
- package/src/ui/stepper/README.md +36 -0
- package/src/ui/stepper/Stepper.stx +17 -0
- package/src/ui/stepper/StepperStep.stx +53 -0
- package/src/ui/stepper/index.ts +19 -0
- package/src/ui/storage/README.md +193 -0
- package/src/ui/storage/index.ts +347 -0
- package/src/ui/switch/README.md +133 -0
- package/src/ui/switch/Switch.stx +74 -0
- package/src/ui/switch/index.ts +10 -0
- package/src/ui/table/README.md +39 -0
- package/src/ui/table/Table.stx +14 -0
- package/src/ui/table/TableBody.stx +10 -0
- package/src/ui/table/TableCell.stx +10 -0
- package/src/ui/table/TableHead.stx +10 -0
- package/src/ui/table/TableHeader.stx +13 -0
- package/src/ui/table/TableRow.stx +11 -0
- package/src/ui/table/index.ts +35 -0
- package/src/ui/tabs/Tabs.stx +145 -0
- package/src/ui/tabs/index.ts +16 -0
- package/src/ui/teleport/Teleport.stx +101 -0
- package/src/ui/teleport/index.ts +7 -0
- package/src/ui/textarea/Textarea.stx +119 -0
- package/src/ui/textarea/index.ts +20 -0
- package/src/ui/tooltip/Tooltip.stx +76 -0
- package/src/ui/tooltip/index.ts +10 -0
- package/src/ui/transition/README.md +30 -0
- package/src/ui/transition/Transition.stx +174 -0
- package/src/ui/transition/index.ts +25 -0
- package/src/ui/video/README.md +48 -0
- package/src/ui/video/Video.stx +45 -0
- package/src/ui/video/index.ts +16 -0
- package/src/ui/virtual-list/VirtualList.stx +83 -0
- package/src/ui/virtual-list/index.ts +10 -0
- package/src/ui/virtual-table/VirtualTable.stx +113 -0
- package/src/ui/virtual-table/index.ts +21 -0
- package/src/utils/PROP_VALIDATION.md +458 -0
- package/src/utils/THEME_SYSTEM.md +702 -0
- package/src/utils/accessibility.ts +841 -0
- package/src/utils/animation/easing.ts +368 -0
- package/src/utils/animation/helpers.ts +223 -0
- package/src/utils/animation/index.ts +5 -0
- package/src/utils/animation/keyframes.ts +230 -0
- package/src/utils/error-handling.ts +473 -0
- package/src/utils/highlighter.ts +93 -0
- package/src/utils/i18n.ts +634 -0
- package/src/utils/keep-alive.ts +818 -0
- package/src/utils/lazy-loading.ts +530 -0
- package/src/utils/prop-validation.ts +478 -0
- package/src/utils/theme.ts +626 -0
|
@@ -0,0 +1,204 @@
|
|
|
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>
|
|
17
|
+
export const options = [
|
|
18
|
+
{ value: 'option1', label: 'Option 1' },
|
|
19
|
+
{ value: 'option2', label: 'Option 2' },
|
|
20
|
+
{ value: 'option3', label: 'Option 3' },
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
export const selected = 'option1'
|
|
24
|
+
|
|
25
|
+
export const handleChange = (value) => {
|
|
26
|
+
console.log('Selected:', value)
|
|
27
|
+
selected = value
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
@component('Select', {
|
|
32
|
+
value: selected,
|
|
33
|
+
onChange: handleChange,
|
|
34
|
+
options: options,
|
|
35
|
+
placeholder: 'Choose an option'
|
|
36
|
+
})
|
|
37
|
+
@endcomponent
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### With Label and Helper Text
|
|
41
|
+
|
|
42
|
+
```stx
|
|
43
|
+
@component('Select', {
|
|
44
|
+
label: 'Country',
|
|
45
|
+
helperText: 'Select your country of residence',
|
|
46
|
+
value: country,
|
|
47
|
+
onChange: (val) => country = val,
|
|
48
|
+
options: countries,
|
|
49
|
+
required: true
|
|
50
|
+
})
|
|
51
|
+
@endcomponent
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### With Error State
|
|
55
|
+
|
|
56
|
+
```stx
|
|
57
|
+
@component('Select', {
|
|
58
|
+
label: 'Category',
|
|
59
|
+
value: category,
|
|
60
|
+
onChange: (val) => category = val,
|
|
61
|
+
options: categories,
|
|
62
|
+
error: true,
|
|
63
|
+
helperText: 'Please select a valid category',
|
|
64
|
+
required: true
|
|
65
|
+
})
|
|
66
|
+
@endcomponent
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Different Sizes
|
|
70
|
+
|
|
71
|
+
```stx
|
|
72
|
+
<!-- Small -->
|
|
73
|
+
@component('Select', {
|
|
74
|
+
size: 'sm',
|
|
75
|
+
options: options
|
|
76
|
+
})
|
|
77
|
+
@endcomponent
|
|
78
|
+
|
|
79
|
+
<!-- Medium (default) -->
|
|
80
|
+
@component('Select', {
|
|
81
|
+
size: 'md',
|
|
82
|
+
options: options
|
|
83
|
+
})
|
|
84
|
+
@endcomponent
|
|
85
|
+
|
|
86
|
+
<!-- Large -->
|
|
87
|
+
@component('Select', {
|
|
88
|
+
size: 'lg',
|
|
89
|
+
options: options
|
|
90
|
+
})
|
|
91
|
+
@endcomponent
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Disabled State
|
|
95
|
+
|
|
96
|
+
```stx
|
|
97
|
+
@component('Select', {
|
|
98
|
+
disabled: true,
|
|
99
|
+
value: 'locked',
|
|
100
|
+
options: options
|
|
101
|
+
})
|
|
102
|
+
@endcomponent
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### With Disabled Options
|
|
106
|
+
|
|
107
|
+
```stx
|
|
108
|
+
<script>
|
|
109
|
+
export const options = [
|
|
110
|
+
{ value: '1', label: 'Available Option' },
|
|
111
|
+
{ value: '2', label: 'Disabled Option', disabled: true },
|
|
112
|
+
{ value: '3', label: 'Another Available' },
|
|
113
|
+
]
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
@component('Select', {
|
|
117
|
+
options: options
|
|
118
|
+
})
|
|
119
|
+
@endcomponent
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Props
|
|
123
|
+
|
|
124
|
+
| Prop | Type | Default | Description |
|
|
125
|
+
|------|------|---------|-------------|
|
|
126
|
+
| `value` | `string \| number` | `''` | Selected value |
|
|
127
|
+
| `onChange` | `function` | - | Callback when selection changes |
|
|
128
|
+
| `options` | `SelectOption[]` | `[]` | Array of options |
|
|
129
|
+
| `placeholder` | `string` | `'Select an option'` | Placeholder text |
|
|
130
|
+
| `disabled` | `boolean` | `false` | Disable the select |
|
|
131
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Select size |
|
|
132
|
+
| `error` | `boolean` | `false` | Show error state |
|
|
133
|
+
| `label` | `string` | `''` | Label text |
|
|
134
|
+
| `helperText` | `string` | `''` | Helper/error text below select |
|
|
135
|
+
| `required` | `boolean` | `false` | Mark as required |
|
|
136
|
+
| `className` | `string` | `''` | Additional CSS classes |
|
|
137
|
+
|
|
138
|
+
## SelectOption Interface
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
interface SelectOption {
|
|
142
|
+
value: string | number
|
|
143
|
+
label: string
|
|
144
|
+
disabled?: boolean
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Features
|
|
149
|
+
|
|
150
|
+
- **Native select** - Uses native `<select>` element for best UX
|
|
151
|
+
- **3 sizes** - Small, medium, and large
|
|
152
|
+
- **Dark mode** - Full dark mode support
|
|
153
|
+
- **Validation states** - Error and required states
|
|
154
|
+
- **Disabled options** - Individual options can be disabled
|
|
155
|
+
- **Labels and helpers** - Support for labels and helper text
|
|
156
|
+
- **Accessible** - Proper ARIA attributes and labels
|
|
157
|
+
- **Keyboard navigation** - Full keyboard support
|
|
158
|
+
- **Modern ES modules** - Clean export syntax
|
|
159
|
+
- **Headwind styling** - Utility-first CSS classes
|
|
160
|
+
|
|
161
|
+
## Accessibility
|
|
162
|
+
|
|
163
|
+
- Proper `label` associations with `for` attribute
|
|
164
|
+
- `aria-invalid` for error states
|
|
165
|
+
- `aria-describedby` for helper text
|
|
166
|
+
- Required field indicator
|
|
167
|
+
- Disabled state properly communicated
|
|
168
|
+
- Keyboard navigable
|
|
169
|
+
|
|
170
|
+
## Example: Form Select
|
|
171
|
+
|
|
172
|
+
```stx
|
|
173
|
+
<script>
|
|
174
|
+
export const countries = [
|
|
175
|
+
{ value: 'us', label: 'United States' },
|
|
176
|
+
{ value: 'ca', label: 'Canada' },
|
|
177
|
+
{ value: 'mx', label: 'Mexico' },
|
|
178
|
+
{ value: 'uk', label: 'United Kingdom' },
|
|
179
|
+
{ value: 'de', label: 'Germany' },
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
export let selectedCountry = ''
|
|
183
|
+
export let hasError = false
|
|
184
|
+
|
|
185
|
+
export const handleCountryChange = (value) => {
|
|
186
|
+
selectedCountry = value
|
|
187
|
+
hasError = !value
|
|
188
|
+
}
|
|
189
|
+
</script>
|
|
190
|
+
|
|
191
|
+
<form>
|
|
192
|
+
@component('Select', {
|
|
193
|
+
label: 'Country',
|
|
194
|
+
value: selectedCountry,
|
|
195
|
+
onChange: handleCountryChange,
|
|
196
|
+
options: countries,
|
|
197
|
+
placeholder: 'Select your country',
|
|
198
|
+
required: true,
|
|
199
|
+
error: hasError,
|
|
200
|
+
helperText: hasError ? 'Country is required' : 'Choose your country'
|
|
201
|
+
})
|
|
202
|
+
@endcomponent
|
|
203
|
+
</form>
|
|
204
|
+
```
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const value = $props.value || ''
|
|
3
|
+
export const onChange = $props.onChange || (() => {})
|
|
4
|
+
export const options = $props.options || []
|
|
5
|
+
export const placeholder = $props.placeholder || 'Select an option'
|
|
6
|
+
export const disabled = $props.disabled || false
|
|
7
|
+
export const size = $props.size || 'md'
|
|
8
|
+
export const error = $props.error || false
|
|
9
|
+
export const label = $props.label || ''
|
|
10
|
+
export const helperText = $props.helperText || ''
|
|
11
|
+
export const required = $props.required || false
|
|
12
|
+
export const className = $props.className || ''
|
|
13
|
+
|
|
14
|
+
// Size classes
|
|
15
|
+
const sizeClasses = {
|
|
16
|
+
sm: 'px-3 py-1.5 text-sm h-8',
|
|
17
|
+
md: 'px-4 py-2 text-base h-10',
|
|
18
|
+
lg: 'px-5 py-3 text-lg h-12',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Base and state classes
|
|
22
|
+
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'
|
|
23
|
+
|
|
24
|
+
const stateClasses = error
|
|
25
|
+
? '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'
|
|
26
|
+
: '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'
|
|
27
|
+
|
|
28
|
+
const disabledClasses = disabled
|
|
29
|
+
? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
|
|
30
|
+
: 'bg-white dark:bg-blue-gray-700 cursor-pointer hover:ring-gray-400 dark:hover:ring-gray-500'
|
|
31
|
+
|
|
32
|
+
export const selectClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses} ${className}`.trim()
|
|
33
|
+
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()
|
|
34
|
+
export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<div class="w-full">
|
|
38
|
+
@if(label)
|
|
39
|
+
<label for="select-{{ value }}" class="{{ labelClasses }}">
|
|
40
|
+
{{ label }}
|
|
41
|
+
@if(required)
|
|
42
|
+
<span class="text-red-500 ml-1">*</span>
|
|
43
|
+
@endif
|
|
44
|
+
</label>
|
|
45
|
+
@endif
|
|
46
|
+
|
|
47
|
+
<div class="{{ label ? 'mt-2' : '' }}">
|
|
48
|
+
<select
|
|
49
|
+
id="select-{{ value }}"
|
|
50
|
+
class="{{ selectClasses }}"
|
|
51
|
+
value="{{ value }}"
|
|
52
|
+
disabled="{{ disabled }}"
|
|
53
|
+
required="{{ required }}"
|
|
54
|
+
onchange="handleChange(event)"
|
|
55
|
+
aria-invalid="{{ error ? 'true' : 'false' }}"
|
|
56
|
+
aria-describedby="{{ helperText ? 'helper-text' : '' }}"
|
|
57
|
+
>
|
|
58
|
+
@if(placeholder)
|
|
59
|
+
<option value="" disabled selected>{{ placeholder }}</option>
|
|
60
|
+
@endif
|
|
61
|
+
|
|
62
|
+
@foreach(option in options)
|
|
63
|
+
<option
|
|
64
|
+
value="{{ option.value }}"
|
|
65
|
+
selected="{{ option.value === value }}"
|
|
66
|
+
disabled="{{ option.disabled }}"
|
|
67
|
+
>
|
|
68
|
+
{{ option.label }}
|
|
69
|
+
</option>
|
|
70
|
+
@endforeach
|
|
71
|
+
</select>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
@if(helperText)
|
|
75
|
+
<p id="helper-text" class="{{ helperClasses }}">
|
|
76
|
+
{{ helperText }}
|
|
77
|
+
</p>
|
|
78
|
+
@endif
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<script>
|
|
82
|
+
function handleChange(event) {
|
|
83
|
+
const newValue = event.target.value
|
|
84
|
+
|
|
85
|
+
if (onChange) {
|
|
86
|
+
onChange(newValue)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
</script>
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/**
|
|
3
|
+
* Sidebar Component - macOS Native Style
|
|
4
|
+
*
|
|
5
|
+
* A native-feeling sidebar component that integrates with Craft for true native rendering
|
|
6
|
+
* when running in a Craft desktop environment, with beautiful macOS Tahoe-style fallback
|
|
7
|
+
* for web environments.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* <Sidebar
|
|
11
|
+
* :sections="sections"
|
|
12
|
+
* :collapsed="false"
|
|
13
|
+
* :width="240"
|
|
14
|
+
* variant="tahoe"
|
|
15
|
+
* >
|
|
16
|
+
* <template #header>
|
|
17
|
+
* <SidebarHeader title="My App" />
|
|
18
|
+
* </template>
|
|
19
|
+
* <template #footer>
|
|
20
|
+
* <SidebarFooter />
|
|
21
|
+
* </template>
|
|
22
|
+
* </Sidebar>
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// Props
|
|
26
|
+
export const sections = $props.sections || []
|
|
27
|
+
export const collapsed = $props.collapsed ?? false
|
|
28
|
+
export const collapsible = $props.collapsible ?? true
|
|
29
|
+
export const width = $props.width || 240
|
|
30
|
+
export const minWidth = $props.minWidth || 64
|
|
31
|
+
export const variant = $props.variant || 'tahoe' // 'tahoe' | 'vibrancy' | 'solid' | 'transparent'
|
|
32
|
+
export const position = $props.position || 'left' // 'left' | 'right'
|
|
33
|
+
export const bordered = $props.bordered ?? true
|
|
34
|
+
export const className = $props.className || ''
|
|
35
|
+
export const onCollapse = $props.onCollapse
|
|
36
|
+
export const onSectionToggle = $props.onSectionToggle
|
|
37
|
+
export const onItemClick = $props.onItemClick
|
|
38
|
+
|
|
39
|
+
// Internal state
|
|
40
|
+
let isHovered = false
|
|
41
|
+
let nativeSidebarId = null
|
|
42
|
+
|
|
43
|
+
// Check if running in Craft environment with native sidebar enabled
|
|
44
|
+
// When Craft uses --native-sidebar, it sets these flags and we should NOT render the web sidebar
|
|
45
|
+
const hasNativeSidebar = typeof window !== 'undefined' && (
|
|
46
|
+
window.__craftNativeSidebar === true ||
|
|
47
|
+
window.craft?.hasNativeSidebar?.() ||
|
|
48
|
+
(window.webkit?.messageHandlers?.craft && document.body?.dataset?.nativeSidebar === 'true')
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
// Check if running in Craft environment (may not have native sidebar)
|
|
52
|
+
const isCraft = typeof window !== 'undefined' && window.craft?.isCraft?.()
|
|
53
|
+
|
|
54
|
+
// Variant styles - macOS Tahoe inspired
|
|
55
|
+
// Native macOS sidebars use vibrancy where desktop background shines through
|
|
56
|
+
const variantStyles = {
|
|
57
|
+
tahoe: {
|
|
58
|
+
// Translucent with warm tint overlay - desktop shows through
|
|
59
|
+
bg: 'bg-white/70 dark:bg-black/50',
|
|
60
|
+
border: 'border-black/5 dark:border-white/10',
|
|
61
|
+
backdrop: 'backdrop-blur-2xl backdrop-saturate-180',
|
|
62
|
+
// Warm tint is applied via pseudo-element in template
|
|
63
|
+
tint: true
|
|
64
|
+
},
|
|
65
|
+
vibrancy: {
|
|
66
|
+
bg: 'bg-white/50 dark:bg-black/40',
|
|
67
|
+
border: 'border-white/20 dark:border-white/10',
|
|
68
|
+
backdrop: 'backdrop-blur-3xl backdrop-saturate-200',
|
|
69
|
+
tint: false
|
|
70
|
+
},
|
|
71
|
+
solid: {
|
|
72
|
+
bg: 'bg-stone-100 dark:bg-neutral-900',
|
|
73
|
+
border: 'border-stone-200 dark:border-neutral-800',
|
|
74
|
+
backdrop: '',
|
|
75
|
+
tint: false
|
|
76
|
+
},
|
|
77
|
+
transparent: {
|
|
78
|
+
bg: 'bg-transparent',
|
|
79
|
+
border: 'border-transparent',
|
|
80
|
+
backdrop: '',
|
|
81
|
+
tint: false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Get current variant style
|
|
86
|
+
function getVariantStyle() {
|
|
87
|
+
return variantStyles[variant] || variantStyles.tahoe
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Calculate sidebar width
|
|
91
|
+
function getSidebarWidth() {
|
|
92
|
+
if (collapsed && !isHovered) {
|
|
93
|
+
return `${minWidth}px`
|
|
94
|
+
}
|
|
95
|
+
return `${width}px`
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check if showing expanded content
|
|
99
|
+
function showExpanded() {
|
|
100
|
+
return !collapsed || isHovered
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Initialize Craft native sidebar if available
|
|
104
|
+
async function initNativeSidebar() {
|
|
105
|
+
if (isCraft && window.craft?.components?.createSidebar) {
|
|
106
|
+
try {
|
|
107
|
+
const config = {
|
|
108
|
+
position,
|
|
109
|
+
width,
|
|
110
|
+
minWidth,
|
|
111
|
+
collapsible,
|
|
112
|
+
sections: sections.map(section => ({
|
|
113
|
+
id: section.id,
|
|
114
|
+
label: section.label,
|
|
115
|
+
items: section.items?.map(item => ({
|
|
116
|
+
id: item.id,
|
|
117
|
+
label: item.label,
|
|
118
|
+
icon: item.icon,
|
|
119
|
+
href: item.href,
|
|
120
|
+
badge: item.badge
|
|
121
|
+
}))
|
|
122
|
+
}))
|
|
123
|
+
}
|
|
124
|
+
nativeSidebarId = await window.craft.components.createSidebar(config)
|
|
125
|
+
} catch (err) {
|
|
126
|
+
console.warn('[Sidebar] Failed to create native sidebar, using web fallback:', err)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Handle collapse toggle
|
|
132
|
+
function handleCollapseToggle() {
|
|
133
|
+
if (onCollapse) {
|
|
134
|
+
onCollapse(!collapsed)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Handle mouse enter/leave for hover expand
|
|
139
|
+
function handleMouseEnter() {
|
|
140
|
+
if (collapsed && collapsible) {
|
|
141
|
+
isHovered = true
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function handleMouseLeave() {
|
|
146
|
+
isHovered = false
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Handle section toggle
|
|
150
|
+
function handleSectionToggle(sectionId) {
|
|
151
|
+
if (onSectionToggle) {
|
|
152
|
+
onSectionToggle(sectionId)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Handle item click
|
|
157
|
+
function handleItemClick(item, event) {
|
|
158
|
+
if (onItemClick) {
|
|
159
|
+
onItemClick(item, event)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Build container classes
|
|
164
|
+
const style = getVariantStyle()
|
|
165
|
+
|
|
166
|
+
// Build border classes properly for Tailwind
|
|
167
|
+
function getBorderClasses() {
|
|
168
|
+
if (!bordered) return ''
|
|
169
|
+
const borderSide = position === 'left' ? 'border-r' : 'border-l'
|
|
170
|
+
return `${borderSide} ${style.border}`
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const containerClasses = [
|
|
174
|
+
'fixed inset-y-0 z-50 flex flex-col',
|
|
175
|
+
'transition-all duration-300 ease-out',
|
|
176
|
+
'overflow-hidden', // Contain the tint overlay
|
|
177
|
+
style.bg,
|
|
178
|
+
style.backdrop,
|
|
179
|
+
getBorderClasses(),
|
|
180
|
+
position === 'left' ? 'left-0' : 'right-0',
|
|
181
|
+
className
|
|
182
|
+
].filter(Boolean).join(' ')
|
|
183
|
+
|
|
184
|
+
// Initialize native sidebar on mount
|
|
185
|
+
if (typeof window !== 'undefined') {
|
|
186
|
+
initNativeSidebar()
|
|
187
|
+
}
|
|
188
|
+
</script>
|
|
189
|
+
|
|
190
|
+
<!-- Only render web sidebar when NOT running in Craft native sidebar mode -->
|
|
191
|
+
@if(!hasNativeSidebar)
|
|
192
|
+
<aside
|
|
193
|
+
class="{{ containerClasses }} sidebar-container"
|
|
194
|
+
style="width: {{ getSidebarWidth() }}"
|
|
195
|
+
@mouseenter="handleMouseEnter"
|
|
196
|
+
@mouseleave="handleMouseLeave"
|
|
197
|
+
role="navigation"
|
|
198
|
+
aria-label="Sidebar navigation"
|
|
199
|
+
>
|
|
200
|
+
<!-- Warm tint overlay for macOS Tahoe effect - desktop shows through -->
|
|
201
|
+
@if(style.tint)
|
|
202
|
+
<div class="absolute inset-0 pointer-events-none bg-gradient-to-b from-amber-50/30 via-orange-50/20 to-stone-100/30 dark:from-neutral-800/20 dark:via-neutral-900/10 dark:to-neutral-800/20 mix-blend-overlay"></div>
|
|
203
|
+
@endif
|
|
204
|
+
|
|
205
|
+
<!-- Header slot -->
|
|
206
|
+
@if($slots.header)
|
|
207
|
+
<div class="flex-shrink-0 relative z-10">
|
|
208
|
+
<slot name="header" />
|
|
209
|
+
</div>
|
|
210
|
+
@endif
|
|
211
|
+
|
|
212
|
+
<!-- Main content area with sections -->
|
|
213
|
+
<div class="flex-1 overflow-y-auto overflow-x-hidden relative z-10">
|
|
214
|
+
@if(sections.length > 0)
|
|
215
|
+
<nav class="px-3 py-2 space-y-1">
|
|
216
|
+
@foreach(sections as section)
|
|
217
|
+
<SidebarSection
|
|
218
|
+
:id="section.id"
|
|
219
|
+
:label="section.label"
|
|
220
|
+
:icon="section.icon"
|
|
221
|
+
:items="section.items"
|
|
222
|
+
:expanded="section.expanded !== false"
|
|
223
|
+
:collapsible="section.collapsible !== false"
|
|
224
|
+
:showLabel="showExpanded()"
|
|
225
|
+
@toggle="handleSectionToggle(section.id)"
|
|
226
|
+
@itemClick="handleItemClick"
|
|
227
|
+
/>
|
|
228
|
+
@endforeach
|
|
229
|
+
</nav>
|
|
230
|
+
@else
|
|
231
|
+
<!-- Default slot for custom content -->
|
|
232
|
+
<div class="px-3 py-2">
|
|
233
|
+
<slot />
|
|
234
|
+
</div>
|
|
235
|
+
@endif
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<!-- Footer slot -->
|
|
239
|
+
@if($slots.footer)
|
|
240
|
+
<div class="flex-shrink-0 relative z-10">
|
|
241
|
+
<slot name="footer" />
|
|
242
|
+
</div>
|
|
243
|
+
@endif
|
|
244
|
+
|
|
245
|
+
<!-- Collapse toggle button -->
|
|
246
|
+
@if(collapsible && showExpanded())
|
|
247
|
+
<button
|
|
248
|
+
type="button"
|
|
249
|
+
class="absolute top-4 {{ position === 'left' ? 'right-2' : 'left-2' }} p-1.5 rounded-lg text-stone-400 hover:text-stone-600 hover:bg-black/5 dark:hover:text-neutral-300 dark:hover:bg-white/10 transition-colors z-20"
|
|
250
|
+
@click="handleCollapseToggle"
|
|
251
|
+
aria-label="{{ collapsed ? 'Expand sidebar' : 'Collapse sidebar' }}"
|
|
252
|
+
>
|
|
253
|
+
<div class="{{ collapsed ? 'i-hugeicons-sidebar-right' : 'i-hugeicons-sidebar-left' }} w-5 h-5"></div>
|
|
254
|
+
</button>
|
|
255
|
+
@endif
|
|
256
|
+
</aside>
|
|
257
|
+
@endif
|
|
258
|
+
|
|
259
|
+
<style scoped>
|
|
260
|
+
/* Smooth scrollbar styling for macOS feel */
|
|
261
|
+
.overflow-y-auto::-webkit-scrollbar {
|
|
262
|
+
width: 6px;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.overflow-y-auto::-webkit-scrollbar-track {
|
|
266
|
+
background: transparent;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.overflow-y-auto::-webkit-scrollbar-thumb {
|
|
270
|
+
background-color: rgba(0, 0, 0, 0.15);
|
|
271
|
+
border-radius: 3px;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.overflow-y-auto::-webkit-scrollbar-thumb:hover {
|
|
275
|
+
background-color: rgba(0, 0, 0, 0.25);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* Dark mode scrollbar */
|
|
279
|
+
@media (prefers-color-scheme: dark) {
|
|
280
|
+
.overflow-y-auto::-webkit-scrollbar-thumb {
|
|
281
|
+
background-color: rgba(255, 255, 255, 0.15);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.overflow-y-auto::-webkit-scrollbar-thumb:hover {
|
|
285
|
+
background-color: rgba(255, 255, 255, 0.25);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
</style>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/**
|
|
3
|
+
* SidebarFooter Component
|
|
4
|
+
*
|
|
5
|
+
* A footer component for the sidebar with quick actions and settings.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* <SidebarFooter
|
|
9
|
+
* :showSettings="true"
|
|
10
|
+
* :showThemeToggle="true"
|
|
11
|
+
* settingsHref="/settings"
|
|
12
|
+
* />
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Props
|
|
16
|
+
export const showSettings = $props.showSettings ?? true
|
|
17
|
+
export const showThemeToggle = $props.showThemeToggle ?? true
|
|
18
|
+
export const settingsHref = $props.settingsHref || '/settings'
|
|
19
|
+
export const settingsLabel = $props.settingsLabel || 'Settings'
|
|
20
|
+
export const collapsed = $props.collapsed ?? false
|
|
21
|
+
export const onThemeToggle = $props.onThemeToggle
|
|
22
|
+
|
|
23
|
+
// Quick action items
|
|
24
|
+
export const actions = $props.actions || []
|
|
25
|
+
|
|
26
|
+
// Handle theme toggle
|
|
27
|
+
function handleThemeToggle() {
|
|
28
|
+
if (onThemeToggle) {
|
|
29
|
+
onThemeToggle()
|
|
30
|
+
} else {
|
|
31
|
+
// Default theme toggle behavior
|
|
32
|
+
document.documentElement.classList.toggle('dark')
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<div class="border-t border-black/5 dark:border-white/10 p-3 space-y-2">
|
|
38
|
+
<!-- Custom actions slot -->
|
|
39
|
+
<slot />
|
|
40
|
+
|
|
41
|
+
<!-- Quick actions row -->
|
|
42
|
+
@if(!collapsed && actions.length > 0)
|
|
43
|
+
<div class="flex items-center gap-2">
|
|
44
|
+
@foreach(actions as action)
|
|
45
|
+
@if(action.href)
|
|
46
|
+
<a
|
|
47
|
+
href="{{ action.href }}"
|
|
48
|
+
class="flex-1 flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-black/70 dark:text-white/70 hover:bg-black/5 dark:hover:bg-white/10 transition-colors"
|
|
49
|
+
>
|
|
50
|
+
@if(action.icon)
|
|
51
|
+
<div class="{{ action.icon }} w-4 h-4"></div>
|
|
52
|
+
@endif
|
|
53
|
+
<span>{{ action.label }}</span>
|
|
54
|
+
</a>
|
|
55
|
+
@else
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
class="flex-1 flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-black/70 dark:text-white/70 hover:bg-black/5 dark:hover:bg-white/10 transition-colors"
|
|
59
|
+
@click="action.onClick"
|
|
60
|
+
>
|
|
61
|
+
@if(action.icon)
|
|
62
|
+
<div class="{{ action.icon }} w-4 h-4"></div>
|
|
63
|
+
@endif
|
|
64
|
+
<span>{{ action.label }}</span>
|
|
65
|
+
</button>
|
|
66
|
+
@endif
|
|
67
|
+
@endforeach
|
|
68
|
+
|
|
69
|
+
@if(showThemeToggle)
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
class="p-2 rounded-lg text-black/50 hover:text-black/80 hover:bg-black/5 dark:text-white/50 dark:hover:text-white/80 dark:hover:bg-white/10 transition-colors"
|
|
73
|
+
@click="handleThemeToggle"
|
|
74
|
+
aria-label="Toggle dark mode"
|
|
75
|
+
>
|
|
76
|
+
<div class="i-hugeicons-moon-02 w-4 h-4 dark:hidden"></div>
|
|
77
|
+
<div class="i-hugeicons-sun-02 w-4 h-4 hidden dark:block"></div>
|
|
78
|
+
</button>
|
|
79
|
+
@endif
|
|
80
|
+
</div>
|
|
81
|
+
@endif
|
|
82
|
+
|
|
83
|
+
<!-- Settings link -->
|
|
84
|
+
@if(showSettings)
|
|
85
|
+
<a
|
|
86
|
+
href="{{ settingsHref }}"
|
|
87
|
+
class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150 text-black/70 dark:text-white/70 hover:bg-black/5 dark:hover:bg-white/10"
|
|
88
|
+
>
|
|
89
|
+
<div class="i-hugeicons-settings-02 w-5 h-5 text-black/50 dark:text-white/50"></div>
|
|
90
|
+
@if(!collapsed)
|
|
91
|
+
<span>{{ settingsLabel }}</span>
|
|
92
|
+
@endif
|
|
93
|
+
</a>
|
|
94
|
+
@endif
|
|
95
|
+
</div>
|