trc-ui 0.1.0 → 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 CHANGED
@@ -23,24 +23,28 @@ function AccentText({ text, accentLast = 0 }) {
23
23
  ] });
24
24
  }
25
25
  var plainText = (text) => text.replaceAll("*", "");
26
- function Heading({ as: Tag = "h2", size = "xl", accentLast, className, children, ...props }) {
27
- return /* @__PURE__ */ jsx(Tag, { className: cx("trc-display", `trc-heading--${size}`, className), ...props, children: typeof children === "string" ? /* @__PURE__ */ jsx(AccentText, { text: children, accentLast }) : children });
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
28
  }
29
- function Text({ as, size = "md", tone = "muted", className, ...props }) {
30
- const Tag = as ?? "p";
31
- return /* @__PURE__ */ jsx(Tag, { className: cx("trc-text", `trc-text--${size}`, `trc-text--${tone}`, className), ...props });
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 });
32
38
  }
33
- function StepNumber({ n, size = "md", className }) {
34
- return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cx("trc-step-number", size === "sm" && "trc-step-number--sm", className), children: String(n).padStart(2, "0") });
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") });
35
41
  }
36
42
 
37
43
  // src/components/pill.tsx
38
- import {
39
- forwardRef
40
- } from "react";
44
+ import { forwardRef } from "react";
41
45
 
42
46
  // src/utils/icons.tsx
43
- import { jsx as jsx2 } from "react/jsx-runtime";
47
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
44
48
  var base = {
45
49
  xmlns: "http://www.w3.org/2000/svg",
46
50
  viewBox: "0 0 24 24",
@@ -59,18 +63,24 @@ var Check = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children
59
63
  var Menu = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "M4 7h16M4 12h16M4 17h16" }) });
60
64
  var Close = (props) => /* @__PURE__ */ jsx2("svg", { ...base, ...props, children: /* @__PURE__ */ jsx2("path", { d: "M18 6 6 18M6 6l12 12" }) });
61
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" }) });
62
72
 
63
73
  // src/components/pill.tsx
64
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
65
- var pillClass = ({ solid, size }, className) => cx("trc-pill", size === "sm" && "trc-pill--sm", solid && "trc-pill--solid", className);
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);
66
76
  function PillInner({ icon, children }) {
67
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
77
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
68
78
  /* @__PURE__ */ jsx3("span", { "aria-hidden": true, className: "trc-pill__fill" }),
69
79
  /* @__PURE__ */ jsx3("span", { "aria-hidden": true, className: "trc-pill__icon", children: icon ?? /* @__PURE__ */ jsx3(ArrowRight, {}) }),
70
80
  /* @__PURE__ */ jsx3("span", { className: "trc-pill__label", children })
71
81
  ] });
72
82
  }
73
- var PillButton = forwardRef(function PillButton2({ solid, size = "md", icon, loading = false, disabled, type = "button", className, children, ...props }, ref) {
83
+ var PillButton = forwardRef(function PillButton2({ solid, size: size2 = "md", icon, loading = false, disabled, type = "button", className, children, ...props }, ref) {
74
84
  return /* @__PURE__ */ jsx3(
75
85
  "button",
76
86
  {
@@ -78,22 +88,27 @@ var PillButton = forwardRef(function PillButton2({ solid, size = "md", icon, loa
78
88
  type,
79
89
  disabled: disabled || loading,
80
90
  "aria-busy": loading || void 0,
81
- className: pillClass({ solid, size }, className),
91
+ className: pillClass({ solid, size: size2 }, className),
82
92
  ...props,
83
93
  children: /* @__PURE__ */ jsx3(PillInner, { icon: loading ? /* @__PURE__ */ jsx3(Spinner, { className: "trc-spin" }) : icon, children })
84
94
  }
85
95
  );
86
96
  });
87
- var PillLink = forwardRef(function PillLink2({ as, solid, size = "md", icon, className, children, ...props }, ref) {
88
- const Tag = as ?? "a";
89
- return /* @__PURE__ */ jsx3(Tag, { ref, className: pillClass({ solid, size }, className), ...props, children: /* @__PURE__ */ jsx3(PillInner, { icon, children }) });
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 }) });
90
100
  });
91
101
 
92
102
  // src/components/surfaces.tsx
93
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
94
- function Container({ as, size = "default", className, ...props }) {
95
- const Tag = as ?? "div";
96
- return /* @__PURE__ */ jsx4(Tag, { className: cx("trc-container", size !== "default" && `trc-container--${size}`, className), ...props });
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 });
97
112
  }
