reusify-ui 2.0.1 → 2.2.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.
@@ -1,40 +1,112 @@
1
1
  "use client";
2
- import { jsxs, jsx, Fragment as Fragment$1 } from "react/jsx-runtime";
3
- import { forwardRef, useState, useEffect, Children, isValidElement, cloneElement, useRef, useCallback, useImperativeHandle, useId, useMemo, Fragment, useContext, createContext } from "react";
2
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
+ import { forwardRef, useRef, useEffect, useState, useCallback, useImperativeHandle, useId, useMemo, Children, isValidElement, Fragment as Fragment$1, cloneElement, useContext, createContext } from "react";
4
4
  import { createPortal } from "react-dom";
5
5
  function cn(...classes) {
6
6
  return classes.filter(Boolean).join(" ");
7
7
  }
8
+ const legacyVariants = {
9
+ primary: ["solid", void 0],
10
+ danger: ["solid", "danger"],
11
+ success: ["solid", "success"]
12
+ };
13
+ const semanticColors$5 = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
14
+ const radii = { none: "0", sm: "6px", md: "10px", lg: "14px", xl: "18px", "2xl": "24px", full: "9999px" };
15
+ function resolveRadius(radius) {
16
+ if (radius === void 0) return void 0;
17
+ if (typeof radius === "number") return `${radius}px`;
18
+ return radii[radius] ?? radius;
19
+ }
8
20
  const Button = forwardRef(function Button2({
9
- variant = "primary",
21
+ variant = "solid",
22
+ color,
23
+ textColor,
24
+ radius,
25
+ justify = "center",
10
26
  size = "md",
11
27
  loading = false,
12
28
  fullWidth = false,
29
+ pill = false,
30
+ iconOnly = false,
13
31
  leftIcon,
14
32
  rightIcon,
15
33
  disabled,
34
+ href,
16
35
  type = "button",
17
36
  className,
37
+ style,
38
+ onPointerMove,
18
39
  children,
19
40
  ...rest
20
41
  }, ref) {
21
- return /* @__PURE__ */ jsxs(
42
+ const [kind, legacyColor] = legacyVariants[variant] ?? [variant, void 0];
43
+ const handlePointerMove = kind === "pixel" ? (e) => {
44
+ const el = e.currentTarget;
45
+ const rect = el.getBoundingClientRect();
46
+ el.style.setProperty("--ru-px", `${e.clientX - rect.left}px`);
47
+ el.style.setProperty("--ru-py", `${e.clientY - rect.top}px`);
48
+ onPointerMove == null ? void 0 : onPointerMove(e);
49
+ } : onPointerMove;
50
+ const resolvedColor = color ?? legacyColor ?? "primary";
51
+ const customColor = !semanticColors$5.has(resolvedColor);
52
+ const classes = cn(
53
+ "ru-btn",
54
+ `ru-btn--${kind}`,
55
+ `ru-btn--${size}`,
56
+ !customColor && `ru-c-${resolvedColor}`,
57
+ fullWidth && "ru-btn--full",
58
+ pill && "ru-btn--pill",
59
+ iconOnly && "ru-btn--icon",
60
+ justify !== "center" && `ru-btn--justify-${justify}`,
61
+ className
62
+ );
63
+ const inlineStyle = {
64
+ ...customColor && { "--_c": resolvedColor },
65
+ ...textColor && { "--_fg": textColor },
66
+ ...radius !== void 0 && { borderRadius: resolveRadius(radius) },
67
+ ...style
68
+ };
69
+ const styleProp = Object.keys(inlineStyle).length ? inlineStyle : void 0;
70
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
71
+ loading ? /* @__PURE__ */ jsx("span", { className: "ru-spinner", "aria-hidden": "true" }) : leftIcon,
72
+ children,
73
+ !loading && rightIcon && /* @__PURE__ */ jsx("span", { className: "ru-btn__end", children: rightIcon })
74
+ ] });
75
+ if (href !== void 0) {
76
+ const inactive = disabled || loading;
77
+ return /* @__PURE__ */ jsx(
78
+ "a",
79
+ {
80
+ ref,
81
+ href: inactive ? void 0 : href,
82
+ "aria-disabled": inactive || void 0,
83
+ "aria-busy": loading || void 0,
84
+ className: classes,
85
+ style: styleProp,
86
+ onPointerMove: handlePointerMove,
87
+ ...rest,
88
+ children: content
89
+ }
90
+ );
91
+ }
92
+ return /* @__PURE__ */ jsx(
22
93
  "button",
23
94
  {
24
95
  ref,
25
96
  type,
26
97
  disabled: disabled || loading,
27
98
  "aria-busy": loading || void 0,
28
- className: cn("ru-btn", `ru-btn--${variant}`, `ru-btn--${size}`, fullWidth && "ru-btn--full", className),
99
+ className: classes,
100
+ style: styleProp,
101
+ onPointerMove: handlePointerMove,
29
102
  ...rest,
30
- children: [
31
- loading ? /* @__PURE__ */ jsx("span", { className: "ru-spinner", "aria-hidden": "true" }) : leftIcon,
32
- children,
33
- !loading && rightIcon
34
- ]
103
+ children: content
35
104
  }
36
105
  );
37
106
  });
107
+ function ButtonGroup({ attached = false, className, ...rest }) {
108
+ return /* @__PURE__ */ jsx("div", { role: "group", className: cn("ru-btn-group", attached && "ru-btn-group--attached", className), ...rest });
109
+ }
38
110
  const base = {
39
111
  xmlns: "http://www.w3.org/2000/svg",
40
112
  viewBox: "0 0 24 24",
@@ -71,6 +143,16 @@ const UserIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
71
143
  /* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "4" }),
72
144
  /* @__PURE__ */ jsx("path", { d: "M4 21a8 8 0 0 1 16 0" })
73
145
  ] });
146
+ const UploadCloudIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
147
+ /* @__PURE__ */ jsx("path", { d: "M4 14.9A7 7 0 1 1 15.7 8h1.8a4.5 4.5 0 0 1 2.5 8.2" }),
148
+ /* @__PURE__ */ jsx("path", { d: "M12 12v9M16 16l-4-4-4 4" })
149
+ ] });
150
+ const FileIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
151
+ /* @__PURE__ */ jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
152
+ /* @__PURE__ */ jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
153
+ ] });
154
+ const TrendUpIcon = (p) => /* @__PURE__ */ jsx("svg", { ...base, ...p, children: /* @__PURE__ */ jsx("path", { d: "M12 19V5M5 12l7-7 7 7" }) });
155
+ const TrendDownIcon = (p) => /* @__PURE__ */ jsx("svg", { ...base, ...p, children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M19 12l-7 7-7-7" }) });
74
156
  const statusIcons = {
75
157
  info: InfoIcon,
76
158
  success: SuccessIcon,
@@ -86,11 +168,17 @@ function Spinner({ size = "md", color = "primary", label = "Loading", className,
86
168
  label && /* @__PURE__ */ jsx("span", { className: "ru-sr-only", children: label })
87
169
  ] });
88
170
  }
171
+ const progressColors = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
89
172
  function Progress({
90
173
  value,
91
174
  max = 100,
92
175
  size = "md",
93
176
  color = "primary",
177
+ variant = "bar",
178
+ ticks = 24,
179
+ glow = false,
180
+ icon,
181
+ endLabel,
94
182
  label,
95
183
  showValue = false,
96
184
  className,
@@ -98,7 +186,86 @@ function Progress({
98
186
  }) {
99
187
  const indeterminate = value === void 0;
100
188
  const percent = indeterminate ? 0 : Math.min(100, Math.max(0, value / max * 100));
101
- return /* @__PURE__ */ jsxs("div", { className: cn("ru-progress", indeterminate && "ru-progress--indeterminate", className), ...rest, children: [
189
+ const semantic = progressColors.has(color);
190
+ const glowColor = glow === true ? semantic ? `var(--ru-${color})` : color : glow || void 0;
191
+ const meterStyle = { ...!semantic && { "--_c": color }, ...glowColor && { "--_glow": glowColor } };
192
+ if (variant === "capsule") {
193
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-progress", "ru-progress--capsule", `ru-progress--${size}`, glowColor && "ru-progress--glow", className), ...rest, children: [
194
+ label && /* @__PURE__ */ jsx("div", { className: "ru-progress__label", children: /* @__PURE__ */ jsx("span", { children: label }) }),
195
+ /* @__PURE__ */ jsxs(
196
+ "div",
197
+ {
198
+ role: "progressbar",
199
+ "aria-valuemin": 0,
200
+ "aria-valuemax": max,
201
+ "aria-valuenow": value ?? 0,
202
+ "aria-label": typeof label === "string" ? label : void 0,
203
+ className: cn("ru-progress__capsule", semantic && `ru-c-${color}`),
204
+ style: meterStyle,
205
+ children: [
206
+ /* @__PURE__ */ jsx("div", { className: "ru-progress__capsule-fill", style: { width: `max(var(--_h), ${percent}%)` }, children: icon ?? (showValue && /* @__PURE__ */ jsxs("span", { className: "ru-progress__capsule-value", children: [
207
+ Math.round(percent),
208
+ "%"
209
+ ] })) }),
210
+ endLabel && /* @__PURE__ */ jsx("span", { className: "ru-progress__capsule-end", children: endLabel })
211
+ ]
212
+ }
213
+ )
214
+ ] });
215
+ }
216
+ if (variant === "bars") {
217
+ const total = Math.max(2, ticks);
218
+ const lit = Math.round(percent / 100 * total);
219
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-progress", "ru-progress--bars", `ru-progress--${size}`, glowColor && "ru-progress--glow", className), ...rest, children: [
220
+ (label || showValue) && /* @__PURE__ */ jsxs("div", { className: "ru-progress__label", children: [
221
+ /* @__PURE__ */ jsx("span", { children: label }),
222
+ showValue && /* @__PURE__ */ jsxs("span", { className: "ru-progress__value ru-progress__value--accent", children: [
223
+ Math.round(percent),
224
+ "%"
225
+ ] })
226
+ ] }),
227
+ /* @__PURE__ */ jsx(
228
+ "div",
229
+ {
230
+ role: "progressbar",
231
+ "aria-valuemin": 0,
232
+ "aria-valuemax": max,
233
+ "aria-valuenow": value ?? 0,
234
+ "aria-label": typeof label === "string" ? label : void 0,
235
+ className: cn("ru-progress__bars", semantic && `ru-c-${color}`),
236
+ style: meterStyle,
237
+ children: Array.from({ length: total }, (_, i) => /* @__PURE__ */ jsx("span", { className: cn(i < lit && "is-on", i === lit - 1 && percent > 0 && percent < 100 && "is-marker") }, i))
238
+ }
239
+ )
240
+ ] });
241
+ }
242
+ if (variant === "ticks") {
243
+ const remaining = percent >= 100 ? 0 : Math.max(1, Math.round(ticks * (1 - percent / 100)));
244
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-progress", "ru-progress--ticks", `ru-progress--${size}`, glowColor && "ru-progress--glow", className), ...rest, children: [
245
+ label && /* @__PURE__ */ jsx("div", { className: "ru-progress__label", children: /* @__PURE__ */ jsx("span", { children: label }) }),
246
+ /* @__PURE__ */ jsxs(
247
+ "div",
248
+ {
249
+ role: "progressbar",
250
+ "aria-valuemin": 0,
251
+ "aria-valuemax": max,
252
+ "aria-valuenow": value ?? 0,
253
+ "aria-label": typeof label === "string" ? label : void 0,
254
+ className: cn("ru-progress__meter", semantic && `ru-c-${color}`),
255
+ style: meterStyle,
256
+ children: [
257
+ percent > 0 && /* @__PURE__ */ jsx("div", { className: "ru-progress__fill", style: { width: `${percent}%` }, children: showValue && /* @__PURE__ */ jsxs("span", { className: "ru-progress__fill-value", children: [
258
+ Math.round(percent),
259
+ "%"
260
+ ] }) }),
261
+ percent > 0 && percent < 100 && /* @__PURE__ */ jsx("span", { className: "ru-progress__marker", "aria-hidden": "true" }),
262
+ remaining > 0 && /* @__PURE__ */ jsx("div", { className: "ru-progress__ticks", "aria-hidden": "true", children: Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx("span", {}, i)) })
263
+ ]
264
+ }
265
+ )
266
+ ] });
267
+ }
268
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-progress", indeterminate && "ru-progress--indeterminate", glowColor && "ru-progress--glow", className), ...rest, children: [
102
269
  (label || showValue) && /* @__PURE__ */ jsxs("div", { className: "ru-progress__label", children: [
103
270
  /* @__PURE__ */ jsx("span", { children: label }),
104
271
  showValue && !indeterminate && /* @__PURE__ */ jsxs("span", { className: "ru-progress__value", children: [
@@ -114,7 +281,8 @@ function Progress({
114
281
  "aria-valuemax": max,
115
282
  "aria-valuenow": indeterminate ? void 0 : value,
116
283
  "aria-label": typeof label === "string" ? label : void 0,
117
- className: cn("ru-progress__track", `ru-progress__track--${size}`, `ru-c-${color}`),
284
+ className: cn("ru-progress__track", `ru-progress__track--${size}`, semantic && `ru-c-${color}`),
285
+ style: meterStyle,
118
286
  children: /* @__PURE__ */ jsx("div", { className: "ru-progress__bar", style: indeterminate ? void 0 : { width: `${percent}%` } })
119
287
  }
120
288
  )
@@ -153,132 +321,6 @@ function Alert({ variant = "info", title, icon, onClose, className, children, ..
153
321
  }
154
322
  );
155
323
  }
156
- function Badge({ variant = "soft", color = "primary", size = "md", dot = false, className, children, ...rest }) {
157
- return /* @__PURE__ */ jsxs("span", { className: cn("ru-badge", `ru-badge--${variant}`, `ru-badge--${size}`, `ru-c-${color}`, className), ...rest, children: [
158
- dot && /* @__PURE__ */ jsx("span", { className: "ru-badge__dot", "aria-hidden": "true" }),
159
- children
160
- ] });
161
- }
162
- function getInitials(name) {
163
- const parts = name.trim().split(/\s+/).filter(Boolean);
164
- if (parts.length === 0) return "";
165
- const first = parts[0][0];
166
- const last = parts.length > 1 ? parts[parts.length - 1][0] : "";
167
- return (first + last).toUpperCase();
168
- }
169
- function Avatar({ src, alt, name, size = "md", shape = "circle", className, ...rest }) {
170
- const [failed, setFailed] = useState(false);
171
- useEffect(() => setFailed(false), [src]);
172
- const showImage = src && !failed;
173
- const initials = name ? getInitials(name) : "";
174
- return /* @__PURE__ */ jsx(
175
- "span",
176
- {
177
- className: cn("ru-avatar", `ru-avatar--${size}`, shape === "square" && "ru-avatar--square", className),
178
- role: showImage ? void 0 : "img",
179
- "aria-label": showImage ? void 0 : alt ?? name ?? "Avatar",
180
- ...rest,
181
- children: showImage ? /* @__PURE__ */ jsx("img", { src, alt: alt ?? name ?? "", onError: () => setFailed(true) }) : initials ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: initials }) : /* @__PURE__ */ jsx(UserIcon, { className: "ru-avatar__icon" })
182
- }
183
- );
184
- }
185
- function AvatarGroup({ max, size = "md", className, children, ...rest }) {
186
- const avatars = Children.toArray(children).filter(isValidElement);
187
- const visible = max ? avatars.slice(0, max) : avatars;
188
- const extra = avatars.length - visible.length;
189
- return /* @__PURE__ */ jsxs("div", { className: cn("ru-avatar-group", className), ...rest, children: [
190
- visible.map((child) => cloneElement(child, { size: child.props.size ?? size })),
191
- extra > 0 && /* @__PURE__ */ jsxs("span", { className: cn("ru-avatar", `ru-avatar--${size}`), "aria-label": `${extra} more`, children: [
192
- "+",
193
- extra
194
- ] })
195
- ] });
196
- }
197
- function Card({ variant = "outline", hoverable = false, className, ...rest }) {
198
- return /* @__PURE__ */ jsx(
199
- "div",
200
- {
201
- className: cn(
202
- "ru-card",
203
- variant === "elevated" && "ru-card--elevated",
204
- hoverable && "ru-card--hoverable",
205
- className
206
- ),
207
- ...rest
208
- }
209
- );
210
- }
211
- function CardHeader({ title, subtitle, action, className, children, ...rest }) {
212
- return /* @__PURE__ */ jsxs("div", { className: cn("ru-card__header", className), ...rest, children: [
213
- /* @__PURE__ */ jsxs("div", { children: [
214
- title && /* @__PURE__ */ jsx("h3", { className: "ru-card__title", children: title }),
215
- subtitle && /* @__PURE__ */ jsx("p", { className: "ru-card__subtitle", children: subtitle }),
216
- children
217
- ] }),
218
- action
219
- ] });
220
- }
221
- function CardBody({ className, ...rest }) {
222
- return /* @__PURE__ */ jsx("div", { className: cn("ru-card__body", className), ...rest });
223
- }
224
- function CardFooter({ className, ...rest }) {
225
- return /* @__PURE__ */ jsx("div", { className: cn("ru-card__footer", className), ...rest });
226
- }
227
- function Divider({ orientation = "horizontal", label, className, ...rest }) {
228
- if (label && orientation === "horizontal") {
229
- return /* @__PURE__ */ jsx("div", { role: "separator", className: cn("ru-divider", "ru-divider--label", className), ...rest, children: label });
230
- }
231
- return /* @__PURE__ */ jsx(
232
- "hr",
233
- {
234
- "aria-orientation": orientation,
235
- className: cn("ru-divider", orientation === "vertical" && "ru-divider--vertical", className),
236
- ...rest
237
- }
238
- );
239
- }
240
- function Table({
241
- columns,
242
- data,
243
- rowKey,
244
- striped = false,
245
- hoverable = false,
246
- size = "md",
247
- caption,
248
- emptyText = "No data",
249
- onRowClick,
250
- className
251
- }) {
252
- const getKey = (row, index) => {
253
- if (typeof rowKey === "function") return rowKey(row, index);
254
- if (rowKey !== void 0) return String(row[rowKey]);
255
- return index;
256
- };
257
- return /* @__PURE__ */ jsx("div", { className: cn("ru-table-wrap", className), children: /* @__PURE__ */ jsxs(
258
- "table",
259
- {
260
- className: cn(
261
- "ru-table",
262
- `ru-table--${size}`,
263
- striped && "ru-table--striped",
264
- (hoverable || onRowClick) && "ru-table--hover"
265
- ),
266
- children: [
267
- caption && /* @__PURE__ */ jsx("caption", { children: caption }),
268
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsx("th", { scope: "col", style: { textAlign: col.align, width: col.width }, children: col.header }, col.key)) }) }),
269
- /* @__PURE__ */ jsx("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { className: "ru-table__empty", colSpan: columns.length, children: emptyText }) }) : data.map((row, i) => /* @__PURE__ */ jsx(
270
- "tr",
271
- {
272
- onClick: onRowClick ? () => onRowClick(row, i) : void 0,
273
- style: onRowClick ? { cursor: "pointer" } : void 0,
274
- children: columns.map((col) => /* @__PURE__ */ jsx("td", { style: { textAlign: col.align }, children: col.render ? col.render(row, i) : row[col.key] }, col.key))
275
- },
276
- getKey(row, i)
277
- )) })
278
- ]
279
- }
280
- ) });
281
- }
282
324
  function useControllableState(value, defaultValue, onChange) {
283
325
  const [internal, setInternal] = useState(defaultValue);
284
326
  const isControlled = value !== void 0;
@@ -535,7 +577,7 @@ function childrenToOptions(children) {
535
577
  result.push({ label, value: value ?? String(label), disabled });
536
578
  } else if (child.type === "optgroup") {
537
579
  result.push({ label: child.props.label, options: childrenToOptions(child.props.children) });
538
- } else if (child.type === Fragment) {
580
+ } else if (child.type === Fragment$1) {
539
581
  result.push(...childrenToOptions(child.props.children));
540
582
  }
541
583
  });
