@gsa-tts/graymatter-ui 1.2.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +59 -91
  2. package/assets/uswds/components/usa-banner.js.mjs +805 -0
  3. package/assets/uswds/components/usa-banner.js.umd.js +127 -0
  4. package/package.json +18 -34
  5. package/src/components/DesktopSideNav.svelte +17 -15
  6. package/src/components/MobileTopNav.svelte +15 -13
  7. package/src/components/ProfileMenu.svelte +22 -15
  8. package/src/components/Tooltip.svelte +288 -0
  9. package/src/components/UsaBanner.svelte +2 -2
  10. package/src/components/index.ts +1 -0
  11. package/src/utils/index.ts +0 -4
  12. package/static/uswds/js/uswds.js +37 -3
  13. package/static/uswds/js/uswds.min.js +1 -1
  14. package/static/uswds/js/uswds.min.js.map +1 -1
  15. package/static/uswds/styles/styles.css +3 -3
  16. package/static/uswds/styles/styles.css.map +1 -1
  17. package/dist/graymatter-ui.js +0 -6025
  18. package/dist/graymatter-ui.umd.cjs +0 -78
  19. package/src/astro-config/index.ts +0 -139
  20. package/src/components/Button.webcomponent.svelte +0 -58
  21. package/src/components/DesktopHeader.webcomponent.svelte +0 -21
  22. package/src/components/DesktopSideNav.webcomponent.svelte +0 -42
  23. package/src/components/Logo.webcomponent.svelte +0 -29
  24. package/src/components/MobileBottomNav.webcomponent.svelte +0 -32
  25. package/src/components/MobileSideMenu.webcomponent.svelte +0 -13
  26. package/src/components/MobileTopNav.webcomponent.svelte +0 -20
  27. package/src/index.ts +0 -19
  28. package/src/layouts/BaseLayout.astro +0 -37
  29. package/src/layouts/DocsLayout.astro +0 -342
  30. package/src/layouts/GlobalAppLayout.astro +0 -536
  31. package/src/layouts/GoogleTagManager.astro +0 -17
  32. package/src/layouts/Head.astro +0 -101
  33. package/src/layouts/fonts.ts +0 -17
  34. package/src/pages/.gitkeep +0 -0
  35. package/src/utils/remarkPlugins.ts +0 -55
