@stacksjs/components 0.2.27 → 0.2.53
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/Accordion-19x8q4rj.stx +88 -0
- package/dist/{Audio-vdmg1258.stx → Audio-hjywk3qc.stx} +4 -4
- package/dist/{Avatar-365cvrh4.stx → Avatar-h3jmwcq0.stx} +23 -34
- package/dist/{Badge-8m3qete4.stx → Badge-k8dy3d53.stx} +11 -19
- package/dist/{Button-3tt4vsqt.stx → Button-1vknasy3.stx} +18 -33
- package/dist/Calendar-reny0et7.stx +189 -0
- package/dist/{Card-6wdbexqr.stx → Card-c1g1fd1w.stx} +27 -33
- package/dist/{Checkbox-p3q3e3nf.stx → Checkbox-zgdmy3p5.stx} +36 -44
- package/dist/{Checkout-e4037tyy.stx → Checkout-agt0ngpw.stx} +8 -16
- package/dist/Combobox-fdkr6cdq.stx +96 -0
- package/dist/{ComboboxButton-8gxwkhdx.stx → ComboboxButton-g76stp1e.stx} +14 -2
- package/dist/{ComboboxInput-mk99z65n.stx → ComboboxInput-z1293bbh.stx} +12 -5
- package/dist/ComboboxOption-hmxt3gte.stx +49 -0
- package/dist/{ListboxOptions-8kqvfp7z.stx → ComboboxOptions-sq5360ae.stx} +2 -2
- package/dist/CommandPalette-36mxf05w.stx +81 -0
- package/dist/CommandPaletteItem-mvk6q7a2.stx +27 -0
- package/dist/Dialog-hczpexs4.stx +37 -0
- package/dist/{Drawer-p7m351bn.stx → Drawer-xhevp5cw.stx} +29 -49
- package/dist/Dropdown-vvhemtc0.stx +82 -0
- package/dist/{DropdownButton-9w8zkfwj.stx → DropdownButton-wcz67dxy.stx} +12 -6
- package/dist/DropdownItem-c7rz08e5.stx +35 -0
- package/dist/{DropdownItems-aygjpxkx.stx → DropdownItems-1sxnd5qn.stx} +2 -2
- package/dist/EmailInput-vak3kkmk.stx +30 -0
- package/dist/Form-mas630hy.stx +95 -0
- package/dist/{Image-jhkm6w1f.stx → Image-yhxcvgzs.stx} +68 -100
- package/dist/Listbox-q49tnr90.stx +101 -0
- package/dist/{ListboxButton-spwx7513.stx → ListboxButton-keb8gm0q.stx} +12 -1
- package/dist/ListboxOption-e0dtv1gc.stx +49 -0
- package/dist/{ComboboxOptions-6b5ywc5x.stx → ListboxOptions-ejkatcfg.stx} +7 -3
- package/dist/{Login-rx9gyqhx.stx → Login-gepemsth.stx} +17 -19
- package/dist/Navigator-rk2a5wvk.stx +113 -0
- package/dist/Notification-tb9b525s.stx +78 -0
- package/dist/{NumberInput-nzpkx1ry.stx → NumberInput-drnvtqej.stx} +40 -53
- package/dist/{Pagination-h61eq390.stx → Pagination-n3d31cns.stx} +73 -108
- package/dist/PasswordInput-n02x6053.stx +142 -0
- package/dist/{PaymentMethods-j42jsk5x.stx → PaymentMethods-x4vrx9yb.stx} +21 -30
- package/dist/Popover-et2q44m7.stx +60 -0
- package/dist/{PopoverButton-fnaqc77g.stx → PopoverButton-mffzc9pr.stx} +12 -6
- package/dist/{PopoverPanel-y0vtf5w9.stx → PopoverPanel-054yh38h.stx} +6 -6
- package/dist/{Portal-s2q37yp0.stx → Portal-1vf14s4x.stx} +20 -37
- package/dist/{Progress-vmdkm06p.stx → Progress-yxcntr4x.stx} +36 -48
- package/dist/{Radio-04bs3d21.stx → Radio-cgjv2hb2.stx} +29 -34
- package/dist/RadioGroup-qxmnm3xm.stx +60 -0
- package/dist/RadioGroupOption-s1d6r221.stx +58 -0
- package/dist/SearchInput-pdx7agn0.stx +44 -0
- package/dist/{Select-g4mmvfv8.stx → Select-28y0pg83.stx} +23 -31
- package/dist/Sidebar-73vc78as.stx +236 -0
- package/dist/SidebarFooter-zsfsgcxb.stx +109 -0
- package/dist/SidebarHeader-7v174a9c.stx +122 -0
- package/dist/SidebarItem-3y3e8g2t.stx +92 -0
- package/dist/SidebarSection-z5ey6awe.stx +113 -0
- package/dist/{Signup-c4dyv6f3.stx → Signup-hxm5hwkr.stx} +26 -28
- package/dist/{StepperStep-en4r00zv.stx → StepperStep-wxgembat.stx} +9 -5
- package/dist/{SubscriptionCheckout-gawjzf3q.stx → SubscriptionCheckout-zy2ycrmw.stx} +99 -129
- package/dist/Switch-r5t5w04w.stx +70 -0
- package/dist/{TableHeader-ydys9rzz.stx → TableHeader-0eyzbqpz.stx} +10 -3
- package/dist/Tabs-bzvvn4ke.stx +126 -0
- package/dist/Teleport-tsx4bt4n.stx +65 -0
- package/dist/{TextInput-k2kfkgy1.stx → TextInput-kf084763.stx} +42 -59
- package/dist/{Textarea-szad57dz.stx → Textarea-xg4rr2xz.stx} +42 -54
- package/dist/{Tooltip-em7t551n.stx → Tooltip-m0tqd72e.stx} +34 -40
- package/dist/Transition-7smenaz8.stx +102 -0
- package/dist/{TwoFactorChallenge-an9yvxvq.stx → TwoFactorChallenge-y5m3nk79.stx} +49 -63
- package/dist/{Video-crxva65p.stx → Video-84j4pv6h.stx} +4 -4
- package/dist/VirtualList-fa7v5a3h.stx +67 -0
- package/dist/VirtualTable-f9crbfjr.stx +103 -0
- package/dist/components/CodeBlock.stx +1 -6
- package/dist/index.js +13 -13
- package/dist/ui/accordion/Accordion.stx +1 -3
- package/dist/ui/auth/Login.stx +0 -1
- package/dist/ui/auth/Signup.stx +0 -1
- package/dist/ui/auth/TwoFactorChallenge.stx +1 -3
- package/dist/ui/avatar/Avatar.stx +0 -2
- package/dist/ui/badge/Badge.stx +0 -1
- package/dist/ui/button/Button.stx +1 -2
- package/dist/ui/calendar/Calendar.stx +12 -2
- package/dist/ui/card/Card.stx +0 -2
- package/dist/ui/checkbox/Checkbox.stx +2 -2
- package/dist/ui/combobox/Combobox.stx +2 -1
- package/dist/ui/combobox/ComboboxButton.stx +1 -0
- package/dist/ui/combobox/ComboboxInput.stx +1 -4
- package/dist/ui/combobox/ComboboxOption.stx +2 -9
- package/dist/ui/combobox/ComboboxOptions.stx +1 -1
- package/dist/ui/command-palette/CommandPalette.stx +3 -6
- package/dist/ui/command-palette/CommandPaletteItem.stx +2 -7
- package/dist/ui/dialog/Dialog.stx +1 -3
- package/dist/ui/drawer/Drawer.stx +1 -3
- package/dist/ui/dropdown/Dropdown.stx +2 -5
- package/dist/ui/dropdown/DropdownButton.stx +1 -3
- package/dist/ui/dropdown/DropdownItem.stx +2 -7
- package/dist/ui/dropdown/DropdownItems.stx +1 -1
- package/dist/ui/form/Form.stx +0 -5
- package/dist/ui/image/Image.stx +1 -6
- package/dist/ui/input/EmailInput.stx +1 -1
- package/dist/ui/input/NumberInput.stx +0 -1
- package/dist/ui/input/PasswordInput.stx +1 -4
- package/dist/ui/input/SearchInput.stx +2 -2
- package/dist/ui/input/TextInput.stx +1 -2
- package/dist/ui/listbox/Listbox.stx +2 -1
- package/dist/ui/listbox/ListboxButton.stx +1 -0
- package/dist/ui/listbox/ListboxOption.stx +2 -9
- package/dist/ui/listbox/ListboxOptions.stx +1 -1
- package/dist/ui/navigator/Navigator.stx +4 -4
- package/dist/ui/notification/Notification.stx +1 -5
- package/dist/ui/pagination/Pagination.stx +1 -2
- package/dist/ui/payment/Checkout.stx +0 -4
- package/dist/ui/payment/PaymentMethods.stx +15 -2
- package/dist/ui/payment/SubscriptionCheckout.stx +3 -184
- package/dist/ui/popover/Popover.stx +2 -5
- package/dist/ui/popover/PopoverButton.stx +1 -3
- package/dist/ui/popover/PopoverPanel.stx +1 -1
- package/dist/ui/portal/Portal.stx +1 -0
- package/dist/ui/progress/Progress.stx +6 -1
- package/dist/ui/radio/Radio.stx +1 -1
- package/dist/ui/radio-group/RadioGroup.stx +2 -1
- package/dist/ui/radio-group/RadioGroupOption.stx +2 -9
- package/dist/ui/select/Select.stx +1 -1
- package/dist/ui/sidebar/Sidebar.stx +9 -26
- package/dist/ui/sidebar/SidebarFooter.stx +6 -21
- package/dist/ui/sidebar/SidebarHeader.stx +7 -18
- package/dist/ui/sidebar/SidebarItem.stx +4 -20
- package/dist/ui/sidebar/SidebarSection.stx +5 -18
- package/dist/ui/sidebar/index.d.ts +13 -1
- package/dist/ui/stepper/StepperStep.stx +1 -5
- package/dist/ui/switch/Switch.stx +4 -10
- package/dist/ui/table/TableHeader.stx +1 -1
- package/dist/ui/tabs/Tabs.stx +0 -1
- package/dist/ui/teleport/Teleport.stx +1 -0
- package/dist/ui/textarea/Textarea.stx +1 -1
- package/dist/ui/tooltip/Tooltip.stx +1 -2
- package/dist/ui/transition/Transition.stx +9 -16
- package/dist/ui/virtual-list/VirtualList.stx +2 -4
- package/dist/ui/virtual-table/VirtualTable.stx +1 -1
- package/package.json +3 -3
- package/src/components/CodeBlock.stx +26 -29
- package/src/components/Footer.stx +1 -1
- package/src/components/Hero.stx +1 -1
- package/src/components/Installation.stx +5 -7
- package/src/ui/accordion/Accordion.stx +48 -68
- package/src/ui/audio/Audio.stx +4 -4
- package/src/ui/auth/Login.stx +17 -19
- package/src/ui/auth/Signup.stx +26 -28
- package/src/ui/auth/TwoFactorChallenge.stx +49 -63
- package/src/ui/avatar/Avatar.stx +23 -34
- package/src/ui/badge/Badge.stx +11 -19
- package/src/ui/button/Button.stx +18 -33
- package/src/ui/calendar/Calendar.stx +93 -136
- package/src/ui/card/Card.stx +27 -33
- package/src/ui/checkbox/Checkbox.stx +36 -44
- package/src/ui/combobox/Combobox.stx +87 -4
- package/src/ui/combobox/ComboboxButton.stx +14 -2
- package/src/ui/combobox/ComboboxInput.stx +12 -5
- package/src/ui/combobox/ComboboxOption.stx +29 -32
- package/src/ui/combobox/ComboboxOptions.stx +7 -3
- package/src/ui/command-palette/CommandPalette.stx +70 -28
- package/src/ui/command-palette/CommandPaletteItem.stx +15 -20
- package/src/ui/dialog/Dialog.stx +28 -10
- package/src/ui/drawer/Drawer.stx +29 -49
- package/src/ui/dropdown/Dropdown.stx +69 -11
- package/src/ui/dropdown/DropdownButton.stx +12 -6
- package/src/ui/dropdown/DropdownItem.stx +18 -26
- package/src/ui/dropdown/DropdownItems.stx +2 -2
- package/src/ui/form/Form.stx +50 -164
- package/src/ui/heatmap/Heatmap.stx +16 -37
- package/src/ui/image/Image.stx +68 -100
- package/src/ui/input/EmailInput.stx +16 -27
- package/src/ui/input/NumberInput.stx +40 -53
- package/src/ui/input/PasswordInput.stx +64 -77
- package/src/ui/input/SearchInput.stx +27 -46
- package/src/ui/input/TextInput.stx +42 -59
- package/src/ui/listbox/Listbox.stx +91 -4
- package/src/ui/listbox/ListboxButton.stx +12 -1
- package/src/ui/listbox/ListboxOption.stx +30 -38
- package/src/ui/listbox/ListboxOptions.stx +2 -2
- package/src/ui/navigator/Navigator.stx +52 -77
- package/src/ui/notification/Notification.stx +46 -39
- package/src/ui/pagination/Pagination.stx +73 -108
- package/src/ui/payment/Checkout.stx +8 -16
- package/src/ui/payment/PaymentMethods.stx +21 -30
- package/src/ui/payment/SubscriptionCheckout.stx +99 -129
- package/src/ui/popover/Popover.stx +50 -14
- package/src/ui/popover/PopoverButton.stx +12 -6
- package/src/ui/popover/PopoverPanel.stx +6 -6
- package/src/ui/portal/Portal.stx +20 -37
- package/src/ui/progress/Progress.stx +36 -48
- package/src/ui/radio/Radio.stx +29 -34
- package/src/ui/radio-group/RadioGroup.stx +51 -4
- package/src/ui/radio-group/RadioGroupOption.stx +37 -30
- package/src/ui/select/Select.stx +23 -31
- package/src/ui/sidebar/Sidebar.stx +136 -188
- package/src/ui/sidebar/SidebarFooter.stx +49 -35
- package/src/ui/sidebar/SidebarHeader.stx +52 -30
- package/src/ui/sidebar/SidebarItem.stx +52 -78
- package/src/ui/sidebar/SidebarSection.stx +56 -51
- package/src/ui/sidebar/index.ts +21 -1
- package/src/ui/stepper/StepperStep.stx +9 -5
- package/src/ui/switch/Switch.stx +38 -42
- package/src/ui/table/TableHeader.stx +10 -3
- package/src/ui/tabs/Tabs.stx +68 -87
- package/src/ui/teleport/Teleport.stx +23 -59
- package/src/ui/textarea/Textarea.stx +42 -54
- package/src/ui/tooltip/Tooltip.stx +34 -40
- package/src/ui/transition/Transition.stx +76 -148
- package/src/ui/video/Video.stx +4 -4
- package/src/ui/virtual-list/VirtualList.stx +45 -61
- package/src/ui/virtual-table/VirtualTable.stx +62 -72
- package/dist/Accordion-sbfra0mz.stx +0 -108
- package/dist/Calendar-58nnkdh8.stx +0 -232
- package/dist/Combobox-th5ebw62.stx +0 -13
- package/dist/ComboboxOption-an9zyan6.stx +0 -52
- package/dist/CommandPalette-kwmdf2np.stx +0 -39
- package/dist/CommandPaletteItem-zx2fbpts.stx +0 -32
- package/dist/Dialog-rgmj8q2j.stx +0 -19
- package/dist/Dropdown-jz4jy0n3.stx +0 -24
- package/dist/DropdownItem-wagdspb8.stx +0 -43
- package/dist/EmailInput-ajsp9vw6.stx +0 -41
- package/dist/Form-1mdmx9b6.stx +0 -209
- package/dist/Listbox-pqqh72g8.stx +0 -14
- package/dist/ListboxOption-7nxzaexq.stx +0 -57
- package/dist/Navigator-aay8bjab.stx +0 -138
- package/dist/Notification-gk33gv7b.stx +0 -71
- package/dist/PasswordInput-zv4hm43j.stx +0 -155
- package/dist/Popover-469gbt6r.stx +0 -24
- package/dist/RadioGroup-bvb1mrhd.stx +0 -13
- package/dist/RadioGroupOption-cpdwgnhw.stx +0 -51
- package/dist/SearchInput-2521m475.stx +0 -63
- package/dist/Sidebar-40ckbzh8.stx +0 -288
- package/dist/SidebarFooter-hnth56mg.stx +0 -95
- package/dist/SidebarHeader-3wrte2p2.stx +0 -100
- package/dist/SidebarItem-zqtvvzpc.stx +0 -118
- package/dist/SidebarSection-3etwnbjr.stx +0 -108
- package/dist/Switch-036j9vm1.stx +0 -74
- package/dist/Tabs-svh721m0.stx +0 -145
- package/dist/Teleport-qfch3w0b.stx +0 -101
- package/dist/Transition-0hmr6zxb.stx +0 -174
- package/dist/VirtualList-cndhjejh.stx +0 -83
- package/dist/VirtualTable-ng0b48yp.stx +0 -113
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
export const sections = $props.sections || []
|
|
3
|
+
export const collapsed = $props.collapsed ?? false
|
|
4
|
+
export const collapsible = $props.collapsible ?? true
|
|
5
|
+
export const width = $props.width || 240
|
|
6
|
+
export const minWidth = $props.minWidth || 64
|
|
7
|
+
export const variant = $props.variant || 'tahoe'
|
|
8
|
+
export const position = $props.position || 'left'
|
|
9
|
+
export const placement = $props.placement || 'fixed'
|
|
10
|
+
export const bordered = $props.bordered ?? true
|
|
11
|
+
export const className = $props.className || ''
|
|
12
|
+
|
|
13
|
+
const variantStyles = {
|
|
14
|
+
tahoe: {
|
|
15
|
+
bg: 'bg-white/70 dark:bg-black/50',
|
|
16
|
+
border: 'border-black/5 dark:border-white/10',
|
|
17
|
+
backdrop: 'backdrop-blur-2xl backdrop-saturate-180',
|
|
18
|
+
tint: true,
|
|
19
|
+
},
|
|
20
|
+
vibrancy: {
|
|
21
|
+
bg: 'bg-white/50 dark:bg-black/40',
|
|
22
|
+
border: 'border-white/20 dark:border-white/10',
|
|
23
|
+
backdrop: 'backdrop-blur-3xl backdrop-saturate-200',
|
|
24
|
+
tint: false,
|
|
25
|
+
},
|
|
26
|
+
solid: {
|
|
27
|
+
bg: 'bg-stone-100 dark:bg-neutral-900',
|
|
28
|
+
border: 'border-stone-200 dark:border-neutral-800',
|
|
29
|
+
backdrop: '',
|
|
30
|
+
tint: false,
|
|
31
|
+
},
|
|
32
|
+
transparent: {
|
|
33
|
+
bg: 'bg-transparent',
|
|
34
|
+
border: 'border-transparent',
|
|
35
|
+
backdrop: '',
|
|
36
|
+
tint: false,
|
|
37
|
+
},
|
|
38
|
+
workspace: {
|
|
39
|
+
bg: 'bg-[#f4f4f3] dark:bg-neutral-950',
|
|
40
|
+
border: 'border-transparent',
|
|
41
|
+
backdrop: '',
|
|
42
|
+
tint: false,
|
|
43
|
+
nav: 'px-5 py-5 space-y-1',
|
|
44
|
+
slot: 'px-5 py-5',
|
|
45
|
+
},
|
|
46
|
+
desktop: {
|
|
47
|
+
bg: 'bg-white/42 dark:bg-black/32',
|
|
48
|
+
border: 'border-white/30 dark:border-white/10',
|
|
49
|
+
backdrop: 'backdrop-blur-3xl backdrop-saturate-200',
|
|
50
|
+
tint: false,
|
|
51
|
+
shimmer: true,
|
|
52
|
+
nav: 'px-4 py-4 space-y-1',
|
|
53
|
+
slot: 'px-4 py-4',
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const style = variantStyles[variant] || variantStyles.tahoe
|
|
58
|
+
export const variantTint = style.tint
|
|
59
|
+
export const variantShimmer = style.shimmer
|
|
60
|
+
const borderSide = position === 'left' ? 'border-r' : 'border-l'
|
|
61
|
+
const borderClasses = bordered ? `${borderSide} ${style.border}` : ''
|
|
62
|
+
const positionClass = position === 'left' ? 'left-0' : 'right-0'
|
|
63
|
+
const placementClasses = {
|
|
64
|
+
fixed: `fixed inset-y-0 z-50 ${positionClass}`,
|
|
65
|
+
sticky: 'sticky top-0 h-screen',
|
|
66
|
+
static: 'relative h-full',
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const containerClasses = [
|
|
70
|
+
placementClasses[placement] || placementClasses.fixed,
|
|
71
|
+
'flex flex-col',
|
|
72
|
+
'transition-all duration-300 ease-out',
|
|
73
|
+
'overflow-hidden',
|
|
74
|
+
style.bg,
|
|
75
|
+
style.backdrop,
|
|
76
|
+
borderClasses,
|
|
77
|
+
className,
|
|
78
|
+
].filter(Boolean).join(' ')
|
|
79
|
+
|
|
80
|
+
export const collapseBtnPosition = position === 'left' ? 'right-2' : 'left-2'
|
|
81
|
+
export const navClasses = style.nav || 'px-3 py-2 space-y-1'
|
|
82
|
+
export const slotClasses = style.slot || 'px-3 py-2'
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<script client>
|
|
86
|
+
const emit = defineEmits()
|
|
87
|
+
const collapsedProp = {{ collapsed }}
|
|
88
|
+
const collapsibleProp = {{ collapsible }}
|
|
89
|
+
const widthProp = {{ width }}
|
|
90
|
+
const minWidthProp = {{ minWidth }}
|
|
91
|
+
const sectionsProp = {{ sections }}
|
|
92
|
+
const positionProp = {{ position }}
|
|
93
|
+
const variantProp = {{ variant }}
|
|
94
|
+
|
|
95
|
+
const collapsed = state(collapsedProp)
|
|
96
|
+
const isHovered = state(false)
|
|
97
|
+
const hasNativeSidebar = state(false)
|
|
98
|
+
|
|
99
|
+
const showExpanded = derived(() => !collapsed() || isHovered())
|
|
100
|
+
const sidebarWidth = derived(() => {
|
|
101
|
+
if (collapsed() && !isHovered()) return `${minWidthProp}px`
|
|
102
|
+
return `${widthProp}px`
|
|
103
|
+
})
|
|
104
|
+
const collapseAriaLabel = derived(() => collapsed() ? 'Expand sidebar' : 'Collapse sidebar')
|
|
105
|
+
const collapseIconClass = derived(() => collapsed() ? 'i-hugeicons-sidebar-right' : 'i-hugeicons-sidebar-left')
|
|
106
|
+
|
|
107
|
+
function onMouseEnter() {
|
|
108
|
+
if (collapsed() && collapsibleProp) isHovered.set(true)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function onMouseLeave() {
|
|
112
|
+
isHovered.set(false)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function toggleCollapse() {
|
|
116
|
+
collapsed.set(!collapsed())
|
|
117
|
+
emit('collapse', collapsed())
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function onSectionToggle(sectionId) {
|
|
121
|
+
emit('sectionToggle', sectionId)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function onItemClick(event) {
|
|
125
|
+
emit('itemClick', event.detail)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
onMount(async () => {
|
|
129
|
+
hasNativeSidebar.set(
|
|
130
|
+
window.__craftNativeSidebar === true
|
|
131
|
+
|| window.craft?.hasNativeSidebar?.()
|
|
132
|
+
|| (window.webkit?.messageHandlers?.craft && document.body?.dataset?.nativeSidebar === 'true'),
|
|
133
|
+
)
|
|
134
|
+
if (window.craft?.isCraft?.() && window.craft?.components?.createSidebar) {
|
|
135
|
+
try {
|
|
136
|
+
await window.craft.components.createSidebar({
|
|
137
|
+
position: positionProp,
|
|
138
|
+
width: widthProp,
|
|
139
|
+
minWidth: minWidthProp,
|
|
140
|
+
collapsible: collapsibleProp,
|
|
141
|
+
variant: variantProp,
|
|
142
|
+
material: variantProp === 'desktop' ? 'sidebar' : undefined,
|
|
143
|
+
backgroundEffect: variantProp === 'desktop' ? 'shimmer' : undefined,
|
|
144
|
+
allowsVibrancy: variantProp === 'desktop' || variantProp === 'vibrancy' || variantProp === 'tahoe',
|
|
145
|
+
sections: sectionsProp.map(s => ({
|
|
146
|
+
id: s.id,
|
|
147
|
+
label: s.label,
|
|
148
|
+
items: s.items?.map(i => ({ id: i.id, label: i.label, icon: i.icon, href: i.href, badge: i.badge })),
|
|
149
|
+
})),
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
console.warn('[Sidebar] Failed to create native sidebar, using web fallback:', err)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
defineExpose({ collapsed, toggle: toggleCollapse })
|
|
159
|
+
</script>
|
|
160
|
+
|
|
161
|
+
<aside
|
|
162
|
+
:show="!hasNativeSidebar()"
|
|
163
|
+
class="{{ containerClasses }} sidebar-container"
|
|
164
|
+
:style="`width: ${sidebarWidth()}`"
|
|
165
|
+
@mouseenter="onMouseEnter()"
|
|
166
|
+
@mouseleave="onMouseLeave()"
|
|
167
|
+
role="navigation"
|
|
168
|
+
aria-label="Sidebar navigation"
|
|
169
|
+
>
|
|
170
|
+
@if(variantTint)
|
|
171
|
+
<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>
|
|
172
|
+
@endif
|
|
173
|
+
@if(variantShimmer)
|
|
174
|
+
<div class="absolute inset-0 pointer-events-none bg-[radial-gradient(circle_at_18%_12%,rgba(255,255,255,0.46),transparent_28%),linear-gradient(120deg,rgba(255,255,255,0.22),rgba(255,255,255,0.04)_42%,rgba(255,255,255,0.18))] dark:bg-[radial-gradient(circle_at_18%_12%,rgba(255,255,255,0.12),transparent_30%),linear-gradient(120deg,rgba(255,255,255,0.10),rgba(255,255,255,0.02)_45%,rgba(255,255,255,0.08))]"></div>
|
|
175
|
+
@endif
|
|
176
|
+
|
|
177
|
+
@if($slots.header)
|
|
178
|
+
<div class="flex-shrink-0 relative z-10">
|
|
179
|
+
<slot name="header" />
|
|
180
|
+
</div>
|
|
181
|
+
@endif
|
|
182
|
+
|
|
183
|
+
<div class="flex-1 overflow-y-auto overflow-x-hidden relative z-10">
|
|
184
|
+
@if(sections.length > 0)
|
|
185
|
+
<nav class="{{ navClasses }}">
|
|
186
|
+
@foreach(section in sections)
|
|
187
|
+
<SidebarSection
|
|
188
|
+
:id="section.id"
|
|
189
|
+
:label="section.label"
|
|
190
|
+
:icon="section.icon"
|
|
191
|
+
:items="section.items"
|
|
192
|
+
:expanded="section.expanded !== false"
|
|
193
|
+
:collapsible="section.collapsible !== false"
|
|
194
|
+
:showLabel="showExpanded()"
|
|
195
|
+
:variant="variant"
|
|
196
|
+
@toggle="onSectionToggle(section.id)"
|
|
197
|
+
@itemClick="onItemClick($event)"
|
|
198
|
+
/>
|
|
199
|
+
@endforeach
|
|
200
|
+
</nav>
|
|
201
|
+
@else
|
|
202
|
+
<div class="{{ slotClasses }}">
|
|
203
|
+
<slot />
|
|
204
|
+
</div>
|
|
205
|
+
@endif
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
@if($slots.footer)
|
|
209
|
+
<div class="flex-shrink-0 relative z-10">
|
|
210
|
+
<slot name="footer" />
|
|
211
|
+
</div>
|
|
212
|
+
@endif
|
|
213
|
+
|
|
214
|
+
@if(collapsible)
|
|
215
|
+
<button
|
|
216
|
+
:show="showExpanded()"
|
|
217
|
+
type="button"
|
|
218
|
+
class="absolute top-4 {{ collapseBtnPosition }} 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"
|
|
219
|
+
@click="toggleCollapse()"
|
|
220
|
+
:aria-label="collapseAriaLabel()"
|
|
221
|
+
>
|
|
222
|
+
<div :class="`${collapseIconClass()} w-5 h-5`"></div>
|
|
223
|
+
</button>
|
|
224
|
+
@endif
|
|
225
|
+
</aside>
|
|
226
|
+
|
|
227
|
+
<style scoped>
|
|
228
|
+
.overflow-y-auto::-webkit-scrollbar { width: 6px; }
|
|
229
|
+
.overflow-y-auto::-webkit-scrollbar-track { background: transparent; }
|
|
230
|
+
.overflow-y-auto::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.15); border-radius: 3px; }
|
|
231
|
+
.overflow-y-auto::-webkit-scrollbar-thumb:hover { background-color: rgba(0, 0, 0, 0.25); }
|
|
232
|
+
@media (prefers-color-scheme: dark) {
|
|
233
|
+
.overflow-y-auto::-webkit-scrollbar-thumb { background-color: rgba(255, 255, 255, 0.15); }
|
|
234
|
+
.overflow-y-auto::-webkit-scrollbar-thumb:hover { background-color: rgba(255, 255, 255, 0.25); }
|
|
235
|
+
}
|
|
236
|
+
</style>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
export const showSettings = $props.showSettings ?? true
|
|
3
|
+
export const showThemeToggle = $props.showThemeToggle ?? true
|
|
4
|
+
export const settingsHref = $props.settingsHref || '/settings'
|
|
5
|
+
export const settingsLabel = $props.settingsLabel || 'Settings'
|
|
6
|
+
export const collapsed = $props.collapsed ?? false
|
|
7
|
+
export const actions = $props.actions || []
|
|
8
|
+
export const variant = $props.variant || 'tahoe'
|
|
9
|
+
|
|
10
|
+
const footerVariants = {
|
|
11
|
+
workspace: {
|
|
12
|
+
container: 'p-5 space-y-2',
|
|
13
|
+
action: 'flex-1 flex items-center gap-3 rounded-[18px] px-4 py-3 text-[17px] text-zinc-700 transition-colors hover:bg-[#ececea] hover:text-zinc-950 dark:text-zinc-300 dark:hover:bg-white/10',
|
|
14
|
+
icon: 'w-5 h-5 text-zinc-500 dark:text-zinc-400',
|
|
15
|
+
},
|
|
16
|
+
desktop: {
|
|
17
|
+
container: 'p-4 space-y-2',
|
|
18
|
+
action: 'flex-1 flex items-center gap-3 rounded-[16px] px-3.5 py-2.5 text-[15px] text-zinc-700 transition-colors hover:bg-white/34 hover:text-zinc-950 dark:text-zinc-300 dark:hover:bg-white/10',
|
|
19
|
+
icon: 'w-5 h-5 text-zinc-500 dark:text-zinc-400',
|
|
20
|
+
},
|
|
21
|
+
default: {
|
|
22
|
+
container: 'border-t border-black/5 dark:border-white/10 p-3 space-y-2',
|
|
23
|
+
action: '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',
|
|
24
|
+
icon: 'w-4 h-4',
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const footerStyle = footerVariants[variant] || footerVariants.default
|
|
29
|
+
export const footerClasses = footerStyle.container
|
|
30
|
+
export const actionClasses = footerStyle.action
|
|
31
|
+
export const footerIconClasses = footerStyle.icon
|
|
32
|
+
export const settingsIconClasses = `i-hugeicons-settings-02 ${footerStyle.icon}`
|
|
33
|
+
export const themeButtonClasses = variant === 'workspace' || variant === 'desktop'
|
|
34
|
+
? 'p-3 rounded-[16px] text-zinc-500 hover:text-zinc-900 hover:bg-white/50 dark:text-zinc-400 dark:hover:text-white dark:hover:bg-white/10 transition-colors'
|
|
35
|
+
: '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'
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<script client>
|
|
39
|
+
const emit = defineEmits()
|
|
40
|
+
|
|
41
|
+
function onThemeToggle() {
|
|
42
|
+
document.documentElement.classList.toggle('dark')
|
|
43
|
+
emit('themeToggle')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function onActionClick(index) {
|
|
47
|
+
emit('actionClick', index)
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<div class="{{ footerClasses }}">
|
|
52
|
+
<!-- Custom actions slot -->
|
|
53
|
+
<slot />
|
|
54
|
+
|
|
55
|
+
<!-- Quick actions row -->
|
|
56
|
+
@if(!collapsed && actions.length > 0)
|
|
57
|
+
<div class="flex items-center gap-2">
|
|
58
|
+
@foreach(actions as action)
|
|
59
|
+
@if(action.href)
|
|
60
|
+
<a
|
|
61
|
+
href="{{ action.href }}"
|
|
62
|
+
class="{{ actionClasses }}"
|
|
63
|
+
>
|
|
64
|
+
@if(action.icon)
|
|
65
|
+
<div class="{{ action.icon }} {{ footerIconClasses }}"></div>
|
|
66
|
+
@endif
|
|
67
|
+
<span>{{ action.label }}</span>
|
|
68
|
+
</a>
|
|
69
|
+
@else
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
class="{{ actionClasses }}"
|
|
73
|
+
@click="onActionClick({{ $loop.index }})"
|
|
74
|
+
>
|
|
75
|
+
@if(action.icon)
|
|
76
|
+
<div class="{{ action.icon }} {{ footerIconClasses }}"></div>
|
|
77
|
+
@endif
|
|
78
|
+
<span>{{ action.label }}</span>
|
|
79
|
+
</button>
|
|
80
|
+
@endif
|
|
81
|
+
@endforeach
|
|
82
|
+
|
|
83
|
+
@if(showThemeToggle)
|
|
84
|
+
<button
|
|
85
|
+
type="button"
|
|
86
|
+
class="{{ themeButtonClasses }}"
|
|
87
|
+
@click="onThemeToggle()"
|
|
88
|
+
aria-label="Toggle dark mode"
|
|
89
|
+
>
|
|
90
|
+
<div class="i-hugeicons-moon-02 w-4 h-4 dark:hidden"></div>
|
|
91
|
+
<div class="i-hugeicons-sun-02 w-4 h-4 hidden dark:block"></div>
|
|
92
|
+
</button>
|
|
93
|
+
@endif
|
|
94
|
+
</div>
|
|
95
|
+
@endif
|
|
96
|
+
|
|
97
|
+
<!-- Settings link -->
|
|
98
|
+
@if(showSettings)
|
|
99
|
+
<a
|
|
100
|
+
href="{{ settingsHref }}"
|
|
101
|
+
class="{{ actionClasses }}"
|
|
102
|
+
>
|
|
103
|
+
<div class="{{ settingsIconClasses }}"></div>
|
|
104
|
+
@if(!collapsed)
|
|
105
|
+
<span>{{ settingsLabel }}</span>
|
|
106
|
+
@endif
|
|
107
|
+
</a>
|
|
108
|
+
@endif
|
|
109
|
+
</div>
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
export const title = $props.title || ''
|
|
3
|
+
export const subtitle = $props.subtitle || ''
|
|
4
|
+
export const logo = $props.logo || ''
|
|
5
|
+
export const logoIcon = $props.logoIcon || ''
|
|
6
|
+
export const showSearch = $props.showSearch ?? false
|
|
7
|
+
export const searchPlaceholder = $props.searchPlaceholder || 'Search...'
|
|
8
|
+
export const searchValue = $props.searchValue || ''
|
|
9
|
+
export const collapsed = $props.collapsed ?? false
|
|
10
|
+
export const variant = $props.variant || 'tahoe'
|
|
11
|
+
|
|
12
|
+
const headerVariants = {
|
|
13
|
+
workspace: {
|
|
14
|
+
titlebar: 'h-18 flex items-center justify-between px-5',
|
|
15
|
+
logo: 'w-9 h-9 rounded-[12px] bg-black text-white flex items-center justify-center font-semibold text-sm',
|
|
16
|
+
title: 'text-[18px] font-semibold text-zinc-950 dark:text-white truncate',
|
|
17
|
+
subtitle: 'text-sm text-zinc-500 dark:text-zinc-400 truncate',
|
|
18
|
+
searchWrap: 'px-5 pb-4',
|
|
19
|
+
searchInput: 'w-full rounded-[16px] border border-zinc-200/70 bg-white/70 px-10 py-2.5 text-[15px] text-zinc-900 placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-zinc-300/80 dark:border-white/10 dark:bg-white/10 dark:text-white',
|
|
20
|
+
},
|
|
21
|
+
desktop: {
|
|
22
|
+
titlebar: 'h-16 flex items-center justify-between px-4',
|
|
23
|
+
logo: 'w-9 h-9 rounded-[12px] bg-black/80 text-white flex items-center justify-center font-semibold text-sm shadow-sm dark:bg-white/14',
|
|
24
|
+
title: 'text-[17px] font-semibold text-zinc-950 dark:text-white truncate',
|
|
25
|
+
subtitle: 'text-sm text-zinc-500 dark:text-zinc-400 truncate',
|
|
26
|
+
searchWrap: 'px-4 pb-4',
|
|
27
|
+
searchInput: 'w-full rounded-[16px] border border-white/40 bg-white/45 px-10 py-2.5 text-[15px] text-zinc-900 placeholder:text-zinc-500 shadow-sm focus:outline-none focus:ring-2 focus:ring-white/70 dark:border-white/10 dark:bg-white/10 dark:text-white',
|
|
28
|
+
},
|
|
29
|
+
default: {
|
|
30
|
+
titlebar: 'h-13 flex items-center justify-between px-4 border-b border-black/5 dark:border-white/10',
|
|
31
|
+
logo: 'w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center text-white font-bold text-sm',
|
|
32
|
+
title: 'text-sm font-semibold text-black/90 dark:text-white truncate',
|
|
33
|
+
subtitle: 'text-xs text-black/50 dark:text-white/50 truncate',
|
|
34
|
+
searchWrap: 'px-3 py-3',
|
|
35
|
+
searchInput: 'w-full pl-9 pr-3 py-2 text-sm bg-black/5 dark:bg-white/10 border border-black/10 dark:border-white/10 rounded-lg text-black/90 dark:text-white placeholder:text-black/40 dark:placeholder:text-white/40 focus:outline-none focus:ring-2 focus:ring-blue-500/30 focus:border-blue-400/50 transition-colors',
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const headerStyle = headerVariants[variant] || headerVariants.default
|
|
40
|
+
export const titlebarClasses = headerStyle.titlebar
|
|
41
|
+
export const logoClasses = headerStyle.logo
|
|
42
|
+
export const titleClasses = headerStyle.title
|
|
43
|
+
export const subtitleClasses = headerStyle.subtitle
|
|
44
|
+
export const searchWrapClasses = headerStyle.searchWrap
|
|
45
|
+
export const searchInputClasses = headerStyle.searchInput
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<script client>
|
|
49
|
+
const emit = defineEmits()
|
|
50
|
+
|
|
51
|
+
function onSearchInput(event) {
|
|
52
|
+
emit('search', event.target.value)
|
|
53
|
+
}
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<div class="flex-shrink-0">
|
|
57
|
+
<!-- Title bar area -->
|
|
58
|
+
<div class="{{ titlebarClasses }}">
|
|
59
|
+
@if(!collapsed)
|
|
60
|
+
<div class="flex items-center gap-3 min-w-0">
|
|
61
|
+
<!-- Logo -->
|
|
62
|
+
@if(logo)
|
|
63
|
+
<img src="{{ logo }}" alt="{{ title }}" class="w-8 h-8 rounded-lg object-cover" />
|
|
64
|
+
@elseif(logoIcon)
|
|
65
|
+
<div class="{{ logoClasses }}">
|
|
66
|
+
<div class="{{ logoIcon }} w-5 h-5 text-white"></div>
|
|
67
|
+
</div>
|
|
68
|
+
@else
|
|
69
|
+
<div class="{{ logoClasses }}">
|
|
70
|
+
{{ title.charAt(0) }}
|
|
71
|
+
</div>
|
|
72
|
+
@endif
|
|
73
|
+
|
|
74
|
+
<!-- Title & Subtitle -->
|
|
75
|
+
<div class="min-w-0">
|
|
76
|
+
@if(title)
|
|
77
|
+
<div class="{{ titleClasses }}">
|
|
78
|
+
{{ title }}
|
|
79
|
+
</div>
|
|
80
|
+
@endif
|
|
81
|
+
@if(subtitle)
|
|
82
|
+
<div class="{{ subtitleClasses }}">
|
|
83
|
+
{{ subtitle }}
|
|
84
|
+
</div>
|
|
85
|
+
@endif
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
@else
|
|
89
|
+
<!-- Collapsed state - just logo -->
|
|
90
|
+
@if(logo)
|
|
91
|
+
<img src="{{ logo }}" alt="{{ title }}" class="w-8 h-8 rounded-lg object-cover mx-auto" />
|
|
92
|
+
@elseif(logoIcon)
|
|
93
|
+
<div class="{{ logoClasses }} mx-auto">
|
|
94
|
+
<div class="{{ logoIcon }} w-5 h-5 text-white"></div>
|
|
95
|
+
</div>
|
|
96
|
+
@else
|
|
97
|
+
<div class="{{ logoClasses }} mx-auto">
|
|
98
|
+
{{ title.charAt(0) }}
|
|
99
|
+
</div>
|
|
100
|
+
@endif
|
|
101
|
+
@endif
|
|
102
|
+
|
|
103
|
+
<!-- Slot for custom header actions -->
|
|
104
|
+
<slot name="actions" />
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- Search (only when expanded) -->
|
|
108
|
+
@if(showSearch && !collapsed)
|
|
109
|
+
<div class="{{ searchWrapClasses }}">
|
|
110
|
+
<div class="relative">
|
|
111
|
+
<div class="i-hugeicons-search-01 absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-black/40 dark:text-white/40"></div>
|
|
112
|
+
<input
|
|
113
|
+
type="text"
|
|
114
|
+
placeholder="{{ searchPlaceholder }}"
|
|
115
|
+
value="{{ searchValue }}"
|
|
116
|
+
class="{{ searchInputClasses }}"
|
|
117
|
+
@input="onSearchInput($event)"
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
@endif
|
|
122
|
+
</div>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
export const id = $props.id || ''
|
|
3
|
+
export const label = $props.label || ''
|
|
4
|
+
export const icon = $props.icon || ''
|
|
5
|
+
export const href = $props.href || ''
|
|
6
|
+
export const badge = $props.badge || ''
|
|
7
|
+
export const active = $props.active ?? false
|
|
8
|
+
export const disabled = $props.disabled ?? false
|
|
9
|
+
export const indent = $props.indent ?? false
|
|
10
|
+
export const variant = $props.variant || 'tahoe'
|
|
11
|
+
|
|
12
|
+
const itemVariants = {
|
|
13
|
+
workspace: {
|
|
14
|
+
base: 'flex w-full items-center gap-3 rounded-[18px] px-4 py-3 text-[17px] leading-tight transition-colors duration-150',
|
|
15
|
+
indent: '',
|
|
16
|
+
active: 'bg-[#e7e7e5] text-zinc-950',
|
|
17
|
+
inactive: 'text-zinc-700 hover:bg-[#ececea] hover:text-zinc-950 dark:text-zinc-300 dark:hover:bg-white/10',
|
|
18
|
+
icon: active ? 'text-zinc-900 dark:text-zinc-100' : 'text-zinc-500 dark:text-zinc-500',
|
|
19
|
+
badge: 'ml-auto rounded-full bg-white/70 px-2.5 py-0.5 text-[13px] font-medium text-zinc-500 shadow-sm dark:bg-white/10 dark:text-zinc-400',
|
|
20
|
+
},
|
|
21
|
+
desktop: {
|
|
22
|
+
base: 'flex w-full items-center gap-3 rounded-[16px] px-3.5 py-2.5 text-[15px] leading-tight transition-colors duration-150',
|
|
23
|
+
indent: '',
|
|
24
|
+
active: 'bg-white/55 text-zinc-950 shadow-sm ring-1 ring-white/50 dark:bg-white/12 dark:text-white dark:ring-white/10',
|
|
25
|
+
inactive: 'text-zinc-700 hover:bg-white/34 hover:text-zinc-950 dark:text-zinc-300 dark:hover:bg-white/10',
|
|
26
|
+
icon: active ? 'text-zinc-900 dark:text-white' : 'text-zinc-500 dark:text-zinc-400',
|
|
27
|
+
badge: 'ml-auto rounded-full bg-white/55 px-2.5 py-0.5 text-xs font-medium text-zinc-500 shadow-sm dark:bg-white/10 dark:text-zinc-400',
|
|
28
|
+
},
|
|
29
|
+
default: {
|
|
30
|
+
base: 'flex items-center gap-2.5 px-2.5 py-1.5 rounded-md text-[13px] transition-all duration-150',
|
|
31
|
+
indent: indent ? 'ml-2' : '',
|
|
32
|
+
active: 'bg-blue-500/20 text-blue-600 dark:bg-blue-400/25 dark:text-blue-300 font-medium',
|
|
33
|
+
inactive: 'text-black/80 dark:text-white/80 hover:bg-black/5 dark:hover:bg-white/10',
|
|
34
|
+
icon: active ? 'text-blue-600 dark:text-blue-300' : 'text-black/50 dark:text-white/50',
|
|
35
|
+
badge: 'ml-auto px-2 py-0.5 text-xs font-medium rounded-full bg-black/10 text-black/70 dark:bg-white/15 dark:text-white/70',
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const itemStyle = itemVariants[variant] || itemVariants.default
|
|
40
|
+
const baseItemList = [itemStyle.base, itemStyle.indent]
|
|
41
|
+
const stateClasses = disabled ? 'opacity-50 cursor-not-allowed' : active ? itemStyle.active : itemStyle.inactive
|
|
42
|
+
|
|
43
|
+
export const itemClasses = [...baseItemList, stateClasses].filter(Boolean).join(' ')
|
|
44
|
+
export const iconClasses = `${icon} w-5 h-5 flex-shrink-0 ${itemStyle.icon}`
|
|
45
|
+
export const badgeClasses = itemStyle.badge
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<script client>
|
|
49
|
+
const emit = defineEmits()
|
|
50
|
+
const isDisabled = {{ disabled }}
|
|
51
|
+
const itemPayload = { id: {{ id }}, label: {{ label }}, href: {{ href }} }
|
|
52
|
+
|
|
53
|
+
function onClick(event) {
|
|
54
|
+
if (isDisabled) {
|
|
55
|
+
event.preventDefault()
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
emit('click', { item: itemPayload, event })
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
@if(href && !disabled)
|
|
63
|
+
<a
|
|
64
|
+
href="{{ href }}"
|
|
65
|
+
class="{{ itemClasses }}"
|
|
66
|
+
@click="onClick($event)"
|
|
67
|
+
@if(active)aria-current="page"@endif
|
|
68
|
+
>
|
|
69
|
+
@if(icon)
|
|
70
|
+
<div class="{{ iconClasses }}"></div>
|
|
71
|
+
@endif
|
|
72
|
+
<span class="flex-1 truncate">{{ label }}</span>
|
|
73
|
+
@if(badge)
|
|
74
|
+
<span class="{{ badgeClasses }}">{{ badge }}</span>
|
|
75
|
+
@endif
|
|
76
|
+
</a>
|
|
77
|
+
@else
|
|
78
|
+
<button
|
|
79
|
+
type="button"
|
|
80
|
+
class="{{ itemClasses }}"
|
|
81
|
+
@click="onClick($event)"
|
|
82
|
+
@if(disabled)disabled aria-disabled="true"@endif
|
|
83
|
+
>
|
|
84
|
+
@if(icon)
|
|
85
|
+
<div class="{{ iconClasses }}"></div>
|
|
86
|
+
@endif
|
|
87
|
+
<span class="flex-1 truncate text-left">{{ label }}</span>
|
|
88
|
+
@if(badge)
|
|
89
|
+
<span class="{{ badgeClasses }}">{{ badge }}</span>
|
|
90
|
+
@endif
|
|
91
|
+
</button>
|
|
92
|
+
@endif
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
export const id = $props.id || ''
|
|
3
|
+
export const label = $props.label || ''
|
|
4
|
+
export const icon = $props.icon || ''
|
|
5
|
+
export const items = $props.items || []
|
|
6
|
+
export const expanded = $props.expanded ?? true
|
|
7
|
+
export const collapsible = $props.collapsible ?? true
|
|
8
|
+
export const showLabel = $props.showLabel ?? true
|
|
9
|
+
export const variant = $props.variant || 'tahoe'
|
|
10
|
+
|
|
11
|
+
const headerVariants = {
|
|
12
|
+
workspace: {
|
|
13
|
+
base: 'w-full flex items-center gap-2.5 px-4 pb-2 pt-5 rounded-[14px] text-left transition-colors duration-150 text-[13px] font-semibold uppercase tracking-[0.12em] text-zinc-400 dark:text-zinc-500',
|
|
14
|
+
hover: 'hover:text-zinc-600 dark:hover:text-zinc-300 cursor-pointer',
|
|
15
|
+
},
|
|
16
|
+
desktop: {
|
|
17
|
+
base: 'w-full flex items-center gap-2.5 px-3.5 pb-2 pt-4 rounded-[14px] text-left transition-colors duration-150 text-[12px] font-semibold uppercase tracking-[0.10em] text-zinc-500/80 dark:text-zinc-400/80',
|
|
18
|
+
hover: 'hover:bg-white/25 dark:hover:bg-white/10 cursor-pointer',
|
|
19
|
+
},
|
|
20
|
+
default: {
|
|
21
|
+
base: 'w-full flex items-center gap-2.5 px-2.5 py-1.5 rounded-md text-left transition-all duration-150 text-black/60 dark:text-white/60',
|
|
22
|
+
hover: 'hover:bg-black/5 dark:hover:bg-white/10 cursor-pointer',
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const headerStyle = headerVariants[variant] || headerVariants.default
|
|
27
|
+
const headerClasses = [headerStyle.base, collapsible ? headerStyle.hover : ''].filter(Boolean).join(' ')
|
|
28
|
+
export const headerClassesValue = headerClasses
|
|
29
|
+
export const sectionClasses = variant === 'workspace' || variant === 'desktop' ? 'py-1.5' : 'py-1'
|
|
30
|
+
export const groupClasses = variant === 'workspace' || variant === 'desktop' ? 'space-y-1 overflow-hidden' : 'mt-1 space-y-0.5 overflow-hidden'
|
|
31
|
+
export const labelClasses = variant === 'workspace' || variant === 'desktop'
|
|
32
|
+
? 'flex-1 truncate'
|
|
33
|
+
: 'flex-1 text-[13px] font-semibold tracking-tight truncate'
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<script client>
|
|
37
|
+
const emit = defineEmits()
|
|
38
|
+
const sectionId = {{ id }}
|
|
39
|
+
const collapsibleProp = {{ collapsible }}
|
|
40
|
+
const isExpanded = state({{ expanded }})
|
|
41
|
+
|
|
42
|
+
function onHeaderClick() {
|
|
43
|
+
if (!collapsibleProp) return
|
|
44
|
+
isExpanded.set(!isExpanded())
|
|
45
|
+
emit('toggle', sectionId)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function onItemClick(event) {
|
|
49
|
+
emit('itemClick', event.detail)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const arrowClass = derived(() => `i-hugeicons-arrow-right-01 w-3 h-3 transition-transform duration-200 opacity-50 ${isExpanded() ? 'rotate-90' : ''}`)
|
|
53
|
+
|
|
54
|
+
defineExpose({ isExpanded })
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div class="{{ sectionClasses }}">
|
|
58
|
+
@if(collapsible)
|
|
59
|
+
<button
|
|
60
|
+
type="button"
|
|
61
|
+
class="{{ headerClassesValue }}"
|
|
62
|
+
@click="onHeaderClick()"
|
|
63
|
+
:aria-expanded="isExpanded() ? 'true' : 'false'"
|
|
64
|
+
aria-controls="sidebar-section-{{ id }}"
|
|
65
|
+
>
|
|
66
|
+
@if(icon)
|
|
67
|
+
<div class="{{ icon }} w-4 h-4 flex-shrink-0 opacity-70"></div>
|
|
68
|
+
@endif
|
|
69
|
+
@if(showLabel)
|
|
70
|
+
<span class="{{ labelClasses }}">
|
|
71
|
+
{{ label }}
|
|
72
|
+
</span>
|
|
73
|
+
<div :class="arrowClass()"></div>
|
|
74
|
+
@endif
|
|
75
|
+
</button>
|
|
76
|
+
@else
|
|
77
|
+
<div class="{{ headerClassesValue }}">
|
|
78
|
+
@if(icon)
|
|
79
|
+
<div class="{{ icon }} w-4 h-4 flex-shrink-0 opacity-70"></div>
|
|
80
|
+
@endif
|
|
81
|
+
@if(showLabel)
|
|
82
|
+
<span class="{{ labelClasses }}">
|
|
83
|
+
{{ label }}
|
|
84
|
+
</span>
|
|
85
|
+
@endif
|
|
86
|
+
</div>
|
|
87
|
+
@endif
|
|
88
|
+
|
|
89
|
+
@if(showLabel && items.length > 0)
|
|
90
|
+
<div
|
|
91
|
+
:show="isExpanded()"
|
|
92
|
+
id="sidebar-section-{{ id }}"
|
|
93
|
+
class="{{ groupClasses }}"
|
|
94
|
+
role="group"
|
|
95
|
+
aria-label="{{ label }}"
|
|
96
|
+
>
|
|
97
|
+
@foreach(item in items)
|
|
98
|
+
<SidebarItem
|
|
99
|
+
:id="item.id"
|
|
100
|
+
:label="item.label"
|
|
101
|
+
:icon="item.icon"
|
|
102
|
+
:href="item.href"
|
|
103
|
+
:badge="item.badge"
|
|
104
|
+
:active="item.active"
|
|
105
|
+
:disabled="item.disabled"
|
|
106
|
+
:indent="variant !== 'workspace' && variant !== 'desktop'"
|
|
107
|
+
:variant="variant"
|
|
108
|
+
@click="onItemClick($event)"
|
|
109
|
+
/>
|
|
110
|
+
@endforeach
|
|
111
|
+
</div>
|
|
112
|
+
@endif
|
|
113
|
+
</div>
|