trc-ui 0.0.0-stage → 0.5.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.js ADDED
@@ -0,0 +1,2623 @@
1
+ "use client";
2
+
3
+ // src/utils/cx.ts
4
+ function cx(...classes) {
5
+ return classes.filter(Boolean).join(" ");
6
+ }
7
+
8
+ // src/components/typography.tsx
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ function AccentText({ text, accentLast = 0 }) {
11
+ if (text.includes("*")) {
12
+ return /* @__PURE__ */ jsx(Fragment, { children: text.split("*").map(
13
+ (part, i) => i % 2 === 1 ? /* @__PURE__ */ jsx("span", { className: "trc-accent", children: part }, i) : part
14
+ ) });
15
+ }
16
+ const words = text.split(" ");
17
+ if (!accentLast || words.length < 2) return /* @__PURE__ */ jsx(Fragment, { children: text });
18
+ const n = Math.min(accentLast, words.length - 1);
19
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
20
+ words.slice(0, -n).join(" "),
21
+ " ",
22
+ /* @__PURE__ */ jsx("span", { className: "trc-accent", children: words.slice(-n).join(" ") })
23
+ ] });
24
+ }
25
+ var plainText = (text) => text.replaceAll("*", "");
26
+ function Heading({ as: Tag2 = "h2", size: size2 = "xl", accentLast, className, children, ...props }) {
27
+ return /* @__PURE__ */ jsx(Tag2, { className: cx("trc-display", `trc-heading--${size2}`, className), ...props, children: typeof children === "string" ? /* @__PURE__ */ jsx(AccentText, { text: children, accentLast }) : children });
28
+ }
29
+ function Text({
30
+ as,
31
+ size: size2 = "md",
32
+ tone = "muted",
33
+ className,
34
+ ...props
35
+ }) {
36
+ const Tag2 = as ?? "p";
37
+ return /* @__PURE__ */ jsx(Tag2, { className: cx("trc-text", `trc-text--${size2}`, `trc-text--${tone}`, className), ...props });
38
+ }
39
+ function StepNumber({ n, size: size2 = "md", className }) {
40
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cx("trc-step-number", size2 === "sm" && "trc-step-number--sm", className), children: String(n).padStart(2, "0") });
41
+ }
42
+
43
+ // src/components/pill.tsx
44
+ import { forwardRef } from "react";
45
+
46
+ // src/utils/icons.tsx
47
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
48
+ var base = {
49
+ xmlns: "http://www.w3.org/2000/svg",
50
+ viewBox: "0 0 24 24",
51
+ fill: "none",
52
+ stroke: "currentColor",
53
+ strokeWidth: 2,
54
+ strokeLinecap: "round",
55
+ strokeLinejoin: "round",
56
+ "aria-hidden": true
57
+ };
58
+ var ArrowRight = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "M5 12h14M13 6l6 6-6 6" }) });
59
+ var ArrowLeft = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "M19 12H5M11 18l-6-6 6-6" }) });
60
+ var Spinner = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "M21 12a9 9 0 1 1-6.22-8.56" }) });
61
+ var ChevronDown = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "m6 9 6 6 6-6" }) });
62
+ var Check = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "M20 6 9 17l-5-5" }) });
63
+ var Menu = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "M4 7h16M4 12h16M4 17h16" }) });
64
+ var Close = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "M18 6 6 18M6 6l12 12" }) });
65
+ var ArrowUp = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "M12 19V5M6 11l6-6 6 6" }) });
66
+ var Calendar = (props) => /* @__PURE__ */ jsxs2("svg", { ...base, ...props, children: [
67
+ /* @__PURE__ */ jsx2("rect", { x: "3", y: "5", width: "18", height: "16", rx: "3" }),
68
+ /* @__PURE__ */ jsx2("path", { d: "M3 10h18M8 3v4M16 3v4" })
69
+ ] });
70
+ var ChevronLeft = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "m15 6-6 6 6 6" }) });
71
+ var ChevronRight = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "m9 6 6 6-6 6" }) });
72
+
73
+ // src/components/pill.tsx
74
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
75
+ var pillClass = ({ solid, size: size2 }, className) => cx("trc-pill", size2 === "sm" && "trc-pill--sm", solid && "trc-pill--solid", className);
76
+ function PillInner({ icon, children }) {
77
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
78
+ /* @__PURE__ */ jsx3("span", { "aria-hidden": true, className: "trc-pill__fill" }),
79
+ /* @__PURE__ */ jsx3("span", { "aria-hidden": true, className: "trc-pill__icon", children: icon ?? /* @__PURE__ */ jsx3(ArrowRight, {}) }),
80
+ /* @__PURE__ */ jsx3("span", { className: "trc-pill__label", children })
81
+ ] });
82
+ }
83
+ var PillButton = forwardRef(function PillButton2({ solid, size: size2 = "md", icon, loading = false, disabled, type = "button", className, children, ...props }, ref) {
84
+ return /* @__PURE__ */ jsx3(
85
+ "button",
86
+ {
87
+ ref,
88
+ type,
89
+ disabled: disabled || loading,
90
+ "aria-busy": loading || void 0,
91
+ className: pillClass({ solid, size: size2 }, className),
92
+ ...props,
93
+ children: /* @__PURE__ */ jsx3(PillInner, { icon: loading ? /* @__PURE__ */ jsx3(Spinner, { className: "trc-spin" }) : icon, children })
94
+ }
95
+ );
96
+ });
97
+ var PillLink = forwardRef(function PillLink2({ as, solid, size: size2 = "md", icon, className, children, ...props }, ref) {
98
+ const Tag2 = as ?? "a";
99
+ return /* @__PURE__ */ jsx3(Tag2, { ref, className: pillClass({ solid, size: size2 }, className), ...props, children: /* @__PURE__ */ jsx3(PillInner, { icon, children }) });
100
+ });
101
+
102
+ // src/components/surfaces.tsx
103
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
104
+ function Container({
105
+ as,
106
+ size: size2 = "default",
107
+ className,
108
+ ...props
109
+ }) {
110
+ const Tag2 = as ?? "div";
111
+ return /* @__PURE__ */ jsx4(Tag2, { className: cx("trc-container", size2 !== "default" && `trc-container--${size2}`, className), ...props });
112
+ }
113
+ function Section({
114
+ as,
115
+ spacing = "md",
116
+ inset = false,
117
+ className,
118
+ children,
119
+ ...props
120
+ }) {
121
+ const Tag2 = as ?? "section";
122
+ if (inset) {
123
+ return /* @__PURE__ */ jsx4(Tag2, { className: cx("trc-section-inset", className), ...props, children: /* @__PURE__ */ jsx4("div", { className: cx("trc-section-inset__card", spacing !== "none" && `trc-section--${spacing}`), children }) });
124
+ }
125
+ return /* @__PURE__ */ jsx4(Tag2, { className: cx(spacing !== "none" && `trc-section--${spacing}`, className), ...props, children });
126
+ }
127
+ function Card({
128
+ as,
129
+ tone = "default",
130
+ padding = "md",
131
+ interactive = false,
132
+ watermark,
133
+ className,
134
+ children,
135
+ ...props
136
+ }) {
137
+ const Tag2 = as ?? "div";
138
+ return /* @__PURE__ */ jsxs4(
139
+ Tag2,
140
+ {
141
+ className: cx(
142
+ "trc-card",
143
+ tone === "alt" && "trc-card--alt",
144
+ padding !== "none" && `trc-card--pad-${padding}`,
145
+ interactive && "trc-card--interactive",
146
+ !!watermark && "trc-card--watermark",
147
+ className
148
+ ),
149
+ ...props,
150
+ children: [
151
+ !!watermark && /* @__PURE__ */ jsx4("span", { "aria-hidden": true, className: "trc-card__watermark", children: watermark }),
152
+ children
153
+ ]
154
+ }
155
+ );
156
+ }
157
+
158
+ // src/components/field.tsx
159
+ import { createContext, forwardRef as forwardRef2, useContext, useId } from "react";
160
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
161
+ var FieldContext = createContext(null);
162
+ function Field({ label, hint, error, required = false, id, className, children }) {
163
+ const generated = useId();
164
+ const fieldId = id ?? generated;
165
+ const messageId = `${fieldId}-message`;
166
+ const message = error ?? hint;
167
+ return /* @__PURE__ */ jsx5(
168
+ FieldContext.Provider,
169
+ {
170
+ value: { id: fieldId, describedBy: message ? messageId : void 0, invalid: error != null, required },
171
+ children: /* @__PURE__ */ jsxs5("div", { className: cx("trc-field", className), children: [
172
+ /* @__PURE__ */ jsxs5("label", { htmlFor: fieldId, className: "trc-field__label", children: [
173
+ label,
174
+ required && /* @__PURE__ */ jsxs5("span", { "aria-hidden": true, className: "trc-field__required", children: [
175
+ " ",
176
+ "*"
177
+ ] })
178
+ ] }),
179
+ children,
180
+ message != null && /* @__PURE__ */ jsx5("p", { id: messageId, className: cx("trc-field__message", error != null && "trc-field__message--error"), children: message })
181
+ ] })
182
+ }
183
+ );
184
+ }
185
+ function useFieldProps(props) {
186
+ const field = useContext(FieldContext);
187
+ const invalid = props.invalid ?? field?.invalid ?? false;
188
+ return {
189
+ id: props.id ?? field?.id,
190
+ required: props.required ?? field?.required,
191
+ "aria-describedby": cx(field?.describedBy, props["aria-describedby"]) || void 0,
192
+ "aria-invalid": invalid || void 0
193
+ };
194
+ }
195
+ var Input = forwardRef2(function Input2({ invalid, className, ...props }, ref) {
196
+ const a11y = useFieldProps({ ...props, invalid });
197
+ return /* @__PURE__ */ jsx5("input", { ref, className: cx("trc-control", className), ...props, ...a11y });
198
+ });
199
+ var Textarea = forwardRef2(function Textarea2({ invalid, rows = 4, className, ...props }, ref) {
200
+ const a11y = useFieldProps({ ...props, invalid });
201
+ return /* @__PURE__ */ jsx5(
202
+ "textarea",
203
+ {
204
+ ref,
205
+ rows,
206
+ className: cx("trc-control", "trc-control--textarea", className),
207
+ ...props,
208
+ ...a11y
209
+ }
210
+ );
211
+ });
212
+ var Select = forwardRef2(function Select2({ invalid, placeholder, defaultValue, className, children, ...props }, ref) {
213
+ const a11y = useFieldProps({ ...props, invalid });
214
+ const initial = defaultValue ?? (props.value === void 0 && placeholder != null ? "" : void 0);
215
+ return /* @__PURE__ */ jsxs5("span", { className: cx("trc-select", className), children: [
216
+ /* @__PURE__ */ jsxs5("select", { ref, className: "trc-control trc-control--select", defaultValue: initial, ...props, ...a11y, children: [
217
+ placeholder != null && /* @__PURE__ */ jsx5("option", { value: "", disabled: true, children: placeholder }),
218
+ children
219
+ ] }),
220
+ /* @__PURE__ */ jsx5(ChevronDown, { className: "trc-select__chevron" })
221
+ ] });
222
+ });
223
+
224
+ // src/components/toggle.tsx
225
+ import { forwardRef as forwardRef3 } from "react";
226
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
227
+ function ToggleLabel({
228
+ kind,
229
+ label,
230
+ description,
231
+ className,
232
+ children
233
+ }) {
234
+ return /* @__PURE__ */ jsxs6("label", { className: cx("trc-toggle", `trc-toggle--${kind}`, className), children: [
235
+ children,
236
+ /* @__PURE__ */ jsxs6("span", { className: "trc-toggle__text", children: [
237
+ /* @__PURE__ */ jsx6("span", { className: "trc-toggle__label", children: label }),
238
+ description != null && /* @__PURE__ */ jsx6("span", { className: "trc-toggle__description", children: description })
239
+ ] })
240
+ ] });
241
+ }
242
+ var Checkbox = forwardRef3(function Checkbox2({ label, description, className, ...props }, ref) {
243
+ return /* @__PURE__ */ jsx6(ToggleLabel, { kind: "checkbox", label, description, className, children: /* @__PURE__ */ jsxs6("span", { className: "trc-checkbox", children: [
244
+ /* @__PURE__ */ jsx6("input", { ref, type: "checkbox", className: "trc-checkbox__input", ...props }),
245
+ /* @__PURE__ */ jsx6(Check, { className: "trc-checkbox__check" })
246
+ ] }) });
247
+ });
248
+ var Switch = forwardRef3(function Switch2({ label, description, className, ...props }, ref) {
249
+ return /* @__PURE__ */ jsx6(ToggleLabel, { kind: "switch", label, description, className, children: /* @__PURE__ */ jsxs6("span", { className: "trc-switch", children: [
250
+ /* @__PURE__ */ jsx6("input", { ref, type: "checkbox", role: "switch", className: "trc-switch__input", ...props }),
251
+ /* @__PURE__ */ jsx6("span", { "aria-hidden": true, className: "trc-switch__thumb" })
252
+ ] }) });
253
+ });
254
+
255
+ // src/components/site-header.tsx
256
+ import { useEffect as useEffect2, useId as useId3, useRef as useRef3, useState } from "react";
257
+
258
+ // src/components/layer.tsx
259
+ import { useId as useId2, useRef as useRef2 } from "react";
260
+
261
+ // src/hooks/use-modal.ts
262
+ import { useEffect, useRef } from "react";
263
+ var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
264
+ function useModal(open, panelRef, onClose) {
265
+ const close = useRef(onClose);
266
+ close.current = onClose;
267
+ useEffect(() => {
268
+ const panel = panelRef.current;
269
+ if (!open || !panel) return;
270
+ const previous = document.activeElement;
271
+ const overflow = document.body.style.overflow;
272
+ document.body.style.overflow = "hidden";
273
+ (panel.querySelector("[data-autofocus]") ?? panel.querySelector(FOCUSABLE) ?? panel).focus();
274
+ const onKeyDown = (e) => {
275
+ if (e.key === "Escape") {
276
+ e.stopPropagation();
277
+ close.current();
278
+ return;
279
+ }
280
+ if (e.key !== "Tab") return;
281
+ const items2 = [...panel.querySelectorAll(FOCUSABLE)].filter((el) => el.offsetParent !== null);
282
+ const first = items2[0];
283
+ const last = items2[items2.length - 1];
284
+ if (!first || !last) {
285
+ e.preventDefault();
286
+ return;
287
+ }
288
+ if (e.shiftKey && document.activeElement === first) {
289
+ e.preventDefault();
290
+ last.focus();
291
+ } else if (!e.shiftKey && document.activeElement === last) {
292
+ e.preventDefault();
293
+ first.focus();
294
+ }
295
+ };
296
+ document.addEventListener("keydown", onKeyDown);
297
+ return () => {
298
+ document.removeEventListener("keydown", onKeyDown);
299
+ document.body.style.overflow = overflow;
300
+ previous?.focus();
301
+ };
302
+ }, [open, panelRef]);
303
+ }
304
+
305
+ // src/utils/portal.tsx
306
+ import { useSyncExternalStore } from "react";
307
+ import { createPortal } from "react-dom";
308
+ var noop = () => () => {
309
+ };
310
+ var useIsClient = () => useSyncExternalStore(
311
+ noop,
312
+ () => true,
313
+ () => false
314
+ );
315
+ function Portal({ children }) {
316
+ return useIsClient() ? createPortal(children, document.body) : null;
317
+ }
318
+
319
+ // src/components/layer.tsx
320
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
321
+ function ModalLayer({
322
+ open,
323
+ onClose,
324
+ title,
325
+ description,
326
+ children,
327
+ footer,
328
+ closeLabel,
329
+ closeOnOverlay,
330
+ rootClassName,
331
+ panelClassName
332
+ }) {
333
+ const titleId = useId2();
334
+ const descriptionId = useId2();
335
+ const panel = useRef2(null);
336
+ useModal(open, panel, onClose);
337
+ return /* @__PURE__ */ jsx7(Portal, { children: /* @__PURE__ */ jsxs7("div", { className: cx("trc-layer", rootClassName, open && "trc-layer--open"), "aria-hidden": !open, children: [
338
+ /* @__PURE__ */ jsx7("div", { className: "trc-layer__overlay", onClick: closeOnOverlay ? onClose : void 0 }),
339
+ /* @__PURE__ */ jsxs7(
340
+ "div",
341
+ {
342
+ ref: panel,
343
+ role: "dialog",
344
+ "aria-modal": "true",
345
+ "aria-labelledby": titleId,
346
+ "aria-describedby": description != null ? descriptionId : void 0,
347
+ tabIndex: -1,
348
+ className: panelClassName,
349
+ children: [
350
+ /* @__PURE__ */ jsxs7("div", { className: "trc-layer__head", children: [
351
+ /* @__PURE__ */ jsx7("h2", { id: titleId, className: "trc-layer__title", children: title }),
352
+ /* @__PURE__ */ jsx7("button", { type: "button", className: "trc-layer__close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ jsx7(Close, {}) })
353
+ ] }),
354
+ description != null && /* @__PURE__ */ jsx7("p", { id: descriptionId, className: "trc-layer__description", children: description }),
355
+ children != null && /* @__PURE__ */ jsx7("div", { className: "trc-layer__body", children }),
356
+ footer != null && /* @__PURE__ */ jsx7("div", { className: "trc-layer__footer", children: footer })
357
+ ]
358
+ }
359
+ )
360
+ ] }) });
361
+ }
362
+
363
+ // src/components/drawer.tsx
364
+ import { jsx as jsx8 } from "react/jsx-runtime";
365
+ function Drawer({
366
+ side = "right",
367
+ size: size2 = "sm",
368
+ closeLabel = "Close",
369
+ closeOnOverlay = true,
370
+ className,
371
+ ...props
372
+ }) {
373
+ return /* @__PURE__ */ jsx8(
374
+ ModalLayer,
375
+ {
376
+ ...props,
377
+ closeLabel,
378
+ closeOnOverlay,
379
+ rootClassName: `trc-layer--drawer trc-layer--${side}`,
380
+ panelClassName: cx("trc-drawer", `trc-drawer--${size2}`, className)
381
+ }
382
+ );
383
+ }
384
+
385
+ // src/components/site-header.tsx
386
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
387
+ var isMenu = (item) => "items" in item;
388
+ function SiteHeader({
389
+ logo,
390
+ logoLabel,
391
+ homeHref = "/",
392
+ nav = [],
393
+ cta,
394
+ actions,
395
+ mobileFooter,
396
+ linkAs: LinkTag = "a",
397
+ scrollThreshold = 24,
398
+ menuLabel = "Menu",
399
+ closeLabel = "Close",
400
+ className
401
+ }) {
402
+ const [scrolled, setScrolled] = useState(false);
403
+ const [open, setOpen] = useState(false);
404
+ useEffect2(() => {
405
+ const update = () => setScrolled(window.scrollY > scrollThreshold);
406
+ update();
407
+ window.addEventListener("scroll", update, { passive: true });
408
+ return () => window.removeEventListener("scroll", update);
409
+ }, [scrollThreshold]);
410
+ return /* @__PURE__ */ jsxs8("header", { className: cx("trc-header", className), children: [
411
+ /* @__PURE__ */ jsxs8("div", { className: cx("trc-header__bar", scrolled && "trc-header__bar--scrolled"), children: [
412
+ /* @__PURE__ */ jsx9(LinkTag, { href: homeHref, "aria-label": logoLabel, className: "trc-header__logo", children: logo }),
413
+ nav.length > 0 && /* @__PURE__ */ jsx9("nav", { className: "trc-header__nav", children: nav.map(
414
+ (item, i) => isMenu(item) ? /* @__PURE__ */ jsx9(NavDropdown, { menu: item, linkAs: LinkTag }, i) : /* @__PURE__ */ jsx9(
415
+ LinkTag,
416
+ {
417
+ href: item.href,
418
+ "aria-current": item.active ? "page" : void 0,
419
+ className: cx("trc-header__link", item.active && "trc-header__link--active"),
420
+ children: item.label
421
+ },
422
+ i
423
+ )
424
+ ) }),
425
+ /* @__PURE__ */ jsxs8("div", { className: "trc-header__end", children: [
426
+ actions && /* @__PURE__ */ jsx9("div", { className: "trc-header__actions", children: actions }),
427
+ cta && /* @__PURE__ */ jsx9(PillLink, { as: LinkTag, href: cta.href, size: "sm", className: "trc-header__cta", children: cta.label }),
428
+ (nav.length > 0 || cta || mobileFooter) && /* @__PURE__ */ jsx9(
429
+ "button",
430
+ {
431
+ type: "button",
432
+ className: "trc-header__burger",
433
+ "aria-label": menuLabel,
434
+ "aria-expanded": open,
435
+ onClick: () => setOpen(true),
436
+ children: /* @__PURE__ */ jsx9(Menu, {})
437
+ }
438
+ )
439
+ ] })
440
+ ] }),
441
+ /* @__PURE__ */ jsx9(
442
+ MobileMenu,
443
+ {
444
+ open,
445
+ onClose: () => setOpen(false),
446
+ nav,
447
+ cta,
448
+ footer: mobileFooter,
449
+ linkAs: LinkTag,
450
+ title: menuLabel,
451
+ closeLabel
452
+ }
453
+ )
454
+ ] });
455
+ }
456
+ function NavDropdown({ menu, linkAs: LinkTag }) {
457
+ const id = useId3();
458
+ const [open, setOpen] = useState(false);
459
+ const closeTimer = useRef3(void 0);
460
+ const button = useRef3(null);
461
+ const openedByHover = useRef3(false);
462
+ const show = () => {
463
+ window.clearTimeout(closeTimer.current);
464
+ if (!open) openedByHover.current = true;
465
+ setOpen(true);
466
+ };
467
+ const hide = () => {
468
+ closeTimer.current = window.setTimeout(() => setOpen(false), 120);
469
+ };
470
+ useEffect2(() => () => window.clearTimeout(closeTimer.current), []);
471
+ return /* @__PURE__ */ jsxs8(
472
+ "div",
473
+ {
474
+ className: "trc-dropdown",
475
+ onMouseEnter: show,
476
+ onMouseLeave: hide,
477
+ onKeyDown: (e) => {
478
+ if (e.key === "Escape" && open) {
479
+ setOpen(false);
480
+ button.current?.focus();
481
+ }
482
+ },
483
+ onBlur: (e) => !e.currentTarget.contains(e.relatedTarget) && setOpen(false),
484
+ children: [
485
+ /* @__PURE__ */ jsxs8(
486
+ "button",
487
+ {
488
+ ref: button,
489
+ type: "button",
490
+ "aria-expanded": open,
491
+ "aria-controls": id,
492
+ onClick: () => {
493
+ if (open && openedByHover.current) openedByHover.current = false;
494
+ else setOpen((o) => !o);
495
+ },
496
+ className: cx("trc-header__link", "trc-dropdown__trigger", open && "trc-dropdown__trigger--open"),
497
+ children: [
498
+ menu.label,
499
+ /* @__PURE__ */ jsx9(ChevronDown, { className: "trc-dropdown__chevron" })
500
+ ]
501
+ }
502
+ ),
503
+ /* @__PURE__ */ jsx9("div", { id, className: cx("trc-dropdown__panel", open && "trc-dropdown__panel--open"), children: /* @__PURE__ */ jsx9("ul", { className: "trc-dropdown__list", children: menu.items.map((item, i) => /* @__PURE__ */ jsx9("li", { children: /* @__PURE__ */ jsxs8(
504
+ LinkTag,
505
+ {
506
+ href: item.href,
507
+ "aria-current": item.active ? "page" : void 0,
508
+ onClick: () => setOpen(false),
509
+ className: "trc-dropdown__item",
510
+ children: [
511
+ /* @__PURE__ */ jsx9("span", { className: "trc-dropdown__label", children: item.label }),
512
+ item.description != null && /* @__PURE__ */ jsx9("span", { className: "trc-dropdown__description", children: item.description })
513
+ ]
514
+ }
515
+ ) }, i)) }) })
516
+ ]
517
+ }
518
+ );
519
+ }
520
+ function MobileMenu({
521
+ open,
522
+ onClose,
523
+ nav,
524
+ cta,
525
+ footer,
526
+ linkAs: LinkTag,
527
+ title,
528
+ closeLabel
529
+ }) {
530
+ return /* @__PURE__ */ jsxs8(Drawer, { open, onClose, title, closeLabel, className: "trc-mobile-menu", children: [
531
+ /* @__PURE__ */ jsx9("nav", { className: "trc-mobile-nav", children: nav.map(
532
+ (item, i) => isMenu(item) ? /* @__PURE__ */ jsxs8("div", { className: "trc-mobile-nav__group", children: [
533
+ /* @__PURE__ */ jsx9("p", { className: "trc-mobile-nav__heading", children: item.label }),
534
+ /* @__PURE__ */ jsx9("ul", { className: "trc-mobile-nav__sub", children: item.items.map((sub, j) => /* @__PURE__ */ jsx9("li", { children: /* @__PURE__ */ jsx9(
535
+ LinkTag,
536
+ {
537
+ href: sub.href,
538
+ "aria-current": sub.active ? "page" : void 0,
539
+ onClick: onClose,
540
+ className: "trc-mobile-nav__sublink",
541
+ children: sub.label
542
+ }
543
+ ) }, j)) })
544
+ ] }, i) : /* @__PURE__ */ jsx9(
545
+ LinkTag,
546
+ {
547
+ href: item.href,
548
+ "aria-current": item.active ? "page" : void 0,
549
+ onClick: onClose,
550
+ className: cx("trc-mobile-nav__link", item.active && "trc-mobile-nav__link--active"),
551
+ children: item.label
552
+ },
553
+ i
554
+ )
555
+ ) }),
556
+ cta && /* @__PURE__ */ jsx9(PillLink, { as: LinkTag, href: cta.href, onClick: onClose, className: "trc-mobile-menu__cta", children: cta.label }),
557
+ footer != null && /* @__PURE__ */ jsx9("div", { className: "trc-mobile-menu__footer", children: footer })
558
+ ] });
559
+ }
560
+
561
+ // src/components/site-footer.tsx
562
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
563
+ function SiteFooter({
564
+ logo,
565
+ logoLabel,
566
+ homeHref = "/",
567
+ tagline,
568
+ children,
569
+ columns = [],
570
+ columnsLabel = "Site map",
571
+ copyright,
572
+ legal = [],
573
+ legalLabel = "Legal",
574
+ linkAs: LinkTag = "a",
575
+ className
576
+ }) {
577
+ return /* @__PURE__ */ jsx10("footer", { className: cx("trc-footer", className), children: /* @__PURE__ */ jsxs9("div", { className: "trc-footer__card", children: [
578
+ /* @__PURE__ */ jsxs9("div", { className: "trc-footer__main", children: [
579
+ /* @__PURE__ */ jsxs9("div", { className: "trc-footer__brand", children: [
580
+ logo != null && /* @__PURE__ */ jsx10(LinkTag, { href: homeHref, "aria-label": logoLabel, className: "trc-footer__logo", children: logo }),
581
+ tagline != null && /* @__PURE__ */ jsx10("p", { className: "trc-footer__tagline", children: tagline }),
582
+ children != null && /* @__PURE__ */ jsx10("div", { className: "trc-footer__extra", children })
583
+ ] }),
584
+ columns.length > 0 && /* @__PURE__ */ jsx10("nav", { className: "trc-footer__columns", "aria-label": columnsLabel, children: columns.map((col, i) => /* @__PURE__ */ jsxs9("div", { children: [
585
+ /* @__PURE__ */ jsx10("p", { className: "trc-footer__heading", children: col.title }),
586
+ /* @__PURE__ */ jsx10("ul", { className: "trc-footer__list", children: col.links.map((link, j) => /* @__PURE__ */ jsx10("li", { children: /* @__PURE__ */ jsx10(LinkTag, { href: link.href, className: "trc-footer__link", children: link.label }) }, j)) })
587
+ ] }, i)) })
588
+ ] }),
589
+ (copyright != null || legal.length > 0) && /* @__PURE__ */ jsxs9("div", { className: "trc-footer__bottom", children: [
590
+ copyright != null && /* @__PURE__ */ jsx10("p", { className: "trc-footer__copyright", children: copyright }),
591
+ legal.length > 0 && /* @__PURE__ */ jsx10("nav", { "aria-label": legalLabel, className: "trc-footer__legal", children: legal.map((link, i) => /* @__PURE__ */ jsx10(LinkTag, { href: link.href, className: "trc-footer__legal-link", children: link.label }, i)) })
592
+ ] })
593
+ ] }) });
594
+ }
595
+
596
+ // src/components/hero.tsx
597
+ import { useEffect as useEffect3, useRef as useRef4, useState as useState2 } from "react";
598
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
599
+ var delay = (ms) => ({ "--trc-delay": `${ms}ms` });
600
+ function PageHero({
601
+ title,
602
+ accentLast,
603
+ description,
604
+ meta,
605
+ image,
606
+ imagePosition = "50% 50%",
607
+ children,
608
+ className
609
+ }) {
610
+ const words = title.split(" ").length;
611
+ return /* @__PURE__ */ jsx11("section", { className: cx("trc-hero-inset", className), children: /* @__PURE__ */ jsxs10("div", { className: "trc-page-hero", children: [
612
+ /* @__PURE__ */ jsx11("div", { "aria-hidden": true, className: "trc-hero__media", children: typeof image === "string" ? /* @__PURE__ */ jsx11("img", { src: image, alt: "", className: "trc-hero__image", style: { objectPosition: imagePosition } }) : image != null ? image : /* @__PURE__ */ jsx11("div", { className: "trc-page-hero__glow" }) }),
613
+ /* @__PURE__ */ jsx11("div", { "aria-hidden": true, className: "trc-page-hero__veil" }),
614
+ /* @__PURE__ */ jsxs10("div", { className: "trc-page-hero__content", children: [
615
+ /* @__PURE__ */ jsx11("h1", { className: "trc-display trc-page-hero__title trc-enter", style: delay(150), children: /* @__PURE__ */ jsx11(AccentText, { text: title, accentLast: accentLast ?? (words > 3 ? 2 : 1) }) }),
616
+ description != null && /* @__PURE__ */ jsx11("p", { className: "trc-page-hero__description trc-enter", style: delay(250), children: description }),
617
+ meta != null && /* @__PURE__ */ jsx11("div", { className: "trc-page-hero__meta trc-enter", style: delay(350), children: meta }),
618
+ children != null && /* @__PURE__ */ jsx11("div", { className: "trc-page-hero__actions trc-enter", style: delay(450), children })
619
+ ] })
620
+ ] }) });
621
+ }
622
+ var prefersReducedMotion = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
623
+ function HeroVideo({ clips, children, aside, fade = 1.2, parallax = true, className }) {
624
+ const videos = useRef4([]);
625
+ const section = useRef4(null);
626
+ const media = useRef4(null);
627
+ const [active, setActive] = useState2(-1);
628
+ useEffect3(() => {
629
+ if (clips.length === 0) return;
630
+ const start = Math.floor(Math.random() * clips.length);
631
+ const video = videos.current[start];
632
+ if (!prefersReducedMotion()) void video?.play().catch(() => {
633
+ });
634
+ const frame = requestAnimationFrame(() => setActive(start));
635
+ return () => {
636
+ cancelAnimationFrame(frame);
637
+ video?.pause();
638
+ };
639
+ }, [clips.length]);
640
+ useEffect3(() => {
641
+ const el = section.current;
642
+ const layer = media.current;
643
+ if (!parallax || !el || !layer || prefersReducedMotion()) return;
644
+ let frame = 0;
645
+ const update = () => {
646
+ frame = 0;
647
+ const rect = el.getBoundingClientRect();
648
+ const progress = Math.min(Math.max(-rect.top / rect.height, 0), 1);
649
+ layer.style.transform = `translate3d(0, ${progress * 120}px, 0)`;
650
+ };
651
+ const onScroll = () => {
652
+ if (!frame) frame = requestAnimationFrame(update);
653
+ };
654
+ update();
655
+ window.addEventListener("scroll", onScroll, { passive: true });
656
+ return () => {
657
+ window.removeEventListener("scroll", onScroll);
658
+ cancelAnimationFrame(frame);
659
+ };
660
+ }, [parallax]);
661
+ const playNext = (i) => {
662
+ const next = videos.current[(i + 1) % clips.length];
663
+ if (!next) return;
664
+ next.currentTime = 0;
665
+ void next.play().catch(() => {
666
+ });
667
+ setActive((i + 1) % clips.length);
668
+ };
669
+ return /* @__PURE__ */ jsx11("section", { ref: section, className: cx("trc-hero-inset", className), children: /* @__PURE__ */ jsxs10("div", { className: "trc-hero-video", children: [
670
+ /* @__PURE__ */ jsx11("div", { ref: media, "aria-hidden": true, className: "trc-hero__media trc-hero-video__media", children: clips.map((clip, i) => /* @__PURE__ */ jsxs10(
671
+ "video",
672
+ {
673
+ ref: (el) => {
674
+ videos.current[i] = el;
675
+ },
676
+ className: cx("trc-hero__image", "trc-hero-video__clip", i === active && "trc-hero-video__clip--active"),
677
+ style: { transitionDuration: `${fade}s` },
678
+ muted: true,
679
+ playsInline: true,
680
+ preload: "metadata",
681
+ poster: clip.poster,
682
+ onTimeUpdate: (e) => {
683
+ const v = e.currentTarget;
684
+ if (i === active && v.duration && v.duration - v.currentTime <= fade) playNext(i);
685
+ },
686
+ onEnded: () => i === active && playNext(i),
687
+ children: [
688
+ clip.webm && /* @__PURE__ */ jsx11("source", { src: clip.webm, type: "video/webm" }),
689
+ clip.mp4 && /* @__PURE__ */ jsx11("source", { src: clip.mp4, type: "video/mp4" })
690
+ ]
691
+ },
692
+ i
693
+ )) }),
694
+ /* @__PURE__ */ jsx11("div", { "aria-hidden": true, className: "trc-hero-video__veil" }),
695
+ /* @__PURE__ */ jsxs10("div", { className: "trc-hero-video__content", children: [
696
+ /* @__PURE__ */ jsx11("div", { className: "trc-hero-video__main", children }),
697
+ aside != null && /* @__PURE__ */ jsx11("div", { className: "trc-hero-video__aside", children: aside })
698
+ ] })
699
+ ] }) });
700
+ }
701
+
702
+ // src/components/back-to-top.tsx
703
+ import { useEffect as useEffect4, useState as useState3 } from "react";
704
+ import { jsx as jsx12 } from "react/jsx-runtime";
705
+ function BackToTop({ label = "Back to top", threshold = 0.8, className }) {
706
+ const [visible, setVisible] = useState3(false);
707
+ useEffect4(() => {
708
+ const update = () => setVisible(window.scrollY > window.innerHeight * threshold);
709
+ update();
710
+ window.addEventListener("scroll", update, { passive: true });
711
+ return () => window.removeEventListener("scroll", update);
712
+ }, [threshold]);
713
+ return /* @__PURE__ */ jsx12(
714
+ "button",
715
+ {
716
+ type: "button",
717
+ onClick: () => window.scrollTo({
718
+ top: 0,
719
+ behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth"
720
+ }),
721
+ "aria-label": label,
722
+ "aria-hidden": !visible,
723
+ tabIndex: visible ? 0 : -1,
724
+ className: cx("trc-back-to-top", visible && "trc-back-to-top--visible", className),
725
+ children: /* @__PURE__ */ jsx12(ArrowUp, {})
726
+ }
727
+ );
728
+ }
729
+
730
+ // src/components/dialog.tsx
731
+ import { jsx as jsx13 } from "react/jsx-runtime";
732
+ function Dialog({ size: size2 = "md", closeLabel = "Close", closeOnOverlay = true, className, ...props }) {
733
+ return /* @__PURE__ */ jsx13(
734
+ ModalLayer,
735
+ {
736
+ ...props,
737
+ closeLabel,
738
+ closeOnOverlay,
739
+ rootClassName: "trc-layer--dialog",
740
+ panelClassName: cx("trc-dialog", `trc-dialog--${size2}`, className)
741
+ }
742
+ );
743
+ }
744
+
745
+ // src/components/dropdown-menu.tsx
746
+ import {
747
+ cloneElement,
748
+ useId as useId4,
749
+ useRef as useRef6,
750
+ useState as useState4
751
+ } from "react";
752
+
753
+ // src/hooks/use-floating.ts
754
+ import { useEffect as useEffect5, useLayoutEffect, useRef as useRef5 } from "react";
755
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect5 : useLayoutEffect;
756
+ var MARGIN = 8;
757
+ function useAnchoredPosition(open, anchorRef, floatingRef, { side = "bottom", align = "start", offset = 8 } = {}) {
758
+ useIsomorphicLayoutEffect(() => {
759
+ if (!open) return;
760
+ const update = () => {
761
+ const anchor = anchorRef.current;
762
+ const floating = floatingRef.current;
763
+ if (!anchor || !floating) return;
764
+ const a = anchor.getBoundingClientRect();
765
+ const f = floating.getBoundingClientRect();
766
+ const vw = window.innerWidth;
767
+ const vh = window.innerHeight;
768
+ const below = a.bottom + offset;
769
+ const above = a.top - offset - f.height;
770
+ const fitsBelow = below + f.height <= vh - MARGIN;
771
+ const fitsAbove = above >= MARGIN;
772
+ const resolved = side === "bottom" ? fitsBelow || !fitsAbove ? "bottom" : "top" : fitsAbove || !fitsBelow ? "top" : "bottom";
773
+ const top = resolved === "bottom" ? below : above;
774
+ let left = align === "start" ? a.left : align === "end" ? a.right - f.width : a.left + a.width / 2 - f.width / 2;
775
+ left = Math.min(Math.max(left, MARGIN), Math.max(MARGIN, vw - f.width - MARGIN));
776
+ floating.style.top = `${Math.round(top)}px`;
777
+ floating.style.left = `${Math.round(left)}px`;
778
+ floating.dataset.side = resolved;
779
+ };
780
+ update();
781
+ window.addEventListener("resize", update);
782
+ window.addEventListener("scroll", update, true);
783
+ return () => {
784
+ window.removeEventListener("resize", update);
785
+ window.removeEventListener("scroll", update, true);
786
+ };
787
+ }, [open, side, align, offset]);
788
+ }
789
+ function useDismiss(open, refs, onDismiss) {
790
+ const dismiss2 = useRef5(onDismiss);
791
+ dismiss2.current = onDismiss;
792
+ useEffect5(() => {
793
+ if (!open) return;
794
+ const onPointerDown = (e) => {
795
+ const target = e.target;
796
+ if (!refs.some((ref) => ref.current?.contains(target))) dismiss2.current("outside");
797
+ };
798
+ const onKeyDown = (e) => {
799
+ if (e.key === "Escape") dismiss2.current("escape");
800
+ };
801
+ document.addEventListener("pointerdown", onPointerDown);
802
+ document.addEventListener("keydown", onKeyDown);
803
+ return () => {
804
+ document.removeEventListener("pointerdown", onPointerDown);
805
+ document.removeEventListener("keydown", onKeyDown);
806
+ };
807
+ }, [open]);
808
+ }
809
+
810
+ // src/utils/compose.ts
811
+ import { version } from "react";
812
+ function callAll(...fns) {
813
+ return (...args) => fns.forEach((fn) => fn?.(...args));
814
+ }
815
+ function mergeRefs(...refs) {
816
+ return (value) => {
817
+ for (const ref of refs) {
818
+ if (typeof ref === "function") ref(value);
819
+ else if (ref) ref.current = value;
820
+ }
821
+ };
822
+ }
823
+ function getElementRef(element) {
824
+ const fromProps = element.props.ref;
825
+ if (Number.parseInt(version, 10) >= 19) return fromProps;
826
+ return element.ref;
827
+ }
828
+
829
+ // src/components/dropdown-menu.tsx
830
+ import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
831
+ var isAction = (item) => item !== "separator";
832
+ var textOf = (item) => (item.textValue ?? (typeof item.label === "string" ? item.label : "")).toLowerCase();
833
+ function DropdownMenu({ trigger, items: items2, align = "start", linkAs: LinkTag = "a", className }) {
834
+ const [open, setOpen] = useState4(false);
835
+ const triggerRef = useRef6(null);
836
+ const menuRef = useRef6(null);
837
+ const itemRefs = useRef6([]);
838
+ const generatedId = useId4();
839
+ const triggerId = trigger.props.id ?? `${generatedId}-trigger`;
840
+ const menuId = `${generatedId}-menu`;
841
+ const enabled = items2.map((item, i) => isAction(item) && !item.disabled ? i : -1).filter((i) => i >= 0);
842
+ const focusItem = (index) => index !== void 0 && itemRefs.current[index]?.focus();
843
+ const openMenu = (focus) => {
844
+ setOpen(true);
845
+ requestAnimationFrame(() => focusItem(focus === "first" ? enabled[0] : enabled[enabled.length - 1]));
846
+ };
847
+ const close = (returnFocus = true) => {
848
+ setOpen(false);
849
+ if (returnFocus) triggerRef.current?.focus();
850
+ };
851
+ useAnchoredPosition(open, triggerRef, menuRef, { side: "bottom", align });
852
+ useDismiss(open, [triggerRef, menuRef], (reason) => close(reason === "escape"));
853
+ const select = (item) => {
854
+ item.onSelect?.();
855
+ close();
856
+ };
857
+ const onMenuKeyDown = (e) => {
858
+ const current = itemRefs.current.findIndex((el) => el === document.activeElement);
859
+ const position = enabled.indexOf(current);
860
+ const move = (to) => {
861
+ e.preventDefault();
862
+ focusItem(enabled[(to + enabled.length) % enabled.length]);
863
+ };
864
+ if (e.key === "ArrowDown") move(position + 1);
865
+ else if (e.key === "ArrowUp") move(position - 1);
866
+ else if (e.key === "Home") move(0);
867
+ else if (e.key === "End") move(enabled.length - 1);
868
+ else if (e.key === "Tab") close(false);
869
+ else if (e.key.length === 1 && /\S/.test(e.key)) {
870
+ const letter = e.key.toLowerCase();
871
+ const order = [...enabled.slice(position + 1), ...enabled.slice(0, position + 1)];
872
+ const match = order.find((i) => textOf(items2[i]).startsWith(letter));
873
+ if (match !== void 0) focusItem(match);
874
+ }
875
+ };
876
+ const triggerElement = cloneElement(trigger, {
877
+ ref: mergeRefs(triggerRef, getElementRef(trigger)),
878
+ id: triggerId,
879
+ "aria-haspopup": "menu",
880
+ "aria-expanded": open,
881
+ "aria-controls": open ? menuId : void 0,
882
+ onClick: callAll(trigger.props.onClick, () => open ? close() : openMenu("first")),
883
+ onKeyDown: callAll(trigger.props.onKeyDown, (e) => {
884
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
885
+ e.preventDefault();
886
+ openMenu(e.key === "ArrowDown" ? "first" : "last");
887
+ }
888
+ })
889
+ });
890
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
891
+ triggerElement,
892
+ open && /* @__PURE__ */ jsx14(Portal, { children: /* @__PURE__ */ jsx14(
893
+ "div",
894
+ {
895
+ ref: menuRef,
896
+ id: menuId,
897
+ role: "menu",
898
+ "aria-labelledby": triggerId,
899
+ className: cx("trc-floating", "trc-menu", className),
900
+ onKeyDown: onMenuKeyDown,
901
+ children: items2.map((item, i) => {
902
+ if (!isAction(item)) return /* @__PURE__ */ jsx14("div", { role: "separator", className: "trc-menu__separator" }, i);
903
+ const shared = {
904
+ ref: (el) => {
905
+ itemRefs.current[i] = el;
906
+ },
907
+ role: "menuitem",
908
+ tabIndex: -1,
909
+ "aria-disabled": item.disabled || void 0,
910
+ className: cx("trc-menu__item", item.danger && "trc-menu__item--danger"),
911
+ onMouseMove: (e) => !item.disabled && e.currentTarget.focus(),
912
+ children: /* @__PURE__ */ jsxs11(Fragment3, { children: [
913
+ /* @__PURE__ */ jsx14("span", { className: "trc-menu__label", children: item.label }),
914
+ item.hint != null && /* @__PURE__ */ jsx14("span", { className: "trc-menu__hint", children: item.hint })
915
+ ] })
916
+ };
917
+ return item.href && !item.disabled ? /* @__PURE__ */ jsx14(LinkTag, { href: item.href, ...shared, onClick: () => select(item) }, i) : /* @__PURE__ */ jsx14(
918
+ "div",
919
+ {
920
+ ...shared,
921
+ onClick: () => !item.disabled && select(item),
922
+ onKeyDown: (e) => {
923
+ if ((e.key === "Enter" || e.key === " ") && !item.disabled) {
924
+ e.preventDefault();
925
+ select(item);
926
+ }
927
+ }
928
+ },
929
+ i
930
+ );
931
+ })
932
+ }
933
+ ) })
934
+ ] });
935
+ }
936
+
937
+ // src/components/popover.tsx
938
+ import { cloneElement as cloneElement2, useEffect as useEffect6, useId as useId5, useRef as useRef7, useState as useState5 } from "react";
939
+ import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
940
+ function Popover({
941
+ trigger,
942
+ title,
943
+ label,
944
+ children,
945
+ side = "bottom",
946
+ align = "start",
947
+ open: controlled,
948
+ onOpenChange,
949
+ className
950
+ }) {
951
+ const [uncontrolled, setUncontrolled] = useState5(false);
952
+ const open = controlled ?? uncontrolled;
953
+ const setOpen = (value) => {
954
+ if (controlled === void 0) setUncontrolled(value);
955
+ onOpenChange?.(value);
956
+ };
957
+ const triggerRef = useRef7(null);
958
+ const panelRef = useRef7(null);
959
+ const id = useId5();
960
+ useAnchoredPosition(open, triggerRef, panelRef, { side, align });
961
+ useDismiss(open, [triggerRef, panelRef], (reason) => {
962
+ setOpen(false);
963
+ if (reason === "escape") triggerRef.current?.focus();
964
+ });
965
+ useEffect6(() => {
966
+ if (!open) return;
967
+ const panel = panelRef.current;
968
+ const first = panel?.querySelector(
969
+ "[data-autofocus], input, select, textarea, button, a[href], [tabindex]:not([tabindex='-1'])"
970
+ );
971
+ (first ?? panel)?.focus();
972
+ }, [open]);
973
+ const triggerElement = cloneElement2(trigger, {
974
+ ref: mergeRefs(triggerRef, getElementRef(trigger)),
975
+ "aria-haspopup": "dialog",
976
+ "aria-expanded": open,
977
+ "aria-controls": open ? `${id}-panel` : void 0,
978
+ onClick: callAll(trigger.props.onClick, () => setOpen(!open))
979
+ });
980
+ return /* @__PURE__ */ jsxs12(Fragment4, { children: [
981
+ triggerElement,
982
+ open && /* @__PURE__ */ jsx15(Portal, { children: /* @__PURE__ */ jsxs12(
983
+ "div",
984
+ {
985
+ ref: panelRef,
986
+ id: `${id}-panel`,
987
+ role: "dialog",
988
+ "aria-labelledby": title != null ? `${id}-title` : void 0,
989
+ "aria-label": title == null ? label : void 0,
990
+ tabIndex: -1,
991
+ className: cx("trc-floating", "trc-popover", className),
992
+ children: [
993
+ title != null && /* @__PURE__ */ jsx15("p", { id: `${id}-title`, className: "trc-popover__title", children: title }),
994
+ /* @__PURE__ */ jsx15("div", { className: "trc-popover__body", children })
995
+ ]
996
+ }
997
+ ) })
998
+ ] });
999
+ }
1000
+
1001
+ // src/components/tooltip.tsx
1002
+ import { cloneElement as cloneElement3, useEffect as useEffect7, useId as useId6, useRef as useRef8, useState as useState6 } from "react";
1003
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1004
+ function Tooltip({ label, children, side = "top", delay: delay2 = 300, className }) {
1005
+ const [open, setOpen] = useState6(false);
1006
+ const anchorRef = useRef8(null);
1007
+ const tipRef = useRef8(null);
1008
+ const timer = useRef8(void 0);
1009
+ const id = useId6();
1010
+ const show = (wait) => {
1011
+ window.clearTimeout(timer.current);
1012
+ timer.current = window.setTimeout(() => setOpen(true), wait);
1013
+ };
1014
+ const hide = () => {
1015
+ window.clearTimeout(timer.current);
1016
+ timer.current = window.setTimeout(() => setOpen(false), 100);
1017
+ };
1018
+ useEffect7(() => () => window.clearTimeout(timer.current), []);
1019
+ useEffect7(() => {
1020
+ if (!open) return;
1021
+ const onKeyDown = (e) => e.key === "Escape" && setOpen(false);
1022
+ document.addEventListener("keydown", onKeyDown);
1023
+ return () => document.removeEventListener("keydown", onKeyDown);
1024
+ }, [open]);
1025
+ useAnchoredPosition(open, anchorRef, tipRef, { side, align: "center", offset: 8 });
1026
+ const child = cloneElement3(children, {
1027
+ ref: mergeRefs(anchorRef, getElementRef(children)),
1028
+ "aria-describedby": [children.props["aria-describedby"], id].filter(Boolean).join(" "),
1029
+ onMouseEnter: callAll(children.props.onMouseEnter, () => show(delay2)),
1030
+ onMouseLeave: callAll(children.props.onMouseLeave, hide),
1031
+ onFocus: callAll(children.props.onFocus, () => show(0)),
1032
+ onBlur: callAll(children.props.onBlur, () => {
1033
+ window.clearTimeout(timer.current);
1034
+ setOpen(false);
1035
+ })
1036
+ });
1037
+ return /* @__PURE__ */ jsxs13(Fragment5, { children: [
1038
+ child,
1039
+ /* @__PURE__ */ jsx16(Portal, { children: /* @__PURE__ */ jsx16(
1040
+ "div",
1041
+ {
1042
+ ref: tipRef,
1043
+ id,
1044
+ role: "tooltip",
1045
+ className: cx("trc-floating", "trc-tooltip", open && "trc-tooltip--open", className),
1046
+ onMouseEnter: () => window.clearTimeout(timer.current),
1047
+ onMouseLeave: hide,
1048
+ children: label
1049
+ }
1050
+ ) })
1051
+ ] });
1052
+ }
1053
+
1054
+ // src/components/toast.tsx
1055
+ import { useEffect as useEffect8, useRef as useRef9, useState as useState7, useSyncExternalStore as useSyncExternalStore2 } from "react";
1056
+ import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1057
+ var items = [];
1058
+ var nextId = 1;
1059
+ var listeners = /* @__PURE__ */ new Set();
1060
+ var emit = () => listeners.forEach((listener) => listener());
1061
+ var subscribe = (listener) => {
1062
+ listeners.add(listener);
1063
+ return () => listeners.delete(listener);
1064
+ };
1065
+ var EMPTY = [];
1066
+ var LEAVE_MS = 200;
1067
+ function dismiss(id) {
1068
+ items = items.map((t) => id === void 0 || t.id === id ? { ...t, leaving: true } : t);
1069
+ emit();
1070
+ window.setTimeout(() => {
1071
+ items = items.filter((t) => !(t.leaving && (id === void 0 || t.id === id)));
1072
+ emit();
1073
+ }, LEAVE_MS);
1074
+ }
1075
+ function toast(title, options = {}) {
1076
+ const id = nextId++;
1077
+ items = [...items, { ...options, id, title, leaving: false }];
1078
+ emit();
1079
+ return id;
1080
+ }
1081
+ toast.success = (title, options = {}) => toast(title, { ...options, tone: "success" });
1082
+ toast.error = (title, options = {}) => toast(title, { ...options, tone: "error" });
1083
+ toast.dismiss = dismiss;
1084
+ function Toaster({
1085
+ position = "bottom-right",
1086
+ duration = 5e3,
1087
+ label = "Notifications",
1088
+ closeLabel = "Close"
1089
+ }) {
1090
+ const toasts = useSyncExternalStore2(
1091
+ subscribe,
1092
+ () => items,
1093
+ () => EMPTY
1094
+ );
1095
+ const [paused, setPaused] = useState7(false);
1096
+ return /* @__PURE__ */ jsx17(Portal, { children: /* @__PURE__ */ jsx17(
1097
+ "section",
1098
+ {
1099
+ "aria-label": label,
1100
+ className: cx("trc-toaster", `trc-toaster--${position}`),
1101
+ onMouseEnter: () => setPaused(true),
1102
+ onMouseLeave: () => setPaused(false),
1103
+ onFocus: () => setPaused(true),
1104
+ onBlur: (e) => !e.currentTarget.contains(e.relatedTarget) && setPaused(false),
1105
+ children: /* @__PURE__ */ jsx17("ol", { "aria-live": "polite", "aria-relevant": "additions", className: "trc-toaster__list", children: toasts.map((t) => /* @__PURE__ */ jsx17(ToastCard, { toast: t, duration: t.duration ?? duration, paused, closeLabel }, t.id)) })
1106
+ }
1107
+ ) });
1108
+ }
1109
+ function ToastCard({
1110
+ toast: t,
1111
+ duration,
1112
+ paused,
1113
+ closeLabel
1114
+ }) {
1115
+ const remaining = useRef9(duration);
1116
+ useEffect8(() => {
1117
+ if (paused || t.leaving || !Number.isFinite(remaining.current)) return;
1118
+ const started = Date.now();
1119
+ const timer = window.setTimeout(() => dismiss(t.id), remaining.current);
1120
+ return () => {
1121
+ window.clearTimeout(timer);
1122
+ remaining.current -= Date.now() - started;
1123
+ };
1124
+ }, [paused, t.leaving, t.id]);
1125
+ const tone = t.tone ?? "default";
1126
+ return /* @__PURE__ */ jsxs14("li", { className: cx("trc-toast", `trc-toast--${tone}`, t.leaving && "trc-toast--leaving"), children: [
1127
+ /* @__PURE__ */ jsxs14("div", { className: "trc-toast__text", role: tone === "error" ? "alert" : void 0, children: [
1128
+ /* @__PURE__ */ jsx17("p", { className: "trc-toast__title", children: t.title }),
1129
+ t.description != null && /* @__PURE__ */ jsx17("p", { className: "trc-toast__description", children: t.description })
1130
+ ] }),
1131
+ t.action && /* @__PURE__ */ jsx17(
1132
+ "button",
1133
+ {
1134
+ type: "button",
1135
+ className: "trc-toast__action",
1136
+ onClick: () => {
1137
+ t.action?.onClick();
1138
+ dismiss(t.id);
1139
+ },
1140
+ children: t.action.label
1141
+ }
1142
+ ),
1143
+ /* @__PURE__ */ jsx17("button", { type: "button", className: "trc-toast__close", "aria-label": closeLabel, onClick: () => dismiss(t.id), children: /* @__PURE__ */ jsx17(Close, {}) })
1144
+ ] });
1145
+ }
1146
+
1147
+ // src/components/alert.tsx
1148
+ import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1149
+ function Alert({ title, tone = "info", actions, className, children, ...props }) {
1150
+ return /* @__PURE__ */ jsxs15("div", { className: cx("trc-alert", `trc-alert--${tone}`, className), ...props, children: [
1151
+ title != null && /* @__PURE__ */ jsx18("p", { className: "trc-alert__title", children: title }),
1152
+ children != null && /* @__PURE__ */ jsx18("div", { className: "trc-alert__body", children }),
1153
+ actions != null && /* @__PURE__ */ jsx18("div", { className: "trc-alert__actions", children: actions })
1154
+ ] });
1155
+ }
1156
+
1157
+ // src/components/tabs.tsx
1158
+ import { useId as useId7, useRef as useRef11 } from "react";
1159
+
1160
+ // src/hooks/use-controllable.ts
1161
+ import { useRef as useRef10, useState as useState8 } from "react";
1162
+ function useControllable(value, defaultValue, onChange) {
1163
+ const [internal, setInternal] = useState8(defaultValue);
1164
+ const controlled = useRef10(value !== void 0).current;
1165
+ const current = controlled ? value : internal;
1166
+ const set = (next) => {
1167
+ if (!controlled) setInternal(next);
1168
+ onChange?.(next);
1169
+ };
1170
+ return [current, set];
1171
+ }
1172
+
1173
+ // src/components/tabs.tsx
1174
+ import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
1175
+ function Tabs({
1176
+ items: items2,
1177
+ value,
1178
+ defaultValue,
1179
+ onValueChange,
1180
+ label,
1181
+ activation = "automatic",
1182
+ className
1183
+ }) {
1184
+ const firstEnabled = items2.find((item) => !item.disabled)?.value ?? "";
1185
+ const [selected, setSelected] = useControllable(value, defaultValue ?? firstEnabled, onValueChange);
1186
+ const id = useId7();
1187
+ const tabRefs = useRef11([]);
1188
+ const enabled = items2.map((item, i) => item.disabled ? -1 : i).filter((i) => i >= 0);
1189
+ const onKeyDown = (e, index) => {
1190
+ const position = enabled.indexOf(index);
1191
+ let target;
1192
+ if (e.key === "ArrowRight") target = enabled[(position + 1) % enabled.length];
1193
+ else if (e.key === "ArrowLeft") target = enabled[(position - 1 + enabled.length) % enabled.length];
1194
+ else if (e.key === "Home") target = enabled[0];
1195
+ else if (e.key === "End") target = enabled[enabled.length - 1];
1196
+ if (target === void 0) return;
1197
+ e.preventDefault();
1198
+ tabRefs.current[target]?.focus();
1199
+ if (activation === "automatic") setSelected(items2[target].value);
1200
+ };
1201
+ return /* @__PURE__ */ jsxs16("div", { className: cx("trc-tabs", className), children: [
1202
+ /* @__PURE__ */ jsx19("div", { role: "tablist", "aria-label": label, className: "trc-tabs__list", children: items2.map((item, i) => {
1203
+ const isSelected = item.value === selected;
1204
+ return /* @__PURE__ */ jsx19(
1205
+ "button",
1206
+ {
1207
+ ref: (el) => {
1208
+ tabRefs.current[i] = el;
1209
+ },
1210
+ type: "button",
1211
+ role: "tab",
1212
+ id: `${id}-tab-${i}`,
1213
+ "aria-selected": isSelected,
1214
+ "aria-controls": `${id}-panel-${i}`,
1215
+ disabled: item.disabled,
1216
+ tabIndex: isSelected ? 0 : -1,
1217
+ className: "trc-tabs__tab",
1218
+ onClick: () => setSelected(item.value),
1219
+ onKeyDown: (e) => onKeyDown(e, i),
1220
+ children: item.label
1221
+ },
1222
+ item.value
1223
+ );
1224
+ }) }),
1225
+ items2.map((item, i) => /* @__PURE__ */ jsx19(
1226
+ "div",
1227
+ {
1228
+ role: "tabpanel",
1229
+ id: `${id}-panel-${i}`,
1230
+ "aria-labelledby": `${id}-tab-${i}`,
1231
+ hidden: item.value !== selected,
1232
+ tabIndex: 0,
1233
+ className: "trc-tabs__panel",
1234
+ children: item.content
1235
+ },
1236
+ item.value
1237
+ ))
1238
+ ] });
1239
+ }
1240
+
1241
+ // src/components/segmented-control.tsx
1242
+ import { useId as useId8 } from "react";
1243
+ import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
1244
+ function SegmentedControl({
1245
+ options,
1246
+ value,
1247
+ defaultValue,
1248
+ onValueChange,
1249
+ label,
1250
+ name,
1251
+ size: size2 = "md",
1252
+ className
1253
+ }) {
1254
+ const [selected, setSelected] = useControllable(value, defaultValue ?? options[0]?.value ?? "", onValueChange);
1255
+ const generated = useId8();
1256
+ return /* @__PURE__ */ jsx20(
1257
+ "div",
1258
+ {
1259
+ role: "radiogroup",
1260
+ "aria-label": label,
1261
+ className: cx("trc-segmented", size2 === "sm" && "trc-segmented--sm", className),
1262
+ children: options.map((option) => /* @__PURE__ */ jsxs17("label", { className: "trc-segmented__option", children: [
1263
+ /* @__PURE__ */ jsx20(
1264
+ "input",
1265
+ {
1266
+ type: "radio",
1267
+ name: name ?? generated,
1268
+ value: option.value,
1269
+ checked: option.value === selected,
1270
+ disabled: option.disabled,
1271
+ onChange: () => setSelected(option.value),
1272
+ className: "trc-segmented__input"
1273
+ }
1274
+ ),
1275
+ /* @__PURE__ */ jsx20("span", { className: "trc-segmented__label", children: option.label })
1276
+ ] }, option.value))
1277
+ }
1278
+ );
1279
+ }
1280
+
1281
+ // src/components/accordion.tsx
1282
+ import { useId as useId9, useRef as useRef12 } from "react";
1283
+ import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
1284
+ function Accordion({
1285
+ items: items2,
1286
+ type = "single",
1287
+ value,
1288
+ defaultValue = [],
1289
+ onValueChange,
1290
+ headingLevel = 3,
1291
+ className
1292
+ }) {
1293
+ const [open, setOpen] = useControllable(value, defaultValue, onValueChange);
1294
+ const id = useId9();
1295
+ const buttons = useRef12([]);
1296
+ const Heading2 = `h${headingLevel}`;
1297
+ const enabled = items2.map((item, i) => item.disabled ? -1 : i).filter((i) => i >= 0);
1298
+ const toggle = (itemValue) => {
1299
+ const isOpen = open.includes(itemValue);
1300
+ if (type === "single") setOpen(isOpen ? [] : [itemValue]);
1301
+ else setOpen(isOpen ? open.filter((v) => v !== itemValue) : [...open, itemValue]);
1302
+ };
1303
+ const onKeyDown = (e, index) => {
1304
+ const position = enabled.indexOf(index);
1305
+ let target;
1306
+ if (e.key === "ArrowDown") target = enabled[(position + 1) % enabled.length];
1307
+ else if (e.key === "ArrowUp") target = enabled[(position - 1 + enabled.length) % enabled.length];
1308
+ else if (e.key === "Home") target = enabled[0];
1309
+ else if (e.key === "End") target = enabled[enabled.length - 1];
1310
+ if (target === void 0) return;
1311
+ e.preventDefault();
1312
+ buttons.current[target]?.focus();
1313
+ };
1314
+ return /* @__PURE__ */ jsx21("div", { className: cx("trc-accordion", className), children: items2.map((item, i) => {
1315
+ const isOpen = open.includes(item.value);
1316
+ return /* @__PURE__ */ jsxs18("div", { className: cx("trc-accordion__item", isOpen && "trc-accordion__item--open"), children: [
1317
+ /* @__PURE__ */ jsx21(Heading2, { className: "trc-accordion__heading", children: /* @__PURE__ */ jsxs18(
1318
+ "button",
1319
+ {
1320
+ ref: (el) => {
1321
+ buttons.current[i] = el;
1322
+ },
1323
+ type: "button",
1324
+ id: `${id}-trigger-${i}`,
1325
+ "aria-expanded": isOpen,
1326
+ "aria-controls": `${id}-panel-${i}`,
1327
+ disabled: item.disabled,
1328
+ className: "trc-accordion__trigger",
1329
+ onClick: () => toggle(item.value),
1330
+ onKeyDown: (e) => onKeyDown(e, i),
1331
+ children: [
1332
+ /* @__PURE__ */ jsx21("span", { children: item.title }),
1333
+ /* @__PURE__ */ jsx21(ChevronDown, { className: "trc-accordion__chevron" })
1334
+ ]
1335
+ }
1336
+ ) }),
1337
+ /* @__PURE__ */ jsx21(
1338
+ "div",
1339
+ {
1340
+ id: `${id}-panel-${i}`,
1341
+ role: "region",
1342
+ "aria-labelledby": `${id}-trigger-${i}`,
1343
+ className: "trc-accordion__panel",
1344
+ children: /* @__PURE__ */ jsx21("div", { className: "trc-accordion__inner", children: /* @__PURE__ */ jsx21("div", { className: "trc-accordion__content", children: item.content }) })
1345
+ }
1346
+ )
1347
+ ] }, item.value);
1348
+ }) });
1349
+ }
1350
+
1351
+ // src/components/steps.tsx
1352
+ import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
1353
+ function Steps({ items: items2, current, className }) {
1354
+ return /* @__PURE__ */ jsx22("ol", { className: cx("trc-steps", className), children: items2.map((item, i) => {
1355
+ const state = current === void 0 ? "default" : i < current ? "complete" : i === current ? "current" : "upcoming";
1356
+ return /* @__PURE__ */ jsxs19(
1357
+ "li",
1358
+ {
1359
+ className: cx("trc-steps__item", `trc-steps__item--${state}`),
1360
+ "aria-current": state === "current" ? "step" : void 0,
1361
+ children: [
1362
+ /* @__PURE__ */ jsx22("span", { "aria-hidden": true, className: "trc-steps__number", children: String(i + 1).padStart(2, "0") }),
1363
+ /* @__PURE__ */ jsxs19("div", { className: "trc-steps__text", children: [
1364
+ /* @__PURE__ */ jsx22("p", { className: "trc-steps__title", children: item.title }),
1365
+ item.description != null && /* @__PURE__ */ jsx22("div", { className: "trc-steps__description", children: item.description })
1366
+ ] })
1367
+ ]
1368
+ },
1369
+ i
1370
+ );
1371
+ }) });
1372
+ }
1373
+
1374
+ // src/components/breadcrumbs.tsx
1375
+ import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
1376
+ function Breadcrumbs({ items: items2, label = "Breadcrumb", linkAs: LinkTag = "a", className }) {
1377
+ return /* @__PURE__ */ jsx23("nav", { "aria-label": label, className: cx("trc-breadcrumbs", className), children: /* @__PURE__ */ jsx23("ol", { className: "trc-breadcrumbs__list", children: items2.map((item, i) => {
1378
+ const last = i === items2.length - 1;
1379
+ return /* @__PURE__ */ jsxs20("li", { className: "trc-breadcrumbs__item", children: [
1380
+ last || !item.href ? /* @__PURE__ */ jsx23("span", { "aria-current": last ? "page" : void 0, className: "trc-breadcrumbs__current", children: item.label }) : /* @__PURE__ */ jsx23(LinkTag, { href: item.href, className: "trc-breadcrumbs__link", children: item.label }),
1381
+ !last && /* @__PURE__ */ jsx23("span", { "aria-hidden": true, className: "trc-breadcrumbs__separator", children: "/" })
1382
+ ] }, i);
1383
+ }) }) });
1384
+ }
1385
+
1386
+ // src/utils/label.ts
1387
+ function fill(template, values) {
1388
+ return template.replace(/\{(\w+)\}/g, (match, key) => key in values ? String(values[key]) : match);
1389
+ }
1390
+
1391
+ // src/components/pagination.tsx
1392
+ import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
1393
+ function paginationRange(page, total, siblings = 1) {
1394
+ const slots = siblings * 2 + 5;
1395
+ if (total <= slots) return Array.from({ length: total }, (_, i) => i + 1);
1396
+ const range = (from, to) => Array.from({ length: to - from + 1 }, (_, i) => from + i);
1397
+ if (page <= siblings + 3) return [...range(1, slots - 2), "ellipsis", total];
1398
+ if (page >= total - siblings - 2) return [1, "ellipsis", ...range(total - slots + 3, total)];
1399
+ return [1, "ellipsis", ...range(page - siblings, page + siblings), "ellipsis", total];
1400
+ }
1401
+ function Pagination({
1402
+ page,
1403
+ total,
1404
+ onPageChange,
1405
+ href,
1406
+ siblings = 1,
1407
+ linkAs: LinkTag = "a",
1408
+ label = "Pagination",
1409
+ previousLabel = "Previous",
1410
+ nextLabel = "Next",
1411
+ pageLabel = "Page {page}",
1412
+ className
1413
+ }) {
1414
+ if (total <= 1) return null;
1415
+ const getHref = href === void 0 ? void 0 : typeof href === "string" ? (p) => href.replace("{page}", String(p)) : href;
1416
+ const control = (target, content, extra) => {
1417
+ const disabled = target < 1 || target > total;
1418
+ const common = {
1419
+ className: cx("trc-pagination__control", extra.className),
1420
+ "aria-label": extra.ariaLabel,
1421
+ "aria-current": extra.current ? "page" : void 0
1422
+ };
1423
+ if (getHref) {
1424
+ return disabled ? /* @__PURE__ */ jsx24("span", { ...common, "aria-disabled": "true", children: content }) : /* @__PURE__ */ jsx24(LinkTag, { href: getHref(target), ...common, children: content });
1425
+ }
1426
+ return /* @__PURE__ */ jsx24("button", { type: "button", ...common, disabled, onClick: () => !extra.current && onPageChange?.(target), children: content });
1427
+ };
1428
+ return /* @__PURE__ */ jsx24("nav", { "aria-label": label, className: cx("trc-pagination", className), children: /* @__PURE__ */ jsxs21("ul", { className: "trc-pagination__list", children: [
1429
+ /* @__PURE__ */ jsx24("li", { children: control(page - 1, /* @__PURE__ */ jsx24(ArrowLeft, {}), { className: "trc-pagination__step", ariaLabel: previousLabel }) }),
1430
+ paginationRange(page, total, siblings).map(
1431
+ (entry, i) => entry === "ellipsis" ? /* @__PURE__ */ jsx24("li", { "aria-hidden": true, className: "trc-pagination__ellipsis", children: "\u2026" }, `e${i}`) : /* @__PURE__ */ jsx24("li", { children: control(entry, entry, {
1432
+ className: "trc-pagination__page",
1433
+ ariaLabel: typeof pageLabel === "string" ? fill(pageLabel, { page: entry }) : pageLabel(entry),
1434
+ current: entry === page
1435
+ }) }, entry)
1436
+ ),
1437
+ /* @__PURE__ */ jsx24("li", { children: control(page + 1, /* @__PURE__ */ jsx24(ArrowRight, {}), { className: "trc-pagination__step", ariaLabel: nextLabel }) })
1438
+ ] }) });
1439
+ }
1440
+
1441
+ // src/components/language-switcher.tsx
1442
+ import { jsx as jsx25 } from "react/jsx-runtime";
1443
+ function LanguageSwitcher({
1444
+ languages,
1445
+ value,
1446
+ onChange,
1447
+ label = "Language",
1448
+ linkAs: LinkTag = "a",
1449
+ className
1450
+ }) {
1451
+ return /* @__PURE__ */ jsx25("nav", { "aria-label": label, className: cx("trc-languages", className), children: languages.map((language) => {
1452
+ const common = {
1453
+ lang: language.code,
1454
+ "aria-label": language.name,
1455
+ "aria-current": language.code === value ? "true" : void 0,
1456
+ className: "trc-languages__item",
1457
+ children: language.code.toUpperCase()
1458
+ };
1459
+ return language.href ? /* @__PURE__ */ jsx25(LinkTag, { href: language.href, hrefLang: language.code, ...common }, language.code) : /* @__PURE__ */ jsx25("button", { type: "button", onClick: () => onChange?.(language.code), ...common }, language.code);
1460
+ }) });
1461
+ }
1462
+
1463
+ // src/components/radio-group.tsx
1464
+ import { useId as useId10 } from "react";
1465
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
1466
+ function RadioGroup({
1467
+ label,
1468
+ options,
1469
+ value,
1470
+ defaultValue,
1471
+ onValueChange,
1472
+ name,
1473
+ hint,
1474
+ error,
1475
+ required = false,
1476
+ orientation = "vertical",
1477
+ className
1478
+ }) {
1479
+ const [selected, setSelected] = useControllable(
1480
+ value,
1481
+ defaultValue,
1482
+ (v) => v && onValueChange?.(v)
1483
+ );
1484
+ const id = useId10();
1485
+ const message = error ?? hint;
1486
+ return /* @__PURE__ */ jsxs22(
1487
+ "fieldset",
1488
+ {
1489
+ className: cx("trc-radio-group", className),
1490
+ "aria-describedby": message != null ? `${id}-message` : void 0,
1491
+ "aria-invalid": error != null || void 0,
1492
+ "aria-required": required || void 0,
1493
+ children: [
1494
+ /* @__PURE__ */ jsxs22("legend", { className: "trc-field__label", children: [
1495
+ label,
1496
+ required && /* @__PURE__ */ jsxs22("span", { "aria-hidden": true, className: "trc-field__required", children: [
1497
+ " ",
1498
+ "*"
1499
+ ] })
1500
+ ] }),
1501
+ /* @__PURE__ */ jsx26("div", { className: cx("trc-radio-group__options", orientation === "horizontal" && "trc-radio-group__options--row"), children: options.map((option) => /* @__PURE__ */ jsxs22("label", { className: "trc-toggle trc-toggle--radio", children: [
1502
+ /* @__PURE__ */ jsx26("span", { className: "trc-radio", children: /* @__PURE__ */ jsx26(
1503
+ "input",
1504
+ {
1505
+ type: "radio",
1506
+ name: name ?? id,
1507
+ value: option.value,
1508
+ checked: selected === option.value,
1509
+ disabled: option.disabled,
1510
+ required,
1511
+ onChange: () => setSelected(option.value),
1512
+ className: "trc-radio__input"
1513
+ }
1514
+ ) }),
1515
+ /* @__PURE__ */ jsxs22("span", { className: "trc-toggle__text", children: [
1516
+ /* @__PURE__ */ jsx26("span", { className: "trc-toggle__label", children: option.label }),
1517
+ option.description != null && /* @__PURE__ */ jsx26("span", { className: "trc-toggle__description", children: option.description })
1518
+ ] })
1519
+ ] }, option.value)) }),
1520
+ message != null && /* @__PURE__ */ jsx26("p", { id: `${id}-message`, className: cx("trc-field__message", error != null && "trc-field__message--error"), children: message })
1521
+ ]
1522
+ }
1523
+ );
1524
+ }
1525
+
1526
+ // src/components/password-input.tsx
1527
+ import { forwardRef as forwardRef4, useState as useState9 } from "react";
1528
+ import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
1529
+ var PasswordInput = forwardRef4(function PasswordInput2({ showLabel = "Show", hideLabel = "Hide", noun = "password", className, autoComplete = "current-password", ...props }, ref) {
1530
+ const [visible, setVisible] = useState9(false);
1531
+ const { id } = useFieldProps(props);
1532
+ return /* @__PURE__ */ jsxs23("span", { className: cx("trc-password", className), children: [
1533
+ /* @__PURE__ */ jsx27(Input, { ref, ...props, id, type: visible ? "text" : "password", autoComplete }),
1534
+ /* @__PURE__ */ jsxs23("button", { type: "button", className: "trc-password__toggle", "aria-controls": id, onClick: () => setVisible((v) => !v), children: [
1535
+ visible ? hideLabel : showLabel,
1536
+ " ",
1537
+ /* @__PURE__ */ jsx27("span", { className: "trc-visually-hidden", children: noun })
1538
+ ] })
1539
+ ] });
1540
+ });
1541
+
1542
+ // src/components/pin-input.tsx
1543
+ import { useRef as useRef13 } from "react";
1544
+ import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
1545
+ function PinInput({
1546
+ length = 6,
1547
+ value,
1548
+ defaultValue = "",
1549
+ onChange,
1550
+ onComplete,
1551
+ type = "numeric",
1552
+ mask = false,
1553
+ label = "Verification code",
1554
+ charLabel = "Character {index} of {length}",
1555
+ invalid,
1556
+ disabled,
1557
+ id,
1558
+ name,
1559
+ className
1560
+ }) {
1561
+ const [code, setCode] = useControllable(value, defaultValue, onChange);
1562
+ const inputs = useRef13([]);
1563
+ const field = useFieldProps({ id, invalid });
1564
+ const accepted = type === "numeric" ? /[0-9]/ : /[0-9a-z]/i;
1565
+ const clean = (text) => [...text].filter((c) => accepted.test(c)).join("");
1566
+ const update = (next) => {
1567
+ const trimmed = next.slice(0, length);
1568
+ setCode(trimmed);
1569
+ if (trimmed.length === length && !trimmed.includes(" ")) onComplete?.(trimmed);
1570
+ };
1571
+ const focus = (index) => {
1572
+ const target = inputs.current[Math.max(0, Math.min(index, length - 1))];
1573
+ target?.focus();
1574
+ target?.select();
1575
+ };
1576
+ const charAt = (i) => code[i] && code[i] !== " " ? code[i] : "";
1577
+ const setCharAt = (i, char) => {
1578
+ const chars = Array.from({ length }, (_, k) => code[k] ?? " ");
1579
+ chars[i] = char || " ";
1580
+ return chars.join("").replace(/\s+$/, "");
1581
+ };
1582
+ const onInput = (i, raw) => {
1583
+ const typed = clean(raw);
1584
+ if (!typed) return;
1585
+ if (typed.length > 1) {
1586
+ const merged = (code.slice(0, i).padEnd(i, " ") + typed).slice(0, length);
1587
+ update(merged);
1588
+ focus(Math.min(i + typed.length, length - 1));
1589
+ return;
1590
+ }
1591
+ update(setCharAt(i, typed.slice(-1)));
1592
+ focus(i + 1);
1593
+ };
1594
+ const onKeyDown = (i, e) => {
1595
+ if (e.key === "Backspace") {
1596
+ e.preventDefault();
1597
+ if (charAt(i)) update(setCharAt(i, ""));
1598
+ else if (i > 0) {
1599
+ update(setCharAt(i - 1, ""));
1600
+ focus(i - 1);
1601
+ }
1602
+ } else if (e.key === "ArrowLeft") {
1603
+ e.preventDefault();
1604
+ focus(i - 1);
1605
+ } else if (e.key === "ArrowRight") {
1606
+ e.preventDefault();
1607
+ focus(i + 1);
1608
+ } else if (e.key === "Home") {
1609
+ e.preventDefault();
1610
+ focus(0);
1611
+ } else if (e.key === "End") {
1612
+ e.preventDefault();
1613
+ focus(length - 1);
1614
+ }
1615
+ };
1616
+ const onPaste = (i, e) => {
1617
+ e.preventDefault();
1618
+ onInput(i, e.clipboardData.getData("text"));
1619
+ };
1620
+ return /* @__PURE__ */ jsxs24(
1621
+ "div",
1622
+ {
1623
+ role: "group",
1624
+ "aria-label": label,
1625
+ "aria-describedby": field["aria-describedby"],
1626
+ className: cx("trc-pin", className),
1627
+ children: [
1628
+ Array.from({ length }, (_, i) => /* @__PURE__ */ jsx28(
1629
+ "input",
1630
+ {
1631
+ ref: (el) => {
1632
+ inputs.current[i] = el;
1633
+ },
1634
+ id: i === 0 ? field.id : void 0,
1635
+ type: mask ? "password" : "text",
1636
+ inputMode: type === "numeric" ? "numeric" : "text",
1637
+ autoComplete: i === 0 ? "one-time-code" : "off",
1638
+ maxLength: length,
1639
+ "aria-label": typeof charLabel === "string" ? fill(charLabel, { index: i + 1, length }) : charLabel(i, length),
1640
+ "aria-invalid": field["aria-invalid"],
1641
+ disabled,
1642
+ value: charAt(i),
1643
+ onChange: (e) => onInput(i, e.target.value.replace(charAt(i), "") || e.target.value),
1644
+ onKeyDown: (e) => onKeyDown(i, e),
1645
+ onPaste: (e) => onPaste(i, e),
1646
+ onFocus: (e) => e.target.select(),
1647
+ className: "trc-control trc-pin__box"
1648
+ },
1649
+ i
1650
+ )),
1651
+ name && /* @__PURE__ */ jsx28("input", { type: "hidden", name, value: code.replace(/\s/g, "") })
1652
+ ]
1653
+ }
1654
+ );
1655
+ }
1656
+
1657
+ // src/components/combobox.tsx
1658
+ import { useEffect as useEffect9, useId as useId11, useRef as useRef14, useState as useState10 } from "react";
1659
+ import { jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
1660
+ var normalize = (text) => text.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
1661
+ function Combobox({
1662
+ options,
1663
+ value,
1664
+ defaultValue = null,
1665
+ onValueChange,
1666
+ placeholder,
1667
+ emptyText = "No results",
1668
+ invalid,
1669
+ disabled,
1670
+ required,
1671
+ id,
1672
+ name,
1673
+ className
1674
+ }) {
1675
+ const [selected, setSelected] = useControllable(value, defaultValue, onValueChange);
1676
+ const selectedOption = options.find((o) => o.value === selected) ?? null;
1677
+ const [query, setQuery] = useState10(selectedOption?.label ?? "");
1678
+ const [open, setOpen] = useState10(false);
1679
+ const [active, setActive] = useState10(-1);
1680
+ const wrapperRef = useRef14(null);
1681
+ const inputRef = useRef14(null);
1682
+ const listRef = useRef14(null);
1683
+ const listId = useId11();
1684
+ const a11y = useFieldProps({ id, invalid, required });
1685
+ const searching = query !== (selectedOption?.label ?? "");
1686
+ const visible = searching ? options.filter((o) => normalize(o.label).includes(normalize(query))) : options;
1687
+ const enabled = visible.map((o, i) => o.disabled ? -1 : i).filter((i) => i >= 0);
1688
+ useEffect9(() => {
1689
+ if (!open) setQuery(selectedOption?.label ?? "");
1690
+ }, [selectedOption?.label, open]);
1691
+ useAnchoredPosition(open, wrapperRef, listRef, { side: "bottom", align: "start", offset: 6 });
1692
+ useIsomorphicLayoutEffect(() => {
1693
+ if (open && listRef.current && wrapperRef.current)
1694
+ listRef.current.style.width = `${wrapperRef.current.offsetWidth}px`;
1695
+ }, [open]);
1696
+ useDismiss(open, [wrapperRef, listRef], () => close());
1697
+ useEffect9(() => {
1698
+ if (open && active >= 0)
1699
+ listRef.current?.querySelector(`[data-index="${active}"]`)?.scrollIntoView({ block: "nearest" });
1700
+ }, [open, active]);
1701
+ const openList = (index) => {
1702
+ setOpen(true);
1703
+ const current = visible.findIndex((o) => o.value === selected);
1704
+ setActive(index ?? (current >= 0 ? current : enabled[0] ?? -1));
1705
+ };
1706
+ const close = () => {
1707
+ setOpen(false);
1708
+ setActive(-1);
1709
+ setQuery(selectedOption?.label ?? "");
1710
+ };
1711
+ const choose = (option) => {
1712
+ if (option.disabled) return;
1713
+ setSelected(option.value);
1714
+ setQuery(option.label);
1715
+ setOpen(false);
1716
+ setActive(-1);
1717
+ };
1718
+ const onKeyDown = (e) => {
1719
+ const position = enabled.indexOf(active);
1720
+ if (e.key === "ArrowDown") {
1721
+ e.preventDefault();
1722
+ if (!open) openList();
1723
+ else setActive(enabled[(position + 1) % enabled.length] ?? -1);
1724
+ } else if (e.key === "ArrowUp") {
1725
+ e.preventDefault();
1726
+ if (!open) openList(enabled[enabled.length - 1]);
1727
+ else setActive(enabled[(position - 1 + enabled.length) % enabled.length] ?? -1);
1728
+ } else if (e.key === "Enter" && open) {
1729
+ e.preventDefault();
1730
+ const option = visible[active];
1731
+ if (option) choose(option);
1732
+ } else if (e.key === "Escape") {
1733
+ if (open) {
1734
+ e.preventDefault();
1735
+ close();
1736
+ } else if (query) {
1737
+ setQuery(selectedOption?.label ?? "");
1738
+ }
1739
+ } else if (e.key === "Tab" && open) {
1740
+ close();
1741
+ }
1742
+ };
1743
+ return /* @__PURE__ */ jsxs25("div", { ref: wrapperRef, className: cx("trc-combobox", className), children: [
1744
+ /* @__PURE__ */ jsx29(
1745
+ "input",
1746
+ {
1747
+ ref: inputRef,
1748
+ type: "text",
1749
+ role: "combobox",
1750
+ "aria-expanded": open,
1751
+ "aria-controls": listId,
1752
+ "aria-autocomplete": "list",
1753
+ "aria-activedescendant": open && active >= 0 ? `${listId}-${active}` : void 0,
1754
+ autoComplete: "off",
1755
+ placeholder,
1756
+ disabled,
1757
+ value: query,
1758
+ onChange: (e) => {
1759
+ setQuery(e.target.value);
1760
+ setOpen(true);
1761
+ setActive(0);
1762
+ },
1763
+ onClick: () => open ? close() : openList(),
1764
+ onKeyDown,
1765
+ className: "trc-control trc-combobox__input",
1766
+ ...a11y
1767
+ }
1768
+ ),
1769
+ /* @__PURE__ */ jsx29(ChevronDown, { "aria-hidden": true, className: "trc-combobox__chevron" }),
1770
+ name && /* @__PURE__ */ jsx29("input", { type: "hidden", name, value: selected ?? "" }),
1771
+ open && /* @__PURE__ */ jsx29(Portal, { children: /* @__PURE__ */ jsxs25("ul", { ref: listRef, id: listId, role: "listbox", className: "trc-floating trc-listbox", children: [
1772
+ visible.length === 0 && /* @__PURE__ */ jsx29("li", { role: "presentation", className: "trc-listbox__empty", children: emptyText }),
1773
+ visible.map((option, i) => /* @__PURE__ */ jsxs25(
1774
+ "li",
1775
+ {
1776
+ id: `${listId}-${i}`,
1777
+ "data-index": i,
1778
+ role: "option",
1779
+ "aria-selected": option.value === selected,
1780
+ "aria-disabled": option.disabled || void 0,
1781
+ className: cx("trc-listbox__option", i === active && "trc-listbox__option--active"),
1782
+ onMouseDown: (e) => e.preventDefault(),
1783
+ onMouseMove: () => !option.disabled && setActive(i),
1784
+ onClick: () => choose(option),
1785
+ children: [
1786
+ /* @__PURE__ */ jsx29("span", { className: "trc-listbox__label", children: option.label }),
1787
+ option.description != null && /* @__PURE__ */ jsx29("span", { className: "trc-listbox__description", children: option.description })
1788
+ ]
1789
+ },
1790
+ option.value
1791
+ ))
1792
+ ] }) })
1793
+ ] });
1794
+ }
1795
+
1796
+ // src/components/file-input.tsx
1797
+ import {
1798
+ forwardRef as forwardRef5,
1799
+ useId as useId12,
1800
+ useState as useState11
1801
+ } from "react";
1802
+ import { jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
1803
+ function formatBytes(bytes) {
1804
+ if (bytes < 1024) return `${bytes} B`;
1805
+ const units = ["KB", "MB", "GB"];
1806
+ let value = bytes / 1024;
1807
+ let unit = 0;
1808
+ while (value >= 1024 && unit < units.length - 1) {
1809
+ value /= 1024;
1810
+ unit++;
1811
+ }
1812
+ return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit]}`;
1813
+ }
1814
+ function acceptsFile(file, accept) {
1815
+ if (!accept) return true;
1816
+ const name = file.name.toLowerCase();
1817
+ const type = file.type.toLowerCase();
1818
+ return accept.split(",").map((rule) => rule.trim().toLowerCase()).filter(Boolean).some(
1819
+ (rule) => rule.startsWith(".") ? name.endsWith(rule) : rule.endsWith("/*") ? type.startsWith(rule.slice(0, -1)) : type === rule
1820
+ );
1821
+ }
1822
+ function validateFiles(files, { accept, maxSize, maxFiles }, alreadyAccepted = 0) {
1823
+ const accepted = [];
1824
+ const rejected = [];
1825
+ for (const file of files) {
1826
+ if (!acceptsFile(file, accept)) rejected.push({ file, reason: "type" });
1827
+ else if (maxSize !== void 0 && file.size > maxSize) rejected.push({ file, reason: "size" });
1828
+ else if (maxFiles !== void 0 && alreadyAccepted + accepted.length >= maxFiles)
1829
+ rejected.push({ file, reason: "count" });
1830
+ else accepted.push(file);
1831
+ }
1832
+ return { accepted, rejected };
1833
+ }
1834
+ var FileInput = forwardRef5(function FileInput2({
1835
+ onFilesChange,
1836
+ buttonLabel = "Choose a file",
1837
+ placeholder = "No file chosen",
1838
+ countLabel = "{count} files",
1839
+ disabled,
1840
+ invalid,
1841
+ className,
1842
+ ...props
1843
+ }, ref) {
1844
+ const [files, setFiles] = useState11([]);
1845
+ const a11y = useFieldProps({ ...props, invalid });
1846
+ const summary = files.length === 0 ? placeholder : files.length === 1 ? files[0].name : typeof countLabel === "string" ? fill(countLabel, { count: files.length }) : countLabel(files.length);
1847
+ return /* @__PURE__ */ jsxs26("label", { className: cx("trc-file-input", disabled && "trc-file-input--disabled", className), children: [
1848
+ /* @__PURE__ */ jsx30(
1849
+ "input",
1850
+ {
1851
+ ref,
1852
+ ...props,
1853
+ ...a11y,
1854
+ type: "file",
1855
+ disabled,
1856
+ className: "trc-visually-hidden",
1857
+ onChange: (e) => {
1858
+ const chosen = [...e.target.files ?? []];
1859
+ setFiles(chosen);
1860
+ onFilesChange?.(chosen);
1861
+ }
1862
+ }
1863
+ ),
1864
+ /* @__PURE__ */ jsx30("span", { "aria-hidden": true, className: "trc-file-input__button", children: buttonLabel }),
1865
+ /* @__PURE__ */ jsx30("span", { className: cx("trc-file-input__name", files.length === 0 && "trc-file-input__name--empty"), children: summary })
1866
+ ] });
1867
+ });
1868
+ var DEFAULT_REJECTIONS = {
1869
+ type: "{name}: this type of file is not accepted",
1870
+ size: "{name}: the file is too large",
1871
+ count: "{name}: too many files"
1872
+ };
1873
+ function Dropzone({
1874
+ accept,
1875
+ maxSize,
1876
+ maxFiles,
1877
+ multiple = true,
1878
+ onFilesChange,
1879
+ title = "Drop files here or",
1880
+ browseLabel = "browse",
1881
+ hint,
1882
+ removeLabel = "Remove {name}",
1883
+ rejectionLabel = DEFAULT_REJECTIONS,
1884
+ name,
1885
+ disabled,
1886
+ id,
1887
+ className
1888
+ }) {
1889
+ const [files, setFiles] = useState11([]);
1890
+ const [rejections, setRejections] = useState11([]);
1891
+ const rejectionText = (rejection) => typeof rejectionLabel === "function" ? rejectionLabel(rejection) : fill(rejectionLabel[rejection.reason] ?? DEFAULT_REJECTIONS[rejection.reason], { name: rejection.file.name });
1892
+ const [dragging, setDragging] = useState11(false);
1893
+ const hintId = useId12();
1894
+ const a11y = useFieldProps({ id });
1895
+ const add = (incoming) => {
1896
+ const base2 = multiple ? files : [];
1897
+ const { accepted, rejected } = validateFiles(
1898
+ multiple ? incoming : incoming.slice(0, 1),
1899
+ { accept, maxSize, maxFiles },
1900
+ base2.length
1901
+ );
1902
+ const next = [...base2, ...accepted];
1903
+ setFiles(next);
1904
+ setRejections(rejected);
1905
+ onFilesChange?.(next);
1906
+ };
1907
+ const remove = (index) => {
1908
+ const next = files.filter((_, i) => i !== index);
1909
+ setFiles(next);
1910
+ setRejections([]);
1911
+ onFilesChange?.(next);
1912
+ };
1913
+ const onDrop = (e) => {
1914
+ e.preventDefault();
1915
+ setDragging(false);
1916
+ if (!disabled) add([...e.dataTransfer.files]);
1917
+ };
1918
+ return /* @__PURE__ */ jsxs26("div", { className: cx("trc-dropzone", className), children: [
1919
+ /* @__PURE__ */ jsxs26(
1920
+ "label",
1921
+ {
1922
+ className: cx(
1923
+ "trc-dropzone__area",
1924
+ dragging && "trc-dropzone__area--dragging",
1925
+ disabled && "trc-dropzone__area--disabled"
1926
+ ),
1927
+ onDragEnter: (e) => {
1928
+ e.preventDefault();
1929
+ if (!disabled) setDragging(true);
1930
+ },
1931
+ onDragOver: (e) => e.preventDefault(),
1932
+ onDragLeave: (e) => {
1933
+ if (!e.currentTarget.contains(e.relatedTarget)) setDragging(false);
1934
+ },
1935
+ onDrop,
1936
+ children: [
1937
+ /* @__PURE__ */ jsx30(
1938
+ "input",
1939
+ {
1940
+ type: "file",
1941
+ accept,
1942
+ multiple,
1943
+ name,
1944
+ disabled,
1945
+ className: "trc-visually-hidden",
1946
+ onChange: (e) => {
1947
+ add([...e.target.files ?? []]);
1948
+ e.target.value = "";
1949
+ },
1950
+ ...a11y,
1951
+ "aria-describedby": cx(a11y["aria-describedby"], hint != null && hintId) || void 0
1952
+ }
1953
+ ),
1954
+ /* @__PURE__ */ jsxs26("span", { className: "trc-dropzone__title", children: [
1955
+ title,
1956
+ " ",
1957
+ /* @__PURE__ */ jsx30("span", { className: "trc-dropzone__browse", children: browseLabel })
1958
+ ] }),
1959
+ hint != null && /* @__PURE__ */ jsx30("span", { id: hintId, "aria-hidden": true, className: "trc-dropzone__hint", children: hint })
1960
+ ]
1961
+ }
1962
+ ),
1963
+ /* @__PURE__ */ jsx30("div", { "aria-live": "polite", children: rejections.length > 0 && /* @__PURE__ */ jsx30("ul", { className: "trc-dropzone__rejections", children: rejections.map((rejection, i) => /* @__PURE__ */ jsx30("li", { children: rejectionText(rejection) }, i)) }) }),
1964
+ files.length > 0 && /* @__PURE__ */ jsx30("ul", { className: "trc-dropzone__files", children: files.map((file, i) => /* @__PURE__ */ jsxs26("li", { className: "trc-dropzone__file", children: [
1965
+ /* @__PURE__ */ jsx30("span", { className: "trc-dropzone__file-name", children: file.name }),
1966
+ /* @__PURE__ */ jsx30("span", { className: "trc-dropzone__file-size", children: formatBytes(file.size) }),
1967
+ /* @__PURE__ */ jsx30(
1968
+ "button",
1969
+ {
1970
+ type: "button",
1971
+ className: "trc-dropzone__remove",
1972
+ "aria-label": typeof removeLabel === "string" ? fill(removeLabel, { name: file.name }) : removeLabel(file),
1973
+ onClick: () => remove(i),
1974
+ children: /* @__PURE__ */ jsx30(Close, {})
1975
+ }
1976
+ )
1977
+ ] }, `${file.name}-${i}`)) })
1978
+ ] });
1979
+ }
1980
+
1981
+ // src/components/slider.tsx
1982
+ import { forwardRef as forwardRef6 } from "react";
1983
+ import { jsx as jsx31, jsxs as jsxs27 } from "react/jsx-runtime";
1984
+ var Slider = forwardRef6(function Slider2({
1985
+ value,
1986
+ defaultValue,
1987
+ onValueChange,
1988
+ min = 0,
1989
+ max = 100,
1990
+ step = 1,
1991
+ showValue = false,
1992
+ formatValue = (v) => v,
1993
+ invalid,
1994
+ className,
1995
+ style,
1996
+ ...props
1997
+ }, ref) {
1998
+ const [current, setCurrent] = useControllable(value, defaultValue ?? min, onValueChange);
1999
+ const a11y = useFieldProps({ ...props, invalid });
2000
+ const progress = max > min ? (current - min) / (max - min) * 100 : 0;
2001
+ const formatted = formatValue(current);
2002
+ return /* @__PURE__ */ jsxs27("div", { className: cx("trc-slider", className), children: [
2003
+ /* @__PURE__ */ jsx31(
2004
+ "input",
2005
+ {
2006
+ ref,
2007
+ ...props,
2008
+ ...a11y,
2009
+ type: "range",
2010
+ min,
2011
+ max,
2012
+ step,
2013
+ value: current,
2014
+ "aria-valuetext": typeof formatted === "string" ? formatted : void 0,
2015
+ onChange: (e) => setCurrent(Number(e.target.value)),
2016
+ className: "trc-slider__input",
2017
+ style: { ...style, "--trc-slider-progress": `${progress}%` }
2018
+ }
2019
+ ),
2020
+ showValue && /* @__PURE__ */ jsx31("output", { htmlFor: a11y.id, className: "trc-slider__value", "aria-hidden": true, children: formatted })
2021
+ ] });
2022
+ });
2023
+
2024
+ // src/components/date-picker.tsx
2025
+ import { useEffect as useEffect10, useId as useId13, useRef as useRef15, useState as useState12 } from "react";
2026
+
2027
+ // src/utils/dates.ts
2028
+ var startOfDay = (date) => new Date(date.getFullYear(), date.getMonth(), date.getDate());
2029
+ var addDays = (date, days) => new Date(date.getFullYear(), date.getMonth(), date.getDate() + days);
2030
+ function addMonths(date, months) {
2031
+ const target = new Date(date.getFullYear(), date.getMonth() + months, 1);
2032
+ const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
2033
+ return new Date(target.getFullYear(), target.getMonth(), Math.min(date.getDate(), lastDay));
2034
+ }
2035
+ var sameDay = (a, b) => !!a && !!b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
2036
+ var toISODate = (date) => `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
2037
+ function gridStart(month, weekStartsOn) {
2038
+ const first = new Date(month.getFullYear(), month.getMonth(), 1);
2039
+ const offset = (first.getDay() - weekStartsOn + 7) % 7;
2040
+ return addDays(first, -offset);
2041
+ }
2042
+ var isBefore = (a, b) => startOfDay(a).getTime() < startOfDay(b).getTime();
2043
+
2044
+ // src/components/date-picker.tsx
2045
+ import { jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
2046
+ function DatePicker({
2047
+ value,
2048
+ defaultValue = null,
2049
+ onValueChange,
2050
+ min,
2051
+ max,
2052
+ locale,
2053
+ weekStartsOn = 1,
2054
+ placeholder = "Choose a date",
2055
+ formatDate,
2056
+ name,
2057
+ label = "Choose a date",
2058
+ previousMonthLabel = "Previous month",
2059
+ nextMonthLabel = "Next month",
2060
+ invalid,
2061
+ disabled,
2062
+ required,
2063
+ id,
2064
+ className
2065
+ }) {
2066
+ const [selected, setSelected] = useControllable(value, defaultValue, onValueChange);
2067
+ const [open, setOpen] = useState12(false);
2068
+ const [focused, setFocused] = useState12(() => startOfDay(selected ?? /* @__PURE__ */ new Date()));
2069
+ const triggerRef = useRef15(null);
2070
+ const panelRef = useRef15(null);
2071
+ const uid = useId13();
2072
+ const a11y = useFieldProps({ id, invalid, required });
2073
+ const display = new Intl.DateTimeFormat(locale, { dateStyle: "medium" });
2074
+ const full = new Intl.DateTimeFormat(locale, { dateStyle: "full" });
2075
+ const monthTitle = new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" });
2076
+ const weekdayShort = new Intl.DateTimeFormat(locale, { weekday: "short" });
2077
+ const weekdayLong = new Intl.DateTimeFormat(locale, { weekday: "long" });
2078
+ const format = formatDate ?? ((d) => display.format(d));
2079
+ const outOfRange = (date) => !!min && isBefore(date, min) || !!max && isBefore(max, date);
2080
+ const clamp = (date) => min && isBefore(date, min) ? startOfDay(min) : max && isBefore(max, date) ? startOfDay(max) : date;
2081
+ const openCalendar = () => {
2082
+ setFocused(clamp(startOfDay(selected ?? /* @__PURE__ */ new Date())));
2083
+ setOpen(true);
2084
+ };
2085
+ const close = (returnFocus = true) => {
2086
+ setOpen(false);
2087
+ if (returnFocus) triggerRef.current?.focus();
2088
+ };
2089
+ const choose = (date) => {
2090
+ if (outOfRange(date)) return;
2091
+ setSelected(date);
2092
+ close();
2093
+ };
2094
+ useAnchoredPosition(open, triggerRef, panelRef, { side: "bottom", align: "start" });
2095
+ useDismiss(open, [triggerRef, panelRef], (reason) => close(reason === "escape"));
2096
+ useEffect10(() => {
2097
+ if (open) panelRef.current?.querySelector(`[data-date="${toISODate(focused)}"]`)?.focus();
2098
+ }, [open, focused]);
2099
+ const onGridKeyDown = (e) => {
2100
+ const moves = {
2101
+ ArrowLeft: () => addDays(focused, -1),
2102
+ ArrowRight: () => addDays(focused, 1),
2103
+ ArrowUp: () => addDays(focused, -7),
2104
+ ArrowDown: () => addDays(focused, 7),
2105
+ Home: () => addDays(focused, -((focused.getDay() - weekStartsOn + 7) % 7)),
2106
+ End: () => addDays(focused, 6 - (focused.getDay() - weekStartsOn + 7) % 7),
2107
+ PageUp: () => addMonths(focused, e.shiftKey ? -12 : -1),
2108
+ PageDown: () => addMonths(focused, e.shiftKey ? 12 : 1)
2109
+ };
2110
+ const move = moves[e.key];
2111
+ if (move) {
2112
+ e.preventDefault();
2113
+ setFocused(clamp(move()));
2114
+ }
2115
+ };
2116
+ const onPanelKeyDown = (e) => {
2117
+ if (e.key !== "Tab") return;
2118
+ const items2 = [
2119
+ ...panelRef.current?.querySelectorAll("button:not([disabled]):not([tabindex='-1'])") ?? []
2120
+ ];
2121
+ const first = items2[0];
2122
+ const last = items2[items2.length - 1];
2123
+ if (e.shiftKey && document.activeElement === first) {
2124
+ e.preventDefault();
2125
+ last?.focus();
2126
+ } else if (!e.shiftKey && document.activeElement === last) {
2127
+ e.preventDefault();
2128
+ first?.focus();
2129
+ }
2130
+ };
2131
+ const start = gridStart(focused, weekStartsOn);
2132
+ const days = Array.from({ length: 42 }, (_, i) => addDays(start, i));
2133
+ const weeks = Array.from({ length: 6 }, (_, w) => days.slice(w * 7, w * 7 + 7));
2134
+ const today = startOfDay(/* @__PURE__ */ new Date());
2135
+ const valueId = `${uid}-value`;
2136
+ return /* @__PURE__ */ jsxs28("div", { className: cx("trc-date-picker", className), children: [
2137
+ /* @__PURE__ */ jsxs28(
2138
+ "button",
2139
+ {
2140
+ ref: triggerRef,
2141
+ type: "button",
2142
+ id: a11y.id,
2143
+ "aria-haspopup": "dialog",
2144
+ "aria-expanded": open,
2145
+ "aria-invalid": a11y["aria-invalid"],
2146
+ "aria-describedby": cx(valueId, a11y["aria-describedby"]) || void 0,
2147
+ disabled,
2148
+ onClick: () => open ? close() : openCalendar(),
2149
+ className: cx("trc-control", "trc-date-picker__trigger", !selected && "trc-date-picker__trigger--empty"),
2150
+ children: [
2151
+ /* @__PURE__ */ jsx32("span", { id: valueId, children: selected ? format(selected) : placeholder }),
2152
+ /* @__PURE__ */ jsx32(Calendar, { className: "trc-date-picker__icon" })
2153
+ ]
2154
+ }
2155
+ ),
2156
+ name && /* @__PURE__ */ jsx32("input", { type: "hidden", name, value: selected ? toISODate(selected) : "", required }),
2157
+ open && /* @__PURE__ */ jsx32(Portal, { children: /* @__PURE__ */ jsxs28(
2158
+ "div",
2159
+ {
2160
+ ref: panelRef,
2161
+ role: "dialog",
2162
+ "aria-modal": "true",
2163
+ "aria-label": label,
2164
+ className: "trc-floating trc-calendar",
2165
+ onKeyDown: onPanelKeyDown,
2166
+ children: [
2167
+ /* @__PURE__ */ jsxs28("div", { className: "trc-calendar__head", children: [
2168
+ /* @__PURE__ */ jsx32(
2169
+ "button",
2170
+ {
2171
+ type: "button",
2172
+ className: "trc-calendar__nav",
2173
+ "aria-label": previousMonthLabel,
2174
+ onClick: () => setFocused(clamp(addMonths(focused, -1))),
2175
+ children: /* @__PURE__ */ jsx32(ChevronLeft, {})
2176
+ }
2177
+ ),
2178
+ /* @__PURE__ */ jsx32("p", { id: `${uid}-title`, className: "trc-calendar__title", "aria-live": "polite", children: monthTitle.format(focused) }),
2179
+ /* @__PURE__ */ jsx32(
2180
+ "button",
2181
+ {
2182
+ type: "button",
2183
+ className: "trc-calendar__nav",
2184
+ "aria-label": nextMonthLabel,
2185
+ onClick: () => setFocused(clamp(addMonths(focused, 1))),
2186
+ children: /* @__PURE__ */ jsx32(ChevronRight, {})
2187
+ }
2188
+ )
2189
+ ] }),
2190
+ /* @__PURE__ */ jsxs28(
2191
+ "table",
2192
+ {
2193
+ role: "grid",
2194
+ "aria-labelledby": `${uid}-title`,
2195
+ className: "trc-calendar__grid",
2196
+ onKeyDown: onGridKeyDown,
2197
+ children: [
2198
+ /* @__PURE__ */ jsx32("thead", { children: /* @__PURE__ */ jsx32("tr", { children: weeks[0].map((day) => /* @__PURE__ */ jsx32("th", { scope: "col", abbr: weekdayLong.format(day), className: "trc-calendar__weekday", children: weekdayShort.format(day).replace(".", "").slice(0, 3) }, day.getDay())) }) }),
2199
+ /* @__PURE__ */ jsx32("tbody", { children: weeks.map((week, w) => /* @__PURE__ */ jsx32("tr", { children: week.map((day) => {
2200
+ const isSelected = sameDay(day, selected);
2201
+ const disabledDay = outOfRange(day);
2202
+ return /* @__PURE__ */ jsx32("td", { role: "gridcell", "aria-selected": isSelected || void 0, children: /* @__PURE__ */ jsx32(
2203
+ "button",
2204
+ {
2205
+ type: "button",
2206
+ "data-date": toISODate(day),
2207
+ tabIndex: sameDay(day, focused) ? 0 : -1,
2208
+ "aria-label": full.format(day),
2209
+ "aria-current": sameDay(day, today) ? "date" : void 0,
2210
+ disabled: disabledDay,
2211
+ onClick: () => choose(day),
2212
+ onKeyDown: (e) => {
2213
+ if (e.key === "Enter" || e.key === " ") {
2214
+ e.preventDefault();
2215
+ choose(day);
2216
+ }
2217
+ },
2218
+ className: cx(
2219
+ "trc-calendar__day",
2220
+ day.getMonth() !== focused.getMonth() && "trc-calendar__day--outside",
2221
+ isSelected && "trc-calendar__day--selected"
2222
+ ),
2223
+ children: day.getDate()
2224
+ }
2225
+ ) }, toISODate(day));
2226
+ }) }, w)) })
2227
+ ]
2228
+ }
2229
+ )
2230
+ ]
2231
+ }
2232
+ ) })
2233
+ ] });
2234
+ }
2235
+
2236
+ // src/components/avatar.tsx
2237
+ import { Children, useState as useState13 } from "react";
2238
+ import { jsx as jsx33, jsxs as jsxs29 } from "react/jsx-runtime";
2239
+ function initials(name) {
2240
+ const words = name.trim().split(/\s+/).filter(Boolean);
2241
+ const letters = words.length > 1 ? [words[0][0], words[words.length - 1][0]] : [words[0]?.[0]];
2242
+ return letters.filter(Boolean).join("").toUpperCase();
2243
+ }
2244
+ function Avatar({ name, src, size: size2 = "md", className }) {
2245
+ const [failed, setFailed] = useState13(false);
2246
+ const showImage = src && !failed;
2247
+ return /* @__PURE__ */ jsx33("span", { className: cx("trc-avatar", `trc-avatar--${size2}`, className), children: showImage ? /* @__PURE__ */ jsx33("img", { src, alt: name, className: "trc-avatar__image", onError: () => setFailed(true) }) : /* @__PURE__ */ jsx33("span", { role: "img", "aria-label": name, className: "trc-avatar__initials", children: initials(name) }) });
2248
+ }
2249
+ function AvatarGroup({
2250
+ children,
2251
+ max = 4,
2252
+ moreLabel = "{count} more",
2253
+ size: size2 = "md",
2254
+ className
2255
+ }) {
2256
+ const items2 = Children.toArray(children);
2257
+ const hidden = items2.length - max;
2258
+ return /* @__PURE__ */ jsxs29("span", { className: cx("trc-avatar-group", className), children: [
2259
+ items2.slice(0, max),
2260
+ hidden > 0 && /* @__PURE__ */ jsx33("span", { className: cx("trc-avatar", `trc-avatar--${size2}`, "trc-avatar--more"), children: /* @__PURE__ */ jsxs29(
2261
+ "span",
2262
+ {
2263
+ role: "img",
2264
+ "aria-label": typeof moreLabel === "string" ? fill(moreLabel, { count: hidden }) : moreLabel(hidden),
2265
+ className: "trc-avatar__initials",
2266
+ children: [
2267
+ "+",
2268
+ hidden
2269
+ ]
2270
+ }
2271
+ ) })
2272
+ ] });
2273
+ }
2274
+
2275
+ // src/components/tag.tsx
2276
+ import { jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
2277
+ function Tag({ tone = "default", onRemove, removeLabel = "Remove", className, children, ...props }) {
2278
+ return /* @__PURE__ */ jsxs30("span", { className: cx("trc-tag", tone === "accent" && "trc-tag--accent", className), ...props, children: [
2279
+ children,
2280
+ onRemove && /* @__PURE__ */ jsx34(
2281
+ "button",
2282
+ {
2283
+ type: "button",
2284
+ className: "trc-tag__remove",
2285
+ "aria-label": `${removeLabel} ${typeof children === "string" ? children : ""}`.trim(),
2286
+ onClick: onRemove,
2287
+ children: /* @__PURE__ */ jsx34(Close, {})
2288
+ }
2289
+ )
2290
+ ] });
2291
+ }
2292
+
2293
+ // src/components/stat.tsx
2294
+ import { jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
2295
+ function Stat({ label, value, change, changeTone = "neutral", description, className }) {
2296
+ return /* @__PURE__ */ jsxs31("dl", { className: cx("trc-stat", className), children: [
2297
+ /* @__PURE__ */ jsx35("dt", { className: "trc-stat__label", children: label }),
2298
+ /* @__PURE__ */ jsx35("dd", { className: "trc-stat__value", children: value }),
2299
+ (change != null || description != null) && /* @__PURE__ */ jsxs31("dd", { className: "trc-stat__meta", children: [
2300
+ change != null && /* @__PURE__ */ jsx35("span", { className: cx("trc-stat__change", `trc-stat__change--${changeTone}`), children: change }),
2301
+ description != null && /* @__PURE__ */ jsx35("span", { className: "trc-stat__description", children: description })
2302
+ ] })
2303
+ ] });
2304
+ }
2305
+
2306
+ // src/components/table.tsx
2307
+ import { useState as useState14 } from "react";
2308
+ import { jsx as jsx36, jsxs as jsxs32 } from "react/jsx-runtime";
2309
+ var compare = (a, b) => {
2310
+ if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
2311
+ if (typeof a === "number" && typeof b === "number") return a - b;
2312
+ return String(a ?? "").localeCompare(String(b ?? ""), void 0, { numeric: true, sensitivity: "base" });
2313
+ };
2314
+ function Table({
2315
+ columns,
2316
+ rows,
2317
+ rowKey,
2318
+ caption,
2319
+ hideCaption = false,
2320
+ defaultSort,
2321
+ dense = false,
2322
+ emptyText = "No data",
2323
+ className
2324
+ }) {
2325
+ const [sort, setSort] = useState14(defaultSort);
2326
+ const field = (row, key) => row[key];
2327
+ const sorted = (() => {
2328
+ const column = columns.find((c) => c.key === sort?.key);
2329
+ if (!sort || !column) return rows;
2330
+ const value = (row) => column.sortValue ? column.sortValue(row) : field(row, column.key);
2331
+ const ordered = [...rows].sort((a, b) => compare(value(a), value(b)));
2332
+ return sort.direction === "ascending" ? ordered : ordered.reverse();
2333
+ })();
2334
+ const toggle = (key) => setSort(
2335
+ (current) => current?.key === key && current.direction === "ascending" ? { key, direction: "descending" } : { key, direction: "ascending" }
2336
+ );
2337
+ return /* @__PURE__ */ jsx36(
2338
+ "div",
2339
+ {
2340
+ className: cx("trc-table", dense && "trc-table--dense", className),
2341
+ role: "region",
2342
+ "aria-label": typeof caption === "string" ? caption : void 0,
2343
+ tabIndex: 0,
2344
+ children: /* @__PURE__ */ jsxs32("table", { className: "trc-table__table", children: [
2345
+ /* @__PURE__ */ jsx36("caption", { className: cx("trc-table__caption", hideCaption && "trc-visually-hidden"), children: caption }),
2346
+ /* @__PURE__ */ jsx36("thead", { children: /* @__PURE__ */ jsx36("tr", { children: columns.map((column) => {
2347
+ const direction = sort?.key === column.key ? sort.direction : void 0;
2348
+ return /* @__PURE__ */ jsx36(
2349
+ "th",
2350
+ {
2351
+ scope: "col",
2352
+ "aria-sort": column.sortable ? direction ?? "none" : void 0,
2353
+ className: cx("trc-table__head", column.align && `trc-table__cell--${column.align}`),
2354
+ children: column.sortable ? /* @__PURE__ */ jsxs32("button", { type: "button", className: "trc-table__sort", onClick: () => toggle(column.key), children: [
2355
+ column.header,
2356
+ /* @__PURE__ */ jsx36(
2357
+ ChevronDown,
2358
+ {
2359
+ className: cx(
2360
+ "trc-table__sort-icon",
2361
+ direction && "trc-table__sort-icon--active",
2362
+ direction === "ascending" && "trc-table__sort-icon--up"
2363
+ )
2364
+ }
2365
+ )
2366
+ ] }) : column.header
2367
+ },
2368
+ column.key
2369
+ );
2370
+ }) }) }),
2371
+ /* @__PURE__ */ jsx36("tbody", { children: sorted.length === 0 ? /* @__PURE__ */ jsx36("tr", { children: /* @__PURE__ */ jsx36("td", { colSpan: columns.length, className: "trc-table__empty", children: emptyText }) }) : sorted.map((row) => /* @__PURE__ */ jsx36("tr", { children: columns.map((column) => /* @__PURE__ */ jsx36(
2372
+ "td",
2373
+ {
2374
+ className: cx("trc-table__cell", column.align && `trc-table__cell--${column.align}`),
2375
+ children: column.render ? column.render(row) : field(row, column.key)
2376
+ },
2377
+ column.key
2378
+ )) }, rowKey(row))) })
2379
+ ] })
2380
+ }
2381
+ );
2382
+ }
2383
+
2384
+ // src/components/loading.tsx
2385
+ import { useId as useId14 } from "react";
2386
+ import { jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
2387
+ var size = (value) => typeof value === "number" ? `${value}px` : value;
2388
+ function Skeleton({ width, height, circle = false, lines, className }) {
2389
+ if (lines && lines > 1) {
2390
+ return /* @__PURE__ */ jsx37("span", { "aria-hidden": true, className: cx("trc-skeleton-lines", className), style: { width: size(width) }, children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx37(
2391
+ "span",
2392
+ {
2393
+ className: "trc-skeleton trc-skeleton--text",
2394
+ style: { width: i === lines - 1 ? "60%" : "100%" }
2395
+ },
2396
+ i
2397
+ )) });
2398
+ }
2399
+ return /* @__PURE__ */ jsx37(
2400
+ "span",
2401
+ {
2402
+ "aria-hidden": true,
2403
+ className: cx(
2404
+ "trc-skeleton",
2405
+ circle && "trc-skeleton--circle",
2406
+ !height && !circle && "trc-skeleton--text",
2407
+ className
2408
+ ),
2409
+ style: { width: size(width), height: size(height) ?? (circle ? size(width) : void 0) }
2410
+ }
2411
+ );
2412
+ }
2413
+ function Spinner2({ size: s = "md", label = "Loading", className }) {
2414
+ return /* @__PURE__ */ jsxs33("span", { role: "status", className: cx("trc-spinner", `trc-spinner--${s}`, className), children: [
2415
+ /* @__PURE__ */ jsxs33("svg", { viewBox: "0 0 24 24", "aria-hidden": true, className: "trc-spinner__svg", children: [
2416
+ /* @__PURE__ */ jsx37("circle", { cx: "12", cy: "12", r: "10", className: "trc-spinner__track" }),
2417
+ /* @__PURE__ */ jsx37("path", { d: "M12 2a10 10 0 0 1 10 10", className: "trc-spinner__arc" })
2418
+ ] }),
2419
+ /* @__PURE__ */ jsx37("span", { className: "trc-visually-hidden", children: label })
2420
+ ] });
2421
+ }
2422
+ function Progress({
2423
+ value,
2424
+ max = 100,
2425
+ label,
2426
+ "aria-label": ariaLabel,
2427
+ showValue = false,
2428
+ formatValue = (v, m) => `${Math.round(v / m * 100)} %`,
2429
+ className
2430
+ }) {
2431
+ const id = useId14();
2432
+ const determinate = value !== void 0 && value !== null;
2433
+ const clamped = determinate ? Math.min(Math.max(value, 0), max) : 0;
2434
+ const text = determinate ? formatValue(clamped, max) : void 0;
2435
+ return /* @__PURE__ */ jsxs33("div", { className: cx("trc-progress", !determinate && "trc-progress--indeterminate", className), children: [
2436
+ (label != null || showValue && text) && /* @__PURE__ */ jsxs33("div", { className: "trc-progress__head", children: [
2437
+ label != null && /* @__PURE__ */ jsx37("span", { id: `${id}-label`, className: "trc-progress__label", children: label }),
2438
+ showValue && text && /* @__PURE__ */ jsx37("span", { "aria-hidden": true, className: "trc-progress__value", children: text })
2439
+ ] }),
2440
+ /* @__PURE__ */ jsx37(
2441
+ "div",
2442
+ {
2443
+ role: "progressbar",
2444
+ "aria-labelledby": label != null ? `${id}-label` : void 0,
2445
+ "aria-label": label == null ? ariaLabel : void 0,
2446
+ "aria-valuemin": 0,
2447
+ "aria-valuemax": max,
2448
+ "aria-valuenow": determinate ? clamped : void 0,
2449
+ "aria-valuetext": text,
2450
+ className: "trc-progress__track",
2451
+ children: /* @__PURE__ */ jsx37(
2452
+ "div",
2453
+ {
2454
+ className: "trc-progress__fill",
2455
+ style: { "--trc-progress": `${clamped / max * 100}%` }
2456
+ }
2457
+ )
2458
+ }
2459
+ )
2460
+ ] });
2461
+ }
2462
+
2463
+ // src/components/kbd.tsx
2464
+ import { jsx as jsx38 } from "react/jsx-runtime";
2465
+ function Kbd({ className, ...props }) {
2466
+ return /* @__PURE__ */ jsx38("kbd", { className: cx("trc-kbd", className), ...props });
2467
+ }
2468
+ function Divider({ label, orientation = "horizontal", className }) {
2469
+ if (orientation === "vertical") {
2470
+ return /* @__PURE__ */ jsx38("span", { role: "separator", "aria-orientation": "vertical", className: cx("trc-divider--vertical", className) });
2471
+ }
2472
+ if (label == null) return /* @__PURE__ */ jsx38("hr", { className: cx("trc-divider", className) });
2473
+ return /* @__PURE__ */ jsx38("div", { role: "separator", className: cx("trc-divider-label", className), children: /* @__PURE__ */ jsx38("span", { className: "trc-divider-label__text", children: label }) });
2474
+ }
2475
+
2476
+ // src/components/code-block.tsx
2477
+ import { useEffect as useEffect11, useRef as useRef16, useState as useState15 } from "react";
2478
+ import { jsx as jsx39, jsxs as jsxs34 } from "react/jsx-runtime";
2479
+ var BADGES = {
2480
+ tsx: "TS",
2481
+ ts: "TS",
2482
+ jsx: "JS",
2483
+ js: "JS",
2484
+ css: "CSS",
2485
+ bash: "SH",
2486
+ sh: "SH",
2487
+ html: "HTML",
2488
+ json: "JSON"
2489
+ };
2490
+ var CopyIcon = () => /* @__PURE__ */ jsxs34(
2491
+ "svg",
2492
+ {
2493
+ viewBox: "0 0 24 24",
2494
+ fill: "none",
2495
+ stroke: "currentColor",
2496
+ strokeWidth: 1.75,
2497
+ strokeLinecap: "round",
2498
+ strokeLinejoin: "round",
2499
+ "aria-hidden": true,
2500
+ children: [
2501
+ /* @__PURE__ */ jsx39("rect", { x: "9", y: "9", width: "12", height: "12", rx: "2.5" }),
2502
+ /* @__PURE__ */ jsx39("path", { d: "M5 15H4.5A1.5 1.5 0 0 1 3 13.5v-9A1.5 1.5 0 0 1 4.5 3h9A1.5 1.5 0 0 1 15 4.5V5" })
2503
+ ]
2504
+ }
2505
+ );
2506
+ function CodeBlock({
2507
+ code,
2508
+ title,
2509
+ language,
2510
+ children,
2511
+ copyLabel = "Copy code",
2512
+ copiedLabel = "Copied",
2513
+ className
2514
+ }) {
2515
+ const [copied, setCopied] = useState15(false);
2516
+ const timer = useRef16(void 0);
2517
+ useEffect11(() => () => window.clearTimeout(timer.current), []);
2518
+ const copy = async () => {
2519
+ await navigator.clipboard.writeText(code);
2520
+ setCopied(true);
2521
+ window.clearTimeout(timer.current);
2522
+ timer.current = window.setTimeout(() => setCopied(false), 1600);
2523
+ };
2524
+ const button = /* @__PURE__ */ jsxs34(
2525
+ "button",
2526
+ {
2527
+ type: "button",
2528
+ onClick: copy,
2529
+ className: "trc-code__copy",
2530
+ "aria-label": copyLabel,
2531
+ title: copyLabel,
2532
+ "data-copied": copied,
2533
+ children: [
2534
+ copied ? /* @__PURE__ */ jsx39(Check, {}) : /* @__PURE__ */ jsx39(CopyIcon, {}),
2535
+ /* @__PURE__ */ jsx39("span", { className: "trc-visually-hidden", "aria-live": "polite", children: copied ? copiedLabel : "" })
2536
+ ]
2537
+ }
2538
+ );
2539
+ return /* @__PURE__ */ jsxs34("div", { className: cx("trc-code", title == null && "trc-code--bare", className), children: [
2540
+ title != null ? /* @__PURE__ */ jsxs34("div", { className: "trc-code__bar", children: [
2541
+ /* @__PURE__ */ jsxs34("span", { className: "trc-code__file", children: [
2542
+ language && /* @__PURE__ */ jsx39("span", { className: "trc-code__lang", children: BADGES[language] ?? language.toUpperCase() }),
2543
+ title
2544
+ ] }),
2545
+ button
2546
+ ] }) : button,
2547
+ /* @__PURE__ */ jsx39("pre", { className: "trc-code__pre", children: /* @__PURE__ */ jsx39("code", { children: children ?? code }) })
2548
+ ] });
2549
+ }
2550
+ export {
2551
+ AccentText,
2552
+ Accordion,
2553
+ Alert,
2554
+ ArrowLeft,
2555
+ ArrowRight,
2556
+ ArrowUp,
2557
+ Avatar,
2558
+ AvatarGroup,
2559
+ BackToTop,
2560
+ Breadcrumbs,
2561
+ Calendar,
2562
+ Card,
2563
+ Check,
2564
+ Checkbox,
2565
+ ChevronDown,
2566
+ ChevronLeft,
2567
+ ChevronRight,
2568
+ Close,
2569
+ CodeBlock,
2570
+ Combobox,
2571
+ Container,
2572
+ DatePicker,
2573
+ Dialog,
2574
+ Divider,
2575
+ Drawer,
2576
+ DropdownMenu,
2577
+ Dropzone,
2578
+ Field,
2579
+ FileInput,
2580
+ Heading,
2581
+ HeroVideo,
2582
+ Input,
2583
+ Kbd,
2584
+ LanguageSwitcher,
2585
+ Menu,
2586
+ PageHero,
2587
+ Pagination,
2588
+ PasswordInput,
2589
+ PillButton,
2590
+ PillLink,
2591
+ PinInput,
2592
+ Popover,
2593
+ Progress,
2594
+ RadioGroup,
2595
+ Section,
2596
+ SegmentedControl,
2597
+ Select,
2598
+ SiteFooter,
2599
+ SiteHeader,
2600
+ Skeleton,
2601
+ Slider,
2602
+ Spinner2 as Spinner,
2603
+ Stat,
2604
+ StepNumber,
2605
+ Steps,
2606
+ Switch,
2607
+ Table,
2608
+ Tabs,
2609
+ Tag,
2610
+ Text,
2611
+ Textarea,
2612
+ Toaster,
2613
+ Tooltip,
2614
+ acceptsFile,
2615
+ cx,
2616
+ formatBytes,
2617
+ initials,
2618
+ paginationRange,
2619
+ plainText,
2620
+ toast,
2621
+ validateFiles
2622
+ };
2623
+ //# sourceMappingURL=index.js.map