@joewinke/jatui 0.1.33 → 0.2.0
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/CLAUDE.md +22 -0
- package/dist/actions/attention.d.ts +24 -0
- package/dist/actions/attention.js +94 -0
- package/dist/actions/floating.d.ts +53 -0
- package/dist/actions/floating.js +158 -0
- package/dist/actions/staggerEntrance.d.ts +28 -0
- package/dist/actions/staggerEntrance.js +71 -0
- package/dist/actions/tooltip.d.ts +28 -0
- package/dist/actions/tooltip.js +186 -0
- package/dist/assets/sounds/README.md +47 -0
- package/dist/assets/sounds/error.d.ts +1 -0
- package/dist/assets/sounds/error.js +4 -0
- package/dist/assets/sounds/notify.d.ts +1 -0
- package/dist/assets/sounds/notify.js +4 -0
- package/dist/assets/sounds/pop.d.ts +1 -0
- package/dist/assets/sounds/pop.js +4 -0
- package/dist/assets/sounds/send.d.ts +1 -0
- package/dist/assets/sounds/send.js +4 -0
- package/dist/assets/sounds/success.d.ts +1 -0
- package/dist/assets/sounds/success.js +4 -0
- package/dist/assets/sounds/toggle.d.ts +1 -0
- package/dist/assets/sounds/toggle.js +4 -0
- package/dist/components/AnimatedNumber.svelte +112 -0
- package/dist/components/AnimatedNumber.svelte.d.ts +8 -0
- package/dist/components/Badge.svelte +75 -4
- package/dist/components/Badge.svelte.d.ts +6 -1
- package/dist/components/Button.svelte +196 -21
- package/dist/components/Button.svelte.d.ts +10 -1
- package/dist/components/Carousel.svelte +265 -0
- package/dist/components/Carousel.svelte.d.ts +21 -0
- package/dist/components/ChipInput.svelte +63 -3
- package/dist/components/Collapse.svelte +70 -14
- package/dist/components/Collapse.svelte.d.ts +11 -0
- package/dist/components/ColorSelector.svelte +21 -24
- package/dist/components/CommandPalette.svelte +475 -0
- package/dist/components/CommandPalette.svelte.d.ts +3 -0
- package/dist/components/ContextMenu.svelte +9 -73
- package/dist/components/CopyButton.svelte +156 -0
- package/dist/components/CopyButton.svelte.d.ts +12 -0
- package/dist/components/HoldToConfirm.svelte +183 -0
- package/dist/components/HoldToConfirm.svelte.d.ts +11 -0
- package/dist/components/KeyboardShortcutsOverlay.svelte +54 -8
- package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +3 -0
- package/dist/components/PasswordMeter.svelte +87 -0
- package/dist/components/PasswordMeter.svelte.d.ts +8 -0
- package/dist/components/PinInput.svelte +186 -0
- package/dist/components/PinInput.svelte.d.ts +13 -0
- package/dist/components/QuantityStepper.svelte +185 -0
- package/dist/components/QuantityStepper.svelte.d.ts +12 -0
- package/dist/components/Skeleton.svelte +123 -0
- package/dist/components/Skeleton.svelte.d.ts +12 -0
- package/dist/components/SpeechForm.svelte +37 -0
- package/dist/components/StatusBadge.svelte +31 -3
- package/dist/components/StatusBadge.svelte.d.ts +0 -5
- package/dist/components/Tabs.svelte +270 -0
- package/dist/components/Tabs.svelte.d.ts +15 -0
- package/dist/components/TextInput.svelte +138 -38
- package/dist/components/TextInput.svelte.d.ts +9 -1
- package/dist/components/VoicePlayer.svelte +51 -0
- package/dist/components/basic/Alert.svelte +36 -0
- package/dist/components/basic/Alert.svelte.d.ts +9 -0
- package/dist/components/basic/Breadcrumb.svelte +27 -0
- package/dist/components/basic/Breadcrumb.svelte.d.ts +11 -0
- package/dist/components/basic/ButtonGroup.svelte +15 -0
- package/dist/components/basic/ButtonGroup.svelte.d.ts +8 -0
- package/dist/components/basic/Checkbox.svelte +85 -0
- package/dist/components/basic/Checkbox.svelte.d.ts +15 -0
- package/dist/components/basic/Empty.svelte +17 -0
- package/dist/components/basic/Empty.svelte.d.ts +8 -0
- package/dist/components/basic/Kbd.svelte +16 -0
- package/dist/components/basic/Kbd.svelte.d.ts +8 -0
- package/dist/components/basic/Pagination.svelte +104 -0
- package/dist/components/basic/Pagination.svelte.d.ts +12 -0
- package/dist/components/basic/RadioGroup.svelte +71 -0
- package/dist/components/basic/RadioGroup.svelte.d.ts +18 -0
- package/dist/components/basic/Separator.svelte +19 -0
- package/dist/components/basic/Separator.svelte.d.ts +8 -0
- package/dist/components/basic/Slider.svelte +72 -0
- package/dist/components/basic/Slider.svelte.d.ts +17 -0
- package/dist/components/basic/Switch.svelte +88 -0
- package/dist/components/basic/Switch.svelte.d.ts +14 -0
- package/dist/components/basic/Toggle.svelte +43 -0
- package/dist/components/basic/Toggle.svelte.d.ts +14 -0
- package/dist/components/graph/GraphCanvas.svelte +103 -0
- package/dist/components/graph/GraphCanvas.svelte.d.ts +22 -0
- package/dist/components/graph/JatuiNode.svelte +128 -0
- package/dist/components/graph/JatuiNode.svelte.d.ts +14 -0
- package/dist/components/graph/theme.d.ts +27 -0
- package/dist/components/graph/theme.js +136 -0
- package/dist/components/messaging/MessageItem.svelte +92 -0
- package/dist/components/messaging/MessageItem.svelte.d.ts +5 -0
- package/dist/components/messaging/TypingIndicator.svelte +63 -0
- package/dist/components/messaging/TypingIndicator.svelte.d.ts +7 -0
- package/dist/components/pipeline/PipelineColumn.svelte +44 -3
- package/dist/components/progress/ProgressBar.svelte +189 -0
- package/dist/components/progress/ProgressBar.svelte.d.ts +17 -0
- package/dist/components/progress/ProgressRing.svelte +138 -0
- package/dist/components/progress/ProgressRing.svelte.d.ts +12 -0
- package/dist/components/progress/Steps.svelte +195 -0
- package/dist/components/progress/Steps.svelte.d.ts +14 -0
- package/dist/components/shell/NavigationMenu.svelte +422 -0
- package/dist/components/shell/NavigationMenu.svelte.d.ts +43 -0
- package/dist/components/shell/Sidebar.svelte +276 -0
- package/dist/components/shell/Sidebar.svelte.d.ts +32 -0
- package/dist/components/shell/SidebarGroup.svelte +180 -0
- package/dist/components/shell/SidebarGroup.svelte.d.ts +19 -0
- package/dist/components/shell/SidebarItem.svelte +170 -0
- package/dist/components/shell/SidebarItem.svelte.d.ts +24 -0
- package/dist/components/shell/shell.d.ts +30 -0
- package/dist/components/shell/shell.js +65 -0
- package/dist/components/table/DataTable.svelte +364 -0
- package/dist/components/table/DataTable.svelte.d.ts +4 -0
- package/dist/components/table/flexRender.d.ts +31 -0
- package/dist/components/table/flexRender.js +32 -0
- package/dist/components/table/types.d.ts +28 -0
- package/dist/components/table/types.js +1 -0
- package/dist/components/toast/ToastHost.svelte +21 -0
- package/dist/components/toast/ToastHost.svelte.d.ts +3 -0
- package/dist/components/toast/ToastItem.svelte +287 -0
- package/dist/components/toast/ToastItem.svelte.d.ts +9 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +67 -0
- package/dist/showcase/AnimatedBeam.svelte +183 -0
- package/dist/showcase/AnimatedBeam.svelte.d.ts +26 -0
- package/dist/showcase/AuroraDrift.svelte +74 -0
- package/dist/showcase/AuroraDrift.svelte.d.ts +9 -0
- package/dist/showcase/BentoCard.svelte +50 -0
- package/dist/showcase/BentoCard.svelte.d.ts +14 -0
- package/dist/showcase/BentoGrid.svelte +43 -0
- package/dist/showcase/BentoGrid.svelte.d.ts +12 -0
- package/dist/showcase/BorderBeam.svelte +79 -0
- package/dist/showcase/BorderBeam.svelte.d.ts +12 -0
- package/dist/showcase/ClipWipe.svelte +82 -0
- package/dist/showcase/ClipWipe.svelte.d.ts +12 -0
- package/dist/showcase/Confetti.svelte +71 -0
- package/dist/showcase/Confetti.svelte.d.ts +10 -0
- package/dist/showcase/CursorSpotlight.svelte +85 -0
- package/dist/showcase/CursorSpotlight.svelte.d.ts +9 -0
- package/dist/showcase/DeviceMock.svelte +186 -0
- package/dist/showcase/DeviceMock.svelte.d.ts +11 -0
- package/dist/showcase/Dock.svelte +123 -0
- package/dist/showcase/Dock.svelte.d.ts +17 -0
- package/dist/showcase/FlipCard.svelte +103 -0
- package/dist/showcase/FlipCard.svelte.d.ts +10 -0
- package/dist/showcase/FocusReveal.svelte +83 -0
- package/dist/showcase/FocusReveal.svelte.d.ts +11 -0
- package/dist/showcase/Globe.svelte +202 -0
- package/dist/showcase/Globe.svelte.d.ts +16 -0
- package/dist/showcase/KenBurns.svelte +105 -0
- package/dist/showcase/KenBurns.svelte.d.ts +13 -0
- package/dist/showcase/Marquee.svelte +89 -0
- package/dist/showcase/Marquee.svelte.d.ts +9 -0
- package/dist/showcase/ParallaxTilt.svelte +98 -0
- package/dist/showcase/ParallaxTilt.svelte.d.ts +10 -0
- package/dist/showcase/ScrambleText.svelte +98 -0
- package/dist/showcase/ScrambleText.svelte.d.ts +11 -0
- package/dist/showcase/ShineSweep.svelte +81 -0
- package/dist/showcase/ShineSweep.svelte.d.ts +9 -0
- package/dist/showcase/Terminal.svelte +230 -0
- package/dist/showcase/Terminal.svelte.d.ts +18 -0
- package/dist/showcase/TextSplitReveal.svelte +146 -0
- package/dist/showcase/TextSplitReveal.svelte.d.ts +13 -0
- package/dist/showcase/TextWave.svelte +55 -0
- package/dist/showcase/TextWave.svelte.d.ts +8 -0
- package/dist/showcase/TypewriterText.svelte +130 -0
- package/dist/showcase/TypewriterText.svelte.d.ts +9 -0
- package/dist/showcase/backdrop/GrainGradient.svelte +126 -0
- package/dist/showcase/backdrop/GrainGradient.svelte.d.ts +23 -0
- package/dist/showcase/backdrop/MeshGradient.svelte +104 -0
- package/dist/showcase/backdrop/MeshGradient.svelte.d.ts +20 -0
- package/dist/showcase/backdrop/Metaballs.svelte +116 -0
- package/dist/showcase/backdrop/Metaballs.svelte.d.ts +18 -0
- package/dist/showcase/backdrop/NeuroNoise.svelte +115 -0
- package/dist/showcase/backdrop/NeuroNoise.svelte.d.ts +20 -0
- package/dist/showcase/backdrop/ShowcaseBackdrop.svelte +48 -0
- package/dist/showcase/backdrop/ShowcaseBackdrop.svelte.d.ts +12 -0
- package/dist/showcase/generative/GenerativeBackdrop.svelte +234 -0
- package/dist/showcase/generative/GenerativeBackdrop.svelte.d.ts +9 -0
- package/dist/showcase/generative/intent.d.ts +7 -0
- package/dist/showcase/generative/intent.js +67 -0
- package/dist/showcase/generative/manifest.d.ts +9 -0
- package/dist/showcase/generative/manifest.js +97 -0
- package/dist/showcase/generative/types.d.ts +48 -0
- package/dist/showcase/generative/types.js +8 -0
- package/dist/showcase/index.d.ts +36 -0
- package/dist/showcase/index.js +46 -0
- package/dist/showcase/scroll/SmoothScroll.svelte +49 -0
- package/dist/showcase/scroll/SmoothScroll.svelte.d.ts +12 -0
- package/dist/showcase/scroll/scrollProgress.d.ts +24 -0
- package/dist/showcase/scroll/scrollProgress.js +65 -0
- package/dist/showcase/scroll/scrollReveal.d.ts +30 -0
- package/dist/showcase/scroll/scrollReveal.js +70 -0
- package/dist/stores/commandPalette.svelte.d.ts +24 -0
- package/dist/stores/commandPalette.svelte.js +82 -0
- package/dist/stores/toast.svelte.d.ts +28 -0
- package/dist/stores/toast.svelte.js +33 -0
- package/dist/styles/motion.css +46 -0
- package/dist/styles/tokens.css +31 -0
- package/dist/transitions/glide.d.ts +31 -0
- package/dist/transitions/glide.js +58 -0
- package/dist/utils/sound.d.ts +6 -0
- package/dist/utils/sound.js +113 -0
- package/dist/utils/splitText.d.ts +25 -0
- package/dist/utils/splitText.js +94 -0
- package/dist/utils/themeWipe.d.ts +21 -0
- package/dist/utils/themeWipe.js +94 -0
- package/dist/utils/viewTransition.d.ts +23 -0
- package/dist/utils/viewTransition.js +75 -0
- package/package.json +50 -3
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Sidebar — app-shell navigation panel.
|
|
4
|
+
*
|
|
5
|
+
* Modes:
|
|
6
|
+
* expanded — full labels visible
|
|
7
|
+
* icon — icon-only rail (no width transition — labels/groups hide via class swap)
|
|
8
|
+
* collapsed — completely hidden; mobile uses a Drawer overlay
|
|
9
|
+
*
|
|
10
|
+
* Pass a sidebarState from createSidebarState() (shell.ts) for persistence
|
|
11
|
+
* and active-item tracking. Without it, the sidebar is stateless.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { Snippet } from 'svelte';
|
|
15
|
+
import type { SidebarState } from './shell.js';
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
/** Sidebar context from createSidebarState() */
|
|
19
|
+
sidebarState?: SidebarState | null;
|
|
20
|
+
/** Fixed width in expanded mode (CSS value) */
|
|
21
|
+
width?: string;
|
|
22
|
+
/** Width in icon-only mode */
|
|
23
|
+
iconWidth?: string;
|
|
24
|
+
/** Show the collapse toggle button */
|
|
25
|
+
showToggle?: boolean;
|
|
26
|
+
/** Sidebar header content (e.g. logo) */
|
|
27
|
+
header?: Snippet;
|
|
28
|
+
/** Sidebar footer content (e.g. user info) */
|
|
29
|
+
footer?: Snippet;
|
|
30
|
+
children?: Snippet;
|
|
31
|
+
class?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let {
|
|
35
|
+
sidebarState = null,
|
|
36
|
+
width = '240px',
|
|
37
|
+
iconWidth = '56px',
|
|
38
|
+
showToggle = true,
|
|
39
|
+
header,
|
|
40
|
+
footer,
|
|
41
|
+
children,
|
|
42
|
+
class: className = ''
|
|
43
|
+
}: Props = $props();
|
|
44
|
+
|
|
45
|
+
const mode = $derived(sidebarState?.mode ?? 'expanded');
|
|
46
|
+
const mobileOpen = $derived(sidebarState?.mobileOpen ?? false);
|
|
47
|
+
|
|
48
|
+
function closeMobile() {
|
|
49
|
+
sidebarState?.setMobileOpen(false);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function handleOverlayKeydown(e: KeyboardEvent) {
|
|
53
|
+
if (e.key === 'Escape' || e.key === 'Enter' || e.key === ' ') {
|
|
54
|
+
closeMobile();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function handleSidebarKeydown(e: KeyboardEvent) {
|
|
59
|
+
if (e.key === 'Escape' && mobileOpen) {
|
|
60
|
+
closeMobile();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<!-- Mobile backdrop -->
|
|
66
|
+
<div
|
|
67
|
+
class="jatui-sidebar-backdrop"
|
|
68
|
+
class:visible={mobileOpen}
|
|
69
|
+
onclick={closeMobile}
|
|
70
|
+
onkeydown={handleOverlayKeydown}
|
|
71
|
+
role="presentation"
|
|
72
|
+
aria-hidden="true"
|
|
73
|
+
></div>
|
|
74
|
+
|
|
75
|
+
<!-- Sidebar panel -->
|
|
76
|
+
<aside
|
|
77
|
+
class="jatui-sidebar {className}"
|
|
78
|
+
class:mode-expanded={mode === 'expanded'}
|
|
79
|
+
class:mode-icon={mode === 'icon'}
|
|
80
|
+
class:mode-collapsed={mode === 'collapsed'}
|
|
81
|
+
class:mobile-open={mobileOpen}
|
|
82
|
+
style="--sidebar-width: {width}; --sidebar-icon-width: {iconWidth};"
|
|
83
|
+
aria-label="Main navigation"
|
|
84
|
+
onkeydown={handleSidebarKeydown}
|
|
85
|
+
>
|
|
86
|
+
<!-- Header slot -->
|
|
87
|
+
{#if header}
|
|
88
|
+
<div class="jatui-sidebar-header">
|
|
89
|
+
{@render header()}
|
|
90
|
+
</div>
|
|
91
|
+
{/if}
|
|
92
|
+
|
|
93
|
+
<!-- Toggle button -->
|
|
94
|
+
{#if showToggle && sidebarState}
|
|
95
|
+
<button
|
|
96
|
+
type="button"
|
|
97
|
+
class="jatui-sidebar-toggle"
|
|
98
|
+
onclick={() => sidebarState!.toggleCollapse()}
|
|
99
|
+
aria-label={mode === 'icon' ? 'Expand sidebar' : 'Collapse sidebar'}
|
|
100
|
+
title={mode === 'icon' ? 'Expand sidebar' : 'Collapse sidebar'}
|
|
101
|
+
>
|
|
102
|
+
<svg
|
|
103
|
+
class="jatui-sidebar-toggle-icon"
|
|
104
|
+
class:flipped={mode === 'icon'}
|
|
105
|
+
width="16"
|
|
106
|
+
height="16"
|
|
107
|
+
viewBox="0 0 16 16"
|
|
108
|
+
fill="none"
|
|
109
|
+
aria-hidden="true"
|
|
110
|
+
>
|
|
111
|
+
<path
|
|
112
|
+
d="M10 3L5 8L10 13"
|
|
113
|
+
stroke="currentColor"
|
|
114
|
+
stroke-width="1.5"
|
|
115
|
+
stroke-linecap="round"
|
|
116
|
+
stroke-linejoin="round"
|
|
117
|
+
/>
|
|
118
|
+
</svg>
|
|
119
|
+
</button>
|
|
120
|
+
{/if}
|
|
121
|
+
|
|
122
|
+
<!-- Nav items -->
|
|
123
|
+
<nav class="jatui-sidebar-nav" aria-label="Navigation">
|
|
124
|
+
<ul class="jatui-sidebar-list">
|
|
125
|
+
{#if children}
|
|
126
|
+
{@render children()}
|
|
127
|
+
{/if}
|
|
128
|
+
</ul>
|
|
129
|
+
</nav>
|
|
130
|
+
|
|
131
|
+
<!-- Footer slot -->
|
|
132
|
+
{#if footer}
|
|
133
|
+
<div class="jatui-sidebar-footer">
|
|
134
|
+
{@render footer()}
|
|
135
|
+
</div>
|
|
136
|
+
{/if}
|
|
137
|
+
</aside>
|
|
138
|
+
|
|
139
|
+
<style>
|
|
140
|
+
/* Backdrop for mobile overlay */
|
|
141
|
+
.jatui-sidebar-backdrop {
|
|
142
|
+
display: none;
|
|
143
|
+
position: fixed;
|
|
144
|
+
inset: 0;
|
|
145
|
+
z-index: 39;
|
|
146
|
+
background: color-mix(in oklch, var(--color-base-300) 60%, transparent);
|
|
147
|
+
backdrop-filter: blur(2px);
|
|
148
|
+
opacity: 0;
|
|
149
|
+
transition: opacity var(--motion-enter) var(--motion-glide);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@media (max-width: 767px) {
|
|
153
|
+
.jatui-sidebar-backdrop.visible {
|
|
154
|
+
display: block;
|
|
155
|
+
opacity: 1;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Sidebar panel */
|
|
160
|
+
.jatui-sidebar {
|
|
161
|
+
display: flex;
|
|
162
|
+
flex-direction: column;
|
|
163
|
+
height: 100%;
|
|
164
|
+
background: var(--color-base-200);
|
|
165
|
+
border-right: var(--jatui-border-w) solid var(--color-base-300);
|
|
166
|
+
overflow: hidden;
|
|
167
|
+
flex-shrink: 0;
|
|
168
|
+
|
|
169
|
+
/* Width driven by mode class — no animated width transitions */
|
|
170
|
+
width: var(--sidebar-width);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Icon mode — swap to narrow width without transition */
|
|
174
|
+
.jatui-sidebar.mode-icon {
|
|
175
|
+
width: var(--sidebar-icon-width);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Collapsed — hidden on desktop */
|
|
179
|
+
.jatui-sidebar.mode-collapsed {
|
|
180
|
+
width: 0;
|
|
181
|
+
border-right-width: 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Mobile: always hidden until mobile-open, slides in as overlay */
|
|
185
|
+
@media (max-width: 767px) {
|
|
186
|
+
.jatui-sidebar {
|
|
187
|
+
position: fixed;
|
|
188
|
+
top: 0;
|
|
189
|
+
left: 0;
|
|
190
|
+
height: 100dvh;
|
|
191
|
+
z-index: 40;
|
|
192
|
+
width: var(--sidebar-width) !important;
|
|
193
|
+
transform: translateX(-100%);
|
|
194
|
+
transition: transform var(--motion-enter) var(--motion-glide);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.jatui-sidebar.mobile-open {
|
|
198
|
+
transform: translateX(0);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* Header */
|
|
203
|
+
.jatui-sidebar-header {
|
|
204
|
+
padding: var(--jatui-space-xl);
|
|
205
|
+
border-bottom: var(--jatui-border-w) solid var(--color-base-300);
|
|
206
|
+
flex-shrink: 0;
|
|
207
|
+
overflow: hidden;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Toggle button */
|
|
211
|
+
.jatui-sidebar-toggle {
|
|
212
|
+
display: flex;
|
|
213
|
+
align-items: center;
|
|
214
|
+
justify-content: center;
|
|
215
|
+
width: 100%;
|
|
216
|
+
padding: var(--jatui-space-base) var(--jatui-space-xl);
|
|
217
|
+
background: transparent;
|
|
218
|
+
border: none;
|
|
219
|
+
cursor: pointer;
|
|
220
|
+
color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
|
|
221
|
+
flex-shrink: 0;
|
|
222
|
+
transition: color var(--motion-instant) var(--motion-glide);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.jatui-sidebar-toggle:hover {
|
|
226
|
+
color: var(--color-base-content);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.jatui-sidebar-toggle-icon {
|
|
230
|
+
transition: transform var(--motion-fast) var(--motion-glide);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.jatui-sidebar-toggle-icon.flipped {
|
|
234
|
+
transform: rotate(180deg);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* Nav */
|
|
238
|
+
.jatui-sidebar-nav {
|
|
239
|
+
flex: 1;
|
|
240
|
+
overflow-y: auto;
|
|
241
|
+
overflow-x: hidden;
|
|
242
|
+
padding: var(--jatui-space-base) var(--jatui-space-base);
|
|
243
|
+
scrollbar-width: thin;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.jatui-sidebar-list {
|
|
247
|
+
list-style: none;
|
|
248
|
+
padding: 0;
|
|
249
|
+
margin: 0;
|
|
250
|
+
display: flex;
|
|
251
|
+
flex-direction: column;
|
|
252
|
+
gap: var(--jatui-space-xs);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Footer */
|
|
256
|
+
.jatui-sidebar-footer {
|
|
257
|
+
padding: var(--jatui-space-xl);
|
|
258
|
+
border-top: var(--jatui-border-w) solid var(--color-base-300);
|
|
259
|
+
flex-shrink: 0;
|
|
260
|
+
overflow: hidden;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
@media (prefers-reduced-motion: reduce) {
|
|
264
|
+
.jatui-sidebar-backdrop {
|
|
265
|
+
transition: none;
|
|
266
|
+
}
|
|
267
|
+
.jatui-sidebar-toggle-icon {
|
|
268
|
+
transition: none;
|
|
269
|
+
}
|
|
270
|
+
@media (max-width: 767px) {
|
|
271
|
+
.jatui-sidebar {
|
|
272
|
+
transition: none;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
</style>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar — app-shell navigation panel.
|
|
3
|
+
*
|
|
4
|
+
* Modes:
|
|
5
|
+
* expanded — full labels visible
|
|
6
|
+
* icon — icon-only rail (no width transition — labels/groups hide via class swap)
|
|
7
|
+
* collapsed — completely hidden; mobile uses a Drawer overlay
|
|
8
|
+
*
|
|
9
|
+
* Pass a sidebarState from createSidebarState() (shell.ts) for persistence
|
|
10
|
+
* and active-item tracking. Without it, the sidebar is stateless.
|
|
11
|
+
*/
|
|
12
|
+
import type { Snippet } from 'svelte';
|
|
13
|
+
import type { SidebarState } from './shell.js';
|
|
14
|
+
interface Props {
|
|
15
|
+
/** Sidebar context from createSidebarState() */
|
|
16
|
+
sidebarState?: SidebarState | null;
|
|
17
|
+
/** Fixed width in expanded mode (CSS value) */
|
|
18
|
+
width?: string;
|
|
19
|
+
/** Width in icon-only mode */
|
|
20
|
+
iconWidth?: string;
|
|
21
|
+
/** Show the collapse toggle button */
|
|
22
|
+
showToggle?: boolean;
|
|
23
|
+
/** Sidebar header content (e.g. logo) */
|
|
24
|
+
header?: Snippet;
|
|
25
|
+
/** Sidebar footer content (e.g. user info) */
|
|
26
|
+
footer?: Snippet;
|
|
27
|
+
children?: Snippet;
|
|
28
|
+
class?: string;
|
|
29
|
+
}
|
|
30
|
+
declare const Sidebar: import("svelte").Component<Props, {}, "">;
|
|
31
|
+
type Sidebar = ReturnType<typeof Sidebar>;
|
|
32
|
+
export default Sidebar;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { SidebarState } from './shell.js';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Group heading label */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** Whether this group is collapsible */
|
|
9
|
+
collapsible?: boolean;
|
|
10
|
+
/** Initial expanded state for collapsible groups */
|
|
11
|
+
expanded?: boolean;
|
|
12
|
+
/** Icon in the group header */
|
|
13
|
+
icon?: Snippet;
|
|
14
|
+
/** Sidebar context state (passed down from Sidebar) */
|
|
15
|
+
sidebarState?: SidebarState | null;
|
|
16
|
+
children?: Snippet;
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
label,
|
|
22
|
+
collapsible = false,
|
|
23
|
+
expanded = $bindable(true),
|
|
24
|
+
icon,
|
|
25
|
+
sidebarState = null,
|
|
26
|
+
children,
|
|
27
|
+
class: className = ''
|
|
28
|
+
}: Props = $props();
|
|
29
|
+
|
|
30
|
+
const iconOnly = $derived(sidebarState?.mode === 'icon');
|
|
31
|
+
|
|
32
|
+
function toggleGroup() {
|
|
33
|
+
if (collapsible) expanded = !expanded;
|
|
34
|
+
}
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<li class="jatui-sidebar-group {className}" class:icon-only={iconOnly}>
|
|
38
|
+
{#if label && !iconOnly}
|
|
39
|
+
{#if collapsible}
|
|
40
|
+
<button
|
|
41
|
+
type="button"
|
|
42
|
+
class="jatui-sidebar-group-header collapsible"
|
|
43
|
+
onclick={toggleGroup}
|
|
44
|
+
aria-expanded={expanded}
|
|
45
|
+
>
|
|
46
|
+
{#if icon}
|
|
47
|
+
<span class="jatui-sidebar-group-icon" aria-hidden="true">
|
|
48
|
+
{@render icon()}
|
|
49
|
+
</span>
|
|
50
|
+
{/if}
|
|
51
|
+
<span class="jatui-sidebar-group-label">{label}</span>
|
|
52
|
+
<svg
|
|
53
|
+
class="jatui-sidebar-group-chevron"
|
|
54
|
+
class:rotated={!expanded}
|
|
55
|
+
width="12"
|
|
56
|
+
height="12"
|
|
57
|
+
viewBox="0 0 12 12"
|
|
58
|
+
fill="none"
|
|
59
|
+
aria-hidden="true"
|
|
60
|
+
>
|
|
61
|
+
<path
|
|
62
|
+
d="M2 4L6 8L10 4"
|
|
63
|
+
stroke="currentColor"
|
|
64
|
+
stroke-width="1.5"
|
|
65
|
+
stroke-linecap="round"
|
|
66
|
+
stroke-linejoin="round"
|
|
67
|
+
/>
|
|
68
|
+
</svg>
|
|
69
|
+
</button>
|
|
70
|
+
{:else}
|
|
71
|
+
<span class="jatui-sidebar-group-header">
|
|
72
|
+
{#if icon}
|
|
73
|
+
<span class="jatui-sidebar-group-icon" aria-hidden="true">
|
|
74
|
+
{@render icon()}
|
|
75
|
+
</span>
|
|
76
|
+
{/if}
|
|
77
|
+
<span class="jatui-sidebar-group-label">{label}</span>
|
|
78
|
+
</span>
|
|
79
|
+
{/if}
|
|
80
|
+
{/if}
|
|
81
|
+
|
|
82
|
+
<!-- Items wrapper: collapses via grid-template-rows -->
|
|
83
|
+
<div
|
|
84
|
+
class="jatui-sidebar-group-items"
|
|
85
|
+
class:collapsed={collapsible && !expanded}
|
|
86
|
+
>
|
|
87
|
+
<ul class="jatui-sidebar-group-list">
|
|
88
|
+
{#if children}
|
|
89
|
+
{@render children()}
|
|
90
|
+
{/if}
|
|
91
|
+
</ul>
|
|
92
|
+
</div>
|
|
93
|
+
</li>
|
|
94
|
+
|
|
95
|
+
<style>
|
|
96
|
+
.jatui-sidebar-group {
|
|
97
|
+
list-style: none;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.jatui-sidebar-group-header {
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
gap: var(--jatui-space-base);
|
|
104
|
+
padding: var(--jatui-space-sm) var(--jatui-space-xl);
|
|
105
|
+
margin-top: var(--jatui-space-lg);
|
|
106
|
+
font-size: 0.6875rem;
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
letter-spacing: 0.08em;
|
|
109
|
+
text-transform: uppercase;
|
|
110
|
+
color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
|
|
111
|
+
width: 100%;
|
|
112
|
+
border: none;
|
|
113
|
+
background: transparent;
|
|
114
|
+
text-align: left;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
button.jatui-sidebar-group-header {
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
border-radius: var(--jatui-radius-selector);
|
|
120
|
+
transition: background-color var(--motion-instant) var(--motion-glide);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
button.jatui-sidebar-group-header:hover {
|
|
124
|
+
background: color-mix(in oklch, var(--color-base-content) 6%, transparent);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.jatui-sidebar-group-icon {
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
flex-shrink: 0;
|
|
131
|
+
width: 1rem;
|
|
132
|
+
height: 1rem;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.jatui-sidebar-group-label {
|
|
136
|
+
flex: 1;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.jatui-sidebar-group-chevron {
|
|
140
|
+
flex-shrink: 0;
|
|
141
|
+
transition: transform var(--motion-fast) var(--motion-glide);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.jatui-sidebar-group-chevron.rotated {
|
|
145
|
+
transform: rotate(-90deg);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Collapse via grid-template-rows — no height/overflow animation */
|
|
149
|
+
.jatui-sidebar-group-items {
|
|
150
|
+
display: grid;
|
|
151
|
+
grid-template-rows: 1fr;
|
|
152
|
+
transition: grid-template-rows var(--motion-enter) var(--motion-glide);
|
|
153
|
+
overflow: hidden;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.jatui-sidebar-group-items.collapsed {
|
|
157
|
+
grid-template-rows: 0fr;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.jatui-sidebar-group-list {
|
|
161
|
+
list-style: none;
|
|
162
|
+
padding: 0;
|
|
163
|
+
margin: 0;
|
|
164
|
+
min-height: 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* In icon-only mode, remove group label spacing */
|
|
168
|
+
.jatui-sidebar-group.icon-only .jatui-sidebar-group-header {
|
|
169
|
+
display: none;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@media (prefers-reduced-motion: reduce) {
|
|
173
|
+
.jatui-sidebar-group-chevron {
|
|
174
|
+
transition: none;
|
|
175
|
+
}
|
|
176
|
+
.jatui-sidebar-group-items {
|
|
177
|
+
transition: none;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { SidebarState } from './shell.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Group heading label */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** Whether this group is collapsible */
|
|
7
|
+
collapsible?: boolean;
|
|
8
|
+
/** Initial expanded state for collapsible groups */
|
|
9
|
+
expanded?: boolean;
|
|
10
|
+
/** Icon in the group header */
|
|
11
|
+
icon?: Snippet;
|
|
12
|
+
/** Sidebar context state (passed down from Sidebar) */
|
|
13
|
+
sidebarState?: SidebarState | null;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
declare const SidebarGroup: import("svelte").Component<Props, {}, "expanded">;
|
|
18
|
+
type SidebarGroup = ReturnType<typeof SidebarGroup>;
|
|
19
|
+
export default SidebarGroup;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { SidebarState } from './shell.js';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Unique ID used to track the active item */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Display label */
|
|
9
|
+
label: string;
|
|
10
|
+
/** Optional icon slot */
|
|
11
|
+
icon?: Snippet;
|
|
12
|
+
/** Href for anchor-based nav. If omitted, renders as a button */
|
|
13
|
+
href?: string;
|
|
14
|
+
/** Badge content (count or dot) */
|
|
15
|
+
badge?: number | string;
|
|
16
|
+
/** Disabled state */
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/** Sidebar context state */
|
|
19
|
+
sidebarState?: SidebarState | null;
|
|
20
|
+
/** Called when item is activated */
|
|
21
|
+
onclick?: (id: string) => void;
|
|
22
|
+
class?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
id,
|
|
27
|
+
label,
|
|
28
|
+
icon,
|
|
29
|
+
href,
|
|
30
|
+
badge,
|
|
31
|
+
disabled = false,
|
|
32
|
+
sidebarState = null,
|
|
33
|
+
onclick,
|
|
34
|
+
class: className = ''
|
|
35
|
+
}: Props = $props();
|
|
36
|
+
|
|
37
|
+
const isActive = $derived(sidebarState?.activeId === id);
|
|
38
|
+
const iconOnly = $derived(sidebarState?.mode === 'icon');
|
|
39
|
+
|
|
40
|
+
function handleClick(e: MouseEvent) {
|
|
41
|
+
if (disabled) {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
sidebarState?.setActive(id);
|
|
46
|
+
onclick?.(id);
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
{#snippet itemContent()}
|
|
51
|
+
{#if icon}
|
|
52
|
+
<span class="jatui-sidebar-item-icon" aria-hidden="true">
|
|
53
|
+
{@render icon()}
|
|
54
|
+
</span>
|
|
55
|
+
{/if}
|
|
56
|
+
{#if !iconOnly}
|
|
57
|
+
<span class="jatui-sidebar-item-label">{label}</span>
|
|
58
|
+
{#if badge !== undefined && badge !== null}
|
|
59
|
+
<span class="badge badge-sm ml-auto" class:badge-primary={isActive}>
|
|
60
|
+
{badge}
|
|
61
|
+
</span>
|
|
62
|
+
{/if}
|
|
63
|
+
{:else if badge !== undefined && badge !== null}
|
|
64
|
+
<span
|
|
65
|
+
class="badge badge-xs badge-primary absolute -top-0.5 -right-0.5"
|
|
66
|
+
aria-label="{badge} notifications"
|
|
67
|
+
></span>
|
|
68
|
+
{/if}
|
|
69
|
+
{/snippet}
|
|
70
|
+
|
|
71
|
+
{#if href}
|
|
72
|
+
<li class={className}>
|
|
73
|
+
<a
|
|
74
|
+
{href}
|
|
75
|
+
class="jatui-sidebar-item"
|
|
76
|
+
class:active={isActive}
|
|
77
|
+
class:disabled
|
|
78
|
+
class:icon-only={iconOnly}
|
|
79
|
+
aria-current={isActive ? 'page' : undefined}
|
|
80
|
+
aria-disabled={disabled || undefined}
|
|
81
|
+
aria-label={iconOnly ? label : undefined}
|
|
82
|
+
onclick={handleClick}
|
|
83
|
+
title={iconOnly ? label : undefined}
|
|
84
|
+
>
|
|
85
|
+
{@render itemContent()}
|
|
86
|
+
</a>
|
|
87
|
+
</li>
|
|
88
|
+
{:else}
|
|
89
|
+
<li class={className}>
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
class="jatui-sidebar-item"
|
|
93
|
+
class:active={isActive}
|
|
94
|
+
class:disabled
|
|
95
|
+
class:icon-only={iconOnly}
|
|
96
|
+
{disabled}
|
|
97
|
+
aria-current={isActive ? 'page' : undefined}
|
|
98
|
+
aria-label={iconOnly ? label : undefined}
|
|
99
|
+
onclick={handleClick}
|
|
100
|
+
title={iconOnly ? label : undefined}
|
|
101
|
+
>
|
|
102
|
+
{@render itemContent()}
|
|
103
|
+
</button>
|
|
104
|
+
</li>
|
|
105
|
+
{/if}
|
|
106
|
+
|
|
107
|
+
<style>
|
|
108
|
+
.jatui-sidebar-item {
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
gap: var(--jatui-space-lg);
|
|
112
|
+
width: 100%;
|
|
113
|
+
padding: var(--jatui-space-base) var(--jatui-space-xl);
|
|
114
|
+
border-radius: var(--jatui-radius-selector);
|
|
115
|
+
font-size: 0.875rem;
|
|
116
|
+
color: var(--color-base-content);
|
|
117
|
+
background: transparent;
|
|
118
|
+
border: none;
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
text-align: left;
|
|
121
|
+
text-decoration: none;
|
|
122
|
+
transition:
|
|
123
|
+
background-color var(--motion-instant) var(--motion-glide),
|
|
124
|
+
color var(--motion-instant) var(--motion-glide);
|
|
125
|
+
position: relative;
|
|
126
|
+
white-space: nowrap;
|
|
127
|
+
overflow: hidden;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.jatui-sidebar-item:hover:not(.disabled) {
|
|
131
|
+
background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.jatui-sidebar-item.active {
|
|
135
|
+
background: color-mix(in oklch, var(--color-primary) 12%, transparent);
|
|
136
|
+
color: var(--color-primary);
|
|
137
|
+
font-weight: 500;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.jatui-sidebar-item.disabled {
|
|
141
|
+
opacity: 0.4;
|
|
142
|
+
cursor: not-allowed;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.jatui-sidebar-item.icon-only {
|
|
146
|
+
justify-content: center;
|
|
147
|
+
padding: var(--jatui-space-base);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.jatui-sidebar-item-icon {
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
justify-content: center;
|
|
154
|
+
flex-shrink: 0;
|
|
155
|
+
width: 1.25rem;
|
|
156
|
+
height: 1.25rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.jatui-sidebar-item-label {
|
|
160
|
+
flex: 1;
|
|
161
|
+
overflow: hidden;
|
|
162
|
+
text-overflow: ellipsis;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@media (prefers-reduced-motion: reduce) {
|
|
166
|
+
.jatui-sidebar-item {
|
|
167
|
+
transition: none;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { SidebarState } from './shell.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Unique ID used to track the active item */
|
|
5
|
+
id: string;
|
|
6
|
+
/** Display label */
|
|
7
|
+
label: string;
|
|
8
|
+
/** Optional icon slot */
|
|
9
|
+
icon?: Snippet;
|
|
10
|
+
/** Href for anchor-based nav. If omitted, renders as a button */
|
|
11
|
+
href?: string;
|
|
12
|
+
/** Badge content (count or dot) */
|
|
13
|
+
badge?: number | string;
|
|
14
|
+
/** Disabled state */
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
/** Sidebar context state */
|
|
17
|
+
sidebarState?: SidebarState | null;
|
|
18
|
+
/** Called when item is activated */
|
|
19
|
+
onclick?: (id: string) => void;
|
|
20
|
+
class?: string;
|
|
21
|
+
}
|
|
22
|
+
declare const SidebarItem: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
type SidebarItem = ReturnType<typeof SidebarItem>;
|
|
24
|
+
export default SidebarItem;
|