@gsa-tts/graymatter-ui 0.2.8 → 0.3.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 (65) hide show
  1. package/README.md +111 -0
  2. package/assets/images/ai-icons/check-circle.svg +3 -0
  3. package/assets/images/ai-icons/console-icon.svg +6 -6
  4. package/assets/images/ai-icons/pause-button.svg +5 -0
  5. package/assets/images/ai-icons/play-button.svg +4 -0
  6. package/assets/images/api-hero-image.webp +0 -0
  7. package/assets/images/api-icon-w.svg +3 -0
  8. package/assets/images/chat-hero-image.webp +0 -0
  9. package/assets/images/chat-icon-b.svg +4 -0
  10. package/assets/images/console-hero-image.webp +0 -0
  11. package/assets/images/console-icon-w.svg +6 -0
  12. package/assets/images/favicon-dark.ico +0 -0
  13. package/assets/images/favicon-dark.svg +7 -0
  14. package/assets/images/favicon-light.svg +7 -0
  15. package/assets/images/favicon.ico +0 -0
  16. package/assets/images/favicon.svg +5 -6
  17. package/assets/images/og-preview.webp +0 -0
  18. package/assets/images/partners/gemini-government-logo.svg +129 -0
  19. package/assets/images/partners/microsoft-logo.svg +14 -0
  20. package/dist/graymatter-ui.js +1864 -1790
  21. package/dist/graymatter-ui.umd.cjs +60 -34
  22. package/package.json +3 -4
  23. package/src/component-options/componentOptions.ts +27 -13
  24. package/src/components/Button.svelte +191 -37
  25. package/src/components/Button.webcomponent.svelte +13 -11
  26. package/src/components/DesktopHeader.svelte +70 -53
  27. package/src/components/DesktopHeader.webcomponent.svelte +13 -9
  28. package/src/components/DesktopSideNav.svelte +200 -158
  29. package/src/components/DesktopSideNav.webcomponent.svelte +9 -5
  30. package/src/components/GoogleTagManager.astro +1 -1
  31. package/src/components/Head.astro +62 -7
  32. package/src/components/Logo.svelte +48 -41
  33. package/src/components/Logo.webcomponent.svelte +16 -16
  34. package/src/components/MobileBottomNav.svelte +102 -69
  35. package/src/components/MobileBottomNav.webcomponent.svelte +13 -4
  36. package/src/components/MobileSideMenu.svelte +70 -42
  37. package/src/components/MobileSideMenu.webcomponent.svelte +6 -4
  38. package/src/components/MobileTopNav.svelte +24 -19
  39. package/src/components/MobileTopNav.webcomponent.svelte +6 -6
  40. package/src/components/NavigationInitializer.svelte +10 -2
  41. package/src/components/ProfileMenu.svelte +116 -27
  42. package/src/components/UsaSkipNav.svelte +23 -0
  43. package/src/components/icons/ApiIcon.svelte +182 -33
  44. package/src/components/icons/ChatIcon.svelte +10 -10
  45. package/src/components/icons/CloseIcon.svelte +10 -10
  46. package/src/components/icons/ConsoleIcon.svelte +68 -13
  47. package/src/components/icons/DiscoverIcon.svelte +14 -14
  48. package/src/components/icons/ExclamationIcon.svelte +24 -0
  49. package/src/components/icons/HelpIcon.svelte +2 -1
  50. package/src/components/icons/MenuIcon.svelte +47 -7
  51. package/src/components/icons/index.ts +1 -0
  52. package/src/components/index.ts +1 -1
  53. package/src/helpers/url.ts +13 -4
  54. package/src/layouts/BaseLayout.astro +3 -5
  55. package/src/layouts/GlobalAppLayout.astro +3 -1
  56. package/src/styles/base/theme.css +6 -1
  57. package/src/utils/getAppUrls.ts +27 -4
  58. package/assets/images/lp-bg-hero-1024.webp +0 -0
  59. package/assets/images/lp-bg-hero-1920.webp +0 -0
  60. package/assets/images/lp-bg-hero-640.webp +0 -0
  61. package/assets/images/lp-bg-hero.png +0 -0
  62. package/src/components/UsaSkipNav.astro +0 -5
  63. /package/assets/images/{api-icon.svg → api-icon-b.svg} +0 -0
  64. /package/assets/images/{chat-icon.svg → chat-icon-w.svg} +0 -0
  65. /package/assets/images/{console-icon.svg → console-icon-b.svg} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsa-tts/graymatter-ui",
