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