@joewinke/jatui 0.1.33 → 0.2.1

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 (221) 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/carousel/index.d.ts +1 -0
  24. package/dist/carousel/index.js +2 -0
  25. package/dist/components/AnimatedNumber.svelte +112 -0
  26. package/dist/components/AnimatedNumber.svelte.d.ts +8 -0
  27. package/dist/components/Badge.svelte +75 -4
  28. package/dist/components/Badge.svelte.d.ts +6 -1
  29. package/dist/components/Button.svelte +196 -21
  30. package/dist/components/Button.svelte.d.ts +10 -1
  31. package/dist/components/Carousel.svelte +265 -0
  32. package/dist/components/Carousel.svelte.d.ts +21 -0
  33. package/dist/components/ChipInput.svelte +63 -3
  34. package/dist/components/Collapse.svelte +70 -14
  35. package/dist/components/Collapse.svelte.d.ts +11 -0
  36. package/dist/components/ColorSelector.svelte +21 -24
  37. package/dist/components/CommandPalette.svelte +475 -0
  38. package/dist/components/CommandPalette.svelte.d.ts +3 -0
  39. package/dist/components/ContextMenu.svelte +9 -73
  40. package/dist/components/CopyButton.svelte +156 -0
  41. package/dist/components/CopyButton.svelte.d.ts +12 -0
  42. package/dist/components/HoldToConfirm.svelte +183 -0
  43. package/dist/components/HoldToConfirm.svelte.d.ts +11 -0
  44. package/dist/components/KeyboardShortcutsOverlay.svelte +54 -8
  45. package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +3 -0
  46. package/dist/components/PasswordMeter.svelte +87 -0
  47. package/dist/components/PasswordMeter.svelte.d.ts +8 -0
  48. package/dist/components/PinInput.svelte +186 -0
  49. package/dist/components/PinInput.svelte.d.ts +13 -0
  50. package/dist/components/QuantityStepper.svelte +185 -0
  51. package/dist/components/QuantityStepper.svelte.d.ts +12 -0
  52. package/dist/components/Skeleton.svelte +123 -0
  53. package/dist/components/Skeleton.svelte.d.ts +12 -0
  54. package/dist/components/SpeechForm.svelte +37 -0
  55. package/dist/components/StatusBadge.svelte +31 -3
  56. package/dist/components/StatusBadge.svelte.d.ts +0 -5
  57. package/dist/components/Tabs.svelte +270 -0
  58. package/dist/components/Tabs.svelte.d.ts +15 -0
  59. package/dist/components/TextInput.svelte +138 -38
  60. package/dist/components/TextInput.svelte.d.ts +9 -1
  61. package/dist/components/VoicePlayer.svelte +51 -0
  62. package/dist/components/basic/Alert.svelte +36 -0
  63. package/dist/components/basic/Alert.svelte.d.ts +9 -0
  64. package/dist/components/basic/Breadcrumb.svelte +27 -0
  65. package/dist/components/basic/Breadcrumb.svelte.d.ts +11 -0
  66. package/dist/components/basic/ButtonGroup.svelte +15 -0
  67. package/dist/components/basic/ButtonGroup.svelte.d.ts +8 -0
  68. package/dist/components/basic/Checkbox.svelte +85 -0
  69. package/dist/components/basic/Checkbox.svelte.d.ts +15 -0
  70. package/dist/components/basic/Empty.svelte +17 -0
  71. package/dist/components/basic/Empty.svelte.d.ts +8 -0
  72. package/dist/components/basic/Kbd.svelte +16 -0
  73. package/dist/components/basic/Kbd.svelte.d.ts +8 -0
  74. package/dist/components/basic/Pagination.svelte +104 -0
  75. package/dist/components/basic/Pagination.svelte.d.ts +12 -0
  76. package/dist/components/basic/RadioGroup.svelte +71 -0
  77. package/dist/components/basic/RadioGroup.svelte.d.ts +18 -0
  78. package/dist/components/basic/Separator.svelte +19 -0
  79. package/dist/components/basic/Separator.svelte.d.ts +8 -0
  80. package/dist/components/basic/Slider.svelte +72 -0
  81. package/dist/components/basic/Slider.svelte.d.ts +17 -0
  82. package/dist/components/basic/Switch.svelte +88 -0
  83. package/dist/components/basic/Switch.svelte.d.ts +14 -0
  84. package/dist/components/basic/Toggle.svelte +43 -0
  85. package/dist/components/basic/Toggle.svelte.d.ts +14 -0
  86. package/dist/components/graph/GraphCanvas.svelte +103 -0
  87. package/dist/components/graph/GraphCanvas.svelte.d.ts +22 -0
  88. package/dist/components/graph/JatuiNode.svelte +128 -0
  89. package/dist/components/graph/JatuiNode.svelte.d.ts +14 -0
  90. package/dist/components/graph/index.d.ts +4 -0
  91. package/dist/components/graph/index.js +4 -0
  92. package/dist/components/graph/theme.d.ts +27 -0
  93. package/dist/components/graph/theme.js +136 -0
  94. package/dist/components/messaging/MessageItem.svelte +92 -0
  95. package/dist/components/messaging/MessageItem.svelte.d.ts +5 -0
  96. package/dist/components/messaging/TypingIndicator.svelte +63 -0
  97. package/dist/components/messaging/TypingIndicator.svelte.d.ts +7 -0
  98. package/dist/components/pipeline/PipelineColumn.svelte +44 -3
  99. package/dist/components/progress/ProgressBar.svelte +189 -0
  100. package/dist/components/progress/ProgressBar.svelte.d.ts +17 -0
  101. package/dist/components/progress/ProgressRing.svelte +138 -0
  102. package/dist/components/progress/ProgressRing.svelte.d.ts +12 -0
  103. package/dist/components/progress/Steps.svelte +195 -0
  104. package/dist/components/progress/Steps.svelte.d.ts +14 -0
  105. package/dist/components/shell/NavigationMenu.svelte +422 -0
  106. package/dist/components/shell/NavigationMenu.svelte.d.ts +43 -0
  107. package/dist/components/shell/Sidebar.svelte +276 -0
  108. package/dist/components/shell/Sidebar.svelte.d.ts +32 -0
  109. package/dist/components/shell/SidebarGroup.svelte +180 -0
  110. package/dist/components/shell/SidebarGroup.svelte.d.ts +19 -0
  111. package/dist/components/shell/SidebarItem.svelte +170 -0
  112. package/dist/components/shell/SidebarItem.svelte.d.ts +24 -0
  113. package/dist/components/shell/shell.d.ts +30 -0
  114. package/dist/components/shell/shell.js +65 -0
  115. package/dist/components/table/DataTable.svelte +364 -0
  116. package/dist/components/table/DataTable.svelte.d.ts +4 -0
  117. package/dist/components/table/flexRender.d.ts +31 -0
  118. package/dist/components/table/flexRender.js +32 -0
  119. package/dist/components/table/index.d.ts +3 -0
  120. package/dist/components/table/index.js +3 -0
  121. package/dist/components/table/types.d.ts +28 -0
  122. package/dist/components/table/types.js +1 -0
  123. package/dist/components/toast/ToastHost.svelte +21 -0
  124. package/dist/components/toast/ToastHost.svelte.d.ts +3 -0
  125. package/dist/components/toast/ToastItem.svelte +287 -0
  126. package/dist/components/toast/ToastItem.svelte.d.ts +9 -0
  127. package/dist/index.d.ts +51 -0
  128. package/dist/index.js +64 -0
  129. package/dist/showcase/AnimatedBeam.svelte +183 -0
  130. package/dist/showcase/AnimatedBeam.svelte.d.ts +26 -0
  131. package/dist/showcase/AuroraDrift.svelte +74 -0
  132. package/dist/showcase/AuroraDrift.svelte.d.ts +9 -0
  133. package/dist/showcase/BentoCard.svelte +50 -0
  134. package/dist/showcase/BentoCard.svelte.d.ts +14 -0
  135. package/dist/showcase/BentoGrid.svelte +43 -0
  136. package/dist/showcase/BentoGrid.svelte.d.ts +12 -0
  137. package/dist/showcase/BorderBeam.svelte +79 -0
  138. package/dist/showcase/BorderBeam.svelte.d.ts +12 -0
  139. package/dist/showcase/ClipWipe.svelte +82 -0
  140. package/dist/showcase/ClipWipe.svelte.d.ts +12 -0
  141. package/dist/showcase/Confetti.svelte +71 -0
  142. package/dist/showcase/Confetti.svelte.d.ts +10 -0
  143. package/dist/showcase/CursorSpotlight.svelte +85 -0
  144. package/dist/showcase/CursorSpotlight.svelte.d.ts +9 -0
  145. package/dist/showcase/DeviceMock.svelte +186 -0
  146. package/dist/showcase/DeviceMock.svelte.d.ts +11 -0
  147. package/dist/showcase/Dock.svelte +123 -0
  148. package/dist/showcase/Dock.svelte.d.ts +17 -0
  149. package/dist/showcase/FlipCard.svelte +103 -0
  150. package/dist/showcase/FlipCard.svelte.d.ts +10 -0
  151. package/dist/showcase/FocusReveal.svelte +83 -0
  152. package/dist/showcase/FocusReveal.svelte.d.ts +11 -0
  153. package/dist/showcase/Globe.svelte +202 -0
  154. package/dist/showcase/Globe.svelte.d.ts +16 -0
  155. package/dist/showcase/KenBurns.svelte +105 -0
  156. package/dist/showcase/KenBurns.svelte.d.ts +13 -0
  157. package/dist/showcase/Marquee.svelte +89 -0
  158. package/dist/showcase/Marquee.svelte.d.ts +9 -0
  159. package/dist/showcase/ParallaxTilt.svelte +98 -0
  160. package/dist/showcase/ParallaxTilt.svelte.d.ts +10 -0
  161. package/dist/showcase/ScrambleText.svelte +98 -0
  162. package/dist/showcase/ScrambleText.svelte.d.ts +11 -0
  163. package/dist/showcase/ShineSweep.svelte +81 -0
  164. package/dist/showcase/ShineSweep.svelte.d.ts +9 -0
  165. package/dist/showcase/Terminal.svelte +230 -0
  166. package/dist/showcase/Terminal.svelte.d.ts +18 -0
  167. package/dist/showcase/TextSplitReveal.svelte +146 -0
  168. package/dist/showcase/TextSplitReveal.svelte.d.ts +13 -0
  169. package/dist/showcase/TextWave.svelte +55 -0
  170. package/dist/showcase/TextWave.svelte.d.ts +8 -0
  171. package/dist/showcase/TypewriterText.svelte +130 -0
  172. package/dist/showcase/TypewriterText.svelte.d.ts +9 -0
  173. package/dist/showcase/backdrop/GrainGradient.svelte +126 -0
  174. package/dist/showcase/backdrop/GrainGradient.svelte.d.ts +23 -0
  175. package/dist/showcase/backdrop/MeshGradient.svelte +104 -0
  176. package/dist/showcase/backdrop/MeshGradient.svelte.d.ts +20 -0
  177. package/dist/showcase/backdrop/Metaballs.svelte +116 -0
  178. package/dist/showcase/backdrop/Metaballs.svelte.d.ts +18 -0
  179. package/dist/showcase/backdrop/NeuroNoise.svelte +115 -0
  180. package/dist/showcase/backdrop/NeuroNoise.svelte.d.ts +20 -0
  181. package/dist/showcase/backdrop/ShowcaseBackdrop.svelte +48 -0
  182. package/dist/showcase/backdrop/ShowcaseBackdrop.svelte.d.ts +12 -0
  183. package/dist/showcase/backdrop/index.d.ts +5 -0
  184. package/dist/showcase/backdrop/index.js +6 -0
  185. package/dist/showcase/generative/GenerativeBackdrop.svelte +234 -0
  186. package/dist/showcase/generative/GenerativeBackdrop.svelte.d.ts +9 -0
  187. package/dist/showcase/generative/intent.d.ts +7 -0
  188. package/dist/showcase/generative/intent.js +67 -0
  189. package/dist/showcase/generative/manifest.d.ts +9 -0
  190. package/dist/showcase/generative/manifest.js +97 -0
  191. package/dist/showcase/generative/types.d.ts +48 -0
  192. package/dist/showcase/generative/types.js +8 -0
  193. package/dist/showcase/globe/index.d.ts +1 -0
  194. package/dist/showcase/globe/index.js +2 -0
  195. package/dist/showcase/index.d.ts +25 -0
  196. package/dist/showcase/index.js +39 -0
  197. package/dist/showcase/scroll/SmoothScroll.svelte +49 -0
  198. package/dist/showcase/scroll/SmoothScroll.svelte.d.ts +12 -0
  199. package/dist/showcase/scroll/index.d.ts +5 -0
  200. package/dist/showcase/scroll/index.js +4 -0
  201. package/dist/showcase/scroll/scrollProgress.d.ts +24 -0
  202. package/dist/showcase/scroll/scrollProgress.js +65 -0
  203. package/dist/showcase/scroll/scrollReveal.d.ts +30 -0
  204. package/dist/showcase/scroll/scrollReveal.js +70 -0
  205. package/dist/stores/commandPalette.svelte.d.ts +24 -0
  206. package/dist/stores/commandPalette.svelte.js +82 -0
  207. package/dist/stores/toast.svelte.d.ts +28 -0
  208. package/dist/stores/toast.svelte.js +33 -0
  209. package/dist/styles/motion.css +46 -0
  210. package/dist/styles/tokens.css +31 -0
  211. package/dist/transitions/glide.d.ts +31 -0
  212. package/dist/transitions/glide.js +58 -0
  213. package/dist/utils/sound.d.ts +6 -0
  214. package/dist/utils/sound.js +113 -0
  215. package/dist/utils/splitText.d.ts +25 -0
  216. package/dist/utils/splitText.js +94 -0
  217. package/dist/utils/themeWipe.d.ts +21 -0
  218. package/dist/utils/themeWipe.js +94 -0
  219. package/dist/utils/viewTransition.d.ts +23 -0
  220. package/dist/utils/viewTransition.js +75 -0
  221. package/package.json +78 -4
