@veracity/vui-poc 0.1.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/dist/index.mjs ADDED
@@ -0,0 +1,653 @@
1
+ import { clsx } from "clsx";
2
+ import { createContext, isValidElement, useContext, useMemo, useState } from "react";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { Button as Button$1, FieldError, Input as Input$1, Label, Link, Text, TextField as TextField$1 } from "react-aria-components";
5
+ //#region src/components/Notification/context.ts
6
+ const NotificationContext = createContext(null);
7
+ /**
8
+ * @param partName Short name of the sub-part (e.g. "Icon", "Dismiss"). Used in
9
+ * the error message when no parent context is found. The message intentionally
10
+ * mentions both `<Notification>` and `<BannerNotification>` since the same
11
+ * parts are re-exported by both components.
12
+ */
13
+ function useNotificationContext(partName) {
14
+ const ctx = useContext(NotificationContext);
15
+ if (!ctx) throw new Error(`<${partName}> must be rendered inside a <Notification> or <BannerNotification>.`);
16
+ return ctx;
17
+ }
18
+ //#endregion
19
+ //#region src/icons/index.tsx
20
+ function Icon({ children, viewBox = "0 0 20 20", width = "1em", height = "1em", fill = "none", ...props }) {
21
+ return /* @__PURE__ */ jsx("svg", {
22
+ xmlns: "http://www.w3.org/2000/svg",
23
+ width,
24
+ height,
25
+ viewBox,
26
+ fill,
27
+ "aria-hidden": "true",
28
+ focusable: "false",
29
+ ...props,
30
+ children
31
+ });
32
+ }
33
+ function CheckCircleIcon(props) {
34
+ return /* @__PURE__ */ jsxs(Icon, {
35
+ ...props,
36
+ children: [/* @__PURE__ */ jsx("circle", {
37
+ cx: "10",
38
+ cy: "10",
39
+ r: "8.25",
40
+ stroke: "currentColor",
41
+ strokeWidth: "1.5"
42
+ }), /* @__PURE__ */ jsx("path", {
43
+ d: "M6.5 10.25L9 12.75L13.75 7.5",
44
+ stroke: "currentColor",
45
+ strokeWidth: "1.5",
46
+ strokeLinecap: "round",
47
+ strokeLinejoin: "round"
48
+ })]
49
+ });
50
+ }
51
+ function WarningTriangleIcon(props) {
52
+ return /* @__PURE__ */ jsxs(Icon, {
53
+ ...props,
54
+ children: [
55
+ /* @__PURE__ */ jsx("path", {
56
+ d: "M10 2.5L18.5 17.25H1.5L10 2.5Z",
57
+ stroke: "currentColor",
58
+ strokeWidth: "1.5",
59
+ strokeLinejoin: "round"
60
+ }),
61
+ /* @__PURE__ */ jsx("path", {
62
+ d: "M10 8V11.5",
63
+ stroke: "currentColor",
64
+ strokeWidth: "1.5",
65
+ strokeLinecap: "round"
66
+ }),
67
+ /* @__PURE__ */ jsx("circle", {
68
+ cx: "10",
69
+ cy: "14.25",
70
+ r: "0.85",
71
+ fill: "currentColor"
72
+ })
73
+ ]
74
+ });
75
+ }
76
+ function InfoCircleIcon(props) {
77
+ return /* @__PURE__ */ jsxs(Icon, {
78
+ ...props,
79
+ children: [
80
+ /* @__PURE__ */ jsx("circle", {
81
+ cx: "10",
82
+ cy: "10",
83
+ r: "8.25",
84
+ stroke: "currentColor",
85
+ strokeWidth: "1.5"
86
+ }),
87
+ /* @__PURE__ */ jsx("path", {
88
+ d: "M10 9V14",
89
+ stroke: "currentColor",
90
+ strokeWidth: "1.5",
91
+ strokeLinecap: "round"
92
+ }),
93
+ /* @__PURE__ */ jsx("circle", {
94
+ cx: "10",
95
+ cy: "6.25",
96
+ r: "0.85",
97
+ fill: "currentColor"
98
+ })
99
+ ]
100
+ });
101
+ }
102
+ function TimesIcon(props) {
103
+ return /* @__PURE__ */ jsx(Icon, {
104
+ ...props,
105
+ viewBox: "0 0 16 16",
106
+ children: /* @__PURE__ */ jsx("path", {
107
+ d: "M12 4L4 12M4 4l8 8",
108
+ stroke: "currentColor",
109
+ strokeWidth: "1.5",
110
+ strokeLinecap: "round"
111
+ })
112
+ });
113
+ }
114
+ function ArrowLeftIcon(props) {
115
+ return /* @__PURE__ */ jsx(Icon, {
116
+ ...props,
117
+ viewBox: "0 0 16 16",
118
+ children: /* @__PURE__ */ jsx("path", {
119
+ d: "M10 12L6 8l4-4",
120
+ stroke: "currentColor",
121
+ strokeWidth: "1.5",
122
+ strokeLinecap: "round",
123
+ strokeLinejoin: "round"
124
+ })
125
+ });
126
+ }
127
+ function ArrowRightIcon(props) {
128
+ return /* @__PURE__ */ jsx(Icon, {
129
+ ...props,
130
+ viewBox: "0 0 16 16",
131
+ children: /* @__PURE__ */ jsx("path", {
132
+ d: "M6 4l4 4-4 4",
133
+ stroke: "currentColor",
134
+ strokeWidth: "1.5",
135
+ strokeLinecap: "round",
136
+ strokeLinejoin: "round"
137
+ })
138
+ });
139
+ }
140
+ /**
141
+ * Indeterminate spinner. Animation is applied via the `vui-spin` class
142
+ * (defined in `styles.css`) rather than inline keyframes so callers can
143
+ * opt out of motion with `prefers-reduced-motion` or scope-level CSS.
144
+ */
145
+ function SpinnerIcon(props) {
146
+ return /* @__PURE__ */ jsxs(Icon, {
147
+ ...props,
148
+ viewBox: "0 0 20 20",
149
+ className: ["vui-spin", props.className].filter(Boolean).join(" "),
150
+ children: [/* @__PURE__ */ jsx("circle", {
151
+ cx: "10",
152
+ cy: "10",
153
+ r: "7.5",
154
+ stroke: "currentColor",
155
+ strokeWidth: "1.5",
156
+ opacity: "0.25"
157
+ }), /* @__PURE__ */ jsx("path", {
158
+ d: "M17.5 10A7.5 7.5 0 0 0 10 2.5",
159
+ stroke: "currentColor",
160
+ strokeWidth: "1.5",
161
+ strokeLinecap: "round"
162
+ })]
163
+ });
164
+ }
165
+ //#endregion
166
+ //#region src/components/Notification/helpers.tsx
167
+ const alertIntents = new Set(["danger", "warning"]);
168
+ function getNotificationRole(intent) {
169
+ return alertIntents.has(intent) ? "alert" : "status";
170
+ }
171
+ /** Default icon for a given notification intent. */
172
+ function defaultIconForIntent(intent) {
173
+ switch (intent) {
174
+ case "success": return /* @__PURE__ */ jsx(CheckCircleIcon, {
175
+ width: "24",
176
+ height: "24"
177
+ });
178
+ case "warning":
179
+ case "danger": return /* @__PURE__ */ jsx(WarningTriangleIcon, {
180
+ width: "24",
181
+ height: "24"
182
+ });
183
+ default: return /* @__PURE__ */ jsx(InfoCircleIcon, {
184
+ width: "24",
185
+ height: "24"
186
+ });
187
+ }
188
+ }
189
+ //#endregion
190
+ //#region src/utils/mergeClassNames.ts
191
+ function mergeClassNames(base, user) {
192
+ return (values) => {
193
+ const resolved = typeof user === "function" ? user(values) : user;
194
+ return clsx(values.defaultClassName, base, resolved);
195
+ };
196
+ }
197
+ //#endregion
198
+ //#region src/components/shared/buttonStyles.ts
199
+ const intentVariantClasses = {
200
+ brand: {
201
+ primary: "border-action-brand-solid bg-action-brand-solid text-action-brand-solid-inverse enabled:hover:border-action-brand-solid-hover enabled:hover:bg-action-brand-solid-hover active:border-action-brand-solid-pressed active:bg-action-brand-solid-pressed data-[pressed]:border-action-brand-solid-pressed data-[pressed]:bg-action-brand-solid-pressed data-[active]:border-action-brand-solid-pressed data-[active]:bg-action-brand-solid-pressed",
202
+ secondary: "border-action-brand-solid bg-transparent text-action-brand-solid enabled:hover:bg-action-brand-subtle-hover active:bg-action-brand-subtle-pressed data-[pressed]:bg-action-brand-subtle-pressed data-[active]:bg-action-brand-subtle-pressed",
203
+ tertiary: "border-transparent bg-transparent text-action-brand-solid enabled:hover:bg-action-brand-subtle-hover active:bg-action-brand-subtle-pressed data-[pressed]:bg-action-brand-subtle-pressed data-[active]:bg-action-brand-subtle-pressed"
204
+ },
205
+ contrast: {
206
+ primary: "border-action-contrast-solid bg-action-contrast-solid text-action-contrast-solid-inverse enabled:hover:border-action-contrast-solid-hover enabled:hover:bg-action-contrast-solid-hover active:border-action-contrast-solid-pressed active:bg-action-contrast-solid-pressed data-[pressed]:border-action-contrast-solid-pressed data-[pressed]:bg-action-contrast-solid-pressed data-[active]:border-action-contrast-solid-pressed data-[active]:bg-action-contrast-solid-pressed",
207
+ secondary: "border-action-contrast-solid bg-transparent text-action-contrast-solid enabled:hover:bg-action-contrast-subtle-hover active:bg-action-contrast-subtle-pressed data-[pressed]:bg-action-contrast-subtle-pressed data-[active]:bg-action-contrast-subtle-pressed",
208
+ tertiary: "border-transparent bg-transparent text-action-contrast-solid enabled:hover:bg-action-contrast-subtle-hover active:bg-action-contrast-subtle-pressed data-[pressed]:bg-action-contrast-subtle-pressed data-[active]:bg-action-contrast-subtle-pressed"
209
+ },
210
+ success: {
211
+ primary: "border-action-success-solid bg-action-success-solid text-action-success-solid-inverse enabled:hover:border-action-success-solid-hover enabled:hover:bg-action-success-solid-hover active:border-action-success-solid-pressed active:bg-action-success-solid-pressed data-[pressed]:border-action-success-solid-pressed data-[pressed]:bg-action-success-solid-pressed data-[active]:border-action-success-solid-pressed data-[active]:bg-action-success-solid-pressed",
212
+ secondary: "border-action-success-solid bg-transparent text-action-success-solid enabled:hover:bg-action-success-subtle-hover active:bg-action-success-subtle-pressed data-[pressed]:bg-action-success-subtle-pressed data-[active]:bg-action-success-subtle-pressed",
213
+ tertiary: "border-transparent bg-transparent text-action-success-solid enabled:hover:bg-action-success-subtle-hover active:bg-action-success-subtle-pressed data-[pressed]:bg-action-success-subtle-pressed data-[active]:bg-action-success-subtle-pressed"
214
+ },
215
+ danger: {
216
+ primary: "border-action-danger-solid bg-action-danger-solid text-action-danger-solid-inverse enabled:hover:border-action-danger-solid-hover enabled:hover:bg-action-danger-solid-hover active:border-action-danger-solid-pressed active:bg-action-danger-solid-pressed data-[pressed]:border-action-danger-solid-pressed data-[pressed]:bg-action-danger-solid-pressed data-[active]:border-action-danger-solid-pressed data-[active]:bg-action-danger-solid-pressed",
217
+ secondary: "border-action-danger-solid bg-transparent text-action-danger-solid enabled:hover:bg-action-danger-subtle-hover active:bg-action-danger-subtle-pressed data-[pressed]:bg-action-danger-subtle-pressed data-[active]:bg-action-danger-subtle-pressed",
218
+ tertiary: "border-transparent bg-transparent text-action-danger-solid enabled:hover:bg-action-danger-subtle-hover active:bg-action-danger-subtle-pressed data-[pressed]:bg-action-danger-subtle-pressed data-[active]:bg-action-danger-subtle-pressed"
219
+ }
220
+ };
221
+ const disabledVariantClasses = {
222
+ primary: "disabled:border-action-disabled-background disabled:bg-action-disabled-background disabled:text-action-disabled-foreground data-[disabled]:border-action-disabled-background data-[disabled]:bg-action-disabled-background data-[disabled]:text-action-disabled-foreground",
223
+ secondary: "disabled:border-action-disabled-background disabled:bg-transparent disabled:text-action-disabled-foreground data-[disabled]:border-action-disabled-background data-[disabled]:bg-transparent data-[disabled]:text-action-disabled-foreground",
224
+ tertiary: "disabled:border-transparent disabled:bg-transparent disabled:text-action-disabled-foreground data-[disabled]:border-transparent data-[disabled]:bg-transparent data-[disabled]:text-action-disabled-foreground"
225
+ };
226
+ //#endregion
227
+ //#region src/components/Button/Button.tsx
228
+ const sizeClasses$2 = {
229
+ sm: "h-6 px-3 py-1 text-xs leading-[1.2]",
230
+ md: "h-8 px-4 py-[7px] text-sm leading-[1.2]",
231
+ lg: "h-10 px-4 py-[9px] text-base leading-[1.2]",
232
+ xl: "h-12 px-4 py-3 text-lg leading-[1.2]"
233
+ };
234
+ function Button(props) {
235
+ const { intent = "brand", isActive, variant = "primary", size = "lg", className, startIcon, endIcon, children, ...rest } = props;
236
+ const classes = [
237
+ "vui-button inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-none border border-solid font-sans font-medium no-underline transition-colors duration-150 ease-in-out",
238
+ "cursor-pointer select-none outline-none outline-offset-0",
239
+ "disabled:cursor-not-allowed data-[disabled]:cursor-not-allowed data-[pending]:cursor-progress",
240
+ sizeClasses$2[size],
241
+ intentVariantClasses[intent][variant],
242
+ disabledVariantClasses[variant]
243
+ ].join(" ");
244
+ const renderContent = (resolvedChildren) => /* @__PURE__ */ jsxs(Fragment, { children: [
245
+ startIcon ? /* @__PURE__ */ jsx("span", {
246
+ "aria-hidden": "true",
247
+ className: "vui-button__icon inline-flex shrink-0",
248
+ children: startIcon
249
+ }) : null,
250
+ resolvedChildren,
251
+ endIcon ? /* @__PURE__ */ jsx("span", {
252
+ "aria-hidden": "true",
253
+ className: "vui-button__icon inline-flex shrink-0",
254
+ children: endIcon
255
+ }) : null
256
+ ] });
257
+ const commonProps = {
258
+ "data-active": isActive || void 0,
259
+ "data-intent": intent,
260
+ "data-variant": variant,
261
+ className: mergeClassNames(classes, className)
262
+ };
263
+ function wrapChildren(c) {
264
+ return typeof c === "function" ? (values) => renderContent(c(values)) : renderContent(c);
265
+ }
266
+ if (props.href != null) return /* @__PURE__ */ jsx(Link, {
267
+ ...rest,
268
+ ...commonProps,
269
+ children: wrapChildren(children)
270
+ });
271
+ return /* @__PURE__ */ jsx(Button$1, {
272
+ ...rest,
273
+ ...commonProps,
274
+ children: wrapChildren(children)
275
+ });
276
+ }
277
+ //#endregion
278
+ //#region src/utils/warnDev.ts
279
+ const warnedMessages = /* @__PURE__ */ new Set();
280
+ function isDevelopment() {
281
+ const meta = import.meta;
282
+ if (typeof meta.env?.DEV === "boolean") return meta.env.DEV;
283
+ if (typeof process !== "undefined") return process.env?.NODE_ENV !== "production";
284
+ return false;
285
+ }
286
+ function warnDev(condition, message) {
287
+ if (!condition || !isDevelopment() || warnedMessages.has(message)) return;
288
+ warnedMessages.add(message);
289
+ console.warn(message);
290
+ }
291
+ //#endregion
292
+ //#region src/components/IconButton/IconButton.tsx
293
+ const sizeClasses$1 = {
294
+ sm: "size-6",
295
+ md: "size-8",
296
+ lg: "size-10",
297
+ xl: "size-12"
298
+ };
299
+ function IconButton(props) {
300
+ const { intent = "brand", isActive, isElevated, isRound, variant = "primary", size = "lg", className, icon, ...rest } = props;
301
+ warnDev(!props["aria-label"] && !props["aria-labelledby"], "[vui] IconButton requires an accessible name. Provide `aria-label` or `aria-labelledby`.");
302
+ const classes = [
303
+ "vui-icon-button inline-flex items-center justify-center border border-solid font-sans no-underline transition-colors duration-150 ease-in-out",
304
+ isActive ? "cursor-default" : "cursor-pointer",
305
+ "select-none outline-none outline-offset-0",
306
+ "disabled:cursor-not-allowed data-[disabled]:cursor-not-allowed data-[pending]:cursor-progress",
307
+ isRound ? "rounded-full" : "rounded-none",
308
+ isElevated ? "shadow-md" : "",
309
+ sizeClasses$1[size],
310
+ intentVariantClasses[intent][variant],
311
+ disabledVariantClasses[variant]
312
+ ].join(" ");
313
+ const iconContent = /* @__PURE__ */ jsx("span", {
314
+ "aria-hidden": "true",
315
+ className: "vui-icon-button__icon inline-flex",
316
+ children: icon
317
+ });
318
+ if (props.href != null) return /* @__PURE__ */ jsx(Link, {
319
+ ...rest,
320
+ "data-active": isActive || void 0,
321
+ "data-intent": intent,
322
+ className: mergeClassNames(classes, className),
323
+ children: iconContent
324
+ });
325
+ return /* @__PURE__ */ jsx(Button$1, {
326
+ ...rest,
327
+ "data-active": isActive || void 0,
328
+ "data-intent": intent,
329
+ className: mergeClassNames(classes, className),
330
+ children: iconContent
331
+ });
332
+ }
333
+ //#endregion
334
+ //#region src/components/IconButton/presets.tsx
335
+ /**
336
+ * Build a preset `IconButton` with a fixed icon and default `aria-label`.
337
+ * Both `variant` and the label remain overridable per call site; only
338
+ * `icon` is locked to the preset's glyph. Used internally to define the
339
+ * `BackButton` / `DismissButton` presets without duplicating prop
340
+ * forwarding and the discriminated-union cast.
341
+ */
342
+ function createIconPreset(icon, defaultAriaLabel) {
343
+ return function IconPreset({ variant = "tertiary", "aria-label": ariaLabel = defaultAriaLabel, ...props }) {
344
+ return /* @__PURE__ */ jsx(IconButton, {
345
+ variant,
346
+ "aria-label": ariaLabel,
347
+ ...props,
348
+ icon
349
+ });
350
+ };
351
+ }
352
+ const BackButton = createIconPreset(/* @__PURE__ */ jsx(ArrowLeftIcon, {}), "Go back");
353
+ const DismissButton = createIconPreset(/* @__PURE__ */ jsx(TimesIcon, {}), "Dismiss");
354
+ //#endregion
355
+ //#region src/components/Notification/parts.tsx
356
+ function NotificationIcon({ children, className }) {
357
+ const { intent, isLoading } = useNotificationContext("Icon");
358
+ const content = isLoading ? /* @__PURE__ */ jsx(SpinnerIcon, {
359
+ width: "24",
360
+ height: "24"
361
+ }) : children ?? defaultIconForIntent(intent);
362
+ return /* @__PURE__ */ jsx("span", {
363
+ "aria-hidden": "true",
364
+ className: clsx("vui-notification-icon shrink-0", className),
365
+ children: content
366
+ });
367
+ }
368
+ function NotificationTitle({ children, className, as: Component = "p" }) {
369
+ const { size } = useNotificationContext("Title");
370
+ return /* @__PURE__ */ jsx(Component, {
371
+ className: clsx("vui-notification-title font-semibold leading-tight", size === "sm" ? "mb-0.5" : "mb-1", className),
372
+ children
373
+ });
374
+ }
375
+ function NotificationBody({ children, className }) {
376
+ return /* @__PURE__ */ jsx("div", {
377
+ className: clsx("vui-notification-body leading-normal", className),
378
+ children
379
+ });
380
+ }
381
+ function NotificationContent({ children, className, ...rest }) {
382
+ return /* @__PURE__ */ jsx("div", {
383
+ ...rest,
384
+ className: clsx("vui-notification-content flex min-w-0 flex-1 flex-col", className),
385
+ children
386
+ });
387
+ }
388
+ function NotificationAction({ variant = "primary", size: sizeProp, style, className, ...rest }) {
389
+ const { size: ctxSize } = useNotificationContext("Action");
390
+ const accentStyle = {
391
+ backgroundColor: "var(--vui-notification-accent)",
392
+ borderColor: "var(--vui-notification-accent)",
393
+ color: "var(--vui-notification-bg)",
394
+ ...style
395
+ };
396
+ return /* @__PURE__ */ jsx(Button, {
397
+ ...rest,
398
+ variant,
399
+ size: sizeProp ?? ctxSize,
400
+ className: clsx("vui-notification-action", className),
401
+ style: accentStyle
402
+ });
403
+ }
404
+ function NotificationActions({ children, className, ...rest }) {
405
+ const { size } = useNotificationContext("Actions");
406
+ const marginClass = size === "sm" ? "mt-2" : "mt-3";
407
+ return /* @__PURE__ */ jsx("div", {
408
+ ...rest,
409
+ className: clsx("vui-notification-actions", marginClass, className),
410
+ children
411
+ });
412
+ }
413
+ function NotificationDismiss({ className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy }) {
414
+ const { onClose, align } = useNotificationContext("Dismiss");
415
+ return /* @__PURE__ */ jsx("span", {
416
+ className: clsx("vui-notification-dismiss-wrap flex items-center", align === "top" ? "-mt-2 -mr-2" : "-mr-2", className),
417
+ children: /* @__PURE__ */ jsx(IconButton, {
418
+ "aria-label": ariaLabelledBy ? void 0 : ariaLabel ?? "Close",
419
+ "aria-labelledby": ariaLabelledBy,
420
+ variant: "tertiary",
421
+ size: "md",
422
+ onPress: onClose,
423
+ className: "vui-notification-dismiss",
424
+ icon: /* @__PURE__ */ jsx(TimesIcon, {
425
+ width: "16",
426
+ height: "16"
427
+ })
428
+ })
429
+ });
430
+ }
431
+ //#endregion
432
+ //#region src/components/Notification/Notification.tsx
433
+ /** Discriminates the object-shape `action` from an arbitrary ReactNode. */
434
+ function isActionObject(a) {
435
+ return typeof a === "object" && a !== null && !isValidElement(a) && "label" in a && "onClick" in a && typeof a.onClick === "function";
436
+ }
437
+ const variantClasses = {
438
+ inline: "flex w-fit max-w-full gap-4 border p-4",
439
+ banner: "vui-banner-notification relative flex w-full items-start gap-4 p-4 shadow-[0_10px_20px_rgba(0,0,0,0.15),0_0_1px_rgba(0,0,0,0.05)]"
440
+ };
441
+ /**
442
+ * Size only affects text and action button sizing inside the content slot.
443
+ * Container padding/gap, icon, and dismiss button are intentionally *not*
444
+ * size-aware so all notifications share a consistent shell footprint.
445
+ */
446
+ const sizeClasses = {
447
+ md: "",
448
+ sm: "text-sm"
449
+ };
450
+ function NotificationRoot({ intent = "info", align = "top", size = "md", variant = "inline", isLoading = false, role: roleProp, onClose, className, children, title, body, text, action, icon, showDismissButton = true, ...rest }) {
451
+ const derivedRole = getNotificationRole(intent);
452
+ const resolvedRole = roleProp === "none" ? void 0 : roleProp ?? derivedRole;
453
+ const [internallyDismissed, setInternallyDismissed] = useState(false);
454
+ if (internallyDismissed) return null;
455
+ const handleClose = () => {
456
+ if (onClose) onClose();
457
+ else setInternallyDismissed(true);
458
+ };
459
+ const resolvedBody = body ?? text;
460
+ const renderedAction = isActionObject(action) ? /* @__PURE__ */ jsx(NotificationAction, {
461
+ onPress: action.onClick,
462
+ children: action.label
463
+ }) : action;
464
+ return /* @__PURE__ */ jsx(NotificationContext.Provider, {
465
+ value: {
466
+ intent,
467
+ align,
468
+ size,
469
+ onClose: handleClose,
470
+ isLoading
471
+ },
472
+ children: /* @__PURE__ */ jsx("div", {
473
+ ...rest,
474
+ role: resolvedRole,
475
+ "aria-atomic": "true",
476
+ "data-intent": intent,
477
+ "data-align": align,
478
+ "data-size": size,
479
+ "data-loading": isLoading || void 0,
480
+ className: clsx("vui-notification", variantClasses[variant], sizeClasses[size], align === "center" ? "items-center" : "items-start", className),
481
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
482
+ icon === false ? null : /* @__PURE__ */ jsx(NotificationIcon, { children: icon }),
483
+ (title || resolvedBody) && /* @__PURE__ */ jsxs(NotificationContent, { children: [
484
+ title ? /* @__PURE__ */ jsx(NotificationTitle, { children: title }) : null,
485
+ resolvedBody ? /* @__PURE__ */ jsx(NotificationBody, { children: resolvedBody }) : null,
486
+ renderedAction ? /* @__PURE__ */ jsx(NotificationActions, { children: renderedAction }) : null
487
+ ] }),
488
+ !title && !resolvedBody && renderedAction ? /* @__PURE__ */ jsx(NotificationActions, { children: renderedAction }) : null,
489
+ showDismissButton ? /* @__PURE__ */ jsx(NotificationDismiss, {}) : null
490
+ ] })
491
+ })
492
+ });
493
+ }
494
+ /**
495
+ * Compound API. Each sub-component reads from `NotificationContext` and is
496
+ * meaningless outside a `<Notification>` (or `<BannerNotification>`) parent.
497
+ */
498
+ const Notification = Object.assign(NotificationRoot, {
499
+ Icon: NotificationIcon,
500
+ Title: NotificationTitle,
501
+ Body: NotificationBody,
502
+ Content: NotificationContent,
503
+ Action: NotificationAction,
504
+ Actions: NotificationActions,
505
+ Dismiss: NotificationDismiss
506
+ });
507
+ //#endregion
508
+ //#region src/components/BannerNotification/BannerNotification.tsx
509
+ function BannerNotificationRoot(props) {
510
+ return /* @__PURE__ */ jsx(Notification, {
511
+ ...props,
512
+ variant: "banner"
513
+ });
514
+ }
515
+ const BannerNotification = Object.assign(BannerNotificationRoot, {
516
+ Icon: Notification.Icon,
517
+ Title: Notification.Title,
518
+ Body: Notification.Body,
519
+ Content: Notification.Content,
520
+ Actions: Notification.Actions,
521
+ Dismiss: Notification.Dismiss
522
+ });
523
+ //#endregion
524
+ //#region src/components/Input/context.ts
525
+ const TextFieldContext = createContext(null);
526
+ //#endregion
527
+ //#region src/components/Input/parts.tsx
528
+ function TextFieldLabel({ className, ...props }) {
529
+ return /* @__PURE__ */ jsx(Label, {
530
+ ...props,
531
+ className: clsx("vui-text-field-label mb-1 inline-block font-bold", className)
532
+ });
533
+ }
534
+ function TextFieldInput({ className, ...props }) {
535
+ return /* @__PURE__ */ jsx(Input$1, {
536
+ ...props,
537
+ className: mergeClassNames("vui-text-field-input block w-full rounded-none border border-solid bg-control-background-default px-1 outline-none", className)
538
+ });
539
+ }
540
+ function TextFieldDescription({ className, ...props }) {
541
+ return /* @__PURE__ */ jsx(Text, {
542
+ ...props,
543
+ slot: "description",
544
+ className: clsx("vui-text-field-description block", className)
545
+ });
546
+ }
547
+ function TextFieldError({ className, ...props }) {
548
+ return /* @__PURE__ */ jsx(FieldError, {
549
+ ...props,
550
+ className: mergeClassNames("vui-text-field-error block", className)
551
+ });
552
+ }
553
+ function TextFieldCounter({ className }) {
554
+ const context = useContext(TextFieldContext);
555
+ if (!context?.showCount) return null;
556
+ return /* @__PURE__ */ jsxs("span", {
557
+ className: clsx("vui-text-field-counter tabular-nums", typeof context.maxLength === "number" && context.count > context.maxLength && "vui-text-field-counter--danger", className),
558
+ children: [context.count, typeof context.maxLength === "number" ? ` / ${context.maxLength}` : null]
559
+ });
560
+ }
561
+ /**
562
+ * Decorative icon for an input. Render *inside* `TextField.InputGroup`,
563
+ * either before or after `TextField.Input`.
564
+ */
565
+ function TextFieldIcon({ position = "start", children, className }) {
566
+ return /* @__PURE__ */ jsx("span", {
567
+ "aria-hidden": "true",
568
+ "data-position": position,
569
+ className: clsx("vui-text-field-icon pointer-events-none inline-flex shrink-0 items-center justify-center", position === "start" ? "vui-text-field-icon--start" : "vui-text-field-icon--end", className),
570
+ children
571
+ });
572
+ }
573
+ /**
574
+ * Wrapper around `TextField.Input` that lays out adjacent
575
+ * `TextField.Icon` siblings in a single row.
576
+ */
577
+ function TextFieldInputGroup({ children, className }) {
578
+ return /* @__PURE__ */ jsx("div", {
579
+ className: clsx("vui-text-field-input-group relative flex w-full items-stretch gap-2", className),
580
+ children
581
+ });
582
+ }
583
+ //#endregion
584
+ //#region src/components/Input/TextField.tsx
585
+ function getValueLength(value) {
586
+ return value == null ? 0 : JSON.stringify(value).length;
587
+ }
588
+ function TextFieldRoot({ className, defaultValue, intent = "brand", maxLength, onChange, showCount = false, size = "lg", value, ...props }) {
589
+ const isControlled = value !== void 0;
590
+ const [uncontrolledCount, setUncontrolledCount] = useState(() => getValueLength(defaultValue ?? value));
591
+ const count = isControlled ? getValueLength(value) : uncontrolledCount;
592
+ const context = useMemo(() => ({
593
+ count,
594
+ intent,
595
+ maxLength,
596
+ showCount,
597
+ size
598
+ }), [
599
+ count,
600
+ intent,
601
+ maxLength,
602
+ showCount,
603
+ size
604
+ ]);
605
+ return /* @__PURE__ */ jsx(TextFieldContext.Provider, {
606
+ value: context,
607
+ children: /* @__PURE__ */ jsx(TextField$1, {
608
+ ...props,
609
+ "data-intent": intent,
610
+ "data-size": size,
611
+ defaultValue,
612
+ maxLength,
613
+ onChange: (nextValue) => {
614
+ if (!isControlled) setUncontrolledCount(getValueLength(nextValue));
615
+ onChange?.(nextValue);
616
+ },
617
+ value,
618
+ className: mergeClassNames("vui-text-field grid w-full gap-1 font-sans", className)
619
+ })
620
+ });
621
+ }
622
+ const TextField = Object.assign(TextFieldRoot, {
623
+ Counter: TextFieldCounter,
624
+ Description: TextFieldDescription,
625
+ Error: TextFieldError,
626
+ Icon: TextFieldIcon,
627
+ Input: TextFieldInput,
628
+ InputGroup: TextFieldInputGroup,
629
+ Label: TextFieldLabel
630
+ });
631
+ //#endregion
632
+ //#region src/components/Input/Input.tsx
633
+ function Input({ description, errorMessage, isInvalid, label, placeholder, showCount, ...props }) {
634
+ warnDev(!Boolean(label || props["aria-label"] || props["aria-labelledby"]), "Input requires an accessible name. Provide label, aria-label, or aria-labelledby.");
635
+ return /* @__PURE__ */ jsxs(TextField, {
636
+ ...props,
637
+ isInvalid: isInvalid ?? Boolean(errorMessage),
638
+ showCount,
639
+ children: [
640
+ label ? /* @__PURE__ */ jsx(TextField.Label, { children: label }) : null,
641
+ /* @__PURE__ */ jsx(TextField.Input, { placeholder }),
642
+ description || errorMessage || showCount ? /* @__PURE__ */ jsxs("div", {
643
+ className: "vui-text-field-footer",
644
+ children: [/* @__PURE__ */ jsx("div", {
645
+ className: "vui-text-field-message",
646
+ children: errorMessage ? /* @__PURE__ */ jsx(TextField.Error, { children: errorMessage }) : description ? /* @__PURE__ */ jsx(TextField.Description, { children: description }) : null
647
+ }), /* @__PURE__ */ jsx(TextField.Counter, {})]
648
+ }) : null
649
+ ]
650
+ });
651
+ }
652
+ //#endregion
653
+ export { ArrowLeftIcon, ArrowRightIcon, BackButton, BannerNotification, Button, CheckCircleIcon, DismissButton, IconButton, InfoCircleIcon, Input, Notification, SpinnerIcon, TextField, TimesIcon, WarningTriangleIcon };