@@ -892,7 +934,7 @@ function RadioGroup({
892
934
  )) })
893
935
  ] });
894
936
  }
895
- const Switch = forwardRef(function Switch2({ checked, defaultChecked = false, onChange, label, size = "md", color = "primary", disabled, name, id, className }, ref) {
937
+ const Switch = forwardRef(function Switch2({ checked, defaultChecked = false, onChange, label, size = "md", color = "primary", disabled, name, id, className, ...rest }, ref) {
896
938
  const [on, setOn] = useControllableState(checked, defaultChecked, onChange);
897
939
  return /* @__PURE__ */ jsxs(
898
940
  "label",
@@ -910,6 +952,7 @@ const Switch = forwardRef(function Switch2({ checked, defaultChecked = false, on
910
952
  disabled,
911
953
  className: "ru-switch__track",
912
954
  onClick: () => setOn(!on),
955
+ ...rest,
913
956
  children: /* @__PURE__ */ jsx("span", { className: "ru-switch__thumb" })
914
957
  }
915
958
  ),
@@ -919,330 +962,78 @@ const Switch = forwardRef(function Switch2({ checked, defaultChecked = false, on
919
962
  }
920
963
  );
921
964
  });
922
- function DialogContent({ titleId, descId, title, description, showCloseButton, onClose, children, footer }) {
923
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
924
- (title || description || showCloseButton) && /* @__PURE__ */ jsxs("div", { className: "ru-modal__header", children: [
925
- /* @__PURE__ */ jsxs("div", { children: [
926
- title && /* @__PURE__ */ jsx("h2", { id: titleId, className: "ru-modal__title", children: title }),
927
- description && /* @__PURE__ */ jsx("p", { id: descId, className: "ru-modal__desc", children: description })
928
- ] }),
929
- showCloseButton && /* @__PURE__ */ jsx(CloseButton, { onClick: onClose })
930
- ] }),
931
- /* @__PURE__ */ jsx("div", { className: "ru-modal__body", children }),
932
- footer && /* @__PURE__ */ jsx("div", { className: "ru-modal__footer", children: footer })
933
- ] });
934
- }
935
- function Modal({
936
- open,
937
- onClose,
938
- size = "md",
939
- closeOnOverlayClick = true,
940
- closeOnEsc = true,
941
- showCloseButton = true,
942
- className,
943
- ...content
944
- }) {
945
- const panelRef = useRef(null);
946
- const mounted = useMounted();
947
- const titleId = useId();
948
- const descId = useId();
949
- useOverlay(open && mounted, panelRef, onClose, closeOnEsc);
950
- if (!open || !mounted) return null;
951
- const onOverlayMouseDown = (e) => {
952
- if (closeOnOverlayClick && e.target === e.currentTarget) onClose();
965
+ function Tabs({ items, value, defaultValue, onChange, variant = "line", fullWidth = false, className }) {
966
+ var _a;
967
+ const baseId = useId();
968
+ const firstEnabled = ((_a = items.find((item) => !item.disabled)) == null ? void 0 : _a.value) ?? "";
969
+ const [active, setActive] = useControllableState(value, defaultValue ?? firstEnabled, onChange);
970
+ const tabRefs = useRef([]);
971
+ const onKeyDown = (e, index) => {
972
+ var _a2;
973
+ const enabled = items.flatMap((item, i) => item.disabled ? [] : [i]);
974
+ const pos = enabled.indexOf(index);
975
+ let next;
976
+ switch (e.key) {
977
+ case "ArrowRight":
978
+ next = enabled[(pos + 1) % enabled.length];
979
+ break;
980
+ case "ArrowLeft":
981
+ next = enabled[(pos - 1 + enabled.length) % enabled.length];
982
+ break;
983
+ case "Home":
984
+ next = enabled[0];
985
+ break;
986
+ case "End":
987
+ next = enabled[enabled.length - 1];
988
+ break;
989
+ default:
990
+ return;
991
+ }
992
+ e.preventDefault();
993
+ (_a2 = tabRefs.current[next]) == null ? void 0 : _a2.focus();
994
+ setActive(items[next].value);
953
995
  };
954
- return createPortal(
955
- /* @__PURE__ */ jsx("div", { className: "ru-overlay", onMouseDown: onOverlayMouseDown, children: /* @__PURE__ */ jsx(
996
+ const activeIndex = items.findIndex((item) => item.value === active);
997
+ const activeItem = items[activeIndex];
998
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-tabs", `ru-tabs--${variant}`, fullWidth && "ru-tabs--full", className), children: [
999
+ /* @__PURE__ */ jsx("div", { role: "tablist", className: "ru-tabs__list", children: items.map((item, i) => {
1000
+ const selected = i === activeIndex;
1001
+ return /* @__PURE__ */ jsxs(
1002
+ "button",
1003
+ {
1004
+ ref: (el) => {
1005
+ tabRefs.current[i] = el;
1006
+ },
1007
+ type: "button",
1008
+ role: "tab",
1009
+ id: `${baseId}-tab-${i}`,
1010
+ "aria-selected": selected,
1011
+ "aria-controls": `${baseId}-panel-${i}`,
1012
+ tabIndex: selected ? 0 : -1,
1013
+ disabled: item.disabled,
1014
+ className: "ru-tabs__tab",
1015
+ onClick: () => setActive(item.value),
1016
+ onKeyDown: (e) => onKeyDown(e, i),
1017
+ children: [
1018
+ item.icon,
1019
+ item.label
1020
+ ]
1021
+ },
1022
+ item.value
1023
+ );
1024
+ }) }),
1025
+ activeItem && activeItem.content !== void 0 && /* @__PURE__ */ jsx(
956
1026
  "div",
957
1027
  {
958
- ref: panelRef,
959
- role: "dialog",
960
- "aria-modal": "true",
961
- "aria-labelledby": content.title ? titleId : void 0,
962
- "aria-describedby": content.description ? descId : void 0,
963
- tabIndex: -1,
964
- className: cn("ru-modal", `ru-modal--${size}`, className),
965
- children: /* @__PURE__ */ jsx(
966
- DialogContent,
967
- {
968
- ...content,
969
- titleId,
970
- descId,
971
- showCloseButton,
972
- onClose
973
- }
974
- )
1028
+ role: "tabpanel",
1029
+ id: `${baseId}-panel-${activeIndex}`,
1030
+ "aria-labelledby": `${baseId}-tab-${activeIndex}`,
1031
+ tabIndex: 0,
1032
+ className: "ru-tabs__panel",
1033
+ children: activeItem.content
975
1034
  }
976
- ) }),
977
- document.body
978
- );
979
- }
980
- const drawerSizes = { sm: "20rem", md: "24rem", lg: "32rem" };
981
- function Drawer({
982
- open,
983
- onClose,
984
- placement = "right",
985
- size = "md",
986
- closeOnOverlayClick = true,
987
- closeOnEsc = true,
988
- showCloseButton = true,
989
- className,
990
- ...content
991
- }) {
992
- const panelRef = useRef(null);
993
- const mounted = useMounted();
994
- const titleId = useId();
995
- const descId = useId();
996
- useOverlay(open && mounted, panelRef, onClose, closeOnEsc);
997
- if (!open || !mounted) return null;
998
- const resolvedSize = typeof size === "number" ? `${size}px` : drawerSizes[size] ?? size;
999
- return createPortal(
1000
- /* @__PURE__ */ jsx(
1001
- "div",
1002
- {
1003
- className: "ru-overlay ru-overlay--drawer",
1004
- onMouseDown: (e) => {
1005
- if (closeOnOverlayClick && e.target === e.currentTarget) onClose();
1006
- },
1007
- children: /* @__PURE__ */ jsx(
1008
- "div",
1009
- {
1010
- ref: panelRef,
1011
- role: "dialog",
1012
- "aria-modal": "true",
1013
- "aria-labelledby": content.title ? titleId : void 0,
1014
- "aria-describedby": content.description ? descId : void 0,
1015
- tabIndex: -1,
1016
- className: cn("ru-drawer", `ru-drawer--${placement}`, className),
1017
- style: { "--ru-drawer-size": resolvedSize },
1018
- children: /* @__PURE__ */ jsx(
1019
- DialogContent,
1020
- {
1021
- ...content,
1022
- titleId,
1023
- descId,
1024
- showCloseButton,
1025
- onClose
1026
- }
1027
- )
1028
- }
1029
- )
1030
- }
1031
- ),
1032
- document.body
1033
- );
1034
- }
1035
- function Tooltip({ content, children, placement = "top", delay = 100, disabled, className }) {
1036
- const [open, setOpen] = useState(false);
1037
- const timer = useRef(void 0);
1038
- const id = useId();
1039
- const visible = open && !disabled && content != null && content !== "";
1040
- useEffect(() => () => clearTimeout(timer.current), []);
1041
- const show = () => {
1042
- clearTimeout(timer.current);
1043
- timer.current = setTimeout(() => setOpen(true), delay);
1044
- };
1045
- const hide = () => {
1046
- clearTimeout(timer.current);
1047
- setOpen(false);
1048
- };
1049
- const trigger = isValidElement(children) ? cloneElement(children, {
1050
- "aria-describedby": visible ? id : void 0
1051
- }) : children;
1052
- return /* @__PURE__ */ jsxs(
1053
- "span",
1054
- {
1055
- className: cn("ru-tooltip", className),
1056
- onMouseEnter: show,
1057
- onMouseLeave: hide,
1058
- onFocus: show,
1059
- onBlur: hide,
1060
- onKeyDown: (e) => e.key === "Escape" && hide(),
1061
- children: [
1062
- trigger,
1063
- visible && /* @__PURE__ */ jsx("span", { role: "tooltip", id, className: cn("ru-tooltip__bubble", `ru-tooltip__bubble--${placement}`), children: content })
1064
- ]
1065
- }
1066
- );
1067
- }
1068
- function Dropdown({ trigger, items, align = "start", closeOnSelect = true, className }) {
1069
- const [open, setOpen] = useState(false);
1070
- const rootRef = useRef(null);
1071
- const menuRef = useRef(null);
1072
- const menuId = useId();
1073
- useClickOutside(rootRef, () => setOpen(false), open);
1074
- const getItems = () => {
1075
- var _a;
1076
- return Array.from(((_a = menuRef.current) == null ? void 0 : _a.querySelectorAll('[role="menuitem"]:not(:disabled)')) ?? []);
1077
- };
1078
- const focusItem = (index) => {
1079
- const els = getItems();
1080
- if (els.length) els[(index + els.length) % els.length].focus();
1081
- };
1082
- const closeAndFocusTrigger = () => {
1083
- var _a, _b;
1084
- setOpen(false);
1085
- (_b = (_a = rootRef.current) == null ? void 0 : _a.querySelector("[aria-haspopup]")) == null ? void 0 : _b.focus();
1086
- };
1087
- useEffect(() => {
1088
- if (open) focusItem(0);
1089
- }, [open]);
1090
- const onMenuKeyDown = (e) => {
1091
- const els = getItems();
1092
- const index = els.indexOf(document.activeElement);
1093
- switch (e.key) {
1094
- case "ArrowDown":
1095
- e.preventDefault();
1096
- focusItem(index + 1);
1097
- break;
1098
- case "ArrowUp":
1099
- e.preventDefault();
1100
- focusItem(index - 1);
1101
- break;
1102
- case "Home":
1103
- e.preventDefault();
1104
- focusItem(0);
1105
- break;
1106
- case "End":
1107
- e.preventDefault();
1108
- focusItem(-1);
1109
- break;
1110
- case "Escape":
1111
- e.preventDefault();
1112
- closeAndFocusTrigger();
1113
- break;
1114
- case "Tab":
1115
- setOpen(false);
1116
- break;
1117
- }
1118
- };
1119
- const triggerProps = trigger.props;
1120
- const triggerEl = cloneElement(trigger, {
1121
- "aria-haspopup": "menu",
1122
- "aria-expanded": open,
1123
- "aria-controls": open ? menuId : void 0,
1124
- onClick: (e) => {
1125
- var _a;
1126
- (_a = triggerProps.onClick) == null ? void 0 : _a.call(triggerProps, e);
1127
- setOpen((v) => !v);
1128
- },
1129
- onKeyDown: (e) => {
1130
- var _a;
1131
- (_a = triggerProps.onKeyDown) == null ? void 0 : _a.call(triggerProps, e);
1132
- if (e.key === "ArrowDown" && !open) {
1133
- e.preventDefault();
1134
- setOpen(true);
1135
- }
1136
- }
1137
- });
1138
- return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn("ru-dropdown", className), children: [
1139
- triggerEl,
1140
- open && /* @__PURE__ */ jsx(
1141
- "div",
1142
- {
1143
- ref: menuRef,
1144
- id: menuId,
1145
- role: "menu",
1146
- className: cn("ru-dropdown__menu", align === "end" && "ru-dropdown__menu--end"),
1147
- onKeyDown: onMenuKeyDown,
1148
- children: items.map(
1149
- (item, i) => item.divider ? /* @__PURE__ */ jsx("div", { role: "separator", className: "ru-dropdown__divider" }, item.key ?? i) : /* @__PURE__ */ jsxs(
1150
- "button",
1151
- {
1152
- type: "button",
1153
- role: "menuitem",
1154
- tabIndex: -1,
1155
- disabled: item.disabled,
1156
- className: cn("ru-dropdown__item", item.danger && "ru-dropdown__item--danger"),
1157
- onClick: () => {
1158
- var _a;
1159
- (_a = item.onClick) == null ? void 0 : _a.call(item);
1160
- if (closeOnSelect) closeAndFocusTrigger();
1161
- },
1162
- children: [
1163
- item.icon && /* @__PURE__ */ jsx("span", { className: "ru-dropdown__icon", children: item.icon }),
1164
- item.label
1165
- ]
1166
- },
1167
- item.key ?? i
1168
- )
1169
- )
1170
- }
1171
- )
1172
- ] });
1173
- }
1174
- function Tabs({ items, value, defaultValue, onChange, variant = "line", fullWidth = false, className }) {
1175
- var _a;
1176
- const baseId = useId();
1177
- const firstEnabled = ((_a = items.find((item) => !item.disabled)) == null ? void 0 : _a.value) ?? "";
1178
- const [active, setActive] = useControllableState(value, defaultValue ?? firstEnabled, onChange);
1179
- const tabRefs = useRef([]);
1180
- const onKeyDown = (e, index) => {
1181
- var _a2;
1182
- const enabled = items.flatMap((item, i) => item.disabled ? [] : [i]);
1183
- const pos = enabled.indexOf(index);
1184
- let next;
1185
- switch (e.key) {
1186
- case "ArrowRight":
1187
- next = enabled[(pos + 1) % enabled.length];
1188
- break;
1189
- case "ArrowLeft":
1190
- next = enabled[(pos - 1 + enabled.length) % enabled.length];
1191
- break;
1192
- case "Home":
1193
- next = enabled[0];
1194
- break;
1195
- case "End":
1196
- next = enabled[enabled.length - 1];
1197
- break;
1198
- default:
1199
- return;
1200
- }
1201
- e.preventDefault();
1202
- (_a2 = tabRefs.current[next]) == null ? void 0 : _a2.focus();
1203
- setActive(items[next].value);
1204
- };
1205
- const activeIndex = items.findIndex((item) => item.value === active);
1206
- const activeItem = items[activeIndex];
1207
- return /* @__PURE__ */ jsxs("div", { className: cn("ru-tabs", `ru-tabs--${variant}`, fullWidth && "ru-tabs--full", className), children: [
1208
- /* @__PURE__ */ jsx("div", { role: "tablist", className: "ru-tabs__list", children: items.map((item, i) => {
1209
- const selected = i === activeIndex;
1210
- return /* @__PURE__ */ jsxs(
1211
- "button",
1212
- {
1213
- ref: (el) => {
1214
- tabRefs.current[i] = el;
1215
- },
1216
- type: "button",
1217
- role: "tab",
1218
- id: `${baseId}-tab-${i}`,
1219
- "aria-selected": selected,
1220
- "aria-controls": `${baseId}-panel-${i}`,
1221
- tabIndex: selected ? 0 : -1,
1222
- disabled: item.disabled,
1223
- className: "ru-tabs__tab",
1224
- onClick: () => setActive(item.value),
1225
- onKeyDown: (e) => onKeyDown(e, i),
1226
- children: [
1227
- item.icon,
1228
- item.label
1229
- ]
1230
- },
1231
- item.value
1232
- );
1233
- }) }),
1234
- activeItem && activeItem.content !== void 0 && /* @__PURE__ */ jsx(
1235
- "div",
1236
- {
1237
- role: "tabpanel",
1238
- id: `${baseId}-panel-${activeIndex}`,
1239
- "aria-labelledby": `${baseId}-tab-${activeIndex}`,
1240
- tabIndex: 0,
1241
- className: "ru-tabs__panel",
1242
- children: activeItem.content
1243
- }
1244
- )
1245
- ] });
1035
+ )
1036
+ ] });
1246
1037
  }
