@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.
- package/README.md +59 -91
- package/assets/uswds/components/usa-banner.js.mjs +805 -0
- package/assets/uswds/components/usa-banner.js.umd.js +127 -0
- package/package.json +18 -34
- package/src/components/DesktopSideNav.svelte +17 -15
- package/src/components/MobileTopNav.svelte +15 -13
- package/src/components/ProfileMenu.svelte +22 -15
- package/src/components/Tooltip.svelte +288 -0
- package/src/components/UsaBanner.svelte +2 -2
- package/src/components/index.ts +1 -0
- package/src/utils/index.ts +0 -4
- package/static/uswds/js/uswds.js +37 -3
- package/static/uswds/js/uswds.min.js +1 -1
- package/static/uswds/js/uswds.min.js.map +1 -1
- package/static/uswds/styles/styles.css +3 -3
- package/static/uswds/styles/styles.css.map +1 -1
- package/dist/graymatter-ui.js +0 -6025
- package/dist/graymatter-ui.umd.cjs +0 -78
- package/src/astro-config/index.ts +0 -139
- package/src/components/Button.webcomponent.svelte +0 -58
- package/src/components/DesktopHeader.webcomponent.svelte +0 -21
- package/src/components/DesktopSideNav.webcomponent.svelte +0 -42
- package/src/components/Logo.webcomponent.svelte +0 -29
- package/src/components/MobileBottomNav.webcomponent.svelte +0 -32
- package/src/components/MobileSideMenu.webcomponent.svelte +0 -13
- package/src/components/MobileTopNav.webcomponent.svelte +0 -20
- package/src/index.ts +0 -19
- package/src/layouts/BaseLayout.astro +0 -37
- package/src/layouts/DocsLayout.astro +0 -342
- package/src/layouts/GlobalAppLayout.astro +0 -536
- package/src/layouts/GoogleTagManager.astro +0 -17
- package/src/layouts/Head.astro +0 -101
- package/src/layouts/fonts.ts +0 -17
- package/src/pages/.gitkeep +0 -0
- 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
|
-
|
|
109
|
+
to the .gov website. Share sensitive information only on official, secure
|
|
110
|
+
websites.
|
|
111
111
|
</p>
|
|
112
112
|
</div>
|
|
113
113
|
</div>
|
package/src/components/index.ts
CHANGED
|
@@ -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';
|
package/src/utils/index.ts
CHANGED
|
@@ -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';
|