@gsa-tts/graymatter-ui 0.4.19 → 0.5.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.
- package/dist/graymatter-ui.js +4306 -3193
- package/dist/graymatter-ui.umd.cjs +53 -52
- package/package.json +4 -4
- package/src/components/ApiDocSubNavMenu.svelte +7 -3
- package/src/components/Button.svelte +4 -2
- package/src/components/Button.webcomponent.svelte +19 -5
- package/src/components/DesktopHeader.svelte +11 -13
- package/src/components/DesktopSideNav.svelte +118 -91
- package/src/components/DesktopSideNav.webcomponent.svelte +3 -3
- package/src/components/Logo.svelte +5 -5
- package/src/components/MobileBottomNav.svelte +25 -20
- package/src/components/MobileSideMenu.svelte +5 -5
- package/src/components/MobileSideMenu.webcomponent.svelte +2 -4
- package/src/components/MobileTopNav.svelte +22 -27
- package/src/components/NavigationInitializer.svelte +7 -4
- package/src/components/ProfileMenu.svelte +27 -18
- package/src/components/UsaBanner.svelte +3 -3
- package/src/components/icons/ApiIcon.svelte +1 -1
- package/src/components/icons/ChatIcon.svelte +1 -1
- package/src/components/icons/ConsoleIcon.svelte +1 -1
- package/src/components/icons/DiscoverIcon.svelte +1 -1
- package/src/components/icons/HelpIcon.svelte +1 -1
- package/src/components/icons/MenuIcon.svelte +15 -4
- package/src/content/api/endpoints.mdx +8 -1
- package/src/content/api/getting-started.mdx +5 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gsa-tts/graymatter-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
"@types/unist": "^3.0.3",
|
|
54
54
|
"@uswds/compile": "1.2.2",
|
|
55
55
|
"@vitest/coverage-istanbul": "^3.2.4",
|
|
56
|
-
"astro": "^5.
|
|
56
|
+
"astro": "^5.15.9",
|
|
57
57
|
"astro-purgecss": "^5.2.2",
|
|
58
58
|
"eslint": "^9.21.0",
|
|
59
59
|
"gulp": "^5.0.0",
|
|
60
|
-
"svelte": "^5.
|
|
60
|
+
"svelte": "^5.53.5",
|
|
61
61
|
"typescript": "5.7.3",
|
|
62
|
-
"vite": "^6.
|
|
62
|
+
"vite": "^6.4.1",
|
|
63
63
|
"vitest": "^3.0.7",
|
|
64
64
|
"@gsa-tts/graymatter-eslint": "0.0.2",
|
|
65
65
|
"@gsa-tts/graymatter-typescript-config": "0.0.2",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { createEventDispatcher
|
|
2
|
+
import { createEventDispatcher } from 'svelte';
|
|
3
3
|
import { subNavReady } from '../stores/subNavReadyStore';
|
|
4
4
|
import type { SubNavData, SubNavItem } from '../types/subnav.ts';
|
|
5
5
|
import ApiDocSubNavList from './ApiDocSubNavList.svelte';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
let { data = null } = $props();
|
|
8
8
|
// TODO: remove legacy /api/documentation path once all consuming apps migrate
|
|
9
9
|
const API_DOC_PATHS = ['/api-documentation', '/api/documentation'];
|
|
10
10
|
|
|
@@ -187,6 +187,7 @@
|
|
|
187
187
|
let currentSelectedId = $state<string | null>(null);
|
|
188
188
|
let hydrated = $state(false);
|
|
189
189
|
let pageLoadComplete = $state(false);
|
|
190
|
+
let hasMounted = $state(false);
|
|
190
191
|
let scrollTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
191
192
|
|
|
192
193
|
function queueScrollToAnchor(hash: string, delay: number) {
|
|
@@ -268,7 +269,10 @@
|
|
|
268
269
|
}
|
|
269
270
|
});
|
|
270
271
|
|
|
271
|
-
|
|
272
|
+
$effect(() => {
|
|
273
|
+
if (hasMounted) return;
|
|
274
|
+
hasMounted = true;
|
|
275
|
+
|
|
272
276
|
hydrated = true;
|
|
273
277
|
|
|
274
278
|
if (!data) return;
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
type?: 'button' | 'submit' | 'reset';
|
|
29
29
|
onclick?: (event: MouseEvent) => void;
|
|
30
30
|
class?: string;
|
|
31
|
+
children?: import('svelte').Snippet;
|
|
31
32
|
[key: `data-${string}`]: string;
|
|
32
33
|
[key: `aria-${string}`]: string;
|
|
33
34
|
}
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
size = 'medium',
|
|
43
44
|
type = 'button',
|
|
44
45
|
class: className = '',
|
|
46
|
+
children,
|
|
45
47
|
// eslint-disable-next-line svelte/valid-compile
|
|
46
48
|
...props
|
|
47
49
|
}: Props = $props();
|
|
@@ -71,11 +73,11 @@
|
|
|
71
73
|
|
|
72
74
|
{#if href}
|
|
73
75
|
<a class={mergedClasses()} {href} class:disabled {onclick} {...props}>
|
|
74
|
-
|
|
76
|
+
{@render children?.()}
|
|
75
77
|
</a>
|
|
76
78
|
{:else}
|
|
77
79
|
<button class={mergedClasses()} {type} {disabled} {onclick} {...props}>
|
|
78
|
-
|
|
80
|
+
{@render children?.()}
|
|
79
81
|
</button>
|
|
80
82
|
{/if}
|
|
81
83
|
|
|
@@ -16,17 +16,32 @@
|
|
|
16
16
|
import Button from './Button.svelte';
|
|
17
17
|
|
|
18
18
|
// Props for the web component
|
|
19
|
+
interface Props {
|
|
20
|
+
variant?: 'primary' | 'secondary' | 'unstyled';
|
|
21
|
+
theme?: 'default' | 'inverted';
|
|
22
|
+
href?: string;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
size?: 'small' | 'medium' | 'large';
|
|
25
|
+
type?: 'button' | 'submit' | 'reset';
|
|
26
|
+
onclick?: (event: MouseEvent) => void;
|
|
27
|
+
class?: string;
|
|
28
|
+
children?: import('svelte').Snippet;
|
|
29
|
+
[key: `data-${string}`]: string;
|
|
30
|
+
[key: `aria-${string}`]: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
let {
|
|
20
34
|
variant = 'primary',
|
|
21
35
|
theme = 'default',
|
|
22
|
-
href =
|
|
36
|
+
href = undefined,
|
|
23
37
|
disabled = false,
|
|
24
38
|
size = 'medium',
|
|
25
39
|
type = 'button',
|
|
26
40
|
onclick,
|
|
27
41
|
class: className = '',
|
|
42
|
+
children,
|
|
28
43
|
...props
|
|
29
|
-
} = $props();
|
|
44
|
+
}: Props = $props();
|
|
30
45
|
</script>
|
|
31
46
|
|
|
32
47
|
<Button
|
|
@@ -37,8 +52,7 @@
|
|
|
37
52
|
{size}
|
|
38
53
|
{type}
|
|
39
54
|
{onclick}
|
|
55
|
+
{children}
|
|
40
56
|
class={className}
|
|
41
57
|
{...props}
|
|
42
|
-
>
|
|
43
|
-
<slot />
|
|
44
|
-
</Button>
|
|
58
|
+
></Button>
|
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
isExpanded,
|
|
5
5
|
selectedSubNavItemTitle,
|
|
6
6
|
} from '../stores/navigationStore.js';
|
|
7
|
-
import { onMount } from 'svelte';
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
let { customHeaderText = '' } = $props();
|
|
10
9
|
|
|
11
|
-
let isLayoutTransitioning = false;
|
|
10
|
+
let isLayoutTransitioning = $state(false);
|
|
12
11
|
|
|
13
12
|
const showHeader = $derived(
|
|
14
13
|
() =>
|
|
@@ -21,20 +20,19 @@
|
|
|
21
20
|
() => customHeaderText || $selectedSubNavItemTitle
|
|
22
21
|
);
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
$effect(() => {
|
|
24
|
+
if (typeof window === 'undefined') return;
|
|
25
25
|
const handleSectionVisible = (event: Event) => {
|
|
26
|
-
if (
|
|
26
|
+
if (window.innerWidth <= 640) {
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
let topLevelTitle = null;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/* ignore parse error */
|
|
37
|
-
}
|
|
30
|
+
topLevelTitle = sessionStorage.getItem('subNav-topLevelTitle');
|
|
31
|
+
if (topLevelTitle) {
|
|
32
|
+
try {
|
|
33
|
+
topLevelTitle = JSON.parse(topLevelTitle);
|
|
34
|
+
} catch {
|
|
35
|
+
/* ignore parse error */
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
if (typeof topLevelTitle === 'string' && topLevelTitle.trim() !== '') {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
import ProfileMenu from './ProfileMenu.svelte';
|
|
27
27
|
import HelpIcon from './icons/HelpIcon.svelte';
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
let {
|
|
30
30
|
ssrSelectedItem = undefined,
|
|
31
31
|
controlledSelectedItem = undefined,
|
|
32
32
|
onNavToggle = undefined,
|
|
@@ -39,9 +39,10 @@
|
|
|
39
39
|
consoleUrl,
|
|
40
40
|
discoverUrl,
|
|
41
41
|
supplementalMenuData,
|
|
42
|
+
children,
|
|
42
43
|
} = $props();
|
|
43
44
|
|
|
44
|
-
const helpUrl = supplementalMenuData?.help?.url?.trim();
|
|
45
|
+
const helpUrl = $derived(() => supplementalMenuData?.help?.url?.trim());
|
|
45
46
|
|
|
46
47
|
// Check if we're on the help page by comparing URLs (help is not a nav/subnav item)
|
|
47
48
|
function getPathname(url: string, baseOrigin: string) {
|
|
@@ -53,23 +54,24 @@
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
function checkIsHelpPage(): boolean {
|
|
56
|
-
|
|
57
|
+
const url = helpUrl();
|
|
58
|
+
if (!url || url === '#' || url.startsWith('#')) {
|
|
57
59
|
return false;
|
|
58
60
|
}
|
|
59
61
|
const location = (
|
|
60
62
|
globalThis as { location?: { pathname?: string; origin?: string } }
|
|
61
63
|
).location;
|
|
62
64
|
if (location?.origin && location?.pathname) {
|
|
63
|
-
const helpPath = getPathname(
|
|
65
|
+
const helpPath = getPathname(url, location.origin);
|
|
64
66
|
return helpPath !== null && location.pathname === helpPath;
|
|
65
67
|
}
|
|
66
68
|
return false;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
const isHelpSelected = $derived(checkIsHelpPage());
|
|
71
|
+
const isHelpSelected = $derived(() => checkIsHelpPage());
|
|
70
72
|
|
|
71
|
-
let hydrated = false;
|
|
72
|
-
let navContainer
|
|
73
|
+
let hydrated = $state(false);
|
|
74
|
+
let navContainer = $state<HTMLDivElement | null>(null);
|
|
73
75
|
let hoveredItem = $state<string | null>(null);
|
|
74
76
|
let menuButtonHovered = $state(false);
|
|
75
77
|
let canHover = $state(false);
|
|
@@ -118,9 +120,20 @@
|
|
|
118
120
|
);
|
|
119
121
|
|
|
120
122
|
onMount(() => {
|
|
123
|
+
if (typeof window === 'undefined') return;
|
|
124
|
+
|
|
121
125
|
hydrated = true;
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
controlledSelectedItem === undefined &&
|
|
129
|
+
ssrSelectedItem &&
|
|
130
|
+
$selectedItem === null
|
|
131
|
+
) {
|
|
132
|
+
navigationStore.setSelectedItem(ssrSelectedItem);
|
|
133
|
+
}
|
|
122
134
|
// Remove the CSS class since reactive state takes over
|
|
123
135
|
document.documentElement.classList.remove('nav-initially-expanded');
|
|
136
|
+
|
|
124
137
|
// Remove the custom property since reactive CSS takes over
|
|
125
138
|
document.documentElement.style.removeProperty('--initial-main-top-padding');
|
|
126
139
|
// Add hydrated class to re-enable transitions
|
|
@@ -129,100 +142,114 @@
|
|
|
129
142
|
}
|
|
130
143
|
|
|
131
144
|
// Detect if device supports hover (desktop only)
|
|
132
|
-
|
|
133
|
-
|
|
145
|
+
const mq =
|
|
146
|
+
typeof window.matchMedia === 'function'
|
|
147
|
+
? window.matchMedia('(hover: hover)')
|
|
148
|
+
: null;
|
|
149
|
+
const handleHoverChange = (e: MediaQueryListEvent) => {
|
|
150
|
+
canHover = e.matches;
|
|
151
|
+
};
|
|
152
|
+
if (mq) {
|
|
134
153
|
canHover = mq.matches;
|
|
135
|
-
mq.addEventListener('change',
|
|
136
|
-
|
|
137
|
-
|
|
154
|
+
mq.addEventListener('change', handleHoverChange);
|
|
155
|
+
} else {
|
|
156
|
+
canHover = false;
|
|
157
|
+
}
|
|
138
158
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
159
|
+
// Listen for layout transitions
|
|
160
|
+
const handleLayoutTransition = (event: Event) => {
|
|
161
|
+
const customEvent = event as CustomEvent;
|
|
162
|
+
const { isMobile } = customEvent.detail;
|
|
143
163
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
164
|
+
if (isMobile) {
|
|
165
|
+
// Switching to mobile: close navigation
|
|
166
|
+
navigationStore.setExpanded(false);
|
|
167
|
+
} else {
|
|
168
|
+
// Switching to desktop: open navigation if on non-discover page
|
|
169
|
+
const currentItem = selectedForUi();
|
|
170
|
+
|
|
171
|
+
if (currentItem && currentItem !== 'discover') {
|
|
172
|
+
navigationStore.setExpanded(true);
|
|
147
173
|
} else {
|
|
148
|
-
// Switching to desktop: open navigation if on non-discover page
|
|
149
|
-
const currentItem = selectedForUi();
|
|
150
|
-
|
|
151
|
-
if (currentItem && currentItem !== 'discover') {
|
|
152
|
-
navigationStore.setExpanded(true);
|
|
153
|
-
} else {
|
|
154
|
-
navigationStore.setExpanded(false);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
window.addEventListener('layoutTransition', handleLayoutTransition);
|
|
160
|
-
|
|
161
|
-
// Add Escape key handler for nav collapse, but ignore if focus is in profile context menu
|
|
162
|
-
const handleEscape = (e: KeyboardEvent) => {
|
|
163
|
-
const active = document.activeElement;
|
|
164
|
-
if (
|
|
165
|
-
e.key === 'Escape' &&
|
|
166
|
-
$isExpanded &&
|
|
167
|
-
!(active && active.closest('.profile-context-menu'))
|
|
168
|
-
) {
|
|
169
174
|
navigationStore.setExpanded(false);
|
|
170
175
|
}
|
|
171
|
-
}
|
|
172
|
-
|
|
176
|
+
}
|
|
177
|
+
};
|
|
173
178
|
|
|
174
|
-
|
|
175
|
-
const handleDesktopNavControlEvent = (event: Event) => {
|
|
176
|
-
const customEvent = event as CustomEvent;
|
|
177
|
-
const { action, isOpen: eventIsOpen } = customEvent.detail;
|
|
179
|
+
window.addEventListener('layoutTransition', handleLayoutTransition);
|
|
178
180
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
181
|
+
// Add Escape key handler for nav collapse, but ignore if focus is in profile context menu
|
|
182
|
+
const handleEscape = (e: KeyboardEvent) => {
|
|
183
|
+
const active = document.activeElement;
|
|
184
|
+
const profileMenuOpen = Boolean(
|
|
185
|
+
document.querySelector('.profile-context-menu')
|
|
186
|
+
);
|
|
187
|
+
if (
|
|
188
|
+
e.key === 'Escape' &&
|
|
189
|
+
$isExpanded &&
|
|
190
|
+
!profileMenuOpen &&
|
|
191
|
+
!(active && active.closest('.profile-context-menu'))
|
|
192
|
+
) {
|
|
193
|
+
navigationStore.setExpanded(false);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
window.addEventListener('keydown', handleEscape);
|
|
197
|
+
|
|
198
|
+
// Custom event system for external control
|
|
199
|
+
const handleDesktopNavControlEvent = (event: Event) => {
|
|
200
|
+
const customEvent = event as CustomEvent;
|
|
201
|
+
const { action, isOpen: eventIsOpen } = customEvent.detail;
|
|
202
|
+
|
|
203
|
+
if (action === 'toggle') {
|
|
204
|
+
navigationStore.toggleExpanded();
|
|
205
|
+
} else if (action === 'open') {
|
|
206
|
+
navigationStore.setExpanded(true);
|
|
207
|
+
} else if (action === 'close') {
|
|
208
|
+
navigationStore.setExpanded(false);
|
|
209
|
+
} else if (action === 'set' && typeof eventIsOpen === 'boolean') {
|
|
210
|
+
navigationStore.setExpanded(eventIsOpen);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// Listen for custom desktop navigation control events
|
|
215
|
+
window.addEventListener(
|
|
216
|
+
'graymatter-desktop-nav-control',
|
|
217
|
+
handleDesktopNavControlEvent
|
|
218
|
+
);
|
|
189
219
|
|
|
190
|
-
|
|
191
|
-
window.
|
|
220
|
+
eventListenerCleanup = () => {
|
|
221
|
+
window.removeEventListener(
|
|
192
222
|
'graymatter-desktop-nav-control',
|
|
193
223
|
handleDesktopNavControlEvent
|
|
194
224
|
);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
eventListenerCleanup();
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Listen for navigation changes to update help button state
|
|
228
|
+
const handleNavigationChange = () => {
|
|
229
|
+
// Force reactivity update by accessing the derived value
|
|
230
|
+
// Accessing isHelpSelected triggers reactivity
|
|
231
|
+
void isHelpSelected();
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
window.addEventListener('popstate', handleNavigationChange);
|
|
235
|
+
|
|
236
|
+
return () => {
|
|
237
|
+
window.removeEventListener('layoutTransition', handleLayoutTransition);
|
|
238
|
+
window.removeEventListener('keydown', handleEscape);
|
|
239
|
+
window.removeEventListener('popstate', handleNavigationChange);
|
|
240
|
+
if (mq) {
|
|
241
|
+
mq.removeEventListener('change', handleHoverChange);
|
|
242
|
+
}
|
|
243
|
+
if (eventListenerCleanup) {
|
|
244
|
+
eventListenerCleanup();
|
|
245
|
+
}
|
|
246
|
+
};
|
|
221
247
|
});
|
|
222
248
|
|
|
223
249
|
function handleHelpClick(event: MouseEvent) {
|
|
224
250
|
// If it's a hash link, prevent default to stay on same page
|
|
225
|
-
|
|
251
|
+
const url = helpUrl();
|
|
252
|
+
if (url === '#' || url?.startsWith('#')) {
|
|
226
253
|
event.preventDefault();
|
|
227
254
|
}
|
|
228
255
|
}
|
|
@@ -480,16 +507,16 @@
|
|
|
480
507
|
|
|
481
508
|
{#if profileMenuData}
|
|
482
509
|
<div class="nav-footer" part="nav-footer">
|
|
483
|
-
{#if helpUrl}
|
|
510
|
+
{#if helpUrl()}
|
|
484
511
|
<div
|
|
485
512
|
class="nav-footer-icon-container"
|
|
486
513
|
part="nav-footer-icon-container"
|
|
487
514
|
>
|
|
488
515
|
<a
|
|
489
516
|
class="help-link"
|
|
490
|
-
class:selected={isHelpSelected}
|
|
491
|
-
aria-current={isHelpSelected ? 'page' : undefined}
|
|
492
|
-
href={helpUrl}
|
|
517
|
+
class:selected={isHelpSelected()}
|
|
518
|
+
aria-current={isHelpSelected() ? 'page' : undefined}
|
|
519
|
+
href={helpUrl()}
|
|
493
520
|
part="help-link"
|
|
494
521
|
title="Help"
|
|
495
522
|
aria-label="Help"
|
|
@@ -497,7 +524,7 @@
|
|
|
497
524
|
>
|
|
498
525
|
<div class="icon-container" part="help-icon-container">
|
|
499
526
|
<div class="icon-wrapper">
|
|
500
|
-
<HelpIcon isSelected={isHelpSelected} />
|
|
527
|
+
<HelpIcon isSelected={isHelpSelected()} />
|
|
501
528
|
</div>
|
|
502
529
|
</div>
|
|
503
530
|
</a>
|
|
@@ -523,7 +550,7 @@
|
|
|
523
550
|
{#if sideNavExpanded()}
|
|
524
551
|
<div class="drawer-content" part="drawer-content">
|
|
525
552
|
<nav aria-label="Sub navigation" part="sub-navigation">
|
|
526
|
-
|
|
553
|
+
{@render children?.()}
|
|
527
554
|
</nav>
|
|
528
555
|
</div>
|
|
529
556
|
{/if}
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
...restProps
|
|
9
9
|
} = $props();
|
|
10
10
|
|
|
11
|
-
const logoColor =
|
|
12
|
-
? 'var(--ai-color-white)'
|
|
13
|
-
|
|
11
|
+
const logoColor = $derived(() =>
|
|
12
|
+
isInverted ? 'var(--ai-color-white)' : 'var(--ai-color-black)'
|
|
13
|
+
);
|
|
14
14
|
</script>
|
|
15
15
|
|
|
16
16
|
{#if !showServiceMark}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
viewBox="0 0 102 30"
|
|
21
21
|
fill="none"
|
|
22
22
|
xmlns="http://www.w3.org/2000/svg"
|
|
23
|
-
style="fill: {logoColor};"
|
|
23
|
+
style="fill: {logoColor()};"
|
|
24
24
|
{...restProps}
|
|
25
25
|
>
|
|
26
26
|
<g>
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
viewBox="0 0 75 20"
|
|
45
45
|
fill="none"
|
|
46
46
|
xmlns="http://www.w3.org/2000/svg"
|
|
47
|
-
style="fill: {logoColor};"
|
|
47
|
+
style="fill: {logoColor()};"
|
|
48
48
|
{...restProps}
|
|
49
49
|
>
|
|
50
50
|
<path
|
|
@@ -31,27 +31,32 @@
|
|
|
31
31
|
|
|
32
32
|
const selectedForUi = $derived(() => controlledSelectedItem ?? $selectedItem);
|
|
33
33
|
|
|
34
|
-
const allNavItems =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
34
|
+
const allNavItems = $derived(
|
|
35
|
+
() =>
|
|
36
|
+
[
|
|
37
|
+
{
|
|
38
|
+
id: 'discover',
|
|
39
|
+
label: 'Discover',
|
|
40
|
+
icon: DiscoverIcon,
|
|
41
|
+
href: discoverUrl || '#',
|
|
42
|
+
},
|
|
43
|
+
{ id: 'chat', label: 'Chat', icon: ChatIcon, href: chatUrl || '#' },
|
|
44
|
+
{
|
|
45
|
+
id: 'console',
|
|
46
|
+
label: 'Console',
|
|
47
|
+
icon: ConsoleIcon,
|
|
48
|
+
href: consoleUrl || '#',
|
|
49
|
+
},
|
|
50
|
+
{ id: 'api', label: 'API', icon: ApiIcon, href: apiUrl || '#' },
|
|
51
|
+
] as const
|
|
52
|
+
);
|
|
50
53
|
|
|
51
54
|
// Filter out discover item if hideDiscover is true
|
|
52
|
-
const navItems =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
const navItems = $derived(() =>
|
|
56
|
+
hideDiscover
|
|
57
|
+
? allNavItems().filter(item => item.id !== 'discover')
|
|
58
|
+
: allNavItems()
|
|
59
|
+
);
|
|
55
60
|
|
|
56
61
|
function handleNavItemClick(item: NavigationItem) {
|
|
57
62
|
// Set navigation flag immediately to prevent header restoration
|
|
@@ -132,7 +137,7 @@
|
|
|
132
137
|
|
|
133
138
|
{#if showAppIcons}
|
|
134
139
|
<nav class="mobile-bottom-nav" part="mobile-bottom-nav">
|
|
135
|
-
{#each navItems as navItem}
|
|
140
|
+
{#each navItems() as navItem}
|
|
136
141
|
<a
|
|
137
142
|
href={navItem.href}
|
|
138
143
|
class={`nav-item ${navItem.id}-item`}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { onMount } from 'svelte';
|
|
3
2
|
import {
|
|
4
3
|
selectedItem,
|
|
5
4
|
isExpanded,
|
|
@@ -8,10 +7,10 @@
|
|
|
8
7
|
import Logo from './Logo.svelte';
|
|
9
8
|
import MenuIcon from './icons/MenuIcon.svelte';
|
|
10
9
|
|
|
11
|
-
let sideMenuElement
|
|
10
|
+
let sideMenuElement = $state<HTMLDivElement | null>(null);
|
|
12
11
|
let pendingMobileHash: string | null = null;
|
|
13
12
|
|
|
14
|
-
let { logoLinkUrl, logoLinkLabel } = $props();
|
|
13
|
+
let { logoLinkUrl, logoLinkLabel, children } = $props();
|
|
15
14
|
|
|
16
15
|
function handleMobileAnchorNavigate(event: Event) {
|
|
17
16
|
const customEvent = event as CustomEvent;
|
|
@@ -82,7 +81,8 @@
|
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
|
|
84
|
+
$effect(() => {
|
|
85
|
+
if (typeof window === 'undefined') return;
|
|
86
86
|
// Listen for the custom event globally
|
|
87
87
|
window.addEventListener('mobileAnchorNavigate', handleMobileAnchorNavigate);
|
|
88
88
|
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
<!-- Show submenu items if available and not on discover -->
|
|
181
181
|
{#if $selectedItem && $selectedItem !== 'discover'}
|
|
182
182
|
<div class="subnav-section" part="subnav-section">
|
|
183
|
-
|
|
183
|
+
{@render children?.()}
|
|
184
184
|
</div>
|
|
185
185
|
{:else}
|
|
186
186
|
<!-- Show message when no submenu is available -->
|
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
<script lang="ts">
|
|
8
8
|
import MobileSideMenu from './MobileSideMenu.svelte';
|
|
9
9
|
|
|
10
|
-
let { logoLinkUrl, logoLinkLabel } = $props();
|
|
10
|
+
let { logoLinkUrl, logoLinkLabel, children } = $props();
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
|
-
<MobileSideMenu {logoLinkUrl} {logoLinkLabel}
|
|
14
|
-
<slot />
|
|
15
|
-
</MobileSideMenu>
|
|
13
|
+
<MobileSideMenu {logoLinkUrl} {logoLinkLabel} {children} />
|