@stacksjs/components 0.2.91 → 0.2.92
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 +458 -0
- package/dist/SidebarFooter-0aegr99p.stx +71 -0
- package/dist/SidebarHeader-ed5wb13t.stx +103 -0
- package/dist/SidebarItem-gtrcw4ky.stx +86 -0
- package/dist/SidebarSection-qf8fhy55.stx +83 -0
- package/dist/index.js +168 -12
- package/dist/ui/sidebar/index.d.ts +67 -107
- package/dist/ui/sidebar/themes.d.ts +74 -0
- package/package.json +10 -4
- package/src/ui/sidebar/Sidebar.stx +356 -228
- package/src/ui/sidebar/SidebarFooter.stx +56 -94
- package/src/ui/sidebar/SidebarHeader.stx +69 -165
- package/src/ui/sidebar/SidebarItem.stx +73 -79
- package/src/ui/sidebar/SidebarSection.stx +70 -101
- package/src/ui/sidebar/index.ts +100 -172
- package/src/ui/sidebar/themes.ts +240 -0
- package/stx-plugin.ts +19 -0
- package/dist/Sidebar-0jzasrmt.stx +0 -330
- package/dist/SidebarFooter-zsfsgcxb.stx +0 -109
- package/dist/SidebarHeader-ywpmx14d.stx +0 -199
- package/dist/SidebarItem-h53sgqzz.stx +0 -92
- package/dist/SidebarSection-g195cb4f.stx +0 -114
|
@@ -1,109 +1,71 @@
|
|
|
1
1
|
<script server>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export const
|
|
5
|
-
export const
|
|
6
|
-
export const
|
|
7
|
-
export const
|
|
8
|
-
export const
|
|
2
|
+
// The bottom strip of a macOS sidebar — e.g. the account row in Music
|
|
3
|
+
// ("Chris Breuer" with an avatar) or custom actions.
|
|
4
|
+
export const theme = $props.theme || $props.variant || 'macos'
|
|
5
|
+
export const avatar = $props.avatar || ''
|
|
6
|
+
export const name = $props.name || ''
|
|
7
|
+
export const detail = $props.detail || ''
|
|
8
|
+
export const actions = $props.actions || [] // [{ id, icon, label }]
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
}
|
|
10
|
+
export const isMacos = theme === 'macos' || theme === 'tahoe' || theme === 'macos-tahoe' || theme === 'macos-latest'
|
|
11
|
+
|
|
12
|
+
export const profileClasses = [
|
|
13
|
+
'flex w-full items-center gap-[8px]',
|
|
14
|
+
'h-[44px] rounded-[10px] px-[6px]',
|
|
15
|
+
'text-[13px] font-medium text-black dark:text-white',
|
|
16
|
+
'transition-colors duration-150',
|
|
17
|
+
'hover:bg-black/4 active:bg-black/8',
|
|
18
|
+
'dark:hover:bg-white/6 dark:active:bg-white/12',
|
|
19
|
+
].join(' ')
|
|
27
20
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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'
|
|
21
|
+
export const actionClasses = [
|
|
22
|
+
'grid h-[28px] w-[28px] place-items-center rounded-[7px]',
|
|
23
|
+
'text-[#3c3c43]/70 dark:text-[#ebebf5]/70',
|
|
24
|
+
'transition-colors duration-150',
|
|
25
|
+
'hover:bg-black/5 dark:hover:bg-white/10',
|
|
26
|
+
].join(' ')
|
|
36
27
|
</script>
|
|
37
28
|
|
|
38
29
|
<script client>
|
|
39
30
|
const emit = defineEmits()
|
|
40
31
|
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
emit('themeToggle')
|
|
32
|
+
function onProfileClick() {
|
|
33
|
+
emit('profileClick')
|
|
44
34
|
}
|
|
45
35
|
|
|
46
|
-
function
|
|
47
|
-
emit('
|
|
36
|
+
function onAction(event) {
|
|
37
|
+
emit('action', event.target.closest('[data-sidebar-action]')?.dataset.sidebarAction)
|
|
48
38
|
}
|
|
49
39
|
</script>
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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>
|
|
41
|
+
@if(isMacos)
|
|
42
|
+
<div class="shrink-0 px-[10px] pb-[8px] pt-[4px]">
|
|
43
|
+
@if(name)
|
|
44
|
+
<button type="button" class="{{ profileClasses }}" @click="onProfileClick()">
|
|
45
|
+
@if(avatar)
|
|
46
|
+
<img src="{{ avatar }}" alt="" class="h-[30px] w-[30px] rounded-full object-cover shadow-sm" />
|
|
69
47
|
@else
|
|
70
|
-
<
|
|
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>
|
|
48
|
+
<span class="grid h-[30px] w-[30px] place-items-center rounded-full bg-black/10 text-[13px] font-semibold dark:bg-white/15">{{ name.charAt(0) }}</span>
|
|
80
49
|
@endif
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<div class="{{ settingsIconClasses }}"></div>
|
|
104
|
-
@if(!collapsed)
|
|
105
|
-
<span>{{ settingsLabel }}</span>
|
|
106
|
-
@endif
|
|
107
|
-
</a>
|
|
108
|
-
@endif
|
|
109
|
-
</div>
|
|
50
|
+
<span class="min-w-0 flex-1 truncate text-left">{{ name }}</span>
|
|
51
|
+
@if(detail)
|
|
52
|
+
<span class="shrink-0 text-[12px] text-[#3c3c43]/60 dark:text-[#ebebf5]/60">{{ detail }}</span>
|
|
53
|
+
@endif
|
|
54
|
+
</button>
|
|
55
|
+
@endif
|
|
56
|
+
@if(actions.length > 0)
|
|
57
|
+
<div class="flex items-center gap-[4px] pt-[4px]">
|
|
58
|
+
@foreach(actions as action)
|
|
59
|
+
<button type="button" class="{{ actionClasses }}" aria-label="{{ action.label }}" data-sidebar-action="{{ action.id }}" @click="onAction($event)">
|
|
60
|
+
<span class="{{ action.icon }} h-[16px] w-[16px]" aria-hidden="true"></span>
|
|
61
|
+
</button>
|
|
62
|
+
@endforeach
|
|
63
|
+
</div>
|
|
64
|
+
@endif
|
|
65
|
+
<slot />
|
|
66
|
+
</div>
|
|
67
|
+
@else
|
|
68
|
+
<div class="shrink-0 border-t border-black/5 p-3 dark:border-white/10">
|
|
69
|
+
<slot />
|
|
70
|
+
</div>
|
|
71
|
+
@endif
|
|
@@ -1,199 +1,103 @@
|
|
|
1
1
|
<script server>
|
|
2
|
+
// The window-chrome strip at the top of a macOS sidebar: traffic lights on
|
|
3
|
+
// the left, floating "Liquid Glass" toolbar buttons on the right, and the
|
|
4
|
+
// whole strip acts as a window drag region inside a Craft window.
|
|
5
|
+
export const theme = $props.theme || $props.variant || 'macos'
|
|
6
|
+
export const showWindowControls = $props.showWindowControls ?? true
|
|
7
|
+
export const actions = $props.actions || [] // [{ id, icon, label }]
|
|
2
8
|
export const title = $props.title || ''
|
|
3
9
|
export const subtitle = $props.subtitle || ''
|
|
4
10
|
export const logo = $props.logo || ''
|
|
5
|
-
export const logoIcon = $props.logoIcon || ''
|
|
6
11
|
export const showSearch = $props.showSearch ?? false
|
|
7
|
-
export const searchPlaceholder = $props.searchPlaceholder || 'Search
|
|
8
|
-
|
|
9
|
-
export const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
trafficLight: 'h-3.5 w-3.5 rounded-full shadow-[inset_0_0_0_1px_rgba(0,0,0,0.08)]',
|
|
27
|
-
navButton: 'inline-flex h-7 w-7 items-center justify-center rounded-full text-zinc-500/80 transition-colors hover:bg-white/35 hover:text-zinc-900 disabled:opacity-35 disabled:hover:bg-transparent disabled:hover:text-zinc-500/80 dark:text-zinc-400 dark:hover:bg-white/10 dark:hover:text-white',
|
|
28
|
-
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',
|
|
29
|
-
title: 'text-[15px] leading-[22px] font-medium text-zinc-950 dark:text-white truncate',
|
|
30
|
-
subtitle: 'text-xs text-zinc-500 dark:text-zinc-400 truncate',
|
|
31
|
-
searchWrap: 'px-4 pb-4',
|
|
32
|
-
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',
|
|
33
|
-
},
|
|
34
|
-
default: {
|
|
35
|
-
titlebar: 'h-13 flex items-center justify-between px-4 border-b border-black/5 dark:border-white/10',
|
|
36
|
-
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',
|
|
37
|
-
title: 'text-sm font-semibold text-black/90 dark:text-white truncate',
|
|
38
|
-
subtitle: 'text-xs text-black/50 dark:text-white/50 truncate',
|
|
39
|
-
searchWrap: 'px-3 py-3',
|
|
40
|
-
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',
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const headerStyle = headerVariants[variant] || headerVariants.default
|
|
45
|
-
export const titlebarClasses = headerStyle.titlebar
|
|
46
|
-
export const chromeClasses = headerStyle.chrome || ''
|
|
47
|
-
export const trafficLightClasses = headerStyle.trafficLight || ''
|
|
48
|
-
export const navButtonClasses = headerStyle.navButton || ''
|
|
49
|
-
export const logoClasses = headerStyle.logo
|
|
50
|
-
export const titleClasses = headerStyle.title
|
|
51
|
-
export const subtitleClasses = headerStyle.subtitle
|
|
52
|
-
export const searchWrapClasses = headerStyle.searchWrap
|
|
53
|
-
export const searchInputClasses = headerStyle.searchInput
|
|
54
|
-
export const hasDesktopChrome = variant === 'desktop' && (showWindowControls || showNavigationControls)
|
|
55
|
-
export const windowControlsClasses = showWindowControls === 'auto' ? 'hidden items-center gap-2' : 'flex items-center gap-2'
|
|
12
|
+
export const searchPlaceholder = $props.searchPlaceholder || 'Search'
|
|
13
|
+
|
|
14
|
+
export const isMacos = theme === 'macos' || theme === 'tahoe' || theme === 'macos-tahoe' || theme === 'macos-latest'
|
|
15
|
+
|
|
16
|
+
// Traffic lights: 13px circles, 7px apart, inset ~21px — measured on
|
|
17
|
+
// macOS Tahoe. Colors are Apple's exact stoplight values.
|
|
18
|
+
export const trafficLightClasses = 'h-[13px] w-[13px] rounded-full shadow-[inset_0_0_1px_rgba(0,0,0,0.2)]'
|
|
19
|
+
|
|
20
|
+
// Floating toolbar button: a 34px glass circle that brightens on hover.
|
|
21
|
+
export const toolbarButtonClasses = [
|
|
22
|
+
'grid h-[34px] w-[34px] place-items-center rounded-full',
|
|
23
|
+
'bg-white/55 dark:bg-white/10',
|
|
24
|
+
'backdrop-blur-xl',
|
|
25
|
+
'shadow-[0_1px_4px_rgba(0,0,0,0.12),inset_0_0.5px_0_rgba(255,255,255,0.6)]',
|
|
26
|
+
'text-[#3c3c43]/75 dark:text-[#ebebf5]/75',
|
|
27
|
+
'transition-colors duration-150',
|
|
28
|
+
'hover:bg-white/80 dark:hover:bg-white/20',
|
|
29
|
+
'active:bg-white/95 dark:active:bg-white/25',
|
|
30
|
+
].join(' ')
|
|
56
31
|
</script>
|
|
57
32
|
|
|
58
33
|
<script client>
|
|
59
34
|
const emit = defineEmits()
|
|
60
35
|
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
|| window.craft?.__craft_bridge_loaded === true
|
|
64
|
-
|| !!window.craft?.window
|
|
65
|
-
|| !!window.webkit?.messageHandlers?.craft
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function usesCustomWindowControls() {
|
|
69
|
-
return window.__craftCustomWindowControls === true
|
|
70
|
-
|| document.documentElement.classList.contains('has-custom-window-controls')
|
|
71
|
-
|| document.body?.dataset?.customWindowControls === 'true'
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function syncWindowControls() {
|
|
75
|
-
document.querySelectorAll('[data-stx-native-window-controls]').forEach((element) => {
|
|
76
|
-
const show = element.dataset.stxNativeWindowControls === 'auto'
|
|
77
|
-
? isCraftRuntime() && usesCustomWindowControls()
|
|
78
|
-
: true
|
|
79
|
-
element.classList.toggle('hidden', !show)
|
|
80
|
-
element.classList.toggle('flex', show)
|
|
81
|
-
})
|
|
36
|
+
function onAction(event) {
|
|
37
|
+
emit('action', event.currentTarget?.dataset?.sidebarAction || event.target.closest('[data-sidebar-action]')?.dataset.sidebarAction)
|
|
82
38
|
}
|
|
83
39
|
|
|
84
40
|
function onSearchInput(event) {
|
|
85
41
|
emit('search', event.target.value)
|
|
86
42
|
}
|
|
87
43
|
|
|
88
|
-
function
|
|
89
|
-
window.
|
|
90
|
-
|
|
44
|
+
function onWindowControl(action) {
|
|
45
|
+
const api = window.craft?.window
|
|
46
|
+
if (action === 'close') api?.close?.()
|
|
47
|
+
if (action === 'minimize') api?.minimize?.()
|
|
48
|
+
if (action === 'zoom') (api?.maximize || api?.zoom)?.call(api)
|
|
49
|
+
emit('windowControl', action)
|
|
91
50
|
}
|
|
92
|
-
|
|
93
|
-
function goForward() {
|
|
94
|
-
window.history.forward()
|
|
95
|
-
emit('forward')
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
onMount(() => {
|
|
99
|
-
syncWindowControls()
|
|
100
|
-
window.addEventListener('craft:ready', syncWindowControls)
|
|
101
|
-
window.addEventListener('stx:load', syncWindowControls)
|
|
102
|
-
|
|
103
|
-
return () => {
|
|
104
|
-
window.removeEventListener('craft:ready', syncWindowControls)
|
|
105
|
-
window.removeEventListener('stx:load', syncWindowControls)
|
|
106
|
-
}
|
|
107
|
-
})
|
|
108
51
|
</script>
|
|
109
52
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
@
|
|
116
|
-
|
|
117
|
-
<span class="{{ trafficLightClasses }} bg-[#ff5f57]"></span>
|
|
118
|
-
<span class="{{ trafficLightClasses }} bg-[#ffbd2e]"></span>
|
|
119
|
-
<span class="{{ trafficLightClasses }} bg-[#28c840]"></span>
|
|
120
|
-
</div>
|
|
121
|
-
@endif
|
|
122
|
-
|
|
123
|
-
@if(showNavigationControls)
|
|
124
|
-
<div class="flex items-center gap-2">
|
|
125
|
-
<button type="button" class="{{ navButtonClasses }}" @click="goBack()" aria-label="Go back">
|
|
126
|
-
<span class="h-5 w-5 i-hugeicons-arrow-left-02" aria-hidden="true"></span>
|
|
127
|
-
</button>
|
|
128
|
-
<button type="button" class="{{ navButtonClasses }}" @click="goForward()" aria-label="Go forward">
|
|
129
|
-
<span class="h-5 w-5 i-hugeicons-arrow-right-02" aria-hidden="true"></span>
|
|
130
|
-
</button>
|
|
131
|
-
</div>
|
|
132
|
-
@endif
|
|
133
|
-
</div>
|
|
134
|
-
@endif
|
|
135
|
-
|
|
136
|
-
@if(!collapsed)
|
|
137
|
-
<div class="flex items-center gap-3 min-w-0">
|
|
138
|
-
<!-- Logo -->
|
|
139
|
-
@if(logo)
|
|
140
|
-
<img src="{{ logo }}" alt="{{ title }}" class="w-8 h-8 rounded-lg object-cover" />
|
|
141
|
-
@elseif(logoIcon)
|
|
142
|
-
<div class="{{ logoClasses }}">
|
|
143
|
-
<div class="{{ logoIcon }} w-5 h-5 text-white"></div>
|
|
144
|
-
</div>
|
|
145
|
-
@else
|
|
146
|
-
<div class="{{ logoClasses }}">
|
|
147
|
-
{{ title.charAt(0) }}
|
|
148
|
-
</div>
|
|
149
|
-
@endif
|
|
150
|
-
|
|
151
|
-
<!-- Title & Subtitle -->
|
|
152
|
-
<div class="min-w-0">
|
|
153
|
-
@if(title)
|
|
154
|
-
<div class="{{ titleClasses }}">
|
|
155
|
-
{{ title }}
|
|
156
|
-
</div>
|
|
157
|
-
@endif
|
|
158
|
-
@if(subtitle)
|
|
159
|
-
<div class="{{ subtitleClasses }}">
|
|
160
|
-
{{ subtitle }}
|
|
161
|
-
</div>
|
|
162
|
-
@endif
|
|
163
|
-
</div>
|
|
53
|
+
@if(isMacos)
|
|
54
|
+
<div class="flex h-[52px] shrink-0 items-center justify-between pl-[21px] pr-[10px] [-webkit-app-region:drag]">
|
|
55
|
+
@if(showWindowControls)
|
|
56
|
+
<div class="group/lights flex items-center gap-[7px] [-webkit-app-region:no-drag]">
|
|
57
|
+
<button type="button" class="{{ trafficLightClasses }} bg-[#ff5f57]" aria-label="Close window" @click="onWindowControl('close')"></button>
|
|
58
|
+
<button type="button" class="{{ trafficLightClasses }} bg-[#febc2e]" aria-label="Minimize window" @click="onWindowControl('minimize')"></button>
|
|
59
|
+
<button type="button" class="{{ trafficLightClasses }} bg-[#28c840]" aria-label="Zoom window" @click="onWindowControl('zoom')"></button>
|
|
164
60
|
</div>
|
|
165
61
|
@else
|
|
166
|
-
|
|
167
|
-
@if(logo)
|
|
168
|
-
<img src="{{ logo }}" alt="{{ title }}" class="w-8 h-8 rounded-lg object-cover mx-auto" />
|
|
169
|
-
@elseif(logoIcon)
|
|
170
|
-
<div class="{{ logoClasses }} mx-auto">
|
|
171
|
-
<div class="{{ logoIcon }} w-5 h-5 text-white"></div>
|
|
172
|
-
</div>
|
|
173
|
-
@else
|
|
174
|
-
<div class="{{ logoClasses }} mx-auto">
|
|
175
|
-
{{ title.charAt(0) }}
|
|
176
|
-
</div>
|
|
177
|
-
@endif
|
|
62
|
+
<span></span>
|
|
178
63
|
@endif
|
|
179
64
|
|
|
180
|
-
|
|
181
|
-
|
|
65
|
+
<div class="flex items-center gap-[8px] [-webkit-app-region:no-drag]">
|
|
66
|
+
@foreach(actions as action)
|
|
67
|
+
<button type="button" class="{{ toolbarButtonClasses }}" aria-label="{{ action.label }}" data-sidebar-action="{{ action.id }}" @click="onAction($event)">
|
|
68
|
+
<span class="{{ action.icon }} h-[16px] w-[16px]" aria-hidden="true"></span>
|
|
69
|
+
</button>
|
|
70
|
+
@endforeach
|
|
71
|
+
<slot name="actions" />
|
|
72
|
+
</div>
|
|
182
73
|
</div>
|
|
183
74
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
<div class="{{ searchWrapClasses }}">
|
|
75
|
+
@if(showSearch)
|
|
76
|
+
<div class="shrink-0 px-[10px] pb-[6px]">
|
|
187
77
|
<div class="relative">
|
|
188
|
-
<
|
|
78
|
+
<span class="i-f7-search absolute left-[8px] top-1/2 h-[13px] w-[13px] -translate-y-1/2 text-[#3c3c43]/50 dark:text-[#ebebf5]/50" aria-hidden="true"></span>
|
|
189
79
|
<input
|
|
190
80
|
type="text"
|
|
191
81
|
placeholder="{{ searchPlaceholder }}"
|
|
192
|
-
|
|
193
|
-
class="{{ searchInputClasses }}"
|
|
82
|
+
class="h-[28px] w-full rounded-[8px] border-0 bg-black/5 pl-[26px] pr-[8px] text-[13px] text-black placeholder:text-[#3c3c43]/50 focus:outline-none focus:ring-2 focus:ring-[#0088ff]/60 dark:bg-white/10 dark:text-white dark:placeholder:text-[#ebebf5]/50"
|
|
194
83
|
@input="onSearchInput($event)"
|
|
195
84
|
/>
|
|
196
85
|
</div>
|
|
197
86
|
</div>
|
|
198
87
|
@endif
|
|
199
|
-
|
|
88
|
+
@else
|
|
89
|
+
<div class="flex h-13 items-center gap-3 border-b border-black/5 px-4 dark:border-white/10">
|
|
90
|
+
@if(logo)
|
|
91
|
+
<img src="{{ logo }}" alt="{{ title }}" class="h-8 w-8 rounded-lg object-cover" />
|
|
92
|
+
@endif
|
|
93
|
+
<div class="min-w-0">
|
|
94
|
+
@if(title)
|
|
95
|
+
<div class="truncate text-sm font-semibold text-black/90 dark:text-white">{{ title }}</div>
|
|
96
|
+
@endif
|
|
97
|
+
@if(subtitle)
|
|
98
|
+
<div class="truncate text-xs text-black/50 dark:text-white/50">{{ subtitle }}</div>
|
|
99
|
+
@endif
|
|
100
|
+
</div>
|
|
101
|
+
<slot name="actions" />
|
|
102
|
+
</div>
|
|
103
|
+
@endif
|
|
@@ -1,92 +1,86 @@
|
|
|
1
1
|
<script server>
|
|
2
|
+
import { resolveIconColor, resolveSidebarTheme } from './themes'
|
|
3
|
+
|
|
2
4
|
export const id = $props.id || ''
|
|
3
5
|
export const label = $props.label || ''
|
|
4
6
|
export const icon = $props.icon || ''
|
|
7
|
+
export const iconColor = $props.iconColor || ''
|
|
8
|
+
export const image = $props.image || ''
|
|
5
9
|
export const href = $props.href || ''
|
|
6
|
-
export const
|
|
10
|
+
export const count = $props.count ?? $props.badge ?? ''
|
|
7
11
|
export const active = $props.active ?? false
|
|
8
12
|
export const disabled = $props.disabled ?? false
|
|
9
|
-
export const
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
base: 'flex w-full items-center gap-3 rounded-[16px] px-3.5 py-2 text-[14px] 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 grid h-5 min-w-5 place-items-center rounded-full bg-white/70 px-1.5 text-[11px] 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-[14px] px-3 py-1.5 text-[13.5px] leading-tight transition-colors duration-150',
|
|
23
|
-
indent: '',
|
|
24
|
-
active: 'bg-[#e8e8e6]/90 text-zinc-950 dark:bg-white/12 dark:text-white',
|
|
25
|
-
inactive: 'text-zinc-700 hover:bg-white/38 hover:text-zinc-950 dark:text-zinc-300 dark:hover:bg-white/10',
|
|
26
|
-
icon: active ? 'text-zinc-900 dark:text-white' : 'text-zinc-600 dark:text-zinc-400',
|
|
27
|
-
badge: 'ml-auto grid h-5 min-w-5 place-items-center rounded-full bg-white/72 px-1.5 text-[11px] 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
|
-
}
|
|
13
|
+
export const expandable = $props.expandable ?? false
|
|
14
|
+
export const expanded = $props.expanded ?? true
|
|
15
|
+
export const depth = $props.depth || 0
|
|
16
|
+
export const parents = $props.parents || ''
|
|
17
|
+
export const theme = $props.theme || $props.variant || 'macos'
|
|
38
18
|
|
|
39
|
-
const
|
|
40
|
-
const baseItemList = [itemStyle.base, itemStyle.indent]
|
|
41
|
-
const stateClasses = disabled ? 'opacity-50 cursor-not-allowed' : active ? itemStyle.active : itemStyle.inactive
|
|
19
|
+
const themeStyle = resolveSidebarTheme(theme).item
|
|
42
20
|
|
|
43
|
-
export const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
21
|
+
export const rowClasses = [
|
|
22
|
+
themeStyle.base,
|
|
23
|
+
disabled ? themeStyle.disabled : themeStyle.hover,
|
|
24
|
+
disabled ? '' : themeStyle.pressed,
|
|
25
|
+
active ? themeStyle.active : '',
|
|
26
|
+
].filter(Boolean).join(' ')
|
|
47
27
|
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
28
|
+
export const disclosureClasses = themeStyle.disclosure
|
|
29
|
+
export const chevronClasses = themeStyle.chevron
|
|
30
|
+
export const iconSlotClasses = themeStyle.iconSlot
|
|
31
|
+
export const iconClasses = `${icon} ${themeStyle.icon}`
|
|
32
|
+
export const imageClasses = themeStyle.image
|
|
33
|
+
export const labelClasses = themeStyle.label
|
|
34
|
+
export const countClasses = themeStyle.count
|
|
52
35
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
36
|
+
// Nested rows are indented; the icon tint accepts macOS system color names
|
|
37
|
+
// ("blue", "yellow", …) or any CSS color.
|
|
38
|
+
export const indentStyle = depth > 0 ? `padding-left: ${depth * themeStyle.indentPerLevel}px` : ''
|
|
39
|
+
const tint = resolveIconColor(iconColor)
|
|
40
|
+
export const iconStyle = tint ? `color: ${tint}` : ''
|
|
41
|
+
export const isLink = Boolean(href) && !disabled
|
|
60
42
|
</script>
|
|
61
43
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
44
|
+
<div
|
|
45
|
+
data-sidebar-row
|
|
46
|
+
data-item-id="{{ id }}"
|
|
47
|
+
@if(parents)data-parents="{{ parents }}"@endif
|
|
48
|
+
@if(expandable)data-expanded="{{ expanded ? 'true' : 'false' }}"@endif
|
|
49
|
+
@if(indentStyle)style="{{ indentStyle }}"@endif
|
|
50
|
+
>
|
|
51
|
+
@if(isLink)
|
|
52
|
+
<a href="{{ href }}" data-sidebar-item data-label="{{ label }}" class="{{ rowClasses }}" @if(active)data-active="true" aria-current="page"@endif>
|
|
53
|
+
<span class="{{ disclosureClasses }}" @if(expandable)data-sidebar-disclosure role="button" aria-label="Toggle {{ label }}"@endif>
|
|
54
|
+
@if(expandable)
|
|
55
|
+
<span class="{{ chevronClasses }}" aria-hidden="true"></span>
|
|
56
|
+
@endif
|
|
57
|
+
</span>
|
|
58
|
+
@if(image)
|
|
59
|
+
<span class="{{ iconSlotClasses }}"><img src="{{ image }}" alt="" class="{{ imageClasses }}" /></span>
|
|
60
|
+
@elseif(icon)
|
|
61
|
+
<span class="{{ iconSlotClasses }}"><span class="{{ iconClasses }}" @if(iconStyle)style="{{ iconStyle }}"@endif aria-hidden="true"></span></span>
|
|
62
|
+
@endif
|
|
63
|
+
<span class="{{ labelClasses }}">{{ label }}</span>
|
|
64
|
+
@if(count !== '' && count !== null && count !== undefined)
|
|
65
|
+
<span class="{{ countClasses }}">{{ count }}</span>
|
|
66
|
+
@endif
|
|
67
|
+
</a>
|
|
68
|
+
@else
|
|
69
|
+
<button type="button" data-sidebar-item data-label="{{ label }}" class="{{ rowClasses }}" @if(active)data-active="true" aria-current="page"@endif @if(disabled)data-disabled disabled aria-disabled="true"@endif>
|
|
70
|
+
<span class="{{ disclosureClasses }}" @if(expandable)data-sidebar-disclosure role="button" aria-label="Toggle {{ label }}"@endif>
|
|
71
|
+
@if(expandable)
|
|
72
|
+
<span class="{{ chevronClasses }}" aria-hidden="true"></span>
|
|
73
|
+
@endif
|
|
74
|
+
</span>
|
|
75
|
+
@if(image)
|
|
76
|
+
<span class="{{ iconSlotClasses }}"><img src="{{ image }}" alt="" class="{{ imageClasses }}" /></span>
|
|
77
|
+
@elseif(icon)
|
|
78
|
+
<span class="{{ iconSlotClasses }}"><span class="{{ iconClasses }}" @if(iconStyle)style="{{ iconStyle }}"@endif aria-hidden="true"></span></span>
|
|
79
|
+
@endif
|
|
80
|
+
<span class="{{ labelClasses }}">{{ label }}</span>
|
|
81
|
+
@if(count !== '' && count !== null && count !== undefined)
|
|
82
|
+
<span class="{{ countClasses }}">{{ count }}</span>
|
|
83
|
+
@endif
|
|
84
|
+
</button>
|
|
85
|
+
@endif
|
|
86
|
+
</div>
|