@underverse-ui/underverse 0.1.38 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +511 -155
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -3
- package/dist/index.d.ts +41 -3
- package/dist/index.js +545 -189
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5333,19 +5333,24 @@ var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
|
5333
5333
|
var pad = (n) => n.toString().padStart(2, "0");
|
|
5334
5334
|
function parseTime(input, fmt = "24", includeSeconds) {
|
|
5335
5335
|
if (!input) return null;
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5336
|
+
try {
|
|
5337
|
+
const s = input.trim().toUpperCase();
|
|
5338
|
+
const ampm = s.endsWith("AM") || s.endsWith("PM");
|
|
5339
|
+
const clean = s.replace(/\s*(AM|PM)\s*$/, "");
|
|
5340
|
+
const segs = clean.split(":");
|
|
5341
|
+
const h = Number(segs[0]);
|
|
5342
|
+
const m = Number(segs[1] ?? 0);
|
|
5343
|
+
const sec = Number(segs[2] ?? 0);
|
|
5344
|
+
if (Number.isNaN(h) || Number.isNaN(m) || Number.isNaN(sec)) return null;
|
|
5345
|
+
if (fmt === "12" || ampm) {
|
|
5346
|
+
const p = s.endsWith("PM") ? "PM" : "AM";
|
|
5347
|
+
return { h: Math.max(1, Math.min(12, h)), m: Math.max(0, Math.min(59, m)), s: Math.max(0, Math.min(59, sec)), p };
|
|
5348
|
+
}
|
|
5349
|
+
return { h: Math.max(0, Math.min(23, h)), m: Math.max(0, Math.min(59, m)), s: Math.max(0, Math.min(59, sec)) };
|
|
5350
|
+
} catch (error) {
|
|
5351
|
+
console.error("Error parsing time:", error);
|
|
5352
|
+
return null;
|
|
5353
|
+
}
|
|
5349
5354
|
}
|
|
5350
5355
|
function formatTime2({ h, m, s, p }, fmt, includeSeconds) {
|
|
5351
5356
|
if (fmt === "12") {
|
|
@@ -5381,6 +5386,16 @@ function TimePicker({
|
|
|
5381
5386
|
showNow = false,
|
|
5382
5387
|
showPresets = false,
|
|
5383
5388
|
allowManualInput = false,
|
|
5389
|
+
customPresets = [],
|
|
5390
|
+
minTime,
|
|
5391
|
+
maxTime,
|
|
5392
|
+
disabledTimes,
|
|
5393
|
+
error,
|
|
5394
|
+
success,
|
|
5395
|
+
helperText,
|
|
5396
|
+
animate = true,
|
|
5397
|
+
onOpen,
|
|
5398
|
+
onClose,
|
|
5384
5399
|
className,
|
|
5385
5400
|
...rest
|
|
5386
5401
|
}) {
|
|
@@ -5390,14 +5405,118 @@ function TimePicker({
|
|
|
5390
5405
|
const [open, setOpen] = React22.useState(false);
|
|
5391
5406
|
const [parts, setParts] = React22.useState(initial);
|
|
5392
5407
|
const [manualInput, setManualInput] = React22.useState("");
|
|
5408
|
+
const [focusedColumn, setFocusedColumn] = React22.useState(null);
|
|
5409
|
+
const hourScrollRef = React22.useRef(null);
|
|
5410
|
+
const minuteScrollRef = React22.useRef(null);
|
|
5411
|
+
const secondScrollRef = React22.useRef(null);
|
|
5393
5412
|
React22.useEffect(() => {
|
|
5394
5413
|
if (isControlled) {
|
|
5395
5414
|
const parsed = parseTime(value, format, includeSeconds);
|
|
5396
5415
|
if (parsed) setParts(parsed);
|
|
5397
5416
|
}
|
|
5398
5417
|
}, [value, isControlled, format, includeSeconds]);
|
|
5418
|
+
React22.useEffect(() => {
|
|
5419
|
+
if (!open) return;
|
|
5420
|
+
const scrollToSelected = (ref, targetValue, step) => {
|
|
5421
|
+
if (!ref.current) return;
|
|
5422
|
+
const buttons = ref.current.querySelectorAll("button");
|
|
5423
|
+
const targetIndex = Math.floor(targetValue / step);
|
|
5424
|
+
const targetButton = buttons[targetIndex];
|
|
5425
|
+
if (targetButton) {
|
|
5426
|
+
targetButton.scrollIntoView({ behavior: animate ? "smooth" : "auto", block: "center" });
|
|
5427
|
+
}
|
|
5428
|
+
};
|
|
5429
|
+
setTimeout(() => {
|
|
5430
|
+
scrollToSelected(hourScrollRef, parts.h, 1);
|
|
5431
|
+
scrollToSelected(minuteScrollRef, parts.m, minuteStep);
|
|
5432
|
+
if (includeSeconds) scrollToSelected(secondScrollRef, parts.s, secondStep);
|
|
5433
|
+
}, 50);
|
|
5434
|
+
}, [open, parts.h, parts.m, parts.s, minuteStep, secondStep, includeSeconds, animate]);
|
|
5435
|
+
const isTimeDisabled = React22.useCallback((timeStr) => {
|
|
5436
|
+
if (!disabledTimes) return false;
|
|
5437
|
+
if (typeof disabledTimes === "function") return disabledTimes(timeStr);
|
|
5438
|
+
return disabledTimes.includes(timeStr);
|
|
5439
|
+
}, [disabledTimes]);
|
|
5440
|
+
const isTimeInRange = React22.useCallback((timeStr) => {
|
|
5441
|
+
if (!minTime && !maxTime) return true;
|
|
5442
|
+
const parsed = parseTime(timeStr, format, includeSeconds);
|
|
5443
|
+
if (!parsed) return true;
|
|
5444
|
+
if (minTime) {
|
|
5445
|
+
const min = parseTime(minTime, format, includeSeconds);
|
|
5446
|
+
if (min) {
|
|
5447
|
+
const currentMinutes = parsed.h * 60 + parsed.m;
|
|
5448
|
+
const minMinutes = min.h * 60 + min.m;
|
|
5449
|
+
if (currentMinutes < minMinutes) return false;
|
|
5450
|
+
}
|
|
5451
|
+
}
|
|
5452
|
+
if (maxTime) {
|
|
5453
|
+
const max = parseTime(maxTime, format, includeSeconds);
|
|
5454
|
+
if (max) {
|
|
5455
|
+
const currentMinutes = parsed.h * 60 + parsed.m;
|
|
5456
|
+
const maxMinutes = max.h * 60 + max.m;
|
|
5457
|
+
if (currentMinutes > maxMinutes) return false;
|
|
5458
|
+
}
|
|
5459
|
+
}
|
|
5460
|
+
return true;
|
|
5461
|
+
}, [minTime, maxTime, format, includeSeconds]);
|
|
5399
5462
|
const emit = (next) => {
|
|
5400
|
-
|
|
5463
|
+
const timeStr = next ? formatTime2(next, format, includeSeconds) : void 0;
|
|
5464
|
+
if (timeStr && !isTimeInRange(timeStr)) return;
|
|
5465
|
+
if (timeStr && isTimeDisabled(timeStr)) return;
|
|
5466
|
+
onChange?.(timeStr);
|
|
5467
|
+
};
|
|
5468
|
+
const handleOpenChange = (newOpen) => {
|
|
5469
|
+
setOpen(newOpen);
|
|
5470
|
+
if (newOpen) {
|
|
5471
|
+
onOpen?.();
|
|
5472
|
+
} else {
|
|
5473
|
+
onClose?.();
|
|
5474
|
+
setFocusedColumn(null);
|
|
5475
|
+
}
|
|
5476
|
+
};
|
|
5477
|
+
const handleKeyDown = (e, column) => {
|
|
5478
|
+
if (!["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End", "PageUp", "PageDown"].includes(e.key)) return;
|
|
5479
|
+
e.preventDefault();
|
|
5480
|
+
let newParts = { ...parts };
|
|
5481
|
+
switch (column) {
|
|
5482
|
+
case "hour":
|
|
5483
|
+
if (e.key === "ArrowUp") newParts.h = format === "24" ? (parts.h + 1) % 24 : parts.h % 12 + 1;
|
|
5484
|
+
if (e.key === "ArrowDown") newParts.h = format === "24" ? (parts.h - 1 + 24) % 24 : (parts.h - 2 + 12) % 12 + 1;
|
|
5485
|
+
if (e.key === "Home") newParts.h = format === "24" ? 0 : 1;
|
|
5486
|
+
if (e.key === "End") newParts.h = format === "24" ? 23 : 12;
|
|
5487
|
+
if (e.key === "PageUp") newParts.h = format === "24" ? (parts.h + 6) % 24 : parts.h % 12 + 3;
|
|
5488
|
+
if (e.key === "PageDown") newParts.h = format === "24" ? (parts.h - 6 + 24) % 24 : (parts.h - 4 + 12) % 12 + 1;
|
|
5489
|
+
if (e.key === "ArrowRight") setFocusedColumn("minute");
|
|
5490
|
+
break;
|
|
5491
|
+
case "minute":
|
|
5492
|
+
if (e.key === "ArrowUp") newParts.m = (parts.m + minuteStep) % 60;
|
|
5493
|
+
if (e.key === "ArrowDown") newParts.m = (parts.m - minuteStep + 60) % 60;
|
|
5494
|
+
if (e.key === "Home") newParts.m = 0;
|
|
5495
|
+
if (e.key === "End") newParts.m = 59 - 59 % minuteStep;
|
|
5496
|
+
if (e.key === "PageUp") newParts.m = (parts.m + minuteStep * 3) % 60;
|
|
5497
|
+
if (e.key === "PageDown") newParts.m = (parts.m - minuteStep * 3 + 60) % 60;
|
|
5498
|
+
if (e.key === "ArrowLeft") setFocusedColumn("hour");
|
|
5499
|
+
if (e.key === "ArrowRight") setFocusedColumn(includeSeconds ? "second" : format === "12" ? "period" : null);
|
|
5500
|
+
break;
|
|
5501
|
+
case "second":
|
|
5502
|
+
if (e.key === "ArrowUp") newParts.s = (parts.s + secondStep) % 60;
|
|
5503
|
+
if (e.key === "ArrowDown") newParts.s = (parts.s - secondStep + 60) % 60;
|
|
5504
|
+
if (e.key === "Home") newParts.s = 0;
|
|
5505
|
+
if (e.key === "End") newParts.s = 59 - 59 % secondStep;
|
|
5506
|
+
if (e.key === "PageUp") newParts.s = (parts.s + secondStep * 3) % 60;
|
|
5507
|
+
if (e.key === "PageDown") newParts.s = (parts.s - secondStep * 3 + 60) % 60;
|
|
5508
|
+
if (e.key === "ArrowLeft") setFocusedColumn("minute");
|
|
5509
|
+
if (e.key === "ArrowRight" && format === "12") setFocusedColumn("period");
|
|
5510
|
+
break;
|
|
5511
|
+
case "period":
|
|
5512
|
+
if (e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Home" || e.key === "End") {
|
|
5513
|
+
newParts.p = newParts.p === "AM" ? "PM" : "AM";
|
|
5514
|
+
}
|
|
5515
|
+
if (e.key === "ArrowLeft") setFocusedColumn(includeSeconds ? "second" : "minute");
|
|
5516
|
+
break;
|
|
5517
|
+
}
|
|
5518
|
+
setParts(newParts);
|
|
5519
|
+
emit(newParts);
|
|
5401
5520
|
};
|
|
5402
5521
|
const setNow = () => {
|
|
5403
5522
|
const now2 = /* @__PURE__ */ new Date();
|
|
@@ -5427,6 +5546,16 @@ function TimePicker({
|
|
|
5427
5546
|
const handleManualInput = (input) => {
|
|
5428
5547
|
setManualInput(input);
|
|
5429
5548
|
const parsed = parseTime(input, format, includeSeconds);
|
|
5549
|
+
if (parsed) {
|
|
5550
|
+
const timeStr = formatTime2(parsed, format, includeSeconds);
|
|
5551
|
+
if (isTimeInRange(timeStr) && !isTimeDisabled(timeStr)) {
|
|
5552
|
+
setParts(parsed);
|
|
5553
|
+
emit(parsed);
|
|
5554
|
+
}
|
|
5555
|
+
}
|
|
5556
|
+
};
|
|
5557
|
+
const handleCustomPreset = (time) => {
|
|
5558
|
+
const parsed = parseTime(time, format, includeSeconds);
|
|
5430
5559
|
if (parsed) {
|
|
5431
5560
|
setParts(parsed);
|
|
5432
5561
|
emit(parsed);
|
|
@@ -5448,23 +5577,30 @@ function TimePicker({
|
|
|
5448
5577
|
{
|
|
5449
5578
|
type: "button",
|
|
5450
5579
|
disabled,
|
|
5580
|
+
"aria-label": "Select time",
|
|
5581
|
+
"aria-haspopup": "dialog",
|
|
5582
|
+
"aria-expanded": open,
|
|
5451
5583
|
className: cn(
|
|
5452
|
-
"flex w-full items-center justify-between border
|
|
5584
|
+
"flex w-full items-center justify-between border bg-background",
|
|
5453
5585
|
sz.height,
|
|
5454
5586
|
sz.padding,
|
|
5455
5587
|
sz.text,
|
|
5456
5588
|
radiusClass,
|
|
5457
5589
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
5458
5590
|
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
5459
|
-
"
|
|
5591
|
+
"transition-all duration-200",
|
|
5592
|
+
error && "border-destructive focus-visible:ring-destructive",
|
|
5593
|
+
success && "border-green-500 focus-visible:ring-green-500",
|
|
5594
|
+
!error && !success && "border-input hover:bg-accent/5",
|
|
5595
|
+
animate && !disabled && "hover:shadow-md",
|
|
5460
5596
|
className
|
|
5461
5597
|
),
|
|
5462
5598
|
children: [
|
|
5463
5599
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
5464
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Clock, { className: cn(sz.icon, "text-muted-foreground") }),
|
|
5600
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Clock, { className: cn(sz.icon, error ? "text-destructive" : success ? "text-green-500" : "text-muted-foreground") }),
|
|
5465
5601
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: cn("truncate", !value && !defaultValue && "text-muted-foreground"), children: value || defaultValue ? display : placeholder })
|
|
5466
5602
|
] }),
|
|
5467
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: cn("ml-2 transition-transform", open && "rotate-180"), children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("svg", { className: sz.icon, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) }) })
|
|
5603
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: cn("ml-2 transition-transform duration-200", open && "rotate-180"), children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("svg", { className: sz.icon, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) }) })
|
|
5468
5604
|
]
|
|
5469
5605
|
}
|
|
5470
5606
|
);
|
|
@@ -5484,111 +5620,217 @@ function TimePicker({
|
|
|
5484
5620
|
"button",
|
|
5485
5621
|
{
|
|
5486
5622
|
type: "button",
|
|
5487
|
-
className:
|
|
5623
|
+
className: cn(
|
|
5624
|
+
"px-2 py-1.5 text-xs rounded-md border border-border hover:bg-accent/10 capitalize transition-all",
|
|
5625
|
+
animate && "hover:scale-105 active:scale-95"
|
|
5626
|
+
),
|
|
5488
5627
|
onClick: () => setPreset(preset),
|
|
5628
|
+
"aria-label": `Set time to ${preset}`,
|
|
5489
5629
|
children: preset
|
|
5490
5630
|
},
|
|
5491
5631
|
preset
|
|
5492
5632
|
)) }),
|
|
5493
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5633
|
+
customPresets && customPresets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "grid grid-cols-2 gap-2", children: customPresets.map((preset, idx) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5634
|
+
"button",
|
|
5635
|
+
{
|
|
5636
|
+
type: "button",
|
|
5637
|
+
className: cn(
|
|
5638
|
+
"px-2 py-1.5 text-xs rounded-md border border-border hover:bg-accent/10 transition-all",
|
|
5639
|
+
animate && "hover:scale-105 active:scale-95"
|
|
5640
|
+
),
|
|
5641
|
+
onClick: () => handleCustomPreset(preset.time),
|
|
5642
|
+
"aria-label": `Set time to ${preset.label}`,
|
|
5643
|
+
children: preset.label
|
|
5644
|
+
},
|
|
5645
|
+
idx
|
|
5646
|
+
)) }),
|
|
5647
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex gap-3", children: [
|
|
5648
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex-1 min-w-[70px]", children: [
|
|
5649
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-xs font-semibold text-muted-foreground mb-2 text-center", children: "Hour" }),
|
|
5650
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5651
|
+
"div",
|
|
5498
5652
|
{
|
|
5499
|
-
|
|
5500
|
-
className:
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5653
|
+
ref: hourScrollRef,
|
|
5654
|
+
className: "max-h-48 overflow-y-auto pr-1 space-y-1 scrollbar-thin scroll-smooth",
|
|
5655
|
+
role: "listbox",
|
|
5656
|
+
"aria-label": "Select hour",
|
|
5657
|
+
tabIndex: focusedColumn === "hour" ? 0 : -1,
|
|
5658
|
+
onKeyDown: (e) => handleKeyDown(e, "hour"),
|
|
5659
|
+
onFocus: () => setFocusedColumn("hour"),
|
|
5660
|
+
children: hours.map((h) => {
|
|
5661
|
+
const isSelected = format === "24" && parts.h === h || format === "12" && (parts.h % 12 || 12) === (h % 12 || 12);
|
|
5662
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5663
|
+
"button",
|
|
5664
|
+
{
|
|
5665
|
+
type: "button",
|
|
5666
|
+
role: "option",
|
|
5667
|
+
"aria-selected": isSelected,
|
|
5668
|
+
className: cn(
|
|
5669
|
+
"w-full text-center px-3 py-2 rounded-md transition-all text-sm font-medium",
|
|
5670
|
+
"hover:bg-accent hover:scale-105 active:scale-95 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
5671
|
+
isSelected && "bg-primary text-primary-foreground shadow-md",
|
|
5672
|
+
!isSelected && "text-foreground/80",
|
|
5673
|
+
animate && "transition-transform duration-150"
|
|
5674
|
+
),
|
|
5675
|
+
onClick: () => {
|
|
5676
|
+
const nextH = format === "24" ? h : (parts.p === "PM" ? h % 12 + 12 : h % 12) % 24;
|
|
5677
|
+
const next = { ...parts, h: format === "24" ? h : nextH === 0 && parts.p === "AM" ? 0 : nextH || (parts.p === "PM" ? 12 : 0) };
|
|
5678
|
+
setParts(next);
|
|
5679
|
+
emit(next);
|
|
5680
|
+
},
|
|
5681
|
+
children: pad(h)
|
|
5682
|
+
},
|
|
5683
|
+
h
|
|
5684
|
+
);
|
|
5685
|
+
})
|
|
5686
|
+
}
|
|
5687
|
+
)
|
|
5514
5688
|
] }),
|
|
5515
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.
|
|
5516
|
-
|
|
5517
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "
|
|
5518
|
-
|
|
5689
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-px bg-border/50 self-stretch my-8" }),
|
|
5690
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex-1 min-w-[70px]", children: [
|
|
5691
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-xs font-semibold text-muted-foreground mb-2 text-center", children: "Min" }),
|
|
5692
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5693
|
+
"div",
|
|
5519
5694
|
{
|
|
5520
|
-
|
|
5521
|
-
className:
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5695
|
+
ref: minuteScrollRef,
|
|
5696
|
+
className: "max-h-48 overflow-y-auto pr-1 space-y-1 scrollbar-thin scroll-smooth",
|
|
5697
|
+
role: "listbox",
|
|
5698
|
+
"aria-label": "Select minute",
|
|
5699
|
+
tabIndex: focusedColumn === "minute" ? 0 : -1,
|
|
5700
|
+
onKeyDown: (e) => handleKeyDown(e, "minute"),
|
|
5701
|
+
onFocus: () => setFocusedColumn("minute"),
|
|
5702
|
+
children: minutes.map((m) => {
|
|
5703
|
+
const isSelected = parts.m === m;
|
|
5704
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5705
|
+
"button",
|
|
5706
|
+
{
|
|
5707
|
+
type: "button",
|
|
5708
|
+
role: "option",
|
|
5709
|
+
"aria-selected": isSelected,
|
|
5710
|
+
className: cn(
|
|
5711
|
+
"w-full text-center px-3 py-2 rounded-md transition-all text-sm font-medium",
|
|
5712
|
+
"hover:bg-accent hover:scale-105 active:scale-95 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
5713
|
+
isSelected && "bg-primary text-primary-foreground shadow-md",
|
|
5714
|
+
!isSelected && "text-foreground/80",
|
|
5715
|
+
animate && "transition-transform duration-150"
|
|
5716
|
+
),
|
|
5717
|
+
onClick: () => {
|
|
5718
|
+
const next = { ...parts, m };
|
|
5719
|
+
setParts(next);
|
|
5720
|
+
emit(next);
|
|
5721
|
+
},
|
|
5722
|
+
children: pad(m)
|
|
5723
|
+
},
|
|
5724
|
+
m
|
|
5725
|
+
);
|
|
5726
|
+
})
|
|
5727
|
+
}
|
|
5728
|
+
)
|
|
5534
5729
|
] }),
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.
|
|
5538
|
-
"
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5730
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-px bg-border/50 self-stretch my-8" }),
|
|
5731
|
+
includeSeconds && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
5732
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex-1 min-w-[70px]", children: [
|
|
5733
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-xs font-semibold text-muted-foreground mb-2 text-center", children: "Sec" }),
|
|
5734
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5735
|
+
"div",
|
|
5736
|
+
{
|
|
5737
|
+
ref: secondScrollRef,
|
|
5738
|
+
className: "max-h-48 overflow-y-auto pr-1 space-y-1 scrollbar-thin scroll-smooth",
|
|
5739
|
+
role: "listbox",
|
|
5740
|
+
"aria-label": "Select second",
|
|
5741
|
+
tabIndex: focusedColumn === "second" ? 0 : -1,
|
|
5742
|
+
onKeyDown: (e) => handleKeyDown(e, "second"),
|
|
5743
|
+
onFocus: () => setFocusedColumn("second"),
|
|
5744
|
+
children: seconds.map((s) => {
|
|
5745
|
+
const isSelected = parts.s === s;
|
|
5746
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5747
|
+
"button",
|
|
5748
|
+
{
|
|
5749
|
+
type: "button",
|
|
5750
|
+
role: "option",
|
|
5751
|
+
"aria-selected": isSelected,
|
|
5752
|
+
className: cn(
|
|
5753
|
+
"w-full text-center px-3 py-2 rounded-md transition-all text-sm font-medium",
|
|
5754
|
+
"hover:bg-accent hover:scale-105 active:scale-95 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
5755
|
+
isSelected && "bg-primary text-primary-foreground shadow-md",
|
|
5756
|
+
!isSelected && "text-foreground/80",
|
|
5757
|
+
animate && "transition-transform duration-150"
|
|
5758
|
+
),
|
|
5759
|
+
onClick: () => {
|
|
5760
|
+
const next = { ...parts, s };
|
|
5761
|
+
setParts(next);
|
|
5762
|
+
emit(next);
|
|
5763
|
+
},
|
|
5764
|
+
children: pad(s)
|
|
5765
|
+
},
|
|
5766
|
+
s
|
|
5767
|
+
);
|
|
5768
|
+
})
|
|
5769
|
+
}
|
|
5770
|
+
)
|
|
5771
|
+
] }),
|
|
5772
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-px bg-border/50 self-stretch my-8" })
|
|
5554
5773
|
] }),
|
|
5555
5774
|
format === "12" && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "w-20", children: [
|
|
5556
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-xs font-
|
|
5557
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5558
|
-
"
|
|
5775
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-xs font-semibold text-muted-foreground mb-2 text-center", children: "Period" }),
|
|
5776
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5777
|
+
"div",
|
|
5559
5778
|
{
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
),
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5779
|
+
className: "flex flex-col gap-2",
|
|
5780
|
+
role: "radiogroup",
|
|
5781
|
+
"aria-label": "Select AM or PM",
|
|
5782
|
+
tabIndex: focusedColumn === "period" ? 0 : -1,
|
|
5783
|
+
onKeyDown: (e) => handleKeyDown(e, "period"),
|
|
5784
|
+
onFocus: () => setFocusedColumn("period"),
|
|
5785
|
+
children: ["AM", "PM"].map((p) => {
|
|
5786
|
+
const isSelected = parts.p === p;
|
|
5787
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5788
|
+
"button",
|
|
5789
|
+
{
|
|
5790
|
+
type: "button",
|
|
5791
|
+
role: "radio",
|
|
5792
|
+
"aria-checked": isSelected,
|
|
5793
|
+
className: cn(
|
|
5794
|
+
"px-4 py-3 rounded-md transition-all text-sm font-semibold",
|
|
5795
|
+
"hover:bg-accent hover:scale-105 active:scale-95 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
5796
|
+
isSelected && "bg-primary text-primary-foreground shadow-md",
|
|
5797
|
+
!isSelected && "text-foreground/80 border border-border",
|
|
5798
|
+
animate && "transition-transform duration-150"
|
|
5799
|
+
),
|
|
5800
|
+
onClick: () => {
|
|
5801
|
+
const pVal = p;
|
|
5802
|
+
let hour = parts.h;
|
|
5803
|
+
if (pVal === "AM" && hour >= 12) hour -= 12;
|
|
5804
|
+
if (pVal === "PM" && hour < 12) hour += 12;
|
|
5805
|
+
const next = { ...parts, p: pVal, h: hour };
|
|
5806
|
+
setParts(next);
|
|
5807
|
+
emit(next);
|
|
5808
|
+
},
|
|
5809
|
+
children: p
|
|
5810
|
+
},
|
|
5811
|
+
p
|
|
5812
|
+
);
|
|
5813
|
+
})
|
|
5814
|
+
}
|
|
5815
|
+
)
|
|
5578
5816
|
] })
|
|
5579
5817
|
] }),
|
|
5580
|
-
(showNow || clearable) && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center justify-between gap-2 pt-
|
|
5818
|
+
(showNow || clearable) && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center justify-between gap-2 pt-3 border-t border-border", children: [
|
|
5581
5819
|
showNow && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
5582
5820
|
"button",
|
|
5583
5821
|
{
|
|
5584
5822
|
type: "button",
|
|
5585
|
-
className:
|
|
5823
|
+
className: cn(
|
|
5824
|
+
"px-3 py-2 text-xs rounded-md border border-border hover:bg-accent/10 transition-all flex items-center gap-2 font-medium",
|
|
5825
|
+
animate && "hover:scale-105 active:scale-95"
|
|
5826
|
+
),
|
|
5586
5827
|
onClick: () => {
|
|
5587
5828
|
setNow();
|
|
5588
|
-
if (variant === "compact")
|
|
5829
|
+
if (variant === "compact") handleOpenChange(false);
|
|
5589
5830
|
},
|
|
5831
|
+
"aria-label": "Set current time",
|
|
5590
5832
|
children: [
|
|
5591
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Clock, { className: "w-3 h-3" }),
|
|
5833
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Clock, { className: "w-3.5 h-3.5" }),
|
|
5592
5834
|
"Now"
|
|
5593
5835
|
]
|
|
5594
5836
|
}
|
|
@@ -5598,14 +5840,18 @@ function TimePicker({
|
|
|
5598
5840
|
"button",
|
|
5599
5841
|
{
|
|
5600
5842
|
type: "button",
|
|
5601
|
-
className:
|
|
5843
|
+
className: cn(
|
|
5844
|
+
"px-3 py-2 text-xs rounded-md border border-border hover:bg-destructive/10 hover:text-destructive transition-all flex items-center gap-2 font-medium",
|
|
5845
|
+
animate && "hover:scale-105 active:scale-95"
|
|
5846
|
+
),
|
|
5602
5847
|
onClick: () => {
|
|
5603
5848
|
setParts(initial);
|
|
5604
5849
|
emit(void 0);
|
|
5605
|
-
|
|
5850
|
+
handleOpenChange(false);
|
|
5606
5851
|
},
|
|
5852
|
+
"aria-label": "Clear selected time",
|
|
5607
5853
|
children: [
|
|
5608
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.X, { className: "w-3 h-3" }),
|
|
5854
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.X, { className: "w-3.5 h-3.5" }),
|
|
5609
5855
|
"Clear"
|
|
5610
5856
|
]
|
|
5611
5857
|
}
|
|
@@ -5622,23 +5868,47 @@ function TimePicker({
|
|
|
5622
5868
|
] });
|
|
5623
5869
|
}
|
|
5624
5870
|
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "w-full", ...rest, children: [
|
|
5625
|
-
label && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex items-center justify-between mb-1.5", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
5626
|
-
label,
|
|
5627
|
-
|
|
5628
|
-
|
|
5871
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex items-center justify-between mb-1.5", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
5872
|
+
"label",
|
|
5873
|
+
{
|
|
5874
|
+
className: cn(sz.label, "font-medium", disabled ? "text-muted-foreground" : "text-foreground", "cursor-pointer"),
|
|
5875
|
+
onClick: () => !disabled && handleOpenChange(true),
|
|
5876
|
+
children: [
|
|
5877
|
+
label,
|
|
5878
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-destructive ml-1", children: "*" })
|
|
5879
|
+
]
|
|
5880
|
+
}
|
|
5881
|
+
) }),
|
|
5629
5882
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
5630
5883
|
Popover,
|
|
5631
5884
|
{
|
|
5632
5885
|
trigger,
|
|
5633
5886
|
open,
|
|
5634
|
-
onOpenChange:
|
|
5887
|
+
onOpenChange: handleOpenChange,
|
|
5635
5888
|
placement: "bottom-start",
|
|
5636
5889
|
matchTriggerWidth: variant === "compact",
|
|
5637
5890
|
contentWidth,
|
|
5638
|
-
contentClassName: cn(
|
|
5891
|
+
contentClassName: cn(
|
|
5892
|
+
"p-4 rounded-lg border bg-popover shadow-xl backdrop-blur-md",
|
|
5893
|
+
error && "border-destructive",
|
|
5894
|
+
success && "border-green-500",
|
|
5895
|
+
!error && !success && "border-border",
|
|
5896
|
+
animate && "animate-in fade-in-0 zoom-in-95 duration-200"
|
|
5897
|
+
),
|
|
5639
5898
|
children: timePickerContent
|
|
5640
5899
|
}
|
|
5641
|
-
)
|
|
5900
|
+
),
|
|
5901
|
+
(error || success || helperText) && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: cn("mt-1.5 flex items-start gap-1.5", sz.label), children: [
|
|
5902
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-1.5 text-destructive", children: [
|
|
5903
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.X, { className: "w-3.5 h-3.5 flex-shrink-0" }),
|
|
5904
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { children: error })
|
|
5905
|
+
] }),
|
|
5906
|
+
success && !error && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-1.5 text-green-600", children: [
|
|
5907
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Check, { className: "w-3.5 h-3.5 flex-shrink-0" }),
|
|
5908
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { children: "Valid time selected" })
|
|
5909
|
+
] }),
|
|
5910
|
+
helperText && !error && !success && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-muted-foreground", children: helperText })
|
|
5911
|
+
] })
|
|
5642
5912
|
] });
|
|
5643
5913
|
}
|
|
5644
5914
|
|
|
@@ -8225,41 +8495,50 @@ var ListItem = React31.forwardRef(
|
|
|
8225
8495
|
setInternalExpanded(newExpanded);
|
|
8226
8496
|
}
|
|
8227
8497
|
};
|
|
8498
|
+
const headerProps = collapsible ? {
|
|
8499
|
+
role: "button",
|
|
8500
|
+
tabIndex: disabled ? -1 : 0,
|
|
8501
|
+
onClick: disabled ? void 0 : () => toggleExpanded(),
|
|
8502
|
+
onKeyDown: (e) => {
|
|
8503
|
+
if (disabled) return;
|
|
8504
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
8505
|
+
e.preventDefault();
|
|
8506
|
+
toggleExpanded();
|
|
8507
|
+
}
|
|
8508
|
+
}
|
|
8509
|
+
} : {};
|
|
8228
8510
|
const inner = /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
8229
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
"
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
toggleExpanded();
|
|
8511
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
8512
|
+
"div",
|
|
8513
|
+
{
|
|
8514
|
+
className: cn("flex items-center gap-3", padding, "group/item relative"),
|
|
8515
|
+
...headerProps,
|
|
8516
|
+
children: [
|
|
8517
|
+
avatar && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: cn("shrink-0", sz.avatar), children: typeof avatar === "string" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("img", { src: avatar, alt: "", className: cn("rounded-full object-cover", sz.avatar) }) : avatar }),
|
|
8518
|
+
Left && !avatar && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: cn("text-muted-foreground shrink-0", sz.icon), children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Left, { className: cn(sz.icon) }) }),
|
|
8519
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
8520
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
8521
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: cn(sz.label, "text-foreground font-medium truncate"), children: label }),
|
|
8522
|
+
badge && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: cn("px-2 py-0.5 rounded-full text-[11px] font-medium shrink-0", BADGE_VARIANTS[badgeVariant]), children: badge })
|
|
8523
|
+
] }),
|
|
8524
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: cn(sz.desc, "text-muted-foreground truncate mt-0.5"), children: description }),
|
|
8525
|
+
children && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "mt-1", children })
|
|
8526
|
+
] }),
|
|
8527
|
+
action && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "opacity-0 group-hover/item:opacity-100 transition-opacity shrink-0", children: action }),
|
|
8528
|
+
collapsible ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
8529
|
+
"span",
|
|
8530
|
+
{
|
|
8531
|
+
className: cn(
|
|
8532
|
+
"text-muted-foreground shrink-0 transition-transform cursor-pointer select-none",
|
|
8533
|
+
sz.icon,
|
|
8534
|
+
isExpanded && "rotate-90"
|
|
8535
|
+
),
|
|
8536
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react22.ChevronRight, { className: cn(sz.icon) })
|
|
8256
8537
|
}
|
|
8257
|
-
}
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
) : Right && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: cn("text-muted-foreground shrink-0", sz.icon), children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Right, { className: cn(sz.icon) }) })
|
|
8262
|
-
] }),
|
|
8538
|
+
) : Right && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: cn("text-muted-foreground shrink-0", sz.icon), children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Right, { className: cn(sz.icon) }) })
|
|
8539
|
+
]
|
|
8540
|
+
}
|
|
8541
|
+
),
|
|
8263
8542
|
collapsible && isExpanded && expandContent && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: cn("border-t border-border/50 bg-muted/20", padding, "pt-3"), children: expandContent })
|
|
8264
8543
|
] });
|
|
8265
8544
|
const baseCls = cn(
|
|
@@ -8272,14 +8551,24 @@ var ListItem = React31.forwardRef(
|
|
|
8272
8551
|
const A = as === "a" ? "a" : "a";
|
|
8273
8552
|
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(A, { ref, href, className: cn(baseCls, "block"), ...rest, children: inner });
|
|
8274
8553
|
}
|
|
8275
|
-
if (as === "button"
|
|
8554
|
+
if (as === "button" && !collapsible) {
|
|
8276
8555
|
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
8277
8556
|
"button",
|
|
8278
8557
|
{
|
|
8279
8558
|
ref,
|
|
8280
8559
|
type: "button",
|
|
8281
8560
|
className: cn(baseCls, "text-left block w-full"),
|
|
8282
|
-
|
|
8561
|
+
...rest,
|
|
8562
|
+
children: inner
|
|
8563
|
+
}
|
|
8564
|
+
);
|
|
8565
|
+
}
|
|
8566
|
+
if (collapsible) {
|
|
8567
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
8568
|
+
"div",
|
|
8569
|
+
{
|
|
8570
|
+
ref,
|
|
8571
|
+
className: cn(baseCls, "text-left block w-full"),
|
|
8283
8572
|
...rest,
|
|
8284
8573
|
children: inner
|
|
8285
8574
|
}
|
|
@@ -8333,16 +8622,27 @@ function useWatermarkDataURL(opts) {
|
|
|
8333
8622
|
const drawText = () => {
|
|
8334
8623
|
ctx.clearRect(0, 0, tileW, tileH);
|
|
8335
8624
|
ctx.save();
|
|
8336
|
-
|
|
8625
|
+
if (pattern === "grid") {
|
|
8626
|
+
ctx.translate(width / 2, height / 2);
|
|
8627
|
+
} else {
|
|
8628
|
+
ctx.translate(tileW / 2, tileH / 2);
|
|
8629
|
+
}
|
|
8337
8630
|
if (pattern === "diagonal") {
|
|
8338
8631
|
ctx.rotate(rotate * Math.PI / 180);
|
|
8339
|
-
} else if (pattern === "straight") {
|
|
8632
|
+
} else if (pattern === "straight" || pattern === "grid") {
|
|
8340
8633
|
}
|
|
8341
8634
|
if (text) {
|
|
8342
8635
|
const textLines = Array.isArray(text) ? text : [text];
|
|
8343
8636
|
ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;
|
|
8344
8637
|
ctx.textAlign = "center";
|
|
8345
8638
|
ctx.textBaseline = "middle";
|
|
8639
|
+
if (opts.textShadow) {
|
|
8640
|
+
const shadowCol = opts.shadowColor || "rgba(255, 255, 255, 0.5)";
|
|
8641
|
+
ctx.shadowColor = shadowCol;
|
|
8642
|
+
ctx.shadowBlur = 8;
|
|
8643
|
+
ctx.shadowOffsetX = 0;
|
|
8644
|
+
ctx.shadowOffsetY = 0;
|
|
8645
|
+
}
|
|
8346
8646
|
if (gradient) {
|
|
8347
8647
|
const splitStops = (input) => {
|
|
8348
8648
|
const s = input.trim();
|
|
@@ -8424,9 +8724,19 @@ function useWatermarkDataURL(opts) {
|
|
|
8424
8724
|
return () => {
|
|
8425
8725
|
cancelled = true;
|
|
8426
8726
|
};
|
|
8427
|
-
}, [opts.text, opts.image, opts.width, opts.height, opts.gapX, opts.gapY, opts.rotate, opts.fontSize, opts.fontFamily, opts.fontWeight, opts.fontStyle, opts.color, opts.gradient, opts.pattern]);
|
|
8727
|
+
}, [opts.text, opts.image, opts.width, opts.height, opts.gapX, opts.gapY, opts.rotate, opts.fontSize, opts.fontFamily, opts.fontWeight, opts.fontStyle, opts.color, opts.gradient, opts.pattern, opts.textShadow, opts.shadowColor]);
|
|
8428
8728
|
return url;
|
|
8429
8729
|
}
|
|
8730
|
+
var getAnimationClass = (variant, visible) => {
|
|
8731
|
+
if (variant === "none") return "";
|
|
8732
|
+
const animations = {
|
|
8733
|
+
fade: visible ? "animate-in fade-in duration-300" : "animate-out fade-out duration-300",
|
|
8734
|
+
slide: visible ? "animate-in slide-in-from-top duration-500" : "animate-out slide-out-to-top duration-500",
|
|
8735
|
+
scale: visible ? "animate-in zoom-in duration-300" : "animate-out zoom-out duration-300",
|
|
8736
|
+
pulse: visible ? "animate-pulse" : "opacity-0"
|
|
8737
|
+
};
|
|
8738
|
+
return animations[variant] || "";
|
|
8739
|
+
};
|
|
8430
8740
|
var Watermark = ({
|
|
8431
8741
|
text: textProp,
|
|
8432
8742
|
image,
|
|
@@ -8450,13 +8760,40 @@ var Watermark = ({
|
|
|
8450
8760
|
pattern = "diagonal",
|
|
8451
8761
|
interactive = false,
|
|
8452
8762
|
animate = false,
|
|
8763
|
+
animationVariant = "fade",
|
|
8764
|
+
blur = false,
|
|
8765
|
+
blurAmount = 4,
|
|
8766
|
+
textShadow = false,
|
|
8767
|
+
shadowColor,
|
|
8768
|
+
darkMode = false,
|
|
8453
8769
|
overlayClassName,
|
|
8770
|
+
ariaLabel,
|
|
8454
8771
|
className,
|
|
8455
8772
|
style,
|
|
8456
8773
|
children,
|
|
8457
8774
|
...rest
|
|
8458
8775
|
}) => {
|
|
8459
8776
|
const [visible, setVisible] = React32.useState(true);
|
|
8777
|
+
const [isDark, setIsDark] = React32.useState(false);
|
|
8778
|
+
React32.useEffect(() => {
|
|
8779
|
+
if (!darkMode) return;
|
|
8780
|
+
const checkDarkMode = () => {
|
|
8781
|
+
const isDarkMode = document.documentElement.classList.contains("dark") || window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
8782
|
+
setIsDark(isDarkMode);
|
|
8783
|
+
};
|
|
8784
|
+
checkDarkMode();
|
|
8785
|
+
const observer = new MutationObserver(checkDarkMode);
|
|
8786
|
+
observer.observe(document.documentElement, {
|
|
8787
|
+
attributes: true,
|
|
8788
|
+
attributeFilter: ["class"]
|
|
8789
|
+
});
|
|
8790
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
8791
|
+
mediaQuery.addEventListener("change", checkDarkMode);
|
|
8792
|
+
return () => {
|
|
8793
|
+
observer.disconnect();
|
|
8794
|
+
mediaQuery.removeEventListener("change", checkDarkMode);
|
|
8795
|
+
};
|
|
8796
|
+
}, [darkMode]);
|
|
8460
8797
|
const presetConfig = preset ? PRESETS2[preset] : null;
|
|
8461
8798
|
const text = textProp ?? presetConfig?.text;
|
|
8462
8799
|
const color = colorProp ?? presetConfig?.color ?? "rgba(0,0,0,0.15)";
|
|
@@ -8464,6 +8801,7 @@ var Watermark = ({
|
|
|
8464
8801
|
const fontSize = fontSizeProp ?? presetConfig?.fontSize ?? 14;
|
|
8465
8802
|
const fontWeight = fontWeightProp ?? presetConfig?.fontWeight ?? "normal";
|
|
8466
8803
|
const fontStyle = fontStyleProp ?? "normal";
|
|
8804
|
+
const finalOpacity = darkMode && isDark ? opacity * 1.3 : opacity;
|
|
8467
8805
|
const dataURL = useWatermarkDataURL({
|
|
8468
8806
|
text,
|
|
8469
8807
|
image,
|
|
@@ -8478,17 +8816,19 @@ var Watermark = ({
|
|
|
8478
8816
|
fontStyle,
|
|
8479
8817
|
color,
|
|
8480
8818
|
gradient,
|
|
8481
|
-
pattern
|
|
8819
|
+
pattern,
|
|
8820
|
+
textShadow,
|
|
8821
|
+
shadowColor
|
|
8482
8822
|
});
|
|
8483
8823
|
const overlayStyle = {
|
|
8484
8824
|
position: fullPage ? "fixed" : "absolute",
|
|
8485
8825
|
top: 0,
|
|
8486
8826
|
left: 0,
|
|
8487
8827
|
zIndex,
|
|
8488
|
-
opacity: visible ?
|
|
8828
|
+
opacity: visible ? finalOpacity : 0,
|
|
8489
8829
|
backgroundRepeat: "repeat",
|
|
8490
8830
|
backgroundPosition: `${offsetLeft}px ${offsetTop}px`,
|
|
8491
|
-
transition: animate ? "opacity 0.3s ease" : void 0
|
|
8831
|
+
transition: animate && animationVariant === "fade" ? "opacity 0.3s ease" : void 0
|
|
8492
8832
|
};
|
|
8493
8833
|
if (fullPage) {
|
|
8494
8834
|
overlayStyle.right = 0;
|
|
@@ -8498,13 +8838,29 @@ var Watermark = ({
|
|
|
8498
8838
|
overlayStyle.height = "100%";
|
|
8499
8839
|
}
|
|
8500
8840
|
if (dataURL) overlayStyle.backgroundImage = `url(${dataURL})`;
|
|
8841
|
+
const animationClass = animate ? getAnimationClass(animationVariant, visible) : "";
|
|
8842
|
+
const blurClass = blur ? `backdrop-blur-[${blurAmount}px]` : "";
|
|
8501
8843
|
const overlay = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
8502
8844
|
"div",
|
|
8503
8845
|
{
|
|
8504
|
-
"
|
|
8505
|
-
|
|
8846
|
+
role: interactive ? "button" : void 0,
|
|
8847
|
+
"aria-label": ariaLabel || (interactive ? "Toggle watermark visibility" : "Watermark overlay"),
|
|
8848
|
+
"aria-hidden": !interactive,
|
|
8849
|
+
tabIndex: interactive ? 0 : void 0,
|
|
8850
|
+
className: cn(
|
|
8851
|
+
interactive ? "cursor-pointer" : "pointer-events-none",
|
|
8852
|
+
blurClass,
|
|
8853
|
+
animationClass,
|
|
8854
|
+
overlayClassName
|
|
8855
|
+
),
|
|
8506
8856
|
style: overlayStyle,
|
|
8507
|
-
onClick: interactive ? () => setVisible(!visible) : void 0
|
|
8857
|
+
onClick: interactive ? () => setVisible(!visible) : void 0,
|
|
8858
|
+
onKeyDown: interactive ? (e) => {
|
|
8859
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
8860
|
+
e.preventDefault();
|
|
8861
|
+
setVisible(!visible);
|
|
8862
|
+
}
|
|
8863
|
+
} : void 0
|
|
8508
8864
|
}
|
|
8509
8865
|
);
|
|
8510
8866
|
if (fullPage) {
|