1247
1038
  function Accordion({ items, multiple = false, value, defaultValue, onChange, className }) {
1248
1039
  const baseId = useId();
@@ -1356,15 +1147,663 @@ function Breadcrumbs({ items, separator, className }) {
1356
1147
  } else if (item.onClick) {
1357
1148
  node = /* @__PURE__ */ jsx("button", { type: "button", className: "ru-breadcrumbs__link", onClick: item.onClick, children: item.label });
1358
1149
  }
1359
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1150
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
1360
1151
  /* @__PURE__ */ jsx("li", { className: "ru-breadcrumbs__item", children: node }),
1361
1152
  !isLast && /* @__PURE__ */ jsx("li", { className: "ru-breadcrumbs__sep", "aria-hidden": "true", children: separator ?? /* @__PURE__ */ jsx(ChevronRightIcon, {}) })
1362
1153
  ] }, i);
1363
1154
  }) }) });
1364
1155
  }
1365
- const ToastContext = createContext(null);
1366
- function ToastProvider({ children, position = "bottom-right", duration = 4e3, max = 5 }) {
1367
- const [toasts, setToasts] = useState([]);
1156
+ function Badge({ variant = "soft", color = "primary", size = "md", dot = false, className, children, ...rest }) {
1157
+ return /* @__PURE__ */ jsxs("span", { className: cn("ru-badge", `ru-badge--${variant}`, `ru-badge--${size}`, `ru-c-${color}`, className), ...rest, children: [
1158
+ dot && /* @__PURE__ */ jsx("span", { className: "ru-badge__dot", "aria-hidden": "true" }),
1159
+ children
1160
+ ] });
1161
+ }
1162
+ function getInitials(name) {
1163
+ const parts = name.trim().split(/\s+/).filter(Boolean);
1164
+ if (parts.length === 0) return "";
1165
+ const first = parts[0][0];
1166
+ const last = parts.length > 1 ? parts[parts.length - 1][0] : "";
1167
+ return (first + last).toUpperCase();
1168
+ }
1169
+ const avatarSemanticColors = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
1170
+ const avatarStatusLabels = { online: "Online", away: "Away", busy: "Busy", offline: "Offline" };
1171
+ function hueFromString(text) {
1172
+ let hash = 0;
1173
+ for (const ch of text) hash = (hash * 31 + ch.charCodeAt(0)) % 360;
1174
+ return hash;
1175
+ }
1176
+ function resolveAvatarColor(color, seed) {
1177
+ if (!color) return void 0;
1178
+ if (color === "auto") return `hsl(${hueFromString(seed)} 70% 52%)`;
1179
+ return avatarSemanticColors.has(color) ? `var(--ru-${color})` : color;
1180
+ }
1181
+ function AvatarTooltip({ content, description, accent, children }) {
1182
+ const [open, setOpen] = useState(false);
1183
+ const bubbleRef = useRef(null);
1184
+ const tiltToward = (e) => {
1185
+ const bubble = bubbleRef.current;
1186
+ if (!bubble) return;
1187
+ const rect = e.currentTarget.getBoundingClientRect();
1188
+ const ratio = Math.max(-1, Math.min(1, (e.clientX - (rect.left + rect.width / 2)) / (rect.width / 2)));
1189
+ bubble.style.setProperty("--_shift", `${ratio * 10}px`);
1190
+ bubble.style.setProperty("--_tilt", `${ratio * 12}deg`);
1191
+ };
1192
+ return /* @__PURE__ */ jsxs(
1193
+ "span",
1194
+ {
1195
+ className: "ru-avatar-tip",
1196
+ onMouseEnter: () => setOpen(true),
1197
+ onMouseLeave: () => setOpen(false),
1198
+ onMouseMove: tiltToward,
1199
+ onFocus: () => setOpen(true),
1200
+ onBlur: () => setOpen(false),
1201
+ children: [
1202
+ children,
1203
+ open && // The avatar's accessible name already includes this text, so the bubble is visual only.
1204
+ /* @__PURE__ */ jsx("span", { ref: bubbleRef, className: "ru-avatar-tip__bubble", "aria-hidden": "true", style: accent ? { "--_accent": accent } : void 0, children: /* @__PURE__ */ jsxs("span", { className: "ru-avatar-tip__inner", children: [
1205
+ /* @__PURE__ */ jsx("span", { className: "ru-avatar-tip__name", children: content }),
1206
+ description && /* @__PURE__ */ jsx("span", { className: "ru-avatar-tip__desc", children: description })
1207
+ ] }) })
1208
+ ]
1209
+ }
1210
+ );
1211
+ }
1212
+ function Avatar({
1213
+ src,
1214
+ alt,
1215
+ name,
1216
+ size = "md",
1217
+ shape = "circle",
1218
+ color,
1219
+ ring,
1220
+ status,
1221
+ statusPulse = false,
1222
+ badge,
1223
+ tooltip,
1224
+ description,
1225
+ tooltipAccent,
1226
+ className,
1227
+ style,
1228
+ ...rest
1229
+ }) {
1230
+ const [failed, setFailed] = useState(false);
1231
+ useEffect(() => setFailed(false), [src]);
1232
+ const showImage = src && !failed;
1233
+ const initials = name ? getInitials(name) : "";
1234
+ const numericSize = typeof size === "number";
1235
+ const tint = resolveAvatarColor(color, name ?? alt ?? "");
1236
+ const ringColor = typeof ring === "string" ? resolveAvatarColor(ring, name ?? "") : void 0;
1237
+ const vars = {
1238
+ ...numericSize && { width: size, height: size, fontSize: Math.round(size * 0.38) },
1239
+ ...tint && { "--_avc": tint },
1240
+ ...ringColor && { "--_ring": ringColor },
1241
+ ...style
1242
+ };
1243
+ const baseLabel = alt ?? name ?? "Avatar";
1244
+ const label = [baseLabel, description && `, ${description}`, status && ` (${avatarStatusLabels[status] ?? status})`, badge !== void 0 && badge !== null && `, ${badge} new`].filter(Boolean).join("");
1245
+ const avatar = /* @__PURE__ */ jsxs(
1246
+ "span",
1247
+ {
1248
+ className: cn(
1249
+ "ru-avatar",
1250
+ !numericSize && `ru-avatar--${size}`,
1251
+ shape !== "circle" && `ru-avatar--${shape}`,
1252
+ tint && "ru-avatar--tinted",
1253
+ ring && "ru-avatar--ring",
1254
+ className
1255
+ ),
1256
+ role: showImage ? void 0 : "img",
1257
+ "aria-label": showImage ? void 0 : label,
1258
+ style: Object.keys(vars).length ? vars : void 0,
1259
+ ...rest,
1260
+ children: [
1261
+ showImage ? /* @__PURE__ */ jsx("img", { src, alt: label === "Avatar" ? "" : label, onError: () => setFailed(true) }) : initials ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: initials }) : /* @__PURE__ */ jsx(UserIcon, { className: "ru-avatar__icon" }),
1262
+ badge !== void 0 && badge !== null && /* @__PURE__ */ jsx("span", { className: "ru-avatar__badge", "aria-hidden": "true", children: badge }),
1263
+ status && /* @__PURE__ */ jsx(
1264
+ "span",
1265
+ {
1266
+ className: cn("ru-avatar__status", `ru-avatar__status--${status}`, statusPulse && "ru-avatar__status--pulse"),
1267
+ "aria-hidden": "true"
1268
+ }
1269
+ )
1270
+ ]
1271
+ }
1272
+ );
1273
+ if (!tooltip) return avatar;
1274
+ return /* @__PURE__ */ jsx(AvatarTooltip, { content: tooltip === true ? baseLabel : tooltip, description, accent: tooltipAccent, children: avatar });
1275
+ }
1276
+ const avatarOverlap = { tight: "-0.75rem", normal: "-0.5rem", loose: "-0.25rem", none: "0.25rem" };
1277
+ function AvatarGroup({ max, size = "md", spacing, tooltips = false, className, style, children, ...rest }) {
1278
+ const avatars = Children.toArray(children).filter(isValidElement);
1279
+ const visible = max ? avatars.slice(0, max) : avatars;
1280
+ const extra = avatars.length - visible.length;
1281
+ const overlap = spacing === void 0 ? void 0 : avatarOverlap[spacing] ?? `-${typeof spacing === "number" ? `${spacing}px` : spacing}`;
1282
+ const vars = overlap ? { "--_overlap": overlap, ...style } : style;
1283
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-avatar-group", className), style: vars, ...rest, children: [
1284
+ visible.map(
1285
+ (child) => cloneElement(child, {
1286
+ size: child.props.size ?? size,
1287
+ ...tooltips && child.props.tooltip === void 0 && { tooltip: true }
1288
+ })
1289
+ ),
1290
+ extra > 0 && /* @__PURE__ */ jsxs(
1291
+ "span",
1292
+ {
1293
+ className: cn("ru-avatar", typeof size === "number" ? void 0 : `ru-avatar--${size}`),
1294
+ style: typeof size === "number" ? { width: size, height: size, fontSize: Math.round(size * 0.34) } : void 0,
1295
+ role: "img",
1296
+ "aria-label": `${extra} more`,
1297
+ children: [
1298
+ "+",
1299
+ extra
1300
+ ]
1301
+ }
1302
+ )
1303
+ ] });
1304
+ }
1305
+ function Card({ variant = "outline", hoverable = false, className, ...rest }) {
1306
+ return /* @__PURE__ */ jsx(
1307
+ "div",
1308
+ {
1309
+ className: cn(
1310
+ "ru-card",
1311
+ variant === "elevated" && "ru-card--elevated",
1312
+ hoverable && "ru-card--hoverable",
1313
+ className
1314
+ ),
1315
+ ...rest
1316
+ }
1317
+ );
1318
+ }
1319
+ function CardHeader({ title, subtitle, action, className, children, ...rest }) {
1320
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-card__header", className), ...rest, children: [
1321
+ /* @__PURE__ */ jsxs("div", { children: [
1322
+ title && /* @__PURE__ */ jsx("h3", { className: "ru-card__title", children: title }),
1323
+ subtitle && /* @__PURE__ */ jsx("p", { className: "ru-card__subtitle", children: subtitle }),
1324
+ children
1325
+ ] }),
1326
+ action
1327
+ ] });
1328
+ }
1329
+ function CardBody({ className, ...rest }) {
1330
+ return /* @__PURE__ */ jsx("div", { className: cn("ru-card__body", className), ...rest });
1331
+ }
1332
+ function CardFooter({ className, ...rest }) {
1333
+ return /* @__PURE__ */ jsx("div", { className: cn("ru-card__footer", className), ...rest });
1334
+ }
1335
+ const dividerSemanticColors = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
1336
+ const dividerThickness = { sm: "1px", md: "2px", lg: "4px" };
1337
+ const dividerSpacing = { none: "0", sm: "0.5rem", md: "1rem", lg: "2rem", xl: "3rem" };
1338
+ const toLength = (v) => typeof v === "number" ? `${v}px` : v;
1339
+ function Divider({
1340
+ orientation = "horizontal",
1341
+ variant = "solid",
1342
+ size,
1343
+ color,
1344
+ length,
1345
+ align = "center",
1346
+ spacing,
1347
+ label,
1348
+ labelPosition = "center",
1349
+ className,
1350
+ style,
1351
+ ...rest
1352
+ }) {
1353
+ const vertical = orientation === "vertical";
1354
+ const labelled = Boolean(label) && !vertical;
1355
+ const vars = {
1356
+ ...size !== void 0 && { "--_t": dividerThickness[size] ?? toLength(size) },
1357
+ ...color && { "--_line": dividerSemanticColors.has(color) ? `var(--ru-${color})` : color },
1358
+ ...length !== void 0 && { "--_len": toLength(length) },
1359
+ ...spacing !== void 0 && { "--_gap": dividerSpacing[spacing] ?? toLength(spacing) },
1360
+ ...style
1361
+ };
1362
+ const styleProp = Object.keys(vars).length ? vars : void 0;
1363
+ const classes = cn(
1364
+ "ru-divider",
1365
+ variant !== "solid" && `ru-divider--${variant}`,
1366
+ vertical && "ru-divider--vertical",
1367
+ labelled && "ru-divider--label",
1368
+ labelled && labelPosition !== "center" && `ru-divider--label-${labelPosition}`,
1369
+ !vertical && align !== "center" && `ru-divider--align-${align}`,
1370
+ className
1371
+ );
1372
+ if (labelled) {
1373
+ return /* @__PURE__ */ jsx("div", { role: "separator", "aria-orientation": "horizontal", className: classes, style: styleProp, ...rest, children: /* @__PURE__ */ jsx("span", { className: "ru-divider__label", children: label }) });
1374
+ }
1375
+ return /* @__PURE__ */ jsx("hr", { "aria-orientation": orientation, className: classes, style: styleProp, ...rest });
1376
+ }
1377
+ function usePagination(pagination, dataLength) {
1378
+ const opts = pagination === true ? {} : pagination || {};
1379
+ const enabled = Boolean(pagination);
1380
+ const [pageSize, setPageSize] = useControllableState(
1381
+ opts.onPageSizeChange ? opts.pageSize : void 0,
1382
+ opts.pageSize ?? opts.defaultPageSize ?? 10,
1383
+ opts.onPageSizeChange
1384
+ );
1385
+ const [page, setPage] = useControllableState(opts.page, opts.defaultPage ?? 1, opts.onPageChange);
1386
+ const serverSide = opts.totalRows !== void 0;
1387
+ const totalRows = serverSide ? opts.totalRows : dataLength;
1388
+ const totalPages = Math.max(1, Math.ceil(totalRows / pageSize));
1389
+ const current = Math.min(Math.max(1, page), totalPages);
1390
+ const start = enabled ? (current - 1) * pageSize : 0;
1391
+ return {
1392
+ enabled,
1393
+ opts,
1394
+ serverSide,
1395
+ page: current,
1396
+ pageSize,
1397
+ totalRows,
1398
+ totalPages,
1399
+ start,
1400
+ setPage,
1401
+ changePageSize: (size) => {
1402
+ setPageSize(size);
1403
+ setPage(1);
1404
+ }
1405
+ };
1406
+ }
1407
+ function Table({
1408
+ columns,
1409
+ data,
1410
+ rowKey,
1411
+ striped = false,
1412
+ hoverable = false,
1413
+ size = "md",
1414
+ caption,
1415
+ emptyText = "No data",
1416
+ onRowClick,
1417
+ pagination,
1418
+ loading = false,
1419
+ className
1420
+ }) {
1421
+ const pager = usePagination(pagination, data.length);
1422
+ const rows = pager.enabled && !pager.serverSide ? data.slice(pager.start, pager.start + pager.pageSize) : data;
1423
+ const getKey = (row, index) => {
1424
+ if (typeof rowKey === "function") return rowKey(row, index);
1425
+ if (rowKey !== void 0) return String(row[rowKey]);
1426
+ return index;
1427
+ };
1428
+ const firstRow = pager.totalRows === 0 ? 0 : pager.start + 1;
1429
+ const lastRow = Math.min(pager.start + (pager.serverSide ? rows.length : pager.pageSize), pager.totalRows);
1430
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-table-wrap", className), children: [
1431
+ /* @__PURE__ */ jsx("div", { className: "ru-table-scroll", children: /* @__PURE__ */ jsxs(
1432
+ "table",
1433
+ {
1434
+ className: cn(
1435
+ "ru-table",
1436
+ `ru-table--${size}`,
1437
+ striped && "ru-table--striped",
1438
+ (hoverable || onRowClick) && "ru-table--hover",
1439
+ loading && "ru-table--loading"
1440
+ ),
1441
+ "aria-busy": loading || void 0,
1442
+ children: [
1443
+ caption && /* @__PURE__ */ jsx("caption", { children: caption }),
1444
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsx("th", { scope: "col", style: { textAlign: col.align, width: col.width }, children: col.header }, col.key)) }) }),
1445
+ /* @__PURE__ */ jsx("tbody", { children: rows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { className: "ru-table__empty", colSpan: columns.length, children: loading ? "Loading…" : emptyText }) }) : rows.map((row, i) => {
1446
+ const index = pager.start + i;
1447
+ return /* @__PURE__ */ jsx(
1448
+ "tr",
1449
+ {
1450
+ onClick: onRowClick ? () => onRowClick(row, index) : void 0,
1451
+ style: onRowClick ? { cursor: "pointer" } : void 0,
1452
+ children: columns.map((col) => /* @__PURE__ */ jsx("td", { style: { textAlign: col.align }, children: col.render ? col.render(row, index) : row[col.key] }, col.key))
1453
+ },
1454
+ getKey(row, index)
1455
+ );
1456
+ }) })
1457
+ ]
1458
+ }
1459
+ ) }),
1460
+ pager.enabled && /* @__PURE__ */ jsxs("div", { className: "ru-table__footer", children: [
1461
+ /* @__PURE__ */ jsx("p", { className: "ru-table__info", "aria-live": "polite", children: pager.totalRows === 0 ? "No results" : /* @__PURE__ */ jsxs(Fragment, { children: [
1462
+ "Showing ",
1463
+ /* @__PURE__ */ jsx("strong", { children: firstRow }),
1464
+ "–",
1465
+ /* @__PURE__ */ jsx("strong", { children: lastRow }),
1466
+ " of ",
1467
+ /* @__PURE__ */ jsx("strong", { children: pager.totalRows })
1468
+ ] }) }),
1469
+ pager.opts.pageSizeOptions && /* @__PURE__ */ jsxs("div", { className: "ru-table__size", children: [
1470
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "Rows per page" }),
1471
+ /* @__PURE__ */ jsx(
1472
+ Select,
1473
+ {
1474
+ size: "sm",
1475
+ "aria-label": "Rows per page",
1476
+ value: pager.pageSize,
1477
+ onChange: pager.changePageSize,
1478
+ options: pager.opts.pageSizeOptions.map((n) => ({ label: String(n), value: n })),
1479
+ containerClassName: "ru-table__size-select"
1480
+ }
1481
+ )
1482
+ ] }),
1483
+ pager.totalPages > 1 && /* @__PURE__ */ jsx(
1484
+ Pagination,
1485
+ {
1486
+ size: "sm",
1487
+ page: pager.page,
1488
+ totalPages: pager.totalPages,
1489
+ onChange: pager.setPage,
1490
+ siblingCount: pager.opts.siblingCount,
1491
+ className: "ru-table__pagination"
1492
+ }
1493
+ )
1494
+ ] })
1495
+ ] });
1496
+ }
1497
+ const EXIT_MS = 200;
1498
+ function usePresence(open) {
1499
+ const [render, setRender] = useState(open);
1500
+ const [closing, setClosing] = useState(false);
1501
+ useEffect(() => {
1502
+ if (open) {
1503
+ setRender(true);
1504
+ setClosing(false);
1505
+ return void 0;
1506
+ }
1507
+ if (!render) return void 0;
1508
+ setClosing(true);
1509
+ const timer = setTimeout(() => {
1510
+ setRender(false);
1511
+ setClosing(false);
1512
+ }, EXIT_MS);
1513
+ return () => clearTimeout(timer);
1514
+ }, [open]);
1515
+ return { render, closing };
1516
+ }
1517
+ function DialogContent({ titleId, descId, title, description, showCloseButton, onClose, children, footer }) {
1518
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1519
+ (title || description || showCloseButton) && /* @__PURE__ */ jsxs("div", { className: "ru-modal__header", children: [
1520
+ /* @__PURE__ */ jsxs("div", { children: [
1521
+ title && /* @__PURE__ */ jsx("h2", { id: titleId, className: "ru-modal__title", children: title }),
1522
+ description && /* @__PURE__ */ jsx("p", { id: descId, className: "ru-modal__desc", children: description })
1523
+ ] }),
1524
+ showCloseButton && /* @__PURE__ */ jsx(CloseButton, { onClick: onClose })
1525
+ ] }),
1526
+ /* @__PURE__ */ jsx("div", { className: "ru-modal__body", children }),
1527
+ footer && /* @__PURE__ */ jsx("div", { className: "ru-modal__footer", children: footer })
1528
+ ] });
1529
+ }
1530
+ function Modal({
1531
+ open,
1532
+ onClose,
1533
+ size = "md",
1534
+ closeOnOverlayClick = true,
1535
+ closeOnEsc = true,
1536
+ showCloseButton = true,
1537
+ className,
1538
+ ...content
1539
+ }) {
1540
+ const panelRef = useRef(null);
1541
+ const mounted = useMounted();
1542
+ const titleId = useId();
1543
+ const descId = useId();
1544
+ const { render, closing } = usePresence(open);
1545
+ useOverlay(open && mounted, panelRef, onClose, closeOnEsc);
1546
+ if (!render || !mounted) return null;
1547
+ const onOverlayMouseDown = (e) => {
1548
+ if (!closing && closeOnOverlayClick && e.target === e.currentTarget) onClose();
1549
+ };
1550
+ return createPortal(
1551
+ /* @__PURE__ */ jsx("div", { className: cn("ru-overlay", closing && "ru-overlay--closing"), onMouseDown: onOverlayMouseDown, children: /* @__PURE__ */ jsx(
1552
+ "div",
1553
+ {
1554
+ ref: panelRef,
1555
+ role: "dialog",
1556
+ "aria-modal": "true",
1557
+ "aria-labelledby": content.title ? titleId : void 0,
1558
+ "aria-describedby": content.description ? descId : void 0,
1559
+ tabIndex: -1,
1560
+ className: cn("ru-modal", `ru-modal--${size}`, closing && "ru-modal--closing", className),
1561
+ children: /* @__PURE__ */ jsx(
1562
+ DialogContent,
1563
+ {
1564
+ ...content,
1565
+ titleId,
1566
+ descId,
1567
+ showCloseButton,
1568
+ onClose
1569
+ }
1570
+ )
1571
+ }
1572
+ ) }),
1573
+ document.body
1574
+ );
1575
+ }
1576
+ const drawerSizes = { sm: "20rem", md: "24rem", lg: "32rem" };
1577
+ function Drawer({
1578
+ open,
1579
+ onClose,
1580
+ placement = "right",
1581
+ size = "md",
1582
+ floating = false,
1583
+ closeOnOverlayClick = true,
1584
+ closeOnEsc = true,
1585
+ showCloseButton = true,
1586
+ className,
1587
+ ...content
1588
+ }) {
1589
+ const panelRef = useRef(null);
1590
+ const mounted = useMounted();
1591
+ const titleId = useId();
1592
+ const descId = useId();
1593
+ const { render, closing } = usePresence(open);
1594
+ useOverlay(open && mounted, panelRef, onClose, closeOnEsc);
1595
+ if (!render || !mounted) return null;
1596
+ const resolvedSize = typeof size === "number" ? `${size}px` : drawerSizes[size] ?? size;
1597
+ return createPortal(
1598
+ /* @__PURE__ */ jsx(
1599
+ "div",
1600
+ {
1601
+ className: cn("ru-overlay", "ru-overlay--drawer", closing && "ru-overlay--closing"),
1602
+ onMouseDown: (e) => {
1603
+ if (!closing && closeOnOverlayClick && e.target === e.currentTarget) onClose();
1604
+ },
1605
+ children: /* @__PURE__ */ jsx(
1606
+ "div",
1607
+ {
1608
+ ref: panelRef,
1609
+ role: "dialog",
1610
+ "aria-modal": "true",
1611
+ "aria-labelledby": content.title ? titleId : void 0,
1612
+ "aria-describedby": content.description ? descId : void 0,
1613
+ tabIndex: -1,
1614
+ className: cn(
1615
+ "ru-drawer",
1616
+ `ru-drawer--${placement}`,
1617
+ floating && "ru-drawer--floating",
1618
+ closing && "ru-drawer--closing",
1619
+ className
1620
+ ),
1621
+ style: { "--ru-drawer-size": resolvedSize },
1622
+ children: /* @__PURE__ */ jsx(
1623
+ DialogContent,
1624
+ {
1625
+ ...content,
1626
+ titleId,
1627
+ descId,
1628
+ showCloseButton,
1629
+ onClose
1630
+ }
1631
+ )
1632
+ }
1633
+ )
1634
+ }
1635
+ ),
1636
+ document.body
1637
+ );
1638
+ }
1639
+ function Tooltip({
1640
+ content,
1641
+ children,
1642
+ placement = "top",
1643
+ delay = 100,
1644
+ size = "md",
1645
+ arrow = true,
1646
+ color,
1647
+ textColor,
1648
+ open: openProp,
1649
+ disabled,
1650
+ className
1651
+ }) {
1652
+ const [open, setOpen] = useState(false);
1653
+ const timer = useRef(void 0);
1654
+ const id = useId();
1655
+ const visible = (openProp ?? open) && !disabled && content != null && content !== "";
1656
+ useEffect(() => () => clearTimeout(timer.current), []);
1657
+ const show = () => {
1658
+ clearTimeout(timer.current);
1659
+ timer.current = setTimeout(() => setOpen(true), delay);
1660
+ };
1661
+ const hide = () => {
1662
+ clearTimeout(timer.current);
1663
+ setOpen(false);
1664
+ };
1665
+ const trigger = isValidElement(children) ? cloneElement(children, {
1666
+ "aria-describedby": visible ? id : void 0
1667
+ }) : children;
1668
+ return /* @__PURE__ */ jsxs(
1669
+ "span",
1670
+ {
1671
+ className: cn("ru-tooltip", className),
1672
+ onMouseEnter: show,
1673
+ onMouseLeave: hide,
1674
+ onFocus: show,
1675
+ onBlur: hide,
1676
+ onKeyDown: (e) => e.key === "Escape" && hide(),
1677
+ children: [
1678
+ trigger,
1679
+ visible && /* @__PURE__ */ jsx(
1680
+ "span",
1681
+ {
1682
+ role: "tooltip",
1683
+ id,
1684
+ className: cn(
1685
+ "ru-tooltip__bubble",
1686
+ `ru-tooltip__bubble--${placement}`,
1687
+ size !== "md" && `ru-tooltip__bubble--${size}`,
1688
+ !arrow && "ru-tooltip__bubble--no-arrow"
1689
+ ),
1690
+ style: color || textColor ? { "--ru-tooltip-bg": color, "--ru-tooltip-text": textColor } : void 0,
1691
+ children: content
1692
+ }
1693
+ )
1694
+ ]
1695
+ }
1696
+ );
1697
+ }
1698
+ function Dropdown({ trigger, items, align = "start", closeOnSelect = true, className }) {
1699
+ const [open, setOpen] = useState(false);
1700
+ const rootRef = useRef(null);
1701
+ const menuRef = useRef(null);
1702
+ const menuId = useId();
1703
+ useClickOutside(rootRef, () => setOpen(false), open);
1704
+ const getItems = () => {
1705
+ var _a;
1706
+ return Array.from(((_a = menuRef.current) == null ? void 0 : _a.querySelectorAll('[role="menuitem"]:not(:disabled)')) ?? []);
1707
+ };
1708
+ const focusItem = (index) => {
1709
+ const els = getItems();
1710
+ if (els.length) els[(index + els.length) % els.length].focus();
1711
+ };
1712
+ const closeAndFocusTrigger = () => {
1713
+ var _a, _b;
1714
+ setOpen(false);
1715
+ (_b = (_a = rootRef.current) == null ? void 0 : _a.querySelector("[aria-haspopup]")) == null ? void 0 : _b.focus();
1716
+ };
1717
+ useEffect(() => {
1718
+ if (open) focusItem(0);
1719
+ }, [open]);
1720
+ const onMenuKeyDown = (e) => {
1721
+ const els = getItems();
1722
+ const index = els.indexOf(document.activeElement);
1723
+ switch (e.key) {
1724
+ case "ArrowDown":
1725
+ e.preventDefault();
1726
+ focusItem(index + 1);
1727
+ break;
1728
+ case "ArrowUp":
1729
+ e.preventDefault();
1730
+ focusItem(index - 1);
1731
+ break;
1732
+ case "Home":
1733
+ e.preventDefault();
1734
+ focusItem(0);
1735
+ break;
1736
+ case "End":
1737
+ e.preventDefault();
1738
+ focusItem(-1);
1739
+ break;
1740
+ case "Escape":
1741
+ e.preventDefault();
1742
+ closeAndFocusTrigger();
1743
+ break;
1744
+ case "Tab":
1745
+ setOpen(false);
1746
+ break;
1747
+ }
1748
+ };
1749
+ const triggerProps = trigger.props;
1750
+ const triggerEl = cloneElement(trigger, {
1751
+ "aria-haspopup": "menu",
1752
+ "aria-expanded": open,
1753
+ "aria-controls": open ? menuId : void 0,
1754
+ onClick: (e) => {
1755
+ var _a;
1756
+ (_a = triggerProps.onClick) == null ? void 0 : _a.call(triggerProps, e);
1757
+ setOpen((v) => !v);
1758
+ },
1759
+ onKeyDown: (e) => {
1760
+ var _a;
1761
+ (_a = triggerProps.onKeyDown) == null ? void 0 : _a.call(triggerProps, e);
1762
+ if (e.key === "ArrowDown" && !open) {
1763
+ e.preventDefault();
1764
+ setOpen(true);
1765
+ }
1766
+ }
1767
+ });
1768
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn("ru-dropdown", className), children: [
1769
+ triggerEl,
1770
+ open && /* @__PURE__ */ jsx(
1771
+ "div",
1772
+ {
1773
+ ref: menuRef,
1774
+ id: menuId,
1775
+ role: "menu",
1776
+ className: cn("ru-dropdown__menu", align === "end" && "ru-dropdown__menu--end"),
1777
+ onKeyDown: onMenuKeyDown,
1778
+ children: items.map(
1779
+ (item, i) => item.divider ? /* @__PURE__ */ jsx("div", { role: "separator", className: "ru-dropdown__divider" }, item.key ?? i) : /* @__PURE__ */ jsxs(
1780
+ "button",
1781
+ {
1782
+ type: "button",
1783
+ role: "menuitem",
1784
+ tabIndex: -1,
1785
+ disabled: item.disabled,
1786
+ className: cn("ru-dropdown__item", item.danger && "ru-dropdown__item--danger"),
1787
+ onClick: () => {
1788
+ var _a;
1789
+ (_a = item.onClick) == null ? void 0 : _a.call(item);
1790
+ if (closeOnSelect) closeAndFocusTrigger();
1791
+ },
1792
+ children: [
1793
+ item.icon && /* @__PURE__ */ jsx("span", { className: "ru-dropdown__icon", children: item.icon }),
1794
+ item.label
1795
+ ]
1796
+ },
1797
+ item.key ?? i
1798
+ )
1799
+ )
1800
+ }
1801
+ )
1802
+ ] });
1803
+ }
1804
+ const ToastContext = createContext(null);
1805
+ function ToastProvider({ children, position = "bottom-right", duration = 4e3, max = 5 }) {
1806
+ const [toasts, setToasts] = useState([]);
1368
1807
  const mounted = useMounted();
1369
1808
  const counter = useRef(0);
1370
1809
  const dismiss = useCallback((id) => {
@@ -1393,73 +1832,962 @@ function ToastProvider({ children, position = "bottom-right", duration = 4e3, ma
1393
1832
  )
1394
1833
  ] });
1395
1834
  }