3
- "version": "0.2.8",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -44,7 +44,6 @@
44
44
  "@testing-library/jest-dom": "^6.6.3",
45
45
  "@testing-library/svelte": "^5.2.8",
46
46
  "@testing-library/user-event": "^14.6.1",
47
- "@turbo/gen": "^2.4.0",
48
47
  "@types/node": "^22.13.0",
49
48
  "@uswds/compile": "1.2.2",
50
49
  "@vitest/coverage-istanbul": "^3.0.7",
@@ -62,9 +61,9 @@
62
61
  "dependencies": {
63
62
  "@fontsource-variable/archivo": "^5.2.6",
64
63
  "@uswds/uswds": "3.12.0",
65
- "astro": "^5.11.0",
64
+ "astro": "^5.12.8",
66
65
  "slugify": "^1.6.6",
67
- "@gsa-tts/graymatter-style-tokens": "0.2.3"
66
+ "@gsa-tts/graymatter-style-tokens": "0.3.0"
68
67
  },
69
68
  "scripts": {
70
69
  "build": "gulp update && vite build",
@@ -2,38 +2,52 @@ interface WebsiteComponentOptions {
2
2
  usaBanner?: {
3
3
  theme?: 'inverse' | 'default';
4
4
  };
5
- agencyHeader?: {
5
+ header?: {
6
6
  theme?: 'inverse' | 'default';
7
7
  };
8
8
  }
9
9
 
10
- interface ApiComponentOptions {
11
- usaBanner?: {
12
- theme?: 'inverse' | 'default';
13
- };
14
- }
15
-
16
- interface ConsoleComponentOptions {
10
+ interface AppComponentOptions {
17
11
  usaBanner?: {
18
12
  theme?: 'inverse' | 'default';
19
13
  };
20
14
  }
21
15
 
22
16
  export interface ComponentOptions {
17
+ api?: AppComponentOptions;
18
+ apiSubpage?: WebsiteComponentOptions;
19
+ chatSubpage?: WebsiteComponentOptions;
20
+ console?: AppComponentOptions;
21
+ consoleSubpage?: WebsiteComponentOptions;
22
+ partnershipsSubpage?: WebsiteComponentOptions;
23
23
  website?: WebsiteComponentOptions;
24
- api?: ApiComponentOptions;
25
- console?: ConsoleComponentOptions;
26
24
  }
27
25
 
28
26
  export const componentOptions: ComponentOptions = {
29
- website: {
27
+ api: {
28
+ usaBanner: { theme: 'default' },
29
+ },
30
+ apiSubpage: {
30
31
  usaBanner: { theme: 'inverse' },
31
- agencyHeader: { theme: 'inverse' },
32
+ header: { theme: 'inverse' },
32
33
  },
33
- api: {
34
+ chatSubpage: {
34
35
  usaBanner: { theme: 'default' },
36
+ header: { theme: 'default' },
35
37
  },
36
38
  console: {
37
39
  usaBanner: { theme: 'default' },
38
40
  },
41
+ consoleSubpage: {
42
+ usaBanner: { theme: 'inverse' },
43
+ header: { theme: 'inverse' },
44
+ },
45
+ partnershipsSubpage: {
46
+ usaBanner: { theme: 'default' },
47
+ header: { theme: 'default' },
48
+ },
49
+ website: {
50
+ usaBanner: { theme: 'inverse' },
51
+ header: { theme: 'inverse' },
52
+ },
39
53
  };
@@ -1,13 +1,33 @@
1
1
  <script lang="ts">
2
+ /**
3
+ * Button Component
4
+ *
5
+ * Props:
6
+ * - variant: 'primary' | 'secondary' | 'unstyled' (default: 'primary')
7
+ * - theme: 'default' | 'inverted' (default: 'default')
8
+ * - shape: 'round' | 'square' (default: 'round')
9
+ * - size: 'small' | 'medium' | 'large' (default: 'medium')
10
+ * - href?: string (optional - renders as <a> if provided)
11
+ * - disabled?: boolean (default: false)
12
+ * - type?: 'button' | 'submit' | 'reset' (default: 'button')
13
+ *
14
+ * Examples:
15
+ * - Round primary button: <Button>Click me</Button>
16
+ * - Square secondary button: <Button variant="secondary" shape="square">Square</Button>
17
+ * - Small button (any shape): <Button size="small">Small Button</Button>
18
+ * - Small square button: <Button variant="secondary" shape="square" size="small">Small Square</Button>
19
+ * - Inverted square button: <Button theme="inverted" shape="square">Inverted Square</Button>
20
+ */
2
21
  interface Props {
3
- variant?: 'primary' | 'secondary';
22
+ variant?: 'primary' | 'secondary' | 'unstyled';
4
23
  theme?: 'default' | 'inverted';
24
+ shape?: 'round' | 'square';
5
25
  href?: string;
6
26
  disabled?: boolean;
7
27
  size: 'small' | 'medium' | 'large';
8
28
  type?: 'button' | 'submit' | 'reset';
9
29
  onclick?: (event: MouseEvent) => void;
10
- children?: string | null;
30
+ children?: import('svelte').Snippet | null;
11
31
  class?: string;
12
32
  [key: `data-${string}`]: string;
13
33
  [key: `aria-${string}`]: string;
@@ -16,7 +36,8 @@
16
36
  let {
17
37
  variant = 'primary',
18
38
  theme = 'default',
19
- href = null,
39
+ shape = 'round',
40
+ href = undefined,
20
41
  disabled = false,
21
42
  onclick,
22
43
  children = null,
@@ -32,11 +53,16 @@
32
53
  'usa-button',
33
54
  'ai-button',
34
55
  size === 'small' ? 'ai-button--small' : '',
35
- theme === 'inverted' && variant === 'primary' ? 'ai-button--inverted' : '',
36
- theme === 'inverted' && variant === 'secondary' ? 'ai-button--outline-inverted' : '',
56
+ theme === 'inverted' && variant === 'primary'
57
+ ? 'ai-button--inverted'
58
+ : '',
59
+ theme === 'inverted' && variant === 'secondary'
60
+ ? 'ai-button--outline-inverted'
61
+ : '',
37
62
  variant === 'primary' ? 'usa-button--primary' : '',
38
63
  variant === 'secondary' ? 'usa-button--outline ai-button--outline' : '',
39
64
  variant === 'unstyled' ? 'ai-button--unstyled' : '',
65
+ shape === 'square' ? 'ai-button--square' : '',
40
66
  ]
41
67
  .filter(Boolean)
42
68
  .join(' ');
@@ -46,29 +72,19 @@
46
72
  </script>
47
73
 
48
74
  {#if href}
49
- <a
50
- class={mergedClasses()}
51
- {href}
52
- class:disabled
53
- {onclick}
54
- {...props}
55
- >
75
+ <a class={mergedClasses()} {href} class:disabled {onclick} {...props}>
56
76
  {@render children?.()}
57
77
  </a>
58
78
  {:else}
59
- <button
60
- class={mergedClasses()}
61
- {type}
62
- {disabled}
63
- {onclick}
64
- {...props}
65
- >
79
+ <button class={mergedClasses()} {type} {disabled} {onclick} {...props}>
66
80
  {@render children?.()}
67
81
  </button>
68
82
  {/if}
69
83
 
70
84
  <style>
71
- :root, :host {
85
+ /*! purgecss start ignore */
86
+ :root,
87
+ :host {
72
88
  /* global button */
73
89
  --ai-button-radius: var(--ai-radius-round);
74
90
  --ai-button-padding-x: 2rem;
@@ -78,12 +94,8 @@
78
94
 
79
95
  /* primary button */
80
96
  --ai-button-background-primary-rest: var(--ai-color-black);
81
- --ai-button-background-primary-hover: var(
82
- --ai-color-neutral-900
83
- );
84
- --ai-button-background-primary-disabled: var(
85
- --ai-color-neutral-200
86
- );
97
+ --ai-button-background-primary-hover: var(--ai-color-neutral-900);
98
+ --ai-button-background-primary-disabled: var(--ai-color-neutral-200);
87
99
  --ai-button-text-primary-rest: var(--ai-color-white);
88
100
  --ai-button-text-primary-hover: var(--ai-button-text-primary-rest);
89
101
  --ai-button-text-primary-disabled: var(--ai-color-neutral-800);
@@ -117,6 +129,37 @@
117
129
  --ai-button-border-outline-inverted-hover: var(
118
130
  --ai-button-border-outline-inverted-rest
119
131
  );
132
+
133
+ /* square button specific tokens */
134
+ --ai-button-radius-square: var(--ai-size-4);
135
+ --ai-button-padding-x-square: var(--ai-size-12);
136
+ --ai-button-padding-y-square: var(--ai-size-6);
137
+ --ai-button-height-square: var(--ai-size-40);
138
+ --ai-button-width-square: 100%;
139
+
140
+ /* small square button tokens */
141
+ --ai-button-padding-x-square-small: var(--ai-size-8);
142
+ --ai-button-padding-y-square-small: var(--ai-size-4);
143
+ --ai-button-height-square-small: var(--ai-size-24);
144
+
145
+ /* square button color tokens */
146
+ --ai-button-background-square-primary-rest: var(--ai-color-black);
147
+ --ai-button-background-square-primary-hover: var(--ai-color-neutral-900);
148
+ --ai-button-background-square-primary-disabled: var(--ai-color-neutral-200);
149
+ --ai-button-text-square-primary-rest: var(--ai-color-white);
150
+ --ai-button-text-square-primary-hover: var(--ai-color-white);
151
+ --ai-button-text-square-primary-disabled: var(--ai-color-neutral-800);
152
+
153
+ --ai-button-background-square-secondary-rest: var(--ai-color-white);
154
+ --ai-button-background-square-secondary-hover: var(--ai-color-neutral-200);
155
+ --ai-button-background-square-secondary-disabled: var(
156
+ --ai-color-neutral-500
157
+ );
158
+ --ai-button-text-square-secondary-rest: var(--ai-color-black);
159
+ --ai-button-text-square-secondary-hover: var(--ai-color-black);
160
+ --ai-button-text-square-secondary-disabled: var(--ai-color-neutral-900);
161
+ --ai-button-border-square-secondary-rest: var(--ai-color-black);
162
+ --ai-button-border-square-secondary-hover: var(--ai-color-black);
120
163
  }
121
164
 
122
165
  .ai-button {
@@ -127,6 +170,7 @@
127
170
  padding-inline: var(--ai-button-padding-x);
128
171
  padding-block: var(--ai-button-padding-y);
129
172
  margin: 0;
173
+ width: fit-content;
130
174
  }
131
175
 
132
176
  .ai-button:is(:hover, :focus) {
@@ -148,19 +192,22 @@
148
192
 
149
193
  .ai-button--outline:is(:hover, :focus) {
150
194
  background-color: var(--ai-button-background-outline-hover);
151
- box-shadow: var(--ai-button-box-shadow) var(--ai-button-border-outline-hover);
195
+ box-shadow: var(--ai-button-box-shadow)
196
+ var(--ai-button-border-outline-hover);
152
197
  color: var(--ai-button-text-outline-hover);
153
198
  }
154
199
 
155
200
  .ai-button--outline-inverted {
156
201
  background-color: transparent;
157
- box-shadow: var(--ai-button-box-shadow) var(--ai-button-border-outline-inverted-rest);
202
+ box-shadow: var(--ai-button-box-shadow)
203
+ var(--ai-button-border-outline-inverted-rest);
158
204
  color: var(--ai-button-text-outline-inverted-rest);
159
205
  }
160
206
 
161
207
  .ai-button--outline-inverted:is(:hover, :focus) {
162
208
  background-color: var(--ai-button-background-outline-inverted-hover);
163
- box-shadow: var(--ai-button-box-shadow) var(--ai-button-border-outline-inverted-hover);
209
+ box-shadow: var(--ai-button-box-shadow)
210
+ var(--ai-button-border-outline-inverted-hover);
164
211
  color: var(--ai-button-text-outline-inverted-hover);
165
212
  }
166
213
 
@@ -177,6 +224,108 @@
177
224
  border: var(--ai-button-border-inverted-hover);
178
225
  }
179
226
 
227
+ /* Square button styles */
228
+ .ai-button--square {
229
+ border-radius: var(--ai-button-radius-square);
230
+ padding-inline: var(--ai-button-padding-x-square);
231
+ padding-block: var(--ai-button-padding-y-square);
232
+ height: var(--ai-button-height-square);
233
+ width: var(--ai-button-width-square);
234
+ width: 100%;
235
+ display: flex;
236
+ align-items: center;
237
+ justify-content: center;
238
+ border: 0.0625rem solid var(--ai-color-neutral-300);
239
+ box-shadow: 0 0.0625rem var(--ai-size-2) 0 rgba(0, 0, 0, 0.1);
240
+ transition: all var(--ai-transition-ease-fast);
241
+ }
242
+
243
+ /* Small square button specific adjustments */
244
+ .ai-button--small.ai-button--square {
245
+ padding-inline: var(--ai-button-padding-x-square-small);
246
+ padding-block: var(--ai-button-padding-y-square-small);
247
+ height: var(--ai-button-height-square-small);
248
+ }
249
+
250
+ .ai-button--square:is(:hover, :focus) {
251
+ border-color: var(--ai-color-neutral-400);
252
+ box-shadow: 0 var(--ai-size-2) var(--ai-size-4) 0 rgba(0, 0, 0, 0.3);
253
+ }
254
+
255
+ .ai-button--square:focus {
256
+ outline-offset: var(--ai-size-2);
257
+ }
258
+
259
+ /* Square button variants */
260
+ .ai-button--square.ai-button--primary {
261
+ background-color: var(--ai-button-background-square-primary-rest);
262
+ color: var(--ai-button-text-square-primary-rest);
263
+ border-color: transparent;
264
+ box-shadow: none;
265
+ }
266
+
267
+ .ai-button--square.ai-button--primary:is(:hover, :focus) {
268
+ background-color: var(--ai-button-background-square-primary-hover);
269
+ color: var(--ai-button-text-square-primary-hover);
270
+ border-color: transparent;
271
+ box-shadow: none;
272
+ }
273
+
274
+ .ai-button--square.ai-button--secondary {
275
+ background-color: var(--ai-button-background-square-secondary-rest);
276
+ color: var(--ai-button-text-square-secondary-rest);
277
+ border-color: var(--ai-button-border-square-secondary-rest);
278
+ }
279
+
280
+ .ai-button--square.ai-button--secondary:is(:hover, :focus) {
281
+ background-color: var(--ai-button-background-square-secondary-hover);
282
+ color: var(--ai-button-text-square-secondary-hover);
283
+ border-color: var(--ai-button-border-square-secondary-hover);
284
+ }
285
+
286
+ .ai-button--square.ai-button--outline {
287
+ background-color: var(--ai-color-white);
288
+ color: var(--ai-color-neutral-800);
289
+ border-color: var(--ai-color-neutral-300);
290
+ }
291
+
292
+ .ai-button--square.ai-button--outline:is(:hover, :focus) {
293
+ background-color: var(--ai-color-neutral-50);
294
+ color: var(--ai-color-black);
295
+ border-color: var(--ai-color-neutral-400);
296
+ }
297
+
298
+ /* Square button inverted themes */
299
+ .ai-button--square.ai-button--inverted {
300
+ background-color: var(--ai-button-background-inverted-rest);
301
+ color: var(--ai-button-text-inverted-rest);
302
+ border-color: transparent;
303
+ }
304
+
305
+ .ai-button--square.ai-button--inverted:is(:hover, :focus) {
306
+ background-color: var(--ai-button-background-inverted-hover);
307
+ color: var(--ai-button-text-inverted-hover);
308
+ border-color: transparent;
309
+ }
310
+
311
+ .ai-button--square.ai-button--outline-inverted {
312
+ background-color: transparent;
313
+ color: var(--ai-color-white);
314
+ border-color: var(--ai-color-neutral-300);
315
+ }
316
+
317
+ .ai-button--square.ai-button--outline-inverted:is(:hover, :focus) {
318
+ background-color: var(--ai-color-neutral-900);
319
+ color: var(--ai-color-white);
320
+ border-color: var(--ai-color-neutral-400);
321
+ }
322
+
323
+ .ai-menu-buttons {
324
+ display: flex;
325
+ gap: var(--ai-size-16);
326
+ }
327
+
328
+ /* Unstyled button styles */
180
329
  .ai-button--unstyled {
181
330
  background: none;
182
331
  color: inherit;
@@ -197,37 +346,42 @@
197
346
  text-decoration: underline;
198
347
  }
199
348
 
200
- .ai-menu-buttons {
201
- display: flex;
202
- gap: var(--ai-size-16);
349
+ /* Unstyled square button - just center the text, reuse existing unstyled styles */
350
+ .ai-button--unstyled.ai-button--square {
351
+ text-align: center;
203
352
  }
204
353
 
205
354
  /* disabled button styles */
206
- :is(.disabled, [disabled], [aria-disabled=true]) {
355
+ :is(.disabled, [disabled], [aria-disabled='true']) {
207
356
  background: var(--ai-button-background-primary-disabled);
208
357
  color: var(--ai-button-text-primary-disabled);
209
358
  cursor: not-allowed;
210
359
  }
211
360
 
212
- .ai-button--inverted:is(.disabled, [disabled], [aria-disabled=true]) {
361
+ .ai-button--inverted:is(.disabled, [disabled], [aria-disabled='true']) {
213
362
  background: var(--ai-button-background-inverted-disabled);
214
363
  color: var(--ai-button-text-inverted-disabled);
215
364
  }
216
365
 
217
- .ai-button--outline:is(.disabled, [disabled], [aria-disabled=true]) {
366
+ .ai-button--outline:is(.disabled, [disabled], [aria-disabled='true']) {
218
367
  background: transparent;
219
368
  box-shadow: var(--ai-button-box-shadow) var(--ai-color-neutral-700);
220
369
  color: var(--ai-color-neutral-700);
221
370
  }
222
371
 
223
- .ai-button--outline-inverted:is(.disabled, [disabled], [aria-disabled=true]) {
372
+ .ai-button--outline-inverted:is(
373
+ .disabled,
374
+ [disabled],
375
+ [aria-disabled='true']
376
+ ) {
224
377
  box-shadow: var(--ai-button-box-shadow) var(--ai-color-neutral-200);
225
378
  color: var(--ai-color-neutral-600);
226
379
  }
227
380
 
228
- .ai-button--unstyled:is(.disabled, [disabled], [aria-disabled=true]) {
381
+ .ai-button--unstyled:is(.disabled, [disabled], [aria-disabled='true']) {
229
382
  background: none;
230
383
  color: inherit;
231
384
  opacity: 0.75;
232
385
  }
386
+ /*! purgecss end ignore */
233
387
  </style>
@@ -1,14 +1,16 @@
1
- <svelte:options customElement={{
2
- tag: 'graymatter-button',
3
- props: {
4
- variant: { reflect: true, type: 'String', attribute: 'variant' },
5
- theme: { reflect: true, type: 'String', attribute: 'theme' },
6
- href: { reflect: true, type: 'String', attribute: 'href' },
7
- disabled: { reflect: true, type: 'Boolean', attribute: 'disabled' },
8
- size: { reflect: true, type: 'String', attribute: 'size' },
9
- type: { reflect: true, type: 'String', attribute: 'type' }
10
- }
11
- }} />
1
+ <svelte:options
2
+ customElement={{
3
+ tag: 'graymatter-button',
4
+ props: {
5
+ variant: { reflect: true, type: 'String', attribute: 'variant' },
6
+ theme: { reflect: true, type: 'String', attribute: 'theme' },
7
+ href: { reflect: true, type: 'String', attribute: 'href' },
8
+ disabled: { reflect: true, type: 'Boolean', attribute: 'disabled' },
9
+ size: { reflect: true, type: 'String', attribute: 'size' },
10
+ type: { reflect: true, type: 'String', attribute: 'type' },
11
+ },
12
+ }}
13
+ />
12
14
 
13
15
  <script lang="ts">
14
16
  import Button from './Button.svelte';
@@ -1,64 +1,81 @@
1
1
  <script lang="ts">
2
- import { selectedItem, isExpanded, selectedSubNavItemTitle } from '../stores/navigationStore.js';
3
- import { onMount } from 'svelte';
4
- import { subNavReady } from '../stores/subNavReadyStore';
2
+ import {
3
+ selectedItem,
4
+ isExpanded,
5
+ selectedSubNavItemTitle,
6
+ } from '../stores/navigationStore.js';
7
+ import { onMount } from 'svelte';
8
+ import { subNavReady } from '../stores/subNavReadyStore';
5
9
 
6
- const { customHeaderText = '' } = $props();
10
+ const { customHeaderText = '' } = $props();
7
11
 
8
- let isLayoutTransitioning = false;
12
+ let isLayoutTransitioning = false;
9
13
 
10
- const showHeader = $derived(() =>
11
- $selectedItem && $selectedItem !== 'discover' && !$isExpanded && !isLayoutTransitioning && $subNavReady
12
- );
13
- const headerText = $derived(() =>
14
- customHeaderText || $selectedSubNavItemTitle
15
- );
14
+ const showHeader = $derived(
15
+ () =>
16
+ $selectedItem &&
17
+ $selectedItem !== 'discover' &&
18
+ !$isExpanded &&
19
+ !isLayoutTransitioning &&
20
+ $subNavReady
21
+ );
22
+ const headerText = $derived(
23
+ () => customHeaderText || $selectedSubNavItemTitle
24
+ );
16
25
 
17
- onMount(() => {
18
- const handleSectionVisible = (event: Event) => {
19
- if (typeof window !== 'undefined' && window.innerWidth <= 640) {
20
- return;
21
- }
22
- let topLevelTitle = null;
23
- if (typeof window !== 'undefined') {
24
- topLevelTitle = sessionStorage.getItem('subNav-topLevelTitle');
25
- if (topLevelTitle) {
26
- try {
27
- topLevelTitle = JSON.parse(topLevelTitle);
28
- } catch {/* ignore parse error */}
26
+ onMount(() => {
27
+ const handleSectionVisible = (event: Event) => {
28
+ if (typeof window !== 'undefined' && window.innerWidth <= 640) {
29
+ return;
29
30
  }
30
- }
31
- if (typeof topLevelTitle === 'string' && topLevelTitle.trim() !== '') {
32
- selectedSubNavItemTitle.set(topLevelTitle);
33
- sessionStorage.setItem('subNav-selectedTitle', JSON.stringify(topLevelTitle));
34
- } else {
35
- const customEvent = event as CustomEvent;
36
- if (customEvent.detail && customEvent.detail.title) {
37
- selectedSubNavItemTitle.set(customEvent.detail.title);
38
- sessionStorage.setItem('subNav-selectedTitle', JSON.stringify(customEvent.detail.title));
31
+ let topLevelTitle = null;
32
+ if (typeof window !== 'undefined') {
33
+ topLevelTitle = sessionStorage.getItem('subNav-topLevelTitle');
34
+ if (topLevelTitle) {
35
+ try {
36
+ topLevelTitle = JSON.parse(topLevelTitle);
37
+ } catch {
38
+ /* ignore parse error */
39
+ }
40
+ }
41
+ }
42
+ if (typeof topLevelTitle === 'string' && topLevelTitle.trim() !== '') {
43
+ selectedSubNavItemTitle.set(topLevelTitle);
44
+ sessionStorage.setItem(
45
+ 'subNav-selectedTitle',
46
+ JSON.stringify(topLevelTitle)
47
+ );
48
+ } else {
49
+ const customEvent = event as CustomEvent;
50
+ if (customEvent.detail && customEvent.detail.title) {
51
+ selectedSubNavItemTitle.set(customEvent.detail.title);
52
+ sessionStorage.setItem(
53
+ 'subNav-selectedTitle',
54
+ JSON.stringify(customEvent.detail.title)
55
+ );
56
+ }
39
57
  }
40
- }
41
- };
58
+ };
42
59
 
43
- const handleLayoutTransition = (event: Event) => {
44
- const customEvent = event as CustomEvent;
45
- const { isMobile } = customEvent.detail;
46
- if (!isMobile) {
47
- isLayoutTransitioning = true;
48
- setTimeout(() => {
49
- isLayoutTransitioning = false;
50
- }, 300);
51
- }
52
- };
60
+ const handleLayoutTransition = (event: Event) => {
61
+ const customEvent = event as CustomEvent;
62
+ const { isMobile } = customEvent.detail;
63
+ if (!isMobile) {
64
+ isLayoutTransitioning = true;
65
+ setTimeout(() => {
66
+ isLayoutTransitioning = false;
67
+ }, 300);
68
+ }
69
+ };
53
70
 
54
- window.addEventListener('sectionVisible', handleSectionVisible);
55
- window.addEventListener('layoutTransition', handleLayoutTransition);
71
+ window.addEventListener('sectionVisible', handleSectionVisible);
72
+ window.addEventListener('layoutTransition', handleLayoutTransition);
56
73
 
57
- return () => {
58
- window.removeEventListener('sectionVisible', handleSectionVisible);
59
- window.removeEventListener('layoutTransition', handleLayoutTransition);
60
- };
61
- });
74
+ return () => {
75
+ window.removeEventListener('sectionVisible', handleSectionVisible);
76
+ window.removeEventListener('layoutTransition', handleLayoutTransition);
77
+ };
78
+ });
62
79
  </script>
63
80
 
64
81
  <div class="global-header" class:visible={showHeader()} part="global-header">
@@ -70,11 +87,11 @@ onMount(() => {
70
87
  <style>
71
88
  .global-header {
72
89
  opacity: 0;
73
- transition: opacity 0.2s;
90
+ transition: opacity var(--ai-duration-fast);
74
91
  }
75
92
  .global-header.visible {
76
93
  opacity: 1;
77
- transition: opacity 0.2s;
94
+ transition: opacity var(--ai-duration-fast);
78
95
  }
79
96
  .header-content {
80
97
  display: flex;
@@ -1,9 +1,15 @@
1
- <svelte:options customElement={{
2
- tag: 'graymatter-desktop-header',
3
- props: {
4
- customHeaderText: { reflect: true, type: 'String', attribute: 'custom-header-text' }
5
- }
6
- }} />
1
+ <svelte:options
2
+ customElement={{
3
+ tag: 'graymatter-desktop-header',
4
+ props: {
5
+ customHeaderText: {
6
+ reflect: true,
7
+ type: 'String',
8
+ attribute: 'custom-header-text',
9
+ },
10
+ },
11
+ }}
12
+ />
7
13
 
8
14
  <script lang="ts">
9
15
  import DesktopHeader from './DesktopHeader.svelte';
@@ -12,6 +18,4 @@
12
18
  let { customHeaderText = '' } = $props();
13
19
  </script>
14
20
 
15
- <DesktopHeader
16
- {customHeaderText}
17
- />
21
+ <DesktopHeader {customHeaderText} />