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