1396
- function ToastView({ toast, duration, onDismiss }) {
1397
- const [paused, setPaused] = useState(false);
1398
- const { id, variant = "default", title, description, action } = toast;
1835
+ function ToastView({ toast, duration, onDismiss }) {
1836
+ const [paused, setPaused] = useState(false);
1837
+ const { id, variant = "default", title, description, action } = toast;
1838
+ useEffect(() => {
1839
+ if (paused || !Number.isFinite(duration) || duration <= 0) return;
1840
+ const timer = setTimeout(() => onDismiss(id), duration);
1841
+ return () => clearTimeout(timer);
1842
+ }, [id, duration, paused, onDismiss]);
1843
+ const Icon = variant === "default" ? null : statusIcons[variant];
1844
+ return /* @__PURE__ */ jsxs(
1845
+ "li",
1846
+ {
1847
+ role: "status",
1848
+ className: cn("ru-toast", variant !== "default" && `ru-c-${variant}`),
1849
+ onMouseEnter: () => setPaused(true),
1850
+ onMouseLeave: () => setPaused(false),
1851
+ children: [
1852
+ Icon && /* @__PURE__ */ jsx("span", { className: "ru-toast__icon", children: /* @__PURE__ */ jsx(Icon, {}) }),
1853
+ /* @__PURE__ */ jsxs("div", { className: "ru-toast__content", children: [
1854
+ title && /* @__PURE__ */ jsx("div", { className: "ru-toast__title", children: title }),
1855
+ description && /* @__PURE__ */ jsx("div", { className: "ru-toast__desc", children: description }),
1856
+ action && /* @__PURE__ */ jsx("div", { className: "ru-toast__action", children: action })
1857
+ ] }),
1858
+ /* @__PURE__ */ jsx(CloseButton, { onClick: () => onDismiss(id), label: "Dismiss notification" })
1859
+ ]
1860
+ }
1861
+ );
1862
+ }
1863
+ function useToast() {
1864
+ const ctx = useContext(ToastContext);
1865
+ if (!ctx) throw new Error("useToast must be used within a <ToastProvider>.");
1866
+ return ctx;
1867
+ }
1868
+ function Kbd({ className, ...rest }) {
1869
+ return /* @__PURE__ */ jsx("kbd", { className: cn("ru-kbd", className), ...rest });
1870
+ }
1871
+ const patternSemanticColors = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
1872
+ const toCssColor = (c) => patternSemanticColors.has(c) ? `var(--ru-${c})` : c;
1873
+ const toPx = (v) => typeof v === "number" ? `${v}px` : v;
1874
+ function Pattern({
1875
+ variant = "grid",
1876
+ size = 24,
1877
+ thickness,
1878
+ color,
1879
+ opacity,
1880
+ fade = true,
1881
+ animate,
1882
+ speed,
1883
+ spotlight = false,
1884
+ spotlightColor,
1885
+ spotlightSize,
1886
+ className,
1887
+ style,
1888
+ ...rest
1889
+ }) {
1890
+ const ref = useRef(null);
1891
+ useEffect(() => {
1892
+ const el = ref.current;
1893
+ const parent = el == null ? void 0 : el.parentElement;
1894
+ if (!spotlight || !parent) return void 0;
1895
+ const move = (e) => {
1896
+ const rect = el.getBoundingClientRect();
1897
+ el.style.setProperty("--ru-mx", `${e.clientX - rect.left}px`);
1898
+ el.style.setProperty("--ru-my", `${e.clientY - rect.top}px`);
1899
+ el.classList.add("ru-pattern--spot-on");
1900
+ };
1901
+ const leave = () => el.classList.remove("ru-pattern--spot-on");
1902
+ parent.addEventListener("pointermove", move);
1903
+ parent.addEventListener("pointerleave", leave);
1904
+ return () => {
1905
+ parent.removeEventListener("pointermove", move);
1906
+ parent.removeEventListener("pointerleave", leave);
1907
+ };
1908
+ }, [spotlight]);
1909
+ const fadeKind = fade === true ? "radial" : fade || null;
1910
+ const vars = {
1911
+ "--_ps": toPx(size),
1912
+ ...thickness !== void 0 && { "--_pt": toPx(thickness) },
1913
+ ...color && { "--_pc": toCssColor(color) },
1914
+ ...spotlightColor && { "--_spc": toCssColor(spotlightColor) },
1915
+ ...spotlightSize !== void 0 && { "--_sr": toPx(spotlightSize) },
1916
+ ...speed !== void 0 && { "--_speed": typeof speed === "number" ? `${speed}s` : speed },
1917
+ ...opacity !== void 0 && { opacity },
1918
+ ...style
1919
+ };
1920
+ const layerClass = `ru-pattern__layer ru-pattern__layer--${variant}`;
1921
+ return /* @__PURE__ */ jsxs(
1922
+ "div",
1923
+ {
1924
+ ref,
1925
+ "aria-hidden": "true",
1926
+ className: cn("ru-pattern", fadeKind && `ru-pattern--fade-${fadeKind}`, animate && `ru-pattern--${animate}`, className),
1927
+ style: vars,
1928
+ ...rest,
1929
+ children: [
1930
+ /* @__PURE__ */ jsx("div", { className: layerClass }),
1931
+ spotlight && /* @__PURE__ */ jsx("div", { className: "ru-pattern__spot", children: /* @__PURE__ */ jsx("div", { className: layerClass }) })
1932
+ ]
1933
+ }
1934
+ );
1935
+ }
1936
+ function SpotlightCard({ color, className, style, onMouseMove, children, ...rest }) {
1937
+ const ref = useRef(null);
1938
+ const handleMouseMove = (e) => {
1939
+ const el = ref.current;
1940
+ if (el) {
1941
+ const rect = el.getBoundingClientRect();
1942
+ el.style.setProperty("--ru-spot-x", `${e.clientX - rect.left}px`);
1943
+ el.style.setProperty("--ru-spot-y", `${e.clientY - rect.top}px`);
1944
+ }
1945
+ onMouseMove == null ? void 0 : onMouseMove(e);
1946
+ };
1947
+ return /* @__PURE__ */ jsx(
1948
+ "div",
1949
+ {
1950
+ ref,
1951
+ className: cn("ru-card", "ru-spotlight", className),
1952
+ style: color ? { "--ru-spot-color": color, ...style } : style,
1953
+ onMouseMove: handleMouseMove,
1954
+ ...rest,
1955
+ children
1956
+ }
1957
+ );
1958
+ }
1959
+ function DotText({ as: Tag = "span", dot = 4, color, glow = false, className, style, children, ...rest }) {
1960
+ return /* @__PURE__ */ jsx(
1961
+ Tag,
1962
+ {
1963
+ className: cn("ru-dot-text", glow && "ru-dot-text--glow", className),
1964
+ style: { "--_d": `${dot}px`, ...color && { color }, ...style },
1965
+ ...rest,
1966
+ children
1967
+ }
1968
+ );
1969
+ }
1970
+ const semanticColors$4 = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
1971
+ function Pager({ onPrev, onNext, prevLabel, nextLabel }) {
1972
+ if (!onPrev && !onNext) return null;
1973
+ return /* @__PURE__ */ jsxs("div", { className: "ru-frame-card__pager", children: [
1974
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": prevLabel, onClick: onPrev, disabled: !onPrev, children: /* @__PURE__ */ jsx(ChevronLeftIcon, {}) }),
1975
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": nextLabel, onClick: onNext, disabled: !onNext, children: /* @__PURE__ */ jsx(ChevronRightIcon, {}) })
1976
+ ] });
1977
+ }
1978
+ function FrameCard({
1979
+ title,
1980
+ color = "warning",
1981
+ textColor,
1982
+ titlePlacement = "panel",
1983
+ collapsible = false,
1984
+ open,
1985
+ defaultOpen = true,
1986
+ onOpenChange,
1987
+ headerAction,
1988
+ footer,
1989
+ onPrev,
1990
+ onNext,
1991
+ prevLabel = "Previous",
1992
+ nextLabel = "Next",
1993
+ className,
1994
+ style,
1995
+ children,
1996
+ ...rest
1997
+ }) {
1998
+ const [isOpen, setOpen] = useControllableState(open, defaultOpen, onOpenChange);
1999
+ const bodyId = useId();
2000
+ const onFrame = titlePlacement === "frame";
2001
+ const pager = /* @__PURE__ */ jsx(Pager, { onPrev, onNext, prevLabel, nextLabel });
2002
+ const hasPager = Boolean(onPrev || onNext);
2003
+ return /* @__PURE__ */ jsxs(
2004
+ "section",
2005
+ {
2006
+ className: cn("ru-frame-card", onFrame && "ru-frame-card--title-frame", className),
2007
+ style: {
2008
+ "--_fc": semanticColors$4.has(color) ? `var(--ru-${color})` : color,
2009
+ ...textColor && { "--_fc-on": textColor, "--_fc-ink": textColor },
2010
+ ...style
2011
+ },
2012
+ ...rest,
2013
+ children: [
2014
+ onFrame && /* @__PURE__ */ jsxs("div", { className: "ru-frame-card__frame-head", children: [
2015
+ /* @__PURE__ */ jsx("h3", { className: "ru-frame-card__title", children: title }),
2016
+ headerAction ?? pager
2017
+ ] }),
2018
+ /* @__PURE__ */ jsxs("div", { className: "ru-frame-card__panel", children: [
2019
+ !onFrame && (title || headerAction || collapsible) && /* @__PURE__ */ jsxs("div", { className: "ru-frame-card__head", children: [
2020
+ /* @__PURE__ */ jsx("h3", { className: "ru-frame-card__title", children: title }),
2021
+ headerAction,
2022
+ collapsible && /* @__PURE__ */ jsx(
2023
+ "button",
2024
+ {
2025
+ type: "button",
2026
+ className: "ru-frame-card__toggle",
2027
+ "aria-expanded": isOpen,
2028
+ "aria-controls": bodyId,
2029
+ "aria-label": isOpen ? "Collapse" : "Expand",
2030
+ onClick: () => setOpen(!isOpen),
2031
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, {})
2032
+ }
2033
+ )
2034
+ ] }),
2035
+ /* @__PURE__ */ jsx("div", { id: bodyId, className: cn("ru-frame-card__collapse", !isOpen && "ru-frame-card__collapse--closed"), children: /* @__PURE__ */ jsx("div", { className: "ru-frame-card__inner", children: /* @__PURE__ */ jsx("div", { className: "ru-frame-card__body", children }) }) })
2036
+ ] }),
2037
+ (footer || !onFrame && hasPager) && /* @__PURE__ */ jsxs("div", { className: "ru-frame-card__foot", children: [
2038
+ /* @__PURE__ */ jsx("div", { className: "ru-frame-card__foot-content", children: footer }),
2039
+ !onFrame && pager
2040
+ ] })
2041
+ ]
2042
+ }
2043
+ );
2044
+ }
2045
+ const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
2046
+ const WEEKDAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
2047
+ const semanticColors$3 = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
2048
+ const dayKey = (d) => d.getFullYear() * 1e4 + d.getMonth() * 100 + d.getDate();
2049
+ const sameDay = (a, b) => Boolean(a && b) && dayKey(a) === dayKey(b);
2050
+ function Calendar({
2051
+ mode = "single",
2052
+ value,
2053
+ defaultValue,
2054
+ onChange,
2055
+ defaultMonth,
2056
+ weekStartsOn = 1,
2057
+ minYear,
2058
+ maxYear,
2059
+ color,
2060
+ today = /* @__PURE__ */ new Date(),
2061
+ className,
2062
+ style,
2063
+ ...rest
2064
+ }) {
2065
+ const emptyValue = mode === "range" ? { start: null, end: null } : null;
2066
+ const [selected, setSelected] = useControllableState(value, defaultValue ?? emptyValue, onChange);
2067
+ const anchor = defaultMonth ?? (mode === "range" ? selected == null ? void 0 : selected.start : selected) ?? today;
2068
+ const [view, setView] = useState({ y: anchor.getFullYear(), m: anchor.getMonth() });
2069
+ const gridRef = useRef(null);
2070
+ const shift = (delta) => setView((v) => {
2071
+ const d = new Date(v.y, v.m + delta, 1);
2072
+ return { y: d.getFullYear(), m: d.getMonth() };
2073
+ });
2074
+ const days = useMemo(() => {
2075
+ const offset = (new Date(view.y, view.m, 1).getDay() - weekStartsOn + 7) % 7;
2076
+ const inMonth = new Date(view.y, view.m + 1, 0).getDate();
2077
+ const cells = Math.ceil((offset + inMonth) / 7) * 7;
2078
+ return Array.from({ length: cells }, (_, i) => new Date(view.y, view.m, 1 - offset + i));
2079
+ }, [view, weekStartsOn]);
2080
+ const weekdays = Array.from({ length: 7 }, (_, i) => WEEKDAYS[(i + weekStartsOn) % 7]);
2081
+ const firstYear = minYear ?? view.y - 10;
2082
+ const lastYear = maxYear ?? view.y + 10;
2083
+ const years = Array.from({ length: lastYear - firstYear + 1 }, (_, i) => firstYear + i);
2084
+ const isSelected = (d) => {
2085
+ if (mode !== "range") return sameDay(d, selected);
2086
+ const { start, end } = selected ?? {};
2087
+ if (start && end) return dayKey(d) >= dayKey(start) && dayKey(d) <= dayKey(end);
2088
+ return sameDay(d, start);
2089
+ };
2090
+ const pick = (d) => {
2091
+ if (d.getMonth() !== view.m) setView({ y: d.getFullYear(), m: d.getMonth() });
2092
+ if (mode !== "range") return setSelected(d);
2093
+ const { start, end } = selected ?? {};
2094
+ if (!start || end) return setSelected({ start: d, end: null });
2095
+ return setSelected(dayKey(d) < dayKey(start) ? { start: d, end: start } : { start, end: d });
2096
+ };
2097
+ const onGridKeyDown = (e) => {
2098
+ const step = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[e.key];
2099
+ if (!step) return;
2100
+ const buttons = Array.from(gridRef.current.querySelectorAll("button"));
2101
+ const next = buttons[buttons.indexOf(document.activeElement) + step];
2102
+ if (next) {
2103
+ e.preventDefault();
2104
+ next.focus();
2105
+ }
2106
+ };
2107
+ const ink = color ? semanticColors$3.has(color) ? `var(--ru-${color})` : color : void 0;
2108
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-calendar", className), style: ink ? { "--_cc": ink, ...style } : style, ...rest, children: [
2109
+ /* @__PURE__ */ jsxs("div", { className: "ru-calendar__head", children: [
2110
+ /* @__PURE__ */ jsx("button", { type: "button", className: "ru-calendar__nav", "aria-label": "Previous month", onClick: () => shift(-1), children: /* @__PURE__ */ jsx(ChevronLeftIcon, {}) }),
2111
+ /* @__PURE__ */ jsxs("div", { className: "ru-calendar__pickers", children: [
2112
+ /* @__PURE__ */ jsx(
2113
+ Select,
2114
+ {
2115
+ size: "sm",
2116
+ "aria-label": "Month",
2117
+ value: view.m,
2118
+ onChange: (m) => setView((v) => ({ ...v, m })),
2119
+ options: MONTHS.map((label, i) => ({ label, value: i })),
2120
+ containerClassName: "ru-calendar__picker"
2121
+ }
2122
+ ),
2123
+ /* @__PURE__ */ jsx(
2124
+ Select,
2125
+ {
2126
+ size: "sm",
2127
+ "aria-label": "Year",
2128
+ value: view.y,
2129
+ onChange: (y) => setView((v) => ({ ...v, y })),
2130
+ options: years.map((y) => ({ label: String(y), value: y })),
2131
+ containerClassName: "ru-calendar__picker ru-calendar__picker--year"
2132
+ }
2133
+ )
2134
+ ] }),
2135
+ /* @__PURE__ */ jsx("button", { type: "button", className: "ru-calendar__nav", "aria-label": "Next month", onClick: () => shift(1), children: /* @__PURE__ */ jsx(ChevronRightIcon, {}) })
2136
+ ] }),
2137
+ /* @__PURE__ */ jsx("div", { className: "ru-calendar__weekdays", "aria-hidden": "true", children: weekdays.map((w) => /* @__PURE__ */ jsx("span", { children: w }, w)) }),
2138
+ /* @__PURE__ */ jsx("div", { ref: gridRef, className: "ru-calendar__grid", onKeyDown: onGridKeyDown, children: days.map((d) => {
2139
+ const outside = d.getMonth() !== view.m;
2140
+ const selectedDay = isSelected(d);
2141
+ return /* @__PURE__ */ jsx(
2142
+ "button",
2143
+ {
2144
+ type: "button",
2145
+ className: cn(
2146
+ "ru-calendar__cell",
2147
+ outside && "ru-calendar__cell--outside",
2148
+ selectedDay && "ru-calendar__cell--selected",
2149
+ sameDay(d, today) && "ru-calendar__cell--today"
2150
+ ),
2151
+ "aria-label": d.toDateString(),
2152
+ "aria-pressed": selectedDay,
2153
+ "aria-current": sameDay(d, today) ? "date" : void 0,
2154
+ onClick: () => pick(d),
2155
+ children: /* @__PURE__ */ jsx("span", { className: "ru-calendar__day", children: d.getDate() })
2156
+ },
2157
+ dayKey(d)
2158
+ );
2159
+ }) })
2160
+ ] });
2161
+ }
2162
+ const normalize = (options) => options.map((o) => typeof o === "object" ? o : { label: String(o), value: o });
2163
+ function SegmentedControl({
2164
+ options,
2165
+ value,
2166
+ defaultValue,
2167
+ onChange,
2168
+ size = "md",
2169
+ fullWidth = false,
2170
+ pill = false,
2171
+ disabled = false,
2172
+ className,
2173
+ style,
2174
+ ...rest
2175
+ }) {
2176
+ var _a;
2177
+ const items = normalize(options);
2178
+ const firstEnabled = (_a = items.find((i) => !i.disabled)) == null ? void 0 : _a.value;
2179
+ const [active, setActive] = useControllableState(value, defaultValue ?? firstEnabled, onChange);
2180
+ const rootRef = useRef(null);
2181
+ const [thumb, setThumb] = useState(null);
2182
+ const activeIndex = items.findIndex((i) => i.value === active);
1399
2183
  useEffect(() => {
1400
- if (paused || !Number.isFinite(duration) || duration <= 0) return;
1401
- const timer = setTimeout(() => onDismiss(id), duration);
1402
- return () => clearTimeout(timer);
1403
- }, [id, duration, paused, onDismiss]);
1404
- const Icon = variant === "default" ? null : statusIcons[variant];
2184
+ const root = rootRef.current;
2185
+ if (!root) return void 0;
2186
+ const measure = () => {
2187
+ const el = root.querySelectorAll('[role="radio"]')[activeIndex];
2188
+ setThumb(el ? { x: el.offsetLeft, w: el.offsetWidth } : null);
2189
+ };
2190
+ measure();
2191
+ if (typeof ResizeObserver === "undefined") return void 0;
2192
+ const ro = new ResizeObserver(measure);
2193
+ ro.observe(root);
2194
+ return () => ro.disconnect();
2195
+ }, [activeIndex, items.length]);
2196
+ const move = (from, step) => {
2197
+ var _a2;
2198
+ const buttons = rootRef.current.querySelectorAll('[role="radio"]');
2199
+ for (let n = 1; n <= items.length; n++) {
2200
+ const i = (from + step * n + items.length) % items.length;
2201
+ if (!items[i].disabled) {
2202
+ setActive(items[i].value);
2203
+ (_a2 = buttons[i]) == null ? void 0 : _a2.focus();
2204
+ return;
2205
+ }
2206
+ }
2207
+ };
2208
+ const onKeyDown = (e, index) => {
2209
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") move(index, 1);
2210
+ else if (e.key === "ArrowLeft" || e.key === "ArrowUp") move(index, -1);
2211
+ else return;
2212
+ e.preventDefault();
2213
+ };
1405
2214
  return /* @__PURE__ */ jsxs(
1406
- "li",
2215
+ "div",
1407
2216
  {
1408
- role: "status",
1409
- className: cn("ru-toast", variant !== "default" && `ru-c-${variant}`),
1410
- onMouseEnter: () => setPaused(true),
1411
- onMouseLeave: () => setPaused(false),
2217
+ ref: rootRef,
2218
+ role: "radiogroup",
2219
+ "aria-disabled": disabled || void 0,
2220
+ className: cn(
2221
+ "ru-segmented",
2222
+ `ru-segmented--${size}`,
2223
+ fullWidth && "ru-segmented--full",
2224
+ pill && "ru-segmented--pill",
2225
+ thumb && "ru-segmented--ready",
2226
+ className
2227
+ ),
2228
+ style: thumb ? { "--_x": `${thumb.x}px`, "--_w": `${thumb.w}px`, ...style } : style,
2229
+ ...rest,
1412
2230
  children: [
1413
- Icon && /* @__PURE__ */ jsx("span", { className: "ru-toast__icon", children: /* @__PURE__ */ jsx(Icon, {}) }),
1414
- /* @__PURE__ */ jsxs("div", { className: "ru-toast__content", children: [
1415
- title && /* @__PURE__ */ jsx("div", { className: "ru-toast__title", children: title }),
1416
- description && /* @__PURE__ */ jsx("div", { className: "ru-toast__desc", children: description }),
1417
- action && /* @__PURE__ */ jsx("div", { className: "ru-toast__action", children: action })
2231
+ thumb && activeIndex >= 0 && /* @__PURE__ */ jsx("span", { className: "ru-segmented__thumb", "aria-hidden": "true" }),
2232
+ items.map((item, i) => {
2233
+ const selected = i === activeIndex;
2234
+ return /* @__PURE__ */ jsxs(
2235
+ "button",
2236
+ {
2237
+ type: "button",
2238
+ role: "radio",
2239
+ "aria-checked": selected,
2240
+ tabIndex: selected || activeIndex < 0 && i === 0 ? 0 : -1,
2241
+ disabled: disabled || item.disabled,
2242
+ className: "ru-segmented__item",
2243
+ onClick: () => setActive(item.value),
2244
+ onKeyDown: (e) => onKeyDown(e, i),
2245
+ children: [
2246
+ item.icon,
2247
+ item.label
2248
+ ]
2249
+ },
2250
+ String(item.value)
2251
+ );
2252
+ })
2253
+ ]
2254
+ }
2255
+ );
2256
+ }
2257
+ const semanticColors$2 = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
2258
+ function ProgressRing({
2259
+ value = 0,
2260
+ max = 100,
2261
+ size = 48,
2262
+ thickness = 4,
2263
+ color = "primary",
2264
+ trackColor,
2265
+ showValue = false,
2266
+ label,
2267
+ className,
2268
+ style,
2269
+ children,
2270
+ ...rest
2271
+ }) {
2272
+ const percent = Math.min(100, Math.max(0, value / max * 100));
2273
+ const r = (size - thickness) / 2;
2274
+ const circumference = 2 * Math.PI * r;
2275
+ const ink = semanticColors$2.has(color) ? `var(--ru-${color})` : color;
2276
+ return /* @__PURE__ */ jsxs(
2277
+ "div",
2278
+ {
2279
+ role: "progressbar",
2280
+ "aria-valuemin": 0,
2281
+ "aria-valuemax": max,
2282
+ "aria-valuenow": value,
2283
+ "aria-label": label,
2284
+ className: cn("ru-ring", className),
2285
+ style: { width: size, height: size, "--_c": ink, ...trackColor && { "--_track": trackColor }, ...style },
2286
+ ...rest,
2287
+ children: [
2288
+ /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, "aria-hidden": "true", children: [
2289
+ /* @__PURE__ */ jsx("circle", { className: "ru-ring__track", cx: size / 2, cy: size / 2, r, strokeWidth: thickness }),
2290
+ /* @__PURE__ */ jsx(
2291
+ "circle",
2292
+ {
2293
+ className: "ru-ring__bar",
2294
+ cx: size / 2,
2295
+ cy: size / 2,
2296
+ r,
2297
+ strokeWidth: thickness,
2298
+ strokeDasharray: circumference,
2299
+ strokeDashoffset: circumference * (1 - percent / 100)
2300
+ }
2301
+ )
1418
2302
  ] }),
1419
- /* @__PURE__ */ jsx(CloseButton, { onClick: () => onDismiss(id), label: "Dismiss notification" })
2303
+ (children !== void 0 || showValue) && /* @__PURE__ */ jsx("span", { className: "ru-ring__label", style: { fontSize: Math.max(10, Math.round(size * 0.24)) }, children: children ?? `${Math.round(percent)}%` })
1420
2304
  ]
1421
2305
  }
1422
2306
  );
1423
2307
  }
