@gsa-tts/graymatter-ui 0.3.1 → 0.3.3
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/README.md +173 -74
- package/assets/images/chat-hero-image-mobile.webp +0 -0
- package/dist/graymatter-ui.js +2172 -2073
- package/dist/graymatter-ui.umd.cjs +21 -21
- package/package.json +5 -4
- package/src/astro-config/index.ts +10 -0
- package/src/components/Button.svelte +4 -0
- package/src/components/DesktopSideNav.svelte +41 -1
- package/src/components/Head.astro +2 -1
- package/src/components/MobileBottomNav.svelte +5 -1
- package/src/components/MobileSideMenu.svelte +2 -2
- package/src/components/MobileSideMenu.webcomponent.svelte +2 -4
- package/src/components/MobileTopNav.svelte +9 -2
- package/src/components/MobileTopNav.webcomponent.svelte +2 -2
- package/src/components/{UsaBanner.astro → UsaBanner.svelte} +33 -31
- package/src/components/index.ts +1 -1
- package/src/index.ts +3 -0
- package/src/layouts/GlobalAppLayout.astro +6 -127
- package/src/styles/base/typography.css +8 -1
- package/src/styles/components/heroCards.css +1 -7
- package/src/utils/navigationControl.ts +78 -0
- package/src/utils/setupMocks.ts +10 -2
- package/static/uswds/styles/styles.css +1 -1
- package/static/uswds/styles/styles.css.map +1 -1
- package/assets/images/api-hero-image.png +0 -0
- package/assets/images/chat-hero-image.png +0 -0
- package/assets/images/console-hero-image.png +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gsa-tts/graymatter-ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -52,14 +52,15 @@
|
|
|
52
52
|
"gulp": "^5.0.0",
|
|
53
53
|
"svelte": "^5.35.4",
|
|
54
54
|
"typescript": "5.7.3",
|
|
55
|
-
"vitest": "^3.0.7",
|
|
56
55
|
"vite": "^6.3.5",
|
|
57
|
-
"
|
|
58
|
-
"@gsa-tts/graymatter-
|
|
56
|
+
"vitest": "^3.0.7",
|
|
57
|
+
"@gsa-tts/graymatter-eslint": "0.0.2",
|
|
58
|
+
"@gsa-tts/graymatter-typescript-config": "0.0.2",
|
|
59
59
|
"@gsa-tts/graymatter-vitest-config": "0.0.1"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@fontsource-variable/archivo": "^5.2.6",
|
|
63
|
+
"@fontsource/space-mono": "^5.2.8",
|
|
63
64
|
"@uswds/uswds": "3.12.0",
|
|
64
65
|
"astro": "^5.12.8",
|
|
65
66
|
"slugify": "^1.6.6",
|
|
@@ -113,6 +113,16 @@ export function createAstroConfig({
|
|
|
113
113
|
styles: ['normal'],
|
|
114
114
|
optimizedFallbacks: true,
|
|
115
115
|
},
|
|
116
|
+
{
|
|
117
|
+
name: 'Space Mono',
|
|
118
|
+
cssVariable: '--ai-font-family-monospace', // Scoped variable name
|
|
119
|
+
provider: fontProviders.fontsource(),
|
|
120
|
+
weights: [400],
|
|
121
|
+
fallbacks: ['monospace'],
|
|
122
|
+
subsets: ['latin'],
|
|
123
|
+
styles: ['normal'],
|
|
124
|
+
optimizedFallbacks: true,
|
|
125
|
+
},
|
|
116
126
|
],
|
|
117
127
|
},
|
|
118
128
|
image: {
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
import ProfileMenu from './ProfileMenu.svelte';
|
|
22
22
|
import { getAppUrls } from '../utils/getAppUrls';
|
|
23
23
|
// import HelpIcon from './icons/HelpIcon.svelte';
|
|
24
|
-
const urls = getAppUrls();
|
|
25
24
|
|
|
26
25
|
const {
|
|
27
26
|
ssrSelectedItem = undefined,
|
|
@@ -36,6 +35,12 @@
|
|
|
36
35
|
discoverUrl,
|
|
37
36
|
} = $props();
|
|
38
37
|
|
|
38
|
+
// Only call getAppUrls if not all URLs are provided via props
|
|
39
|
+
const urls =
|
|
40
|
+
apiUrl && chatUrl && consoleUrl && discoverUrl
|
|
41
|
+
? { api: '#', chat: '#', console: '#', discover: '#' }
|
|
42
|
+
: getAppUrls();
|
|
43
|
+
|
|
39
44
|
let hydrated = false;
|
|
40
45
|
let navContainer: HTMLDivElement;
|
|
41
46
|
let hoveredItem = $state<string | null>(null);
|
|
@@ -47,6 +52,9 @@
|
|
|
47
52
|
let consoleNavRef = $state<HTMLAnchorElement | null>(null);
|
|
48
53
|
let apiNavRef = $state<HTMLAnchorElement | null>(null);
|
|
49
54
|
|
|
55
|
+
// Event system for external control
|
|
56
|
+
let eventListenerCleanup: (() => void) | null = null;
|
|
57
|
+
|
|
50
58
|
function ssrOrHydrated<T>(hydratedValue: T, ssrValue: T): T {
|
|
51
59
|
return hydrated ? hydratedValue : ssrValue;
|
|
52
60
|
}
|
|
@@ -134,9 +142,40 @@
|
|
|
134
142
|
};
|
|
135
143
|
window.addEventListener('keydown', handleEscape);
|
|
136
144
|
|
|
145
|
+
// Custom event system for external control
|
|
146
|
+
const handleDesktopNavControlEvent = (event: CustomEvent) => {
|
|
147
|
+
const { action, isOpen: eventIsOpen } = event.detail;
|
|
148
|
+
|
|
149
|
+
if (action === 'toggle') {
|
|
150
|
+
navigationStore.toggleExpanded();
|
|
151
|
+
} else if (action === 'open') {
|
|
152
|
+
navigationStore.setExpanded(true);
|
|
153
|
+
} else if (action === 'close') {
|
|
154
|
+
navigationStore.setExpanded(false);
|
|
155
|
+
} else if (action === 'set' && typeof eventIsOpen === 'boolean') {
|
|
156
|
+
navigationStore.setExpanded(eventIsOpen);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Listen for custom desktop navigation control events
|
|
161
|
+
window.addEventListener(
|
|
162
|
+
'graymatter-desktop-nav-control',
|
|
163
|
+
handleDesktopNavControlEvent
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
eventListenerCleanup = () => {
|
|
167
|
+
window.removeEventListener(
|
|
168
|
+
'graymatter-desktop-nav-control',
|
|
169
|
+
handleDesktopNavControlEvent
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
137
173
|
return () => {
|
|
138
174
|
window.removeEventListener('layoutTransition', handleLayoutTransition);
|
|
139
175
|
window.removeEventListener('keydown', handleEscape);
|
|
176
|
+
if (eventListenerCleanup) {
|
|
177
|
+
eventListenerCleanup();
|
|
178
|
+
}
|
|
140
179
|
};
|
|
141
180
|
}
|
|
142
181
|
});
|
|
@@ -255,6 +294,7 @@
|
|
|
255
294
|
class:expanded={sideNavExpanded()}
|
|
256
295
|
bind:this={navContainer}
|
|
257
296
|
part="side-nav-container"
|
|
297
|
+
data-testid="side-nav-container"
|
|
258
298
|
>
|
|
259
299
|
<div class="nav-content" part="nav-content">
|
|
260
300
|
<div class="nav-main" part="nav-main">
|
|
@@ -7,7 +7,7 @@ import GoogleTagManager from './GoogleTagManager.astro';
|
|
|
7
7
|
|
|
8
8
|
// Default values for Open Graph
|
|
9
9
|
const defaultDescription =
|
|
10
|
-
'Accelerating
|
|
10
|
+
'Accelerating trusted AI adoption across government. Three powerful AI tools. Industry-leading models. One integrated platform.';
|
|
11
11
|
const defaultOgImage = getUrlFromBase('/common/assets/images/og-preview.webp');
|
|
12
12
|
|
|
13
13
|
const pageTitle = `${title} | USAi`;
|
|
@@ -29,6 +29,7 @@ const canonicalUrl = new URL(Astro.url.pathname, siteUrl).href;
|
|
|
29
29
|
<script is:inline src=`${getUrlFromBase('uswds/js/uswds-init.min.js')}`
|
|
30
30
|
></script>
|
|
31
31
|
<Font cssVariable="--ai-font-family-sans" preload />
|
|
32
|
+
<Font cssVariable="--ai-font-family-monospace" />
|
|
32
33
|
<style>
|
|
33
34
|
body {
|
|
34
35
|
font-family: var(--ai-font-family-sans);
|
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
discoverUrl?: string;
|
|
28
28
|
} = $props();
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
// Only call getAppUrls if not all URLs are provided via props
|
|
31
|
+
const urls =
|
|
32
|
+
apiUrl && chatUrl && consoleUrl && discoverUrl
|
|
33
|
+
? { api: '#', chat: '#', console: '#', discover: '#' }
|
|
34
|
+
: getAppUrls();
|
|
31
35
|
|
|
32
36
|
const navItems = [
|
|
33
37
|
{
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
let sideMenuElement: HTMLDivElement;
|
|
12
12
|
let pendingMobileHash: string | null = null;
|
|
13
13
|
|
|
14
|
-
let { logoLinkUrl, logoLinkLabel } = $props();
|
|
14
|
+
let { logoLinkUrl, logoLinkLabel, children } = $props();
|
|
15
15
|
|
|
16
16
|
function handleMobileAnchorNavigate(event: Event) {
|
|
17
17
|
const customEvent = event as CustomEvent;
|
|
@@ -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} />
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import MenuIcon from './icons/MenuIcon.svelte';
|
|
9
9
|
import { onMount } from 'svelte';
|
|
10
10
|
|
|
11
|
-
let { profileMenuData } = $props();
|
|
11
|
+
let { profileMenuData, logoLinkUrl, logoLinkLabel } = $props();
|
|
12
12
|
|
|
13
13
|
import ProfileMenu from './ProfileMenu.svelte';
|
|
14
14
|
|
|
@@ -90,7 +90,14 @@
|
|
|
90
90
|
</button>
|
|
91
91
|
|
|
92
92
|
<div class="brand">
|
|
93
|
-
|
|
93
|
+
{#if logoLinkUrl}
|
|
94
|
+
<a class="display-flex" href={logoLinkUrl} aria-label={logoLinkLabel}>
|
|
95
|
+
<Logo width={75} height={22} />
|
|
96
|
+
</a>
|
|
97
|
+
{:else}
|
|
98
|
+
<Logo width={75} height={22} />
|
|
99
|
+
{/if}
|
|
100
|
+
|
|
94
101
|
<span class="brand-text">{$selectedSubNavItemTitle || ''}</span>
|
|
95
102
|
</div>
|
|
96
103
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import MobileTopNav from './MobileTopNav.svelte';
|
|
9
9
|
|
|
10
10
|
// Props for the web component
|
|
11
|
-
let { profileMenuData } = $props();
|
|
11
|
+
let { profileMenuData, logoLinkUrl, logoLinkLabel } = $props();
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
|
-
<MobileTopNav {profileMenuData} />
|
|
14
|
+
<MobileTopNav {profileMenuData} {logoLinkUrl} {logoLinkLabel} />
|
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
import { getUrlFromBase } from '@gsa-tts/graymatter-ui/helpers/url.js';
|
|
3
|
-
const { isInverted = false, maxWidth = 'none' } = Astro.props as {
|
|
4
|
-
isInverted?: boolean;
|
|
5
|
-
maxWidth?: 'none' | 'widescreen';
|
|
6
|
-
};
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getUrlFromBase } from '@gsa-tts/graymatter-ui/helpers/url.js';
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
4
|
+
interface Props {
|
|
5
|
+
isInverted?: boolean;
|
|
6
|
+
maxWidth?: 'none' | 'widescreen';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let { isInverted = false, maxWidth = 'none' }: Props = $props();
|
|
10
|
+
|
|
11
|
+
const sizeMap: Record<'widescreen' | 'none', string> = {
|
|
12
|
+
none: 'ai-maxw-none',
|
|
13
|
+
widescreen: 'ai-maxw-widescreen',
|
|
14
|
+
};
|
|
12
15
|
|
|
13
|
-
const mappedSize = sizeMap[maxWidth] || sizeMap.none;
|
|
14
|
-
|
|
16
|
+
const mappedSize = sizeMap[maxWidth] || sizeMap.none;
|
|
17
|
+
</script>
|
|
15
18
|
|
|
16
19
|
<section
|
|
17
|
-
class=
|
|
20
|
+
class="usa-banner site-banner"
|
|
21
|
+
class:inverted={isInverted}
|
|
18
22
|
aria-label="Official website of the United States government"
|
|
19
23
|
>
|
|
20
24
|
<div class="usa-accordion">
|
|
@@ -22,43 +26,42 @@ const mappedSize = sizeMap[maxWidth] || sizeMap.none;
|
|
|
22
26
|
class="usa-banner__header ai-banner__header"
|
|
23
27
|
aria-label="USA banner"
|
|
24
28
|
>
|
|
25
|
-
<div class
|
|
29
|
+
<div class="usa-banner__inner {mappedSize}">
|
|
26
30
|
<div class="grid-col-auto">
|
|
27
31
|
<img
|
|
28
32
|
aria-hidden="true"
|
|
29
33
|
class="usa-banner__header-flag"
|
|
30
|
-
src
|
|
34
|
+
src={getUrlFromBase('uswds/img/us_flag_small.png')}
|
|
31
35
|
alt=""
|
|
32
36
|
/>
|
|
33
37
|
</div>
|
|
34
38
|
<div class="grid-col-fill tablet:grid-col-auto" aria-hidden="true">
|
|
35
|
-
<p class=
|
|
39
|
+
<p class="usa-banner__header-text ai-banner__header-text">
|
|
36
40
|
An official website of the United States government
|
|
37
41
|
</p>
|
|
38
|
-
<p class=
|
|
39
|
-
Here
|
|
42
|
+
<p class="usa-banner__header-action ai-banner__header-action">
|
|
43
|
+
Here's how you know
|
|
40
44
|
</p>
|
|
41
45
|
</div>
|
|
42
46
|
<button
|
|
43
47
|
type="button"
|
|
44
|
-
class=
|
|
48
|
+
class="usa-accordion__button usa-banner__button ai-banner__button"
|
|
45
49
|
aria-expanded="false"
|
|
46
50
|
aria-controls="gov-banner-default"
|
|
47
51
|
>
|
|
48
|
-
<span class="usa-banner__button-text">Here
|
|
52
|
+
<span class="usa-banner__button-text">Here's how you know</span>
|
|
49
53
|
</button>
|
|
50
54
|
</div>
|
|
51
55
|
</header>
|
|
52
56
|
<div
|
|
53
|
-
class=
|
|
57
|
+
class="usa-banner__content usa-accordion__content ai-banner__content {mappedSize}"
|
|
54
58
|
id="gov-banner-default"
|
|
55
59
|
>
|
|
56
60
|
<div class="grid-row grid-gap-lg">
|
|
57
61
|
<div class="usa-banner__guidance tablet:grid-col-6">
|
|
58
62
|
<img
|
|
59
63
|
class="usa-banner__icon ai-banner__icon usa-media-block__img"
|
|
60
|
-
src
|
|
61
|
-
role="img"
|
|
64
|
+
src={getUrlFromBase('uswds/img/icon-dot-gov.svg')}
|
|
62
65
|
alt=""
|
|
63
66
|
aria-hidden="true"
|
|
64
67
|
/>
|
|
@@ -73,8 +76,7 @@ const mappedSize = sizeMap[maxWidth] || sizeMap.none;
|
|
|
73
76
|
<div class="usa-banner__guidance tablet:grid-col-6">
|
|
74
77
|
<img
|
|
75
78
|
class="usa-banner__icon ai-banner__icon usa-media-block__img"
|
|
76
|
-
src
|
|
77
|
-
role="img"
|
|
79
|
+
src={getUrlFromBase('uswds/img/icon-https.svg')}
|
|
78
80
|
alt=""
|
|
79
81
|
aria-hidden="true"
|
|
80
82
|
/>
|
|
@@ -82,8 +84,8 @@ const mappedSize = sizeMap[maxWidth] || sizeMap.none;
|
|
|
82
84
|
<p>
|
|
83
85
|
<strong>Secure .gov websites use HTTPS</strong><br />A
|
|
84
86
|
<strong>lock</strong> (
|
|
85
|
-
<span class="icon-lock"
|
|
86
|
-
|
|
87
|
+
<span class="icon-lock">
|
|
88
|
+
<svg
|
|
87
89
|
xmlns="http://www.w3.org/2000/svg"
|
|
88
90
|
width="52"
|
|
89
91
|
height="64"
|
|
@@ -94,16 +96,16 @@ const mappedSize = sizeMap[maxWidth] || sizeMap.none;
|
|
|
94
96
|
focusable="false"
|
|
95
97
|
>
|
|
96
98
|
<title id="banner-lock-title-default">Lock</title>
|
|
97
|
-
<desc id="banner-lock-description-default"
|
|
98
|
-
|
|
99
|
-
>
|
|
99
|
+
<desc id="banner-lock-description-default">
|
|
100
|
+
Locked padlock icon
|
|
101
|
+
</desc>
|
|
100
102
|
<path
|
|
101
103
|
fill="#000000"
|
|
102
104
|
fill-rule="evenodd"
|
|
103
105
|
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
|
|
104
106
|
></path>
|
|
105
107
|
</svg>
|
|
106
|
-
</span>) or <strong>https://</strong> means you
|
|
108
|
+
</span>) or <strong>https://</strong> means you've safely connected
|
|
107
109
|
to the .gov website. Share sensitive information only on official,
|
|
108
110
|
secure websites.
|
|
109
111
|
</p>
|
package/src/components/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Astro Components
|
|
2
2
|
export { default as Head } from './Head.astro';
|
|
3
|
-
export { default as UsaBanner } from './UsaBanner.astro';
|
|
4
3
|
|
|
5
4
|
// Svelte Components
|
|
5
|
+
export { default as UsaBanner } from './UsaBanner.svelte';
|
|
6
6
|
export { default as Logo } from './Logo.svelte';
|
|
7
7
|
export { default as Button } from './Button.svelte';
|
|
8
8
|
export { default as DesktopSideNav } from './DesktopSideNav.svelte';
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,9 @@ import Logo from './components/Logo.webcomponent.svelte';
|
|
|
5
5
|
import MobileBottomNav from './components/MobileBottomNav.webcomponent.svelte';
|
|
6
6
|
import MobileSideMenu from './components/MobileSideMenu.webcomponent.svelte';
|
|
7
7
|
import MobileTopNav from './components/MobileTopNav.webcomponent.svelte';
|
|
8
|
+
|
|
9
|
+
export * from './utils/navigationControl';
|
|
10
|
+
|
|
8
11
|
export {
|
|
9
12
|
Button,
|
|
10
13
|
DesktopHeader,
|
|
@@ -50,7 +50,12 @@ const {
|
|
|
50
50
|
|
|
51
51
|
<!-- Mobile Navigation -->
|
|
52
52
|
<nav aria-label="Mobile navigation">
|
|
53
|
-
<MobileTopNav
|
|
53
|
+
<MobileTopNav
|
|
54
|
+
client:load
|
|
55
|
+
{profileMenuData}
|
|
56
|
+
{logoLinkUrl}
|
|
57
|
+
{logoLinkLabel}
|
|
58
|
+
/>
|
|
54
59
|
<MobileSideMenu client:load {logoLinkUrl} {logoLinkLabel}>
|
|
55
60
|
<slot name="sub-nav" />
|
|
56
61
|
</MobileSideMenu>
|
|
@@ -104,10 +109,6 @@ const {
|
|
|
104
109
|
const mobileRect = mobileContent.getBoundingClientRect();
|
|
105
110
|
const scrollTop =
|
|
106
111
|
mobileContent.scrollTop + rect.top - mobileRect.top;
|
|
107
|
-
console.log(
|
|
108
|
-
'[GlobalAppLayout] Immediate mobile scroll to:',
|
|
109
|
-
scrollTop
|
|
110
|
-
);
|
|
111
112
|
mobileContent.scrollTo({ top: scrollTop, behavior: 'auto' });
|
|
112
113
|
|
|
113
114
|
setTimeout(() => {
|
|
@@ -140,11 +141,6 @@ const {
|
|
|
140
141
|
if (mainContent) {
|
|
141
142
|
const desktopAnchor = mainContent.querySelector(`[id="${anchor}"]`);
|
|
142
143
|
if (desktopAnchor) {
|
|
143
|
-
console.log(
|
|
144
|
-
'[GlobalAppLayout] Immediate desktop scroll for:',
|
|
145
|
-
anchor
|
|
146
|
-
);
|
|
147
|
-
|
|
148
144
|
try {
|
|
149
145
|
// Add temporary scroll margin to account for header
|
|
150
146
|
const originalScrollMargin =
|
|
@@ -202,27 +198,16 @@ const {
|
|
|
202
198
|
// Check navigation flag first, before any components mount
|
|
203
199
|
const isMainNavNavigation =
|
|
204
200
|
sessionStorage.getItem('navigatingToMainNav') === 'true';
|
|
205
|
-
console.log(
|
|
206
|
-
'[GlobalAppLayout] DOMContentLoaded - navigation flag:',
|
|
207
|
-
isMainNavNavigation
|
|
208
|
-
);
|
|
209
201
|
|
|
210
202
|
if (isMainNavNavigation) {
|
|
211
203
|
// Clear the flag immediately
|
|
212
204
|
sessionStorage.removeItem('navigatingToMainNav');
|
|
213
|
-
console.log(
|
|
214
|
-
'[GlobalAppLayout] Cleared navigation flag for main nav navigation'
|
|
215
|
-
);
|
|
216
205
|
// Don't clear header state here - let navigation components handle it
|
|
217
206
|
}
|
|
218
207
|
|
|
219
208
|
// Handle URL hash scrolling (fallback for cases where immediate scroll didn't work)
|
|
220
209
|
if (window.location.hash) {
|
|
221
210
|
const anchor = window.location.hash.substring(1);
|
|
222
|
-
console.log(
|
|
223
|
-
'[GlobalAppLayout] DOMContentLoaded fallback hash scroll for:',
|
|
224
|
-
anchor
|
|
225
|
-
);
|
|
226
211
|
|
|
227
212
|
// Only proceed if we haven't already scrolled
|
|
228
213
|
if (sessionStorage.getItem('globalLayoutScrolling') !== 'true') {
|
|
@@ -243,10 +228,6 @@ const {
|
|
|
243
228
|
const mobileRect = mobileContent.getBoundingClientRect();
|
|
244
229
|
const scrollTop =
|
|
245
230
|
mobileContent.scrollTop + rect.top - mobileRect.top;
|
|
246
|
-
console.log(
|
|
247
|
-
'[GlobalAppLayout] Fallback mobile scroll to:',
|
|
248
|
-
scrollTop
|
|
249
|
-
);
|
|
250
231
|
mobileContent.scrollTo({ top: scrollTop, behavior: 'auto' });
|
|
251
232
|
|
|
252
233
|
setTimeout(() => {
|
|
@@ -287,11 +268,6 @@ const {
|
|
|
287
268
|
);
|
|
288
269
|
if (desktopAnchor) {
|
|
289
270
|
setTimeout(() => {
|
|
290
|
-
console.log(
|
|
291
|
-
'[GlobalAppLayout] Fallback desktop scroll for:',
|
|
292
|
-
anchor
|
|
293
|
-
);
|
|
294
|
-
|
|
295
271
|
// Try scrollIntoView first with a temporary offset
|
|
296
272
|
try {
|
|
297
273
|
// Add temporary scroll margin to account for header
|
|
@@ -305,43 +281,18 @@ const {
|
|
|
305
281
|
inline: 'nearest',
|
|
306
282
|
});
|
|
307
283
|
|
|
308
|
-
console.log(
|
|
309
|
-
'[GlobalAppLayout] Used fallback scrollIntoView for desktop'
|
|
310
|
-
);
|
|
311
|
-
|
|
312
284
|
// Restore original scroll margin after a delay
|
|
313
285
|
setTimeout(() => {
|
|
314
286
|
desktopAnchor.style.scrollMarginTop =
|
|
315
287
|
originalScrollMargin;
|
|
316
288
|
}, 1000);
|
|
317
289
|
} catch (e) {
|
|
318
|
-
console.log(
|
|
319
|
-
'[GlobalAppLayout] Fallback scrollIntoView failed, using manual calculation:',
|
|
320
|
-
e
|
|
321
|
-
);
|
|
322
|
-
|
|
323
290
|
// Fallback to manual calculation
|
|
324
291
|
const headerHeight = 56;
|
|
325
292
|
const rect = desktopAnchor.getBoundingClientRect();
|
|
326
293
|
const scrollTop = window.scrollY + rect.top - headerHeight;
|
|
327
|
-
console.log(
|
|
328
|
-
'[GlobalAppLayout] Fallback desktop scroll calculation:',
|
|
329
|
-
{
|
|
330
|
-
anchor,
|
|
331
|
-
rectTop: rect.top,
|
|
332
|
-
windowScrollY: window.scrollY,
|
|
333
|
-
headerHeight,
|
|
334
|
-
scrollTop,
|
|
335
|
-
elementText: desktopAnchor.textContent?.trim(),
|
|
336
|
-
}
|
|
337
|
-
);
|
|
338
|
-
|
|
339
294
|
// Ensure scrollTop is not negative
|
|
340
295
|
const finalScrollTop = Math.max(0, scrollTop);
|
|
341
|
-
console.log(
|
|
342
|
-
'[GlobalAppLayout] Fallback final scroll position:',
|
|
343
|
-
finalScrollTop
|
|
344
|
-
);
|
|
345
296
|
|
|
346
297
|
window.scrollTo({ top: finalScrollTop, behavior: 'auto' });
|
|
347
298
|
}
|
|
@@ -374,25 +325,14 @@ const {
|
|
|
374
325
|
}, 100);
|
|
375
326
|
}, 100);
|
|
376
327
|
} else {
|
|
377
|
-
console.log(
|
|
378
|
-
'[GlobalAppLayout] Fallback desktop anchor not found in main-content container:',
|
|
379
|
-
anchor
|
|
380
|
-
);
|
|
381
328
|
// Log all available anchors for debugging
|
|
382
329
|
const allAnchors = mainContent.querySelectorAll('[id]');
|
|
383
|
-
console.log(
|
|
384
|
-
'[GlobalAppLayout] Available anchors in main-content:',
|
|
385
|
-
Array.from(allAnchors).map(el => el.id)
|
|
386
|
-
);
|
|
387
330
|
// Clear the flag if anchor not found
|
|
388
331
|
if (typeof window !== 'undefined') {
|
|
389
332
|
sessionStorage.removeItem('globalLayoutScrolling');
|
|
390
333
|
}
|
|
391
334
|
}
|
|
392
335
|
} else {
|
|
393
|
-
console.log(
|
|
394
|
-
'[GlobalAppLayout] Fallback desktop main-content container not found'
|
|
395
|
-
);
|
|
396
336
|
// Clear the flag if container not found
|
|
397
337
|
if (typeof window !== 'undefined') {
|
|
398
338
|
sessionStorage.removeItem('globalLayoutScrolling');
|
|
@@ -420,29 +360,10 @@ const {
|
|
|
420
360
|
const wasMobile = lastWidth <= 640;
|
|
421
361
|
const isMobile = currentWidth <= 640;
|
|
422
362
|
|
|
423
|
-
console.log('[GlobalAppLayout] Resize detected:', {
|
|
424
|
-
lastWidth,
|
|
425
|
-
currentWidth,
|
|
426
|
-
wasMobile,
|
|
427
|
-
isMobile,
|
|
428
|
-
changed: wasMobile !== isMobile,
|
|
429
|
-
});
|
|
430
|
-
|
|
431
363
|
if (wasMobile !== isMobile) {
|
|
432
|
-
console.log(
|
|
433
|
-
'[GlobalAppLayout] Layout changed from',
|
|
434
|
-
wasMobile ? 'mobile' : 'desktop',
|
|
435
|
-
'to',
|
|
436
|
-
isMobile ? 'mobile' : 'desktop'
|
|
437
|
-
);
|
|
438
|
-
|
|
439
364
|
// Handle anchor scrolling for layout changes
|
|
440
365
|
if (window.location.hash) {
|
|
441
366
|
const anchor = window.location.hash.substring(1);
|
|
442
|
-
console.log(
|
|
443
|
-
'[GlobalAppLayout] Layout change with URL hash:',
|
|
444
|
-
anchor
|
|
445
|
-
);
|
|
446
367
|
|
|
447
368
|
// Wait for navigation state to update, then scroll
|
|
448
369
|
setTimeout(() => {
|
|
@@ -460,10 +381,6 @@ const {
|
|
|
460
381
|
const mobileRect = mobileContent.getBoundingClientRect();
|
|
461
382
|
const scrollTop =
|
|
462
383
|
mobileContent.scrollTop + rect.top - mobileRect.top;
|
|
463
|
-
console.log(
|
|
464
|
-
'[GlobalAppLayout] Scrolling mobile to anchor:',
|
|
465
|
-
scrollTop
|
|
466
|
-
);
|
|
467
384
|
mobileContent.scrollTo({
|
|
468
385
|
top: scrollTop,
|
|
469
386
|
behavior: 'auto',
|
|
@@ -501,15 +418,6 @@ const {
|
|
|
501
418
|
`[id="${anchor}"]`
|
|
502
419
|
);
|
|
503
420
|
if (desktopAnchor) {
|
|
504
|
-
console.log(
|
|
505
|
-
'[GlobalAppLayout] Desktop anchor found for layout change:',
|
|
506
|
-
{
|
|
507
|
-
anchor,
|
|
508
|
-
elementText: desktopAnchor.textContent?.trim(),
|
|
509
|
-
elementId: desktopAnchor.id,
|
|
510
|
-
}
|
|
511
|
-
);
|
|
512
|
-
|
|
513
421
|
// Try scrollIntoView first with a temporary offset
|
|
514
422
|
try {
|
|
515
423
|
// Add temporary scroll margin to account for header
|
|
@@ -523,44 +431,20 @@ const {
|
|
|
523
431
|
inline: 'nearest',
|
|
524
432
|
});
|
|
525
433
|
|
|
526
|
-
console.log(
|
|
527
|
-
'[GlobalAppLayout] Used scrollIntoView for desktop layout change with scroll margin'
|
|
528
|
-
);
|
|
529
|
-
|
|
530
434
|
// Restore original scroll margin after a delay
|
|
531
435
|
setTimeout(() => {
|
|
532
436
|
desktopAnchor.style.scrollMarginTop =
|
|
533
437
|
originalScrollMargin;
|
|
534
438
|
}, 1000);
|
|
535
439
|
} catch (e) {
|
|
536
|
-
console.log(
|
|
537
|
-
'[GlobalAppLayout] scrollIntoView failed for layout change, using manual calculation:',
|
|
538
|
-
e
|
|
539
|
-
);
|
|
540
|
-
|
|
541
440
|
// Fallback to manual calculation
|
|
542
441
|
const headerHeight = 56;
|
|
543
442
|
const rect = desktopAnchor.getBoundingClientRect();
|
|
544
443
|
const scrollTop =
|
|
545
444
|
window.scrollY + rect.top - headerHeight;
|
|
546
|
-
console.log(
|
|
547
|
-
'[GlobalAppLayout] Desktop scroll calculation for layout change:',
|
|
548
|
-
{
|
|
549
|
-
anchor,
|
|
550
|
-
rectTop: rect.top,
|
|
551
|
-
windowScrollY: window.scrollY,
|
|
552
|
-
headerHeight,
|
|
553
|
-
scrollTop,
|
|
554
|
-
elementText: desktopAnchor.textContent?.trim(),
|
|
555
|
-
}
|
|
556
|
-
);
|
|
557
445
|
|
|
558
446
|
// Ensure scrollTop is not negative
|
|
559
447
|
const finalScrollTop = Math.max(0, scrollTop);
|
|
560
|
-
console.log(
|
|
561
|
-
'[GlobalAppLayout] Final scroll position for layout change:',
|
|
562
|
-
finalScrollTop
|
|
563
|
-
);
|
|
564
448
|
|
|
565
449
|
window.scrollTo({
|
|
566
450
|
top: finalScrollTop,
|
|
@@ -603,11 +487,6 @@ const {
|
|
|
603
487
|
detail: { isMobile },
|
|
604
488
|
})
|
|
605
489
|
);
|
|
606
|
-
console.log(
|
|
607
|
-
'[GlobalAppLayout] Dispatched layoutTransition event for',
|
|
608
|
-
isMobile ? 'mobile' : 'desktop',
|
|
609
|
-
'layout'
|
|
610
|
-
);
|
|
611
490
|
}
|
|
612
491
|
|
|
613
492
|
lastWidth = currentWidth;
|
|
@@ -50,7 +50,14 @@ h6,
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
pre {
|
|
53
|
-
padding: var(--ai-size-
|
|
53
|
+
padding: var(--ai-size-16);
|
|
54
|
+
border-radius: var(--ai-size-4);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
code {
|
|
58
|
+
font-family: var(--ai-font-family-monospace);
|
|
59
|
+
font-weight: var(--ai-font-weight-normal);
|
|
60
|
+
font-size: var(--ai-size-14);
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
a {
|