98
113
  function Section({
99
114
  as,
@@ -103,11 +118,11 @@ function Section({
103
118
  children,
104
119
  ...props
105
120
  }) {
106
- const Tag = as ?? "section";
121
+ const Tag2 = as ?? "section";
107
122
  if (inset) {
108
- return /* @__PURE__ */ jsx4(Tag, { className: cx("trc-section-inset", className), ...props, children: /* @__PURE__ */ jsx4("div", { className: cx("trc-section-inset__card", spacing !== "none" && `trc-section--${spacing}`), children }) });
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 }) });
109
124
  }
110
- return /* @__PURE__ */ jsx4(Tag, { className: cx(spacing !== "none" && `trc-section--${spacing}`, className), ...props, children });
125
+ return /* @__PURE__ */ jsx4(Tag2, { className: cx(spacing !== "none" && `trc-section--${spacing}`, className), ...props, children });
111
126
  }
112
127
  function Card({
113
128
  as,
@@ -119,9 +134,9 @@ function Card({
119
134
  children,
120
135
  ...props
121
136
  }) {
122
- const Tag = as ?? "div";
123
- return /* @__PURE__ */ jsxs3(
124
- Tag,
137
+ const Tag2 = as ?? "div";
138
+ return /* @__PURE__ */ jsxs4(
139
+ Tag2,
125
140
  {
126
141
  className: cx(
127
142
  "trc-card",
@@ -142,7 +157,7 @@ function Card({
142
157
 
143
158
  // src/components/field.tsx
144
159
  import { createContext, forwardRef as forwardRef2, useContext, useId } from "react";
145
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
160
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
146
161
  var FieldContext = createContext(null);
147
162
  function Field({ label, hint, error, required = false, id, className, children }) {
148
163
  const generated = useId();
@@ -153,10 +168,10 @@ function Field({ label, hint, error, required = false, id, className, children }
153
168
  FieldContext.Provider,
154
169
  {
155
170
  value: { id: fieldId, describedBy: message ? messageId : void 0, invalid: error != null, required },
156
- children: /* @__PURE__ */ jsxs4("div", { className: cx("trc-field", className), children: [
157
- /* @__PURE__ */ jsxs4("label", { htmlFor: fieldId, className: "trc-field__label", children: [
171
+ children: /* @__PURE__ */ jsxs5("div", { className: cx("trc-field", className), children: [
172
+ /* @__PURE__ */ jsxs5("label", { htmlFor: fieldId, className: "trc-field__label", children: [
158
173
  label,
159
- required && /* @__PURE__ */ jsxs4("span", { "aria-hidden": true, className: "trc-field__required", children: [
174
+ required && /* @__PURE__ */ jsxs5("span", { "aria-hidden": true, className: "trc-field__required", children: [
160
175
  " ",
161
176
  "*"
162
177
  ] })
@@ -197,8 +212,8 @@ var Textarea = forwardRef2(function Textarea2({ invalid, rows = 4, className, ..
197
212
  var Select = forwardRef2(function Select2({ invalid, placeholder, defaultValue, className, children, ...props }, ref) {
198
213
  const a11y = useFieldProps({ ...props, invalid });
199
214
  const initial = defaultValue ?? (props.value === void 0 && placeholder != null ? "" : void 0);
200
- return /* @__PURE__ */ jsxs4("span", { className: cx("trc-select", className), children: [
201
- /* @__PURE__ */ jsxs4("select", { ref, className: "trc-control trc-control--select", defaultValue: initial, ...props, ...a11y, children: [
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: [
202
217
  placeholder != null && /* @__PURE__ */ jsx5("option", { value: "", disabled: true, children: placeholder }),
203
218
  children
204
219
  ] }),
@@ -208,7 +223,7 @@ var Select = forwardRef2(function Select2({ invalid, placeholder, defaultValue,
208
223
 
209
224
  // src/components/toggle.tsx
210
225
  import { forwardRef as forwardRef3 } from "react";
211
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
226
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
212
227
  function ToggleLabel({
213
228
  kind,
214
229
  label,
@@ -216,30 +231,32 @@ function ToggleLabel({
216
231
  className,
217
232
  children
218
233
  }) {
219
- return /* @__PURE__ */ jsxs5("label", { className: cx("trc-toggle", `trc-toggle--${kind}`, className), children: [
234
+ return /* @__PURE__ */ jsxs6("label", { className: cx("trc-toggle", `trc-toggle--${kind}`, className), children: [
220
235
  children,
221
- /* @__PURE__ */ jsxs5("span", { className: "trc-toggle__text", children: [
236
+ /* @__PURE__ */ jsxs6("span", { className: "trc-toggle__text", children: [
222
237
  /* @__PURE__ */ jsx6("span", { className: "trc-toggle__label", children: label }),
223
238
  description != null && /* @__PURE__ */ jsx6("span", { className: "trc-toggle__description", children: description })
224
239
  ] })
225
240
  ] });
226
241
  }
227
242
  var Checkbox = forwardRef3(function Checkbox2({ label, description, className, ...props }, ref) {
228
- return /* @__PURE__ */ jsx6(ToggleLabel, { kind: "checkbox", label, description, className, children: /* @__PURE__ */ jsxs5("span", { className: "trc-checkbox", children: [
243
+ return /* @__PURE__ */ jsx6(ToggleLabel, { kind: "checkbox", label, description, className, children: /* @__PURE__ */ jsxs6("span", { className: "trc-checkbox", children: [
229
244
  /* @__PURE__ */ jsx6("input", { ref, type: "checkbox", className: "trc-checkbox__input", ...props }),
230
245
  /* @__PURE__ */ jsx6(Check, { className: "trc-checkbox__check" })
231
246
  ] }) });
232
247
  });
233
248
  var Switch = forwardRef3(function Switch2({ label, description, className, ...props }, ref) {
234
- return /* @__PURE__ */ jsx6(ToggleLabel, { kind: "switch", label, description, className, children: /* @__PURE__ */ jsxs5("span", { className: "trc-switch", children: [
249
+ return /* @__PURE__ */ jsx6(ToggleLabel, { kind: "switch", label, description, className, children: /* @__PURE__ */ jsxs6("span", { className: "trc-switch", children: [
235
250
  /* @__PURE__ */ jsx6("input", { ref, type: "checkbox", role: "switch", className: "trc-switch__input", ...props }),
236
251
  /* @__PURE__ */ jsx6("span", { "aria-hidden": true, className: "trc-switch__thumb" })
237
252
  ] }) });
238
253
  });
239
254
 
240
255
  // src/components/site-header.tsx
241
- import { useEffect as useEffect2, useId as useId2, useRef as useRef2, useState } from "react";
242
- import { createPortal } from "react-dom";
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";
243
260
 
244
261
  // src/hooks/use-modal.ts
245
262
  import { useEffect, useRef } from "react";
@@ -261,9 +278,9 @@ function useModal(open, panelRef, onClose) {
261
278
  return;
262
279
  }
263
280
  if (e.key !== "Tab") return;
264
- const items = [...panel.querySelectorAll(FOCUSABLE)].filter((el) => el.offsetParent !== null);
265
- const first = items[0];
266
- const last = items[items.length - 1];
281
+ const items2 = [...panel.querySelectorAll(FOCUSABLE)].filter((el) => el.offsetParent !== null);
282
+ const first = items2[0];
283
+ const last = items2[items2.length - 1];
267
284
  if (!first || !last) {
268
285
  e.preventDefault();
269
286
  return;
@@ -285,8 +302,88 @@ function useModal(open, panelRef, onClose) {
285
302
  }, [open, panelRef]);
286
303
  }
287
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
+
288
385
  // src/components/site-header.tsx
289
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
386
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
290
387
  var isMenu = (item) => "items" in item;
291
388
  function SiteHeader({
292
389
  logo,
@@ -310,11 +407,11 @@ function SiteHeader({
310
407
  window.addEventListener("scroll", update, { passive: true });
311
408
  return () => window.removeEventListener("scroll", update);
312
409
  }, [scrollThreshold]);
313
- return /* @__PURE__ */ jsxs6("header", { className: cx("trc-header", className), children: [
314
- /* @__PURE__ */ jsxs6("div", { className: cx("trc-header__bar", scrolled && "trc-header__bar--scrolled"), children: [
315
- /* @__PURE__ */ jsx7(LinkTag, { href: homeHref, "aria-label": logoLabel, className: "trc-header__logo", children: logo }),
316
- nav.length > 0 && /* @__PURE__ */ jsx7("nav", { className: "trc-header__nav", children: nav.map(
317
- (item, i) => isMenu(item) ? /* @__PURE__ */ jsx7(NavDropdown, { menu: item, linkAs: LinkTag }, i) : /* @__PURE__ */ jsx7(
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(
318
415
  LinkTag,
319
416
  {
320
417
  href: item.href,
@@ -325,10 +422,10 @@ function SiteHeader({
325
422
  i
326
423
  )
327
424
  ) }),
328
- /* @__PURE__ */ jsxs6("div", { className: "trc-header__end", children: [
329
- actions && /* @__PURE__ */ jsx7("div", { className: "trc-header__actions", children: actions }),
330
- cta && /* @__PURE__ */ jsx7(PillLink, { as: LinkTag, href: cta.href, size: "sm", className: "trc-header__cta", children: cta.label }),
331
- (nav.length > 0 || cta || mobileFooter) && /* @__PURE__ */ jsx7(
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(
332
429
  "button",
333
430
  {
334
431
  type: "button",
@@ -336,12 +433,12 @@ function SiteHeader({
336
433
  "aria-label": menuLabel,
337
434
  "aria-expanded": open,
338
435
  onClick: () => setOpen(true),
339
- children: /* @__PURE__ */ jsx7(Menu, {})
436
+ children: /* @__PURE__ */ jsx9(Menu, {})
340
437
  }
341
438
  )
342
439
  ] })
343
440
  ] }),
344
- /* @__PURE__ */ jsx7(
441
+ /* @__PURE__ */ jsx9(
345
442
  MobileMenu,
346
443
  {
347
444
  open,
@@ -357,19 +454,21 @@ function SiteHeader({
357
454
  ] });
358
455
  }
359
456
  function NavDropdown({ menu, linkAs: LinkTag }) {
360
- const id = useId2();
457
+ const id = useId3();
361
458
  const [open, setOpen] = useState(false);
362
- const closeTimer = useRef2(void 0);
363
- const button = useRef2(null);
459
+ const closeTimer = useRef3(void 0);
460
+ const button = useRef3(null);
461
+ const openedByHover = useRef3(false);
364
462
  const show = () => {
365
463
  window.clearTimeout(closeTimer.current);
464
+ if (!open) openedByHover.current = true;
366
465
  setOpen(true);
367
466
  };
368
467
  const hide = () => {
369
468
  closeTimer.current = window.setTimeout(() => setOpen(false), 120);
370
469
  };
371
470
  useEffect2(() => () => window.clearTimeout(closeTimer.current), []);
372
- return /* @__PURE__ */ jsxs6(
471
+ return /* @__PURE__ */ jsxs8(
373
472
  "div",
374
473
  {
375
474
  className: "trc-dropdown",
@@ -383,22 +482,25 @@ function NavDropdown({ menu, linkAs: LinkTag }) {
383
482
  },
384
483
  onBlur: (e) => !e.currentTarget.contains(e.relatedTarget) && setOpen(false),
385
484
  children: [
386
- /* @__PURE__ */ jsxs6(
485
+ /* @__PURE__ */ jsxs8(
387
486
  "button",
388
487
  {
389
488
  ref: button,
390
489
  type: "button",
391
490
  "aria-expanded": open,
392
491
  "aria-controls": id,
393
- onClick: () => setOpen((o) => !o),
492
+ onClick: () => {
493
+ if (open && openedByHover.current) openedByHover.current = false;
494
+ else setOpen((o) => !o);
495
+ },
394
496
  className: cx("trc-header__link", "trc-dropdown__trigger", open && "trc-dropdown__trigger--open"),
395
497
  children: [
396
498
  menu.label,
397
- /* @__PURE__ */ jsx7(ChevronDown, { className: "trc-dropdown__chevron" })
499
+ /* @__PURE__ */ jsx9(ChevronDown, { className: "trc-dropdown__chevron" })
398
500
  ]
399
501
  }
400
502
  ),
401
- /* @__PURE__ */ jsx7("div", { id, className: cx("trc-dropdown__panel", open && "trc-dropdown__panel--open"), children: /* @__PURE__ */ jsx7("ul", { className: "trc-dropdown__list", children: menu.items.map((item, i) => /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsxs6(
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(
402
504
  LinkTag,
403
505
  {
404
506
  href: item.href,
@@ -406,8 +508,8 @@ function NavDropdown({ menu, linkAs: LinkTag }) {
406
508
  onClick: () => setOpen(false),
407
509
  className: "trc-dropdown__item",
408
510
  children: [
409
- /* @__PURE__ */ jsx7("span", { className: "trc-dropdown__label", children: item.label }),
410
- item.description != null && /* @__PURE__ */ jsx7("span", { className: "trc-dropdown__description", children: item.description })
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 })
411
513
  ]
412
514
  }
413
515
  ) }, i)) }) })
@@ -425,66 +527,39 @@ function MobileMenu({
425
527
  title,
426
528
  closeLabel
427
529
  }) {
428
- const titleId = useId2();
429
- const panel = useRef2(null);
430
- const [mounted, setMounted] = useState(false);
431
- useEffect2(() => setMounted(true), []);
432
- useModal(open, panel, onClose);
433
- if (!mounted) return null;
434
- return createPortal(
435
- /* @__PURE__ */ jsxs6("div", { className: cx("trc-drawer", open && "trc-drawer--open"), "aria-hidden": !open, children: [
436
- /* @__PURE__ */ jsx7("div", { className: "trc-drawer__overlay", onClick: onClose }),
437
- /* @__PURE__ */ jsxs6(
438
- "div",
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,
439
546
  {
440
- ref: panel,
441
- role: "dialog",
442
- "aria-modal": "true",
443
- "aria-labelledby": titleId,
444
- tabIndex: -1,
445
- className: "trc-drawer__panel",
446
- children: [
447
- /* @__PURE__ */ jsxs6("div", { className: "trc-drawer__head", children: [
448
- /* @__PURE__ */ jsx7("p", { id: titleId, className: "trc-drawer__title", children: title }),
449
- /* @__PURE__ */ jsx7("button", { type: "button", className: "trc-drawer__close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ jsx7(Close, {}) })
450
- ] }),
451
- /* @__PURE__ */ jsx7("nav", { className: "trc-mobile-nav", children: nav.map(
452
- (item, i) => isMenu(item) ? /* @__PURE__ */ jsxs6("div", { className: "trc-mobile-nav__group", children: [
453
- /* @__PURE__ */ jsx7("p", { className: "trc-mobile-nav__heading", children: item.label }),
454
- /* @__PURE__ */ jsx7("ul", { className: "trc-mobile-nav__sub", children: item.items.map((sub, j) => /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsx7(
455
- LinkTag,
456
- {
457
- href: sub.href,
458
- "aria-current": sub.active ? "page" : void 0,
459
- onClick: onClose,
460
- className: "trc-mobile-nav__sublink",
461
- children: sub.label
462
- }
463
- ) }, j)) })
464
- ] }, i) : /* @__PURE__ */ jsx7(
465
- LinkTag,
466
- {
467
- href: item.href,
468
- "aria-current": item.active ? "page" : void 0,
469
- onClick: onClose,
470
- className: cx("trc-mobile-nav__link", item.active && "trc-mobile-nav__link--active"),
471
- children: item.label
472
- },
473
- i
474
- )
475
- ) }),
476
- cta && /* @__PURE__ */ jsx7(PillLink, { as: LinkTag, href: cta.href, onClick: onClose, className: "trc-drawer__cta", children: cta.label }),
477
- footer != null && /* @__PURE__ */ jsx7("div", { className: "trc-drawer__footer", children: footer })
478
- ]
479
- }
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
480
554
  )
481
- ] }),
482
- document.body
483
- );
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
+ ] });
484
559
  }
485
560
 
486
561
  // src/components/site-footer.tsx
487
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
562
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
488
563
  function SiteFooter({
489
564
  logo,
490
565
  logoLabel,
@@ -499,28 +574,28 @@ function SiteFooter({
499
574
  linkAs: LinkTag = "a",
500
575
  className
501
576
  }) {
502
- return /* @__PURE__ */ jsx8("footer", { className: cx("trc-footer", className), children: /* @__PURE__ */ jsxs7("div", { className: "trc-footer__card", children: [
503
- /* @__PURE__ */ jsxs7("div", { className: "trc-footer__main", children: [
504
- /* @__PURE__ */ jsxs7("div", { className: "trc-footer__brand", children: [
505
- logo != null && /* @__PURE__ */ jsx8(LinkTag, { href: homeHref, "aria-label": logoLabel, className: "trc-footer__logo", children: logo }),
506
- tagline != null && /* @__PURE__ */ jsx8("p", { className: "trc-footer__tagline", children: tagline }),
507
- children != null && /* @__PURE__ */ jsx8("div", { className: "trc-footer__extra", children })
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 })
508
583
  ] }),
509
- columns.length > 0 && /* @__PURE__ */ jsx8("nav", { className: "trc-footer__columns", "aria-label": columnsLabel, children: columns.map((col, i) => /* @__PURE__ */ jsxs7("div", { children: [
510
- /* @__PURE__ */ jsx8("p", { className: "trc-footer__heading", children: col.title }),
511
- /* @__PURE__ */ jsx8("ul", { className: "trc-footer__list", children: col.links.map((link, j) => /* @__PURE__ */ jsx8("li", { children: /* @__PURE__ */ jsx8(LinkTag, { href: link.href, className: "trc-footer__link", children: link.label }) }, j)) })
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)) })
512
587
  ] }, i)) })
513
588
  ] }),
514
- (copyright != null || legal.length > 0) && /* @__PURE__ */ jsxs7("div", { className: "trc-footer__bottom", children: [
515
- copyright != null && /* @__PURE__ */ jsx8("p", { className: "trc-footer__copyright", children: copyright }),
516
- legal.length > 0 && /* @__PURE__ */ jsx8("nav", { "aria-label": legalLabel, className: "trc-footer__legal", children: legal.map((link, i) => /* @__PURE__ */ jsx8(LinkTag, { href: link.href, className: "trc-footer__legal-link", children: link.label }, i)) })
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)) })
517
592
  ] })
518
593
  ] }) });
519
594
  }
520
595
 
521
596
  // src/components/hero.tsx
522
- import { useEffect as useEffect3, useRef as useRef3, useState as useState2 } from "react";
523
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
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";
524
599
  var delay = (ms) => ({ "--trc-delay": `${ms}ms` });
525
600
  function PageHero({
526
601
  title,
@@ -533,22 +608,22 @@ function PageHero({
533
608
  className
534
609
  }) {
535
610
  const words = title.split(" ").length;
536
- return /* @__PURE__ */ jsx9("section", { className: cx("trc-hero-inset", className), children: /* @__PURE__ */ jsxs8("div", { className: "trc-page-hero", children: [
537
- /* @__PURE__ */ jsx9("div", { "aria-hidden": true, className: "trc-hero__media", children: typeof image === "string" ? /* @__PURE__ */ jsx9("img", { src: image, alt: "", className: "trc-hero__image", style: { objectPosition: imagePosition } }) : image != null ? image : /* @__PURE__ */ jsx9("div", { className: "trc-page-hero__glow" }) }),
538
- /* @__PURE__ */ jsx9("div", { "aria-hidden": true, className: "trc-page-hero__veil" }),
539
- /* @__PURE__ */ jsxs8("div", { className: "trc-page-hero__content", children: [
540
- /* @__PURE__ */ jsx9("h1", { className: "trc-display trc-page-hero__title trc-enter", style: delay(150), children: /* @__PURE__ */ jsx9(AccentText, { text: title, accentLast: accentLast ?? (words > 3 ? 2 : 1) }) }),
541
- description != null && /* @__PURE__ */ jsx9("p", { className: "trc-page-hero__description trc-enter", style: delay(250), children: description }),
542
- meta != null && /* @__PURE__ */ jsx9("div", { className: "trc-page-hero__meta trc-enter", style: delay(350), children: meta }),
543
- children != null && /* @__PURE__ */ jsx9("div", { className: "trc-page-hero__actions trc-enter", style: delay(450), children })
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 })
544
619
  ] })
545
620
  ] }) });
546
621
  }
547
622
  var prefersReducedMotion = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
548
623
  function HeroVideo({ clips, children, aside, fade = 1.2, parallax = true, className }) {
549
- const videos = useRef3([]);
550
- const section = useRef3(null);
551
- const media = useRef3(null);
624
+ const videos = useRef4([]);
625
+ const section = useRef4(null);
626
+ const media = useRef4(null);
552
627
  const [active, setActive] = useState2(-1);
553
628
  useEffect3(() => {
554
629
  if (clips.length === 0) return;
@@ -591,8 +666,8 @@ function HeroVideo({ clips, children, aside, fade = 1.2, parallax = true, classN
591
666
  });
592
667
  setActive((i + 1) % clips.length);
593
668
  };
594
- return /* @__PURE__ */ jsx9("section", { ref: section, className: cx("trc-hero-inset", className), children: /* @__PURE__ */ jsxs8("div", { className: "trc-hero-video", children: [
595
- /* @__PURE__ */ jsx9("div", { ref: media, "aria-hidden": true, className: "trc-hero__media trc-hero-video__media", children: clips.map((clip, i) => /* @__PURE__ */ jsxs8(
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(
596
671
  "video",
597
672
  {
598
673
  ref: (el) => {
@@ -610,23 +685,23 @@ function HeroVideo({ clips, children, aside, fade = 1.2, parallax = true, classN
610
685
  },
611
686
  onEnded: () => i === active && playNext(i),
612
687
  children: [
613
- clip.webm && /* @__PURE__ */ jsx9("source", { src: clip.webm, type: "video/webm" }),
614
- clip.mp4 && /* @__PURE__ */ jsx9("source", { src: clip.mp4, type: "video/mp4" })
688
+ clip.webm && /* @__PURE__ */ jsx11("source", { src: clip.webm, type: "video/webm" }),
689
+ clip.mp4 && /* @__PURE__ */ jsx11("source", { src: clip.mp4, type: "video/mp4" })
615
690
  ]
616
691
  },
617
692
  i
618
693
  )) }),
619
- /* @__PURE__ */ jsx9("div", { "aria-hidden": true, className: "trc-hero-video__veil" }),
620
- /* @__PURE__ */ jsxs8("div", { className: "trc-hero-video__content", children: [
621
- /* @__PURE__ */ jsx9("div", { className: "trc-hero-video__main", children }),
622
- aside != null && /* @__PURE__ */ jsx9("div", { className: "trc-hero-video__aside", children: aside })
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 })
623
698
  ] })
624
699
  ] }) });
625
700
  }
626
701
 
627
702
  // src/components/back-to-top.tsx
628
703
  import { useEffect as useEffect4, useState as useState3 } from "react";
629
- import { jsx as jsx10 } from "react/jsx-runtime";
704
+ import { jsx as jsx12 } from "react/jsx-runtime";
630
705
  function BackToTop({ label = "Back to top", threshold = 0.8, className }) {
631
706
  const [visible, setVisible] = useState3(false);
632
707
  useEffect4(() => {
@@ -635,7 +710,7 @@ function BackToTop({ label = "Back to top", threshold = 0.8, className }) {
635
710
  window.addEventListener("scroll", update, { passive: true });
636
711
  return () => window.removeEventListener("scroll", update);
637
712
  }, [threshold]);
638
- return /* @__PURE__ */ jsx10(
713
+ return /* @__PURE__ */ jsx12(
639
714
  "button",
640
715
  {
641
716
  type: "button",
@@ -647,39 +722,1902 @@ function BackToTop({ label = "Back to top", threshold = 0.8, className }) {
647
722
  "aria-hidden": !visible,
648
723
  tabIndex: visible ? 0 : -1,
649
724
  className: cx("trc-back-to-top", visible && "trc-back-to-top--visible", className),
650
- children: /* @__PURE__ */ jsx10(ArrowUp, {})
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))
651
1277
  }
652
1278
  );
653
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
+ }
654
2550
  export {
655
2551
  AccentText,
2552
+ Accordion,
2553
+ Alert,
656
2554
  ArrowLeft,
657
2555
  ArrowRight,
658
2556
  ArrowUp,
2557
+ Avatar,
2558
+ AvatarGroup,
659
2559
  BackToTop,
2560
+ Breadcrumbs,
2561
+ Calendar,
660
2562
  Card,
661
2563
  Check,
662
2564
  Checkbox,
663
2565
  ChevronDown,
2566
+ ChevronLeft,
2567
+ ChevronRight,
664
2568
  Close,
2569
+ CodeBlock,
2570
+ Combobox,
665
2571
  Container,
2572
+ DatePicker,
2573
+ Dialog,
2574
+ Divider,
2575
+ Drawer,
2576
+ DropdownMenu,
2577
+ Dropzone,
666
2578
  Field,
2579
+ FileInput,
667
2580
  Heading,
668
2581
  HeroVideo,
669
2582
  Input,
2583
+ Kbd,
2584
+ LanguageSwitcher,
670
2585
  Menu,
671
2586
  PageHero,
2587
+ Pagination,
2588
+ PasswordInput,
672
2589
  PillButton,
673
2590
  PillLink,
2591
+ PinInput,
2592
+ Popover,
2593
+ Progress,
2594
+ RadioGroup,
674
2595
  Section,
2596
+ SegmentedControl,
675
2597
  Select,
676
2598
  SiteFooter,
677
2599
  SiteHeader,
2600
+ Skeleton,
2601
+ Slider,
2602
+ Spinner2 as Spinner,
2603
+ Stat,
678
2604
  StepNumber,
2605
+ Steps,
679
2606
  Switch,
2607
+ Table,
2608
+ Tabs,
2609
+ Tag,
680
2610
  Text,
681
2611
  Textarea,
2612
+ Toaster,
2613
+ Tooltip,
2614
+ acceptsFile,
682
2615
  cx,
683
- plainText
2616
+ formatBytes,
2617
+ initials,
2618
+ paginationRange,
2619
+ plainText,
2620
+ toast,
2621
+ validateFiles
684
2622
  };
685
2623
  //# sourceMappingURL=index.js.map