@@ -0,0 +1,288 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Tooltip Component
4
+ *
5
+ * Ported from usai-frontend's Tooltip:
6
+ * https://github.com/GSA-TTS/usai-frontend/blob/4050b68aeccf21572c3897a0b2e01c4719b9456a/src/lib/components/shared/Tooltip.svelte
7
+ * (kept behaviorally identical; renamed `.tooltip*` classes to `.ai-tooltip*`
8
+ * and `--tooltip-*` vars to `--ai-tooltip-*` to match this package's naming).
9
+ *
10
+ * The bubble is portaled to <body> and positioned with `position: fixed` +
11
+ * JS coordinates so it never gets clipped by an `overflow: hidden` ancestor.
12
+ *
13
+ * Appearance is driven entirely by `--ai-tooltip-*` CSS custom properties. To
14
+ * restyle per app, override those variables at `:root` (or any scope that
15
+ * reaches <body>) — no component changes needed.
16
+ *
17
+ * `children` is a snippet that receives accessibility props. Callers that
18
+ * don't need them can pass plain content; the extra args are simply ignored.
19
+ *
20
+ * Props:
21
+ * - text: string - Text shown inside the tooltip bubble
22
+ * - position?: 'top' | 'bottom' | 'left' | 'right' (default: 'top')
23
+ * - align?: 'center' | 'left' | 'right' (default: 'center')
24
+ * - showOn?: 'hover' | 'disabled' (default: 'hover')
25
+ * - disabled?: boolean (default: false)
26
+ * - showArrow?: boolean (default: false)
27
+ * - minWidth?: string
28
+ * - maxWidth?: string
29
+ * - tooltipId?: string - Auto-generated if omitted
30
+ * - wrapperClass?: string
31
+ * - children?: Snippet<[TooltipChildrenProps]>
32
+ *
33
+ * Example:
34
+ * ```svelte
35
+ * <Tooltip text="Help" position="right">
36
+ * <a href="/help" aria-label="Help"><HelpIcon /></a>
37
+ * </Tooltip>
38
+ * ```
39
+ */
40
+ import { onMount, onDestroy } from 'svelte';
41
+
42
+ interface TooltipChildrenProps {
43
+ disabled: boolean;
44
+ tooltipId: string;
45
+ ariaDisabled: 'true' | 'false';
46
+ ariaDescribedby: string;
47
+ }
48
+
49
+ interface Props {
50
+ text: string;
51
+ position?: 'top' | 'bottom' | 'left' | 'right';
52
+ align?: 'center' | 'left' | 'right';
53
+ showOn?: 'hover' | 'disabled';
54
+ disabled?: boolean;
55
+ showArrow?: boolean;
56
+ minWidth?: string;
57
+ maxWidth?: string;
58
+ tooltipId?: string;
59
+ wrapperClass?: string;
60
+ children?: import('svelte').Snippet<[TooltipChildrenProps]>;
61
+ }
62
+
63
+ let {
64
+ text,
65
+ position = 'top',
66
+ align = 'center',
67
+ showOn = 'hover',
68
+ disabled = false,
69
+ showArrow = false,
70
+ minWidth,
71
+ maxWidth,
72
+ tooltipId = `tooltip-${Math.random().toString(36).substring(2, 9)}`,
73
+ wrapperClass = '',
74
+ children,
75
+ }: Props = $props();
76
+
77
+ let isVisible = $state(false);
78
+ let tooltipElement: HTMLDivElement;
79
+ let wrapperElement: HTMLDivElement;
80
+
81
+ // Gap in px between the trigger and the bubble.
82
+ const GAP = 6;
83
+
84
+ // 'hover' → always available; 'disabled' → only when the trigger is disabled.
85
+ let shouldShow = $derived(
86
+ showOn === 'hover' || (showOn === 'disabled' && disabled)
87
+ );
88
+
89
+ let customStyle = $derived(
90
+ [minWidth && `min-width: ${minWidth}`, maxWidth && `max-width: ${maxWidth}`]
91
+ .filter(Boolean)
92
+ .join('; ') || undefined
93
+ );
94
+
95
+ function updatePosition() {
96
+ if (!tooltipElement || !wrapperElement || typeof window === 'undefined') {
97
+ return;
98
+ }
99
+
100
+ const wrap = wrapperElement.getBoundingClientRect();
101
+ const tip = tooltipElement.getBoundingClientRect();
102
+ const centerX = wrap.left + wrap.width / 2;
103
+ const centerY = wrap.top + wrap.height / 2;
104
+
105
+ // Clear any previously-set coordinates so only the ones we set below apply.
106
+ tooltipElement.style.top = '';
107
+ tooltipElement.style.left = '';
108
+ tooltipElement.style.transform = 'none';
109
+
110
+ let top = 0;
111
+ let left = 0;
112
+
113
+ if (position === 'top' || position === 'bottom') {
114
+ top =
115
+ position === 'top' ? wrap.top - tip.height - GAP : wrap.bottom + GAP;
116
+ if (align === 'left') left = wrap.left;
117
+ else if (align === 'right') left = wrap.right - tip.width;
118
+ else left = centerX - tip.width / 2;
119
+ } else {
120
+ left =
121
+ position === 'left' ? wrap.left - tip.width - GAP : wrap.right + GAP;
122
+ top = centerY - tip.height / 2;
123
+ }
124
+
125
+ tooltipElement.style.top = `${top}px`;
126
+ tooltipElement.style.left = `${left}px`;
127
+
128
+ // Point the arrow at the trigger's center regardless of alignment.
129
+ if (showArrow && arrowElement) {
130
+ if (position === 'top' || position === 'bottom') {
131
+ arrowElement.style.left = `${centerX - left}px`;
132
+ arrowElement.style.top = '';
133
+ } else {
134
+ arrowElement.style.top = `${centerY - top}px`;
135
+ arrowElement.style.left = '';
136
+ }
137
+ }
138
+ }
139
+
140
+ let arrowElement = $state<HTMLDivElement>();
141
+
142
+ $effect(() => {
143
+ if (isVisible && tooltipElement) {
144
+ requestAnimationFrame(updatePosition);
145
+ }
146
+ });
147
+
148
+ onMount(() => {
149
+ if (typeof window !== 'undefined' && tooltipElement) {
150
+ document.body.appendChild(tooltipElement);
151
+ }
152
+ });
153
+
154
+ onDestroy(() => {
155
+ if (typeof window !== 'undefined' && tooltipElement?.parentNode) {
156
+ tooltipElement.parentNode.removeChild(tooltipElement);
157
+ }
158
+ });
159
+
160
+ function open() {
161
+ if (shouldShow) isVisible = true;
162
+ }
163
+ function close() {
164
+ isVisible = false;
165
+ }
166
+ </script>
167
+
168
+ <div
169
+ bind:this={wrapperElement}
170
+ class={`ai-tooltip-trigger ${wrapperClass}`}
171
+ role="none"
172
+ onmouseenter={open}
173
+ onmouseleave={close}
174
+ onfocusin={open}
175
+ onfocusout={close}
176
+ >
177
+ {@render children?.({
178
+ disabled,
179
+ tooltipId,
180
+ ariaDisabled: disabled ? 'true' : 'false',
181
+ ariaDescribedby: shouldShow ? tooltipId : '',
182
+ })}
183
+ </div>
184
+
185
+ <div
186
+ bind:this={tooltipElement}
187
+ id={tooltipId}
188
+ class={`ai-tooltip pos-${position}`}
189
+ class:visible={isVisible && shouldShow}
190
+ style={customStyle}
191
+ role="tooltip"
192
+ aria-live="polite"
193
+ >
194
+ {text}{#if showArrow}<div
195
+ bind:this={arrowElement}
196
+ class="ai-tooltip-arrow"
197
+ aria-hidden="true"
198
+ ></div>{/if}
199
+ </div>
200
+
201
+ <style>
202
+ /*! purgecss start ignore */
203
+ :root,
204
+ :host {
205
+ --ai-tooltip-bg: var(--ai-color-black);
206
+ --ai-tooltip-fg: var(--ai-color-white);
207
+ --ai-tooltip-radius: var(--ai-size-8, 0.625rem);
208
+ --ai-tooltip-padding: var(--ai-size-8, 0.5rem);
209
+ --ai-tooltip-font-size: 0.8125rem;
210
+ --ai-tooltip-font-weight: 500;
211
+ --ai-tooltip-line-height: 1.4;
212
+ --ai-tooltip-shadow: 0 0.25rem 0.375rem rgba(0, 0, 0, 0.3);
213
+ --ai-tooltip-max-width: 21.875rem;
214
+ --ai-tooltip-max-width-mobile: 18rem;
215
+ --ai-tooltip-arrow-size: 0.3125rem;
216
+ }
217
+
218
+ .ai-tooltip-trigger {
219
+ position: relative;
220
+ display: inline-block;
221
+ }
222
+
223
+ .ai-tooltip {
224
+ position: fixed;
225
+ top: 0;
226
+ left: 0;
227
+ z-index: var(--ai-layer-100);
228
+ display: none;
229
+ max-width: var(--ai-tooltip-max-width);
230
+ padding: var(--ai-tooltip-padding);
231
+ border-radius: var(--ai-tooltip-radius);
232
+ background-color: var(--ai-tooltip-bg);
233
+ box-shadow: var(--ai-tooltip-shadow);
234
+ color: var(--ai-tooltip-fg);
235
+ font-size: var(--ai-tooltip-font-size);
236
+ font-weight: var(--ai-tooltip-font-weight);
237
+ line-height: var(--ai-tooltip-line-height);
238
+ pointer-events: none;
239
+ white-space: normal;
240
+ word-wrap: break-word;
241
+ overflow-wrap: break-word;
242
+ }
243
+
244
+ .ai-tooltip.visible {
245
+ display: block;
246
+ }
247
+
248
+ /* Arrow — JS positions it along the trigger-facing edge; here we only place
249
+ * it on the correct side and point it in the right direction. */
250
+ .ai-tooltip-arrow {
251
+ position: absolute;
252
+ width: 0;
253
+ height: 0;
254
+ border: var(--ai-tooltip-arrow-size) solid transparent;
255
+ }
256
+
257
+ .ai-tooltip.pos-top .ai-tooltip-arrow {
258
+ top: 100%;
259
+ transform: translateX(-50%);
260
+ border-top-color: var(--ai-tooltip-bg);
261
+ }
262
+
263
+ .ai-tooltip.pos-bottom .ai-tooltip-arrow {
264
+ bottom: 100%;
265
+ transform: translateX(-50%);
266
+ border-bottom-color: var(--ai-tooltip-bg);
267
+ }
268
+
269
+ .ai-tooltip.pos-left .ai-tooltip-arrow {
270
+ left: 100%;
271
+ transform: translateY(-50%);
272
+ border-left-color: var(--ai-tooltip-bg);
273
+ }
274
+
275
+ .ai-tooltip.pos-right .ai-tooltip-arrow {
276
+ right: 100%;
277
+ transform: translateY(-50%);
278
+ border-right-color: var(--ai-tooltip-bg);
279
+ }
280
+
281
+ /* Mobile: cap width so long messages stay readable. */
282
+ @media (max-width: 48rem) {
283
+ .ai-tooltip {
284
+ max-width: var(--ai-tooltip-max-width-mobile);
285
+ }
286
+ }
287
+ /*! purgecss end ignore */
288
+ </style>
@@ -106,8 +106,8 @@
106
106
  ></path>
107
107
  </svg>
108
108
  </span>) or <strong>https://</strong> means you've safely connected
109
- to the .gov website. Share sensitive information only on official,
110
- secure websites.
109
+ to the .gov website. Share sensitive information only on official, secure
110
+ websites.
111
111
  </p>
112
112
  </div>
113
113
  </div>
@@ -15,6 +15,7 @@ export { default as ApiDocSubNavMenuItem } from './ApiDocSubNavMenuItem.svelte';
15
15
  export { default as PlatformFooter } from './PlatformFooter.svelte';
16
16
  export { default as PlatformFooterMenu } from './PlatformFooterMenu.svelte';
17
17
  export { default as Modal } from './Modal.svelte';
18
+ export { default as Tooltip } from './Tooltip.svelte';
18
19
 
19
20
  // Icon Components
20
21
  export * from './icons/index.js';
@@ -1,7 +1,3 @@
1
- // Note: Do not export remarkPlugins.js or apiDocumentationConfig.js from here.
2
- // These are Astro-specific utilities that should only be imported directly by their specific paths.
3
- // Exporting them would cause bundling issues for Svelte/SvelteKit apps that import from this barrel export.
4
-
5
1
  export * from './navigationControl.js';
6
2
  export * from './copyButtonScript.js';
7
3
  export * from './generateApiNavData.js';