@icen.ai/ui 0.4.0 → 0.5.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.
Files changed (55) hide show
  1. package/dist/behaviors/back-top.mjs +84 -0
  2. package/dist/behaviors/command-palette.mjs +177 -0
  3. package/dist/behaviors/date-picker.mjs +228 -0
  4. package/dist/behaviors/input.mjs +170 -1
  5. package/dist/behaviors/notification.mjs +754 -0
  6. package/dist/behaviors/select.mjs +151 -22
  7. package/dist/behaviors/split-pane.mjs +102 -0
  8. package/dist/behaviors/upload.mjs +165 -14
  9. package/dist/components/back-top.css +88 -0
  10. package/dist/components/back-top.mjs +3 -0
  11. package/dist/components/badge.css +131 -0
  12. package/dist/components/badge.mjs +2 -0
  13. package/dist/components/breadcrumb.css +57 -0
  14. package/dist/components/card.css +68 -0
  15. package/dist/components/command-palette.css +246 -0
  16. package/dist/components/command-palette.mjs +3 -0
  17. package/dist/components/content.css +214 -0
  18. package/dist/components/copy.css +93 -0
  19. package/dist/components/copy.mjs +3 -0
  20. package/dist/components/date-picker.css +244 -0
  21. package/dist/components/date-picker.mjs +3 -0
  22. package/dist/components/empty.css +73 -3
  23. package/dist/components/feedback.css +239 -1
  24. package/dist/components/form.css +119 -0
  25. package/dist/components/form.mjs +2 -0
  26. package/dist/components/input.css +24 -1
  27. package/dist/components/media.css +144 -1
  28. package/dist/components/nav.css +99 -0
  29. package/dist/components/notification.css +387 -0
  30. package/dist/components/notification.mjs +3 -0
  31. package/dist/components/panel.css +100 -5
  32. package/dist/components/pill.css +70 -0
  33. package/dist/components/scroll-area.css +112 -0
  34. package/dist/components/scroll-area.mjs +2 -0
  35. package/dist/components/segmented.css +75 -0
  36. package/dist/components/select.css +97 -2
  37. package/dist/components/sidebar.css +118 -0
  38. package/dist/components/slider.css +113 -0
  39. package/dist/components/split-pane.css +112 -0
  40. package/dist/components/split-pane.mjs +3 -0
  41. package/dist/components/stat.css +129 -6
  42. package/dist/components/steps.css +97 -0
  43. package/dist/components/switch.css +142 -0
  44. package/dist/components/table.css +86 -3
  45. package/dist/components/tabs.css +122 -1
  46. package/dist/components/tag.css +164 -0
  47. package/dist/components/tag.mjs +2 -0
  48. package/dist/components/toast.css +114 -0
  49. package/dist/components/toolbar.css +101 -0
  50. package/dist/components/toolbar.mjs +2 -0
  51. package/dist/components/upload.css +61 -4
  52. package/dist/index.mjs +1858 -69
  53. package/dist/registry.json +64 -1
  54. package/dist/ui.css +4320 -278
  55. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@icen.ai/ui",
4
- version: "0.4.0",
4
+ version: "0.5.0",
5
5
  description: "Icen Design System \u2014 \u8BBE\u8BA1 tokens\uFF086 \u9884\u8BBE \xD7 \u660E\u6697\uFF09\u3001\u65E0\u6846\u67B6\u7EC4\u4EF6 CSS \u4E0E\u884C\u4E3A JS",