1424
- function useToast() {
1425
- const ctx = useContext(ToastContext);
1426
- if (!ctx) throw new Error("useToast must be used within a <ToastProvider>.");
1427
- return ctx;
2308
+ const semanticColors$1 = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
2309
+ function Stat({
2310
+ label,
2311
+ value,
2312
+ trend,
2313
+ trendLabel,
2314
+ invertTrend = false,
2315
+ helpText,
2316
+ size = "md",
2317
+ align = "start",
2318
+ className,
2319
+ ...rest
2320
+ }) {
2321
+ const hasTrend = typeof trend === "number";
2322
+ const up = hasTrend && trend >= 0;
2323
+ const good = up !== invertTrend;
2324
+ const TrendIcon = up ? TrendUpIcon : TrendDownIcon;
2325
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-stat", `ru-stat--${size}`, align === "end" && "ru-stat--end", className), ...rest, children: [
2326
+ /* @__PURE__ */ jsxs("div", { className: "ru-stat__top", children: [
2327
+ label && /* @__PURE__ */ jsx("span", { className: "ru-stat__label", children: label }),
2328
+ hasTrend && /* @__PURE__ */ jsxs("span", { className: cn("ru-stat__trend", good ? "ru-stat__trend--good" : "ru-stat__trend--bad"), children: [
2329
+ /* @__PURE__ */ jsx("span", { className: "ru-stat__trend-icon", children: /* @__PURE__ */ jsx(TrendIcon, {}) }),
2330
+ trendLabel ?? `${Math.abs(trend).toLocaleString()}%`,
2331
+ /* @__PURE__ */ jsx("span", { className: "ru-sr-only", children: up ? " increase" : " decrease" })
2332
+ ] })
2333
+ ] }),
2334
+ /* @__PURE__ */ jsx("div", { className: "ru-stat__value", children: value }),
2335
+ helpText && /* @__PURE__ */ jsx("div", { className: "ru-stat__help", children: helpText })
2336
+ ] });
1428
2337
  }
