@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.
Files changed (209) hide show
  1. package/CLAUDE.md +22 -0
  2. package/dist/actions/attention.d.ts +24 -0
  3. package/dist/actions/attention.js +94 -0
  4. package/dist/actions/floating.d.ts +53 -0
  5. package/dist/actions/floating.js +158 -0
  6. package/dist/actions/staggerEntrance.d.ts +28 -0
  7. package/dist/actions/staggerEntrance.js +71 -0
  8. package/dist/actions/tooltip.d.ts +28 -0
  9. package/dist/actions/tooltip.js +186 -0
  10. package/dist/assets/sounds/README.md +47 -0
  11. package/dist/assets/sounds/error.d.ts +1 -0
  12. package/dist/assets/sounds/error.js +4 -0
  13. package/dist/assets/sounds/notify.d.ts +1 -0
  14. package/dist/assets/sounds/notify.js +4 -0
  15. package/dist/assets/sounds/pop.d.ts +1 -0
  16. package/dist/assets/sounds/pop.js +4 -0
  17. package/dist/assets/sounds/send.d.ts +1 -0
  18. package/dist/assets/sounds/send.js +4 -0
  19. package/dist/assets/sounds/success.d.ts +1 -0
  20. package/dist/assets/sounds/success.js +4 -0
  21. package/dist/assets/sounds/toggle.d.ts +1 -0
  22. package/dist/assets/sounds/toggle.js +4 -0
  23. package/dist/components/AnimatedNumber.svelte +112 -0
  24. package/dist/components/AnimatedNumber.svelte.d.ts +8 -0
  25. package/dist/components/Badge.svelte +75 -4
  26. package/dist/components/Badge.svelte.d.ts +6 -1
  27. package/dist/components/Button.svelte +196 -21
  28. package/dist/components/Button.svelte.d.ts +10 -1
  29. package/dist/components/Carousel.svelte +265 -0
  30. package/dist/components/Carousel.svelte.d.ts +21 -0
  31. package/dist/components/ChipInput.svelte +63 -3
  32. package/dist/components/Collapse.svelte +70 -14
  33. package/dist/components/Collapse.svelte.d.ts +11 -0
  34. package/dist/components/ColorSelector.svelte +21 -24
  35. package/dist/components/CommandPalette.svelte +475 -0
  36. package/dist/components/CommandPalette.svelte.d.ts +3 -0
  37. package/dist/components/ContextMenu.svelte +9 -73
  38. package/dist/components/CopyButton.svelte +156 -0
  39. package/dist/components/CopyButton.svelte.d.ts +12 -0
  40. package/dist/components/HoldToConfirm.svelte +183 -0
  41. package/dist/components/HoldToConfirm.svelte.d.ts +11 -0
  42. package/dist/components/KeyboardShortcutsOverlay.svelte +54 -8
  43. package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +3 -0
  44. package/dist/components/PasswordMeter.svelte +87 -0
  45. package/dist/components/PasswordMeter.svelte.d.ts +8 -0
  46. package/dist/components/PinInput.svelte +186 -0
  47. package/dist/components/PinInput.svelte.d.ts +13 -0
  48. package/dist/components/QuantityStepper.svelte +185 -0
  49. package/dist/components/QuantityStepper.svelte.d.ts +12 -0
  50. package/dist/components/Skeleton.svelte +123 -0
  51. package/dist/components/Skeleton.svelte.d.ts +12 -0
  52. package/dist/components/SpeechForm.svelte +37 -0
  53. package/dist/components/StatusBadge.svelte +31 -3
  54. package/dist/components/StatusBadge.svelte.d.ts +0 -5
  55. package/dist/components/Tabs.svelte +270 -0
  56. package/dist/components/Tabs.svelte.d.ts +15 -0
  57. package/dist/components/TextInput.svelte +138 -38
  58. package/dist/components/TextInput.svelte.d.ts +9 -1
  59. package/dist/components/VoicePlayer.svelte +51 -0
  60. package/dist/components/basic/Alert.svelte +36 -0
  61. package/dist/components/basic/Alert.svelte.d.ts +9 -0
  62. package/dist/components/basic/Breadcrumb.svelte +27 -0
  63. package/dist/components/basic/Breadcrumb.svelte.d.ts +11 -0
  64. package/dist/components/basic/ButtonGroup.svelte +15 -0
  65. package/dist/components/basic/ButtonGroup.svelte.d.ts +8 -0
  66. package/dist/components/basic/Checkbox.svelte +85 -0
  67. package/dist/components/basic/Checkbox.svelte.d.ts +15 -0
  68. package/dist/components/basic/Empty.svelte +17 -0
  69. package/dist/components/basic/Empty.svelte.d.ts +8 -0
  70. package/dist/components/basic/Kbd.svelte +16 -0
  71. package/dist/components/basic/Kbd.svelte.d.ts +8 -0
  72. package/dist/components/basic/Pagination.svelte +104 -0
  73. package/dist/components/basic/Pagination.svelte.d.ts +12 -0
  74. package/dist/components/basic/RadioGroup.svelte +71 -0
  75. package/dist/components/basic/RadioGroup.svelte.d.ts +18 -0
  76. package/dist/components/basic/Separator.svelte +19 -0
  77. package/dist/components/basic/Separator.svelte.d.ts +8 -0
  78. package/dist/components/basic/Slider.svelte +72 -0
  79. package/dist/components/basic/Slider.svelte.d.ts +17 -0
  80. package/dist/components/basic/Switch.svelte +88 -0
  81. package/dist/components/basic/Switch.svelte.d.ts +14 -0
  82. package/dist/components/basic/Toggle.svelte +43 -0
  83. package/dist/components/basic/Toggle.svelte.d.ts +14 -0
  84. package/dist/components/graph/GraphCanvas.svelte +103 -0
  85. package/dist/components/graph/GraphCanvas.svelte.d.ts +22 -0
  86. package/dist/components/graph/JatuiNode.svelte +128 -0
  87. package/dist/components/graph/JatuiNode.svelte.d.ts +14 -0
  88. package/dist/components/graph/theme.d.ts +27 -0
  89. package/dist/components/graph/theme.js +136 -0
  90. package/dist/components/messaging/MessageItem.svelte +92 -0
  91. package/dist/components/messaging/MessageItem.svelte.d.ts +5 -0
  92. package/dist/components/messaging/TypingIndicator.svelte +63 -0
  93. package/dist/components/messaging/TypingIndicator.svelte.d.ts +7 -0
  94. package/dist/components/pipeline/PipelineColumn.svelte +44 -3
  95. package/dist/components/progress/ProgressBar.svelte +189 -0
  96. package/dist/components/progress/ProgressBar.svelte.d.ts +17 -0
  97. package/dist/components/progress/ProgressRing.svelte +138 -0
  98. package/dist/components/progress/ProgressRing.svelte.d.ts +12 -0
  99. package/dist/components/progress/Steps.svelte +195 -0
  100. package/dist/components/progress/Steps.svelte.d.ts +14 -0
  101. package/dist/components/shell/NavigationMenu.svelte +422 -0
  102. package/dist/components/shell/NavigationMenu.svelte.d.ts +43 -0
  103. package/dist/components/shell/Sidebar.svelte +276 -0
  104. package/dist/components/shell/Sidebar.svelte.d.ts +32 -0
  105. package/dist/components/shell/SidebarGroup.svelte +180 -0
  106. package/dist/components/shell/SidebarGroup.svelte.d.ts +19 -0
  107. package/dist/components/shell/SidebarItem.svelte +170 -0
  108. package/dist/components/shell/SidebarItem.svelte.d.ts +24 -0
  109. package/dist/components/shell/shell.d.ts +30 -0
  110. package/dist/components/shell/shell.js +65 -0
  111. package/dist/components/table/DataTable.svelte +364 -0
  112. package/dist/components/table/DataTable.svelte.d.ts +4 -0
  113. package/dist/components/table/flexRender.d.ts +31 -0
  114. package/dist/components/table/flexRender.js +32 -0
  115. package/dist/components/table/types.d.ts +28 -0
  116. package/dist/components/table/types.js +1 -0
  117. package/dist/components/toast/ToastHost.svelte +21 -0
  118. package/dist/components/toast/ToastHost.svelte.d.ts +3 -0
  119. package/dist/components/toast/ToastItem.svelte +287 -0
  120. package/dist/components/toast/ToastItem.svelte.d.ts +9 -0
  121. package/dist/index.d.ts +59 -0
  122. package/dist/index.js +67 -0
  123. package/dist/showcase/AnimatedBeam.svelte +183 -0
  124. package/dist/showcase/AnimatedBeam.svelte.d.ts +26 -0
  125. package/dist/showcase/AuroraDrift.svelte +74 -0
  126. package/dist/showcase/AuroraDrift.svelte.d.ts +9 -0
  127. package/dist/showcase/BentoCard.svelte +50 -0
  128. package/dist/showcase/BentoCard.svelte.d.ts +14 -0
  129. package/dist/showcase/BentoGrid.svelte +43 -0
  130. package/dist/showcase/BentoGrid.svelte.d.ts +12 -0
  131. package/dist/showcase/BorderBeam.svelte +79 -0
  132. package/dist/showcase/BorderBeam.svelte.d.ts +12 -0
  133. package/dist/showcase/ClipWipe.svelte +82 -0
  134. package/dist/showcase/ClipWipe.svelte.d.ts +12 -0
  135. package/dist/showcase/Confetti.svelte +71 -0
  136. package/dist/showcase/Confetti.svelte.d.ts +10 -0
  137. package/dist/showcase/CursorSpotlight.svelte +85 -0
  138. package/dist/showcase/CursorSpotlight.svelte.d.ts +9 -0
  139. package/dist/showcase/DeviceMock.svelte +186 -0
  140. package/dist/showcase/DeviceMock.svelte.d.ts +11 -0
  141. package/dist/showcase/Dock.svelte +123 -0
  142. package/dist/showcase/Dock.svelte.d.ts +17 -0
  143. package/dist/showcase/FlipCard.svelte +103 -0
  144. package/dist/showcase/FlipCard.svelte.d.ts +10 -0
  145. package/dist/showcase/FocusReveal.svelte +83 -0
  146. package/dist/showcase/FocusReveal.svelte.d.ts +11 -0
  147. package/dist/showcase/Globe.svelte +202 -0
  148. package/dist/showcase/Globe.svelte.d.ts +16 -0
  149. package/dist/showcase/KenBurns.svelte +105 -0
  150. package/dist/showcase/KenBurns.svelte.d.ts +13 -0
  151. package/dist/showcase/Marquee.svelte +89 -0
  152. package/dist/showcase/Marquee.svelte.d.ts +9 -0
  153. package/dist/showcase/ParallaxTilt.svelte +98 -0
  154. package/dist/showcase/ParallaxTilt.svelte.d.ts +10 -0
  155. package/dist/showcase/ScrambleText.svelte +98 -0
  156. package/dist/showcase/ScrambleText.svelte.d.ts +11 -0
  157. package/dist/showcase/ShineSweep.svelte +81 -0
  158. package/dist/showcase/ShineSweep.svelte.d.ts +9 -0
  159. package/dist/showcase/Terminal.svelte +230 -0
  160. package/dist/showcase/Terminal.svelte.d.ts +18 -0
  161. package/dist/showcase/TextSplitReveal.svelte +146 -0
  162. package/dist/showcase/TextSplitReveal.svelte.d.ts +13 -0
  163. package/dist/showcase/TextWave.svelte +55 -0
  164. package/dist/showcase/TextWave.svelte.d.ts +8 -0
  165. package/dist/showcase/TypewriterText.svelte +130 -0
  166. package/dist/showcase/TypewriterText.svelte.d.ts +9 -0
  167. package/dist/showcase/backdrop/GrainGradient.svelte +126 -0
  168. package/dist/showcase/backdrop/GrainGradient.svelte.d.ts +23 -0
  169. package/dist/showcase/backdrop/MeshGradient.svelte +104 -0
  170. package/dist/showcase/backdrop/MeshGradient.svelte.d.ts +20 -0
  171. package/dist/showcase/backdrop/Metaballs.svelte +116 -0
  172. package/dist/showcase/backdrop/Metaballs.svelte.d.ts +18 -0
  173. package/dist/showcase/backdrop/NeuroNoise.svelte +115 -0
  174. package/dist/showcase/backdrop/NeuroNoise.svelte.d.ts +20 -0
  175. package/dist/showcase/backdrop/ShowcaseBackdrop.svelte +48 -0
  176. package/dist/showcase/backdrop/ShowcaseBackdrop.svelte.d.ts +12 -0
  177. package/dist/showcase/generative/GenerativeBackdrop.svelte +234 -0
  178. package/dist/showcase/generative/GenerativeBackdrop.svelte.d.ts +9 -0
  179. package/dist/showcase/generative/intent.d.ts +7 -0
  180. package/dist/showcase/generative/intent.js +67 -0
  181. package/dist/showcase/generative/manifest.d.ts +9 -0
  182. package/dist/showcase/generative/manifest.js +97 -0
  183. package/dist/showcase/generative/types.d.ts +48 -0
  184. package/dist/showcase/generative/types.js +8 -0
  185. package/dist/showcase/index.d.ts +36 -0
  186. package/dist/showcase/index.js +46 -0
  187. package/dist/showcase/scroll/SmoothScroll.svelte +49 -0
  188. package/dist/showcase/scroll/SmoothScroll.svelte.d.ts +12 -0
  189. package/dist/showcase/scroll/scrollProgress.d.ts +24 -0
  190. package/dist/showcase/scroll/scrollProgress.js +65 -0
  191. package/dist/showcase/scroll/scrollReveal.d.ts +30 -0
  192. package/dist/showcase/scroll/scrollReveal.js +70 -0
  193. package/dist/stores/commandPalette.svelte.d.ts +24 -0
  194. package/dist/stores/commandPalette.svelte.js +82 -0
  195. package/dist/stores/toast.svelte.d.ts +28 -0
  196. package/dist/stores/toast.svelte.js +33 -0
  197. package/dist/styles/motion.css +46 -0
  198. package/dist/styles/tokens.css +31 -0
  199. package/dist/transitions/glide.d.ts +31 -0
  200. package/dist/transitions/glide.js +58 -0
  201. package/dist/utils/sound.d.ts +6 -0
  202. package/dist/utils/sound.js +113 -0
  203. package/dist/utils/splitText.d.ts +25 -0
  204. package/dist/utils/splitText.js +94 -0
  205. package/dist/utils/themeWipe.d.ts +21 -0
  206. package/dist/utils/themeWipe.js +94 -0
  207. package/dist/utils/viewTransition.d.ts +23 -0
  208. package/dist/utils/viewTransition.js +75 -0
  209. 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;