@underverse-ui/underverse 0.1.38 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
- const s = input.trim().toUpperCase();
5337
- const ampm = s.endsWith("AM") || s.endsWith("PM");
5338
- const clean = s.replace(/\s*(AM|PM)\s*$/, "");
5339
- const segs = clean.split(":");
5340
- const h = Number(segs[0]);
5341
- const m = Number(segs[1] ?? 0);
5342
- const sec = Number(segs[2] ?? 0);
5343
- if (Number.isNaN(h) || Number.isNaN(m) || Number.isNaN(sec)) return null;
5344
- if (fmt === "12" || ampm) {
5345
- const p = s.endsWith("PM") ? "PM" : "AM";
5346
- 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 };
5347
- }
5348
- 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)) };
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
- onChange?.(next ? formatTime2(next, format, includeSeconds) : void 0);
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 border-input bg-background",
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
- "hover:bg-accent/5 transition-colors",
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: "px-2 py-1.5 text-xs rounded-md border border-border hover:bg-accent/10 capitalize transition-colors",
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.jsxs)("div", { className: "flex gap-2", children: [
5494
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex-1 min-w-[60px]", children: [
5495
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-xs font-medium text-muted-foreground mb-1.5", children: "Hour" }),
5496
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "max-h-48 overflow-y-auto pr-1 space-y-1 scrollbar-thin", children: hours.map((h) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
5497
- "button",
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
- type: "button",
5500
- className: cn(
5501
- "w-full text-center px-2 py-1.5 rounded-md hover:bg-accent transition-colors text-sm",
5502
- (format === "24" && parts.h === h || format === "12" && (parts.h % 12 || 12) === (h % 12 || 12)) && "bg-primary text-primary-foreground font-semibold"
5503
- ),
5504
- onClick: () => {
5505
- const nextH = format === "24" ? h : (parts.p === "PM" ? h % 12 + 12 : h % 12) % 24;
5506
- const next = { ...parts, h: format === "24" ? h : nextH === 0 && parts.p === "AM" ? 0 : nextH || (parts.p === "PM" ? 12 : 0) };
5507
- setParts(next);
5508
- emit(next);
5509
- },
5510
- children: pad(h)
5511
- },
5512
- h
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.jsxs)("div", { className: "flex-1 min-w-[60px]", children: [
5516
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-xs font-medium text-muted-foreground mb-1.5", children: "Min" }),
5517
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "max-h-48 overflow-y-auto pr-1 space-y-1 scrollbar-thin", children: minutes.map((m) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
5518
- "button",
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
- type: "button",
5521
- className: cn(
5522
- "w-full text-center px-2 py-1.5 rounded-md hover:bg-accent transition-colors text-sm",
5523
- parts.m === m && "bg-primary text-primary-foreground font-semibold"
5524
- ),
5525
- onClick: () => {
5526
- const next = { ...parts, m };
5527
- setParts(next);
5528
- emit(next);
5529
- },
5530
- children: pad(m)
5531
- },
5532
- m
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
- includeSeconds && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex-1 min-w-[60px]", children: [
5536
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-xs font-medium text-muted-foreground mb-1.5", children: "Sec" }),
5537
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "max-h-48 overflow-y-auto pr-1 space-y-1 scrollbar-thin", children: seconds.map((s) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
5538
- "button",
5539
- {
5540
- type: "button",
5541
- className: cn(
5542
- "w-full text-center px-2 py-1.5 rounded-md hover:bg-accent transition-colors text-sm",
5543
- parts.s === s && "bg-primary text-primary-foreground font-semibold"
5544
- ),
5545
- onClick: () => {
5546
- const next = { ...parts, s };
5547
- setParts(next);
5548
- emit(next);
5549
- },
5550
- children: pad(s)
5551
- },
5552
- s
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-medium text-muted-foreground mb-1.5", children: "Period" }),
5557
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex flex-col gap-1.5", children: ["AM", "PM"].map((p) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
5558
- "button",
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
- type: "button",
5561
- className: cn(
5562
- "px-3 py-2 rounded-md hover:bg-accent transition-colors text-sm font-medium",
5563
- parts.p === p && "bg-primary text-primary-foreground"
5564
- ),
5565
- onClick: () => {
5566
- const pVal = p;
5567
- let hour = parts.h;
5568
- if (pVal === "AM" && hour >= 12) hour -= 12;
5569
- if (pVal === "PM" && hour < 12) hour += 12;
5570
- const next = { ...parts, p: pVal, h: hour };
5571
- setParts(next);
5572
- emit(next);
5573
- },
5574
- children: p
5575
- },
5576
- p
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-2 border-t border-border", children: [
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: "px-3 py-1.5 text-xs rounded-md border border-border hover:bg-accent/10 transition-colors flex items-center gap-1.5",
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") setOpen(false);
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: "px-3 py-1.5 text-xs rounded-md border border-border hover:bg-destructive/10 transition-colors flex items-center gap-1.5",
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
- setOpen(false);
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)("label", { className: cn(sz.label, "font-medium", disabled ? "text-muted-foreground" : "text-foreground"), onClick: () => !disabled && setOpen(true), children: [
5626
- label,
5627
- required && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-destructive ml-1", children: "*" })
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: (o) => setOpen(o),
5887
+ onOpenChange: handleOpenChange,
5635
5888
  placement: "bottom-start",
5636
5889
  matchTriggerWidth: variant === "compact",
5637
5890
  contentWidth,
5638
- contentClassName: cn("p-3 rounded-lg border border-border bg-popover shadow-lg backdrop-blur-sm bg-popover/95"),
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)("div", { className: cn("flex items-center gap-3", padding, "group/item relative"), children: [
8230
- 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 }),
8231
- 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) }) }),
8232
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "min-w-0 flex-1", children: [
8233
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex items-center gap-2", children: [
8234
- label && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: cn(sz.label, "text-foreground font-medium truncate"), children: label }),
8235
- 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 })
8236
- ] }),
8237
- description && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: cn(sz.desc, "text-muted-foreground truncate mt-0.5"), children: description }),
8238
- children && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "mt-1", children })
8239
- ] }),
8240
- action && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "opacity-0 group-hover/item:opacity-100 transition-opacity shrink-0", children: action }),
8241
- collapsible ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
8242
- "span",
8243
- {
8244
- role: "button",
8245
- "aria-label": "Toggle",
8246
- tabIndex: 0,
8247
- onClick: (e) => {
8248
- e.stopPropagation();
8249
- toggleExpanded();
8250
- },
8251
- onKeyDown: (e) => {
8252
- if (e.key === "Enter" || e.key === " ") {
8253
- e.preventDefault();
8254
- e.stopPropagation();
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
- className: cn("text-muted-foreground shrink-0 transition-transform cursor-pointer select-none", sz.icon, isExpanded && "rotate-90"),
8259
- children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react22.ChevronRight, { className: cn(sz.icon) })
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" || collapsible) {
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
- onClick: collapsible ? toggleExpanded : void 0,
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
- ctx.translate(tileW / 2, tileH / 2);
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 ? opacity : 0,
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
- "aria-hidden": true,
8505
- className: cn("pointer-events-none", overlayClassName),
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) {