6
6
  repository: {
7
7
  type: "git",
@@ -456,9 +456,18 @@ function initCopy(root = document) {
456
456
  }
457
457
 
458
458
  // src/behaviors/input.ts
459
+ var isComposing = false;
460
+ if (typeof document !== "undefined") {
461
+ document.addEventListener("compositionstart", () => {
462
+ isComposing = true;
463
+ });
464
+ document.addEventListener("compositionend", () => {
465
+ isComposing = false;
466
+ });
467
+ }
459
468
  function setupClear(wrap) {
460
469
  const btn = wrap.querySelector(".input-clear");
461
- const field = wrap.querySelector("input.input");
470
+ const field = wrap.querySelector("input.input, textarea.textarea");
462
471
  if (!btn || !field) return;
463
472
  btn.addEventListener("click", () => {
464
473
  field.value = "";
@@ -500,6 +509,140 @@ function setupAutosize(ta) {
500
509
  ta.addEventListener("input", () => resizeTextarea(ta));
501
510
  resizeTextarea(ta);
502
511
  }
512
+ function setupCounter(field) {
513
+ const maxAttr = field.getAttribute("maxlength");
514
+ const dataCount = field.getAttribute("data-count");
515
+ if (!maxAttr && dataCount === null) return;
516
+ const max = maxAttr ? Number(maxAttr) : 0;
517
+ let counter = null;
518
+ const wrap = field.closest(".input-wrap, .textarea-wrap");
519
+ if (wrap) {
520
+ counter = wrap.querySelector(".input-counter");
521
+ if (!counter) {
522
+ counter = document.createElement("span");
523
+ counter.className = "input-counter";
524
+ wrap.appendChild(counter);
525
+ if (wrap.classList.contains("input-wrap")) {
526
+ wrap.classList.add("input-wrap--has-counter");
527
+ }
528
+ }
529
+ }
530
+ const counterEl = counter;
531
+ if (!counterEl) return;
532
+ function update() {
533
+ const len = field.value.length;
534
+ counterEl.textContent = max > 0 ? `${len} / ${max}` : `${len}`;
535
+ counterEl.classList.remove("is-warn", "is-error");
536
+ if (max > 0) {
537
+ const ratio = len / max;
538
+ if (ratio >= 1) counterEl.classList.add("is-error");
539
+ else if (ratio >= 0.8) counterEl.classList.add("is-warn");
540
+ }
541
+ }
542
+ field.addEventListener("input", () => {
543
+ if (!isComposing) update();
544
+ });
545
+ update();
546
+ }
547
+ function setupMask(field) {
548
+ const mask = field.getAttribute("data-mask");
549
+ if (!mask) return;
550
+ const maskChars = [];
551
+ for (const ch of mask) maskChars.push(ch);
552
+ function isInputSlot(ch) {
553
+ return ch === "#" || ch === "A" || ch === "*";
554
+ }
555
+ function validate(rawChar, slotType) {
556
+ if (slotType === "#") return /[0-9]/.test(rawChar);
557
+ if (slotType === "A") return /[a-zA-Z]/.test(rawChar);
558
+ return true;
559
+ }
560
+ function applyMask(input) {
561
+ let result = "";
562
+ let rawIdx = 0;
563
+ const rawChars = input.split("");
564
+ for (let i = 0; i < maskChars.length && rawIdx < rawChars.length; i++) {
565
+ const slot = maskChars[i];
566
+ if (isInputSlot(slot)) {
567
+ while (rawIdx < rawChars.length && !validate(rawChars[rawIdx], slot)) {
568
+ rawIdx++;
569
+ }
570
+ if (rawIdx < rawChars.length) {
571
+ result += rawChars[rawIdx];
572
+ rawIdx++;
573
+ }
574
+ } else {
575
+ result += slot;
576
+ }
577
+ }
578
+ return result;
579
+ }
580
+ function extractRaw(formatted) {
581
+ let raw = "";
582
+ for (let i = 0; i < formatted.length && i < maskChars.length; i++) {
583
+ if (isInputSlot(maskChars[i])) raw += formatted[i];
584
+ }
585
+ return raw;
586
+ }
587
+ let lastFormatted = applyMask(field.value);
588
+ field.value = lastFormatted;
589
+ field.setAttribute("data-raw-value", extractRaw(lastFormatted));
590
+ field.addEventListener("input", () => {
591
+ if (isComposing) return;
592
+ const cursorPos = field.selectionStart ?? 0;
593
+ const oldRaw = extractRaw(lastFormatted);
594
+ const newFull = field.value;
595
+ const newRaw = extractRaw(newFull);
596
+ const formatted = applyMask(newRaw);
597
+ field.value = formatted;
598
+ field.setAttribute("data-raw-value", extractRaw(formatted));
599
+ lastFormatted = formatted;
600
+ const rawDelta = newRaw.length - oldRaw.length;
601
+ let targetCursor = cursorPos;
602
+ if (rawDelta > 0) {
603
+ let count = 0;
604
+ for (let i = 0; i < formatted.length; i++) {
605
+ if (isInputSlot(maskChars[i])) {
606
+ count++;
607
+ if (count >= newRaw.length) {
608
+ targetCursor = i + 1;
609
+ break;
610
+ }
611
+ }
612
+ }
613
+ } else if (rawDelta < 0) {
614
+ targetCursor = Math.min(cursorPos, formatted.length);
615
+ }
616
+ field.setSelectionRange(targetCursor, targetCursor);
617
+ });
618
+ field.addEventListener("keydown", (ev) => {
619
+ if (isComposing) return;
620
+ if (ev.ctrlKey || ev.metaKey || ev.altKey) return;
621
+ if (["Backspace", "Delete", "Tab", "Escape", "ArrowLeft", "ArrowRight", "Home", "End", "Enter"].includes(ev.key)) return;
622
+ const currentRaw = extractRaw(field.value);
623
+ const nextSlotIdx = maskChars.findIndex((ch, i) => {
624
+ if (!isInputSlot(ch)) return false;
625
+ return i >= field.value.length || !isInputSlot(maskChars[Math.min(field.value.length, maskChars.length - 1)]);
626
+ });
627
+ const nextSlot = maskChars.find((ch, i) => {
628
+ const filledSlots = maskChars.slice(0, i).filter(isInputSlot).length;
629
+ return isInputSlot(ch) && filledSlots >= currentRaw.length;
630
+ });
631
+ if (nextSlot && !validate(ev.key, nextSlot)) {
632
+ ev.preventDefault();
633
+ }
634
+ });
635
+ }
636
+ function setupTrim(field) {
637
+ if (!field.hasAttribute("data-trim")) return;
638
+ field.addEventListener("blur", () => {
639
+ const trimmed = field.value.trim();
640
+ if (trimmed !== field.value) {
641
+ field.value = trimmed;
642
+ field.dispatchEvent(new Event("input", { bubbles: true }));
643
+ }
644
+ });
645
+ }
503
646
  function setupOtp(group) {
504
647
  const el = group;
505
648
  if (el.__icenInputInit) return;
@@ -514,6 +657,7 @@ function setupOtp(group) {
514
657
  }
515
658
  syncFilled(cell);
516
659
  cell.addEventListener("input", () => {
660
+ if (isComposing) return;
517
661
  const ch = cell.value.slice(-1);
518
662
  cell.value = ch;
519
663
  syncFilled(cell);
@@ -568,6 +712,31 @@ function initInput(root = document) {
568
712
  if (root instanceof HTMLTextAreaElement && root.matches("[data-autosize]")) textareas.push(root);
569
713
  textareas.push(...Array.from(root.querySelectorAll("textarea[data-autosize]")));
570
714
  for (const ta of textareas) setupAutosize(ta);
715
+ const countedFields = [];
716
+ if (root instanceof Element) {
717
+ const fields = Array.from(root.querySelectorAll(
718
+ "input[maxlength], input[data-count], textarea[maxlength], textarea[data-count]"
719
+ ));
720
+ countedFields.push(...fields);
721
+ if (root instanceof HTMLInputElement || root instanceof HTMLTextAreaElement) {
722
+ if (root.hasAttribute("maxlength") || root.hasAttribute("data-count")) countedFields.push(root);
723
+ }
724
+ }
725
+ for (const f of countedFields) setupCounter(f);
726
+ const maskedInputs = [];
727
+ if (root instanceof Element) {
728
+ maskedInputs.push(...Array.from(root.querySelectorAll("input[data-mask]")));
729
+ if (root instanceof HTMLInputElement && root.hasAttribute("data-mask")) maskedInputs.push(root);
730
+ }
731
+ for (const inp of maskedInputs) setupMask(inp);
732
+ const trimFields = [];
733
+ if (root instanceof Element) {
734
+ trimFields.push(...Array.from(root.querySelectorAll("[data-trim]")));
735
+ if ((root instanceof HTMLInputElement || root instanceof HTMLTextAreaElement) && root.hasAttribute("data-trim")) {
736
+ trimFields.push(root);
737
+ }
738
+ }
739
+ for (const f of trimFields) setupTrim(f);
571
740
  const otps = [];
572
741
  if (root instanceof Element && root.matches(".otp")) otps.push(root);
573
742
  otps.push(...Array.from(root.querySelectorAll(".otp")));
@@ -580,27 +749,44 @@ function setup2(container) {
580
749
  if (el.__icenSelectInit) return;
581
750
  el.__icenSelectInit = true;
582
751
  const triggerEl = container.querySelector(".select-trigger");
583
- const panelEl = container.querySelector(".select-panel");
752
+ const panelEl2 = container.querySelector(".select-panel");
584
753
  const valueEl = container.querySelector(".select-value");
585
754
  const hiddenInput = container.querySelector("input[data-select-value]");
586
- if (!triggerEl || !panelEl) return;
755
+ if (!triggerEl || !panelEl2) return;
587
756
  const trigger = triggerEl;
588
- const panel2 = panelEl;
589
- const options = () => Array.from(panel2.querySelectorAll(".select-option"));
590
- const isOpen = () => !panel2.hidden;
757
+ const panel2 = panelEl2;
758
+ const isMultiple = container.hasAttribute("data-select-multiple");
759
+ const isSearchable = container.hasAttribute("data-select-search");
760
+ const maxSelect = Number(container.getAttribute("data-select-max")) || 0;
761
+ const placeholder = container.getAttribute("data-select-placeholder") ?? (valueEl?.classList.contains("is-empty") ? valueEl?.textContent ?? "" : "");
762
+ let searchInput = null;
591
763
  let focusIdx = -1;
764
+ if (isSearchable && !panel2.querySelector(".select-search-input")) {
765
+ const searchWrap = document.createElement("div");
766
+ searchWrap.className = "select-search";
767
+ const input = document.createElement("input");
768
+ input.className = "select-search-input";
769
+ input.type = "search";
770
+ input.placeholder = "\u641C\u7D22\u2026";
771
+ searchWrap.appendChild(input);
772
+ panel2.insertBefore(searchWrap, panel2.firstChild);
773
+ }
774
+ searchInput = panel2.querySelector(".select-search-input");
775
+ const allOptions = () => Array.from(panel2.querySelectorAll(".select-option"));
776
+ const visibleOptions = () => allOptions().filter((o) => !o.hidden);
777
+ const isOpen = () => !panel2.hidden;
592
778
  function setFocus(idx) {
593
- const opts = options();
779
+ const opts = visibleOptions();
594
780
  focusIdx = Math.max(-1, Math.min(idx, opts.length - 1));
595
781
  opts.forEach((o, i) => o.classList.toggle("is-focused", i === focusIdx));
596
782
  const cur = focusIdx >= 0 ? opts[focusIdx] : void 0;
597
783
  cur?.scrollIntoView({ block: "nearest" });
598
784
  }
599
785
  function firstEnabled() {
600
- return options().findIndex((o) => !o.classList.contains("is-disabled"));
786
+ return visibleOptions().findIndex((o) => !o.classList.contains("is-disabled"));
601
787
  }
602
788
  function moveFocus(delta) {
603
- const opts = options();
789
+ const opts = visibleOptions();
604
790
  if (opts.length === 0) return;
605
791
  let n = focusIdx;
606
792
  for (let guard = 0; guard < opts.length; guard++) {
@@ -610,38 +796,135 @@ function setup2(container) {
610
796
  }
611
797
  setFocus(n);
612
798
  }
799
+ let emptyEl = panel2.querySelector(".select-empty");
800
+ if (!emptyEl) {
801
+ emptyEl = document.createElement("div");
802
+ emptyEl.className = "select-empty";
803
+ emptyEl.textContent = "\u65E0\u5339\u914D\u9879";
804
+ emptyEl.hidden = true;
805
+ panel2.appendChild(emptyEl);
806
+ }
807
+ function updateEmptyState() {
808
+ const hasVisible = visibleOptions().length > 0;
809
+ if (emptyEl) emptyEl.hidden = hasVisible;
810
+ }
811
+ function filterOptions(query) {
812
+ const q = query.trim().toLowerCase();
813
+ const groups = Array.from(panel2.querySelectorAll(".select-group"));
814
+ for (const opt of allOptions()) {
815
+ const text = (opt.textContent ?? "").toLowerCase();
816
+ const kw = (opt.getAttribute("data-keywords") ?? "").toLowerCase();
817
+ opt.hidden = q !== "" && !text.includes(q) && !kw.includes(q);
818
+ }
819
+ for (const g of groups) {
820
+ const hasVisible = Array.from(g.querySelectorAll(".select-option")).some((o) => !o.hidden);
821
+ g.hidden = !hasVisible;
822
+ }
823
+ updateEmptyState();
824
+ setFocus(firstEnabled());
825
+ }
826
+ if (searchInput) {
827
+ searchInput.addEventListener("input", () => filterOptions(searchInput.value));
828
+ searchInput.addEventListener("keydown", (ev) => {
829
+ if (ev.key === "Escape") {
830
+ ev.stopPropagation();
831
+ searchInput.value = "";
832
+ filterOptions("");
833
+ searchInput.focus();
834
+ }
835
+ });
836
+ }
613
837
  function openPanel() {
614
838
  if (isOpen()) return;
615
839
  panel2.hidden = false;
616
840
  container.classList.add("is-open");
617
841
  trigger.setAttribute("aria-expanded", "true");
618
- const sel = options().findIndex((o) => o.classList.contains("is-selected"));
619
- setFocus(sel >= 0 ? sel : firstEnabled());
842
+ if (searchInput) {
843
+ searchInput.value = "";
844
+ filterOptions("");
845
+ setTimeout(() => searchInput?.focus(), 0);
846
+ } else {
847
+ const sel = allOptions().findIndex((o) => o.classList.contains("is-selected"));
848
+ setFocus(sel >= 0 ? sel : firstEnabled());
849
+ }
620
850
  }
621
- function closePanel() {
851
+ function closePanel2() {
622
852
  if (!isOpen()) return;
623
853
  panel2.hidden = true;
624
854
  container.classList.remove("is-open");
625
855
  trigger.setAttribute("aria-expanded", "false");
626
856
  setFocus(-1);
627
857
  }
628
- function choose(opt) {
629
- if (opt.classList.contains("is-disabled")) return;
630
- options().forEach((o) => o.classList.toggle("is-selected", o === opt));
631
- if (valueEl) {
632
- valueEl.textContent = opt.textContent ?? "";
858
+ function getSelectedValues() {
859
+ return allOptions().filter((o) => o.classList.contains("is-selected")).map((o) => o.getAttribute("data-value") ?? "");
860
+ }
861
+ function updateMultiTrigger() {
862
+ if (!valueEl) return;
863
+ const selected = allOptions().filter((o) => o.classList.contains("is-selected"));
864
+ if (selected.length === 0) {
865
+ valueEl.textContent = placeholder;
866
+ valueEl.classList.add("is-empty");
867
+ container.removeAttribute("data-value");
868
+ } else {
633
869
  valueEl.classList.remove("is-empty");
870
+ const maxLabel = Number(container.getAttribute("data-select-max-label")) || 2;
871
+ if (selected.length <= maxLabel) {
872
+ valueEl.textContent = selected.map((o) => o.textContent ?? "").join("\u3001");
873
+ } else {
874
+ valueEl.textContent = `\u5DF2\u9009 ${selected.length} \u9879`;
875
+ }
634
876
  }
635
- const v = opt.getAttribute("data-value") ?? "";
636
- container.setAttribute("data-value", v);
637
- if (hiddenInput) {
638
- hiddenInput.value = v;
639
- hiddenInput.dispatchEvent(new Event("change", { bubbles: true }));
877
+ if (maxSelect > 0) {
878
+ const atMax = selected.length >= maxSelect;
879
+ allOptions().forEach((o) => {
880
+ if (!o.classList.contains("is-selected")) {
881
+ o.classList.toggle("is-disabled", atMax);
882
+ }
883
+ });
640
884
  }
641
- closePanel();
642
- trigger.focus();
643
885
  }
644
- trigger.addEventListener("click", () => isOpen() ? closePanel() : openPanel());
886
+ function syncHiddenInput() {
887
+ if (!hiddenInput) return;
888
+ const values = getSelectedValues();
889
+ hiddenInput.value = isMultiple ? values.join(",") : values[0] ?? "";
890
+ hiddenInput.dispatchEvent(new Event("change", { bubbles: true }));
891
+ }
892
+ function choose(opt) {
893
+ if (opt.classList.contains("is-disabled")) return;
894
+ if (isMultiple) {
895
+ opt.classList.toggle("is-selected");
896
+ container.setAttribute(
897
+ "data-value",
898
+ getSelectedValues().join(",")
899
+ );
900
+ updateMultiTrigger();
901
+ syncHiddenInput();
902
+ if (searchInput) searchInput.focus();
903
+ else opt.focus();
904
+ } else {
905
+ allOptions().forEach((o) => o.classList.toggle("is-selected", o === opt));
906
+ if (valueEl) {
907
+ valueEl.textContent = opt.textContent ?? "";
908
+ valueEl.classList.remove("is-empty");
909
+ }
910
+ const v = opt.getAttribute("data-value") ?? "";
911
+ container.setAttribute("data-value", v);
912
+ syncHiddenInput();
913
+ closePanel2();
914
+ trigger.focus();
915
+ }
916
+ }
917
+ const clearBtn = container.querySelector(".select-clear");
918
+ if (clearBtn && isMultiple) {
919
+ clearBtn.addEventListener("click", (ev) => {
920
+ ev.stopPropagation();
921
+ allOptions().forEach((o) => o.classList.remove("is-selected"));
922
+ updateMultiTrigger();
923
+ syncHiddenInput();
924
+ });
925
+ clearBtn.addEventListener("pointerdown", (ev) => ev.stopPropagation());
926
+ }
927
+ trigger.addEventListener("click", () => isOpen() ? closePanel2() : openPanel());
645
928
  trigger.addEventListener("keydown", (ev) => {
646
929
  if (!isOpen()) {
647
930
  if (ev.key === "ArrowDown" || ev.key === "ArrowUp" || ev.key === "Enter" || ev.key === " ") {
@@ -650,6 +933,20 @@ function setup2(container) {
650
933
  }
651
934
  return;
652
935
  }
936
+ if (searchInput && document.activeElement === searchInput) {
937
+ if (ev.key === "ArrowDown") {
938
+ ev.preventDefault();
939
+ moveFocus(1);
940
+ } else if (ev.key === "ArrowUp") {
941
+ ev.preventDefault();
942
+ moveFocus(-1);
943
+ } else if (ev.key === "Enter") {
944
+ ev.preventDefault();
945
+ const opt = visibleOptions()[focusIdx];
946
+ if (opt) choose(opt);
947
+ }
948
+ return;
949
+ }
653
950
  switch (ev.key) {
654
951
  case "ArrowDown":
655
952
  ev.preventDefault();
@@ -662,13 +959,13 @@ function setup2(container) {
662
959
  case "Enter":
663
960
  case " ": {
664
961
  ev.preventDefault();
665
- const opt = options()[focusIdx];
962
+ const opt = visibleOptions()[focusIdx];
666
963
  if (opt) choose(opt);
667
964
  break;
668
965
  }
669
966
  case "Escape":
670
967
  ev.preventDefault();
671
- closePanel();
968
+ closePanel2();
672
969
  break;
673
970
  }
674
971
  });
@@ -682,19 +979,20 @@ function setup2(container) {
682
979
  const t = ev.target;
683
980
  if (!(t instanceof Element)) return;
684
981
  const opt = t.closest(".select-option");
685
- if (!opt) return;
686
- const idx = options().indexOf(opt);
982
+ if (!opt || opt.hidden) return;
983
+ const idx = visibleOptions().indexOf(opt);
687
984
  if (idx >= 0 && !opt.classList.contains("is-disabled")) setFocus(idx);
688
985
  });
689
986
  document.addEventListener("pointerdown", (ev) => {
690
- if (isOpen() && ev.target instanceof Node && !container.contains(ev.target)) closePanel();
987
+ if (isOpen() && ev.target instanceof Node && !container.contains(ev.target)) closePanel2();
691
988
  });
692
989
  document.addEventListener("keydown", (ev) => {
693
990
  if (ev.key === "Escape" && isOpen()) {
694
- closePanel();
991
+ closePanel2();
695
992
  trigger.focus();
696
993
  }
697
994
  });
995
+ if (isMultiple) updateMultiTrigger();
698
996
  }
699
997
  function initSelect(root = document) {
700
998
  if (typeof document === "undefined") return;
@@ -890,47 +1188,198 @@ function initTagInput(root = document) {
890
1188
  }
891
1189
 
892
1190
  // src/behaviors/upload.ts
1191
+ function formatSize(bytes) {
1192
+ if (bytes < 1024) return `${bytes} B`;
1193
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1194
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1195
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
1196
+ }
1197
+ function isAccepted(file, accept) {
1198
+ if (!accept.trim()) return true;
1199
+ const types = accept.split(",").map((t) => t.trim().toLowerCase());
1200
+ const fileName = file.name.toLowerCase();
1201
+ const mime = file.type.toLowerCase();
1202
+ return types.some((t) => {
1203
+ if (t.startsWith(".")) return fileName.endsWith(t);
1204
+ if (t.endsWith("/*")) return mime.startsWith(t.slice(0, -1));
1205
+ return mime === t;
1206
+ });
1207
+ }
1208
+ var fileIconSvg = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>';
1209
+ var xIconSvg = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>';
893
1210
  function setup5(zone) {
894
1211
  const el = zone;
895
1212
  if (el.__icenUploadInit) return;
896
1213
  el.__icenUploadInit = true;
897
1214
  const input = zone.querySelector('input[type="file"]');
898
1215
  if (!input) return;
899
- const openPicker = () => input.click();
1216
+ const hasList = zone.hasAttribute("data-upload-list");
1217
+ const maxSize = Number(zone.getAttribute("data-max-size")) || 0;
1218
+ const maxFiles = Number(zone.getAttribute("data-max-files")) || 0;
1219
+ const accept = input.getAttribute("accept") ?? "";
1220
+ let currentFiles = [];
1221
+ const urlMap = /* @__PURE__ */ new Map();
1222
+ function getListEl() {
1223
+ if (!hasList) return null;
1224
+ let list = zone.querySelector(".upload-list");
1225
+ if (!list) {
1226
+ list = document.createElement("ul");
1227
+ list.className = "upload-list";
1228
+ zone.appendChild(list);
1229
+ }
1230
+ return list;
1231
+ }
1232
+ function renderList() {
1233
+ const list = getListEl();
1234
+ if (!list) return;
1235
+ urlMap.forEach((url) => URL.revokeObjectURL(url));
1236
+ urlMap.clear();
1237
+ list.textContent = "";
1238
+ if (currentFiles.length === 0) {
1239
+ list.hidden = true;
1240
+ return;
1241
+ }
1242
+ list.hidden = false;
1243
+ for (const file of currentFiles) {
1244
+ const li = document.createElement("li");
1245
+ li.className = "upload-list-item";
1246
+ const isImage = file.type.startsWith("image/");
1247
+ if (isImage) {
1248
+ try {
1249
+ const url = URL.createObjectURL(file);
1250
+ urlMap.set(file, url);
1251
+ const img = document.createElement("img");
1252
+ img.className = "upload-list-item-thumb";
1253
+ img.src = url;
1254
+ img.alt = file.name;
1255
+ li.appendChild(img);
1256
+ } catch {
1257
+ const icon = document.createElement("span");
1258
+ icon.className = "upload-list-item-icon";
1259
+ icon.innerHTML = fileIconSvg;
1260
+ li.appendChild(icon);
1261
+ }
1262
+ } else {
1263
+ const icon = document.createElement("span");
1264
+ icon.className = "upload-list-item-icon";
1265
+ icon.innerHTML = fileIconSvg;
1266
+ li.appendChild(icon);
1267
+ }
1268
+ const info = document.createElement("div");
1269
+ info.className = "upload-list-item-info";
1270
+ const name = document.createElement("span");
1271
+ name.className = "upload-list-item-name";
1272
+ name.textContent = file.name;
1273
+ const size = document.createElement("span");
1274
+ size.className = "upload-list-item-size";
1275
+ size.textContent = formatSize(file.size);
1276
+ info.appendChild(name);
1277
+ info.appendChild(size);
1278
+ li.appendChild(info);
1279
+ const removeBtn = document.createElement("button");
1280
+ removeBtn.className = "upload-list-item-x";
1281
+ removeBtn.type = "button";
1282
+ removeBtn.setAttribute("aria-label", `\u79FB\u9664 ${file.name}`);
1283
+ removeBtn.innerHTML = xIconSvg;
1284
+ removeBtn.addEventListener("click", (ev) => {
1285
+ ev.stopPropagation();
1286
+ currentFiles = currentFiles.filter((f) => f !== file);
1287
+ zone.dispatchEvent(new CustomEvent("icen:upload-remove", { bubbles: true, detail: { file } }));
1288
+ renderList();
1289
+ syncInputFiles();
1290
+ });
1291
+ li.appendChild(removeBtn);
1292
+ list.appendChild(li);
1293
+ }
1294
+ }
1295
+ function syncInputFiles() {
1296
+ try {
1297
+ const dt = new DataTransfer();
1298
+ for (const f of currentFiles) dt.items.add(f);
1299
+ input.files = dt.files;
1300
+ input.dispatchEvent(new Event("change", { bubbles: true }));
1301
+ } catch {
1302
+ }
1303
+ }
1304
+ function validate(file) {
1305
+ if (accept && !isAccepted(file, accept)) return "\u6587\u4EF6\u7C7B\u578B\u4E0D\u652F\u6301";
1306
+ if (maxSize > 0 && file.size > maxSize) return `\u6587\u4EF6\u8D85\u8FC7 ${formatSize(maxSize)}`;
1307
+ return null;
1308
+ }
1309
+ function handleFiles(files) {
1310
+ const valid = [];
1311
+ for (const file of files) {
1312
+ const reason = validate(file);
1313
+ if (reason) {
1314
+ zone.dispatchEvent(new CustomEvent("icen:upload-error", { bubbles: true, detail: { file, reason } }));
1315
+ continue;
1316
+ }
1317
+ valid.push(file);
1318
+ }
1319
+ if (valid.length === 0) return;
1320
+ if (hasList) {
1321
+ if (maxFiles > 0) {
1322
+ const remaining = maxFiles - currentFiles.length;
1323
+ if (remaining <= 0) {
1324
+ zone.dispatchEvent(new CustomEvent("icen:upload-error", {
1325
+ bubbles: true,
1326
+ detail: { reason: `\u6700\u591A ${maxFiles} \u4E2A\u6587\u4EF6` }
1327
+ }));
1328
+ return;
1329
+ }
1330
+ valid.splice(remaining);
1331
+ }
1332
+ currentFiles.push(...valid);
1333
+ renderList();
1334
+ syncInputFiles();
1335
+ }
1336
+ zone.dispatchEvent(new CustomEvent("icen:upload", { bubbles: true, detail: { files: valid } }));
1337
+ }
1338
+ const openPicker2 = () => input.click();
900
1339
  zone.addEventListener("click", (ev) => {
901
1340
  if (ev.target === input) return;
902
- openPicker();
1341
+ const tgt = ev.target;
1342
+ if (tgt instanceof Element && tgt.closest(".upload-list")) return;
1343
+ openPicker2();
903
1344
  });
904
1345
  zone.addEventListener("keydown", (ev) => {
905
1346
  if (ev.key === "Enter" || ev.key === " ") {
906
1347
  ev.preventDefault();
907
- openPicker();
1348
+ openPicker2();
1349
+ }
1350
+ });
1351
+ input.addEventListener("change", () => {
1352
+ if (!input.files) return;
1353
+ const files = Array.from(input.files);
1354
+ if (hasList) {
1355
+ handleFiles(files);
1356
+ input.value = "";
1357
+ } else {
1358
+ handleFiles(files);
908
1359
  }
909
1360
  });
910
1361
  zone.addEventListener("dragover", (ev) => {
911
1362
  ev.preventDefault();
912
1363
  zone.classList.add("is-drag");
913
1364
  });
914
- zone.addEventListener("dragleave", () => zone.classList.remove("is-drag"));
1365
+ zone.addEventListener("dragleave", (ev) => {
1366
+ if (ev.target === zone) zone.classList.remove("is-drag");
1367
+ });
915
1368
  zone.addEventListener("drop", (ev) => {
916
1369
  ev.preventDefault();
917
1370
  zone.classList.remove("is-drag");
918
1371
  const files = ev.dataTransfer?.files;
919
1372
  if (!files || files.length === 0) return;
920
- let assigned = false;
921
- try {
922
- const dt = new DataTransfer();
923
- for (const f of Array.from(files)) dt.items.add(f);
924
- input.files = dt.files;
925
- assigned = true;
926
- } catch {
927
- assigned = false;
928
- }
929
- if (assigned) {
930
- input.dispatchEvent(new Event("change", { bubbles: true }));
1373
+ handleFiles(Array.from(files));
1374
+ if (!hasList) {
1375
+ try {
1376
+ const dt = new DataTransfer();
1377
+ for (const f of Array.from(files)) dt.items.add(f);
1378
+ input.files = dt.files;
1379
+ input.dispatchEvent(new Event("change", { bubbles: true }));
1380
+ } catch {
1381
+ }
931
1382
  }
932
- const list = Array.from(files);
933
- zone.dispatchEvent(new CustomEvent("icen:upload", { bubbles: true, detail: { files: list } }));
934
1383
  });
935
1384
  }
936
1385
  function initUpload(root = document) {
@@ -1997,19 +2446,19 @@ function renderLine(el, opts) {
1997
2446
  const values = opts.values.slice(0, n);
1998
2447
  const width = 360;
1999
2448
  const height = 150;
2000
- const pad = 14;
2449
+ const pad2 = 14;
2001
2450
  const max = Math.max(...values, 1);
2002
2451
  const tone = toneVar(opts.tone);
2003
2452
  const points = values.map((value, i) => ({
2004
- x: values.length <= 1 ? width / 2 : pad + i / (values.length - 1) * (width - pad * 2),
2005
- y: height - pad - value / max * (height - pad * 2),
2453
+ x: values.length <= 1 ? width / 2 : pad2 + i / (values.length - 1) * (width - pad2 * 2),
2454
+ y: height - pad2 - value / max * (height - pad2 * 2),
2006
2455
  value,
2007
2456
  label: labels[i] ?? ""
2008
2457
  }));
2009
2458
  const line = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ");
2010
2459
  const last = points[points.length - 1];
2011
2460
  const first = points[0];
2012
- const area = `${line} L${last.x.toFixed(1)},${height - pad} L${first.x.toFixed(1)},${height - pad} Z`;
2461
+ const area = `${line} L${last.x.toFixed(1)},${height - pad2} L${first.x.toFixed(1)},${height - pad2} Z`;
2013
2462
  const gradientId = `icen-chart-line-${++lineGradientSeq}`;
2014
2463
  const wrap = document.createElement("div");
2015
2464
  wrap.className = "chart-line";
@@ -2021,10 +2470,10 @@ function renderLine(el, opts) {
2021
2470
  defs.appendChild(gradient);
2022
2471
  svg.appendChild(defs);
2023
2472
  for (let i = 0; i < 4; i++) {
2024
- const y = pad + i * (height - pad * 2) / 3;
2473
+ const y = pad2 + i * (height - pad2 * 2) / 3;
2025
2474
  svg.appendChild(svgEl("line", {
2026
- x1: String(pad),
2027
- x2: String(width - pad),
2475
+ x1: String(pad2),
2476
+ x2: String(width - pad2),
2028
2477
  y1: String(y),
2029
2478
  y2: String(y),
2030
2479
  stroke: "var(--token-line-soft)",
@@ -2069,19 +2518,19 @@ function renderArea(el, opts) {
2069
2518
  const values = opts.values.slice(0, n);
2070
2519
  const width = 360;
2071
2520
  const height = 150;
2072
- const pad = 14;
2521
+ const pad2 = 14;
2073
2522
  const max = Math.max(...values, 1);
2074
2523
  const tone = toneVar(opts.tone);
2075
2524
  const points = values.map((value, i) => ({
2076
- x: values.length <= 1 ? width / 2 : pad + i / (values.length - 1) * (width - pad * 2),
2077
- y: height - pad - value / max * (height - pad * 2),
2525
+ x: values.length <= 1 ? width / 2 : pad2 + i / (values.length - 1) * (width - pad2 * 2),
2526
+ y: height - pad2 - value / max * (height - pad2 * 2),
2078
2527
  value,
2079
2528
  label: labels[i] ?? ""
2080
2529
  }));
2081
2530
  const line = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ");
2082
2531
  const last = points[points.length - 1];
2083
2532
  const first = points[0];
2084
- const area = `${line} L${last.x.toFixed(1)},${height - pad} L${first.x.toFixed(1)},${height - pad} Z`;
2533
+ const area = `${line} L${last.x.toFixed(1)},${height - pad2} L${first.x.toFixed(1)},${height - pad2} Z`;
2085
2534
  const gradientId = `icen-chart-area-${++lineGradientSeq}`;
2086
2535
  const wrap = document.createElement("div");
2087
2536
  wrap.className = "chart-area";
@@ -2093,10 +2542,10 @@ function renderArea(el, opts) {
2093
2542
  defs.appendChild(gradient);
2094
2543
  svg.appendChild(defs);
2095
2544
  for (let i = 0; i < 4; i++) {
2096
- const y = pad + i * (height - pad * 2) / 3;
2545
+ const y = pad2 + i * (height - pad2 * 2) / 3;
2097
2546
  svg.appendChild(svgEl("line", {
2098
- x1: String(pad),
2099
- x2: String(width - pad),
2547
+ x1: String(pad2),
2548
+ x2: String(width - pad2),
2100
2549
  y1: String(y),
2101
2550
  y2: String(y),
2102
2551
  stroke: "var(--token-line-soft)",
@@ -2276,8 +2725,8 @@ function renderHeatmap(el, opts) {
2276
2725
  grid.className = "chart-heatmap-grid";
2277
2726
  grid.setAttribute("role", "img");
2278
2727
  const first = /* @__PURE__ */ new Date(`${entries[0].date}T00:00:00`);
2279
- const pad = (first.getDay() + 6) % 7;
2280
- for (let i = 0; i < pad; i++) {
2728
+ const pad2 = (first.getDay() + 6) % 7;
2729
+ for (let i = 0; i < pad2; i++) {
2281
2730
  const blank = document.createElement("span");
2282
2731
  blank.className = "chart-heatmap-cell is-blank";
2283
2732
  grid.appendChild(blank);
@@ -2316,18 +2765,18 @@ function renderSparkline(el, opts) {
2316
2765
  }
2317
2766
  const width = 96;
2318
2767
  const height = 28;
2319
- const pad = 3;
2768
+ const pad2 = 3;
2320
2769
  const max = Math.max(...values, 1);
2321
2770
  const tone = toneVar(opts.tone);
2322
2771
  const points = values.map((value, i) => ({
2323
- x: values.length <= 1 ? width / 2 : pad + i / (values.length - 1) * (width - pad * 2),
2324
- y: height - pad - value / max * (height - pad * 2),
2772
+ x: values.length <= 1 ? width / 2 : pad2 + i / (values.length - 1) * (width - pad2 * 2),
2773
+ y: height - pad2 - value / max * (height - pad2 * 2),
2325
2774
  value
2326
2775
  }));
2327
2776
  const line = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ");
2328
2777
  const last = points[points.length - 1];
2329
2778
  const first = points[0];
2330
- const area = `${line} L${last.x.toFixed(1)},${height - pad} L${first.x.toFixed(1)},${height - pad} Z`;
2779
+ const area = `${line} L${last.x.toFixed(1)},${height - pad2} L${first.x.toFixed(1)},${height - pad2} Z`;
2331
2780
  const gradientId = `icen-chart-line-${++lineGradientSeq}`;
2332
2781
  const wrap = document.createElement("div");
2333
2782
  wrap.className = "chart-sparkline";
@@ -2500,6 +2949,1337 @@ function initSidebar(root = document) {
2500
2949
  for (const s of sidebars) setup10(s);
2501
2950
  }
2502
2951
 
2952
+ // src/behaviors/notification.ts
2953
+ var KIND_ICON = {
2954
+ success: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><path d="m9 11 3 3L22 4"/></svg>',
2955
+ warning: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>',
2956
+ error: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>',
2957
+ info: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>'
2958
+ };
2959
+ var CLOSE_SVG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
2960
+ var PROGRESS_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>';
2961
+ var cfg = {
2962
+ position: "top-right",
2963
+ maxStack: 5,
2964
+ pauseOnHover: true,
2965
+ persist: false,
2966
+ sound: false
2967
+ };
2968
+ var store = /* @__PURE__ */ new Map();
2969
+ var globalInit = { done: false };
2970
+ function isBrowser3() {
2971
+ return typeof document !== "undefined" && typeof window !== "undefined";
2972
+ }
2973
+ function genId() {
2974
+ return "notif-" + Math.random().toString(36).slice(2, 10);
2975
+ }
2976
+ function ensureContainer2() {
2977
+ const sel = `.notification-container[data-notif-default]`;
2978
+ const existing = document.body.querySelector(sel);
2979
+ if (existing) {
2980
+ syncContainerPosition(existing);
2981
+ return existing;
2982
+ }
2983
+ const el = document.createElement("div");
2984
+ el.className = "notification-container";
2985
+ el.setAttribute("role", "region");
2986
+ el.setAttribute("aria-label", "\u901A\u77E5");
2987
+ el.dataset.notifDefault = "";
2988
+ syncContainerPosition(el);
2989
+ document.body.appendChild(el);
2990
+ return el;
2991
+ }
2992
+ function syncContainerPosition(el) {
2993
+ el.dataset.position = cfg.position;
2994
+ }
2995
+ var DEFAULT_PERSIST_KEY = "icen.ui.notifications";
2996
+ function persistKey() {
2997
+ if (cfg.persist === true) return DEFAULT_PERSIST_KEY;
2998
+ if (typeof cfg.persist === "string") return cfg.persist;
2999
+ return null;
3000
+ }
3001
+ function persistSave() {
3002
+ const key = persistKey();
3003
+ if (!key) return;
3004
+ try {
3005
+ const items = Array.from(store.values()).filter((r) => !r.opts.duration || r.opts.duration === 0).map((r) => ({
3006
+ id: r.id,
3007
+ title: r.title,
3008
+ kind: r.kind,
3009
+ description: r.opts.description ?? "",
3010
+ tag: r.opts.tag,
3011
+ unread: r.opts.unread,
3012
+ priority: r.opts.priority,
3013
+ progress: r.opts.progress,
3014
+ progressLabel: r.opts.progressLabel,
3015
+ link: r.opts.link,
3016
+ linkLabel: r.opts.linkLabel,
3017
+ data: r.opts.data,
3018
+ createdAt: r.createdAt
3019
+ }));
3020
+ localStorage.setItem(key, JSON.stringify(items));
3021
+ } catch {
3022
+ }
3023
+ }
3024
+ function persistLoad() {
3025
+ const key = persistKey();
3026
+ if (!key) return [];
3027
+ try {
3028
+ const raw = localStorage.getItem(key);
3029
+ if (!raw) return [];
3030
+ const arr = JSON.parse(raw);
3031
+ return Array.isArray(arr) ? arr : [];
3032
+ } catch {
3033
+ return [];
3034
+ }
3035
+ }
3036
+ var audioCtx = null;
3037
+ function playSound() {
3038
+ if (!cfg.sound) return;
3039
+ try {
3040
+ if (typeof cfg.sound === "string") {
3041
+ const audio = new Audio(cfg.sound);
3042
+ void audio.play();
3043
+ return;
3044
+ }
3045
+ if (!audioCtx) {
3046
+ const Ctor = window.AudioContext ?? window.webkitAudioContext;
3047
+ if (!Ctor) return;
3048
+ audioCtx = new Ctor();
3049
+ }
3050
+ if (audioCtx.state === "suspended") void audioCtx.resume();
3051
+ const now = audioCtx.currentTime;
3052
+ [880, 1320].forEach((freq, i) => {
3053
+ const osc = audioCtx.createOscillator();
3054
+ const gain = audioCtx.createGain();
3055
+ osc.connect(gain);
3056
+ gain.connect(audioCtx.destination);
3057
+ osc.frequency.value = freq;
3058
+ osc.type = "sine";
3059
+ const t = now + i * 0.08;
3060
+ gain.gain.setValueAtTime(0, t);
3061
+ gain.gain.linearRampToValueAtTime(0.08, t + 0.01);
3062
+ gain.gain.exponentialRampToValueAtTime(1e-3, t + 0.12);
3063
+ osc.start(t);
3064
+ osc.stop(t + 0.13);
3065
+ });
3066
+ } catch {
3067
+ }
3068
+ }
3069
+ function buildIcon(record) {
3070
+ const wrap = document.createElement("span");
3071
+ wrap.className = "notification-icon";
3072
+ wrap.setAttribute("aria-hidden", "true");
3073
+ const { icon, avatar } = record.opts;
3074
+ if (avatar) {
3075
+ const img = document.createElement("img");
3076
+ img.className = "notification-avatar";
3077
+ img.src = avatar;
3078
+ img.alt = "";
3079
+ img.loading = "lazy";
3080
+ wrap.appendChild(img);
3081
+ return wrap;
3082
+ }
3083
+ if (icon === false) {
3084
+ wrap.style.display = "none";
3085
+ return wrap;
3086
+ }
3087
+ if (typeof icon === "string" && icon.length > 0) {
3088
+ if (/^https?:\/\/|^data:image\//.test(icon) || icon.endsWith(".png") || icon.endsWith(".jpg") || icon.endsWith(".svg") || icon.endsWith(".webp")) {
3089
+ const img = document.createElement("img");
3090
+ img.className = "notification-avatar";
3091
+ img.src = icon;
3092
+ img.alt = "";
3093
+ wrap.appendChild(img);
3094
+ } else {
3095
+ wrap.innerHTML = icon;
3096
+ }
3097
+ return wrap;
3098
+ }
3099
+ if (record.opts.progress !== void 0 && record.opts.progress >= 0) {
3100
+ wrap.innerHTML = PROGRESS_ICON;
3101
+ } else {
3102
+ wrap.innerHTML = KIND_ICON[record.kind];
3103
+ }
3104
+ return wrap;
3105
+ }
3106
+ function buildTitle(title) {
3107
+ const el = document.createElement("p");
3108
+ el.className = "notification-title";
3109
+ el.textContent = title;
3110
+ return el;
3111
+ }
3112
+ function buildDescription(text) {
3113
+ const el = document.createElement("p");
3114
+ el.className = "notification-message";
3115
+ el.style.whiteSpace = "pre-wrap";
3116
+ el.textContent = text;
3117
+ return el;
3118
+ }
3119
+ function buildActions(record, getHandle) {
3120
+ const actions = record.opts.actions?.length ? record.opts.actions : record.opts.actionLabel ? [{ label: record.opts.actionLabel, onClick: record.opts.onAction }] : null;
3121
+ if (!actions || actions.length === 0) return null;
3122
+ const wrap = document.createElement("div");
3123
+ wrap.className = "notification-actions";
3124
+ for (const a of actions) {
3125
+ const btn = document.createElement("button");
3126
+ btn.type = "button";
3127
+ btn.className = "notification-action";
3128
+ if (a.type === "primary") btn.classList.add("notification-action--primary");
3129
+ else if (a.type === "danger") btn.classList.add("notification-action--danger");
3130
+ btn.textContent = a.label;
3131
+ btn.addEventListener("click", (ev) => {
3132
+ ev.stopPropagation();
3133
+ const h = getHandle();
3134
+ if (a.onClick) {
3135
+ const ret = a.onClick();
3136
+ if (ret === false) return;
3137
+ }
3138
+ if (!a.keepOpen) dismissRecord(record.id);
3139
+ else void h;
3140
+ });
3141
+ wrap.appendChild(btn);
3142
+ }
3143
+ return wrap;
3144
+ }
3145
+ function buildLink(record) {
3146
+ const { link, linkLabel } = record.opts;
3147
+ if (!link) return null;
3148
+ const wrap = document.createElement("div");
3149
+ wrap.className = "notification-link-wrap";
3150
+ const a = document.createElement("a");
3151
+ a.className = "notification-link";
3152
+ a.href = link;
3153
+ a.textContent = linkLabel ?? "\u67E5\u770B\u8BE6\u60C5";
3154
+ a.addEventListener("click", (ev) => ev.stopPropagation());
3155
+ wrap.appendChild(a);
3156
+ return wrap;
3157
+ }
3158
+ function buildProgressBar(record) {
3159
+ const { progress, progressLabel } = record.opts;
3160
+ if (progress === void 0 || progress < 0) return null;
3161
+ const wrap = document.createElement("div");
3162
+ wrap.className = "notification-progress";
3163
+ const track = document.createElement("div");
3164
+ track.className = "notification-progress-track";
3165
+ const fill = document.createElement("div");
3166
+ fill.className = "notification-progress-bar";
3167
+ const pct3 = Math.min(100, Math.max(0, progress * 100));
3168
+ fill.style.width = pct3 + "%";
3169
+ track.appendChild(fill);
3170
+ wrap.appendChild(track);
3171
+ if (progressLabel) {
3172
+ const lbl = document.createElement("span");
3173
+ lbl.className = "notification-progress-label";
3174
+ lbl.textContent = progressLabel;
3175
+ wrap.appendChild(lbl);
3176
+ } else if (progress > 0) {
3177
+ const lbl = document.createElement("span");
3178
+ lbl.className = "notification-progress-label";
3179
+ lbl.textContent = Math.round(pct3) + "%";
3180
+ wrap.appendChild(lbl);
3181
+ }
3182
+ return wrap;
3183
+ }
3184
+ function buildCountdownBar(record) {
3185
+ if (!record.duration || record.duration <= 0) return null;
3186
+ if (record.opts.showCountdown === false) return null;
3187
+ const bar = document.createElement("div");
3188
+ bar.className = "notification-countdown";
3189
+ bar.setAttribute("aria-hidden", "true");
3190
+ const fill = document.createElement("div");
3191
+ fill.className = "notification-countdown-fill";
3192
+ fill.style.animationDuration = record.duration + "ms";
3193
+ fill.style.animationPlayState = "running";
3194
+ bar.appendChild(fill);
3195
+ return bar;
3196
+ }
3197
+ function buildClose(record) {
3198
+ if (record.opts.closable === false) return null;
3199
+ const btn = document.createElement("button");
3200
+ btn.type = "button";
3201
+ btn.className = "notification-close";
3202
+ btn.setAttribute("aria-label", "\u5173\u95ED");
3203
+ btn.innerHTML = CLOSE_SVG;
3204
+ btn.addEventListener("click", (ev) => {
3205
+ ev.stopPropagation();
3206
+ dismissRecord(record.id);
3207
+ });
3208
+ return btn;
3209
+ }
3210
+ function renderNotification(record, container, getHandle) {
3211
+ const el = document.createElement("div");
3212
+ let cls = record.kind === "info" ? "notification" : `notification notif--${record.kind}`;
3213
+ if (record.opts.priority === "high") cls += " is-priority";
3214
+ if (record.opts.unread) cls += " is-unread";
3215
+ if (record.opts.progress !== void 0 && record.opts.progress >= 0) cls += " is-progress";
3216
+ if (record.opts.avatar) cls += " has-avatar";
3217
+ if (record.opts.onClick) {
3218
+ cls += " is-clickable";
3219
+ el.setAttribute("role", "button");
3220
+ el.tabIndex = 0;
3221
+ } else {
3222
+ el.setAttribute("role", "alert");
3223
+ }
3224
+ el.className = cls;
3225
+ el.id = record.id;
3226
+ const icon = buildIcon(record);
3227
+ el.appendChild(icon);
3228
+ const content = document.createElement("div");
3229
+ content.className = "notification-content";
3230
+ content.appendChild(buildTitle(record.title));
3231
+ if (record.opts.description) content.appendChild(buildDescription(record.opts.description));
3232
+ const actions = buildActions(record, getHandle);
3233
+ if (actions) content.appendChild(actions);
3234
+ const link = buildLink(record);
3235
+ if (link) content.appendChild(link);
3236
+ const progress = buildProgressBar(record);
3237
+ if (progress) content.appendChild(progress);
3238
+ el.appendChild(content);
3239
+ const close = buildClose(record);
3240
+ if (close) el.appendChild(close);
3241
+ const countdown = buildCountdownBar(record);
3242
+ if (countdown) el.appendChild(countdown);
3243
+ if (record.opts.onClick) {
3244
+ const onClick2 = record.opts.onClick;
3245
+ const handleClick = () => onClick2(getHandle());
3246
+ el.addEventListener("click", handleClick);
3247
+ el.addEventListener("keydown", (ev) => {
3248
+ if (ev.key === "Enter" || ev.key === " ") {
3249
+ ev.preventDefault();
3250
+ handleClick();
3251
+ }
3252
+ });
3253
+ }
3254
+ record.el = el;
3255
+ return el;
3256
+ }
3257
+ function pushToContainer(container, el, priority) {
3258
+ if (priority === "high") {
3259
+ const firstNormal = Array.from(container.children).find(
3260
+ (c) => c instanceof HTMLElement && !c.classList.contains("is-priority")
3261
+ );
3262
+ if (firstNormal) container.insertBefore(el, firstNormal);
3263
+ else container.appendChild(el);
3264
+ } else {
3265
+ container.appendChild(el);
3266
+ }
3267
+ }
3268
+ function removeOne(el, onClose) {
3269
+ if (el.classList.contains("leaving")) return;
3270
+ el.classList.add("leaving");
3271
+ let done = false;
3272
+ const finish = () => {
3273
+ if (done) return;
3274
+ done = true;
3275
+ el.remove();
3276
+ onClose?.();
3277
+ };
3278
+ el.addEventListener("transitionend", finish, { once: true });
3279
+ window.setTimeout(finish, 360);
3280
+ }
3281
+ function trimStack2(container) {
3282
+ for (; ; ) {
3283
+ const items = Array.from(container.querySelectorAll(":scope > .notification"));
3284
+ if (items.length <= cfg.maxStack) return;
3285
+ const target = items.find((it) => !it.classList.contains("is-priority")) ?? items[0];
3286
+ if (!target) return;
3287
+ removeOne(target);
3288
+ }
3289
+ }
3290
+ function attachTimer(record, handle) {
3291
+ if (!record.duration || record.duration <= 0) return;
3292
+ const el = record.el;
3293
+ if (!el) return;
3294
+ const start = () => {
3295
+ if (record.timer) window.clearTimeout(record.timer);
3296
+ record.timer = window.setTimeout(() => dismissRecord(record.id), record.remaining);
3297
+ record.paused = false;
3298
+ };
3299
+ const pause = () => {
3300
+ if (record.paused) return;
3301
+ if (record.timer) {
3302
+ window.clearTimeout(record.timer);
3303
+ record.timer = 0;
3304
+ }
3305
+ record.paused = true;
3306
+ el.classList.add("is-paused");
3307
+ const fill = el.querySelector(".notification-countdown-fill");
3308
+ if (fill) fill.style.animationPlayState = "paused";
3309
+ };
3310
+ const resume = () => {
3311
+ if (!record.paused) return;
3312
+ el.classList.remove("is-paused");
3313
+ const fill = el.querySelector(".notification-countdown-fill");
3314
+ if (fill) fill.style.animationPlayState = "running";
3315
+ start();
3316
+ };
3317
+ const pauseOnHover = record.opts.pauseOnHover ?? cfg.pauseOnHover;
3318
+ if (pauseOnHover) {
3319
+ el.addEventListener("mouseenter", () => handle.pause());
3320
+ el.addEventListener("mouseleave", () => handle.resume());
3321
+ }
3322
+ handle.pause = pause;
3323
+ handle.resume = resume;
3324
+ start();
3325
+ }
3326
+ function makeHandle(record) {
3327
+ const handle = {
3328
+ id: record.id,
3329
+ el: record.el ?? document.createElement("div"),
3330
+ dismiss: () => dismissRecord(record.id),
3331
+ update: (patch) => updateRecord(record.id, patch),
3332
+ setProgress: (value, label) => updateRecord(record.id, {
3333
+ progress: Math.min(1, Math.max(0, value)),
3334
+ progressLabel: label
3335
+ }),
3336
+ markRead: () => updateRecord(record.id, { unread: false }),
3337
+ pause: () => {
3338
+ },
3339
+ resume: () => {
3340
+ }
3341
+ };
3342
+ return handle;
3343
+ }
3344
+ function showRecord(title, kind, opts) {
3345
+ if (!isBrowser3()) return null;
3346
+ const container = ensureContainer2();
3347
+ const id = opts.id ?? genId();
3348
+ if (store.has(id)) dismissRecord(id);
3349
+ const duration = opts.duration ?? 0;
3350
+ const record = {
3351
+ id,
3352
+ title,
3353
+ kind,
3354
+ opts: { unread: opts.unread ?? false, ...opts, id },
3355
+ el: null,
3356
+ createdAt: Date.now(),
3357
+ duration,
3358
+ remaining: duration,
3359
+ paused: false,
3360
+ timer: 0
3361
+ };
3362
+ const handle = makeHandle(record);
3363
+ const el = renderNotification(record, container, () => handle);
3364
+ record.el = el;
3365
+ handle.el = el;
3366
+ pushToContainer(container, el, record.opts.priority ?? "normal");
3367
+ store.set(id, record);
3368
+ attachTimer(record, handle);
3369
+ trimStack2(container);
3370
+ persistSave();
3371
+ playSound();
3372
+ opts.onShow?.(handle);
3373
+ return handle;
3374
+ }
3375
+ function dismissRecord(id) {
3376
+ const record = store.get(id);
3377
+ if (!record) {
3378
+ const el = document.getElementById(id);
3379
+ if (el && el.classList.contains("notification")) removeOne(el);
3380
+ return;
3381
+ }
3382
+ if (record.timer) window.clearTimeout(record.timer);
3383
+ const handle = makeHandle(record);
3384
+ const onClose = record.opts.onClose;
3385
+ store.delete(id);
3386
+ if (record.el) removeOne(record.el, () => onClose?.(handle));
3387
+ else onClose?.(handle);
3388
+ persistSave();
3389
+ }
3390
+ function updateRecord(id, patch) {
3391
+ const record = store.get(id);
3392
+ if (!record) return;
3393
+ if (patch.title !== void 0) record.title = patch.title;
3394
+ if (patch.description !== void 0) record.opts.description = patch.description;
3395
+ if (patch.duration !== void 0) {
3396
+ record.duration = patch.duration;
3397
+ record.remaining = patch.duration;
3398
+ }
3399
+ if (patch.unread !== void 0) record.opts.unread = patch.unread;
3400
+ if (patch.progress !== void 0) record.opts.progress = patch.progress;
3401
+ if (patch.progressLabel !== void 0) record.opts.progressLabel = patch.progressLabel;
3402
+ if (patch.actions !== void 0) record.opts.actions = patch.actions;
3403
+ if (patch.tag !== void 0) record.opts.tag = patch.tag;
3404
+ if (patch.priority !== void 0) record.opts.priority = patch.priority;
3405
+ if (patch.icon !== void 0) record.opts.icon = patch.icon;
3406
+ if (patch.avatar !== void 0) record.opts.avatar = patch.avatar;
3407
+ if (patch.link !== void 0) record.opts.link = patch.link;
3408
+ if (patch.linkLabel !== void 0) record.opts.linkLabel = patch.linkLabel;
3409
+ const el = record.el;
3410
+ if (!el) return;
3411
+ if (patch.title !== void 0) {
3412
+ const t = el.querySelector(".notification-title");
3413
+ if (t) t.textContent = patch.title;
3414
+ }
3415
+ if (patch.description !== void 0) {
3416
+ let m = el.querySelector(".notification-message");
3417
+ if (patch.description) {
3418
+ if (!m) {
3419
+ m = document.createElement("p");
3420
+ m.className = "notification-message";
3421
+ m.style.whiteSpace = "pre-wrap";
3422
+ const content = el.querySelector(".notification-content");
3423
+ const titleEl = content?.querySelector(".notification-title");
3424
+ if (titleEl && titleEl.nextSibling) content?.insertBefore(m, titleEl.nextSibling);
3425
+ else content?.appendChild(m);
3426
+ }
3427
+ m.textContent = patch.description;
3428
+ } else if (m) {
3429
+ m.remove();
3430
+ }
3431
+ }
3432
+ if (patch.unread !== void 0) {
3433
+ el.classList.toggle("is-unread", patch.unread);
3434
+ }
3435
+ if (patch.priority !== void 0) {
3436
+ el.classList.toggle("is-priority", patch.priority === "high");
3437
+ }
3438
+ if (patch.progress !== void 0 || patch.progressLabel !== void 0) {
3439
+ el.classList.add("is-progress");
3440
+ let progWrap = el.querySelector(".notification-progress");
3441
+ const pct3 = Math.min(100, Math.max(0, (record.opts.progress ?? 0) * 100));
3442
+ if (!progWrap) {
3443
+ progWrap = document.createElement("div");
3444
+ progWrap.className = "notification-progress";
3445
+ const track = document.createElement("div");
3446
+ track.className = "notification-progress-track";
3447
+ const fill2 = document.createElement("div");
3448
+ fill2.className = "notification-progress-bar";
3449
+ track.appendChild(fill2);
3450
+ progWrap.appendChild(track);
3451
+ el.querySelector(".notification-content")?.appendChild(progWrap);
3452
+ }
3453
+ const fill = progWrap.querySelector(".notification-progress-bar");
3454
+ if (fill) fill.style.width = pct3 + "%";
3455
+ let lbl = progWrap.querySelector(".notification-progress-label");
3456
+ const labelText = record.opts.progressLabel ?? Math.round(pct3) + "%";
3457
+ if (!lbl) {
3458
+ lbl = document.createElement("span");
3459
+ lbl.className = "notification-progress-label";
3460
+ progWrap.appendChild(lbl);
3461
+ }
3462
+ lbl.textContent = labelText;
3463
+ }
3464
+ persistSave();
3465
+ }
3466
+ function confirmDialog(title, opts = {}) {
3467
+ return new Promise((resolve) => {
3468
+ if (!isBrowser3()) {
3469
+ resolve(false);
3470
+ return;
3471
+ }
3472
+ let resolved = false;
3473
+ const settle = (val) => {
3474
+ if (resolved) return;
3475
+ resolved = true;
3476
+ resolve(val);
3477
+ };
3478
+ const confirmLabel = opts.confirmLabel ?? "\u786E\u8BA4";
3479
+ const cancelLabel = opts.cancelLabel ?? "\u53D6\u6D88";
3480
+ const actions = [
3481
+ {
3482
+ label: cancelLabel,
3483
+ type: "ghost",
3484
+ onClick: () => settle(false)
3485
+ },
3486
+ {
3487
+ label: confirmLabel,
3488
+ type: "primary",
3489
+ onClick: () => settle(true)
3490
+ }
3491
+ ];
3492
+ const handle = showRecord(title, "info", {
3493
+ ...opts,
3494
+ actions,
3495
+ closable: false,
3496
+ onClose: () => settle(false)
3497
+ });
3498
+ if (!handle) settle(false);
3499
+ });
3500
+ }
3501
+ function createNotificationCenter(parent = document.body, opts = {}) {
3502
+ if (!isBrowser3()) {
3503
+ return {
3504
+ push: () => null,
3505
+ dismiss() {
3506
+ },
3507
+ clear() {
3508
+ },
3509
+ markAllRead() {
3510
+ },
3511
+ get: () => null,
3512
+ getAll: () => [],
3513
+ config() {
3514
+ },
3515
+ el: {}
3516
+ };
3517
+ }
3518
+ const local = {
3519
+ position: opts.position ?? "top-right",
3520
+ maxStack: opts.maxStack ?? 5,
3521
+ pauseOnHover: opts.pauseOnHover ?? true,
3522
+ persist: opts.persist ?? false,
3523
+ sound: opts.sound ?? false
3524
+ };
3525
+ const container = document.createElement("div");
3526
+ container.className = "notification-container";
3527
+ container.setAttribute("role", "region");
3528
+ container.setAttribute("aria-label", "\u901A\u77E5");
3529
+ container.dataset.position = local.position;
3530
+ parent.appendChild(container);
3531
+ const localStore = /* @__PURE__ */ new Map();
3532
+ function push(input) {
3533
+ const kind = input.kind ?? "info";
3534
+ const opts2 = input.options ?? {};
3535
+ const id = opts2.id ?? genId();
3536
+ const duration = opts2.duration ?? 0;
3537
+ const record = {
3538
+ id,
3539
+ title: input.title,
3540
+ kind,
3541
+ opts: { unread: opts2.unread ?? false, ...opts2, id },
3542
+ el: null,
3543
+ createdAt: Date.now(),
3544
+ duration,
3545
+ remaining: duration,
3546
+ paused: false,
3547
+ timer: 0
3548
+ };
3549
+ const handle = makeHandle(record);
3550
+ const el = renderNotification(record, container, () => handle);
3551
+ record.el = el;
3552
+ handle.el = el;
3553
+ pushToContainer(container, el, record.opts.priority ?? "normal");
3554
+ localStore.set(id, record);
3555
+ store.set(id, record);
3556
+ attachTimer(record, handle);
3557
+ for (; ; ) {
3558
+ const items = Array.from(container.querySelectorAll(":scope > .notification"));
3559
+ if (items.length <= local.maxStack) break;
3560
+ const target = items.find((it) => !it.classList.contains("is-priority")) ?? items[0];
3561
+ if (!target) break;
3562
+ const tid = target.id;
3563
+ removeOne(target);
3564
+ const rec = tid ? localStore.get(tid) : null;
3565
+ if (rec) {
3566
+ localStore.delete(tid);
3567
+ store.delete(tid);
3568
+ }
3569
+ }
3570
+ opts2.onShow?.(handle);
3571
+ return handle;
3572
+ }
3573
+ function dismiss(id) {
3574
+ if (id) {
3575
+ const rec = localStore.get(id);
3576
+ if (!rec) return;
3577
+ if (rec.timer) window.clearTimeout(rec.timer);
3578
+ const handle = makeHandle(rec);
3579
+ localStore.delete(id);
3580
+ store.delete(id);
3581
+ if (rec.el) removeOne(rec.el, () => rec.opts.onClose?.(handle));
3582
+ return;
3583
+ }
3584
+ Array.from(localStore.keys()).forEach((k) => dismiss(k));
3585
+ }
3586
+ function clear() {
3587
+ dismiss();
3588
+ }
3589
+ function markAllRead() {
3590
+ localStore.forEach((rec) => {
3591
+ rec.opts.unread = false;
3592
+ rec.el?.classList.remove("is-unread");
3593
+ });
3594
+ }
3595
+ return {
3596
+ push,
3597
+ dismiss,
3598
+ clear,
3599
+ markAllRead,
3600
+ get: (id) => localStore.get(id) ?? null,
3601
+ getAll: () => Array.from(localStore.values()),
3602
+ config: (next) => {
3603
+ if (next.position) {
3604
+ local.position = next.position;
3605
+ container.dataset.position = next.position;
3606
+ }
3607
+ if (typeof next.maxStack === "number" && next.maxStack > 0) local.maxStack = next.maxStack;
3608
+ if (typeof next.pauseOnHover === "boolean") local.pauseOnHover = next.pauseOnHover;
3609
+ if (next.persist !== void 0) local.persist = next.persist;
3610
+ if (next.sound !== void 0) local.sound = next.sound;
3611
+ },
3612
+ el: container
3613
+ };
3614
+ }
3615
+ var notify = Object.assign(
3616
+ (title, opts) => showRecord(title, "info", opts ?? {}),
3617
+ {
3618
+ success: (title, opts) => showRecord(title, "success", opts ?? {}),
3619
+ warn: (title, opts) => showRecord(title, "warning", opts ?? {}),
3620
+ error: (title, opts) => showRecord(title, "error", opts ?? {}),
3621
+ info: (title, opts) => showRecord(title, "info", opts ?? {}),
3622
+ progress: (title, opts) => showRecord(title, "info", { progress: opts?.progress ?? 0, ...opts }),
3623
+ confirm: (title, opts) => confirmDialog(title, opts),
3624
+ dismiss: (id) => {
3625
+ if (!isBrowser3()) return;
3626
+ if (id) {
3627
+ dismissRecord(id);
3628
+ return;
3629
+ }
3630
+ Array.from(store.keys()).forEach((k) => dismissRecord(k));
3631
+ },
3632
+ clear: () => {
3633
+ if (!isBrowser3()) return;
3634
+ Array.from(store.keys()).forEach((k) => dismissRecord(k));
3635
+ },
3636
+ markRead: (id) => {
3637
+ if (!isBrowser3()) return;
3638
+ if (id) {
3639
+ updateRecord(id, { unread: false });
3640
+ return;
3641
+ }
3642
+ store.forEach((_, k) => updateRecord(k, { unread: false }));
3643
+ },
3644
+ update: (id, patch) => updateRecord(id, patch),
3645
+ get: (id) => store.get(id) ?? null,
3646
+ getAll: () => Array.from(store.values()),
3647
+ config: (opts) => {
3648
+ if (opts.position) {
3649
+ cfg.position = opts.position;
3650
+ const c = document.body.querySelector(".notification-container[data-notif-default]");
3651
+ if (c) c.dataset.position = opts.position;
3652
+ }
3653
+ if (typeof opts.maxStack === "number" && opts.maxStack > 0) cfg.maxStack = opts.maxStack;
3654
+ if (typeof opts.pauseOnHover === "boolean") cfg.pauseOnHover = opts.pauseOnHover;
3655
+ if (opts.persist !== void 0) cfg.persist = opts.persist;
3656
+ if (opts.sound !== void 0) cfg.sound = opts.sound;
3657
+ }
3658
+ }
3659
+ );
3660
+ function initNotification() {
3661
+ if (!isBrowser3()) return;
3662
+ if (globalInit.done) return;
3663
+ globalInit.done = true;
3664
+ ensureContainer2();
3665
+ if (cfg.persist) {
3666
+ const items = persistLoad();
3667
+ const container = ensureContainer2();
3668
+ for (const item of items) {
3669
+ const record = {
3670
+ id: item.id,
3671
+ title: item.title,
3672
+ kind: item.kind,
3673
+ opts: {
3674
+ id: item.id,
3675
+ description: item.description,
3676
+ tag: item.tag,
3677
+ unread: item.unread,
3678
+ priority: item.priority,
3679
+ progress: item.progress,
3680
+ progressLabel: item.progressLabel,
3681
+ link: item.link,
3682
+ linkLabel: item.linkLabel,
3683
+ data: item.data
3684
+ },
3685
+ el: null,
3686
+ createdAt: item.createdAt ?? Date.now(),
3687
+ duration: 0,
3688
+ remaining: 0,
3689
+ paused: false,
3690
+ timer: 0
3691
+ };
3692
+ const handle = makeHandle(record);
3693
+ const el = renderNotification(record, container, () => handle);
3694
+ record.el = el;
3695
+ handle.el = el;
3696
+ pushToContainer(container, el, record.opts.priority ?? "normal");
3697
+ store.set(record.id, record);
3698
+ }
3699
+ }
3700
+ }
3701
+
3702
+ // src/behaviors/back-top.ts
3703
+ var AUTO_BTN_SVG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg>';
3704
+ function prefersReducedMotion2() {
3705
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
3706
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
3707
+ }
3708
+ function getScrollTarget(target) {
3709
+ if (!target) return { el: null, isWindow: true };
3710
+ const el = document.querySelector(target);
3711
+ return { el, isWindow: false };
3712
+ }
3713
+ function getScrollY(target) {
3714
+ if (!target) return window.scrollY;
3715
+ const el = document.querySelector(target);
3716
+ return el ? el.scrollTop : window.scrollY;
3717
+ }
3718
+ function scrollToTop(target, offset = 0) {
3719
+ const reduce = prefersReducedMotion2();
3720
+ const behavior = reduce ? "auto" : "smooth";
3721
+ if (!target) {
3722
+ window.scrollTo({ top: offset, behavior });
3723
+ return;
3724
+ }
3725
+ const el = document.querySelector(target);
3726
+ if (el) el.scrollTo({ top: offset, behavior });
3727
+ }
3728
+ function setup11(btn, opts = {}) {
3729
+ const el = btn;
3730
+ if (el.__icenBackTopInit) return;
3731
+ el.__icenBackTopInit = true;
3732
+ const threshold = Number(btn.dataset.backTopThreshold ?? opts.threshold ?? 320) || 320;
3733
+ const target = btn.dataset.backTopTarget ?? opts.target;
3734
+ const offset = Number(btn.dataset.backTopOffset ?? opts.offset ?? 0) || 0;
3735
+ let ticking = false;
3736
+ const apply = () => {
3737
+ ticking = false;
3738
+ const y = getScrollY(target);
3739
+ btn.classList.toggle("is-shown", y > threshold);
3740
+ };
3741
+ const onScroll2 = () => {
3742
+ if (ticking) return;
3743
+ ticking = true;
3744
+ window.requestAnimationFrame(apply);
3745
+ };
3746
+ const { el: targetEl, isWindow } = getScrollTarget(target);
3747
+ if (isWindow) {
3748
+ window.addEventListener("scroll", onScroll2, { passive: true });
3749
+ } else if (targetEl) {
3750
+ targetEl.addEventListener("scroll", onScroll2, { passive: true });
3751
+ }
3752
+ window.addEventListener("resize", onScroll2, { passive: true });
3753
+ btn.addEventListener("click", () => scrollToTop(target, offset));
3754
+ apply();
3755
+ }
3756
+ function initBackTop(opts = {}) {
3757
+ if (typeof document === "undefined") return;
3758
+ if (typeof window === "undefined") return;
3759
+ const root = opts.root ?? document;
3760
+ if (opts.autoCreate) {
3761
+ let btn = document.body.querySelector(".back-top[data-back-top-auto]");
3762
+ if (!btn) {
3763
+ btn = document.createElement("button");
3764
+ btn.type = "button";
3765
+ btn.className = "back-top";
3766
+ btn.setAttribute("data-back-top", "");
3767
+ btn.setAttribute("data-back-top-auto", "");
3768
+ btn.setAttribute("aria-label", "\u56DE\u5230\u9876\u90E8");
3769
+ btn.innerHTML = AUTO_BTN_SVG;
3770
+ document.body.appendChild(btn);
3771
+ }
3772
+ setup11(btn, opts);
3773
+ }
3774
+ const candidates = [];
3775
+ if (root instanceof Element && root.matches(".back-top[data-back-top]")) candidates.push(root);
3776
+ candidates.push(...Array.from(root.querySelectorAll(".back-top[data-back-top]")));
3777
+ for (const c of candidates) {
3778
+ const el = c;
3779
+ if (el.hasAttribute("data-back-top-auto") && opts.autoCreate) continue;
3780
+ setup11(el, opts);
3781
+ }
3782
+ }
3783
+
3784
+ // src/behaviors/command-palette.ts
3785
+ var FOCUSABLE2 = 'button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex="-1"])';
3786
+ var openId2 = null;
3787
+ var restoreFocusTo2 = null;
3788
+ var prevBodyOverflow2 = "";
3789
+ var keyHandler2 = null;
3790
+ var globalKeyHandler = null;
3791
+ var initialized5 = false;
3792
+ function findBackdrop2(id) {
3793
+ return document.querySelector(
3794
+ `.command-palette-backdrop[data-command-palette="${CSS.escape(id)}"]`
3795
+ );
3796
+ }
3797
+ function getVisibleItems(panel2) {
3798
+ return Array.from(
3799
+ panel2.querySelectorAll(".command-palette-item:not([hidden]):not([disabled])")
3800
+ ).filter((el) => el.offsetParent !== null || el.getClientRects().length > 0);
3801
+ }
3802
+ function isDisabledItem(el) {
3803
+ return el.hasAttribute("disabled") || el.getAttribute("aria-disabled") === "true";
3804
+ }
3805
+ function setActive(panel2, idx) {
3806
+ const items = getVisibleItems(panel2);
3807
+ items.forEach((el, i) => el.classList.toggle("is-active", i === idx));
3808
+ const active2 = items[idx];
3809
+ if (active2 && typeof active2.scrollIntoView === "function") {
3810
+ active2.scrollIntoView({ block: "nearest" });
3811
+ }
3812
+ }
3813
+ function filterPanel(panel2, query) {
3814
+ const q = query.trim().toLowerCase();
3815
+ let visibleCount = 0;
3816
+ panel2.querySelectorAll(".command-palette-item").forEach((item) => {
3817
+ if (isDisabledItem(item)) return;
3818
+ const label = (item.querySelector(".command-palette-item-label")?.textContent ?? "").toLowerCase();
3819
+ const hint = (item.querySelector(".command-palette-item-hint")?.textContent ?? "").toLowerCase();
3820
+ const keyword = (item.dataset.commandPaletteKeyword ?? "").toLowerCase();
3821
+ const match = !q || label.includes(q) || hint.includes(q) || keyword.includes(q);
3822
+ item.hidden = !match;
3823
+ item.classList.remove("is-active");
3824
+ if (match) visibleCount++;
3825
+ });
3826
+ panel2.querySelectorAll(".command-palette-group").forEach((group) => {
3827
+ const hasVisible = Array.from(group.querySelectorAll(".command-palette-item")).some(
3828
+ (it) => !it.hidden
3829
+ );
3830
+ group.hidden = !hasVisible;
3831
+ });
3832
+ const empty = panel2.querySelector(".command-palette-empty");
3833
+ if (empty) empty.hidden = visibleCount !== 0;
3834
+ if (visibleCount > 0) setActive(panel2, 0);
3835
+ }
3836
+ function openCommandPalette(id) {
3837
+ if (typeof document === "undefined") return;
3838
+ if (openId2 === id) return;
3839
+ if (openId2) closeCommandPalette(openId2);
3840
+ const backdrop = findBackdrop2(id);
3841
+ if (!backdrop) return;
3842
+ const panel2 = backdrop.querySelector(".command-palette");
3843
+ if (!panel2) return;
3844
+ if (backdrop.parentElement !== document.body) document.body.appendChild(backdrop);
3845
+ restoreFocusTo2 = document.activeElement instanceof HTMLElement ? document.activeElement : null;
3846
+ openId2 = id;
3847
+ backdrop.hidden = false;
3848
+ prevBodyOverflow2 = document.body.style.overflow;
3849
+ document.body.style.overflow = "hidden";
3850
+ const input = panel2.querySelector(".command-palette-input");
3851
+ if (input) {
3852
+ input.value = "";
3853
+ input.focus();
3854
+ } else {
3855
+ panel2.focus();
3856
+ }
3857
+ filterPanel(panel2, "");
3858
+ keyHandler2 = (e) => {
3859
+ if (e.key === "Escape") {
3860
+ e.preventDefault();
3861
+ closeCommandPalette(id);
3862
+ return;
3863
+ }
3864
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
3865
+ e.preventDefault();
3866
+ const items = getVisibleItems(panel2);
3867
+ if (items.length === 0) return;
3868
+ const cur = items.findIndex((el) => el.classList.contains("is-active"));
3869
+ const next = e.key === "ArrowDown" ? (cur + 1) % items.length : (cur - 1 + items.length) % items.length;
3870
+ setActive(panel2, next);
3871
+ return;
3872
+ }
3873
+ if (e.key === "Enter") {
3874
+ const active2 = panel2.querySelector(".command-palette-item.is-active");
3875
+ if (active2 && !isDisabledItem(active2)) {
3876
+ e.preventDefault();
3877
+ active2.click();
3878
+ }
3879
+ return;
3880
+ }
3881
+ if (e.key === "Tab") {
3882
+ e.preventDefault();
3883
+ const els = Array.from(panel2.querySelectorAll(FOCUSABLE2)).filter(
3884
+ (el) => !el.hidden && el.offsetParent !== null
3885
+ );
3886
+ if (els.length === 0) return;
3887
+ const cur = els.findIndex((el) => el === document.activeElement);
3888
+ const next = e.shiftKey ? (cur - 1 + els.length) % els.length : (cur + 1) % els.length;
3889
+ els[next]?.focus();
3890
+ }
3891
+ };
3892
+ window.addEventListener("keydown", keyHandler2);
3893
+ }
3894
+ function closeCommandPalette(id) {
3895
+ if (typeof document === "undefined") return;
3896
+ const backdrop = findBackdrop2(id);
3897
+ if (!backdrop) return;
3898
+ backdrop.hidden = true;
3899
+ if (openId2 !== id) return;
3900
+ openId2 = null;
3901
+ if (keyHandler2) {
3902
+ window.removeEventListener("keydown", keyHandler2);
3903
+ keyHandler2 = null;
3904
+ }
3905
+ document.body.style.overflow = prevBodyOverflow2;
3906
+ if (restoreFocusTo2 && restoreFocusTo2.isConnected) restoreFocusTo2.focus();
3907
+ restoreFocusTo2 = null;
3908
+ }
3909
+ function initCommandPalette(root = document) {
3910
+ if (typeof document === "undefined") return;
3911
+ if (typeof window === "undefined") return;
3912
+ root.querySelectorAll("[data-command-palette-open]").forEach((el) => el.setAttribute("aria-haspopup", "dialog"));
3913
+ if (initialized5) return;
3914
+ initialized5 = true;
3915
+ document.addEventListener("click", (e) => {
3916
+ const target = e.target instanceof Element ? e.target : null;
3917
+ if (!target) return;
3918
+ const opener = target.closest("[data-command-palette-open]");
3919
+ if (opener?.dataset.commandPaletteOpen) {
3920
+ openCommandPalette(opener.dataset.commandPaletteOpen);
3921
+ return;
3922
+ }
3923
+ const item = target.closest(".command-palette-item");
3924
+ if (item) {
3925
+ const backdrop = item.closest("[data-command-palette]");
3926
+ if (backdrop?.dataset.commandPalette) {
3927
+ if (!item.hasAttribute("data-command-palette-keep-open")) {
3928
+ closeCommandPalette(backdrop.dataset.commandPalette);
3929
+ }
3930
+ }
3931
+ return;
3932
+ }
3933
+ if (target instanceof HTMLElement && target.classList.contains("command-palette-backdrop") && target.dataset.commandPalette) {
3934
+ closeCommandPalette(target.dataset.commandPalette);
3935
+ }
3936
+ });
3937
+ document.addEventListener("input", (e) => {
3938
+ if (!(e.target instanceof HTMLElement)) return;
3939
+ if (!e.target.matches(".command-palette-input")) return;
3940
+ const backdrop = e.target.closest("[data-command-palette]");
3941
+ const panel2 = backdrop?.querySelector(".command-palette");
3942
+ if (panel2) filterPanel(panel2, e.target.value);
3943
+ });
3944
+ globalKeyHandler = (e) => {
3945
+ if ((e.metaKey || e.ctrlKey) && (e.key === "k" || e.key === "K")) {
3946
+ e.preventDefault();
3947
+ const first = document.querySelector(".command-palette-backdrop[data-command-palette]");
3948
+ if (first?.dataset.commandPalette) {
3949
+ if (openId2 === first.dataset.commandPalette) closeCommandPalette(openId2);
3950
+ else openCommandPalette(first.dataset.commandPalette);
3951
+ }
3952
+ }
3953
+ };
3954
+ window.addEventListener("keydown", globalKeyHandler);
3955
+ }
3956
+
3957
+ // src/behaviors/date-picker.ts
3958
+ var openPicker = null;
3959
+ var panelEl = null;
3960
+ var outsideHandler = null;
3961
+ var escHandler = null;
3962
+ function pad(n) {
3963
+ return n < 10 ? "0" + n : String(n);
3964
+ }
3965
+ function toDateStr(d) {
3966
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
3967
+ }
3968
+ function parseDateStr(s) {
3969
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s.trim());
3970
+ if (!m) return null;
3971
+ const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
3972
+ return Number.isNaN(d.getTime()) ? null : d;
3973
+ }
3974
+ function format(d, fmt2) {
3975
+ if (!d) return "";
3976
+ return fmt2.replace(/yyyy/g, String(d.getFullYear())).replace(/yy/g, String(d.getFullYear()).slice(-2)).replace(/mm/g, pad(d.getMonth() + 1)).replace(/m/g, String(d.getMonth() + 1)).replace(/dd/g, pad(d.getDate())).replace(/d/g, String(d.getDate()));
3977
+ }
3978
+ function ensurePanel() {
3979
+ if (panelEl && panelEl.isConnected) return panelEl;
3980
+ const el = document.createElement("div");
3981
+ el.className = "date-picker-panel";
3982
+ el.setAttribute("role", "dialog");
3983
+ el.setAttribute("aria-label", "\u9009\u62E9\u65E5\u671F");
3984
+ el.hidden = true;
3985
+ document.body.appendChild(el);
3986
+ panelEl = el;
3987
+ return el;
3988
+ }
3989
+ function positionPanel(panel2, trigger) {
3990
+ const r = trigger.getBoundingClientRect();
3991
+ const pw = 280;
3992
+ const ph = panel2.offsetHeight || 320;
3993
+ let left = r.left;
3994
+ let top = r.bottom + 4;
3995
+ if (left + pw > window.innerWidth - 12) left = window.innerWidth - pw - 12;
3996
+ if (left < 12) left = 12;
3997
+ if (top + ph > window.innerHeight - 12) top = r.top - ph - 4;
3998
+ if (top < 12) top = 12;
3999
+ panel2.style.left = left + "px";
4000
+ panel2.style.top = top + "px";
4001
+ }
4002
+ var CAL_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/></svg>';
4003
+ var WEEK_LABELS_ZH = ["\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D", "\u65E5"];
4004
+ var WEEK_LABELS_SUN = ["\u65E5", "\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D"];
4005
+ function renderCalendar(panel2, ctx) {
4006
+ const { view, selected, min, max, weekStart, onPick } = ctx;
4007
+ const year = view.getFullYear();
4008
+ const month = view.getMonth();
4009
+ const labels = weekStart === 0 ? WEEK_LABELS_SUN : WEEK_LABELS_ZH;
4010
+ const firstDay = new Date(year, month, 1);
4011
+ const offsetRaw = firstDay.getDay();
4012
+ const leadDays = weekStart === 0 ? offsetRaw : (offsetRaw + 6) % 7;
4013
+ const gridStart = new Date(year, month, 1 - leadDays);
4014
+ const today = /* @__PURE__ */ new Date();
4015
+ today.setHours(0, 0, 0, 0);
4016
+ const cells = [];
4017
+ for (let i = 0; i < 42; i++) {
4018
+ cells.push(new Date(gridStart.getFullYear(), gridStart.getMonth(), gridStart.getDate() + i));
4019
+ }
4020
+ let html = `<div class="date-picker-head">
4021
+ <button class="date-picker-nav" data-dp-nav="-1" type="button" aria-label="\u4E0A\u4E00\u6708">\u2039</button>
4022
+ <span class="date-picker-title">${year} \u5E74 ${month + 1} \u6708</span>
4023
+ <button class="date-picker-nav" data-dp-nav="1" type="button" aria-label="\u4E0B\u4E00\u6708">\u203A</button>
4024
+ </div><div class="date-picker-weekdays">${labels.map((l) => `<span>${l}</span>`).join("")}</div><div class="date-picker-grid">`;
4025
+ for (const d of cells) {
4026
+ const isOutside = d.getMonth() !== month;
4027
+ const ds = toDateStr(d);
4028
+ const isToday = d.getTime() === today.getTime();
4029
+ const isSelected = selected && ds === toDateStr(selected);
4030
+ const disabled = min && d < min || max && d > max;
4031
+ const classes = ["date-picker-cell"];
4032
+ if (isOutside) classes.push("is-outside");
4033
+ if (isToday) classes.push("is-today");
4034
+ if (isSelected) classes.push("is-selected");
4035
+ if (disabled) classes.push("is-disabled");
4036
+ html += `<button type="button" class="${classes.join(" ")}" data-dp-date="${ds}"${disabled ? " disabled" : ""}>${d.getDate()}</button>`;
4037
+ }
4038
+ html += `</div><div class="date-picker-foot">
4039
+ <button type="button" class="date-picker-today" data-dp-today>\u4ECA\u5929</button>
4040
+ <button type="button" class="date-picker-clear" data-dp-clear>\u6E05\u9664</button>
4041
+ </div>`;
4042
+ panel2.innerHTML = html;
4043
+ panel2.querySelector('[data-dp-nav="-1"]')?.addEventListener("click", () => {
4044
+ ctx.view = new Date(year, month - 1, 15);
4045
+ renderCalendar(panel2, { ...ctx, view: ctx.view });
4046
+ });
4047
+ panel2.querySelector('[data-dp-nav="1"]')?.addEventListener("click", () => {
4048
+ ctx.view = new Date(year, month + 1, 15);
4049
+ renderCalendar(panel2, { ...ctx, view: ctx.view });
4050
+ });
4051
+ panel2.querySelectorAll("[data-dp-date]").forEach((btn) => {
4052
+ btn.addEventListener("click", () => {
4053
+ const ds = btn.dataset.dpDate;
4054
+ if (!ds) return;
4055
+ const d = parseDateStr(ds);
4056
+ if (d) onPick(d);
4057
+ });
4058
+ });
4059
+ panel2.querySelector("[data-dp-today]")?.addEventListener("click", () => {
4060
+ onPick(new Date(today.getFullYear(), today.getMonth(), today.getDate()));
4061
+ });
4062
+ panel2.querySelector("[data-dp-clear]")?.addEventListener("click", () => {
4063
+ onPick(null);
4064
+ });
4065
+ }
4066
+ function closePanel() {
4067
+ if (!panelEl) return;
4068
+ panelEl.hidden = true;
4069
+ openPicker?.querySelector(".date-picker-trigger")?.classList.remove("is-open");
4070
+ openPicker = null;
4071
+ if (outsideHandler) {
4072
+ document.removeEventListener("mousedown", outsideHandler);
4073
+ outsideHandler = null;
4074
+ }
4075
+ if (escHandler) {
4076
+ window.removeEventListener("keydown", escHandler);
4077
+ escHandler = null;
4078
+ }
4079
+ }
4080
+ function setupPicker(container) {
4081
+ const el = container;
4082
+ if (el.__icenDatePickerInit) return;
4083
+ el.__icenDatePickerInit = true;
4084
+ const fmt2 = el.dataset.datePickerFormat ?? "yyyy-mm-dd";
4085
+ const minStr = el.dataset.datePickerMin;
4086
+ const maxStr = el.dataset.datePickerMax;
4087
+ const weekStart = Number(el.dataset.datePickerWeekStart ?? "1");
4088
+ const placeholder = el.dataset.datePickerPlaceholder ?? "\u9009\u62E9\u65E5\u671F";
4089
+ const min = minStr ? parseDateStr(minStr) : null;
4090
+ const max = maxStr ? parseDateStr(maxStr) : null;
4091
+ const hidden = el.querySelector('input[type="hidden"]');
4092
+ const initialStr = hidden?.value ?? "";
4093
+ const initialDate = initialStr ? parseDateStr(initialStr) : null;
4094
+ let trigger = el.querySelector(".date-picker-trigger");
4095
+ if (!trigger) {
4096
+ trigger = document.createElement("button");
4097
+ trigger.type = "button";
4098
+ trigger.className = "date-picker-trigger";
4099
+ trigger.innerHTML = `<span class="date-picker-value${initialDate ? "" : " is-empty"}"></span><span class="date-picker-icon">${CAL_ICON}</span>`;
4100
+ el.prepend(trigger);
4101
+ }
4102
+ trigger.setAttribute("aria-haspopup", "dialog");
4103
+ const valueEl = trigger.querySelector(".date-picker-value");
4104
+ function paint() {
4105
+ const v = hidden?.value ?? "";
4106
+ const d = v ? parseDateStr(v) : null;
4107
+ if (valueEl) {
4108
+ valueEl.textContent = d ? format(d, fmt2) : placeholder;
4109
+ valueEl.classList.toggle("is-empty", !d);
4110
+ }
4111
+ }
4112
+ paint();
4113
+ if (hidden) {
4114
+ const obs = new MutationObserver(() => paint());
4115
+ obs.observe(hidden, { attributes: true, attributeFilter: ["value"] });
4116
+ }
4117
+ trigger.addEventListener("click", () => {
4118
+ if (openPicker === el) {
4119
+ closePanel();
4120
+ return;
4121
+ }
4122
+ closePanel();
4123
+ openPicker = el;
4124
+ trigger.classList.add("is-open");
4125
+ const panel2 = ensurePanel();
4126
+ panel2.hidden = false;
4127
+ const selected = hidden?.value ? parseDateStr(hidden.value) : null;
4128
+ const view = selected ?? /* @__PURE__ */ new Date();
4129
+ view.setDate(15);
4130
+ positionPanel(panel2, trigger);
4131
+ renderCalendar(panel2, {
4132
+ view,
4133
+ selected,
4134
+ min,
4135
+ max,
4136
+ weekStart,
4137
+ onPick: (d) => {
4138
+ if (d === null) {
4139
+ if (hidden) {
4140
+ hidden.value = "";
4141
+ hidden.dispatchEvent(new Event("change", { bubbles: true }));
4142
+ }
4143
+ } else {
4144
+ if (hidden) {
4145
+ hidden.value = toDateStr(d);
4146
+ hidden.dispatchEvent(new Event("change", { bubbles: true }));
4147
+ }
4148
+ }
4149
+ paint();
4150
+ closePanel();
4151
+ }
4152
+ });
4153
+ outsideHandler = (ev) => {
4154
+ const t = ev.target instanceof Node ? ev.target : null;
4155
+ if (!t) return;
4156
+ if (panel2.contains(t) || el.contains(t)) return;
4157
+ closePanel();
4158
+ };
4159
+ document.addEventListener("mousedown", outsideHandler);
4160
+ escHandler = (ev) => {
4161
+ if (ev.key === "Escape") {
4162
+ ev.preventDefault();
4163
+ closePanel();
4164
+ }
4165
+ };
4166
+ window.addEventListener("keydown", escHandler);
4167
+ const reposition = () => {
4168
+ if (openPicker === el && panel2 && !panel2.hidden) positionPanel(panel2, trigger);
4169
+ };
4170
+ window.addEventListener("scroll", reposition, { passive: true, once: false });
4171
+ window.addEventListener("resize", reposition, { passive: true });
4172
+ });
4173
+ }
4174
+ function initDatePicker(root = document) {
4175
+ if (typeof document === "undefined") return;
4176
+ if (typeof window === "undefined") return;
4177
+ const pickers = [];
4178
+ if (root instanceof Element && root.matches(".date-picker[data-date-picker]")) pickers.push(root);
4179
+ pickers.push(...Array.from(root.querySelectorAll(".date-picker[data-date-picker]")));
4180
+ for (const p of pickers) setupPicker(p);
4181
+ }
4182
+
4183
+ // src/behaviors/split-pane.ts
4184
+ var initialized6 = /* @__PURE__ */ new WeakSet();
4185
+ function pct2(value, min, max) {
4186
+ return Math.min(max, Math.max(min, value));
4187
+ }
4188
+ function setup12(container) {
4189
+ const el = container;
4190
+ if (el.__icenSplitPaneInit) return;
4191
+ el.__icenSplitPaneInit = true;
4192
+ const dividerQuery = el.querySelector(".split-pane-divider");
4193
+ if (!dividerQuery) return;
4194
+ const divider = dividerQuery;
4195
+ const min = Number(el.dataset.splitMin ?? "10") || 10;
4196
+ const max = Number(el.dataset.splitMax ?? "90") || 90;
4197
+ const step = Number(el.dataset.splitStep ?? "2") || 2;
4198
+ const vertical = el.classList.contains("split-pane--vertical");
4199
+ const orientation = vertical ? "vertical" : "horizontal";
4200
+ divider.setAttribute("role", "separator");
4201
+ divider.setAttribute("aria-orientation", orientation === "vertical" ? "horizontal" : "vertical");
4202
+ divider.setAttribute("aria-valuemin", String(min));
4203
+ divider.setAttribute("aria-valuemax", String(max));
4204
+ divider.tabIndex = 0;
4205
+ function readCurrent() {
4206
+ const v = parseFloat(getComputedStyle(el).getPropertyValue("--split"));
4207
+ return Number.isFinite(v) ? v : 50;
4208
+ }
4209
+ function write(p) {
4210
+ const v = pct2(p, min, max);
4211
+ el.style.setProperty("--split", v + "%");
4212
+ divider.setAttribute("aria-valuenow", String(Math.round(v)));
4213
+ }
4214
+ if (!el.style.getPropertyValue("--split")) write(50);
4215
+ divider.setAttribute("aria-valuenow", String(Math.round(readCurrent())));
4216
+ let dragging = false;
4217
+ divider.addEventListener("pointerdown", (ev) => {
4218
+ dragging = true;
4219
+ divider.classList.add("is-dragging");
4220
+ divider.setPointerCapture(ev.pointerId);
4221
+ el.style.userSelect = "none";
4222
+ });
4223
+ const onUp = (ev) => {
4224
+ if (!dragging) return;
4225
+ dragging = false;
4226
+ divider.classList.remove("is-dragging");
4227
+ try {
4228
+ divider.releasePointerCapture(ev.pointerId);
4229
+ } catch {
4230
+ }
4231
+ el.style.userSelect = "";
4232
+ };
4233
+ divider.addEventListener("pointerup", onUp);
4234
+ divider.addEventListener("pointercancel", () => {
4235
+ dragging = false;
4236
+ divider.classList.remove("is-dragging");
4237
+ el.style.userSelect = "";
4238
+ });
4239
+ divider.addEventListener("pointermove", (ev) => {
4240
+ if (!dragging) return;
4241
+ const rect = el.getBoundingClientRect();
4242
+ if (vertical) {
4243
+ if (rect.height <= 0) return;
4244
+ const p = (ev.clientY - rect.top) / rect.height * 100;
4245
+ write(p);
4246
+ } else {
4247
+ if (rect.width <= 0) return;
4248
+ const p = (ev.clientX - rect.left) / rect.width * 100;
4249
+ write(p);
4250
+ }
4251
+ });
4252
+ divider.addEventListener("dblclick", () => write(50));
4253
+ divider.addEventListener("keydown", (ev) => {
4254
+ const current = readCurrent();
4255
+ let next = null;
4256
+ if (vertical) {
4257
+ if (ev.key === "ArrowUp") next = current - step;
4258
+ else if (ev.key === "ArrowDown") next = current + step;
4259
+ } else {
4260
+ if (ev.key === "ArrowLeft") next = current - step;
4261
+ else if (ev.key === "ArrowRight") next = current + step;
4262
+ }
4263
+ if (ev.key === "Home") next = min;
4264
+ else if (ev.key === "End") next = max;
4265
+ if (next === null) return;
4266
+ ev.preventDefault();
4267
+ write(next);
4268
+ });
4269
+ }
4270
+ function initSplitPane(root = document) {
4271
+ if (typeof document === "undefined") return;
4272
+ const panes = [];
4273
+ if (root instanceof Element && root.matches(".split-pane[data-split-pane]")) panes.push(root);
4274
+ panes.push(...Array.from(root.querySelectorAll(".split-pane[data-split-pane]")));
4275
+ for (const p of panes) {
4276
+ const el = p;
4277
+ if (initialized6.has(el)) continue;
4278
+ initialized6.add(el);
4279
+ setup12(el);
4280
+ }
4281
+ }
4282
+
2503
4283
  // src/index.ts
2504
4284
  var VERSION = package_default.version;
2505
4285
  export {
@@ -2508,28 +4288,37 @@ export {
2508
4288
  STYLES,
2509
4289
  VERSION,
2510
4290
  applyTheme,
4291
+ closeCommandPalette,
2511
4292
  closeMenu,
2512
4293
  closeModal,
2513
4294
  closePopover,
2514
4295
  computePopoverLayout,
4296
+ createNotificationCenter,
2515
4297
  getDefaultTheme,
2516
4298
  getTheme,
2517
4299
  initAccordion,
4300
+ initBackTop,
2518
4301
  initCarousel,
4302
+ initCommandPalette,
2519
4303
  initContextMenu,
2520
4304
  initCopy,
4305
+ initDatePicker,
2521
4306
  initDropdown,
2522
4307
  initInput,
2523
4308
  initModal,
2524
4309
  initNav,
4310
+ initNotification,
2525
4311
  initSelect,
2526
4312
  initSidebar,
2527
4313
  initSlider,
4314
+ initSplitPane,
2528
4315
  initTabs,
2529
4316
  initTagInput,
2530
4317
  initTheme,
2531
4318
  initTree,
2532
4319
  initUpload,
4320
+ notify,
4321
+ openCommandPalette,
2533
4322
  openContextMenu,
2534
4323
  openModal,
2535
4324
  openPopover,