@stacksjs/components 0.2.119 → 0.2.120
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-hjazqxv8.stx → Sidebar-vgckg8f7.stx} +185 -33
- package/dist/{SidebarFooter-0aegr99p.stx → SidebarFooter-s7skj92y.stx} +46 -0
- package/dist/{SidebarHeader-ed5wb13t.stx → SidebarHeader-sa9mtdst.stx} +94 -1
- package/dist/SidebarPinned-tvj28bm0.stx +88 -0
- package/dist/SidebarSpace-61b662jx.stx +134 -0
- package/dist/SidebarSpaceSwitcher-882njcxq.stx +98 -0
- package/dist/SidebarSpaces-gq3mx984.stx +560 -0
- package/dist/index.js +164 -7
- package/dist/ui/sidebar/index.d.ts +17 -30
- package/dist/ui/sidebar/rows.d.ts +43 -0
- package/dist/ui/sidebar/spaces.d.ts +105 -0
- package/dist/ui/sidebar/themes.d.ts +5 -0
- package/package.json +3 -3
- package/src/ui/sidebar/Sidebar.stx +185 -33
- package/src/ui/sidebar/SidebarFooter.stx +46 -0
- package/src/ui/sidebar/SidebarHeader.stx +94 -1
- package/src/ui/sidebar/SidebarPinned.stx +88 -0
- package/src/ui/sidebar/SidebarSpace.stx +134 -0
- package/src/ui/sidebar/SidebarSpaceSwitcher.stx +98 -0
- package/src/ui/sidebar/SidebarSpaces.stx +560 -0
- package/src/ui/sidebar/index.ts +38 -42
- package/src/ui/sidebar/rows.ts +55 -0
- package/src/ui/sidebar/spaces.ts +260 -0
- package/src/ui/sidebar/themes.ts +108 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
import { resolveSidebarTheme } from './themes'
|
|
3
|
+
|
|
4
|
+
// One scene of a swipeable sidebar: a pinned grid, a title, the rows, and the
|
|
5
|
+
// space's own actions. Everything below the title is the ordinary Sidebar
|
|
6
|
+
// machinery — `<SidebarSection>` flattens the item tree and `<SidebarItem>`
|
|
7
|
+
// renders the rows — so a space inherits disclosure, counts, icon tints,
|
|
8
|
+
// route selection and keyboard navigation for free.
|
|
9
|
+
export const id = $props.id || ''
|
|
10
|
+
export const label = $props.label || ''
|
|
11
|
+
export const icon = $props.icon || ''
|
|
12
|
+
export const pinned = $props.pinned || []
|
|
13
|
+
export const sections = $props.sections || []
|
|
14
|
+
export const action = $props.action || null
|
|
15
|
+
export const clear = $props.clear || null
|
|
16
|
+
export const theme = $props.theme || 'arc'
|
|
17
|
+
export const active = $props.active ?? false
|
|
18
|
+
|
|
19
|
+
const themeStyle = resolveSidebarTheme(theme)
|
|
20
|
+
export const scrollAreaClasses = themeStyle.scrollArea
|
|
21
|
+
|
|
22
|
+
// The title takes the space's accent, which is how Arc signals which space you
|
|
23
|
+
// are looking at even before the panel color registers.
|
|
24
|
+
export const titleClasses = [
|
|
25
|
+
'flex items-center gap-[8px]',
|
|
26
|
+
'px-[8px] pb-[4px] pt-[10px]',
|
|
27
|
+
'text-[13px] font-semibold leading-[16px]',
|
|
28
|
+
].join(' ')
|
|
29
|
+
|
|
30
|
+
// Actions read as quieter than rows: no card on hover, just a wash.
|
|
31
|
+
export const actionClasses = [
|
|
32
|
+
'flex w-full items-center gap-[8px]',
|
|
33
|
+
'h-[30px] rounded-[8px] px-[6px]',
|
|
34
|
+
'text-[13px] leading-[16px]',
|
|
35
|
+
'opacity-55 transition-[background-color,opacity] duration-150 ease-out',
|
|
36
|
+
'hover:bg-white/45 hover:opacity-100 dark:hover:bg-white/8',
|
|
37
|
+
'cursor-default',
|
|
38
|
+
].join(' ')
|
|
39
|
+
|
|
40
|
+
export const clearClasses = [
|
|
41
|
+
'flex items-center gap-[4px] rounded-[6px] px-[5px] py-[2px]',
|
|
42
|
+
'text-[11px] leading-[14px]',
|
|
43
|
+
'opacity-45 transition-opacity duration-150',
|
|
44
|
+
'hover:opacity-90',
|
|
45
|
+
'cursor-default',
|
|
46
|
+
].join(' ')
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<script client>
|
|
50
|
+
const emit = defineEmits()
|
|
51
|
+
|
|
52
|
+
function onAction(event) {
|
|
53
|
+
const button = event.target.closest('[data-space-action]')
|
|
54
|
+
if (!button) return
|
|
55
|
+
emit('spaceAction', {
|
|
56
|
+
spaceId: button.dataset.spaceOwner,
|
|
57
|
+
actionId: button.dataset.spaceAction,
|
|
58
|
+
role: button.dataset.spaceActionRole,
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<section
|
|
64
|
+
data-space
|
|
65
|
+
data-space-id="{{ id }}"
|
|
66
|
+
data-space-active="{{ active ? 'true' : 'false' }}"
|
|
67
|
+
class="flex min-w-0 flex-1 flex-col"
|
|
68
|
+
role="tabpanel"
|
|
69
|
+
aria-label="{{ label || id }}"
|
|
70
|
+
@click="onAction($event)"
|
|
71
|
+
>
|
|
72
|
+
<div class="{{ scrollAreaClasses }} stx-sidebar-scroll">
|
|
73
|
+
<SidebarPinned :items="pinned" />
|
|
74
|
+
|
|
75
|
+
@if(label)
|
|
76
|
+
<div class="{{ titleClasses }}">
|
|
77
|
+
@if(icon)
|
|
78
|
+
<span class="{{ icon }} h-[15px] w-[15px] shrink-0 stx-space-title-icon" aria-hidden="true"></span>
|
|
79
|
+
@endif
|
|
80
|
+
<span class="truncate">{{ label }}</span>
|
|
81
|
+
</div>
|
|
82
|
+
@endif
|
|
83
|
+
|
|
84
|
+
@foreach(sections as section)
|
|
85
|
+
<SidebarSection
|
|
86
|
+
:id="section.id"
|
|
87
|
+
:label="section.label"
|
|
88
|
+
:items="section.items"
|
|
89
|
+
:collapsible="section.collapsible"
|
|
90
|
+
:collapsed="section.collapsed"
|
|
91
|
+
:theme="theme"
|
|
92
|
+
/>
|
|
93
|
+
@endforeach
|
|
94
|
+
|
|
95
|
+
@if(clear)
|
|
96
|
+
<div class="flex items-center gap-[8px] px-[8px] pt-[10px]">
|
|
97
|
+
<span class="h-px flex-1 bg-current opacity-15" aria-hidden="true"></span>
|
|
98
|
+
<button
|
|
99
|
+
type="button"
|
|
100
|
+
data-space-action="{{ clear.id }}"
|
|
101
|
+
data-space-action-role="clear"
|
|
102
|
+
data-space-owner="{{ id }}"
|
|
103
|
+
class="{{ clearClasses }}"
|
|
104
|
+
>
|
|
105
|
+
@if(clear.icon)
|
|
106
|
+
<span class="{{ clear.icon }} h-[10px] w-[10px]" aria-hidden="true"></span>
|
|
107
|
+
@endif
|
|
108
|
+
<span>{{ clear.label }}</span>
|
|
109
|
+
</button>
|
|
110
|
+
</div>
|
|
111
|
+
@endif
|
|
112
|
+
|
|
113
|
+
@if(action)
|
|
114
|
+
<button
|
|
115
|
+
type="button"
|
|
116
|
+
data-space-action="{{ action.id }}"
|
|
117
|
+
data-space-action-role="action"
|
|
118
|
+
data-space-owner="{{ id }}"
|
|
119
|
+
class="{{ actionClasses }}"
|
|
120
|
+
>
|
|
121
|
+
<span class="{{ action.icon }} h-[14px] w-[14px] shrink-0" aria-hidden="true"></span>
|
|
122
|
+
<span class="truncate">{{ action.label }}</span>
|
|
123
|
+
</button>
|
|
124
|
+
@endif
|
|
125
|
+
</div>
|
|
126
|
+
</section>
|
|
127
|
+
|
|
128
|
+
<style scoped>
|
|
129
|
+
/* The title icon rides the space accent. Published as a custom property by
|
|
130
|
+
SidebarSpaces so it crossfades along with the panel instead of cutting. */
|
|
131
|
+
.stx-space-title-icon {
|
|
132
|
+
color: var(--stx-space-accent, currentColor);
|
|
133
|
+
}
|
|
134
|
+
</style>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
// The rail along the bottom of an Arc sidebar: one small icon per space, plus
|
|
3
|
+
// an optional trailing "+". It is the discoverable counterpart to the swipe —
|
|
4
|
+
// the gesture is what you use once you know the spaces exist, the rail is how
|
|
5
|
+
// you find out.
|
|
6
|
+
//
|
|
7
|
+
// The buttons are markup only. `<SidebarSpaces>` owns the single click handler
|
|
8
|
+
// and flips `data-space-current`, so the rail never holds its own copy of
|
|
9
|
+
// which space is active — there is exactly one source of truth.
|
|
10
|
+
export const spaces = ($props.spaces || []).map(space => ({
|
|
11
|
+
id: space.id,
|
|
12
|
+
label: space.label || space.id,
|
|
13
|
+
icon: space.icon || 'i-f7-circle',
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
export const active = $props.active || (spaces[0] && spaces[0].id) || ''
|
|
17
|
+
export const showAdd = $props.showAdd ?? false
|
|
18
|
+
export const addLabel = $props.addLabel || 'New space'
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<div
|
|
22
|
+
data-space-switcher
|
|
23
|
+
class="flex h-[38px] shrink-0 items-center gap-[1px] px-[8px] pb-[6px]"
|
|
24
|
+
role="tablist"
|
|
25
|
+
aria-label="Spaces"
|
|
26
|
+
aria-orientation="horizontal"
|
|
27
|
+
>
|
|
28
|
+
@foreach(spaces as space)
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
data-space-target="{{ space.id }}"
|
|
32
|
+
data-space-current="{{ space.id === active ? 'true' : 'false' }}"
|
|
33
|
+
class="stx-space-tab"
|
|
34
|
+
role="tab"
|
|
35
|
+
aria-selected="{{ space.id === active ? 'true' : 'false' }}"
|
|
36
|
+
aria-label="{{ space.label }}"
|
|
37
|
+
title="{{ space.label }}"
|
|
38
|
+
tabindex="{{ space.id === active ? '0' : '-1' }}"
|
|
39
|
+
>
|
|
40
|
+
<span class="{{ space.icon }} h-[15px] w-[15px]" aria-hidden="true"></span>
|
|
41
|
+
</button>
|
|
42
|
+
@endforeach
|
|
43
|
+
|
|
44
|
+
<span class="flex-1"></span>
|
|
45
|
+
|
|
46
|
+
@if(showAdd)
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
data-space-add
|
|
50
|
+
class="stx-space-tab"
|
|
51
|
+
aria-label="{{ addLabel }}"
|
|
52
|
+
title="{{ addLabel }}"
|
|
53
|
+
>
|
|
54
|
+
<span class="i-f7-plus h-[14px] w-[14px]" aria-hidden="true"></span>
|
|
55
|
+
</button>
|
|
56
|
+
@endif
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<style scoped>
|
|
60
|
+
/* Rail buttons are painted here rather than with utility classes because the
|
|
61
|
+
current one has to pick up `--stx-space-accent`, which changes as you swipe.
|
|
62
|
+
A class swap would have to be re-applied on every transition; an attribute
|
|
63
|
+
rule keyed on `data-space-current` just follows along. */
|
|
64
|
+
.stx-space-tab {
|
|
65
|
+
display: grid;
|
|
66
|
+
place-items: center;
|
|
67
|
+
width: 28px;
|
|
68
|
+
height: 26px;
|
|
69
|
+
border-radius: 7px;
|
|
70
|
+
color: currentColor;
|
|
71
|
+
opacity: 0.4;
|
|
72
|
+
background: transparent;
|
|
73
|
+
transition: opacity 150ms ease, background-color 150ms ease, color 300ms ease;
|
|
74
|
+
cursor: default;
|
|
75
|
+
}
|
|
76
|
+
.stx-space-tab:hover {
|
|
77
|
+
opacity: 0.75;
|
|
78
|
+
background: rgba(255, 255, 255, 0.4);
|
|
79
|
+
}
|
|
80
|
+
.stx-space-tab[data-space-current="true"] {
|
|
81
|
+
opacity: 1;
|
|
82
|
+
color: var(--stx-space-accent, currentColor);
|
|
83
|
+
background: rgba(255, 255, 255, 0.55);
|
|
84
|
+
}
|
|
85
|
+
.stx-space-tab:focus-visible {
|
|
86
|
+
outline: 2px solid var(--stx-space-accent, currentColor);
|
|
87
|
+
outline-offset: 1px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@media (prefers-color-scheme: dark) {
|
|
91
|
+
.stx-space-tab:hover {
|
|
92
|
+
background: rgba(255, 255, 255, 0.08);
|
|
93
|
+
}
|
|
94
|
+
.stx-space-tab[data-space-current="true"] {
|
|
95
|
+
background: rgba(255, 255, 255, 0.12);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
</style>
|