@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.d.mts +584 -0
- package/dist/index.mjs +653 -0
- package/package.json +47 -0
- package/src/color-dark.css +185 -0
- package/src/color-light.css +185 -0
- package/src/color-primitives.css +318 -0
- package/src/default.css +184 -0
- package/src/styles.css +323 -0
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 };
|