@@ -0,0 +1,422 @@
1
+ <script lang="ts">
2
+ /**
3
+ * NavigationMenu — horizontal top-nav with dropdown panels.
4
+ *
5
+ * WAI-ARIA menubar pattern: Arrow keys navigate items; Enter/Space opens
6
+ * the dropdown; Escape closes it. Active-route highlighting via activeId.
7
+ *
8
+ * Flyouts use transform/opacity with var(--motion-glide) var(--motion-fast).
9
+ */
10
+
11
+ import type { Snippet } from 'svelte';
12
+
13
+ export interface NavItem {
14
+ /** Unique item ID — used for active-route highlight */
15
+ id: string;
16
+ /** Display label */
17
+ label: string;
18
+ /** Top-level href (navigates directly when no children) */
19
+ href?: string;
20
+ /** Dropdown panel items */
21
+ children?: NavChild[];
22
+ }
23
+
24
+ export interface NavChild {
25
+ id: string;
26
+ label: string;
27
+ href: string;
28
+ description?: string;
29
+ /** Icon slot key — consumer provides icons via the `icons` prop */
30
+ icon?: string;
31
+ }
32
+
33
+ interface Props {
34
+ /** Navigation items */
35
+ items: NavItem[];
36
+ /** Currently active item/child id */
37
+ activeId?: string;
38
+ /** Logo/brand slot (rendered at the leading edge) */
39
+ logo?: Snippet;
40
+ /** Trailing slot (e.g. user avatar, CTA) */
41
+ trailing?: Snippet;
42
+ /** Called when a nav item or child is clicked */
43
+ onNavigate?: (item: NavItem | NavChild) => void;
44
+ class?: string;
45
+ }
46
+
47
+ let {
48
+ items = [],
49
+ activeId = '',
50
+ logo,
51
+ trailing,
52
+ onNavigate,
53
+ class: className = ''
54
+ }: Props = $props();
55
+
56
+ let openId = $state<string | null>(null);
57
+ let menubarEl = $state<HTMLElement | null>(null);
58
+
59
+ function isActive(item: NavItem | NavChild): boolean {
60
+ return item.id === activeId;
61
+ }
62
+
63
+ function hasActiveChild(item: NavItem): boolean {
64
+ return item.children?.some((c) => c.id === activeId) ?? false;
65
+ }
66
+
67
+ function openItem(id: string) {
68
+ openId = id;
69
+ }
70
+
71
+ function closeAll() {
72
+ openId = null;
73
+ }
74
+
75
+ function handleItemClick(item: NavItem) {
76
+ if (item.children?.length) {
77
+ openId = openId === item.id ? null : item.id;
78
+ } else {
79
+ closeAll();
80
+ onNavigate?.(item);
81
+ }
82
+ }
83
+
84
+ function handleChildClick(child: NavChild) {
85
+ closeAll();
86
+ onNavigate?.(child);
87
+ }
88
+
89
+ function handleMenubarKeydown(e: KeyboardEvent) {
90
+ const items_els = Array.from(
91
+ menubarEl?.querySelectorAll<HTMLElement>('[role="menuitem"]') ?? []
92
+ ).filter((el) => !el.closest('[role="menu"]'));
93
+
94
+ const focused = document.activeElement as HTMLElement;
95
+ const idx = items_els.indexOf(focused);
96
+
97
+ if (e.key === 'ArrowRight') {
98
+ e.preventDefault();
99
+ const next = items_els[(idx + 1) % items_els.length];
100
+ next?.focus();
101
+ } else if (e.key === 'ArrowLeft') {
102
+ e.preventDefault();
103
+ const prev = items_els[(idx - 1 + items_els.length) % items_els.length];
104
+ prev?.focus();
105
+ } else if (e.key === 'Escape') {
106
+ closeAll();
107
+ (focused as HTMLElement)?.blur();
108
+ }
109
+ }
110
+
111
+ function handleItemKeydown(e: KeyboardEvent, item: NavItem) {
112
+ if (e.key === 'ArrowDown' && item.children?.length) {
113
+ e.preventDefault();
114
+ openItem(item.id);
115
+ // Focus first child after open
116
+ setTimeout(() => {
117
+ menubarEl
118
+ ?.querySelector<HTMLElement>(`[data-menu="${item.id}"] [role="menuitem"]`)
119
+ ?.focus();
120
+ }, 10);
121
+ } else if (e.key === 'Enter' || e.key === ' ') {
122
+ e.preventDefault();
123
+ handleItemClick(item);
124
+ }
125
+ }
126
+
127
+ function handleDropdownKeydown(e: KeyboardEvent, itemId: string) {
128
+ const menu = menubarEl?.querySelector(`[data-menu="${itemId}"]`);
129
+ const items_els = Array.from(
130
+ menu?.querySelectorAll<HTMLElement>('[role="menuitem"]') ?? []
131
+ );
132
+ const focused = document.activeElement as HTMLElement;
133
+ const idx = items_els.indexOf(focused);
134
+
135
+ if (e.key === 'ArrowDown') {
136
+ e.preventDefault();
137
+ items_els[(idx + 1) % items_els.length]?.focus();
138
+ } else if (e.key === 'ArrowUp') {
139
+ e.preventDefault();
140
+ items_els[(idx - 1 + items_els.length) % items_els.length]?.focus();
141
+ } else if (e.key === 'Escape') {
142
+ e.preventDefault();
143
+ closeAll();
144
+ // Return focus to parent trigger
145
+ menubarEl?.querySelector<HTMLElement>(`[data-trigger="${itemId}"]`)?.focus();
146
+ } else if (e.key === 'Tab') {
147
+ closeAll();
148
+ }
149
+ }
150
+ </script>
151
+
152
+ <svelte:window
153
+ onclick={(e) => {
154
+ if (!menubarEl?.contains(e.target as Node)) closeAll();
155
+ }}
156
+ />
157
+
158
+ <header class="jatui-navmenu {className}">
159
+ <!-- Brand/logo -->
160
+ {#if logo}
161
+ <div class="jatui-navmenu-logo">
162
+ {@render logo()}
163
+ </div>
164
+ {/if}
165
+
166
+ <!-- Menubar -->
167
+ <nav
168
+ bind:this={menubarEl}
169
+ role="menubar"
170
+ aria-label="Main navigation"
171
+ class="jatui-navmenu-bar"
172
+ onkeydown={handleMenubarKeydown}
173
+ >
174
+ {#each items as item (item.id)}
175
+ {@const active = isActive(item) || hasActiveChild(item)}
176
+ {@const isOpen = openId === item.id}
177
+ {@const hasChildren = !!item.children?.length}
178
+
179
+ <div class="jatui-navmenu-item-wrap" class:open={isOpen}>
180
+ <!-- Top-level trigger -->
181
+ {#if item.href && !hasChildren}
182
+ <a
183
+ href={item.href}
184
+ role="menuitem"
185
+ class="jatui-navmenu-trigger"
186
+ class:active
187
+ aria-current={active ? 'page' : undefined}
188
+ data-trigger={item.id}
189
+ onclick={() => handleItemClick(item)}
190
+ onkeydown={(e) => handleItemKeydown(e, item)}
191
+ >
192
+ {item.label}
193
+ </a>
194
+ {:else}
195
+ <button
196
+ type="button"
197
+ role="menuitem"
198
+ class="jatui-navmenu-trigger"
199
+ class:active
200
+ class:has-children={hasChildren}
201
+ aria-haspopup={hasChildren ? 'menu' : undefined}
202
+ aria-expanded={hasChildren ? isOpen : undefined}
203
+ data-trigger={item.id}
204
+ onclick={() => handleItemClick(item)}
205
+ onkeydown={(e) => handleItemKeydown(e, item)}
206
+ >
207
+ {item.label}
208
+ {#if hasChildren}
209
+ <svg
210
+ class="jatui-navmenu-chevron"
211
+ class:open={isOpen}
212
+ width="12"
213
+ height="12"
214
+ viewBox="0 0 12 12"
215
+ fill="none"
216
+ aria-hidden="true"
217
+ >
218
+ <path
219
+ d="M2 4L6 8L10 4"
220
+ stroke="currentColor"
221
+ stroke-width="1.5"
222
+ stroke-linecap="round"
223
+ stroke-linejoin="round"
224
+ />
225
+ </svg>
226
+ {/if}
227
+ </button>
228
+ {/if}
229
+
230
+ <!-- Dropdown panel -->
231
+ {#if hasChildren}
232
+ <div
233
+ class="jatui-navmenu-dropdown"
234
+ class:open={isOpen}
235
+ role="menu"
236
+ aria-label="{item.label} submenu"
237
+ data-menu={item.id}
238
+ onkeydown={(e) => handleDropdownKeydown(e, item.id)}
239
+ >
240
+ {#each item.children! as child (child.id)}
241
+ <a
242
+ href={child.href}
243
+ role="menuitem"
244
+ class="jatui-navmenu-child"
245
+ class:active={isActive(child)}
246
+ aria-current={isActive(child) ? 'page' : undefined}
247
+ onclick={() => handleChildClick(child)}
248
+ >
249
+ <span class="jatui-navmenu-child-label">{child.label}</span>
250
+ {#if child.description}
251
+ <span class="jatui-navmenu-child-desc">{child.description}</span>
252
+ {/if}
253
+ </a>
254
+ {/each}
255
+ </div>
256
+ {/if}
257
+ </div>
258
+ {/each}
259
+ </nav>
260
+
261
+ <!-- Trailing slot -->
262
+ {#if trailing}
263
+ <div class="jatui-navmenu-trailing">
264
+ {@render trailing()}
265
+ </div>
266
+ {/if}
267
+ </header>
268
+
269
+ <style>
270
+ .jatui-navmenu {
271
+ display: flex;
272
+ align-items: center;
273
+ gap: var(--jatui-space-xl);
274
+ height: 56px;
275
+ padding: 0 var(--jatui-space-2xl);
276
+ background: var(--color-base-100);
277
+ border-bottom: var(--jatui-border-w) solid var(--color-base-300);
278
+ position: relative;
279
+ z-index: 30;
280
+ }
281
+
282
+ .jatui-navmenu-logo {
283
+ flex-shrink: 0;
284
+ display: flex;
285
+ align-items: center;
286
+ }
287
+
288
+ .jatui-navmenu-bar {
289
+ display: flex;
290
+ align-items: center;
291
+ gap: var(--jatui-space-xs);
292
+ flex: 1;
293
+ }
294
+
295
+ .jatui-navmenu-trailing {
296
+ flex-shrink: 0;
297
+ display: flex;
298
+ align-items: center;
299
+ gap: var(--jatui-space-base);
300
+ }
301
+
302
+ /* Top-level triggers */
303
+ .jatui-navmenu-item-wrap {
304
+ position: relative;
305
+ }
306
+
307
+ .jatui-navmenu-trigger {
308
+ display: flex;
309
+ align-items: center;
310
+ gap: var(--jatui-space-sm);
311
+ padding: var(--jatui-space-base) var(--jatui-space-lg);
312
+ border-radius: var(--jatui-radius-selector);
313
+ font-size: 0.875rem;
314
+ font-weight: 500;
315
+ color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
316
+ background: transparent;
317
+ border: none;
318
+ cursor: pointer;
319
+ text-decoration: none;
320
+ white-space: nowrap;
321
+ transition:
322
+ color var(--motion-instant) var(--motion-glide),
323
+ background-color var(--motion-instant) var(--motion-glide);
324
+ }
325
+
326
+ .jatui-navmenu-trigger:hover {
327
+ color: var(--color-base-content);
328
+ background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
329
+ }
330
+
331
+ .jatui-navmenu-trigger.active {
332
+ color: var(--color-primary);
333
+ }
334
+
335
+ /* Chevron */
336
+ .jatui-navmenu-chevron {
337
+ transition: transform var(--motion-fast) var(--motion-glide);
338
+ flex-shrink: 0;
339
+ }
340
+
341
+ .jatui-navmenu-chevron.open {
342
+ transform: rotate(180deg);
343
+ }
344
+
345
+ /* Dropdown panel */
346
+ .jatui-navmenu-dropdown {
347
+ position: absolute;
348
+ top: calc(100% + var(--jatui-space-base));
349
+ left: 0;
350
+ min-width: 220px;
351
+ background: var(--color-base-100);
352
+ border: var(--jatui-border-w) solid var(--color-base-300);
353
+ border-radius: var(--jatui-radius-box);
354
+ padding: var(--jatui-space-base);
355
+ display: flex;
356
+ flex-direction: column;
357
+ gap: 2px;
358
+ z-index: 50;
359
+
360
+ /* Flyout: transform + opacity, no layout changes */
361
+ opacity: 0;
362
+ transform: translateY(-6px) scale(0.97);
363
+ pointer-events: none;
364
+ transform-origin: top left;
365
+ transition:
366
+ opacity var(--motion-fast) var(--motion-glide),
367
+ transform var(--motion-fast) var(--motion-glide);
368
+ }
369
+
370
+ .jatui-navmenu-dropdown.open {
371
+ opacity: 1;
372
+ transform: translateY(0) scale(1);
373
+ pointer-events: auto;
374
+ }
375
+
376
+ /* Dropdown children */
377
+ .jatui-navmenu-child {
378
+ display: flex;
379
+ flex-direction: column;
380
+ gap: 2px;
381
+ padding: var(--jatui-space-base) var(--jatui-space-lg);
382
+ border-radius: var(--jatui-radius-selector);
383
+ text-decoration: none;
384
+ color: var(--color-base-content);
385
+ transition:
386
+ background-color var(--motion-instant) var(--motion-glide),
387
+ color var(--motion-instant) var(--motion-glide);
388
+ }
389
+
390
+ .jatui-navmenu-child:hover {
391
+ background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
392
+ }
393
+
394
+ .jatui-navmenu-child.active {
395
+ background: color-mix(in oklch, var(--color-primary) 12%, transparent);
396
+ color: var(--color-primary);
397
+ }
398
+
399
+ .jatui-navmenu-child-label {
400
+ font-size: 0.875rem;
401
+ font-weight: 500;
402
+ }
403
+
404
+ .jatui-navmenu-child-desc {
405
+ font-size: 0.75rem;
406
+ color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
407
+ line-height: 1.3;
408
+ }
409
+
410
+ @media (prefers-reduced-motion: reduce) {
411
+ .jatui-navmenu-trigger,
412
+ .jatui-navmenu-child {
413
+ transition: none;
414
+ }
415
+ .jatui-navmenu-chevron {
416
+ transition: none;
417
+ }
418
+ .jatui-navmenu-dropdown {
419
+ transition: none;
420
+ }
421
+ }
422
+ </style>
@@ -0,0 +1,43 @@
1
+ /**
2
+ * NavigationMenu — horizontal top-nav with dropdown panels.
3
+ *
4
+ * WAI-ARIA menubar pattern: Arrow keys navigate items; Enter/Space opens
5
+ * the dropdown; Escape closes it. Active-route highlighting via activeId.
6
+ *
7
+ * Flyouts use transform/opacity with var(--motion-glide) var(--motion-fast).
8
+ */
9
+ import type { Snippet } from 'svelte';
10
+ export interface NavChild {
11
+ id: string;
12
+ label: string;
13
+ href: string;
14
+ description?: string;
15
+ /** Icon slot key — consumer provides icons via the `icons` prop */
16
+ icon?: string;
17
+ }
18
+ export interface NavItem {
19
+ /** Unique item ID — used for active-route highlight */
20
+ id: string;
21
+ /** Display label */
22
+ label: string;
23
+ /** Top-level href (navigates directly when no children) */
24
+ href?: string;
25
+ /** Dropdown panel items */
26
+ children?: NavChild[];
27
+ }
28
+ interface Props {
29
+ /** Navigation items */
30
+ items: NavItem[];
31
+ /** Currently active item/child id */
32
+ activeId?: string;
33
+ /** Logo/brand slot (rendered at the leading edge) */
34
+ logo?: Snippet;
35
+ /** Trailing slot (e.g. user avatar, CTA) */
36
+ trailing?: Snippet;
37
+ /** Called when a nav item or child is clicked */
38
+ onNavigate?: (item: NavItem | NavChild) => void;
39
+ class?: string;
40
+ }
41
+ declare const NavigationMenu: import("svelte").Component<Props, {}, "">;
42
+ type NavigationMenu = ReturnType<typeof NavigationMenu>;
43
+ export default NavigationMenu;