1429
- function Kbd({ className, ...rest }) {
1430
- return /* @__PURE__ */ jsx("kbd", { className: cn("ru-kbd", className), ...rest });
2338
+ function DateTile({ date, color = "primary", size = "md", className, style, ...rest }) {
2339
+ const d = date instanceof Date ? date : new Date(date);
2340
+ const ink = semanticColors$1.has(color) ? `var(--ru-${color})` : color;
2341
+ return /* @__PURE__ */ jsxs(
2342
+ "time",
2343
+ {
2344
+ dateTime: d.toISOString().slice(0, 10),
2345
+ "aria-label": d.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric", year: "numeric" }),
2346
+ className: cn("ru-date-tile", `ru-date-tile--${size}`, className),
2347
+ style: { "--_c": ink, ...style },
2348
+ ...rest,
2349
+ children: [
2350
+ /* @__PURE__ */ jsx("span", { className: "ru-date-tile__month", "aria-hidden": "true", children: d.toLocaleDateString("en-US", { month: "short" }) }),
2351
+ /* @__PURE__ */ jsx("span", { className: "ru-date-tile__day", "aria-hidden": "true", children: d.getDate() })
2352
+ ]
2353
+ }
2354
+ );
1431
2355
  }
1432
- function Pattern({ variant = "grid", size = 24, fade = true, className, style, ...rest }) {
1433
- return /* @__PURE__ */ jsx(
2356
+ function matchesAccept(file, accept) {
2357
+ if (!accept) return true;
2358
+ return accept.split(",").some((rule) => {
2359
+ const r = rule.trim().toLowerCase();
2360
+ if (!r) return false;
2361
+ if (r.startsWith(".")) return file.name.toLowerCase().endsWith(r);
2362
+ if (r.endsWith("/*")) return file.type.toLowerCase().startsWith(r.slice(0, -1));
2363
+ return file.type.toLowerCase() === r;
2364
+ });
2365
+ }
2366
+ function formatBytes(bytes) {
2367
+ if (typeof bytes !== "number") return bytes;
2368
+ if (bytes < 1024) return `${bytes} B`;
2369
+ const units = ["KB", "MB", "GB"];
2370
+ let n = bytes / 1024;
2371
+ let i = 0;
2372
+ while (n >= 1024 && i < units.length - 1) {
2373
+ n /= 1024;
2374
+ i++;
2375
+ }
2376
+ return `${Number(n.toFixed(1))}${units[i]}`;
2377
+ }
2378
+ function Dropzone({
2379
+ onFiles,
2380
+ onReject,
2381
+ accept,
2382
+ multiple = true,
2383
+ maxSize,
2384
+ disabled = false,
2385
+ icon,
2386
+ title = "Drag & drop files here",
2387
+ description,
2388
+ className,
2389
+ children,
2390
+ ...rest
2391
+ }) {
2392
+ const inputRef = useRef(null);
2393
+ const [dragging, setDragging] = useState(false);
2394
+ const handle = (list) => {
2395
+ const files = Array.from(list ?? []);
2396
+ const accepted = [];
2397
+ const rejected = [];
2398
+ for (const file of multiple ? files : files.slice(0, 1)) {
2399
+ if (!matchesAccept(file, accept)) rejected.push({ file, reason: "type" });
2400
+ else if (maxSize && file.size > maxSize) rejected.push({ file, reason: "size" });
2401
+ else accepted.push(file);
2402
+ }
2403
+ if (accepted.length) onFiles == null ? void 0 : onFiles(accepted);
2404
+ if (rejected.length) onReject == null ? void 0 : onReject(rejected);
2405
+ };
2406
+ const open = () => {
2407
+ var _a;
2408
+ return !disabled && ((_a = inputRef.current) == null ? void 0 : _a.click());
2409
+ };
2410
+ return /* @__PURE__ */ jsxs(
1434
2411
  "div",
1435
2412
  {
1436
- "aria-hidden": "true",
1437
- className: cn("ru-pattern", `ru-pattern--${variant}`, fade && "ru-pattern--fade", className),
1438
- style: { "--ru-pattern-size": `${size}px`, ...style },
1439
- ...rest
2413
+ role: "button",
2414
+ tabIndex: disabled ? -1 : 0,
2415
+ "aria-disabled": disabled || void 0,
2416
+ className: cn("ru-dropzone", dragging && "ru-dropzone--active", disabled && "ru-dropzone--disabled", className),
2417
+ onClick: open,
2418
+ onKeyDown: (e) => {
2419
+ if (e.key === "Enter" || e.key === " ") {
2420
+ e.preventDefault();
2421
+ open();
2422
+ }
2423
+ },
2424
+ onDragEnter: (e) => {
2425
+ e.preventDefault();
2426
+ if (!disabled) setDragging(true);
2427
+ },
2428
+ onDragOver: (e) => e.preventDefault(),
2429
+ onDragLeave: (e) => {
2430
+ if (!e.currentTarget.contains(e.relatedTarget)) setDragging(false);
2431
+ },
2432
+ onDrop: (e) => {
2433
+ e.preventDefault();
2434
+ setDragging(false);
2435
+ if (!disabled) handle(e.dataTransfer.files);
2436
+ },
2437
+ ...rest,
2438
+ children: [
2439
+ /* @__PURE__ */ jsx(
2440
+ "input",
2441
+ {
2442
+ ref: inputRef,
2443
+ type: "file",
2444
+ hidden: true,
2445
+ accept,
2446
+ multiple,
2447
+ disabled,
2448
+ onChange: (e) => {
2449
+ handle(e.target.files);
2450
+ e.target.value = "";
2451
+ }
2452
+ }
2453
+ ),
2454
+ children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
2455
+ /* @__PURE__ */ jsx("span", { className: "ru-dropzone__icon", children: icon ?? /* @__PURE__ */ jsx(UploadCloudIcon, {}) }),
2456
+ /* @__PURE__ */ jsx("span", { className: "ru-dropzone__title", children: title }),
2457
+ /* @__PURE__ */ jsx("span", { className: "ru-dropzone__desc", children: description ?? /* @__PURE__ */ jsxs(Fragment, { children: [
2458
+ "or ",
2459
+ /* @__PURE__ */ jsx("span", { className: "ru-dropzone__browse", children: "browse" }),
2460
+ " to choose files",
2461
+ maxSize ? ` (max ${formatBytes(maxSize)})` : ""
2462
+ ] }) })
2463
+ ] })
2464
+ ]
1440
2465
  }
1441
2466
  );
1442
2467
  }
