@sovereignfs/ui 0.23.0 → 0.33.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/Button.module.css +75 -8
- package/dist/Calendar.module.css +126 -0
- package/dist/Card.module.css +4 -2
- package/dist/Checkbox.module.css +14 -2
- package/dist/CodeTextarea.module.css +53 -0
- package/dist/ConfirmDialog.module.css +109 -0
- package/dist/DatePicker.module.css +57 -0
- package/dist/Dialog.module.css +73 -46
- package/dist/DragHandleRow.module.css +9 -3
- package/dist/Drawer.module.css +61 -0
- package/dist/Icon.module.css +5 -0
- package/dist/Menu.module.css +82 -0
- package/dist/NavTabs.module.css +4 -2
- package/dist/OverlayHeader.module.css +98 -0
- package/dist/Popover.module.css +31 -1
- package/dist/SegmentedControl.module.css +4 -2
- package/dist/Sheet.module.css +55 -0
- package/dist/SplitPane.module.css +85 -0
- package/dist/StatusBadge.module.css +59 -0
- package/dist/SystemBanner.module.css +4 -2
- package/dist/Tabs.module.css +4 -2
- package/dist/TagInput.module.css +141 -0
- package/dist/Toast.module.css +4 -2
- package/dist/Tooltip.module.css +6 -1
- package/dist/index.d.ts +493 -17
- package/dist/index.js +1390 -209
- package/dist/tokens/primitives.css +19 -0
- package/dist/tokens/semantic.css +13 -0
- package/package.json +1 -1
- package/src/__tests__/motion.test.tsx +105 -0
- package/src/components/Button/Button.module.css +75 -8
- package/src/components/Button/Button.stories.tsx +6 -0
- package/src/components/Button/Button.tsx +17 -1
- package/src/components/Button/__tests__/Button.test.tsx +19 -0
- package/src/components/Calendar/Calendar.module.css +126 -0
- package/src/components/Calendar/Calendar.tsx +213 -0
- package/src/components/Calendar/__tests__/Calendar.test.tsx +98 -0
- package/src/components/Calendar/dateUtils.ts +97 -0
- package/src/components/Card/Card.module.css +4 -2
- package/src/components/Checkbox/Checkbox.module.css +14 -2
- package/src/components/Checkbox/Checkbox.tsx +22 -12
- package/src/components/CodeTextarea/CodeTextarea.module.css +53 -0
- package/src/components/CodeTextarea/CodeTextarea.stories.tsx +77 -0
- package/src/components/CodeTextarea/CodeTextarea.tsx +36 -0
- package/src/components/ConfirmDialog/ConfirmDialog.module.css +109 -0
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +124 -0
- package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +146 -0
- package/src/components/DatePicker/DatePicker.module.css +57 -0
- package/src/components/DatePicker/DatePicker.tsx +103 -0
- package/src/components/DatePicker/__tests__/DatePicker.test.tsx +89 -0
- package/src/components/Dialog/Dialog.module.css +73 -46
- package/src/components/Dialog/Dialog.stories.tsx +51 -1
- package/src/components/Dialog/Dialog.tsx +86 -22
- package/src/components/Dialog/__tests__/Dialog.test.tsx +67 -3
- package/src/components/DragHandleRow/DragHandleRow.module.css +9 -3
- package/src/components/Drawer/Drawer.module.css +61 -0
- package/src/components/Drawer/Drawer.tsx +117 -13
- package/src/components/Drawer/__tests__/Drawer.test.tsx +123 -2
- package/src/components/Icon/Icon.module.css +5 -0
- package/src/components/Icon/Icon.stories.tsx +4 -3
- package/src/components/Icon/Icon.tsx +1 -1
- package/src/components/Icon/icons/calendar.tsx +23 -0
- package/src/components/Icon/icons/ellipsis-vertical.tsx +22 -0
- package/src/components/Icon/icons/index.ts +8 -4
- package/src/components/Icon/icons/sliders-horizontal.tsx +9 -9
- package/src/components/Menu/Menu.module.css +82 -0
- package/src/components/Menu/Menu.tsx +137 -0
- package/src/components/Menu/__tests__/Menu.test.tsx +197 -0
- package/src/components/NavTabs/NavTabs.module.css +4 -2
- package/src/components/OverlayHeader/OverlayHeader.module.css +98 -0
- package/src/components/OverlayHeader/OverlayHeader.tsx +62 -0
- package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +51 -0
- package/src/components/Popover/Popover.module.css +31 -1
- package/src/components/Popover/Popover.stories.tsx +126 -1
- package/src/components/Popover/Popover.tsx +140 -7
- package/src/components/SegmentedControl/SegmentedControl.module.css +4 -2
- package/src/components/Sheet/Sheet.module.css +55 -0
- package/src/components/Sheet/Sheet.tsx +151 -0
- package/src/components/Sheet/__tests__/Sheet.test.tsx +99 -0
- package/src/components/SplitPane/SplitPane.module.css +85 -0
- package/src/components/SplitPane/SplitPane.stories.tsx +160 -0
- package/src/components/SplitPane/SplitPane.tsx +149 -0
- package/src/components/SplitPane/__tests__/SplitPane.test.tsx +51 -0
- package/src/components/StatusBadge/StatusBadge.module.css +59 -0
- package/src/components/StatusBadge/StatusBadge.stories.tsx +74 -0
- package/src/components/StatusBadge/StatusBadge.tsx +65 -0
- package/src/components/SystemBanner/SystemBanner.module.css +4 -2
- package/src/components/Tabs/Tabs.module.css +4 -2
- package/src/components/TagInput/TagInput.module.css +141 -0
- package/src/components/TagInput/TagInput.stories.tsx +109 -0
- package/src/components/TagInput/TagInput.tsx +174 -0
- package/src/components/TagInput/__tests__/TagInput.test.tsx +69 -0
- package/src/components/Toast/Toast.module.css +4 -2
- package/src/components/Tooltip/Tooltip.module.css +6 -1
- package/src/hooks/__tests__/useDoubleTap.test.tsx +117 -0
- package/src/hooks/__tests__/useIsMobile.test.tsx +80 -0
- package/src/hooks/__tests__/useLongPress.test.tsx +158 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useDoubleTap.ts +91 -0
- package/src/hooks/useIsMobile.ts +33 -0
- package/src/hooks/useLongPress.ts +185 -0
- package/src/index.ts +30 -1
- package/src/motion.ts +78 -0
- package/src/stories/DesignSystemOverview.stories.tsx +292 -4
- package/src/stories/InteractionHooks.stories.tsx +388 -0
- package/src/stories/TokenGallery.stories.tsx +85 -1
- package/src/tokens/primitives.css +19 -0
- package/src/tokens/semantic.css +13 -0
- package/src/components/Icon/icons/terminal.tsx +0 -19
package/dist/index.js
CHANGED
|
@@ -591,9 +591,9 @@ function AlertTriangleIcon(props) {
|
|
|
591
591
|
);
|
|
592
592
|
}
|
|
593
593
|
|
|
594
|
-
// src/components/Icon/icons/
|
|
594
|
+
// src/components/Icon/icons/calendar.tsx
|
|
595
595
|
import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
596
|
-
function
|
|
596
|
+
function CalendarIcon(props) {
|
|
597
597
|
return /* @__PURE__ */ jsxs21(
|
|
598
598
|
"svg",
|
|
599
599
|
{
|
|
@@ -606,23 +606,18 @@ function SlidersHorizontalIcon(props) {
|
|
|
606
606
|
strokeLinejoin: "round",
|
|
607
607
|
...props,
|
|
608
608
|
children: [
|
|
609
|
-
/* @__PURE__ */ jsx28("
|
|
610
|
-
/* @__PURE__ */ jsx28("
|
|
611
|
-
/* @__PURE__ */ jsx28("
|
|
612
|
-
/* @__PURE__ */ jsx28("
|
|
613
|
-
/* @__PURE__ */ jsx28("line", { x1: "21", x2: "16", y1: "20", y2: "20" }),
|
|
614
|
-
/* @__PURE__ */ jsx28("line", { x1: "12", x2: "3", y1: "20", y2: "20" }),
|
|
615
|
-
/* @__PURE__ */ jsx28("line", { x1: "14", x2: "14", y1: "2", y2: "6" }),
|
|
616
|
-
/* @__PURE__ */ jsx28("line", { x1: "8", x2: "8", y1: "10", y2: "14" }),
|
|
617
|
-
/* @__PURE__ */ jsx28("line", { x1: "16", x2: "16", y1: "18", y2: "22" })
|
|
609
|
+
/* @__PURE__ */ jsx28("path", { d: "M8 2v4" }),
|
|
610
|
+
/* @__PURE__ */ jsx28("path", { d: "M16 2v4" }),
|
|
611
|
+
/* @__PURE__ */ jsx28("rect", { width: "18", height: "18", x: "3", y: "4", rx: "2" }),
|
|
612
|
+
/* @__PURE__ */ jsx28("path", { d: "M3 10h18" })
|
|
618
613
|
]
|
|
619
614
|
}
|
|
620
615
|
);
|
|
621
616
|
}
|
|
622
617
|
|
|
623
|
-
// src/components/Icon/icons/
|
|
618
|
+
// src/components/Icon/icons/sliders-horizontal.tsx
|
|
624
619
|
import { jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
625
|
-
function
|
|
620
|
+
function SlidersHorizontalIcon(props) {
|
|
626
621
|
return /* @__PURE__ */ jsxs22(
|
|
627
622
|
"svg",
|
|
628
623
|
{
|
|
@@ -635,8 +630,38 @@ function TerminalIcon(props) {
|
|
|
635
630
|
strokeLinejoin: "round",
|
|
636
631
|
...props,
|
|
637
632
|
children: [
|
|
638
|
-
/* @__PURE__ */ jsx29("
|
|
639
|
-
/* @__PURE__ */ jsx29("
|
|
633
|
+
/* @__PURE__ */ jsx29("path", { d: "M10 5H3" }),
|
|
634
|
+
/* @__PURE__ */ jsx29("path", { d: "M12 19H3" }),
|
|
635
|
+
/* @__PURE__ */ jsx29("path", { d: "M14 3v4" }),
|
|
636
|
+
/* @__PURE__ */ jsx29("path", { d: "M16 17v4" }),
|
|
637
|
+
/* @__PURE__ */ jsx29("path", { d: "M21 12h-9" }),
|
|
638
|
+
/* @__PURE__ */ jsx29("path", { d: "M21 19h-5" }),
|
|
639
|
+
/* @__PURE__ */ jsx29("path", { d: "M21 5h-7" }),
|
|
640
|
+
/* @__PURE__ */ jsx29("path", { d: "M8 10v4" }),
|
|
641
|
+
/* @__PURE__ */ jsx29("path", { d: "M8 12H3" })
|
|
642
|
+
]
|
|
643
|
+
}
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/components/Icon/icons/ellipsis-vertical.tsx
|
|
648
|
+
import { jsx as jsx30, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
649
|
+
function EllipsisVerticalIcon(props) {
|
|
650
|
+
return /* @__PURE__ */ jsxs23(
|
|
651
|
+
"svg",
|
|
652
|
+
{
|
|
653
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
654
|
+
viewBox: "0 0 24 24",
|
|
655
|
+
fill: "none",
|
|
656
|
+
stroke: "currentColor",
|
|
657
|
+
strokeWidth: "2",
|
|
658
|
+
strokeLinecap: "round",
|
|
659
|
+
strokeLinejoin: "round",
|
|
660
|
+
...props,
|
|
661
|
+
children: [
|
|
662
|
+
/* @__PURE__ */ jsx30("circle", { cx: "12", cy: "12", r: "1" }),
|
|
663
|
+
/* @__PURE__ */ jsx30("circle", { cx: "12", cy: "5", r: "1" }),
|
|
664
|
+
/* @__PURE__ */ jsx30("circle", { cx: "12", cy: "19", r: "1" })
|
|
640
665
|
]
|
|
641
666
|
}
|
|
642
667
|
);
|
|
@@ -670,13 +695,14 @@ var ICONS = {
|
|
|
670
695
|
"grid-2x2": Grid2x2Icon,
|
|
671
696
|
info: InfoIcon,
|
|
672
697
|
"alert-triangle": AlertTriangleIcon,
|
|
698
|
+
calendar: CalendarIcon,
|
|
673
699
|
"sliders-horizontal": SlidersHorizontalIcon,
|
|
674
|
-
|
|
700
|
+
"ellipsis-vertical": EllipsisVerticalIcon
|
|
675
701
|
};
|
|
676
702
|
|
|
677
703
|
// src/components/Icon/Icon.tsx
|
|
678
704
|
import styles2 from "./Icon.module.css";
|
|
679
|
-
import { jsx as
|
|
705
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
680
706
|
function Icon({
|
|
681
707
|
name,
|
|
682
708
|
size = "md",
|
|
@@ -685,14 +711,14 @@ function Icon({
|
|
|
685
711
|
}) {
|
|
686
712
|
const Svg = ICONS[name];
|
|
687
713
|
const classes = [styles2.root, styles2[size], className].filter(Boolean).join(" ");
|
|
688
|
-
return /* @__PURE__ */
|
|
714
|
+
return /* @__PURE__ */ jsx31(Svg, { className: classes, role: aria["aria-label"] ? "img" : void 0, ...aria });
|
|
689
715
|
}
|
|
690
716
|
|
|
691
717
|
// src/components/Toggle/Toggle.tsx
|
|
692
718
|
import styles3 from "./Toggle.module.css";
|
|
693
|
-
import { jsx as
|
|
719
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
694
720
|
function Toggle({ checked, onChange, disabled, className, ...rest }) {
|
|
695
|
-
return /* @__PURE__ */
|
|
721
|
+
return /* @__PURE__ */ jsx32(
|
|
696
722
|
"button",
|
|
697
723
|
{
|
|
698
724
|
type: "button",
|
|
@@ -702,14 +728,14 @@ function Toggle({ checked, onChange, disabled, className, ...rest }) {
|
|
|
702
728
|
className: [styles3.track, checked ? styles3.on : styles3.off, className].filter(Boolean).join(" "),
|
|
703
729
|
onClick: () => onChange(!checked),
|
|
704
730
|
...rest,
|
|
705
|
-
children: /* @__PURE__ */
|
|
731
|
+
children: /* @__PURE__ */ jsx32("span", { className: styles3.thumb })
|
|
706
732
|
}
|
|
707
733
|
);
|
|
708
734
|
}
|
|
709
735
|
|
|
710
736
|
// src/components/SegmentedControl/SegmentedControl.tsx
|
|
711
737
|
import styles4 from "./SegmentedControl.module.css";
|
|
712
|
-
import { jsx as
|
|
738
|
+
import { jsx as jsx33 } from "react/jsx-runtime";
|
|
713
739
|
function SegmentedControl({
|
|
714
740
|
value,
|
|
715
741
|
onChange,
|
|
@@ -717,7 +743,7 @@ function SegmentedControl({
|
|
|
717
743
|
size = "md",
|
|
718
744
|
"aria-label": ariaLabel
|
|
719
745
|
}) {
|
|
720
|
-
return /* @__PURE__ */
|
|
746
|
+
return /* @__PURE__ */ jsx33(
|
|
721
747
|
"div",
|
|
722
748
|
{
|
|
723
749
|
role: "radiogroup",
|
|
@@ -725,7 +751,7 @@ function SegmentedControl({
|
|
|
725
751
|
className: [styles4.track, styles4[size]].join(" "),
|
|
726
752
|
children: options.map((opt) => {
|
|
727
753
|
const active = opt.value === value;
|
|
728
|
-
return /* @__PURE__ */
|
|
754
|
+
return /* @__PURE__ */ jsx33(
|
|
729
755
|
"button",
|
|
730
756
|
{
|
|
731
757
|
type: "button",
|
|
@@ -744,11 +770,11 @@ function SegmentedControl({
|
|
|
744
770
|
|
|
745
771
|
// src/components/Tabs/Tabs.tsx
|
|
746
772
|
import styles5 from "./Tabs.module.css";
|
|
747
|
-
import { jsx as
|
|
773
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
748
774
|
function Tabs({ items, value, onChange, "aria-label": ariaLabel }) {
|
|
749
|
-
return /* @__PURE__ */
|
|
775
|
+
return /* @__PURE__ */ jsx34("div", { role: "tablist", "aria-label": ariaLabel, className: styles5.tablist, children: items.map((item) => {
|
|
750
776
|
const active = item.value === value;
|
|
751
|
-
return /* @__PURE__ */
|
|
777
|
+
return /* @__PURE__ */ jsx34(
|
|
752
778
|
"button",
|
|
753
779
|
{
|
|
754
780
|
type: "button",
|
|
@@ -765,15 +791,15 @@ function Tabs({ items, value, onChange, "aria-label": ariaLabel }) {
|
|
|
765
791
|
|
|
766
792
|
// src/components/Select/Select.tsx
|
|
767
793
|
import styles6 from "./Select.module.css";
|
|
768
|
-
import { jsx as
|
|
794
|
+
import { jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
769
795
|
function Select({ className, size = "md", children, ...rest }) {
|
|
770
|
-
return /* @__PURE__ */
|
|
796
|
+
return /* @__PURE__ */ jsxs24(
|
|
771
797
|
"div",
|
|
772
798
|
{
|
|
773
799
|
className: [styles6.wrapper, size === "sm" ? styles6.sm : void 0, className].filter(Boolean).join(" "),
|
|
774
800
|
children: [
|
|
775
|
-
/* @__PURE__ */
|
|
776
|
-
/* @__PURE__ */
|
|
801
|
+
/* @__PURE__ */ jsx35("select", { className: styles6.select, ...rest, children }),
|
|
802
|
+
/* @__PURE__ */ jsx35(
|
|
777
803
|
"svg",
|
|
778
804
|
{
|
|
779
805
|
className: styles6.chevron,
|
|
@@ -786,7 +812,7 @@ function Select({ className, size = "md", children, ...rest }) {
|
|
|
786
812
|
strokeWidth: "2",
|
|
787
813
|
strokeLinecap: "round",
|
|
788
814
|
strokeLinejoin: "round",
|
|
789
|
-
children: /* @__PURE__ */
|
|
815
|
+
children: /* @__PURE__ */ jsx35("polyline", { points: "6 9 12 15 18 9" })
|
|
790
816
|
}
|
|
791
817
|
)
|
|
792
818
|
]
|
|
@@ -796,18 +822,18 @@ function Select({ className, size = "md", children, ...rest }) {
|
|
|
796
822
|
|
|
797
823
|
// src/components/SystemBanner/SystemBanner.tsx
|
|
798
824
|
import styles7 from "./SystemBanner.module.css";
|
|
799
|
-
import { jsx as
|
|
825
|
+
import { jsx as jsx36, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
800
826
|
function SystemBanner({ variant = "info", onDismiss, children }) {
|
|
801
|
-
return /* @__PURE__ */
|
|
802
|
-
/* @__PURE__ */
|
|
803
|
-
onDismiss && /* @__PURE__ */
|
|
827
|
+
return /* @__PURE__ */ jsxs25("div", { role: "status", "aria-live": "polite", className: [styles7.banner, styles7[variant]].join(" "), children: [
|
|
828
|
+
/* @__PURE__ */ jsx36("span", { className: styles7.content, children }),
|
|
829
|
+
onDismiss && /* @__PURE__ */ jsx36(
|
|
804
830
|
"button",
|
|
805
831
|
{
|
|
806
832
|
type: "button",
|
|
807
833
|
className: styles7.dismiss,
|
|
808
834
|
onClick: onDismiss,
|
|
809
835
|
"aria-label": "Dismiss banner",
|
|
810
|
-
children: /* @__PURE__ */
|
|
836
|
+
children: /* @__PURE__ */ jsx36(
|
|
811
837
|
"svg",
|
|
812
838
|
{
|
|
813
839
|
width: "14",
|
|
@@ -816,7 +842,7 @@ function SystemBanner({ variant = "info", onDismiss, children }) {
|
|
|
816
842
|
fill: "none",
|
|
817
843
|
xmlns: "http://www.w3.org/2000/svg",
|
|
818
844
|
"aria-hidden": true,
|
|
819
|
-
children: /* @__PURE__ */
|
|
845
|
+
children: /* @__PURE__ */ jsx36(
|
|
820
846
|
"path",
|
|
821
847
|
{
|
|
822
848
|
d: "M1 1L13 13M13 1L1 13",
|
|
@@ -833,19 +859,71 @@ function SystemBanner({ variant = "info", onDismiss, children }) {
|
|
|
833
859
|
}
|
|
834
860
|
|
|
835
861
|
// src/components/Popover/Popover.tsx
|
|
836
|
-
import {
|
|
862
|
+
import {
|
|
863
|
+
useEffect,
|
|
864
|
+
useLayoutEffect,
|
|
865
|
+
useRef,
|
|
866
|
+
useState
|
|
867
|
+
} from "react";
|
|
837
868
|
import styles8 from "./Popover.module.css";
|
|
838
|
-
import { jsx as
|
|
869
|
+
import { jsx as jsx37, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
870
|
+
var TRIGGER_GAP = 8;
|
|
871
|
+
var VIEWPORT_MARGIN = 4;
|
|
839
872
|
function Popover({
|
|
840
873
|
trigger,
|
|
841
874
|
open,
|
|
842
875
|
onClose,
|
|
843
876
|
align = "right",
|
|
844
877
|
width = 288,
|
|
878
|
+
panelStyle,
|
|
845
879
|
"aria-label": ariaLabel,
|
|
846
880
|
children
|
|
847
881
|
}) {
|
|
848
882
|
const containerRef = useRef(null);
|
|
883
|
+
const panelRef = useRef(null);
|
|
884
|
+
const [triggerWidth, setTriggerWidth] = useState(null);
|
|
885
|
+
const [openUpward, setOpenUpward] = useState(false);
|
|
886
|
+
const [maxHeight, setMaxHeight] = useState(null);
|
|
887
|
+
useEffect(() => {
|
|
888
|
+
if (width !== "trigger") return;
|
|
889
|
+
const el = containerRef.current?.firstElementChild;
|
|
890
|
+
if (!el) return;
|
|
891
|
+
function recompute() {
|
|
892
|
+
if (el) setTriggerWidth(el.getBoundingClientRect().width);
|
|
893
|
+
}
|
|
894
|
+
recompute();
|
|
895
|
+
const ro = new ResizeObserver(recompute);
|
|
896
|
+
ro.observe(el);
|
|
897
|
+
return () => ro.disconnect();
|
|
898
|
+
}, [width]);
|
|
899
|
+
useLayoutEffect(() => {
|
|
900
|
+
if (!open) {
|
|
901
|
+
setOpenUpward(false);
|
|
902
|
+
setMaxHeight(null);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
const container = containerRef.current;
|
|
906
|
+
const panel = panelRef.current;
|
|
907
|
+
if (!container || !panel) return;
|
|
908
|
+
const measure = () => {
|
|
909
|
+
const triggerRect = container.getBoundingClientRect();
|
|
910
|
+
const panelHeight2 = panel.getBoundingClientRect().height;
|
|
911
|
+
return {
|
|
912
|
+
panelHeight: panelHeight2,
|
|
913
|
+
spaceBelow: window.innerHeight - triggerRect.bottom - TRIGGER_GAP - VIEWPORT_MARGIN,
|
|
914
|
+
spaceAbove: triggerRect.top - TRIGGER_GAP - VIEWPORT_MARGIN
|
|
915
|
+
};
|
|
916
|
+
};
|
|
917
|
+
let { panelHeight, spaceBelow, spaceAbove } = measure();
|
|
918
|
+
if (panelHeight > spaceBelow && panelHeight > spaceAbove) {
|
|
919
|
+
container.scrollIntoView({ block: "start", behavior: "instant" });
|
|
920
|
+
({ panelHeight, spaceBelow, spaceAbove } = measure());
|
|
921
|
+
}
|
|
922
|
+
const upward = panelHeight > spaceBelow && spaceAbove > spaceBelow;
|
|
923
|
+
setOpenUpward(upward);
|
|
924
|
+
const available = upward ? spaceAbove : spaceBelow;
|
|
925
|
+
setMaxHeight(panelHeight > available ? Math.max(available, 0) : null);
|
|
926
|
+
}, [open, children]);
|
|
849
927
|
useEffect(() => {
|
|
850
928
|
if (!open) return;
|
|
851
929
|
function handleMouseDown(e) {
|
|
@@ -864,16 +942,21 @@ function Popover({
|
|
|
864
942
|
document.addEventListener("keydown", handleKeyDown);
|
|
865
943
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
866
944
|
}, [open, onClose]);
|
|
867
|
-
return /* @__PURE__ */
|
|
945
|
+
return /* @__PURE__ */ jsxs26("div", { ref: containerRef, className: styles8.container, children: [
|
|
868
946
|
trigger,
|
|
869
|
-
open && /* @__PURE__ */
|
|
947
|
+
open && /* @__PURE__ */ jsx37(
|
|
870
948
|
"div",
|
|
871
949
|
{
|
|
950
|
+
ref: panelRef,
|
|
872
951
|
role: "dialog",
|
|
873
952
|
"aria-label": ariaLabel,
|
|
874
953
|
"aria-modal": false,
|
|
875
|
-
className: [styles8.panel, styles8[align]].join(" "),
|
|
876
|
-
style: {
|
|
954
|
+
className: [styles8.panel, styles8[align], openUpward ? styles8.upward : ""].filter(Boolean).join(" "),
|
|
955
|
+
style: {
|
|
956
|
+
width: width === "trigger" ? triggerWidth ?? void 0 : width,
|
|
957
|
+
maxHeight: maxHeight ?? void 0,
|
|
958
|
+
...panelStyle
|
|
959
|
+
},
|
|
877
960
|
children
|
|
878
961
|
}
|
|
879
962
|
)
|
|
@@ -882,37 +965,372 @@ function Popover({
|
|
|
882
965
|
|
|
883
966
|
// src/components/Button/Button.tsx
|
|
884
967
|
import styles9 from "./Button.module.css";
|
|
885
|
-
import { jsx as
|
|
968
|
+
import { jsx as jsx38, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
886
969
|
function Button({
|
|
887
970
|
variant = "primary",
|
|
888
971
|
size = "md",
|
|
889
972
|
type = "button",
|
|
973
|
+
loading = false,
|
|
974
|
+
disabled,
|
|
890
975
|
className,
|
|
891
976
|
children,
|
|
892
977
|
...rest
|
|
893
978
|
}) {
|
|
894
979
|
const classes = [styles9.button, styles9[variant], styles9[size], className].filter(Boolean).join(" ");
|
|
895
|
-
return /* @__PURE__ */
|
|
980
|
+
return /* @__PURE__ */ jsxs27(
|
|
981
|
+
"button",
|
|
982
|
+
{
|
|
983
|
+
type,
|
|
984
|
+
className: classes,
|
|
985
|
+
disabled: disabled || loading,
|
|
986
|
+
"aria-busy": loading || void 0,
|
|
987
|
+
...rest,
|
|
988
|
+
children: [
|
|
989
|
+
loading && // aria-hidden: the button's own accessible name (its visible children,
|
|
990
|
+
// unchanged below) plus aria-busy already communicate the pending
|
|
991
|
+
// state — an unhidden Spinner-style status region would double-narrate it.
|
|
992
|
+
/* @__PURE__ */ jsx38("span", { className: styles9.spinner, "aria-hidden": "true" }),
|
|
993
|
+
children
|
|
994
|
+
]
|
|
995
|
+
}
|
|
996
|
+
);
|
|
896
997
|
}
|
|
897
998
|
|
|
898
999
|
// src/components/Input/Input.tsx
|
|
899
1000
|
import styles10 from "./Input.module.css";
|
|
900
|
-
import { jsx as
|
|
1001
|
+
import { jsx as jsx39 } from "react/jsx-runtime";
|
|
901
1002
|
function Input({ type = "text", className, ...rest }) {
|
|
902
1003
|
const classes = [styles10.input, className].filter(Boolean).join(" ");
|
|
903
|
-
return /* @__PURE__ */
|
|
1004
|
+
return /* @__PURE__ */ jsx39("input", { type, className: classes, ...rest });
|
|
904
1005
|
}
|
|
905
1006
|
|
|
906
1007
|
// src/components/Textarea/Textarea.tsx
|
|
907
1008
|
import styles11 from "./Textarea.module.css";
|
|
908
|
-
import { jsx as
|
|
1009
|
+
import { jsx as jsx40 } from "react/jsx-runtime";
|
|
909
1010
|
function Textarea({ className, rows = 4, ...rest }) {
|
|
910
1011
|
const classes = [styles11.textarea, className].filter(Boolean).join(" ");
|
|
911
|
-
return /* @__PURE__ */
|
|
1012
|
+
return /* @__PURE__ */ jsx40("textarea", { rows, className: classes, ...rest });
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// src/components/CodeTextarea/CodeTextarea.tsx
|
|
1016
|
+
import styles12 from "./CodeTextarea.module.css";
|
|
1017
|
+
import { jsx as jsx41 } from "react/jsx-runtime";
|
|
1018
|
+
function CodeTextarea({
|
|
1019
|
+
className,
|
|
1020
|
+
rows = 12,
|
|
1021
|
+
spellCheck = false,
|
|
1022
|
+
wrap = "off",
|
|
1023
|
+
invalid = false,
|
|
1024
|
+
"aria-invalid": ariaInvalid,
|
|
1025
|
+
...rest
|
|
1026
|
+
}) {
|
|
1027
|
+
const classes = [styles12.textarea, invalid ? styles12.invalid : void 0, className].filter(Boolean).join(" ");
|
|
1028
|
+
return /* @__PURE__ */ jsx41(
|
|
1029
|
+
"textarea",
|
|
1030
|
+
{
|
|
1031
|
+
rows,
|
|
1032
|
+
spellCheck,
|
|
1033
|
+
wrap,
|
|
1034
|
+
className: classes,
|
|
1035
|
+
"aria-invalid": ariaInvalid ?? (invalid || void 0),
|
|
1036
|
+
...rest
|
|
1037
|
+
}
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// src/components/StatusBadge/StatusBadge.tsx
|
|
1042
|
+
import styles13 from "./StatusBadge.module.css";
|
|
1043
|
+
import { jsx as jsx42, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
1044
|
+
var STATUS_CLASS = {
|
|
1045
|
+
unmodified: styles13.neutral,
|
|
1046
|
+
draft: styles13.info,
|
|
1047
|
+
committed: styles13.success,
|
|
1048
|
+
conflict: styles13.error,
|
|
1049
|
+
"pending-delete": styles13.warning,
|
|
1050
|
+
synced: styles13.success,
|
|
1051
|
+
warning: styles13.warning,
|
|
1052
|
+
error: styles13.error
|
|
1053
|
+
};
|
|
1054
|
+
var STATUS_LABEL = {
|
|
1055
|
+
unmodified: "Unmodified",
|
|
1056
|
+
draft: "Draft",
|
|
1057
|
+
committed: "Committed",
|
|
1058
|
+
conflict: "Conflict",
|
|
1059
|
+
"pending-delete": "Pending delete",
|
|
1060
|
+
synced: "Synced",
|
|
1061
|
+
warning: "Warning",
|
|
1062
|
+
error: "Error"
|
|
1063
|
+
};
|
|
1064
|
+
function StatusBadge({
|
|
1065
|
+
status,
|
|
1066
|
+
children,
|
|
1067
|
+
className,
|
|
1068
|
+
"aria-label": ariaLabel
|
|
1069
|
+
}) {
|
|
1070
|
+
const label = children ?? STATUS_LABEL[status];
|
|
1071
|
+
return /* @__PURE__ */ jsxs28(
|
|
1072
|
+
"span",
|
|
1073
|
+
{
|
|
1074
|
+
className: [styles13.badge, STATUS_CLASS[status], className].filter(Boolean).join(" "),
|
|
1075
|
+
"aria-label": ariaLabel,
|
|
1076
|
+
children: [
|
|
1077
|
+
/* @__PURE__ */ jsx42("span", { className: styles13.dot, "aria-hidden": "true" }),
|
|
1078
|
+
/* @__PURE__ */ jsx42("span", { className: styles13.label, children: label })
|
|
1079
|
+
]
|
|
1080
|
+
}
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// src/components/SplitPane/SplitPane.tsx
|
|
1085
|
+
import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
1086
|
+
import styles14 from "./SplitPane.module.css";
|
|
1087
|
+
import { jsx as jsx43, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
1088
|
+
var clampSize = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
1089
|
+
function SplitPane({
|
|
1090
|
+
primary,
|
|
1091
|
+
secondary,
|
|
1092
|
+
defaultPrimarySize = 50,
|
|
1093
|
+
minPrimarySize = 30,
|
|
1094
|
+
maxPrimarySize = 70,
|
|
1095
|
+
resizable = true,
|
|
1096
|
+
primaryLabel = "Primary pane",
|
|
1097
|
+
secondaryLabel = "Secondary pane",
|
|
1098
|
+
resizeLabel = "Resize panes",
|
|
1099
|
+
className
|
|
1100
|
+
}) {
|
|
1101
|
+
const initialSize = clampSize(defaultPrimarySize, minPrimarySize, maxPrimarySize);
|
|
1102
|
+
const [primarySize, setPrimarySize] = useState2(initialSize);
|
|
1103
|
+
const containerRef = useRef2(null);
|
|
1104
|
+
const dragState = useRef2(null);
|
|
1105
|
+
const updateFromClientX = useCallback(
|
|
1106
|
+
(clientX) => {
|
|
1107
|
+
const rect = containerRef.current?.getBoundingClientRect();
|
|
1108
|
+
if (!rect || rect.width === 0) {
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
const next = (clientX - rect.left) / rect.width * 100;
|
|
1112
|
+
setPrimarySize(clampSize(next, minPrimarySize, maxPrimarySize));
|
|
1113
|
+
},
|
|
1114
|
+
[maxPrimarySize, minPrimarySize]
|
|
1115
|
+
);
|
|
1116
|
+
useEffect2(() => {
|
|
1117
|
+
const handlePointerMove = (event) => {
|
|
1118
|
+
if (!dragState.current || event.pointerId !== dragState.current.pointerId) {
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
updateFromClientX(event.clientX);
|
|
1122
|
+
};
|
|
1123
|
+
const stopDragging = (event) => {
|
|
1124
|
+
if (!dragState.current || event.pointerId !== dragState.current.pointerId) {
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
dragState.current = null;
|
|
1128
|
+
document.body.style.cursor = "";
|
|
1129
|
+
document.body.style.userSelect = "";
|
|
1130
|
+
};
|
|
1131
|
+
window.addEventListener("pointermove", handlePointerMove);
|
|
1132
|
+
window.addEventListener("pointerup", stopDragging);
|
|
1133
|
+
window.addEventListener("pointercancel", stopDragging);
|
|
1134
|
+
return () => {
|
|
1135
|
+
window.removeEventListener("pointermove", handlePointerMove);
|
|
1136
|
+
window.removeEventListener("pointerup", stopDragging);
|
|
1137
|
+
window.removeEventListener("pointercancel", stopDragging);
|
|
1138
|
+
document.body.style.cursor = "";
|
|
1139
|
+
document.body.style.userSelect = "";
|
|
1140
|
+
};
|
|
1141
|
+
}, [updateFromClientX]);
|
|
1142
|
+
const startDragging = (event) => {
|
|
1143
|
+
if (!resizable) {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
dragState.current = { pointerId: event.pointerId };
|
|
1147
|
+
document.body.style.cursor = "col-resize";
|
|
1148
|
+
document.body.style.userSelect = "none";
|
|
1149
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
1150
|
+
};
|
|
1151
|
+
const resizeWithKeyboard = (event) => {
|
|
1152
|
+
const step = event.shiftKey ? 10 : 5;
|
|
1153
|
+
if (event.key === "ArrowLeft") {
|
|
1154
|
+
event.preventDefault();
|
|
1155
|
+
setPrimarySize((size) => clampSize(size - step, minPrimarySize, maxPrimarySize));
|
|
1156
|
+
}
|
|
1157
|
+
if (event.key === "ArrowRight") {
|
|
1158
|
+
event.preventDefault();
|
|
1159
|
+
setPrimarySize((size) => clampSize(size + step, minPrimarySize, maxPrimarySize));
|
|
1160
|
+
}
|
|
1161
|
+
if (event.key === "Home") {
|
|
1162
|
+
event.preventDefault();
|
|
1163
|
+
setPrimarySize(minPrimarySize);
|
|
1164
|
+
}
|
|
1165
|
+
if (event.key === "End") {
|
|
1166
|
+
event.preventDefault();
|
|
1167
|
+
setPrimarySize(maxPrimarySize);
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
const style = {
|
|
1171
|
+
"--split-pane-primary-size": `${primarySize}%`
|
|
1172
|
+
};
|
|
1173
|
+
return /* @__PURE__ */ jsxs29(
|
|
1174
|
+
"div",
|
|
1175
|
+
{
|
|
1176
|
+
ref: containerRef,
|
|
1177
|
+
className: [styles14.splitPane, className].filter(Boolean).join(" "),
|
|
1178
|
+
style,
|
|
1179
|
+
children: [
|
|
1180
|
+
/* @__PURE__ */ jsx43("section", { className: styles14.pane, "aria-label": primaryLabel, children: primary }),
|
|
1181
|
+
resizable && /* @__PURE__ */ jsx43(
|
|
1182
|
+
"button",
|
|
1183
|
+
{
|
|
1184
|
+
type: "button",
|
|
1185
|
+
className: styles14.handle,
|
|
1186
|
+
"aria-label": `${resizeLabel}, primary pane ${Math.round(primarySize)} percent`,
|
|
1187
|
+
onPointerDown: startDragging,
|
|
1188
|
+
onKeyDown: resizeWithKeyboard
|
|
1189
|
+
}
|
|
1190
|
+
),
|
|
1191
|
+
/* @__PURE__ */ jsx43("section", { className: styles14.pane, "aria-label": secondaryLabel, children: secondary })
|
|
1192
|
+
]
|
|
1193
|
+
}
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// src/components/TagInput/TagInput.tsx
|
|
1198
|
+
import { useMemo, useState as useState3 } from "react";
|
|
1199
|
+
import styles15 from "./TagInput.module.css";
|
|
1200
|
+
import { jsx as jsx44, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
1201
|
+
var DEFAULT_SEPARATORS = /[\n,]+/;
|
|
1202
|
+
var splitTags = (value, separators) => value.split(separators).map((tag) => tag.trim()).filter(Boolean);
|
|
1203
|
+
function TagInput({
|
|
1204
|
+
value,
|
|
1205
|
+
onChange,
|
|
1206
|
+
id,
|
|
1207
|
+
name,
|
|
1208
|
+
placeholder = "Add tag",
|
|
1209
|
+
disabled = false,
|
|
1210
|
+
required = false,
|
|
1211
|
+
error,
|
|
1212
|
+
hint,
|
|
1213
|
+
validateTag,
|
|
1214
|
+
separators = DEFAULT_SEPARATORS,
|
|
1215
|
+
className,
|
|
1216
|
+
"aria-describedby": ariaDescribedBy,
|
|
1217
|
+
"aria-invalid": ariaInvalid,
|
|
1218
|
+
"aria-label": ariaLabel
|
|
1219
|
+
}) {
|
|
1220
|
+
const [draft, setDraft] = useState3("");
|
|
1221
|
+
const [localError, setLocalError] = useState3();
|
|
1222
|
+
const message = error ?? localError ?? hint;
|
|
1223
|
+
const messageId = message && id ? `${id}-tag-message` : void 0;
|
|
1224
|
+
const describedBy = [ariaDescribedBy, messageId].filter(Boolean).join(" ") || void 0;
|
|
1225
|
+
const normalized = useMemo(() => new Set(value.map((tag) => tag.toLocaleLowerCase())), [value]);
|
|
1226
|
+
const addTags = (tags) => {
|
|
1227
|
+
const next = [...value];
|
|
1228
|
+
const nextNormalized = new Set(normalized);
|
|
1229
|
+
for (const tag of tags) {
|
|
1230
|
+
const normalizedTag = tag.toLocaleLowerCase();
|
|
1231
|
+
const duplicate = nextNormalized.has(normalizedTag);
|
|
1232
|
+
const validationMessage = duplicate ? "Tag already exists." : validateTag?.(tag, next) ?? void 0;
|
|
1233
|
+
if (validationMessage) {
|
|
1234
|
+
setLocalError(validationMessage);
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
next.push(tag);
|
|
1238
|
+
nextNormalized.add(normalizedTag);
|
|
1239
|
+
}
|
|
1240
|
+
setLocalError(void 0);
|
|
1241
|
+
setDraft("");
|
|
1242
|
+
onChange(next);
|
|
1243
|
+
};
|
|
1244
|
+
const commitDraft = () => {
|
|
1245
|
+
const tags = splitTags(draft, separators);
|
|
1246
|
+
if (tags.length === 0) {
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
addTags(tags);
|
|
1250
|
+
};
|
|
1251
|
+
const removeTag = (index) => {
|
|
1252
|
+
setLocalError(void 0);
|
|
1253
|
+
onChange(value.filter((_, tagIndex) => tagIndex !== index));
|
|
1254
|
+
};
|
|
1255
|
+
const handleKeyDown = (event) => {
|
|
1256
|
+
if (event.key === "Enter" || event.key === ",") {
|
|
1257
|
+
event.preventDefault();
|
|
1258
|
+
commitDraft();
|
|
1259
|
+
}
|
|
1260
|
+
if (event.key === "Backspace" && draft.length === 0 && value.length > 0) {
|
|
1261
|
+
event.preventDefault();
|
|
1262
|
+
removeTag(value.length - 1);
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
const handlePaste = (event) => {
|
|
1266
|
+
const text = event.clipboardData.getData("text");
|
|
1267
|
+
const tags = splitTags(text, separators);
|
|
1268
|
+
if (tags.length <= 1) {
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
event.preventDefault();
|
|
1272
|
+
addTags(tags);
|
|
1273
|
+
};
|
|
1274
|
+
return /* @__PURE__ */ jsxs30("div", { className: [styles15.wrapper, className].filter(Boolean).join(" "), children: [
|
|
1275
|
+
/* @__PURE__ */ jsxs30(
|
|
1276
|
+
"div",
|
|
1277
|
+
{
|
|
1278
|
+
className: [
|
|
1279
|
+
styles15.control,
|
|
1280
|
+
disabled ? styles15.disabled : void 0,
|
|
1281
|
+
error || localError || ariaInvalid ? styles15.invalid : void 0
|
|
1282
|
+
].filter(Boolean).join(" "),
|
|
1283
|
+
children: [
|
|
1284
|
+
value.map((tag, index) => /* @__PURE__ */ jsxs30("span", { className: styles15.tag, children: [
|
|
1285
|
+
/* @__PURE__ */ jsx44("span", { className: styles15.tagLabel, children: tag }),
|
|
1286
|
+
/* @__PURE__ */ jsx44(
|
|
1287
|
+
"button",
|
|
1288
|
+
{
|
|
1289
|
+
type: "button",
|
|
1290
|
+
className: styles15.removeButton,
|
|
1291
|
+
onClick: () => removeTag(index),
|
|
1292
|
+
disabled,
|
|
1293
|
+
"aria-label": `Remove ${tag}`,
|
|
1294
|
+
children: "\xD7"
|
|
1295
|
+
}
|
|
1296
|
+
)
|
|
1297
|
+
] }, `${tag}-${index}`)),
|
|
1298
|
+
/* @__PURE__ */ jsx44(
|
|
1299
|
+
"input",
|
|
1300
|
+
{
|
|
1301
|
+
id,
|
|
1302
|
+
name,
|
|
1303
|
+
className: styles15.input,
|
|
1304
|
+
value: draft,
|
|
1305
|
+
placeholder: value.length === 0 ? placeholder : void 0,
|
|
1306
|
+
disabled,
|
|
1307
|
+
required: required && value.length === 0,
|
|
1308
|
+
"aria-describedby": describedBy,
|
|
1309
|
+
"aria-invalid": ariaInvalid ?? (Boolean(error || localError) || void 0),
|
|
1310
|
+
"aria-label": ariaLabel,
|
|
1311
|
+
onChange: (event) => setDraft(event.currentTarget.value),
|
|
1312
|
+
onKeyDown: handleKeyDown,
|
|
1313
|
+
onBlur: commitDraft,
|
|
1314
|
+
onPaste: handlePaste
|
|
1315
|
+
}
|
|
1316
|
+
)
|
|
1317
|
+
]
|
|
1318
|
+
}
|
|
1319
|
+
),
|
|
1320
|
+
message && /* @__PURE__ */ jsx44(
|
|
1321
|
+
"p",
|
|
1322
|
+
{
|
|
1323
|
+
id: messageId,
|
|
1324
|
+
className: error || localError ? styles15.error : styles15.hint,
|
|
1325
|
+
role: error || localError ? "alert" : void 0,
|
|
1326
|
+
children: message
|
|
1327
|
+
}
|
|
1328
|
+
)
|
|
1329
|
+
] });
|
|
912
1330
|
}
|
|
913
1331
|
|
|
914
1332
|
// src/components/Dialog/Dialog.tsx
|
|
915
|
-
import { useEffect as
|
|
1333
|
+
import { createContext, useContext, useEffect as useEffect4, useRef as useRef3, useState as useState5 } from "react";
|
|
916
1334
|
|
|
917
1335
|
// src/scroll-lock.ts
|
|
918
1336
|
function lockBodyScroll() {
|
|
@@ -926,9 +1344,74 @@ function unlockBodyScroll() {
|
|
|
926
1344
|
if (next === 0) document.body.style.overflowY = "";
|
|
927
1345
|
}
|
|
928
1346
|
|
|
1347
|
+
// src/motion.ts
|
|
1348
|
+
import { useEffect as useEffect3, useState as useState4 } from "react";
|
|
1349
|
+
function useMountTransition(open, durationMs) {
|
|
1350
|
+
const [phase, setPhase] = useState4(open ? "entering" : "closed");
|
|
1351
|
+
useEffect3(() => {
|
|
1352
|
+
if (open) {
|
|
1353
|
+
setPhase("entering");
|
|
1354
|
+
const raf = requestAnimationFrame(() => setPhase("open"));
|
|
1355
|
+
return () => cancelAnimationFrame(raf);
|
|
1356
|
+
}
|
|
1357
|
+
setPhase((p) => p === "closed" ? "closed" : "closing");
|
|
1358
|
+
const timer = setTimeout(() => setPhase("closed"), durationMs);
|
|
1359
|
+
return () => clearTimeout(timer);
|
|
1360
|
+
}, [open, durationMs]);
|
|
1361
|
+
return { mounted: phase !== "closed", phase };
|
|
1362
|
+
}
|
|
1363
|
+
function usePrefersReducedMotion() {
|
|
1364
|
+
const [reduced, setReduced] = useState4(false);
|
|
1365
|
+
useEffect3(() => {
|
|
1366
|
+
const mql = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
1367
|
+
setReduced(mql.matches);
|
|
1368
|
+
function handleChange(e) {
|
|
1369
|
+
setReduced(e.matches);
|
|
1370
|
+
}
|
|
1371
|
+
mql.addEventListener("change", handleChange);
|
|
1372
|
+
return () => mql.removeEventListener("change", handleChange);
|
|
1373
|
+
}, []);
|
|
1374
|
+
return reduced;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// src/components/OverlayHeader/OverlayHeader.tsx
|
|
1378
|
+
import styles16 from "./OverlayHeader.module.css";
|
|
1379
|
+
import { jsx as jsx45, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
1380
|
+
function OverlayHeader({
|
|
1381
|
+
title,
|
|
1382
|
+
onClose,
|
|
1383
|
+
onBack,
|
|
1384
|
+
action,
|
|
1385
|
+
secondRow,
|
|
1386
|
+
className
|
|
1387
|
+
}) {
|
|
1388
|
+
return /* @__PURE__ */ jsxs31("div", { className: [styles16.header, className].filter(Boolean).join(" "), children: [
|
|
1389
|
+
/* @__PURE__ */ jsxs31("div", { className: styles16.row, children: [
|
|
1390
|
+
onBack && /* @__PURE__ */ jsx45("button", { type: "button", className: styles16.backButton, "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx45(Icon, { name: "chevron-left", size: "md", "aria-hidden": true }) }),
|
|
1391
|
+
/* @__PURE__ */ jsx45("span", { className: styles16.title, children: title }),
|
|
1392
|
+
action && /* @__PURE__ */ jsx45("div", { className: styles16.action, children: action }),
|
|
1393
|
+
/* @__PURE__ */ jsx45("button", { type: "button", className: styles16.closeButton, "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx45(Icon, { name: "x", size: "sm", "aria-hidden": true }) })
|
|
1394
|
+
] }),
|
|
1395
|
+
secondRow && /* @__PURE__ */ jsx45("div", { className: styles16.secondRow, children: secondRow })
|
|
1396
|
+
] });
|
|
1397
|
+
}
|
|
1398
|
+
|
|
929
1399
|
// src/components/Dialog/Dialog.tsx
|
|
930
|
-
import
|
|
931
|
-
import { jsx as
|
|
1400
|
+
import styles17 from "./Dialog.module.css";
|
|
1401
|
+
import { jsx as jsx46, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
1402
|
+
var MOTION_DURATION_MS = 250;
|
|
1403
|
+
var OverlaySecondRowContext = createContext(
|
|
1404
|
+
void 0
|
|
1405
|
+
);
|
|
1406
|
+
function useOverlaySecondRow(node) {
|
|
1407
|
+
const setSecondRow = useContext(OverlaySecondRowContext);
|
|
1408
|
+
useEffect4(() => {
|
|
1409
|
+
if (!setSecondRow) return;
|
|
1410
|
+
setSecondRow(node);
|
|
1411
|
+
return () => setSecondRow(null);
|
|
1412
|
+
}, [setSecondRow, node]);
|
|
1413
|
+
return setSecondRow !== void 0;
|
|
1414
|
+
}
|
|
932
1415
|
var FOCUSABLE = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
933
1416
|
function Dialog({
|
|
934
1417
|
open,
|
|
@@ -938,14 +1421,17 @@ function Dialog({
|
|
|
938
1421
|
title,
|
|
939
1422
|
children
|
|
940
1423
|
}) {
|
|
941
|
-
const panelRef =
|
|
942
|
-
const previouslyFocused =
|
|
943
|
-
|
|
944
|
-
|
|
1424
|
+
const panelRef = useRef3(null);
|
|
1425
|
+
const previouslyFocused = useRef3(null);
|
|
1426
|
+
const reducedMotion = usePrefersReducedMotion();
|
|
1427
|
+
const { mounted, phase } = useMountTransition(open, reducedMotion ? 0 : MOTION_DURATION_MS);
|
|
1428
|
+
const [secondRow, setSecondRow] = useState5(null);
|
|
1429
|
+
useEffect4(() => {
|
|
1430
|
+
if (!mounted) return;
|
|
945
1431
|
lockBodyScroll();
|
|
946
1432
|
return unlockBodyScroll;
|
|
947
|
-
}, [
|
|
948
|
-
|
|
1433
|
+
}, [mounted]);
|
|
1434
|
+
useEffect4(() => {
|
|
949
1435
|
if (!open) return;
|
|
950
1436
|
previouslyFocused.current = document.activeElement;
|
|
951
1437
|
const panel = panelRef.current;
|
|
@@ -953,7 +1439,7 @@ function Dialog({
|
|
|
953
1439
|
(first ?? panel)?.focus();
|
|
954
1440
|
return () => previouslyFocused.current?.focus();
|
|
955
1441
|
}, [open]);
|
|
956
|
-
|
|
1442
|
+
useEffect4(() => {
|
|
957
1443
|
if (!open) return;
|
|
958
1444
|
function handleKeyDown(e) {
|
|
959
1445
|
if (e.key === "Escape") {
|
|
@@ -983,14 +1469,15 @@ function Dialog({
|
|
|
983
1469
|
document.addEventListener("keydown", handleKeyDown);
|
|
984
1470
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
985
1471
|
}, [open, onClose]);
|
|
986
|
-
if (!
|
|
1472
|
+
if (!mounted) return null;
|
|
1473
|
+
const isOpenPhase = phase === "open";
|
|
987
1474
|
return (
|
|
988
1475
|
// role="presentation" removes the scrim from the AT (it is purely visual).
|
|
989
1476
|
// e.target check lets clicks inside the panel bubble without triggering dismiss.
|
|
990
|
-
/* @__PURE__ */
|
|
1477
|
+
/* @__PURE__ */ jsx46(
|
|
991
1478
|
"div",
|
|
992
1479
|
{
|
|
993
|
-
className:
|
|
1480
|
+
className: [styles17.scrim, isOpenPhase ? styles17.scrimOpen : ""].filter(Boolean).join(" "),
|
|
994
1481
|
role: "presentation",
|
|
995
1482
|
onClick: (e) => {
|
|
996
1483
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -998,7 +1485,7 @@ function Dialog({
|
|
|
998
1485
|
onKeyDown: (e) => {
|
|
999
1486
|
if (e.key === "Escape") onClose();
|
|
1000
1487
|
},
|
|
1001
|
-
children: /* @__PURE__ */
|
|
1488
|
+
children: /* @__PURE__ */ jsxs32(
|
|
1002
1489
|
"div",
|
|
1003
1490
|
{
|
|
1004
1491
|
ref: panelRef,
|
|
@@ -1006,23 +1493,19 @@ function Dialog({
|
|
|
1006
1493
|
"aria-modal": "true",
|
|
1007
1494
|
"aria-label": ariaLabel ?? title,
|
|
1008
1495
|
tabIndex: -1,
|
|
1009
|
-
className: [
|
|
1496
|
+
className: [styles17.panel, styles17[size], isOpenPhase ? styles17.panelOpen : ""].filter(Boolean).join(" "),
|
|
1010
1497
|
children: [
|
|
1011
|
-
/* @__PURE__ */
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
)
|
|
1023
|
-
] }),
|
|
1024
|
-
/* @__PURE__ */ jsx40("button", { type: "button", className: styles12.close, "aria-label": "Close", onClick: onClose, children: "\xD7" }),
|
|
1025
|
-
/* @__PURE__ */ jsx40("div", { className: styles12.content, children })
|
|
1498
|
+
/* @__PURE__ */ jsx46(
|
|
1499
|
+
OverlayHeader,
|
|
1500
|
+
{
|
|
1501
|
+
title,
|
|
1502
|
+
onClose,
|
|
1503
|
+
secondRow,
|
|
1504
|
+
className: styles17.mobileHeader
|
|
1505
|
+
}
|
|
1506
|
+
),
|
|
1507
|
+
/* @__PURE__ */ jsx46("button", { type: "button", className: styles17.close, "aria-label": "Close", onClick: onClose, children: "\xD7" }),
|
|
1508
|
+
/* @__PURE__ */ jsx46("div", { className: styles17.content, children: /* @__PURE__ */ jsx46(OverlaySecondRowContext.Provider, { value: setSecondRow, children }) })
|
|
1026
1509
|
]
|
|
1027
1510
|
}
|
|
1028
1511
|
)
|
|
@@ -1032,19 +1515,30 @@ function Dialog({
|
|
|
1032
1515
|
}
|
|
1033
1516
|
|
|
1034
1517
|
// src/components/Drawer/Drawer.tsx
|
|
1035
|
-
import { useEffect as
|
|
1036
|
-
import
|
|
1037
|
-
import { jsx as
|
|
1518
|
+
import { useEffect as useEffect5, useRef as useRef4 } from "react";
|
|
1519
|
+
import styles18 from "./Drawer.module.css";
|
|
1520
|
+
import { jsx as jsx47, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
1521
|
+
var MOTION_DURATION_MS2 = 250;
|
|
1038
1522
|
var FOCUSABLE2 = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1523
|
+
var SWIPE_DISMISS_THRESHOLD_PX = 100;
|
|
1524
|
+
function Drawer({
|
|
1525
|
+
open,
|
|
1526
|
+
onClose,
|
|
1527
|
+
"aria-label": ariaLabel,
|
|
1528
|
+
snapHeight = "content",
|
|
1529
|
+
children
|
|
1530
|
+
}) {
|
|
1531
|
+
const panelRef = useRef4(null);
|
|
1532
|
+
const previouslyFocused = useRef4(null);
|
|
1533
|
+
const reducedMotion = usePrefersReducedMotion();
|
|
1534
|
+
const { mounted, phase } = useMountTransition(open, reducedMotion ? 0 : MOTION_DURATION_MS2);
|
|
1535
|
+
const dragStartY = useRef4(null);
|
|
1536
|
+
useEffect5(() => {
|
|
1537
|
+
if (!mounted) return;
|
|
1044
1538
|
lockBodyScroll();
|
|
1045
1539
|
return unlockBodyScroll;
|
|
1046
|
-
}, [
|
|
1047
|
-
|
|
1540
|
+
}, [mounted]);
|
|
1541
|
+
useEffect5(() => {
|
|
1048
1542
|
if (!open) return;
|
|
1049
1543
|
previouslyFocused.current = document.activeElement;
|
|
1050
1544
|
const panel = panelRef.current;
|
|
@@ -1052,7 +1546,7 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1052
1546
|
(first ?? panel)?.focus();
|
|
1053
1547
|
return () => previouslyFocused.current?.focus();
|
|
1054
1548
|
}, [open]);
|
|
1055
|
-
|
|
1549
|
+
useEffect5(() => {
|
|
1056
1550
|
if (!open) return;
|
|
1057
1551
|
function handleKeyDown(e) {
|
|
1058
1552
|
if (e.key === "Escape") {
|
|
@@ -1082,14 +1576,40 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1082
1576
|
document.addEventListener("keydown", handleKeyDown);
|
|
1083
1577
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1084
1578
|
}, [open, onClose]);
|
|
1085
|
-
|
|
1579
|
+
function handleHandlePointerDown(e) {
|
|
1580
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
1581
|
+
dragStartY.current = e.clientY;
|
|
1582
|
+
}
|
|
1583
|
+
function handleHandlePointerMove(e) {
|
|
1584
|
+
const startY = dragStartY.current;
|
|
1585
|
+
if (startY === null) return;
|
|
1586
|
+
const dy = Math.max(0, e.clientY - startY);
|
|
1587
|
+
const panel = panelRef.current;
|
|
1588
|
+
if (!panel) return;
|
|
1589
|
+
panel.style.transition = "none";
|
|
1590
|
+
panel.style.transform = `translateY(${dy}px)`;
|
|
1591
|
+
}
|
|
1592
|
+
function releaseDrag(e, honorDistance) {
|
|
1593
|
+
const startY = dragStartY.current;
|
|
1594
|
+
dragStartY.current = null;
|
|
1595
|
+
if (startY === null) return;
|
|
1596
|
+
const dy = Math.max(0, e.clientY - startY);
|
|
1597
|
+
const panel = panelRef.current;
|
|
1598
|
+
if (panel) {
|
|
1599
|
+
panel.style.transform = "";
|
|
1600
|
+
panel.style.transition = "";
|
|
1601
|
+
}
|
|
1602
|
+
if (honorDistance && dy > SWIPE_DISMISS_THRESHOLD_PX) onClose();
|
|
1603
|
+
}
|
|
1604
|
+
if (!mounted) return null;
|
|
1605
|
+
const isOpenPhase = phase === "open";
|
|
1086
1606
|
return (
|
|
1087
1607
|
// role="presentation" removes the scrim from the AT (it is purely visual).
|
|
1088
1608
|
// e.target check lets clicks inside the panel bubble without triggering dismiss.
|
|
1089
|
-
/* @__PURE__ */
|
|
1609
|
+
/* @__PURE__ */ jsx47(
|
|
1090
1610
|
"div",
|
|
1091
1611
|
{
|
|
1092
|
-
className:
|
|
1612
|
+
className: [styles18.scrim, isOpenPhase ? styles18.scrimOpen : ""].filter(Boolean).join(" "),
|
|
1093
1613
|
role: "presentation",
|
|
1094
1614
|
onClick: (e) => {
|
|
1095
1615
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -1097,7 +1617,7 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1097
1617
|
onKeyDown: (e) => {
|
|
1098
1618
|
if (e.key === "Escape") onClose();
|
|
1099
1619
|
},
|
|
1100
|
-
children: /* @__PURE__ */
|
|
1620
|
+
children: /* @__PURE__ */ jsxs33(
|
|
1101
1621
|
"div",
|
|
1102
1622
|
{
|
|
1103
1623
|
ref: panelRef,
|
|
@@ -1105,8 +1625,25 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1105
1625
|
"aria-modal": "true",
|
|
1106
1626
|
"aria-label": ariaLabel,
|
|
1107
1627
|
tabIndex: -1,
|
|
1108
|
-
className:
|
|
1109
|
-
|
|
1628
|
+
className: [
|
|
1629
|
+
styles18.panel,
|
|
1630
|
+
snapHeight === "half" ? styles18.panelHalf : "",
|
|
1631
|
+
isOpenPhase ? styles18.panelOpen : ""
|
|
1632
|
+
].filter(Boolean).join(" "),
|
|
1633
|
+
children: [
|
|
1634
|
+
/* @__PURE__ */ jsx47(
|
|
1635
|
+
"div",
|
|
1636
|
+
{
|
|
1637
|
+
className: styles18.handle,
|
|
1638
|
+
"aria-hidden": "true",
|
|
1639
|
+
onPointerDown: handleHandlePointerDown,
|
|
1640
|
+
onPointerMove: handleHandlePointerMove,
|
|
1641
|
+
onPointerUp: (e) => releaseDrag(e, true),
|
|
1642
|
+
onPointerCancel: (e) => releaseDrag(e, false)
|
|
1643
|
+
}
|
|
1644
|
+
),
|
|
1645
|
+
children
|
|
1646
|
+
]
|
|
1110
1647
|
}
|
|
1111
1648
|
)
|
|
1112
1649
|
}
|
|
@@ -1114,37 +1651,391 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1114
1651
|
);
|
|
1115
1652
|
}
|
|
1116
1653
|
|
|
1654
|
+
// src/components/Sheet/Sheet.tsx
|
|
1655
|
+
import { useEffect as useEffect6, useRef as useRef5 } from "react";
|
|
1656
|
+
import styles19 from "./Sheet.module.css";
|
|
1657
|
+
import { jsx as jsx48, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
1658
|
+
var FOCUSABLE3 = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
1659
|
+
var MOTION_DURATION_MS3 = 250;
|
|
1660
|
+
function Sheet({
|
|
1661
|
+
open,
|
|
1662
|
+
onClose,
|
|
1663
|
+
"aria-label": ariaLabel,
|
|
1664
|
+
title,
|
|
1665
|
+
onBack,
|
|
1666
|
+
headerAction,
|
|
1667
|
+
slideFrom = "bottom",
|
|
1668
|
+
children
|
|
1669
|
+
}) {
|
|
1670
|
+
const panelRef = useRef5(null);
|
|
1671
|
+
const previouslyFocused = useRef5(null);
|
|
1672
|
+
const reducedMotion = usePrefersReducedMotion();
|
|
1673
|
+
const { mounted, phase } = useMountTransition(open, reducedMotion ? 0 : MOTION_DURATION_MS3);
|
|
1674
|
+
useEffect6(() => {
|
|
1675
|
+
if (!mounted) return;
|
|
1676
|
+
lockBodyScroll();
|
|
1677
|
+
return unlockBodyScroll;
|
|
1678
|
+
}, [mounted]);
|
|
1679
|
+
useEffect6(() => {
|
|
1680
|
+
if (!mounted) return;
|
|
1681
|
+
previouslyFocused.current = document.activeElement;
|
|
1682
|
+
const panel = panelRef.current;
|
|
1683
|
+
const first = panel?.querySelector(FOCUSABLE3);
|
|
1684
|
+
(first ?? panel)?.focus();
|
|
1685
|
+
return () => previouslyFocused.current?.focus();
|
|
1686
|
+
}, [mounted]);
|
|
1687
|
+
useEffect6(() => {
|
|
1688
|
+
if (!open) return;
|
|
1689
|
+
function handleKeyDown(e) {
|
|
1690
|
+
if (e.key === "Escape") {
|
|
1691
|
+
e.stopPropagation();
|
|
1692
|
+
onClose();
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
if (e.key !== "Tab") return;
|
|
1696
|
+
const panel = panelRef.current;
|
|
1697
|
+
if (!panel) return;
|
|
1698
|
+
const focusable = [...panel.querySelectorAll(FOCUSABLE3)];
|
|
1699
|
+
const first = focusable[0];
|
|
1700
|
+
const last = focusable.at(-1);
|
|
1701
|
+
if (!first || !last) {
|
|
1702
|
+
e.preventDefault();
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
const active = document.activeElement;
|
|
1706
|
+
if (e.shiftKey && active === first) {
|
|
1707
|
+
e.preventDefault();
|
|
1708
|
+
last.focus();
|
|
1709
|
+
} else if (!e.shiftKey && active === last) {
|
|
1710
|
+
e.preventDefault();
|
|
1711
|
+
first.focus();
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1715
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1716
|
+
}, [open, onClose]);
|
|
1717
|
+
if (!mounted) return null;
|
|
1718
|
+
return /* @__PURE__ */ jsxs34(
|
|
1719
|
+
"div",
|
|
1720
|
+
{
|
|
1721
|
+
ref: panelRef,
|
|
1722
|
+
role: "dialog",
|
|
1723
|
+
"aria-modal": "true",
|
|
1724
|
+
"aria-label": ariaLabel ?? title,
|
|
1725
|
+
tabIndex: -1,
|
|
1726
|
+
className: [
|
|
1727
|
+
styles19.panel,
|
|
1728
|
+
slideFrom === "top" ? styles19.panelFromTop : "",
|
|
1729
|
+
phase === "open" ? styles19.panelOpen : ""
|
|
1730
|
+
].filter(Boolean).join(" "),
|
|
1731
|
+
children: [
|
|
1732
|
+
title && /* @__PURE__ */ jsx48(OverlayHeader, { title, onClose, onBack, action: headerAction }),
|
|
1733
|
+
/* @__PURE__ */ jsx48("div", { className: styles19.content, children })
|
|
1734
|
+
]
|
|
1735
|
+
}
|
|
1736
|
+
);
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
// src/components/ConfirmDialog/ConfirmDialog.tsx
|
|
1740
|
+
import { useEffect as useEffect7, useRef as useRef6 } from "react";
|
|
1741
|
+
import styles20 from "./ConfirmDialog.module.css";
|
|
1742
|
+
import { jsx as jsx49, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
1743
|
+
function ConfirmDialog({
|
|
1744
|
+
open,
|
|
1745
|
+
onClose,
|
|
1746
|
+
title,
|
|
1747
|
+
message,
|
|
1748
|
+
onConfirm,
|
|
1749
|
+
confirmLabel = "Confirm",
|
|
1750
|
+
cancelLabel = "Cancel",
|
|
1751
|
+
destructive = false,
|
|
1752
|
+
pending = false,
|
|
1753
|
+
error
|
|
1754
|
+
}) {
|
|
1755
|
+
const dialogRef = useRef6(null);
|
|
1756
|
+
useEffect7(() => {
|
|
1757
|
+
const el = dialogRef.current;
|
|
1758
|
+
if (!el) return;
|
|
1759
|
+
if (open) el.showModal();
|
|
1760
|
+
else el.close();
|
|
1761
|
+
}, [open]);
|
|
1762
|
+
useEffect7(() => {
|
|
1763
|
+
const el = dialogRef.current;
|
|
1764
|
+
if (!el) return;
|
|
1765
|
+
const handleClose = () => onClose();
|
|
1766
|
+
el.addEventListener("close", handleClose);
|
|
1767
|
+
return () => el.removeEventListener("close", handleClose);
|
|
1768
|
+
}, [onClose]);
|
|
1769
|
+
return (
|
|
1770
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
|
|
1771
|
+
/* @__PURE__ */ jsx49(
|
|
1772
|
+
"dialog",
|
|
1773
|
+
{
|
|
1774
|
+
ref: dialogRef,
|
|
1775
|
+
className: styles20.dialog,
|
|
1776
|
+
"aria-label": title,
|
|
1777
|
+
onClick: (e) => {
|
|
1778
|
+
if (e.target === e.currentTarget) onClose();
|
|
1779
|
+
},
|
|
1780
|
+
children: /* @__PURE__ */ jsxs35("div", { className: styles20.body, children: [
|
|
1781
|
+
/* @__PURE__ */ jsx49("h2", { className: styles20.title, children: title }),
|
|
1782
|
+
/* @__PURE__ */ jsx49("div", { className: styles20.message, children: message }),
|
|
1783
|
+
error && /* @__PURE__ */ jsx49("p", { className: styles20.error, children: error }),
|
|
1784
|
+
/* @__PURE__ */ jsxs35("div", { className: styles20.actions, children: [
|
|
1785
|
+
/* @__PURE__ */ jsx49(Button, { type: "button", variant: "secondary", onClick: onClose, disabled: pending, children: cancelLabel }),
|
|
1786
|
+
destructive ? (
|
|
1787
|
+
// A solid red fill, not Button's own `destructive` variant (an
|
|
1788
|
+
// outlined/ghost style meant for lower-emphasis placements) — a
|
|
1789
|
+
// confirm action should read as strongly as `Button`'s primary
|
|
1790
|
+
// variant does. Uses the same --sv-color-error-solid /
|
|
1791
|
+
// -text-on-error tokens the codebase already introduced for this
|
|
1792
|
+
// exact "reads as a filled action button, not a tinted status
|
|
1793
|
+
// surface" case (see semantic.css).
|
|
1794
|
+
/* @__PURE__ */ jsx49(
|
|
1795
|
+
"button",
|
|
1796
|
+
{
|
|
1797
|
+
type: "button",
|
|
1798
|
+
className: styles20.destructiveConfirm,
|
|
1799
|
+
onClick: onConfirm,
|
|
1800
|
+
disabled: pending,
|
|
1801
|
+
children: confirmLabel
|
|
1802
|
+
}
|
|
1803
|
+
)
|
|
1804
|
+
) : /* @__PURE__ */ jsx49(Button, { type: "button", variant: "primary", onClick: onConfirm, disabled: pending, children: confirmLabel })
|
|
1805
|
+
] })
|
|
1806
|
+
] })
|
|
1807
|
+
}
|
|
1808
|
+
)
|
|
1809
|
+
);
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
// src/hooks/useLongPress.ts
|
|
1813
|
+
import { useCallback as useCallback2, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
|
|
1814
|
+
var DEFAULT_DELAY_MS = 500;
|
|
1815
|
+
var DEFAULT_MOVE_TOLERANCE_PX = 10;
|
|
1816
|
+
var DEFAULT_SUPPRESS_CLICK_MS = 700;
|
|
1817
|
+
function usePrefersCoarsePointer() {
|
|
1818
|
+
const [coarse, setCoarse] = useState6(false);
|
|
1819
|
+
useEffect8(() => {
|
|
1820
|
+
const mql = window.matchMedia("(pointer: coarse)");
|
|
1821
|
+
setCoarse(mql.matches);
|
|
1822
|
+
function handleChange(e) {
|
|
1823
|
+
setCoarse(e.matches);
|
|
1824
|
+
}
|
|
1825
|
+
mql.addEventListener("change", handleChange);
|
|
1826
|
+
return () => mql.removeEventListener("change", handleChange);
|
|
1827
|
+
}, []);
|
|
1828
|
+
return coarse;
|
|
1829
|
+
}
|
|
1830
|
+
function useLongPress({
|
|
1831
|
+
onLongPress,
|
|
1832
|
+
delay = DEFAULT_DELAY_MS,
|
|
1833
|
+
moveTolerance = DEFAULT_MOVE_TOLERANCE_PX,
|
|
1834
|
+
suppressClickMs = DEFAULT_SUPPRESS_CLICK_MS,
|
|
1835
|
+
vibrate = true,
|
|
1836
|
+
disabled = false
|
|
1837
|
+
}) {
|
|
1838
|
+
const timerRef = useRef7(null);
|
|
1839
|
+
const startRef = useRef7(null);
|
|
1840
|
+
const suppressUntilRef = useRef7(0);
|
|
1841
|
+
const clearTimer = useCallback2(() => {
|
|
1842
|
+
if (timerRef.current) {
|
|
1843
|
+
clearTimeout(timerRef.current);
|
|
1844
|
+
timerRef.current = null;
|
|
1845
|
+
}
|
|
1846
|
+
}, []);
|
|
1847
|
+
useEffect8(() => clearTimer, [clearTimer]);
|
|
1848
|
+
const onPointerDown = useCallback2(
|
|
1849
|
+
(e) => {
|
|
1850
|
+
if (disabled || e.pointerType !== "touch") return;
|
|
1851
|
+
startRef.current = { x: e.clientX, y: e.clientY };
|
|
1852
|
+
clearTimer();
|
|
1853
|
+
timerRef.current = setTimeout(() => {
|
|
1854
|
+
timerRef.current = null;
|
|
1855
|
+
suppressUntilRef.current = Date.now() + suppressClickMs;
|
|
1856
|
+
if (vibrate && typeof navigator !== "undefined" && navigator.vibrate) {
|
|
1857
|
+
navigator.vibrate(10);
|
|
1858
|
+
}
|
|
1859
|
+
onLongPress();
|
|
1860
|
+
}, delay);
|
|
1861
|
+
},
|
|
1862
|
+
[disabled, delay, suppressClickMs, vibrate, onLongPress, clearTimer]
|
|
1863
|
+
);
|
|
1864
|
+
const onPointerMove = useCallback2(
|
|
1865
|
+
(e) => {
|
|
1866
|
+
const start = startRef.current;
|
|
1867
|
+
if (!start || !timerRef.current) return;
|
|
1868
|
+
const dx = e.clientX - start.x;
|
|
1869
|
+
const dy = e.clientY - start.y;
|
|
1870
|
+
if (Math.hypot(dx, dy) > moveTolerance) clearTimer();
|
|
1871
|
+
},
|
|
1872
|
+
[moveTolerance, clearTimer]
|
|
1873
|
+
);
|
|
1874
|
+
const cancelPress = useCallback2(() => {
|
|
1875
|
+
clearTimer();
|
|
1876
|
+
startRef.current = null;
|
|
1877
|
+
}, [clearTimer]);
|
|
1878
|
+
const onContextMenu = useCallback2((e) => {
|
|
1879
|
+
if (timerRef.current || Date.now() < suppressUntilRef.current) e.preventDefault();
|
|
1880
|
+
}, []);
|
|
1881
|
+
const onClick = useCallback2((e) => {
|
|
1882
|
+
if (Date.now() < suppressUntilRef.current) {
|
|
1883
|
+
e.preventDefault();
|
|
1884
|
+
e.stopPropagation();
|
|
1885
|
+
}
|
|
1886
|
+
}, []);
|
|
1887
|
+
const isCoarsePointer = usePrefersCoarsePointer();
|
|
1888
|
+
const style = isCoarsePointer ? {
|
|
1889
|
+
WebkitTouchCallout: "none",
|
|
1890
|
+
WebkitUserSelect: "none",
|
|
1891
|
+
userSelect: "none",
|
|
1892
|
+
touchAction: "manipulation"
|
|
1893
|
+
} : {};
|
|
1894
|
+
return {
|
|
1895
|
+
onPointerDown,
|
|
1896
|
+
onPointerMove,
|
|
1897
|
+
onPointerUp: cancelPress,
|
|
1898
|
+
onPointerCancel: cancelPress,
|
|
1899
|
+
onPointerLeave: cancelPress,
|
|
1900
|
+
onContextMenu,
|
|
1901
|
+
onClick,
|
|
1902
|
+
style
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
// src/hooks/useDoubleTap.ts
|
|
1907
|
+
import { useRef as useRef8 } from "react";
|
|
1908
|
+
var DOUBLE_TAP_MS = 350;
|
|
1909
|
+
function useDoubleTapHandler(onDoubleTap) {
|
|
1910
|
+
const lastTime = useRef8(0);
|
|
1911
|
+
return (e) => {
|
|
1912
|
+
if (e.detail === 2) {
|
|
1913
|
+
onDoubleTap(e);
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1916
|
+
const now = Date.now();
|
|
1917
|
+
if (now - lastTime.current < DOUBLE_TAP_MS) {
|
|
1918
|
+
lastTime.current = 0;
|
|
1919
|
+
onDoubleTap(e);
|
|
1920
|
+
} else {
|
|
1921
|
+
lastTime.current = now;
|
|
1922
|
+
}
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
function useSingleOrDoubleTap(onSingle, onDouble) {
|
|
1926
|
+
const timer = useRef8(null);
|
|
1927
|
+
return (e) => {
|
|
1928
|
+
if (e.detail === 2) {
|
|
1929
|
+
if (timer.current) {
|
|
1930
|
+
clearTimeout(timer.current);
|
|
1931
|
+
timer.current = null;
|
|
1932
|
+
}
|
|
1933
|
+
onDouble(e);
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
if (timer.current) {
|
|
1937
|
+
clearTimeout(timer.current);
|
|
1938
|
+
timer.current = null;
|
|
1939
|
+
onDouble(e);
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
timer.current = setTimeout(() => {
|
|
1943
|
+
timer.current = null;
|
|
1944
|
+
onSingle(e);
|
|
1945
|
+
}, DOUBLE_TAP_MS);
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
// src/hooks/useIsMobile.ts
|
|
1950
|
+
import { useEffect as useEffect9, useState as useState7 } from "react";
|
|
1951
|
+
var MOBILE_BREAKPOINT_PX = 768;
|
|
1952
|
+
function useIsMobile(breakpointPx = MOBILE_BREAKPOINT_PX) {
|
|
1953
|
+
const [isMobile, setIsMobile] = useState7(false);
|
|
1954
|
+
useEffect9(() => {
|
|
1955
|
+
const mql = window.matchMedia(`(max-width: ${breakpointPx}px)`);
|
|
1956
|
+
setIsMobile(mql.matches);
|
|
1957
|
+
function handleChange(e) {
|
|
1958
|
+
setIsMobile(e.matches);
|
|
1959
|
+
}
|
|
1960
|
+
mql.addEventListener("change", handleChange);
|
|
1961
|
+
return () => mql.removeEventListener("change", handleChange);
|
|
1962
|
+
}, [breakpointPx]);
|
|
1963
|
+
return isMobile;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
// src/components/Menu/Menu.tsx
|
|
1967
|
+
import styles21 from "./Menu.module.css";
|
|
1968
|
+
import { Fragment, jsx as jsx50, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
1969
|
+
function Menu({ trigger, open, onClose, items, "aria-label": ariaLabel, align }) {
|
|
1970
|
+
const isMobile = useIsMobile();
|
|
1971
|
+
const list = /* @__PURE__ */ jsx50("ul", { className: styles21.list, role: "menu", children: items.map((entry, index) => {
|
|
1972
|
+
if (entry.type === "separator") {
|
|
1973
|
+
return /* @__PURE__ */ jsx50("li", { role: "separator", className: styles21.separator }, index);
|
|
1974
|
+
}
|
|
1975
|
+
if (entry.type === "label") {
|
|
1976
|
+
return /* @__PURE__ */ jsx50("li", { role: "presentation", className: styles21.label, children: entry.label }, index);
|
|
1977
|
+
}
|
|
1978
|
+
const isCheckable = entry.checked !== void 0;
|
|
1979
|
+
return /* @__PURE__ */ jsx50("li", { role: "none", children: /* @__PURE__ */ jsxs36(
|
|
1980
|
+
"button",
|
|
1981
|
+
{
|
|
1982
|
+
role: isCheckable ? "menuitemradio" : "menuitem",
|
|
1983
|
+
"aria-checked": isCheckable ? entry.checked : void 0,
|
|
1984
|
+
type: "button",
|
|
1985
|
+
className: [styles21.item, entry.destructive ? styles21.itemDestructive : ""].filter(Boolean).join(" "),
|
|
1986
|
+
disabled: entry.disabled,
|
|
1987
|
+
onClick: () => {
|
|
1988
|
+
onClose();
|
|
1989
|
+
entry.onSelect();
|
|
1990
|
+
},
|
|
1991
|
+
children: [
|
|
1992
|
+
isCheckable && /* @__PURE__ */ jsx50("span", { className: styles21.check, "aria-hidden": true, children: entry.checked && /* @__PURE__ */ jsx50(Icon, { name: "check", size: "sm", "aria-hidden": true }) }),
|
|
1993
|
+
entry.icon && /* @__PURE__ */ jsx50(Icon, { name: entry.icon, size: "sm", "aria-hidden": true }),
|
|
1994
|
+
entry.label
|
|
1995
|
+
]
|
|
1996
|
+
}
|
|
1997
|
+
) }, index);
|
|
1998
|
+
}) });
|
|
1999
|
+
if (isMobile) {
|
|
2000
|
+
return /* @__PURE__ */ jsxs36(Fragment, { children: [
|
|
2001
|
+
trigger,
|
|
2002
|
+
/* @__PURE__ */ jsx50(Drawer, { open, onClose, "aria-label": ariaLabel, children: list })
|
|
2003
|
+
] });
|
|
2004
|
+
}
|
|
2005
|
+
return /* @__PURE__ */ jsx50(Popover, { trigger, open, onClose, "aria-label": ariaLabel, align, children: list });
|
|
2006
|
+
}
|
|
2007
|
+
|
|
1117
2008
|
// src/components/Toast/Toast.tsx
|
|
1118
2009
|
import {
|
|
1119
|
-
createContext,
|
|
1120
|
-
useCallback,
|
|
1121
|
-
useContext,
|
|
1122
|
-
useEffect as
|
|
2010
|
+
createContext as createContext2,
|
|
2011
|
+
useCallback as useCallback3,
|
|
2012
|
+
useContext as useContext2,
|
|
2013
|
+
useEffect as useEffect10,
|
|
1123
2014
|
useId,
|
|
1124
|
-
useMemo,
|
|
1125
|
-
useRef as
|
|
1126
|
-
useState
|
|
2015
|
+
useMemo as useMemo2,
|
|
2016
|
+
useRef as useRef9,
|
|
2017
|
+
useState as useState8
|
|
1127
2018
|
} from "react";
|
|
1128
|
-
import
|
|
1129
|
-
import { jsx as
|
|
1130
|
-
var ToastContext =
|
|
2019
|
+
import styles22 from "./Toast.module.css";
|
|
2020
|
+
import { jsx as jsx51, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
2021
|
+
var ToastContext = createContext2(null);
|
|
1131
2022
|
function useToast() {
|
|
1132
|
-
const ctx =
|
|
2023
|
+
const ctx = useContext2(ToastContext);
|
|
1133
2024
|
if (!ctx) throw new Error("useToast() must be used inside <ToastProvider>");
|
|
1134
2025
|
return ctx;
|
|
1135
2026
|
}
|
|
1136
2027
|
function ToastProvider({ children }) {
|
|
1137
|
-
const [toasts, setToasts] =
|
|
1138
|
-
const timers =
|
|
2028
|
+
const [toasts, setToasts] = useState8([]);
|
|
2029
|
+
const timers = useRef9(/* @__PURE__ */ new Map());
|
|
1139
2030
|
const seedId = useId();
|
|
1140
|
-
const counter =
|
|
1141
|
-
const dismiss =
|
|
2031
|
+
const counter = useRef9(0);
|
|
2032
|
+
const dismiss = useCallback3((id) => {
|
|
1142
2033
|
setToasts((prev) => prev.map((t) => t.id === id ? { ...t, exiting: true } : t));
|
|
1143
2034
|
setTimeout(() => {
|
|
1144
2035
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
1145
2036
|
}, 220);
|
|
1146
2037
|
}, []);
|
|
1147
|
-
const show =
|
|
2038
|
+
const show = useCallback3(
|
|
1148
2039
|
(item) => {
|
|
1149
2040
|
const id = `${seedId}-${++counter.current}`;
|
|
1150
2041
|
const duration = item.duration ?? 5e3;
|
|
@@ -1157,23 +2048,23 @@ function ToastProvider({ children }) {
|
|
|
1157
2048
|
},
|
|
1158
2049
|
[seedId, dismiss]
|
|
1159
2050
|
);
|
|
1160
|
-
|
|
2051
|
+
useEffect10(() => {
|
|
1161
2052
|
const currentTimers = timers.current;
|
|
1162
2053
|
return () => {
|
|
1163
2054
|
currentTimers.forEach((t) => clearTimeout(t));
|
|
1164
2055
|
};
|
|
1165
2056
|
}, []);
|
|
1166
|
-
const value =
|
|
1167
|
-
return /* @__PURE__ */
|
|
2057
|
+
const value = useMemo2(() => ({ show, dismiss }), [show, dismiss]);
|
|
2058
|
+
return /* @__PURE__ */ jsxs37(ToastContext.Provider, { value, children: [
|
|
1168
2059
|
children,
|
|
1169
|
-
/* @__PURE__ */
|
|
2060
|
+
/* @__PURE__ */ jsx51(
|
|
1170
2061
|
"ol",
|
|
1171
2062
|
{
|
|
1172
|
-
className:
|
|
2063
|
+
className: styles22.region,
|
|
1173
2064
|
"aria-label": "Notifications",
|
|
1174
2065
|
"aria-live": "polite",
|
|
1175
2066
|
"aria-atomic": "false",
|
|
1176
|
-
children: toasts.map((toast) => /* @__PURE__ */
|
|
2067
|
+
children: toasts.map((toast) => /* @__PURE__ */ jsx51(Toast, { ...toast, onDismiss: dismiss }, toast.id))
|
|
1177
2068
|
}
|
|
1178
2069
|
)
|
|
1179
2070
|
] });
|
|
@@ -1194,24 +2085,24 @@ function Toast({
|
|
|
1194
2085
|
exiting,
|
|
1195
2086
|
onDismiss
|
|
1196
2087
|
}) {
|
|
1197
|
-
const categoryClass =
|
|
2088
|
+
const categoryClass = styles22[category] ?? styles22.info;
|
|
1198
2089
|
const iconName = CATEGORY_ICON[category] ?? "info";
|
|
1199
|
-
return /* @__PURE__ */
|
|
2090
|
+
return /* @__PURE__ */ jsxs37(
|
|
1200
2091
|
"li",
|
|
1201
2092
|
{
|
|
1202
2093
|
role: "status",
|
|
1203
|
-
className: `${
|
|
2094
|
+
className: `${styles22.toast} ${String(categoryClass)} ${exiting ? styles22.exiting : ""}`,
|
|
1204
2095
|
children: [
|
|
1205
|
-
/* @__PURE__ */
|
|
1206
|
-
/* @__PURE__ */
|
|
1207
|
-
/* @__PURE__ */
|
|
1208
|
-
message && /* @__PURE__ */
|
|
2096
|
+
/* @__PURE__ */ jsx51("span", { className: styles22.icon, "aria-hidden": "true", children: /* @__PURE__ */ jsx51(Icon, { name: iconName, size: "sm", "aria-hidden": true }) }),
|
|
2097
|
+
/* @__PURE__ */ jsxs37("div", { className: styles22.body, children: [
|
|
2098
|
+
/* @__PURE__ */ jsx51("div", { className: styles22.title, children: title }),
|
|
2099
|
+
message && /* @__PURE__ */ jsx51("div", { className: styles22.message, children: message })
|
|
1209
2100
|
] }),
|
|
1210
|
-
/* @__PURE__ */
|
|
2101
|
+
/* @__PURE__ */ jsx51(
|
|
1211
2102
|
"button",
|
|
1212
2103
|
{
|
|
1213
2104
|
type: "button",
|
|
1214
|
-
className:
|
|
2105
|
+
className: styles22.close,
|
|
1215
2106
|
"aria-label": "Dismiss notification",
|
|
1216
2107
|
onClick: () => onDismiss(id),
|
|
1217
2108
|
children: "\u2715"
|
|
@@ -1223,12 +2114,12 @@ function Toast({
|
|
|
1223
2114
|
}
|
|
1224
2115
|
|
|
1225
2116
|
// src/components/Card/Card.tsx
|
|
1226
|
-
import
|
|
1227
|
-
import { jsx as
|
|
2117
|
+
import styles23 from "./Card.module.css";
|
|
2118
|
+
import { jsx as jsx52 } from "react/jsx-runtime";
|
|
1228
2119
|
var paddingClass = {
|
|
1229
|
-
sm:
|
|
1230
|
-
md:
|
|
1231
|
-
lg:
|
|
2120
|
+
sm: styles23.paddingSm,
|
|
2121
|
+
md: styles23.paddingMd,
|
|
2122
|
+
lg: styles23.paddingLg
|
|
1232
2123
|
};
|
|
1233
2124
|
function Card({
|
|
1234
2125
|
as: Tag = "div",
|
|
@@ -1238,14 +2129,14 @@ function Card({
|
|
|
1238
2129
|
children,
|
|
1239
2130
|
...rest
|
|
1240
2131
|
}) {
|
|
1241
|
-
const cls = [
|
|
1242
|
-
return /* @__PURE__ */
|
|
2132
|
+
const cls = [styles23.card, paddingClass[padding], interactive && styles23.interactive, className].filter(Boolean).join(" ");
|
|
2133
|
+
return /* @__PURE__ */ jsx52(Tag, { className: cls, ...rest, children });
|
|
1243
2134
|
}
|
|
1244
2135
|
|
|
1245
2136
|
// src/components/FormField/FormField.tsx
|
|
1246
2137
|
import { useId as useId2 } from "react";
|
|
1247
|
-
import
|
|
1248
|
-
import { jsx as
|
|
2138
|
+
import styles24 from "./FormField.module.css";
|
|
2139
|
+
import { jsx as jsx53, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
1249
2140
|
function FormField({
|
|
1250
2141
|
label,
|
|
1251
2142
|
hint,
|
|
@@ -1266,98 +2157,98 @@ function FormField({
|
|
|
1266
2157
|
"aria-invalid": error ? true : void 0,
|
|
1267
2158
|
required: required || void 0
|
|
1268
2159
|
};
|
|
1269
|
-
return /* @__PURE__ */
|
|
1270
|
-
/* @__PURE__ */
|
|
2160
|
+
return /* @__PURE__ */ jsxs38("div", { className: [styles24.field, className].filter(Boolean).join(" "), children: [
|
|
2161
|
+
/* @__PURE__ */ jsxs38("label", { className: styles24.label, htmlFor: fieldId, children: [
|
|
1271
2162
|
label,
|
|
1272
|
-
required && /* @__PURE__ */
|
|
2163
|
+
required && /* @__PURE__ */ jsx53("span", { className: styles24.required, "aria-hidden": "true", children: "*" })
|
|
1273
2164
|
] }),
|
|
1274
2165
|
children(field),
|
|
1275
|
-
hint && !error && /* @__PURE__ */
|
|
1276
|
-
error && /* @__PURE__ */
|
|
2166
|
+
hint && !error && /* @__PURE__ */ jsx53("p", { id: hintId, className: styles24.hint, children: hint }),
|
|
2167
|
+
error && /* @__PURE__ */ jsx53("p", { id: errorId, className: styles24.error, role: "alert", children: error })
|
|
1277
2168
|
] });
|
|
1278
2169
|
}
|
|
1279
2170
|
|
|
1280
2171
|
// src/components/PageHeader/PageHeader.tsx
|
|
1281
|
-
import
|
|
1282
|
-
import { jsx as
|
|
2172
|
+
import styles25 from "./PageHeader.module.css";
|
|
2173
|
+
import { jsx as jsx54, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
1283
2174
|
function PageHeader({ title, description, action, className }) {
|
|
1284
|
-
return /* @__PURE__ */
|
|
1285
|
-
/* @__PURE__ */
|
|
1286
|
-
/* @__PURE__ */
|
|
1287
|
-
description && /* @__PURE__ */
|
|
2175
|
+
return /* @__PURE__ */ jsxs39("header", { className: [styles25.header, className].filter(Boolean).join(" "), children: [
|
|
2176
|
+
/* @__PURE__ */ jsxs39("div", { className: styles25.text, children: [
|
|
2177
|
+
/* @__PURE__ */ jsx54("h1", { className: styles25.title, children: title }),
|
|
2178
|
+
description && /* @__PURE__ */ jsx54("p", { className: styles25.description, children: description })
|
|
1288
2179
|
] }),
|
|
1289
|
-
action && /* @__PURE__ */
|
|
2180
|
+
action && /* @__PURE__ */ jsx54("div", { className: styles25.action, children: action })
|
|
1290
2181
|
] });
|
|
1291
2182
|
}
|
|
1292
2183
|
|
|
1293
2184
|
// src/components/EmptyState/EmptyState.tsx
|
|
1294
|
-
import
|
|
1295
|
-
import { jsx as
|
|
2185
|
+
import styles26 from "./EmptyState.module.css";
|
|
2186
|
+
import { jsx as jsx55, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
1296
2187
|
function EmptyState({ icon, heading, description, action, className }) {
|
|
1297
|
-
return /* @__PURE__ */
|
|
1298
|
-
icon && /* @__PURE__ */
|
|
1299
|
-
/* @__PURE__ */
|
|
1300
|
-
description && /* @__PURE__ */
|
|
2188
|
+
return /* @__PURE__ */ jsxs40("div", { className: [styles26.root, className].filter(Boolean).join(" "), children: [
|
|
2189
|
+
icon && /* @__PURE__ */ jsx55("span", { className: styles26.icon, children: /* @__PURE__ */ jsx55(Icon, { name: icon, size: "lg", "aria-hidden": true }) }),
|
|
2190
|
+
/* @__PURE__ */ jsx55("h2", { className: styles26.heading, children: heading }),
|
|
2191
|
+
description && /* @__PURE__ */ jsx55("p", { className: styles26.description, children: description }),
|
|
1301
2192
|
action
|
|
1302
2193
|
] });
|
|
1303
2194
|
}
|
|
1304
2195
|
|
|
1305
2196
|
// src/components/Spinner/Spinner.tsx
|
|
1306
|
-
import
|
|
1307
|
-
import { jsx as
|
|
2197
|
+
import styles27 from "./Spinner.module.css";
|
|
2198
|
+
import { jsx as jsx56 } from "react/jsx-runtime";
|
|
1308
2199
|
var sizeClass = {
|
|
1309
|
-
sm:
|
|
1310
|
-
md:
|
|
1311
|
-
lg:
|
|
2200
|
+
sm: styles27.sm,
|
|
2201
|
+
md: styles27.md,
|
|
2202
|
+
lg: styles27.lg
|
|
1312
2203
|
};
|
|
1313
2204
|
function Spinner({ size = "md", label = "Loading\u2026", className }) {
|
|
1314
|
-
return /* @__PURE__ */
|
|
2205
|
+
return /* @__PURE__ */ jsx56(
|
|
1315
2206
|
"span",
|
|
1316
2207
|
{
|
|
1317
2208
|
role: "status",
|
|
1318
2209
|
"aria-label": label,
|
|
1319
|
-
className: [
|
|
2210
|
+
className: [styles27.spinner, sizeClass[size], className].filter(Boolean).join(" ")
|
|
1320
2211
|
}
|
|
1321
2212
|
);
|
|
1322
2213
|
}
|
|
1323
2214
|
|
|
1324
2215
|
// src/components/Avatar/Avatar.tsx
|
|
1325
|
-
import { useState as
|
|
1326
|
-
import
|
|
1327
|
-
import { jsx as
|
|
2216
|
+
import { useState as useState9 } from "react";
|
|
2217
|
+
import styles28 from "./Avatar.module.css";
|
|
2218
|
+
import { jsx as jsx57 } from "react/jsx-runtime";
|
|
1328
2219
|
function initials(name) {
|
|
1329
2220
|
const parts = name.trim().split(/\s+/);
|
|
1330
2221
|
if (parts.length === 1) return (parts[0] ?? "").slice(0, 2).toUpperCase();
|
|
1331
2222
|
return ((parts[0]?.[0] ?? "") + (parts[parts.length - 1]?.[0] ?? "")).toUpperCase();
|
|
1332
2223
|
}
|
|
1333
2224
|
var sizeClass2 = {
|
|
1334
|
-
sm:
|
|
1335
|
-
md:
|
|
1336
|
-
lg:
|
|
2225
|
+
sm: styles28.sm,
|
|
2226
|
+
md: styles28.md,
|
|
2227
|
+
lg: styles28.lg
|
|
1337
2228
|
};
|
|
1338
2229
|
function Avatar({ name, src, size = "md", className }) {
|
|
1339
|
-
const [imgFailed, setImgFailed] =
|
|
1340
|
-
const cls = [
|
|
2230
|
+
const [imgFailed, setImgFailed] = useState9(false);
|
|
2231
|
+
const cls = [styles28.avatar, sizeClass2[size], className].filter(Boolean).join(" ");
|
|
1341
2232
|
if (src && !imgFailed) {
|
|
1342
|
-
return /* @__PURE__ */
|
|
2233
|
+
return /* @__PURE__ */ jsx57("span", { className: cls, "aria-label": name, children: /* @__PURE__ */ jsx57("img", { src, alt: name, className: styles28.img, onError: () => setImgFailed(true) }) });
|
|
1343
2234
|
}
|
|
1344
|
-
return /* @__PURE__ */
|
|
2235
|
+
return /* @__PURE__ */ jsx57("span", { className: cls, "aria-label": name, children: initials(name) });
|
|
1345
2236
|
}
|
|
1346
2237
|
|
|
1347
2238
|
// src/components/NavTabs/NavTabs.tsx
|
|
1348
|
-
import
|
|
1349
|
-
import { jsx as
|
|
2239
|
+
import styles29 from "./NavTabs.module.css";
|
|
2240
|
+
import { jsx as jsx58 } from "react/jsx-runtime";
|
|
1350
2241
|
function NavTabs({ items, className, "aria-label": ariaLabel }) {
|
|
1351
|
-
return /* @__PURE__ */
|
|
2242
|
+
return /* @__PURE__ */ jsx58(
|
|
1352
2243
|
"nav",
|
|
1353
2244
|
{
|
|
1354
|
-
className: [
|
|
2245
|
+
className: [styles29.nav, className].filter(Boolean).join(" "),
|
|
1355
2246
|
"aria-label": ariaLabel ?? "Page navigation",
|
|
1356
|
-
children: items.map((item) => /* @__PURE__ */
|
|
2247
|
+
children: items.map((item) => /* @__PURE__ */ jsx58(
|
|
1357
2248
|
"a",
|
|
1358
2249
|
{
|
|
1359
2250
|
href: item.href,
|
|
1360
|
-
className: [
|
|
2251
|
+
className: [styles29.link, item.active && styles29.active].filter(Boolean).join(" "),
|
|
1361
2252
|
"aria-current": item.active ? "page" : void 0,
|
|
1362
2253
|
children: item.label
|
|
1363
2254
|
},
|
|
@@ -1369,25 +2260,25 @@ function NavTabs({ items, className, "aria-label": ariaLabel }) {
|
|
|
1369
2260
|
|
|
1370
2261
|
// src/components/Tooltip/Tooltip.tsx
|
|
1371
2262
|
import { useId as useId3 } from "react";
|
|
1372
|
-
import
|
|
1373
|
-
import { jsx as
|
|
2263
|
+
import styles30 from "./Tooltip.module.css";
|
|
2264
|
+
import { jsx as jsx59, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
1374
2265
|
var sideClass = {
|
|
1375
|
-
top:
|
|
1376
|
-
bottom:
|
|
1377
|
-
left:
|
|
1378
|
-
right:
|
|
2266
|
+
top: styles30.top,
|
|
2267
|
+
bottom: styles30.bottom,
|
|
2268
|
+
left: styles30.left,
|
|
2269
|
+
right: styles30.right
|
|
1379
2270
|
};
|
|
1380
2271
|
function Tooltip({ content, children, side = "top" }) {
|
|
1381
2272
|
const tipId = useId3();
|
|
1382
|
-
return /* @__PURE__ */
|
|
1383
|
-
/* @__PURE__ */
|
|
1384
|
-
/* @__PURE__ */
|
|
2273
|
+
return /* @__PURE__ */ jsxs41("span", { className: styles30.wrapper, children: [
|
|
2274
|
+
/* @__PURE__ */ jsx59("span", { "aria-describedby": tipId, children }),
|
|
2275
|
+
/* @__PURE__ */ jsx59("span", { id: tipId, role: "tooltip", className: [styles30.tip, sideClass[side]].join(" "), children: content })
|
|
1385
2276
|
] });
|
|
1386
2277
|
}
|
|
1387
2278
|
|
|
1388
2279
|
// src/components/Checkbox/Checkbox.tsx
|
|
1389
|
-
import
|
|
1390
|
-
import { jsx as
|
|
2280
|
+
import styles31 from "./Checkbox.module.css";
|
|
2281
|
+
import { jsx as jsx60, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
1391
2282
|
function Checkbox({
|
|
1392
2283
|
checked,
|
|
1393
2284
|
onChange,
|
|
@@ -1399,9 +2290,9 @@ function Checkbox({
|
|
|
1399
2290
|
...rest
|
|
1400
2291
|
}) {
|
|
1401
2292
|
const inputId = id ?? `checkbox-${label.toLowerCase().replace(/\s+/g, "-")}`;
|
|
1402
|
-
return /* @__PURE__ */
|
|
1403
|
-
/* @__PURE__ */
|
|
1404
|
-
/* @__PURE__ */
|
|
2293
|
+
return /* @__PURE__ */ jsxs42("span", { className: [styles31.root, className].filter(Boolean).join(" "), children: [
|
|
2294
|
+
/* @__PURE__ */ jsxs42("span", { className: [styles31.box, checked ? styles31.checked : ""].filter(Boolean).join(" "), children: [
|
|
2295
|
+
/* @__PURE__ */ jsx60(
|
|
1405
2296
|
"input",
|
|
1406
2297
|
{
|
|
1407
2298
|
...rest,
|
|
@@ -1409,11 +2300,11 @@ function Checkbox({
|
|
|
1409
2300
|
type: "checkbox",
|
|
1410
2301
|
checked,
|
|
1411
2302
|
disabled,
|
|
1412
|
-
className:
|
|
2303
|
+
className: styles31.input,
|
|
1413
2304
|
onChange: (e) => onChange(e.target.checked)
|
|
1414
2305
|
}
|
|
1415
2306
|
),
|
|
1416
|
-
checked && /* @__PURE__ */
|
|
2307
|
+
checked && /* @__PURE__ */ jsx60("svg", { className: styles31.tick, viewBox: "0 0 10 8", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx60(
|
|
1417
2308
|
"path",
|
|
1418
2309
|
{
|
|
1419
2310
|
d: "M1 4l3 3 5-6",
|
|
@@ -1424,14 +2315,14 @@ function Checkbox({
|
|
|
1424
2315
|
}
|
|
1425
2316
|
) })
|
|
1426
2317
|
] }),
|
|
1427
|
-
/* @__PURE__ */
|
|
2318
|
+
label && /* @__PURE__ */ jsx60(
|
|
1428
2319
|
"label",
|
|
1429
2320
|
{
|
|
1430
2321
|
htmlFor: inputId,
|
|
1431
2322
|
className: [
|
|
1432
|
-
|
|
1433
|
-
strikeThrough && checked ?
|
|
1434
|
-
disabled ?
|
|
2323
|
+
styles31.label,
|
|
2324
|
+
strikeThrough && checked ? styles31.struck : "",
|
|
2325
|
+
disabled ? styles31.disabled : ""
|
|
1435
2326
|
].filter(Boolean).join(" "),
|
|
1436
2327
|
children: label
|
|
1437
2328
|
}
|
|
@@ -1440,8 +2331,8 @@ function Checkbox({
|
|
|
1440
2331
|
}
|
|
1441
2332
|
|
|
1442
2333
|
// src/components/DragHandleRow/DragHandleRow.tsx
|
|
1443
|
-
import
|
|
1444
|
-
import { jsx as
|
|
2334
|
+
import styles32 from "./DragHandleRow.module.css";
|
|
2335
|
+
import { jsx as jsx61, jsxs as jsxs43 } from "react/jsx-runtime";
|
|
1445
2336
|
function DragHandleRow({
|
|
1446
2337
|
children,
|
|
1447
2338
|
handleProps,
|
|
@@ -1449,30 +2340,30 @@ function DragHandleRow({
|
|
|
1449
2340
|
className,
|
|
1450
2341
|
...rest
|
|
1451
2342
|
}) {
|
|
1452
|
-
return /* @__PURE__ */
|
|
2343
|
+
return /* @__PURE__ */ jsxs43(
|
|
1453
2344
|
"div",
|
|
1454
2345
|
{
|
|
1455
2346
|
...rest,
|
|
1456
|
-
className: [
|
|
2347
|
+
className: [styles32.row, isDragging ? styles32.dragging : "", className].filter(Boolean).join(" "),
|
|
1457
2348
|
children: [
|
|
1458
|
-
/* @__PURE__ */
|
|
2349
|
+
/* @__PURE__ */ jsx61(
|
|
1459
2350
|
"button",
|
|
1460
2351
|
{
|
|
1461
2352
|
type: "button",
|
|
1462
2353
|
"aria-label": "Drag to reorder",
|
|
1463
|
-
className:
|
|
2354
|
+
className: styles32.handle,
|
|
1464
2355
|
tabIndex: -1,
|
|
1465
2356
|
...handleProps,
|
|
1466
|
-
children: /* @__PURE__ */
|
|
2357
|
+
children: /* @__PURE__ */ jsx61(DragIcon, {})
|
|
1467
2358
|
}
|
|
1468
2359
|
),
|
|
1469
|
-
/* @__PURE__ */
|
|
2360
|
+
/* @__PURE__ */ jsx61("div", { className: styles32.content, children })
|
|
1470
2361
|
]
|
|
1471
2362
|
}
|
|
1472
2363
|
);
|
|
1473
2364
|
}
|
|
1474
2365
|
function DragIcon() {
|
|
1475
|
-
return /* @__PURE__ */
|
|
2366
|
+
return /* @__PURE__ */ jsx61(
|
|
1476
2367
|
"svg",
|
|
1477
2368
|
{
|
|
1478
2369
|
width: "14",
|
|
@@ -1480,19 +2371,297 @@ function DragIcon() {
|
|
|
1480
2371
|
viewBox: "0 0 14 14",
|
|
1481
2372
|
fill: "none",
|
|
1482
2373
|
"aria-hidden": "true",
|
|
1483
|
-
className:
|
|
2374
|
+
className: styles32.icon,
|
|
1484
2375
|
children: [3, 7, 11].map(
|
|
1485
|
-
(cy) => [4, 10].map((cx) => /* @__PURE__ */
|
|
2376
|
+
(cy) => [4, 10].map((cx) => /* @__PURE__ */ jsx61("circle", { cx, cy, r: 1.2, fill: "currentColor" }, `${cx}-${cy}`))
|
|
1486
2377
|
)
|
|
1487
2378
|
}
|
|
1488
2379
|
);
|
|
1489
2380
|
}
|
|
2381
|
+
|
|
2382
|
+
// src/components/Calendar/Calendar.tsx
|
|
2383
|
+
import { useRef as useRef10, useState as useState10 } from "react";
|
|
2384
|
+
|
|
2385
|
+
// src/components/Calendar/dateUtils.ts
|
|
2386
|
+
function startOfMonth(date) {
|
|
2387
|
+
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
2388
|
+
}
|
|
2389
|
+
function addMonths(date, count) {
|
|
2390
|
+
return new Date(date.getFullYear(), date.getMonth() + count, date.getDate());
|
|
2391
|
+
}
|
|
2392
|
+
function addDays(date, count) {
|
|
2393
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + count);
|
|
2394
|
+
}
|
|
2395
|
+
function isSameDay(a, b) {
|
|
2396
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
2397
|
+
}
|
|
2398
|
+
function isSameMonth(a, b) {
|
|
2399
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
2400
|
+
}
|
|
2401
|
+
function isBeforeDay(a, b) {
|
|
2402
|
+
return a.getFullYear() < b.getFullYear() || a.getFullYear() === b.getFullYear() && (a.getMonth() < b.getMonth() || a.getMonth() === b.getMonth() && a.getDate() < b.getDate());
|
|
2403
|
+
}
|
|
2404
|
+
function isAfterDay(a, b) {
|
|
2405
|
+
return isBeforeDay(b, a);
|
|
2406
|
+
}
|
|
2407
|
+
function isWithinRange(date, min, max) {
|
|
2408
|
+
if (min && isBeforeDay(date, min)) return false;
|
|
2409
|
+
if (max && isAfterDay(date, max)) return false;
|
|
2410
|
+
return true;
|
|
2411
|
+
}
|
|
2412
|
+
function getMonthGrid(month) {
|
|
2413
|
+
const first = startOfMonth(month);
|
|
2414
|
+
const startOffset = first.getDay();
|
|
2415
|
+
const gridStart = addDays(first, -startOffset);
|
|
2416
|
+
const weeks = [];
|
|
2417
|
+
for (let week = 0; week < 6; week++) {
|
|
2418
|
+
const days = [];
|
|
2419
|
+
for (let day = 0; day < 7; day++) {
|
|
2420
|
+
days.push(addDays(gridStart, week * 7 + day));
|
|
2421
|
+
}
|
|
2422
|
+
weeks.push(days);
|
|
2423
|
+
}
|
|
2424
|
+
return weeks;
|
|
2425
|
+
}
|
|
2426
|
+
function formatMonthYear(date) {
|
|
2427
|
+
return new Intl.DateTimeFormat(void 0, { month: "long", year: "numeric" }).format(date);
|
|
2428
|
+
}
|
|
2429
|
+
function formatWeekdayShort(date) {
|
|
2430
|
+
return new Intl.DateTimeFormat(void 0, { weekday: "short" }).format(date);
|
|
2431
|
+
}
|
|
2432
|
+
function formatDateShort(date) {
|
|
2433
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
2434
|
+
month: "short",
|
|
2435
|
+
day: "numeric",
|
|
2436
|
+
year: "numeric"
|
|
2437
|
+
}).format(date);
|
|
2438
|
+
}
|
|
2439
|
+
function formatDateLabel(date) {
|
|
2440
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
2441
|
+
weekday: "long",
|
|
2442
|
+
month: "long",
|
|
2443
|
+
day: "numeric",
|
|
2444
|
+
year: "numeric"
|
|
2445
|
+
}).format(date);
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
// src/components/Calendar/Calendar.tsx
|
|
2449
|
+
import styles33 from "./Calendar.module.css";
|
|
2450
|
+
import { jsx as jsx62, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
2451
|
+
var WEEKDAY_LABEL_WEEK = getMonthGrid(new Date(2026, 0, 1))[0] ?? [];
|
|
2452
|
+
function Calendar({
|
|
2453
|
+
value,
|
|
2454
|
+
onChange,
|
|
2455
|
+
minDate,
|
|
2456
|
+
maxDate,
|
|
2457
|
+
"aria-label": ariaLabel
|
|
2458
|
+
}) {
|
|
2459
|
+
const [displayedMonth, setDisplayedMonth] = useState10(() => startOfMonth(value ?? /* @__PURE__ */ new Date()));
|
|
2460
|
+
const [focusedDate, setFocusedDate] = useState10(() => value ?? /* @__PURE__ */ new Date());
|
|
2461
|
+
const gridRef = useRef10(null);
|
|
2462
|
+
const weeks = getMonthGrid(displayedMonth);
|
|
2463
|
+
function goToMonth(next) {
|
|
2464
|
+
setDisplayedMonth(startOfMonth(next));
|
|
2465
|
+
}
|
|
2466
|
+
function moveFocus(next) {
|
|
2467
|
+
setFocusedDate(next);
|
|
2468
|
+
if (!isSameMonth(next, displayedMonth)) goToMonth(next);
|
|
2469
|
+
requestAnimationFrame(() => {
|
|
2470
|
+
gridRef.current?.querySelector('[tabindex="0"]')?.focus();
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
function selectDate(date) {
|
|
2474
|
+
if (!isWithinRange(date, minDate, maxDate)) return;
|
|
2475
|
+
onChange(date);
|
|
2476
|
+
setFocusedDate(date);
|
|
2477
|
+
}
|
|
2478
|
+
function handleKeyDown(e) {
|
|
2479
|
+
switch (e.key) {
|
|
2480
|
+
case "ArrowLeft":
|
|
2481
|
+
e.preventDefault();
|
|
2482
|
+
moveFocus(addDays(focusedDate, -1));
|
|
2483
|
+
return;
|
|
2484
|
+
case "ArrowRight":
|
|
2485
|
+
e.preventDefault();
|
|
2486
|
+
moveFocus(addDays(focusedDate, 1));
|
|
2487
|
+
return;
|
|
2488
|
+
case "ArrowUp":
|
|
2489
|
+
e.preventDefault();
|
|
2490
|
+
moveFocus(addDays(focusedDate, -7));
|
|
2491
|
+
return;
|
|
2492
|
+
case "ArrowDown":
|
|
2493
|
+
e.preventDefault();
|
|
2494
|
+
moveFocus(addDays(focusedDate, 7));
|
|
2495
|
+
return;
|
|
2496
|
+
case "Home":
|
|
2497
|
+
e.preventDefault();
|
|
2498
|
+
moveFocus(addDays(focusedDate, -focusedDate.getDay()));
|
|
2499
|
+
return;
|
|
2500
|
+
case "End":
|
|
2501
|
+
e.preventDefault();
|
|
2502
|
+
moveFocus(addDays(focusedDate, 6 - focusedDate.getDay()));
|
|
2503
|
+
return;
|
|
2504
|
+
case "PageUp":
|
|
2505
|
+
e.preventDefault();
|
|
2506
|
+
moveFocus(addMonths(focusedDate, -1));
|
|
2507
|
+
return;
|
|
2508
|
+
case "PageDown":
|
|
2509
|
+
e.preventDefault();
|
|
2510
|
+
moveFocus(addMonths(focusedDate, 1));
|
|
2511
|
+
return;
|
|
2512
|
+
case "Enter":
|
|
2513
|
+
case " ":
|
|
2514
|
+
e.preventDefault();
|
|
2515
|
+
selectDate(focusedDate);
|
|
2516
|
+
return;
|
|
2517
|
+
default:
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
return /* @__PURE__ */ jsxs44("div", { className: styles33.calendar, "aria-label": ariaLabel, children: [
|
|
2521
|
+
/* @__PURE__ */ jsxs44("div", { className: styles33.header, children: [
|
|
2522
|
+
/* @__PURE__ */ jsx62(
|
|
2523
|
+
"button",
|
|
2524
|
+
{
|
|
2525
|
+
type: "button",
|
|
2526
|
+
className: styles33.navButton,
|
|
2527
|
+
"aria-label": "Previous month",
|
|
2528
|
+
onClick: () => goToMonth(addMonths(displayedMonth, -1)),
|
|
2529
|
+
children: /* @__PURE__ */ jsx62(Icon, { name: "chevron-left", size: "sm", "aria-hidden": true })
|
|
2530
|
+
}
|
|
2531
|
+
),
|
|
2532
|
+
/* @__PURE__ */ jsx62("span", { className: styles33.monthLabel, children: formatMonthYear(displayedMonth) }),
|
|
2533
|
+
/* @__PURE__ */ jsx62(
|
|
2534
|
+
"button",
|
|
2535
|
+
{
|
|
2536
|
+
type: "button",
|
|
2537
|
+
className: styles33.navButton,
|
|
2538
|
+
"aria-label": "Next month",
|
|
2539
|
+
onClick: () => goToMonth(addMonths(displayedMonth, 1)),
|
|
2540
|
+
children: /* @__PURE__ */ jsx62(Icon, { name: "chevron-right", size: "sm", "aria-hidden": true })
|
|
2541
|
+
}
|
|
2542
|
+
)
|
|
2543
|
+
] }),
|
|
2544
|
+
/* @__PURE__ */ jsxs44(
|
|
2545
|
+
"div",
|
|
2546
|
+
{
|
|
2547
|
+
ref: gridRef,
|
|
2548
|
+
role: "grid",
|
|
2549
|
+
"aria-label": formatMonthYear(displayedMonth),
|
|
2550
|
+
className: styles33.grid,
|
|
2551
|
+
onKeyDown: handleKeyDown,
|
|
2552
|
+
tabIndex: -1,
|
|
2553
|
+
children: [
|
|
2554
|
+
/* @__PURE__ */ jsx62("div", { role: "row", className: styles33.weekdayRow, children: WEEKDAY_LABEL_WEEK.map((d) => /* @__PURE__ */ jsx62("span", { role: "columnheader", className: styles33.weekday, "aria-hidden": true, children: formatWeekdayShort(d) }, d.getDay())) }),
|
|
2555
|
+
weeks.map((week) => {
|
|
2556
|
+
const firstDayOfWeek = week[0];
|
|
2557
|
+
if (!firstDayOfWeek) return null;
|
|
2558
|
+
return /* @__PURE__ */ jsx62("div", { role: "row", className: styles33.week, children: week.map((date) => {
|
|
2559
|
+
const isCurrentMonth = isSameMonth(date, displayedMonth);
|
|
2560
|
+
const isSelected = value !== null && isSameDay(date, value);
|
|
2561
|
+
const isToday = isSameDay(date, /* @__PURE__ */ new Date());
|
|
2562
|
+
const isFocusTarget = isSameDay(date, focusedDate);
|
|
2563
|
+
const disabled = !isWithinRange(date, minDate, maxDate);
|
|
2564
|
+
return /* @__PURE__ */ jsx62("div", { role: "gridcell", "aria-selected": isSelected, children: /* @__PURE__ */ jsx62(
|
|
2565
|
+
"button",
|
|
2566
|
+
{
|
|
2567
|
+
type: "button",
|
|
2568
|
+
tabIndex: isFocusTarget ? 0 : -1,
|
|
2569
|
+
disabled,
|
|
2570
|
+
"aria-current": isToday ? "date" : void 0,
|
|
2571
|
+
"aria-label": formatDateLabel(date),
|
|
2572
|
+
className: [
|
|
2573
|
+
styles33.day,
|
|
2574
|
+
!isCurrentMonth ? styles33.dayOutside : "",
|
|
2575
|
+
isSelected ? styles33.daySelected : "",
|
|
2576
|
+
isToday && !isSelected ? styles33.dayToday : ""
|
|
2577
|
+
].filter(Boolean).join(" "),
|
|
2578
|
+
onClick: () => selectDate(date),
|
|
2579
|
+
onFocus: () => setFocusedDate(date),
|
|
2580
|
+
children: date.getDate()
|
|
2581
|
+
}
|
|
2582
|
+
) }, date.toISOString());
|
|
2583
|
+
}) }, firstDayOfWeek.toISOString());
|
|
2584
|
+
})
|
|
2585
|
+
]
|
|
2586
|
+
}
|
|
2587
|
+
)
|
|
2588
|
+
] });
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
// src/components/DatePicker/DatePicker.tsx
|
|
2592
|
+
import { useState as useState11 } from "react";
|
|
2593
|
+
import styles34 from "./DatePicker.module.css";
|
|
2594
|
+
import { Fragment as Fragment2, jsx as jsx63, jsxs as jsxs45 } from "react/jsx-runtime";
|
|
2595
|
+
var POPOVER_WIDTH = 320;
|
|
2596
|
+
function DatePicker({
|
|
2597
|
+
value,
|
|
2598
|
+
onChange,
|
|
2599
|
+
minDate,
|
|
2600
|
+
maxDate,
|
|
2601
|
+
placeholder = "Select date",
|
|
2602
|
+
"aria-label": ariaLabel,
|
|
2603
|
+
disabled = false
|
|
2604
|
+
}) {
|
|
2605
|
+
const [open, setOpen] = useState11(false);
|
|
2606
|
+
const isMobile = useIsMobile();
|
|
2607
|
+
function handleSelect(date) {
|
|
2608
|
+
onChange(date);
|
|
2609
|
+
setOpen(false);
|
|
2610
|
+
}
|
|
2611
|
+
const trigger = /* @__PURE__ */ jsxs45(
|
|
2612
|
+
"button",
|
|
2613
|
+
{
|
|
2614
|
+
type: "button",
|
|
2615
|
+
className: styles34.trigger,
|
|
2616
|
+
disabled,
|
|
2617
|
+
"aria-label": ariaLabel,
|
|
2618
|
+
"aria-haspopup": "dialog",
|
|
2619
|
+
"aria-expanded": open,
|
|
2620
|
+
onClick: () => setOpen((o) => !o),
|
|
2621
|
+
children: [
|
|
2622
|
+
/* @__PURE__ */ jsx63("span", { className: value ? styles34.triggerValue : styles34.triggerPlaceholder, children: value ? formatDateShort(value) : placeholder }),
|
|
2623
|
+
/* @__PURE__ */ jsx63(Icon, { name: "calendar", size: "sm", "aria-hidden": true })
|
|
2624
|
+
]
|
|
2625
|
+
}
|
|
2626
|
+
);
|
|
2627
|
+
const calendar = /* @__PURE__ */ jsx63(
|
|
2628
|
+
Calendar,
|
|
2629
|
+
{
|
|
2630
|
+
value,
|
|
2631
|
+
onChange: handleSelect,
|
|
2632
|
+
minDate,
|
|
2633
|
+
maxDate,
|
|
2634
|
+
"aria-label": ariaLabel
|
|
2635
|
+
}
|
|
2636
|
+
);
|
|
2637
|
+
if (isMobile) {
|
|
2638
|
+
return /* @__PURE__ */ jsxs45(Fragment2, { children: [
|
|
2639
|
+
trigger,
|
|
2640
|
+
/* @__PURE__ */ jsx63(Drawer, { open, onClose: () => setOpen(false), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx63("div", { className: styles34.drawerBody, children: calendar }) })
|
|
2641
|
+
] });
|
|
2642
|
+
}
|
|
2643
|
+
return /* @__PURE__ */ jsx63(
|
|
2644
|
+
Popover,
|
|
2645
|
+
{
|
|
2646
|
+
trigger,
|
|
2647
|
+
open,
|
|
2648
|
+
onClose: () => setOpen(false),
|
|
2649
|
+
"aria-label": ariaLabel,
|
|
2650
|
+
width: POPOVER_WIDTH,
|
|
2651
|
+
children: /* @__PURE__ */ jsx63("div", { className: styles34.popoverBody, children: calendar })
|
|
2652
|
+
}
|
|
2653
|
+
);
|
|
2654
|
+
}
|
|
1490
2655
|
export {
|
|
1491
2656
|
Avatar,
|
|
1492
2657
|
Badge,
|
|
1493
2658
|
Button,
|
|
2659
|
+
Calendar,
|
|
1494
2660
|
Card,
|
|
1495
2661
|
Checkbox,
|
|
2662
|
+
CodeTextarea,
|
|
2663
|
+
ConfirmDialog,
|
|
2664
|
+
DatePicker,
|
|
1496
2665
|
Dialog,
|
|
1497
2666
|
DragHandleRow,
|
|
1498
2667
|
Drawer,
|
|
@@ -1500,17 +2669,29 @@ export {
|
|
|
1500
2669
|
FormField,
|
|
1501
2670
|
Icon,
|
|
1502
2671
|
Input,
|
|
2672
|
+
MOBILE_BREAKPOINT_PX,
|
|
2673
|
+
Menu,
|
|
1503
2674
|
NavTabs,
|
|
2675
|
+
OverlayHeader,
|
|
1504
2676
|
PageHeader,
|
|
1505
2677
|
Popover,
|
|
1506
2678
|
SegmentedControl,
|
|
1507
2679
|
Select,
|
|
2680
|
+
Sheet,
|
|
1508
2681
|
Spinner,
|
|
2682
|
+
SplitPane,
|
|
2683
|
+
StatusBadge,
|
|
1509
2684
|
SystemBanner,
|
|
1510
2685
|
Tabs,
|
|
2686
|
+
TagInput,
|
|
1511
2687
|
Textarea,
|
|
1512
2688
|
ToastProvider,
|
|
1513
2689
|
Toggle,
|
|
1514
2690
|
Tooltip,
|
|
2691
|
+
useDoubleTapHandler,
|
|
2692
|
+
useIsMobile,
|
|
2693
|
+
useLongPress,
|
|
2694
|
+
useOverlaySecondRow,
|
|
2695
|
+
useSingleOrDoubleTap,
|
|
1515
2696
|
useToast
|
|
1516
2697
|
};
|