reusify-ui 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -6
- package/dist/reusify-ui.cjs +963 -30
- package/dist/reusify-ui.js +964 -31
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/reusify-ui.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { forwardRef, useRef, useEffect,
|
|
3
|
+
import { forwardRef, useState, useRef, useEffect, useLayoutEffect, useCallback, useImperativeHandle, useId, useMemo, Children, isValidElement, Fragment as Fragment$1, cloneElement, useContext, createContext } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
5
|
function cn(...classes) {
|
|
6
6
|
return classes.filter(Boolean).join(" ");
|
|
@@ -10,7 +10,7 @@ const legacyVariants = {
|
|
|
10
10
|
danger: ["solid", "danger"],
|
|
11
11
|
success: ["solid", "success"]
|
|
12
12
|
};
|
|
13
|
-
const semanticColors$
|
|
13
|
+
const semanticColors$5 = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
|
|
14
14
|
const radii = { none: "0", sm: "6px", md: "10px", lg: "14px", xl: "18px", "2xl": "24px", full: "9999px" };
|
|
15
15
|
function resolveRadius(radius) {
|
|
16
16
|
if (radius === void 0) return void 0;
|
|
@@ -48,7 +48,7 @@ const Button = forwardRef(function Button2({
|
|
|
48
48
|
onPointerMove == null ? void 0 : onPointerMove(e);
|
|
49
49
|
} : onPointerMove;
|
|
50
50
|
const resolvedColor = color ?? legacyColor ?? "primary";
|
|
51
|
-
const customColor = !semanticColors$
|
|
51
|
+
const customColor = !semanticColors$5.has(resolvedColor);
|
|
52
52
|
const classes = cn(
|
|
53
53
|
"ru-btn",
|
|
54
54
|
`ru-btn--${kind}`,
|
|
@@ -143,6 +143,16 @@ const UserIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
|
|
|
143
143
|
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "4" }),
|
|
144
144
|
/* @__PURE__ */ jsx("path", { d: "M4 21a8 8 0 0 1 16 0" })
|
|
145
145
|
] });
|
|
146
|
+
const UploadCloudIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
|
|
147
|
+
/* @__PURE__ */ jsx("path", { d: "M4 14.9A7 7 0 1 1 15.7 8h1.8a4.5 4.5 0 0 1 2.5 8.2" }),
|
|
148
|
+
/* @__PURE__ */ jsx("path", { d: "M12 12v9M16 16l-4-4-4 4" })
|
|
149
|
+
] });
|
|
150
|
+
const FileIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
|
|
151
|
+
/* @__PURE__ */ jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
152
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
153
|
+
] });
|
|
154
|
+
const TrendUpIcon = (p) => /* @__PURE__ */ jsx("svg", { ...base, ...p, children: /* @__PURE__ */ jsx("path", { d: "M12 19V5M5 12l7-7 7 7" }) });
|
|
155
|
+
const TrendDownIcon = (p) => /* @__PURE__ */ jsx("svg", { ...base, ...p, children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M19 12l-7 7-7-7" }) });
|
|
146
156
|
const statusIcons = {
|
|
147
157
|
info: InfoIcon,
|
|
148
158
|
success: SuccessIcon,
|
|
@@ -158,11 +168,17 @@ function Spinner({ size = "md", color = "primary", label = "Loading", className,
|
|
|
158
168
|
label && /* @__PURE__ */ jsx("span", { className: "ru-sr-only", children: label })
|
|
159
169
|
] });
|
|
160
170
|
}
|
|
171
|
+
const progressColors = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
|
|
161
172
|
function Progress({
|
|
162
173
|
value,
|
|
163
174
|
max = 100,
|
|
164
175
|
size = "md",
|
|
165
176
|
color = "primary",
|
|
177
|
+
variant = "bar",
|
|
178
|
+
ticks = 24,
|
|
179
|
+
glow = false,
|
|
180
|
+
icon,
|
|
181
|
+
endLabel,
|
|
166
182
|
label,
|
|
167
183
|
showValue = false,
|
|
168
184
|
className,
|
|
@@ -170,7 +186,86 @@ function Progress({
|
|
|
170
186
|
}) {
|
|
171
187
|
const indeterminate = value === void 0;
|
|
172
188
|
const percent = indeterminate ? 0 : Math.min(100, Math.max(0, value / max * 100));
|
|
173
|
-
|
|
189
|
+
const semantic = progressColors.has(color);
|
|
190
|
+
const glowColor = glow === true ? semantic ? `var(--ru-${color})` : color : glow || void 0;
|
|
191
|
+
const meterStyle = { ...!semantic && { "--_c": color }, ...glowColor && { "--_glow": glowColor } };
|
|
192
|
+
if (variant === "capsule") {
|
|
193
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("ru-progress", "ru-progress--capsule", `ru-progress--${size}`, glowColor && "ru-progress--glow", className), ...rest, children: [
|
|
194
|
+
label && /* @__PURE__ */ jsx("div", { className: "ru-progress__label", children: /* @__PURE__ */ jsx("span", { children: label }) }),
|
|
195
|
+
/* @__PURE__ */ jsxs(
|
|
196
|
+
"div",
|
|
197
|
+
{
|
|
198
|
+
role: "progressbar",
|
|
199
|
+
"aria-valuemin": 0,
|
|
200
|
+
"aria-valuemax": max,
|
|
201
|
+
"aria-valuenow": value ?? 0,
|
|
202
|
+
"aria-label": typeof label === "string" ? label : void 0,
|
|
203
|
+
className: cn("ru-progress__capsule", semantic && `ru-c-${color}`),
|
|
204
|
+
style: meterStyle,
|
|
205
|
+
children: [
|
|
206
|
+
/* @__PURE__ */ jsx("div", { className: "ru-progress__capsule-fill", style: { width: `max(var(--_h), ${percent}%)` }, children: icon ?? (showValue && /* @__PURE__ */ jsxs("span", { className: "ru-progress__capsule-value", children: [
|
|
207
|
+
Math.round(percent),
|
|
208
|
+
"%"
|
|
209
|
+
] })) }),
|
|
210
|
+
endLabel && /* @__PURE__ */ jsx("span", { className: "ru-progress__capsule-end", children: endLabel })
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
] });
|
|
215
|
+
}
|
|
216
|
+
if (variant === "bars") {
|
|
217
|
+
const total = Math.max(2, ticks);
|
|
218
|
+
const lit = Math.round(percent / 100 * total);
|
|
219
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("ru-progress", "ru-progress--bars", `ru-progress--${size}`, glowColor && "ru-progress--glow", className), ...rest, children: [
|
|
220
|
+
(label || showValue) && /* @__PURE__ */ jsxs("div", { className: "ru-progress__label", children: [
|
|
221
|
+
/* @__PURE__ */ jsx("span", { children: label }),
|
|
222
|
+
showValue && /* @__PURE__ */ jsxs("span", { className: "ru-progress__value ru-progress__value--accent", children: [
|
|
223
|
+
Math.round(percent),
|
|
224
|
+
"%"
|
|
225
|
+
] })
|
|
226
|
+
] }),
|
|
227
|
+
/* @__PURE__ */ jsx(
|
|
228
|
+
"div",
|
|
229
|
+
{
|
|
230
|
+
role: "progressbar",
|
|
231
|
+
"aria-valuemin": 0,
|
|
232
|
+
"aria-valuemax": max,
|
|
233
|
+
"aria-valuenow": value ?? 0,
|
|
234
|
+
"aria-label": typeof label === "string" ? label : void 0,
|
|
235
|
+
className: cn("ru-progress__bars", semantic && `ru-c-${color}`),
|
|
236
|
+
style: meterStyle,
|
|
237
|
+
children: Array.from({ length: total }, (_, i) => /* @__PURE__ */ jsx("span", { className: cn(i < lit && "is-on", i === lit - 1 && percent > 0 && percent < 100 && "is-marker") }, i))
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
] });
|
|
241
|
+
}
|
|
242
|
+
if (variant === "ticks") {
|
|
243
|
+
const remaining = percent >= 100 ? 0 : Math.max(1, Math.round(ticks * (1 - percent / 100)));
|
|
244
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("ru-progress", "ru-progress--ticks", `ru-progress--${size}`, glowColor && "ru-progress--glow", className), ...rest, children: [
|
|
245
|
+
label && /* @__PURE__ */ jsx("div", { className: "ru-progress__label", children: /* @__PURE__ */ jsx("span", { children: label }) }),
|
|
246
|
+
/* @__PURE__ */ jsxs(
|
|
247
|
+
"div",
|
|
248
|
+
{
|
|
249
|
+
role: "progressbar",
|
|
250
|
+
"aria-valuemin": 0,
|
|
251
|
+
"aria-valuemax": max,
|
|
252
|
+
"aria-valuenow": value ?? 0,
|
|
253
|
+
"aria-label": typeof label === "string" ? label : void 0,
|
|
254
|
+
className: cn("ru-progress__meter", semantic && `ru-c-${color}`),
|
|
255
|
+
style: meterStyle,
|
|
256
|
+
children: [
|
|
257
|
+
percent > 0 && /* @__PURE__ */ jsx("div", { className: "ru-progress__fill", style: { width: `${percent}%` }, children: showValue && /* @__PURE__ */ jsxs("span", { className: "ru-progress__fill-value", children: [
|
|
258
|
+
Math.round(percent),
|
|
259
|
+
"%"
|
|
260
|
+
] }) }),
|
|
261
|
+
percent > 0 && percent < 100 && /* @__PURE__ */ jsx("span", { className: "ru-progress__marker", "aria-hidden": "true" }),
|
|
262
|
+
remaining > 0 && /* @__PURE__ */ jsx("div", { className: "ru-progress__ticks", "aria-hidden": "true", children: Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx("span", {}, i)) })
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
)
|
|
266
|
+
] });
|
|
267
|
+
}
|
|
268
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("ru-progress", indeterminate && "ru-progress--indeterminate", glowColor && "ru-progress--glow", className), ...rest, children: [
|
|
174
269
|
(label || showValue) && /* @__PURE__ */ jsxs("div", { className: "ru-progress__label", children: [
|
|
175
270
|
/* @__PURE__ */ jsx("span", { children: label }),
|
|
176
271
|
showValue && !indeterminate && /* @__PURE__ */ jsxs("span", { className: "ru-progress__value", children: [
|
|
@@ -186,7 +281,8 @@ function Progress({
|
|
|
186
281
|
"aria-valuemax": max,
|
|
187
282
|
"aria-valuenow": indeterminate ? void 0 : value,
|
|
188
283
|
"aria-label": typeof label === "string" ? label : void 0,
|
|
189
|
-
className: cn("ru-progress__track", `ru-progress__track--${size}`, `ru-c-${color}`),
|
|
284
|
+
className: cn("ru-progress__track", `ru-progress__track--${size}`, semantic && `ru-c-${color}`),
|
|
285
|
+
style: meterStyle,
|
|
190
286
|
children: /* @__PURE__ */ jsx("div", { className: "ru-progress__bar", style: indeterminate ? void 0 : { width: `${percent}%` } })
|
|
191
287
|
}
|
|
192
288
|
)
|
|
@@ -225,6 +321,98 @@ function Alert({ variant = "info", title, icon, onClose, className, children, ..
|
|
|
225
321
|
}
|
|
226
322
|
);
|
|
227
323
|
}
|
|
324
|
+
const useBrowserLayoutEffect = typeof document === "undefined" ? useEffect : useLayoutEffect;
|
|
325
|
+
const TOOLTIP_EDGE = 8;
|
|
326
|
+
const TOOLTIP_COMFORT = 16;
|
|
327
|
+
const ARROW_INSET = 14;
|
|
328
|
+
const oppositeSide = { top: "bottom", bottom: "top", left: "right", right: "left" };
|
|
329
|
+
const AUTO_ORDER = ["top", "bottom", "right", "left"];
|
|
330
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
331
|
+
function visibleArea(el) {
|
|
332
|
+
const area = {
|
|
333
|
+
top: TOOLTIP_EDGE,
|
|
334
|
+
left: TOOLTIP_EDGE,
|
|
335
|
+
right: document.documentElement.clientWidth - TOOLTIP_EDGE,
|
|
336
|
+
bottom: window.innerHeight - TOOLTIP_EDGE
|
|
337
|
+
};
|
|
338
|
+
for (let node = el; node && node !== document.body; node = node.parentElement) {
|
|
339
|
+
const { overflowX, overflowY, position } = getComputedStyle(node);
|
|
340
|
+
if (node !== el) {
|
|
341
|
+
const rect = node.getBoundingClientRect();
|
|
342
|
+
if (overflowX !== "visible") {
|
|
343
|
+
area.left = Math.max(area.left, rect.left + TOOLTIP_EDGE);
|
|
344
|
+
area.right = Math.min(area.right, rect.right - TOOLTIP_EDGE);
|
|
345
|
+
}
|
|
346
|
+
if (overflowY !== "visible") {
|
|
347
|
+
area.top = Math.max(area.top, rect.top + TOOLTIP_EDGE);
|
|
348
|
+
area.bottom = Math.min(area.bottom, rect.bottom - TOOLTIP_EDGE);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (position === "fixed") break;
|
|
352
|
+
}
|
|
353
|
+
return area;
|
|
354
|
+
}
|
|
355
|
+
function slideAlong(start, size, low, high, triggerLow, triggerHigh) {
|
|
356
|
+
const nearLow = start < low + TOOLTIP_COMFORT;
|
|
357
|
+
const nearHigh = start + size > high - TOOLTIP_COMFORT;
|
|
358
|
+
if (!nearLow && !nearHigh) return 0;
|
|
359
|
+
const clamped = clamp(start, low, high - size);
|
|
360
|
+
if (nearLow && nearHigh) return clamped - start;
|
|
361
|
+
const snapped = nearHigh ? triggerHigh - size : triggerLow;
|
|
362
|
+
const canSnap = snapped >= low - TOOLTIP_EDGE && snapped + size <= high + TOOLTIP_EDGE;
|
|
363
|
+
return (canSnap ? snapped : clamped) - start;
|
|
364
|
+
}
|
|
365
|
+
const samePosition = (a, b) => a && Object.keys(b).every((key) => a[key] === b[key]);
|
|
366
|
+
function placeTooltip(trigger, bubble, placement) {
|
|
367
|
+
const t = trigger.getBoundingClientRect();
|
|
368
|
+
const w = bubble.offsetWidth;
|
|
369
|
+
const h = bubble.offsetHeight;
|
|
370
|
+
const gap = parseFloat(getComputedStyle(bubble).getPropertyValue("--_gap")) || 10;
|
|
371
|
+
const area = visibleArea(trigger);
|
|
372
|
+
const room = { top: t.top - area.top, bottom: area.bottom - t.bottom, left: t.left - area.left, right: area.right - t.right };
|
|
373
|
+
const needs = (side2) => (side2 === "top" || side2 === "bottom" ? h : w) + gap;
|
|
374
|
+
const fits = (s) => room[s] >= needs(s);
|
|
375
|
+
const aligned = (s) => s === "top" || s === "bottom" ? slideAlong(t.left + t.width / 2 - w / 2, w, area.left, area.right, t.left, t.right) === 0 : slideAlong(t.top + t.height / 2 - h / 2, h, area.top, area.bottom, t.top, t.bottom) === 0;
|
|
376
|
+
const roomiest = AUTO_ORDER.reduce((best, s) => room[s] > room[best] ? s : best);
|
|
377
|
+
let side = placement === "auto" ? AUTO_ORDER.find((s) => fits(s) && aligned(s)) ?? AUTO_ORDER.find(fits) ?? roomiest : placement;
|
|
378
|
+
if (room[side] < needs(side) && room[oppositeSide[side]] > room[side]) side = oppositeSide[side];
|
|
379
|
+
if (room[side] < needs(side) && (side === "left" || side === "right")) {
|
|
380
|
+
const vertical = room.bottom >= room.top ? "bottom" : "top";
|
|
381
|
+
if (room[vertical] >= needs(vertical)) side = vertical;
|
|
382
|
+
}
|
|
383
|
+
if (side === "top" || side === "bottom") {
|
|
384
|
+
const start2 = t.left + t.width / 2 - w / 2;
|
|
385
|
+
const shift2 = slideAlong(start2, w, area.left, area.right, t.left, t.right);
|
|
386
|
+
return { side, shiftX: shift2, shiftY: 0, arrowX: clamp(-shift2, ARROW_INSET - w / 2, w / 2 - ARROW_INSET), arrowY: 0 };
|
|
387
|
+
}
|
|
388
|
+
const start = t.top + t.height / 2 - h / 2;
|
|
389
|
+
const shift = slideAlong(start, h, area.top, area.bottom, t.top, t.bottom);
|
|
390
|
+
return { side, shiftX: 0, shiftY: shift, arrowX: 0, arrowY: clamp(-shift, ARROW_INSET - h / 2, h / 2 - ARROW_INSET) };
|
|
391
|
+
}
|
|
392
|
+
function usePlacement(open, triggerRef, bubbleRef, placement = "top") {
|
|
393
|
+
const [position, setPosition] = useState(null);
|
|
394
|
+
const update = () => {
|
|
395
|
+
if (!triggerRef.current || !bubbleRef.current) return;
|
|
396
|
+
const next = placeTooltip(triggerRef.current, bubbleRef.current, placement);
|
|
397
|
+
setPosition((prev) => samePosition(prev, next) ? prev : next);
|
|
398
|
+
};
|
|
399
|
+
const updateRef = useRef(update);
|
|
400
|
+
updateRef.current = update;
|
|
401
|
+
useBrowserLayoutEffect(() => {
|
|
402
|
+
if (open) updateRef.current();
|
|
403
|
+
});
|
|
404
|
+
useEffect(() => {
|
|
405
|
+
if (!open) return void 0;
|
|
406
|
+
const onChange = () => updateRef.current();
|
|
407
|
+
window.addEventListener("scroll", onChange, true);
|
|
408
|
+
window.addEventListener("resize", onChange);
|
|
409
|
+
return () => {
|
|
410
|
+
window.removeEventListener("scroll", onChange, true);
|
|
411
|
+
window.removeEventListener("resize", onChange);
|
|
412
|
+
};
|
|
413
|
+
}, [open]);
|
|
414
|
+
return position;
|
|
415
|
+
}
|
|
228
416
|
function useControllableState(value, defaultValue, onChange) {
|
|
229
417
|
const [internal, setInternal] = useState(defaultValue);
|
|
230
418
|
const isControlled = value !== void 0;
|
|
@@ -1084,18 +1272,22 @@ function resolveAvatarColor(color, seed) {
|
|
|
1084
1272
|
}
|
|
1085
1273
|
function AvatarTooltip({ content, description, accent, children }) {
|
|
1086
1274
|
const [open, setOpen] = useState(false);
|
|
1275
|
+
const rootRef = useRef(null);
|
|
1087
1276
|
const bubbleRef = useRef(null);
|
|
1277
|
+
const position = usePlacement(open, rootRef, bubbleRef, "top");
|
|
1278
|
+
const sliding = Boolean(position == null ? void 0 : position.shiftX);
|
|
1088
1279
|
const tiltToward = (e) => {
|
|
1089
1280
|
const bubble = bubbleRef.current;
|
|
1090
1281
|
if (!bubble) return;
|
|
1091
1282
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
1092
1283
|
const ratio = Math.max(-1, Math.min(1, (e.clientX - (rect.left + rect.width / 2)) / (rect.width / 2)));
|
|
1093
|
-
bubble.style.setProperty("--_shift", `${ratio * 10}px`);
|
|
1284
|
+
bubble.style.setProperty("--_shift", sliding ? "0px" : `${ratio * 10}px`);
|
|
1094
1285
|
bubble.style.setProperty("--_tilt", `${ratio * 12}deg`);
|
|
1095
1286
|
};
|
|
1096
1287
|
return /* @__PURE__ */ jsxs(
|
|
1097
1288
|
"span",
|
|
1098
1289
|
{
|
|
1290
|
+
ref: rootRef,
|
|
1099
1291
|
className: "ru-avatar-tip",
|
|
1100
1292
|
onMouseEnter: () => setOpen(true),
|
|
1101
1293
|
onMouseLeave: () => setOpen(false),
|
|
@@ -1105,10 +1297,23 @@ function AvatarTooltip({ content, description, accent, children }) {
|
|
|
1105
1297
|
children: [
|
|
1106
1298
|
children,
|
|
1107
1299
|
open && // The avatar's accessible name already includes this text, so the bubble is visual only.
|
|
1108
|
-
/* @__PURE__ */ jsx(
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1300
|
+
/* @__PURE__ */ jsx(
|
|
1301
|
+
"span",
|
|
1302
|
+
{
|
|
1303
|
+
ref: bubbleRef,
|
|
1304
|
+
className: cn("ru-avatar-tip__bubble", (position == null ? void 0 : position.side) === "bottom" && "ru-avatar-tip__bubble--bottom"),
|
|
1305
|
+
"aria-hidden": "true",
|
|
1306
|
+
style: {
|
|
1307
|
+
...accent && { "--_accent": accent },
|
|
1308
|
+
...position && { "--_sx": `${position.shiftX}px`, "--_ax": `${position.arrowX}px` }
|
|
1309
|
+
},
|
|
1310
|
+
children: /* @__PURE__ */ jsxs("span", { className: "ru-avatar-tip__inner", children: [
|
|
1311
|
+
/* @__PURE__ */ jsx("span", { className: "ru-avatar-tip__dots" }),
|
|
1312
|
+
/* @__PURE__ */ jsx("span", { className: "ru-avatar-tip__name", children: content }),
|
|
1313
|
+
description && /* @__PURE__ */ jsx("span", { className: "ru-avatar-tip__desc", children: description })
|
|
1314
|
+
] })
|
|
1315
|
+
}
|
|
1316
|
+
)
|
|
1112
1317
|
]
|
|
1113
1318
|
}
|
|
1114
1319
|
);
|
|
@@ -1268,6 +1473,7 @@ function Divider({
|
|
|
1268
1473
|
"ru-divider",
|
|
1269
1474
|
variant !== "solid" && `ru-divider--${variant}`,
|
|
1270
1475
|
vertical && "ru-divider--vertical",
|
|
1476
|
+
vertical && length !== void 0 && "ru-divider--sized",
|
|
1271
1477
|
labelled && "ru-divider--label",
|
|
1272
1478
|
labelled && labelPosition !== "center" && `ru-divider--label-${labelPosition}`,
|
|
1273
1479
|
!vertical && align !== "center" && `ru-divider--align-${align}`,
|
|
@@ -1398,7 +1604,7 @@ function Table({
|
|
|
1398
1604
|
] })
|
|
1399
1605
|
] });
|
|
1400
1606
|
}
|
|
1401
|
-
const EXIT_MS = 200;
|
|
1607
|
+
const EXIT_MS$1 = 200;
|
|
1402
1608
|
function usePresence(open) {
|
|
1403
1609
|
const [render, setRender] = useState(open);
|
|
1404
1610
|
const [closing, setClosing] = useState(false);
|
|
@@ -1413,7 +1619,7 @@ function usePresence(open) {
|
|
|
1413
1619
|
const timer = setTimeout(() => {
|
|
1414
1620
|
setRender(false);
|
|
1415
1621
|
setClosing(false);
|
|
1416
|
-
}, EXIT_MS);
|
|
1622
|
+
}, EXIT_MS$1);
|
|
1417
1623
|
return () => clearTimeout(timer);
|
|
1418
1624
|
}, [open]);
|
|
1419
1625
|
return { render, closing };
|
|
@@ -1446,7 +1652,7 @@ function Modal({
|
|
|
1446
1652
|
const titleId = useId();
|
|
1447
1653
|
const descId = useId();
|
|
1448
1654
|
const { render, closing } = usePresence(open);
|
|
1449
|
-
useOverlay(open && mounted, panelRef, onClose, closeOnEsc);
|
|
1655
|
+
useOverlay(open && mounted && render, panelRef, onClose, closeOnEsc);
|
|
1450
1656
|
if (!render || !mounted) return null;
|
|
1451
1657
|
const onOverlayMouseDown = (e) => {
|
|
1452
1658
|
if (!closing && closeOnOverlayClick && e.target === e.currentTarget) onClose();
|
|
@@ -1495,7 +1701,7 @@ function Drawer({
|
|
|
1495
1701
|
const titleId = useId();
|
|
1496
1702
|
const descId = useId();
|
|
1497
1703
|
const { render, closing } = usePresence(open);
|
|
1498
|
-
useOverlay(open && mounted, panelRef, onClose, closeOnEsc);
|
|
1704
|
+
useOverlay(open && mounted && render, panelRef, onClose, closeOnEsc);
|
|
1499
1705
|
if (!render || !mounted) return null;
|
|
1500
1706
|
const resolvedSize = typeof size === "number" ? `${size}px` : drawerSizes[size] ?? size;
|
|
1501
1707
|
return createPortal(
|
|
@@ -1551,13 +1757,24 @@ function Tooltip({
|
|
|
1551
1757
|
textColor,
|
|
1552
1758
|
open: openProp,
|
|
1553
1759
|
disabled,
|
|
1760
|
+
onPlacementChange,
|
|
1554
1761
|
className
|
|
1555
1762
|
}) {
|
|
1556
1763
|
const [open, setOpen] = useState(false);
|
|
1557
1764
|
const timer = useRef(void 0);
|
|
1765
|
+
const rootRef = useRef(null);
|
|
1766
|
+
const bubbleRef = useRef(null);
|
|
1558
1767
|
const id = useId();
|
|
1559
1768
|
const visible = (openProp ?? open) && !disabled && content != null && content !== "";
|
|
1560
1769
|
useEffect(() => () => clearTimeout(timer.current), []);
|
|
1770
|
+
const position = usePlacement(visible, rootRef, bubbleRef, placement);
|
|
1771
|
+
const side = position == null ? void 0 : position.side;
|
|
1772
|
+
const onPlacementChangeRef = useRef(onPlacementChange);
|
|
1773
|
+
onPlacementChangeRef.current = onPlacementChange;
|
|
1774
|
+
useEffect(() => {
|
|
1775
|
+
var _a;
|
|
1776
|
+
if (side) (_a = onPlacementChangeRef.current) == null ? void 0 : _a.call(onPlacementChangeRef, side);
|
|
1777
|
+
}, [side]);
|
|
1561
1778
|
const show = () => {
|
|
1562
1779
|
clearTimeout(timer.current);
|
|
1563
1780
|
timer.current = setTimeout(() => setOpen(true), delay);
|
|
@@ -1572,6 +1789,7 @@ function Tooltip({
|
|
|
1572
1789
|
return /* @__PURE__ */ jsxs(
|
|
1573
1790
|
"span",
|
|
1574
1791
|
{
|
|
1792
|
+
ref: rootRef,
|
|
1575
1793
|
className: cn("ru-tooltip", className),
|
|
1576
1794
|
onMouseEnter: show,
|
|
1577
1795
|
onMouseLeave: hide,
|
|
@@ -1583,15 +1801,25 @@ function Tooltip({
|
|
|
1583
1801
|
visible && /* @__PURE__ */ jsx(
|
|
1584
1802
|
"span",
|
|
1585
1803
|
{
|
|
1804
|
+
ref: bubbleRef,
|
|
1586
1805
|
role: "tooltip",
|
|
1587
1806
|
id,
|
|
1588
1807
|
className: cn(
|
|
1589
1808
|
"ru-tooltip__bubble",
|
|
1590
|
-
`ru-tooltip__bubble--${placement}`,
|
|
1809
|
+
`ru-tooltip__bubble--${side ?? (placement === "auto" ? "top" : placement)}`,
|
|
1591
1810
|
size !== "md" && `ru-tooltip__bubble--${size}`,
|
|
1592
1811
|
!arrow && "ru-tooltip__bubble--no-arrow"
|
|
1593
1812
|
),
|
|
1594
|
-
style:
|
|
1813
|
+
style: {
|
|
1814
|
+
...color && { "--ru-tooltip-bg": color },
|
|
1815
|
+
...textColor && { "--ru-tooltip-text": textColor },
|
|
1816
|
+
...position && {
|
|
1817
|
+
"--_sx": `${position.shiftX}px`,
|
|
1818
|
+
"--_sy": `${position.shiftY}px`,
|
|
1819
|
+
"--_ax": `${position.arrowX}px`,
|
|
1820
|
+
"--_ay": `${position.arrowY}px`
|
|
1821
|
+
}
|
|
1822
|
+
},
|
|
1595
1823
|
children: content
|
|
1596
1824
|
}
|
|
1597
1825
|
)
|
|
@@ -1706,12 +1934,23 @@ function Dropdown({ trigger, items, align = "start", closeOnSelect = true, class
|
|
|
1706
1934
|
] });
|
|
1707
1935
|
}
|
|
1708
1936
|
const ToastContext = createContext(null);
|
|
1709
|
-
|
|
1937
|
+
const EXIT_MS = 200;
|
|
1938
|
+
function ToastProvider({
|
|
1939
|
+
children,
|
|
1940
|
+
position = "bottom-right",
|
|
1941
|
+
duration = 4e3,
|
|
1942
|
+
max = 5,
|
|
1943
|
+
appearance = "default",
|
|
1944
|
+
animation = "slide",
|
|
1945
|
+
progress = false
|
|
1946
|
+
}) {
|
|
1710
1947
|
const [toasts, setToasts] = useState([]);
|
|
1711
1948
|
const mounted = useMounted();
|
|
1712
1949
|
const counter = useRef(0);
|
|
1713
1950
|
const dismiss = useCallback((id) => {
|
|
1714
|
-
|
|
1951
|
+
const matches = (t) => !id || t.id === id;
|
|
1952
|
+
setToasts((list) => list.map((t) => matches(t) ? { ...t, closing: true } : t));
|
|
1953
|
+
setTimeout(() => setToasts((list) => list.filter((t) => !(matches(t) && t.closing))), EXIT_MS);
|
|
1715
1954
|
}, []);
|
|
1716
1955
|
const api = useMemo(() => {
|
|
1717
1956
|
const show = (options) => {
|
|
@@ -1731,25 +1970,61 @@ function ToastProvider({ children, position = "bottom-right", duration = 4e3, ma
|
|
|
1731
1970
|
return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: api, children: [
|
|
1732
1971
|
children,
|
|
1733
1972
|
mounted && createPortal(
|
|
1734
|
-
|
|
1973
|
+
// One stack per position in use, so toasts can override the provider's position.
|
|
1974
|
+
[.../* @__PURE__ */ new Set([position, ...toasts.map((t) => t.position ?? position)])].map((spot) => /* @__PURE__ */ jsx("ol", { className: cn("ru-toaster", `ru-toaster--${spot}`), "aria-live": "polite", "aria-label": "Notifications", children: toasts.filter((t) => (t.position ?? position) === spot).map((t) => /* @__PURE__ */ jsx(
|
|
1975
|
+
ToastView,
|
|
1976
|
+
{
|
|
1977
|
+
toast: t,
|
|
1978
|
+
duration: t.duration ?? duration,
|
|
1979
|
+
appearance: t.appearance ?? appearance,
|
|
1980
|
+
animation: t.animation ?? animation,
|
|
1981
|
+
progress: t.progress ?? progress,
|
|
1982
|
+
onDismiss: dismiss
|
|
1983
|
+
},
|
|
1984
|
+
t.id
|
|
1985
|
+
)) }, spot)),
|
|
1735
1986
|
document.body
|
|
1736
1987
|
)
|
|
1737
1988
|
] });
|
|
1738
1989
|
}
|
|
1739
|
-
function ToastView({ toast, duration, onDismiss }) {
|
|
1990
|
+
function ToastView({ toast, duration, appearance, animation, progress, onDismiss }) {
|
|
1740
1991
|
const [paused, setPaused] = useState(false);
|
|
1741
|
-
const { id, variant = "default", title, description, action } = toast;
|
|
1992
|
+
const { id, variant = "default", title, description, action, closing } = toast;
|
|
1993
|
+
const ref = useRef(null);
|
|
1994
|
+
const remaining = useRef(duration);
|
|
1995
|
+
const timed = Number.isFinite(duration) && duration > 0;
|
|
1742
1996
|
useEffect(() => {
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1997
|
+
remaining.current = duration;
|
|
1998
|
+
}, [id, duration]);
|
|
1999
|
+
useEffect(() => {
|
|
2000
|
+
if (paused || closing || !timed) return void 0;
|
|
2001
|
+
const startedAt = Date.now();
|
|
2002
|
+
const timer = setTimeout(() => onDismiss(id), remaining.current);
|
|
2003
|
+
return () => {
|
|
2004
|
+
clearTimeout(timer);
|
|
2005
|
+
remaining.current -= Date.now() - startedAt;
|
|
2006
|
+
};
|
|
2007
|
+
}, [id, duration, paused, closing, timed, onDismiss]);
|
|
2008
|
+
useLayoutEffect(() => {
|
|
2009
|
+
if (closing && ref.current) ref.current.style.setProperty("--_h", `${ref.current.offsetHeight}px`);
|
|
2010
|
+
}, [closing]);
|
|
1747
2011
|
const Icon = variant === "default" ? null : statusIcons[variant];
|
|
2012
|
+
const countdown = progress && timed;
|
|
2013
|
+
const traced = countdown && appearance === "accent";
|
|
2014
|
+
const countdownStyle = { animationDuration: `${duration}ms`, animationPlayState: paused ? "paused" : "running" };
|
|
1748
2015
|
return /* @__PURE__ */ jsxs(
|
|
1749
2016
|
"li",
|
|
1750
2017
|
{
|
|
2018
|
+
ref,
|
|
1751
2019
|
role: "status",
|
|
1752
|
-
className: cn(
|
|
2020
|
+
className: cn(
|
|
2021
|
+
"ru-toast",
|
|
2022
|
+
`ru-toast--${appearance}`,
|
|
2023
|
+
`ru-toast--in-${animation}`,
|
|
2024
|
+
traced && "ru-toast--traced",
|
|
2025
|
+
closing && "ru-toast--closing",
|
|
2026
|
+
variant !== "default" && `ru-c-${variant}`
|
|
2027
|
+
),
|
|
1753
2028
|
onMouseEnter: () => setPaused(true),
|
|
1754
2029
|
onMouseLeave: () => setPaused(false),
|
|
1755
2030
|
children: [
|
|
@@ -1759,7 +2034,9 @@ function ToastView({ toast, duration, onDismiss }) {
|
|
|
1759
2034
|
description && /* @__PURE__ */ jsx("div", { className: "ru-toast__desc", children: description }),
|
|
1760
2035
|
action && /* @__PURE__ */ jsx("div", { className: "ru-toast__action", children: action })
|
|
1761
2036
|
] }),
|
|
1762
|
-
/* @__PURE__ */ jsx(CloseButton, { onClick: () => onDismiss(id), label: "Dismiss notification" })
|
|
2037
|
+
/* @__PURE__ */ jsx(CloseButton, { onClick: () => onDismiss(id), label: "Dismiss notification" }),
|
|
2038
|
+
traced && /* @__PURE__ */ jsx("svg", { className: "ru-toast__trace", "aria-hidden": "true", children: /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", rx: "13", pathLength: "1", style: countdownStyle }) }),
|
|
2039
|
+
countdown && !traced && /* @__PURE__ */ jsx("span", { className: "ru-toast__progress", "aria-hidden": "true", style: countdownStyle })
|
|
1763
2040
|
]
|
|
1764
2041
|
}
|
|
1765
2042
|
);
|
|
@@ -1860,7 +2137,18 @@ function SpotlightCard({ color, className, style, onMouseMove, children, ...rest
|
|
|
1860
2137
|
}
|
|
1861
2138
|
);
|
|
1862
2139
|
}
|
|
1863
|
-
|
|
2140
|
+
function DotText({ as: Tag = "span", dot = 4, color, glow = false, className, style, children, ...rest }) {
|
|
2141
|
+
return /* @__PURE__ */ jsx(
|
|
2142
|
+
Tag,
|
|
2143
|
+
{
|
|
2144
|
+
className: cn("ru-dot-text", glow && "ru-dot-text--glow", className),
|
|
2145
|
+
style: { "--_d": `${dot}px`, ...color && { color }, ...style },
|
|
2146
|
+
...rest,
|
|
2147
|
+
children
|
|
2148
|
+
}
|
|
2149
|
+
);
|
|
2150
|
+
}
|
|
2151
|
+
const semanticColors$4 = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
|
|
1864
2152
|
function Pager({ onPrev, onNext, prevLabel, nextLabel }) {
|
|
1865
2153
|
if (!onPrev && !onNext) return null;
|
|
1866
2154
|
return /* @__PURE__ */ jsxs("div", { className: "ru-frame-card__pager", children: [
|
|
@@ -1898,7 +2186,7 @@ function FrameCard({
|
|
|
1898
2186
|
{
|
|
1899
2187
|
className: cn("ru-frame-card", onFrame && "ru-frame-card--title-frame", className),
|
|
1900
2188
|
style: {
|
|
1901
|
-
"--_fc": semanticColors$
|
|
2189
|
+
"--_fc": semanticColors$4.has(color) ? `var(--ru-${color})` : color,
|
|
1902
2190
|
...textColor && { "--_fc-on": textColor, "--_fc-ink": textColor },
|
|
1903
2191
|
...style
|
|
1904
2192
|
},
|
|
@@ -1937,7 +2225,7 @@ function FrameCard({
|
|
|
1937
2225
|
}
|
|
1938
2226
|
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
1939
2227
|
const WEEKDAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
1940
|
-
const semanticColors = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
|
|
2228
|
+
const semanticColors$3 = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
|
|
1941
2229
|
const dayKey = (d) => d.getFullYear() * 1e4 + d.getMonth() * 100 + d.getDate();
|
|
1942
2230
|
const sameDay = (a, b) => Boolean(a && b) && dayKey(a) === dayKey(b);
|
|
1943
2231
|
function Calendar({
|
|
@@ -1997,7 +2285,7 @@ function Calendar({
|
|
|
1997
2285
|
next.focus();
|
|
1998
2286
|
}
|
|
1999
2287
|
};
|
|
2000
|
-
const ink = color ? semanticColors.has(color) ? `var(--ru-${color})` : color : void 0;
|
|
2288
|
+
const ink = color ? semanticColors$3.has(color) ? `var(--ru-${color})` : color : void 0;
|
|
2001
2289
|
return /* @__PURE__ */ jsxs("div", { className: cn("ru-calendar", className), style: ink ? { "--_cc": ink, ...style } : style, ...rest, children: [
|
|
2002
2290
|
/* @__PURE__ */ jsxs("div", { className: "ru-calendar__head", children: [
|
|
2003
2291
|
/* @__PURE__ */ jsx("button", { type: "button", className: "ru-calendar__nav", "aria-label": "Previous month", onClick: () => shift(-1), children: /* @__PURE__ */ jsx(ChevronLeftIcon, {}) }),
|
|
@@ -2052,6 +2340,638 @@ function Calendar({
|
|
|
2052
2340
|
}) })
|
|
2053
2341
|
] });
|
|
2054
2342
|
}
|
|
2343
|
+
const normalize = (options) => options.map((o) => typeof o === "object" ? o : { label: String(o), value: o });
|
|
2344
|
+
function SegmentedControl({
|
|
2345
|
+
options,
|
|
2346
|
+
value,
|
|
2347
|
+
defaultValue,
|
|
2348
|
+
onChange,
|
|
2349
|
+
size = "md",
|
|
2350
|
+
fullWidth = false,
|
|
2351
|
+
pill = false,
|
|
2352
|
+
disabled = false,
|
|
2353
|
+
className,
|
|
2354
|
+
style,
|
|
2355
|
+
...rest
|
|
2356
|
+
}) {
|
|
2357
|
+
var _a;
|
|
2358
|
+
const items = normalize(options);
|
|
2359
|
+
const firstEnabled = (_a = items.find((i) => !i.disabled)) == null ? void 0 : _a.value;
|
|
2360
|
+
const [active, setActive] = useControllableState(value, defaultValue ?? firstEnabled, onChange);
|
|
2361
|
+
const rootRef = useRef(null);
|
|
2362
|
+
const [thumb, setThumb] = useState(null);
|
|
2363
|
+
const activeIndex = items.findIndex((i) => i.value === active);
|
|
2364
|
+
useEffect(() => {
|
|
2365
|
+
const root = rootRef.current;
|
|
2366
|
+
if (!root) return void 0;
|
|
2367
|
+
const measure = () => {
|
|
2368
|
+
const el = root.querySelectorAll('[role="radio"]')[activeIndex];
|
|
2369
|
+
setThumb(el ? { x: el.offsetLeft, w: el.offsetWidth } : null);
|
|
2370
|
+
};
|
|
2371
|
+
measure();
|
|
2372
|
+
if (typeof ResizeObserver === "undefined") return void 0;
|
|
2373
|
+
const ro = new ResizeObserver(measure);
|
|
2374
|
+
ro.observe(root);
|
|
2375
|
+
return () => ro.disconnect();
|
|
2376
|
+
}, [activeIndex, items.length]);
|
|
2377
|
+
const move = (from, step) => {
|
|
2378
|
+
var _a2;
|
|
2379
|
+
const buttons = rootRef.current.querySelectorAll('[role="radio"]');
|
|
2380
|
+
for (let n = 1; n <= items.length; n++) {
|
|
2381
|
+
const i = (from + step * n + items.length) % items.length;
|
|
2382
|
+
if (!items[i].disabled) {
|
|
2383
|
+
setActive(items[i].value);
|
|
2384
|
+
(_a2 = buttons[i]) == null ? void 0 : _a2.focus();
|
|
2385
|
+
return;
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
const onKeyDown = (e, index) => {
|
|
2390
|
+
if (e.key === "ArrowRight" || e.key === "ArrowDown") move(index, 1);
|
|
2391
|
+
else if (e.key === "ArrowLeft" || e.key === "ArrowUp") move(index, -1);
|
|
2392
|
+
else return;
|
|
2393
|
+
e.preventDefault();
|
|
2394
|
+
};
|
|
2395
|
+
return /* @__PURE__ */ jsxs(
|
|
2396
|
+
"div",
|
|
2397
|
+
{
|
|
2398
|
+
ref: rootRef,
|
|
2399
|
+
role: "radiogroup",
|
|
2400
|
+
"aria-disabled": disabled || void 0,
|
|
2401
|
+
className: cn(
|
|
2402
|
+
"ru-segmented",
|
|
2403
|
+
`ru-segmented--${size}`,
|
|
2404
|
+
fullWidth && "ru-segmented--full",
|
|
2405
|
+
pill && "ru-segmented--pill",
|
|
2406
|
+
thumb && "ru-segmented--ready",
|
|
2407
|
+
className
|
|
2408
|
+
),
|
|
2409
|
+
style: thumb ? { "--_x": `${thumb.x}px`, "--_w": `${thumb.w}px`, ...style } : style,
|
|
2410
|
+
...rest,
|
|
2411
|
+
children: [
|
|
2412
|
+
thumb && activeIndex >= 0 && /* @__PURE__ */ jsx("span", { className: "ru-segmented__thumb", "aria-hidden": "true" }),
|
|
2413
|
+
items.map((item, i) => {
|
|
2414
|
+
const selected = i === activeIndex;
|
|
2415
|
+
return /* @__PURE__ */ jsxs(
|
|
2416
|
+
"button",
|
|
2417
|
+
{
|
|
2418
|
+
type: "button",
|
|
2419
|
+
role: "radio",
|
|
2420
|
+
"aria-checked": selected,
|
|
2421
|
+
tabIndex: selected || activeIndex < 0 && i === 0 ? 0 : -1,
|
|
2422
|
+
disabled: disabled || item.disabled,
|
|
2423
|
+
className: "ru-segmented__item",
|
|
2424
|
+
onClick: () => setActive(item.value),
|
|
2425
|
+
onKeyDown: (e) => onKeyDown(e, i),
|
|
2426
|
+
children: [
|
|
2427
|
+
item.icon,
|
|
2428
|
+
item.label
|
|
2429
|
+
]
|
|
2430
|
+
},
|
|
2431
|
+
String(item.value)
|
|
2432
|
+
);
|
|
2433
|
+
})
|
|
2434
|
+
]
|
|
2435
|
+
}
|
|
2436
|
+
);
|
|
2437
|
+
}
|
|
2438
|
+
const semanticColors$2 = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
|
|
2439
|
+
function ProgressRing({
|
|
2440
|
+
value = 0,
|
|
2441
|
+
max = 100,
|
|
2442
|
+
size = 48,
|
|
2443
|
+
thickness = 4,
|
|
2444
|
+
color = "primary",
|
|
2445
|
+
trackColor,
|
|
2446
|
+
showValue = false,
|
|
2447
|
+
label,
|
|
2448
|
+
className,
|
|
2449
|
+
style,
|
|
2450
|
+
children,
|
|
2451
|
+
...rest
|
|
2452
|
+
}) {
|
|
2453
|
+
const percent = Math.min(100, Math.max(0, value / max * 100));
|
|
2454
|
+
const r = (size - thickness) / 2;
|
|
2455
|
+
const circumference = 2 * Math.PI * r;
|
|
2456
|
+
const ink = semanticColors$2.has(color) ? `var(--ru-${color})` : color;
|
|
2457
|
+
return /* @__PURE__ */ jsxs(
|
|
2458
|
+
"div",
|
|
2459
|
+
{
|
|
2460
|
+
role: "progressbar",
|
|
2461
|
+
"aria-valuemin": 0,
|
|
2462
|
+
"aria-valuemax": max,
|
|
2463
|
+
"aria-valuenow": value,
|
|
2464
|
+
"aria-label": label,
|
|
2465
|
+
className: cn("ru-ring", className),
|
|
2466
|
+
style: { width: size, height: size, "--_c": ink, ...trackColor && { "--_track": trackColor }, ...style },
|
|
2467
|
+
...rest,
|
|
2468
|
+
children: [
|
|
2469
|
+
/* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, "aria-hidden": "true", children: [
|
|
2470
|
+
/* @__PURE__ */ jsx("circle", { className: "ru-ring__track", cx: size / 2, cy: size / 2, r, strokeWidth: thickness }),
|
|
2471
|
+
/* @__PURE__ */ jsx(
|
|
2472
|
+
"circle",
|
|
2473
|
+
{
|
|
2474
|
+
className: "ru-ring__bar",
|
|
2475
|
+
cx: size / 2,
|
|
2476
|
+
cy: size / 2,
|
|
2477
|
+
r,
|
|
2478
|
+
strokeWidth: thickness,
|
|
2479
|
+
strokeDasharray: circumference,
|
|
2480
|
+
strokeDashoffset: circumference * (1 - percent / 100)
|
|
2481
|
+
}
|
|
2482
|
+
)
|
|
2483
|
+
] }),
|
|
2484
|
+
(children !== void 0 || showValue) && /* @__PURE__ */ jsx("span", { className: "ru-ring__label", style: { fontSize: Math.max(10, Math.round(size * 0.24)) }, children: children ?? `${Math.round(percent)}%` })
|
|
2485
|
+
]
|
|
2486
|
+
}
|
|
2487
|
+
);
|
|
2488
|
+
}
|
|
2489
|
+
const semanticColors$1 = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
|
|
2490
|
+
function Stat({
|
|
2491
|
+
label,
|
|
2492
|
+
value,
|
|
2493
|
+
trend,
|
|
2494
|
+
trendLabel,
|
|
2495
|
+
invertTrend = false,
|
|
2496
|
+
helpText,
|
|
2497
|
+
size = "md",
|
|
2498
|
+
align = "start",
|
|
2499
|
+
className,
|
|
2500
|
+
...rest
|
|
2501
|
+
}) {
|
|
2502
|
+
const hasTrend = typeof trend === "number";
|
|
2503
|
+
const up = hasTrend && trend >= 0;
|
|
2504
|
+
const good = up !== invertTrend;
|
|
2505
|
+
const TrendIcon = up ? TrendUpIcon : TrendDownIcon;
|
|
2506
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("ru-stat", `ru-stat--${size}`, align === "end" && "ru-stat--end", className), ...rest, children: [
|
|
2507
|
+
/* @__PURE__ */ jsxs("div", { className: "ru-stat__top", children: [
|
|
2508
|
+
label && /* @__PURE__ */ jsx("span", { className: "ru-stat__label", children: label }),
|
|
2509
|
+
hasTrend && /* @__PURE__ */ jsxs("span", { className: cn("ru-stat__trend", good ? "ru-stat__trend--good" : "ru-stat__trend--bad"), children: [
|
|
2510
|
+
/* @__PURE__ */ jsx("span", { className: "ru-stat__trend-icon", children: /* @__PURE__ */ jsx(TrendIcon, {}) }),
|
|
2511
|
+
trendLabel ?? `${Math.abs(trend).toLocaleString()}%`,
|
|
2512
|
+
/* @__PURE__ */ jsx("span", { className: "ru-sr-only", children: up ? " increase" : " decrease" })
|
|
2513
|
+
] })
|
|
2514
|
+
] }),
|
|
2515
|
+
/* @__PURE__ */ jsx("div", { className: "ru-stat__value", children: value }),
|
|
2516
|
+
helpText && /* @__PURE__ */ jsx("div", { className: "ru-stat__help", children: helpText })
|
|
2517
|
+
] });
|
|
2518
|
+
}
|
|
2519
|
+
function DateTile({ date, color = "primary", size = "md", className, style, ...rest }) {
|
|
2520
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
2521
|
+
const ink = semanticColors$1.has(color) ? `var(--ru-${color})` : color;
|
|
2522
|
+
return /* @__PURE__ */ jsxs(
|
|
2523
|
+
"time",
|
|
2524
|
+
{
|
|
2525
|
+
dateTime: d.toISOString().slice(0, 10),
|
|
2526
|
+
"aria-label": d.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric", year: "numeric" }),
|
|
2527
|
+
className: cn("ru-date-tile", `ru-date-tile--${size}`, className),
|
|
2528
|
+
style: { "--_c": ink, ...style },
|
|
2529
|
+
...rest,
|
|
2530
|
+
children: [
|
|
2531
|
+
/* @__PURE__ */ jsx("span", { className: "ru-date-tile__month", "aria-hidden": "true", children: d.toLocaleDateString("en-US", { month: "short" }) }),
|
|
2532
|
+
/* @__PURE__ */ jsx("span", { className: "ru-date-tile__day", "aria-hidden": "true", children: d.getDate() })
|
|
2533
|
+
]
|
|
2534
|
+
}
|
|
2535
|
+
);
|
|
2536
|
+
}
|
|
2537
|
+
function matchesAccept(file, accept) {
|
|
2538
|
+
if (!accept) return true;
|
|
2539
|
+
return accept.split(",").some((rule) => {
|
|
2540
|
+
const r = rule.trim().toLowerCase();
|
|
2541
|
+
if (!r) return false;
|
|
2542
|
+
if (r.startsWith(".")) return file.name.toLowerCase().endsWith(r);
|
|
2543
|
+
if (r.endsWith("/*")) return file.type.toLowerCase().startsWith(r.slice(0, -1));
|
|
2544
|
+
return file.type.toLowerCase() === r;
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
function formatBytes(bytes) {
|
|
2548
|
+
if (typeof bytes !== "number") return bytes;
|
|
2549
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
2550
|
+
const units = ["KB", "MB", "GB"];
|
|
2551
|
+
let n = bytes / 1024;
|
|
2552
|
+
let i = 0;
|
|
2553
|
+
while (n >= 1024 && i < units.length - 1) {
|
|
2554
|
+
n /= 1024;
|
|
2555
|
+
i++;
|
|
2556
|
+
}
|
|
2557
|
+
return `${Number(n.toFixed(1))}${units[i]}`;
|
|
2558
|
+
}
|
|
2559
|
+
function Dropzone({
|
|
2560
|
+
onFiles,
|
|
2561
|
+
onReject,
|
|
2562
|
+
accept,
|
|
2563
|
+
multiple = true,
|
|
2564
|
+
maxSize,
|
|
2565
|
+
disabled = false,
|
|
2566
|
+
icon,
|
|
2567
|
+
title = "Drag & drop files here",
|
|
2568
|
+
description,
|
|
2569
|
+
className,
|
|
2570
|
+
children,
|
|
2571
|
+
...rest
|
|
2572
|
+
}) {
|
|
2573
|
+
const inputRef = useRef(null);
|
|
2574
|
+
const [dragging, setDragging] = useState(false);
|
|
2575
|
+
const handle = (list) => {
|
|
2576
|
+
const files = Array.from(list ?? []);
|
|
2577
|
+
const accepted = [];
|
|
2578
|
+
const rejected = [];
|
|
2579
|
+
for (const file of multiple ? files : files.slice(0, 1)) {
|
|
2580
|
+
if (!matchesAccept(file, accept)) rejected.push({ file, reason: "type" });
|
|
2581
|
+
else if (maxSize && file.size > maxSize) rejected.push({ file, reason: "size" });
|
|
2582
|
+
else accepted.push(file);
|
|
2583
|
+
}
|
|
2584
|
+
if (accepted.length) onFiles == null ? void 0 : onFiles(accepted);
|
|
2585
|
+
if (rejected.length) onReject == null ? void 0 : onReject(rejected);
|
|
2586
|
+
};
|
|
2587
|
+
const open = () => {
|
|
2588
|
+
var _a;
|
|
2589
|
+
return !disabled && ((_a = inputRef.current) == null ? void 0 : _a.click());
|
|
2590
|
+
};
|
|
2591
|
+
return /* @__PURE__ */ jsxs(
|
|
2592
|
+
"div",
|
|
2593
|
+
{
|
|
2594
|
+
role: "button",
|
|
2595
|
+
tabIndex: disabled ? -1 : 0,
|
|
2596
|
+
"aria-disabled": disabled || void 0,
|
|
2597
|
+
className: cn("ru-dropzone", dragging && "ru-dropzone--active", disabled && "ru-dropzone--disabled", className),
|
|
2598
|
+
onClick: open,
|
|
2599
|
+
onKeyDown: (e) => {
|
|
2600
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
2601
|
+
e.preventDefault();
|
|
2602
|
+
open();
|
|
2603
|
+
}
|
|
2604
|
+
},
|
|
2605
|
+
onDragEnter: (e) => {
|
|
2606
|
+
e.preventDefault();
|
|
2607
|
+
if (!disabled) setDragging(true);
|
|
2608
|
+
},
|
|
2609
|
+
onDragOver: (e) => e.preventDefault(),
|
|
2610
|
+
onDragLeave: (e) => {
|
|
2611
|
+
if (!e.currentTarget.contains(e.relatedTarget)) setDragging(false);
|
|
2612
|
+
},
|
|
2613
|
+
onDrop: (e) => {
|
|
2614
|
+
e.preventDefault();
|
|
2615
|
+
setDragging(false);
|
|
2616
|
+
if (!disabled) handle(e.dataTransfer.files);
|
|
2617
|
+
},
|
|
2618
|
+
...rest,
|
|
2619
|
+
children: [
|
|
2620
|
+
/* @__PURE__ */ jsx(
|
|
2621
|
+
"input",
|
|
2622
|
+
{
|
|
2623
|
+
ref: inputRef,
|
|
2624
|
+
type: "file",
|
|
2625
|
+
hidden: true,
|
|
2626
|
+
accept,
|
|
2627
|
+
multiple,
|
|
2628
|
+
disabled,
|
|
2629
|
+
onChange: (e) => {
|
|
2630
|
+
handle(e.target.files);
|
|
2631
|
+
e.target.value = "";
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
),
|
|
2635
|
+
children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2636
|
+
/* @__PURE__ */ jsx("span", { className: "ru-dropzone__icon", children: icon ?? /* @__PURE__ */ jsx(UploadCloudIcon, {}) }),
|
|
2637
|
+
/* @__PURE__ */ jsx("span", { className: "ru-dropzone__title", children: title }),
|
|
2638
|
+
/* @__PURE__ */ jsx("span", { className: "ru-dropzone__desc", children: description ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2639
|
+
"or ",
|
|
2640
|
+
/* @__PURE__ */ jsx("span", { className: "ru-dropzone__browse", children: "browse" }),
|
|
2641
|
+
" to choose files",
|
|
2642
|
+
maxSize ? ` (max ${formatBytes(maxSize)})` : ""
|
|
2643
|
+
] }) })
|
|
2644
|
+
] })
|
|
2645
|
+
]
|
|
2646
|
+
}
|
|
2647
|
+
);
|
|
2648
|
+
}
|
|
2649
|
+
const statusText = { uploading: "Uploading…", complete: "Completed", error: "Failed" };
|
|
2650
|
+
function FileItem({
|
|
2651
|
+
name,
|
|
2652
|
+
size,
|
|
2653
|
+
status = "complete",
|
|
2654
|
+
progress = 0,
|
|
2655
|
+
icon,
|
|
2656
|
+
error,
|
|
2657
|
+
onRemove,
|
|
2658
|
+
action,
|
|
2659
|
+
removeLabel,
|
|
2660
|
+
className,
|
|
2661
|
+
...rest
|
|
2662
|
+
}) {
|
|
2663
|
+
const uploading = status === "uploading";
|
|
2664
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("ru-file-item", `ru-file-item--${status}`, className), ...rest, children: [
|
|
2665
|
+
/* @__PURE__ */ jsx("span", { className: "ru-file-item__icon", children: icon ?? /* @__PURE__ */ jsx(FileIcon, {}) }),
|
|
2666
|
+
/* @__PURE__ */ jsxs("div", { className: "ru-file-item__body", children: [
|
|
2667
|
+
/* @__PURE__ */ jsx("div", { className: "ru-file-item__name", title: name, children: name }),
|
|
2668
|
+
/* @__PURE__ */ jsxs("div", { className: "ru-file-item__meta", children: [
|
|
2669
|
+
size !== void 0 && /* @__PURE__ */ jsx("span", { children: formatBytes(size) }),
|
|
2670
|
+
size !== void 0 && /* @__PURE__ */ jsx("span", { className: "ru-file-item__dot", "aria-hidden": "true" }),
|
|
2671
|
+
/* @__PURE__ */ jsx("span", { className: "ru-file-item__status", children: status === "error" && error ? error : statusText[status] })
|
|
2672
|
+
] }),
|
|
2673
|
+
uploading && /* @__PURE__ */ jsx(
|
|
2674
|
+
"div",
|
|
2675
|
+
{
|
|
2676
|
+
className: "ru-file-item__bar",
|
|
2677
|
+
role: "progressbar",
|
|
2678
|
+
"aria-valuemin": 0,
|
|
2679
|
+
"aria-valuemax": 100,
|
|
2680
|
+
"aria-valuenow": progress,
|
|
2681
|
+
"aria-label": `Uploading ${name}`,
|
|
2682
|
+
children: /* @__PURE__ */ jsx("span", { style: { width: `${progress}%` } })
|
|
2683
|
+
}
|
|
2684
|
+
)
|
|
2685
|
+
] }),
|
|
2686
|
+
uploading && /* @__PURE__ */ jsxs("span", { className: "ru-file-item__percent", children: [
|
|
2687
|
+
Math.round(progress),
|
|
2688
|
+
"%"
|
|
2689
|
+
] }),
|
|
2690
|
+
action,
|
|
2691
|
+
onRemove && /* @__PURE__ */ jsx("button", { type: "button", className: "ru-file-item__remove", "aria-label": removeLabel ?? `Remove ${name}`, onClick: onRemove, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
|
|
2692
|
+
] });
|
|
2693
|
+
}
|
|
2694
|
+
const UndoIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
2695
|
+
/* @__PURE__ */ jsx("path", { d: "M9 14 4 9l5-5" }),
|
|
2696
|
+
/* @__PURE__ */ jsx("path", { d: "M4 9h10.5a5.5 5.5 0 0 1 0 11H11" })
|
|
2697
|
+
] });
|
|
2698
|
+
const EraserIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
2699
|
+
/* @__PURE__ */ jsx("path", { d: "m7 21-4.3-4.3a1 1 0 0 1 0-1.4l10-10a1 1 0 0 1 1.4 0l5.6 5.6a1 1 0 0 1 0 1.4L11 21" }),
|
|
2700
|
+
/* @__PURE__ */ jsx("path", { d: "M22 21H7M5 11l9 9" })
|
|
2701
|
+
] });
|
|
2702
|
+
const SignaturePad = forwardRef(function SignaturePad2({
|
|
2703
|
+
onChange,
|
|
2704
|
+
strokeColor,
|
|
2705
|
+
strokeWidth = 2.5,
|
|
2706
|
+
height = 180,
|
|
2707
|
+
placeholder = "Sign here",
|
|
2708
|
+
showControls = true,
|
|
2709
|
+
undoLabel = "Undo",
|
|
2710
|
+
clearLabel = "Clear",
|
|
2711
|
+
actions,
|
|
2712
|
+
disabled = false,
|
|
2713
|
+
className,
|
|
2714
|
+
style,
|
|
2715
|
+
...rest
|
|
2716
|
+
}, ref) {
|
|
2717
|
+
const canvasRef = useRef(null);
|
|
2718
|
+
const strokes = useRef([]);
|
|
2719
|
+
const drawing = useRef(null);
|
|
2720
|
+
const [count, setCount] = useState(0);
|
|
2721
|
+
const ink = useCallback(() => strokeColor ?? getComputedStyle(canvasRef.current).color, [strokeColor]);
|
|
2722
|
+
const drawStroke = useCallback(
|
|
2723
|
+
(ctx, points) => {
|
|
2724
|
+
ctx.strokeStyle = ink();
|
|
2725
|
+
ctx.fillStyle = ctx.strokeStyle;
|
|
2726
|
+
if (points.length === 1) {
|
|
2727
|
+
ctx.beginPath();
|
|
2728
|
+
ctx.arc(points[0].x, points[0].y, strokeWidth / 2, 0, Math.PI * 2);
|
|
2729
|
+
ctx.fill();
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
for (let i = 1; i < points.length; i++) {
|
|
2733
|
+
const a = points[i - 1];
|
|
2734
|
+
const b = points[i];
|
|
2735
|
+
const mid = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
2736
|
+
ctx.lineWidth = b.w;
|
|
2737
|
+
ctx.beginPath();
|
|
2738
|
+
ctx.moveTo(a.mx ?? a.x, a.my ?? a.y);
|
|
2739
|
+
ctx.quadraticCurveTo(a.x, a.y, mid.x, mid.y);
|
|
2740
|
+
ctx.stroke();
|
|
2741
|
+
b.mx = mid.x;
|
|
2742
|
+
b.my = mid.y;
|
|
2743
|
+
}
|
|
2744
|
+
},
|
|
2745
|
+
[ink, strokeWidth]
|
|
2746
|
+
);
|
|
2747
|
+
const redraw = useCallback(() => {
|
|
2748
|
+
const canvas = canvasRef.current;
|
|
2749
|
+
if (!canvas) return;
|
|
2750
|
+
const ctx = canvas.getContext("2d");
|
|
2751
|
+
if (!ctx) return;
|
|
2752
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2753
|
+
ctx.lineCap = "round";
|
|
2754
|
+
ctx.lineJoin = "round";
|
|
2755
|
+
strokes.current.forEach((s) => drawStroke(ctx, s.map(({ x, y, w }) => ({ x, y, w }))));
|
|
2756
|
+
}, [drawStroke]);
|
|
2757
|
+
useEffect(() => {
|
|
2758
|
+
const canvas = canvasRef.current;
|
|
2759
|
+
if (!canvas) return void 0;
|
|
2760
|
+
const fit = () => {
|
|
2761
|
+
var _a;
|
|
2762
|
+
const dpr = window.devicePixelRatio || 1;
|
|
2763
|
+
const { width, height: h } = canvas.getBoundingClientRect();
|
|
2764
|
+
canvas.width = Math.max(1, Math.round(width * dpr));
|
|
2765
|
+
canvas.height = Math.max(1, Math.round(h * dpr));
|
|
2766
|
+
(_a = canvas.getContext("2d")) == null ? void 0 : _a.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
2767
|
+
redraw();
|
|
2768
|
+
};
|
|
2769
|
+
fit();
|
|
2770
|
+
if (typeof ResizeObserver === "undefined") return void 0;
|
|
2771
|
+
const ro = new ResizeObserver(fit);
|
|
2772
|
+
ro.observe(canvas);
|
|
2773
|
+
return () => ro.disconnect();
|
|
2774
|
+
}, [redraw]);
|
|
2775
|
+
const emit = () => onChange == null ? void 0 : onChange(strokes.current.length ? canvasRef.current.toDataURL("image/png") : null);
|
|
2776
|
+
const point = (e) => {
|
|
2777
|
+
const rect = canvasRef.current.getBoundingClientRect();
|
|
2778
|
+
return { x: e.clientX - rect.left, y: e.clientY - rect.top, t: e.timeStamp };
|
|
2779
|
+
};
|
|
2780
|
+
const onPointerDown = (e) => {
|
|
2781
|
+
if (disabled || e.pointerType === "mouse" && e.button !== 0) return;
|
|
2782
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
2783
|
+
const p = { ...point(e), w: strokeWidth };
|
|
2784
|
+
drawing.current = [p];
|
|
2785
|
+
const ctx = canvasRef.current.getContext("2d");
|
|
2786
|
+
ctx.lineCap = "round";
|
|
2787
|
+
ctx.lineJoin = "round";
|
|
2788
|
+
drawStroke(ctx, [p]);
|
|
2789
|
+
};
|
|
2790
|
+
const onPointerMove = (e) => {
|
|
2791
|
+
const stroke = drawing.current;
|
|
2792
|
+
if (!stroke) return;
|
|
2793
|
+
const prev = stroke[stroke.length - 1];
|
|
2794
|
+
const p = point(e);
|
|
2795
|
+
const dist = Math.hypot(p.x - prev.x, p.y - prev.y);
|
|
2796
|
+
if (dist < 1.5) return;
|
|
2797
|
+
const speed = dist / Math.max(1, p.t - prev.t);
|
|
2798
|
+
const target = strokeWidth * Math.max(0.45, Math.min(1.35, 1.4 - speed * 0.45));
|
|
2799
|
+
p.w = prev.w * 0.6 + target * 0.4;
|
|
2800
|
+
stroke.push(p);
|
|
2801
|
+
drawStroke(canvasRef.current.getContext("2d"), stroke.slice(-3));
|
|
2802
|
+
};
|
|
2803
|
+
const onPointerUp = () => {
|
|
2804
|
+
if (!drawing.current) return;
|
|
2805
|
+
strokes.current = [...strokes.current, drawing.current];
|
|
2806
|
+
drawing.current = null;
|
|
2807
|
+
setCount(strokes.current.length);
|
|
2808
|
+
emit();
|
|
2809
|
+
};
|
|
2810
|
+
const undo = () => {
|
|
2811
|
+
strokes.current = strokes.current.slice(0, -1);
|
|
2812
|
+
setCount(strokes.current.length);
|
|
2813
|
+
redraw();
|
|
2814
|
+
emit();
|
|
2815
|
+
};
|
|
2816
|
+
const clear = () => {
|
|
2817
|
+
strokes.current = [];
|
|
2818
|
+
setCount(0);
|
|
2819
|
+
redraw();
|
|
2820
|
+
emit();
|
|
2821
|
+
};
|
|
2822
|
+
useImperativeHandle(ref, () => ({
|
|
2823
|
+
clear,
|
|
2824
|
+
undo,
|
|
2825
|
+
isEmpty: () => strokes.current.length === 0,
|
|
2826
|
+
toDataURL: (type = "image/png") => canvasRef.current.toDataURL(type)
|
|
2827
|
+
}));
|
|
2828
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("ru-signature", disabled && "ru-signature--disabled", className), style, ...rest, children: [
|
|
2829
|
+
/* @__PURE__ */ jsxs("div", { className: "ru-signature__canvas-wrap", style: { height }, children: [
|
|
2830
|
+
count === 0 && placeholder && /* @__PURE__ */ jsx("span", { className: "ru-signature__placeholder", "aria-hidden": "true", children: placeholder }),
|
|
2831
|
+
/* @__PURE__ */ jsx(
|
|
2832
|
+
"canvas",
|
|
2833
|
+
{
|
|
2834
|
+
ref: canvasRef,
|
|
2835
|
+
className: "ru-signature__canvas",
|
|
2836
|
+
role: "img",
|
|
2837
|
+
"aria-label": count ? "Signature drawn" : "Signature pad, empty",
|
|
2838
|
+
onPointerDown,
|
|
2839
|
+
onPointerMove,
|
|
2840
|
+
onPointerUp,
|
|
2841
|
+
onPointerCancel: onPointerUp
|
|
2842
|
+
}
|
|
2843
|
+
),
|
|
2844
|
+
/* @__PURE__ */ jsx("span", { className: "ru-signature__baseline", "aria-hidden": "true" })
|
|
2845
|
+
] }),
|
|
2846
|
+
(showControls || actions) && /* @__PURE__ */ jsxs("div", { className: "ru-signature__bar", children: [
|
|
2847
|
+
showControls && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2848
|
+
/* @__PURE__ */ jsxs("button", { type: "button", className: "ru-signature__btn", onClick: undo, disabled: disabled || count === 0, children: [
|
|
2849
|
+
/* @__PURE__ */ jsx(UndoIcon, {}),
|
|
2850
|
+
undoLabel
|
|
2851
|
+
] }),
|
|
2852
|
+
/* @__PURE__ */ jsx("span", { className: "ru-signature__sep", "aria-hidden": "true" }),
|
|
2853
|
+
/* @__PURE__ */ jsxs("button", { type: "button", className: "ru-signature__btn", onClick: clear, disabled: disabled || count === 0, children: [
|
|
2854
|
+
/* @__PURE__ */ jsx(EraserIcon, {}),
|
|
2855
|
+
clearLabel
|
|
2856
|
+
] })
|
|
2857
|
+
] }),
|
|
2858
|
+
actions && /* @__PURE__ */ jsx("div", { className: "ru-signature__actions", children: actions })
|
|
2859
|
+
] })
|
|
2860
|
+
] });
|
|
2861
|
+
});
|
|
2862
|
+
const statusLabels = {
|
|
2863
|
+
running: "In progress",
|
|
2864
|
+
queued: "Queued",
|
|
2865
|
+
pending: "Not started",
|
|
2866
|
+
partial: "Partly done",
|
|
2867
|
+
complete: "Complete",
|
|
2868
|
+
error: "Failed"
|
|
2869
|
+
};
|
|
2870
|
+
function StatusIcon({ status = "pending", progress = 50, size = 20, label, className, style, ...rest }) {
|
|
2871
|
+
const r = 8;
|
|
2872
|
+
const c = 2 * Math.PI * r;
|
|
2873
|
+
return /* @__PURE__ */ jsx(
|
|
2874
|
+
"span",
|
|
2875
|
+
{
|
|
2876
|
+
role: "img",
|
|
2877
|
+
"aria-label": label ?? statusLabels[status] ?? status,
|
|
2878
|
+
className: cn("ru-status-icon", `ru-status-icon--${status}`, className),
|
|
2879
|
+
style: { width: size, height: size, ...style },
|
|
2880
|
+
...rest,
|
|
2881
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", width: size, height: size, "aria-hidden": "true", children: status === "complete" || status === "error" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2882
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "10", className: "ru-status-icon__fill" }),
|
|
2883
|
+
status === "complete" ? /* @__PURE__ */ jsx("path", { d: "m6 10.2 2.6 2.6L14 7.4", className: "ru-status-icon__glyph" }) : /* @__PURE__ */ jsx("path", { d: "m7 7 6 6M13 7l-6 6", className: "ru-status-icon__glyph" })
|
|
2884
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2885
|
+
/* @__PURE__ */ jsx(
|
|
2886
|
+
"circle",
|
|
2887
|
+
{
|
|
2888
|
+
cx: "10",
|
|
2889
|
+
cy: "10",
|
|
2890
|
+
r,
|
|
2891
|
+
className: "ru-status-icon__ring",
|
|
2892
|
+
strokeDasharray: status === "running" || status === "queued" ? "2.6 2.4" : void 0
|
|
2893
|
+
}
|
|
2894
|
+
),
|
|
2895
|
+
status === "partial" && /* @__PURE__ */ jsx(
|
|
2896
|
+
"circle",
|
|
2897
|
+
{
|
|
2898
|
+
cx: "10",
|
|
2899
|
+
cy: "10",
|
|
2900
|
+
r,
|
|
2901
|
+
className: "ru-status-icon__arc",
|
|
2902
|
+
strokeDasharray: c,
|
|
2903
|
+
strokeDashoffset: c * (1 - Math.min(100, Math.max(0, progress)) / 100)
|
|
2904
|
+
}
|
|
2905
|
+
)
|
|
2906
|
+
] }) })
|
|
2907
|
+
}
|
|
2908
|
+
);
|
|
2909
|
+
}
|
|
2910
|
+
function StepList({ items, connector = true, dividers = true, className, ...rest }) {
|
|
2911
|
+
return /* @__PURE__ */ jsx("ol", { className: cn("ru-step-list", !dividers && "ru-step-list--plain", className), ...rest, children: items.map((item, i) => /* @__PURE__ */ jsxs(
|
|
2912
|
+
"li",
|
|
2913
|
+
{
|
|
2914
|
+
className: cn("ru-step-list__item", `ru-step-list__item--${item.status ?? "pending"}`),
|
|
2915
|
+
"aria-current": item.status === "running" ? "step" : void 0,
|
|
2916
|
+
children: [
|
|
2917
|
+
/* @__PURE__ */ jsxs("span", { className: "ru-step-list__rail", children: [
|
|
2918
|
+
/* @__PURE__ */ jsx("span", { className: "ru-step-list__icon", children: /* @__PURE__ */ jsx(StatusIcon, { status: item.status, progress: item.progress, size: 22 }) }),
|
|
2919
|
+
connector && i < items.length - 1 && /* @__PURE__ */ jsx("span", { className: "ru-step-list__line", "aria-hidden": "true" })
|
|
2920
|
+
] }),
|
|
2921
|
+
/* @__PURE__ */ jsxs("div", { className: "ru-step-list__body", children: [
|
|
2922
|
+
/* @__PURE__ */ jsx("div", { className: "ru-step-list__title", children: item.title }),
|
|
2923
|
+
item.description && /* @__PURE__ */ jsx("div", { className: "ru-step-list__desc", children: item.description })
|
|
2924
|
+
] }),
|
|
2925
|
+
item.meta && /* @__PURE__ */ jsx("div", { className: "ru-step-list__meta", children: item.meta })
|
|
2926
|
+
]
|
|
2927
|
+
},
|
|
2928
|
+
item.key ?? i
|
|
2929
|
+
)) });
|
|
2930
|
+
}
|
|
2931
|
+
function StackedProgress({ segments, max, size = "md", showLegend = true, gap = 4, label, className, ...rest }) {
|
|
2932
|
+
const total = max ?? segments.reduce((sum, s) => sum + s.value, 0);
|
|
2933
|
+
const summary = segments.map((s) => `${s.label} ${Math.round(s.value / total * 100)}%`).join(", ");
|
|
2934
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("ru-stacked", `ru-stacked--${size}`, className), ...rest, children: [
|
|
2935
|
+
(label || showLegend) && /* @__PURE__ */ jsxs("div", { className: "ru-stacked__head", children: [
|
|
2936
|
+
label && /* @__PURE__ */ jsx("span", { className: "ru-stacked__label", children: label }),
|
|
2937
|
+
showLegend && /* @__PURE__ */ jsx("ul", { className: "ru-stacked__legend", children: segments.map((s) => /* @__PURE__ */ jsxs("li", { children: [
|
|
2938
|
+
/* @__PURE__ */ jsx("span", { className: "ru-stacked__dot", style: { background: s.color } }),
|
|
2939
|
+
s.label
|
|
2940
|
+
] }, s.label)) })
|
|
2941
|
+
] }),
|
|
2942
|
+
/* @__PURE__ */ jsxs("div", { className: "ru-stacked__bar", role: "img", "aria-label": summary, style: { gap }, children: [
|
|
2943
|
+
segments.map((s) => /* @__PURE__ */ jsx("span", { className: "ru-stacked__seg", title: `${s.label}: ${s.value}`, style: { flexGrow: s.value, background: s.color } }, s.label)),
|
|
2944
|
+
max !== void 0 && total > segments.reduce((sum, s) => sum + s.value, 0) && /* @__PURE__ */ jsx("span", { className: "ru-stacked__rest", style: { flexGrow: total - segments.reduce((sum, s) => sum + s.value, 0) } })
|
|
2945
|
+
] })
|
|
2946
|
+
] });
|
|
2947
|
+
}
|
|
2948
|
+
const semanticColors = /* @__PURE__ */ new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
|
|
2949
|
+
function IconTile({
|
|
2950
|
+
color = "primary",
|
|
2951
|
+
size = 48,
|
|
2952
|
+
radius,
|
|
2953
|
+
badge,
|
|
2954
|
+
glow = true,
|
|
2955
|
+
label,
|
|
2956
|
+
className,
|
|
2957
|
+
style,
|
|
2958
|
+
children,
|
|
2959
|
+
...rest
|
|
2960
|
+
}) {
|
|
2961
|
+
const ink = semanticColors.has(color) ? `var(--ru-${color})` : color;
|
|
2962
|
+
return /* @__PURE__ */ jsxs(
|
|
2963
|
+
"span",
|
|
2964
|
+
{
|
|
2965
|
+
className: cn("ru-icon-tile", glow && "ru-icon-tile--glow", className),
|
|
2966
|
+
style: { "--_c": ink, "--_s": `${size}px`, ...radius !== void 0 && { "--_r": typeof radius === "number" ? `${radius}px` : radius }, ...style },
|
|
2967
|
+
...rest,
|
|
2968
|
+
children: [
|
|
2969
|
+
/* @__PURE__ */ jsx("span", { className: "ru-icon-tile__face", role: label ? "img" : void 0, "aria-label": label, "aria-hidden": label ? void 0 : true, children }),
|
|
2970
|
+
badge && /* @__PURE__ */ jsx("span", { className: "ru-icon-tile__badge", "aria-hidden": "true", children: badge })
|
|
2971
|
+
]
|
|
2972
|
+
}
|
|
2973
|
+
);
|
|
2974
|
+
}
|
|
2055
2975
|
export {
|
|
2056
2976
|
Accordion,
|
|
2057
2977
|
Alert,
|
|
@@ -2067,10 +2987,15 @@ export {
|
|
|
2067
2987
|
CardFooter,
|
|
2068
2988
|
CardHeader,
|
|
2069
2989
|
Checkbox,
|
|
2990
|
+
DateTile,
|
|
2070
2991
|
Divider,
|
|
2992
|
+
DotText,
|
|
2071
2993
|
Drawer,
|
|
2072
2994
|
Dropdown,
|
|
2995
|
+
Dropzone,
|
|
2996
|
+
FileItem,
|
|
2073
2997
|
FrameCard,
|
|
2998
|
+
IconTile,
|
|
2074
2999
|
Input,
|
|
2075
3000
|
Kbd,
|
|
2076
3001
|
Modal,
|
|
@@ -2078,12 +3003,19 @@ export {
|
|
|
2078
3003
|
Pagination,
|
|
2079
3004
|
Pattern,
|
|
2080
3005
|
Progress,
|
|
3006
|
+
ProgressRing,
|
|
2081
3007
|
Radio,
|
|
2082
3008
|
RadioGroup,
|
|
3009
|
+
SegmentedControl,
|
|
2083
3010
|
Select,
|
|
3011
|
+
SignaturePad,
|
|
2084
3012
|
Skeleton,
|
|
2085
3013
|
Spinner,
|
|
2086
3014
|
SpotlightCard,
|
|
3015
|
+
StackedProgress,
|
|
3016
|
+
Stat,
|
|
3017
|
+
StatusIcon,
|
|
3018
|
+
StepList,
|
|
2087
3019
|
Switch,
|
|
2088
3020
|
Table,
|
|
2089
3021
|
Tabs,
|
|
@@ -2091,6 +3023,7 @@ export {
|
|
|
2091
3023
|
ToastProvider,
|
|
2092
3024
|
Tooltip,
|
|
2093
3025
|
cn,
|
|
3026
|
+
formatBytes,
|
|
2094
3027
|
useClickOutside,
|
|
2095
3028
|
useControllableState,
|
|
2096
3029
|
useDisclosure,
|