1443
- function SpotlightCard({ color, className, style, onMouseMove, children, ...rest }) {
1444
- const ref = useRef(null);
1445
- const handleMouseMove = (e) => {
1446
- const el = ref.current;
1447
- if (el) {
1448
- const rect = el.getBoundingClientRect();
1449
- el.style.setProperty("--ru-spot-x", `${e.clientX - rect.left}px`);
1450
- el.style.setProperty("--ru-spot-y", `${e.clientY - rect.top}px`);
1451
- }
1452
- onMouseMove == null ? void 0 : onMouseMove(e);
2468
+ const statusText = { uploading: "Uploading…", complete: "Completed", error: "Failed" };
2469
+ function FileItem({
2470
+ name,
2471
+ size,
2472
+ status = "complete",
2473
+ progress = 0,
2474
+ icon,
2475
+ error,
2476
+ onRemove,
2477
+ action,
2478
+ removeLabel,
2479
+ className,
2480
+ ...rest
2481
+ }) {
2482
+ const uploading = status === "uploading";
2483
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-file-item", `ru-file-item--${status}`, className), ...rest, children: [
2484
+ /* @__PURE__ */ jsx("span", { className: "ru-file-item__icon", children: icon ?? /* @__PURE__ */ jsx(FileIcon, {}) }),
2485
+ /* @__PURE__ */ jsxs("div", { className: "ru-file-item__body", children: [
2486
+ /* @__PURE__ */ jsx("div", { className: "ru-file-item__name", title: name, children: name }),
2487
+ /* @__PURE__ */ jsxs("div", { className: "ru-file-item__meta", children: [
2488
+ size !== void 0 && /* @__PURE__ */ jsx("span", { children: formatBytes(size) }),
2489
+ size !== void 0 && /* @__PURE__ */ jsx("span", { className: "ru-file-item__dot", "aria-hidden": "true" }),
2490
+ /* @__PURE__ */ jsx("span", { className: "ru-file-item__status", children: status === "error" && error ? error : statusText[status] })
2491
+ ] }),
2492
+ uploading && /* @__PURE__ */ jsx(
2493
+ "div",
2494
+ {
2495
+ className: "ru-file-item__bar",
2496
+ role: "progressbar",
2497
+ "aria-valuemin": 0,
2498
+ "aria-valuemax": 100,
2499
+ "aria-valuenow": progress,
2500
+ "aria-label": `Uploading ${name}`,
2501
+ children: /* @__PURE__ */ jsx("span", { style: { width: `${progress}%` } })
2502
+ }
2503
+ )
2504
+ ] }),
2505
+ uploading && /* @__PURE__ */ jsxs("span", { className: "ru-file-item__percent", children: [
2506
+ Math.round(progress),
2507
+ "%"
2508
+ ] }),
2509
+ action,
2510
+ onRemove && /* @__PURE__ */ jsx("button", { type: "button", className: "ru-file-item__remove", "aria-label": removeLabel ?? `Remove ${name}`, onClick: onRemove, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
2511
+ ] });
2512
+ }
2513
+ const UndoIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2514
+ /* @__PURE__ */ jsx("path", { d: "M9 14 4 9l5-5" }),
2515
+ /* @__PURE__ */ jsx("path", { d: "M4 9h10.5a5.5 5.5 0 0 1 0 11H11" })
2516
+ ] });
2517
+ const EraserIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2518
+ /* @__PURE__ */ jsx("path", { d: "m7 21-4.3-4.3a1 1 0 0 1 0-1.4l10-10a1 1 0 0 1 1.4 0l5.6 5.6a1 1 0 0 1 0 1.4L11 21" }),
2519
+ /* @__PURE__ */ jsx("path", { d: "M22 21H7M5 11l9 9" })
2520
+ ] });
2521
+ const SignaturePad = forwardRef(function SignaturePad2({
2522
+ onChange,
2523
+ strokeColor,
2524
+ strokeWidth = 2.5,
2525
+ height = 180,
2526
+ placeholder = "Sign here",
2527
+ showControls = true,
2528
+ undoLabel = "Undo",
2529
+ clearLabel = "Clear",
2530
+ actions,
2531
+ disabled = false,
2532
+ className,
2533
+ style,
2534
+ ...rest
2535
+ }, ref) {
2536
+ const canvasRef = useRef(null);
2537
+ const strokes = useRef([]);
2538
+ const drawing = useRef(null);
2539
+ const [count, setCount] = useState(0);
2540
+ const ink = useCallback(() => strokeColor ?? getComputedStyle(canvasRef.current).color, [strokeColor]);
2541
+ const drawStroke = useCallback(
2542
+ (ctx, points) => {
2543
+ ctx.strokeStyle = ink();
2544
+ ctx.fillStyle = ctx.strokeStyle;
2545
+ if (points.length === 1) {
2546
+ ctx.beginPath();
2547
+ ctx.arc(points[0].x, points[0].y, strokeWidth / 2, 0, Math.PI * 2);
2548
+ ctx.fill();
2549
+ return;
2550
+ }
2551
+ for (let i = 1; i < points.length; i++) {
2552
+ const a = points[i - 1];
2553
+ const b = points[i];
2554
+ const mid = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
2555
+ ctx.lineWidth = b.w;
2556
+ ctx.beginPath();
2557
+ ctx.moveTo(a.mx ?? a.x, a.my ?? a.y);
2558
+ ctx.quadraticCurveTo(a.x, a.y, mid.x, mid.y);
2559
+ ctx.stroke();
2560
+ b.mx = mid.x;
2561
+ b.my = mid.y;
2562
+ }
2563
+ },
2564
+ [ink, strokeWidth]
2565
+ );
2566
+ const redraw = useCallback(() => {
2567
+ const canvas = canvasRef.current;
2568
+ if (!canvas) return;
2569
+ const ctx = canvas.getContext("2d");
2570
+ if (!ctx) return;
2571
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
2572
+ ctx.lineCap = "round";
2573
+ ctx.lineJoin = "round";
2574
+ strokes.current.forEach((s) => drawStroke(ctx, s.map(({ x, y, w }) => ({ x, y, w }))));
2575
+ }, [drawStroke]);
2576
+ useEffect(() => {
2577
+ const canvas = canvasRef.current;
2578
+ if (!canvas) return void 0;
2579
+ const fit = () => {
2580
+ var _a;
2581
+ const dpr = window.devicePixelRatio || 1;
2582
+ const { width, height: h } = canvas.getBoundingClientRect();
2583
+ canvas.width = Math.max(1, Math.round(width * dpr));
2584
+ canvas.height = Math.max(1, Math.round(h * dpr));
2585
+ (_a = canvas.getContext("2d")) == null ? void 0 : _a.setTransform(dpr, 0, 0, dpr, 0, 0);
2586
+ redraw();
2587
+ };
2588
+ fit();
2589
+ if (typeof ResizeObserver === "undefined") return void 0;
2590
+ const ro = new ResizeObserver(fit);
2591
+ ro.observe(canvas);
2592
+ return () => ro.disconnect();
2593
+ }, [redraw]);
2594
+ const emit = () => onChange == null ? void 0 : onChange(strokes.current.length ? canvasRef.current.toDataURL("image/png") : null);
2595
+ const point = (e) => {
2596
+ const rect = canvasRef.current.getBoundingClientRect();
2597
+ return { x: e.clientX - rect.left, y: e.clientY - rect.top, t: e.timeStamp };
2598
+ };
2599
+ const onPointerDown = (e) => {
2600
+ if (disabled || e.pointerType === "mouse" && e.button !== 0) return;
2601
+ e.currentTarget.setPointerCapture(e.pointerId);
2602
+ const p = { ...point(e), w: strokeWidth };
2603
+ drawing.current = [p];
2604
+ const ctx = canvasRef.current.getContext("2d");
2605
+ ctx.lineCap = "round";
2606
+ ctx.lineJoin = "round";
2607
+ drawStroke(ctx, [p]);
1453
2608
  };
2609
+ const onPointerMove = (e) => {
2610
+ const stroke = drawing.current;
2611
+ if (!stroke) return;
2612
+ const prev = stroke[stroke.length - 1];
2613
+ const p = point(e);
2614
+ const dist = Math.hypot(p.x - prev.x, p.y - prev.y);
2615
+ if (dist < 1.5) return;
2616
+ const speed = dist / Math.max(1, p.t - prev.t);
2617
+ const target = strokeWidth * Math.max(0.45, Math.min(1.35, 1.4 - speed * 0.45));
2618
+ p.w = prev.w * 0.6 + target * 0.4;
2619
+ stroke.push(p);
2620
+ drawStroke(canvasRef.current.getContext("2d"), stroke.slice(-3));
2621
+ };
2622
+ const onPointerUp = () => {
2623
+ if (!drawing.current) return;
2624
+ strokes.current = [...strokes.current, drawing.current];
2625
+ drawing.current = null;
2626
+ setCount(strokes.current.length);
2627
+ emit();
2628
+ };
2629
+ const undo = () => {
2630
+ strokes.current = strokes.current.slice(0, -1);
2631
+ setCount(strokes.current.length);
2632
+ redraw();
2633
+ emit();
2634
+ };
2635
+ const clear = () => {
2636
+ strokes.current = [];
2637
+ setCount(0);
2638
+ redraw();
2639
+ emit();
2640
+ };
2641
+ useImperativeHandle(ref, () => ({
2642
+ clear,
2643
+ undo,
2644
+ isEmpty: () => strokes.current.length === 0,
2645
+ toDataURL: (type = "image/png") => canvasRef.current.toDataURL(type)
2646
+ }));
2647
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-signature", disabled && "ru-signature--disabled", className), style, ...rest, children: [
2648
+ /* @__PURE__ */ jsxs("div", { className: "ru-signature__canvas-wrap", style: { height }, children: [
2649
+ count === 0 && placeholder && /* @__PURE__ */ jsx("span", { className: "ru-signature__placeholder", "aria-hidden": "true", children: placeholder }),
2650
+ /* @__PURE__ */ jsx(
2651
+ "canvas",
2652
+ {
2653
+ ref: canvasRef,
2654
+ className: "ru-signature__canvas",
2655
+ role: "img",
2656
+ "aria-label": count ? "Signature drawn" : "Signature pad, empty",
2657
+ onPointerDown,
2658
+ onPointerMove,
2659
+ onPointerUp,
2660
+ onPointerCancel: onPointerUp
2661
+ }
2662
+ ),
2663
+ /* @__PURE__ */ jsx("span", { className: "ru-signature__baseline", "aria-hidden": "true" })
2664
+ ] }),
2665
+ (showControls || actions) && /* @__PURE__ */ jsxs("div", { className: "ru-signature__bar", children: [
2666
+ showControls && /* @__PURE__ */ jsxs(Fragment, { children: [
2667
+ /* @__PURE__ */ jsxs("button", { type: "button", className: "ru-signature__btn", onClick: undo, disabled: disabled || count === 0, children: [
2668
+ /* @__PURE__ */ jsx(UndoIcon, {}),
2669
+ undoLabel
2670
+ ] }),
2671
+ /* @__PURE__ */ jsx("span", { className: "ru-signature__sep", "aria-hidden": "true" }),
2672
+ /* @__PURE__ */ jsxs("button", { type: "button", className: "ru-signature__btn", onClick: clear, disabled: disabled || count === 0, children: [
2673
+ /* @__PURE__ */ jsx(EraserIcon, {}),
2674
+ clearLabel
2675
+ ] })
2676
+ ] }),
2677
+ actions && /* @__PURE__ */ jsx("div", { className: "ru-signature__actions", children: actions })
2678
+ ] })
2679
+ ] });
2680
+ });
2681
+ const statusLabels = {
2682
+ running: "In progress",
2683
+ queued: "Queued",
2684
+ pending: "Not started",
2685
+ partial: "Partly done",
2686
+ complete: "Complete",
2687
+ error: "Failed"
2688
+ };
2689
+ function StatusIcon({ status = "pending", progress = 50, size = 20, label, className, style, ...rest }) {
2690
+ const r = 8;
2691
+ const c = 2 * Math.PI * r;
1454
2692
  return /* @__PURE__ */ jsx(
1455
- "div",
2693
+ "span",
1456
2694
  {
1457
- ref,
1458
- className: cn("ru-card", "ru-spotlight", className),
1459
- style: color ? { "--ru-spot-color": color, ...style } : style,
1460
- onMouseMove: handleMouseMove,
2695
+ role: "img",
2696
+ "aria-label": label ?? statusLabels[status] ?? status,
2697
+ className: cn("ru-status-icon", `ru-status-icon--${status}`, className),
2698
+ style: { width: size, height: size, ...style },
1461
2699
  ...rest,
1462
- children
2700
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", width: size, height: size, "aria-hidden": "true", children: status === "complete" || status === "error" ? /* @__PURE__ */ jsxs(Fragment, { children: [
2701
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "10", className: "ru-status-icon__fill" }),
2702
+ status === "complete" ? /* @__PURE__ */ jsx("path", { d: "m6 10.2 2.6 2.6L14 7.4", className: "ru-status-icon__glyph" }) : /* @__PURE__ */ jsx("path", { d: "m7 7 6 6M13 7l-6 6", className: "ru-status-icon__glyph" })
2703
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2704
+ /* @__PURE__ */ jsx(
2705
+ "circle",
2706
+ {
2707
+ cx: "10",
2708
+ cy: "10",
2709
+ r,
2710
+ className: "ru-status-icon__ring",
2711
+ strokeDasharray: status === "running" || status === "queued" ? "2.6 2.4" : void 0
2712
+ }
2713
+ ),
2714
+ status === "partial" && /* @__PURE__ */ jsx(
2715
+ "circle",
2716
+ {
2717
+ cx: "10",
2718
+ cy: "10",
2719
+ r,
2720
+ className: "ru-status-icon__arc",
2721
+ strokeDasharray: c,
2722
+ strokeDashoffset: c * (1 - Math.min(100, Math.max(0, progress)) / 100)
2723
+ }
2724
+ )
2725
+ ] }) })
2726
+ }
2727
+ );
2728
+ }
2729
+ function StepList({ items, connector = true, dividers = true, className, ...rest }) {
2730
+ return /* @__PURE__ */ jsx("ol", { className: cn("ru-step-list", !dividers && "ru-step-list--plain", className), ...rest, children: items.map((item, i) => /* @__PURE__ */ jsxs(
2731
+ "li",
2732
+ {
2733
+ className: cn("ru-step-list__item", `ru-step-list__item--${item.status ?? "pending"}`),
2734
+ "aria-current": item.status === "running" ? "step" : void 0,
2735
+ children: [
2736
+ /* @__PURE__ */ jsxs("span", { className: "ru-step-list__rail", children: [
2737
+ /* @__PURE__ */ jsx("span", { className: "ru-step-list__icon", children: /* @__PURE__ */ jsx(StatusIcon, { status: item.status, progress: item.progress, size: 22 }) }),
2738
+ connector && i < items.length - 1 && /* @__PURE__ */ jsx("span", { className: "ru-step-list__line", "aria-hidden": "true" })
2739
+ ] }),
2740
+ /* @__PURE__ */ jsxs("div", { className: "ru-step-list__body", children: [
2741
+ /* @__PURE__ */ jsx("div", { className: "ru-step-list__title", children: item.title }),
2742
+ item.description && /* @__PURE__ */ jsx("div", { className: "ru-step-list__desc", children: item.description })
2743
+ ] }),
2744
+ item.meta && /* @__PURE__ */ jsx("div", { className: "ru-step-list__meta", children: item.meta })
2745
+ ]
2746
+ },
2747
+ item.key ?? i
2748
+ )) });
2749
+ }
2750
+ function StackedProgress({ segments, max, size = "md", showLegend = true, gap = 4, label, className, ...rest }) {
2751
+ const total = max ?? segments.reduce((sum, s) => sum + s.value, 0);
2752
+ const summary = segments.map((s) => `${s.label} ${Math.round(s.value / total * 100)}%`).join(", ");
2753
+ return /* @__PURE__ */ jsxs("div", { className: cn("ru-stacked", `ru-stacked--${size}`, className), ...rest, children: [
2754
+ (label || showLegend) && /* @__PURE__ */ jsxs("div", { className: "ru-stacked__head", children: [
2755
+ label && /* @__PURE__ */ jsx("span", { className: "ru-stacked__label", children: label }),
2756
+ showLegend && /* @__PURE__ */ jsx("ul", { className: "ru-stacked__legend", children: segments.map((s) => /* @__PURE__ */ jsxs("li", { children: [
2757
+ /* @__PURE__ */ jsx("span", { className: "ru-stacked__dot", style: { background: s.color } }),
2758
+ s.label
2759
+ ] }, s.label)) })
2760
+ ] }),
2761
+ /* @__PURE__ */ jsxs("div", { className: "ru-stacked__bar", role: "img", "aria-label": summary, style: { gap }, children: [
2762
+ segments.map((s) => /* @__PURE__ */ jsx("span", { className: "ru-stacked__seg", title: `${s.label}: ${s.value}`, style: { flexGrow: s.value, background: s.color } }, s.label)),
2763
+ max !== void 0 && total > segments.reduce((sum, s) => sum + s.value, 0) && /* @__PURE__ */ jsx("span", { className: "ru-stacked__rest", style: { flexGrow: total - segments.reduce((sum, s) => sum + s.value, 0) } })
2764
+ ] })
2765
+ ] });
2766
+ }
2767
+ const semanticColors = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
2768
+ function IconTile({
2769
+ color = "primary",
2770
+ size = 48,
2771
+ radius,
2772
+ badge,
2773
+ glow = true,
2774
+ label,
2775
+ className,
2776
+ style,
2777
+ children,
2778
+ ...rest
2779
+ }) {
2780
+ const ink = semanticColors.has(color) ? `var(--ru-${color})` : color;
2781
+ return /* @__PURE__ */ jsxs(
2782
+ "span",
2783
+ {
2784
+ className: cn("ru-icon-tile", glow && "ru-icon-tile--glow", className),
2785
+ style: { "--_c": ink, "--_s": `${size}px`, ...radius !== void 0 && { "--_r": typeof radius === "number" ? `${radius}px` : radius }, ...style },
2786
+ ...rest,
2787
+ children: [
2788
+ /* @__PURE__ */ jsx("span", { className: "ru-icon-tile__face", role: label ? "img" : void 0, "aria-label": label, "aria-hidden": label ? void 0 : true, children }),
2789
+ badge && /* @__PURE__ */ jsx("span", { className: "ru-icon-tile__badge", "aria-hidden": "true", children: badge })
2790
+ ]
1463
2791
  }
1464
2792
  );
1465
2793
  }
@@ -1471,14 +2799,22 @@ export {
1471
2799
  Badge,
1472
2800
  Breadcrumbs,
1473
2801
  Button,
2802
+ ButtonGroup,
2803
+ Calendar,
1474
2804
  Card,
1475
2805
  CardBody,
1476
2806
  CardFooter,
1477
2807
  CardHeader,
1478
2808
  Checkbox,
2809
+ DateTile,
1479
2810
  Divider,
2811
+ DotText,
1480
2812
  Drawer,
1481
2813
  Dropdown,
2814
+ Dropzone,
2815
+ FileItem,
2816
+ FrameCard,
2817
+ IconTile,
1482
2818
  Input,
1483
2819
  Kbd,
1484
2820
  Modal,
@@ -1486,12 +2822,19 @@ export {
1486
2822
  Pagination,
1487
2823
  Pattern,
1488
2824
  Progress,
2825
+ ProgressRing,
1489
2826
  Radio,
1490
2827
  RadioGroup,
2828
+ SegmentedControl,
1491
2829
  Select,
2830
+ SignaturePad,
1492
2831
  Skeleton,
1493
2832
  Spinner,
1494
2833
  SpotlightCard,
2834
+ StackedProgress,
2835
+ Stat,
2836
+ StatusIcon,
2837
+ StepList,
1495
2838
  Switch,
1496
2839
  Table,
1497
2840
  Tabs,
@@ -1499,6 +2842,7 @@ export {
1499
2842
  ToastProvider,
1500
2843
  Tooltip,
1501
2844
  cn,
2845
+ formatBytes,
1502
2846
  useClickOutside,
1503
2847
  useControllableState,
1504
2848
  useDisclosure,