@sovereignfs/ui 0.21.3 → 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 +109 -0
- 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 +57 -0
- package/dist/Drawer.module.css +61 -0
- package/dist/FormField.module.css +5 -0
- package/dist/Icon.module.css +5 -0
- package/dist/Input.module.css +10 -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/Select.module.css +9 -0
- 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/Textarea.module.css +39 -0
- package/dist/Toast.module.css +7 -5
- package/dist/Toggle.module.css +1 -1
- package/dist/Tooltip.module.css +6 -1
- package/dist/index.d.ts +549 -20
- package/dist/index.js +1496 -193
- package/dist/tokens/primitives.css +19 -0
- package/dist/tokens/semantic.css +19 -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 +109 -0
- package/src/components/Checkbox/Checkbox.tsx +78 -0
- 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 +57 -0
- package/src/components/DragHandleRow/DragHandleRow.tsx +61 -0
- 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/FormField/FormField.module.css +5 -0
- package/src/components/FormField/FormField.tsx +36 -11
- package/src/components/FormField/__tests__/FormField.test.tsx +83 -0
- 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/Input/Input.module.css +10 -0
- 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/Select/Select.module.css +9 -0
- package/src/components/Select/Select.tsx +10 -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/Textarea/Textarea.module.css +39 -0
- package/src/components/Textarea/Textarea.tsx +15 -0
- package/src/components/Textarea/__tests__/Textarea.test.tsx +29 -0
- package/src/components/Toast/Toast.module.css +7 -5
- package/src/components/Toggle/Toggle.module.css +1 -1
- 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 +37 -2
- package/src/motion.ts +78 -0
- package/src/stories/Checkbox.stories.tsx +45 -0
- package/src/stories/DesignSystemOverview.stories.tsx +354 -8
- package/src/stories/DragHandleRow.stories.tsx +41 -0
- package/src/stories/FormField.stories.tsx +8 -8
- package/src/stories/InteractionHooks.stories.tsx +388 -0
- package/src/stories/MobilePatterns.stories.tsx +4 -4
- package/src/stories/Textarea.stories.tsx +51 -0
- package/src/stories/TokenGallery.stories.tsx +87 -1
- package/src/tokens/primitives.css +19 -0
- package/src/tokens/semantic.css +19 -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
|
-
className: [styles6.wrapper, size === "sm" ? styles6.sm : void 0].filter(Boolean).join(" "),
|
|
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,29 +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 });
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// src/components/Textarea/Textarea.tsx
|
|
1008
|
+
import styles11 from "./Textarea.module.css";
|
|
1009
|
+
import { jsx as jsx40 } from "react/jsx-runtime";
|
|
1010
|
+
function Textarea({ className, rows = 4, ...rest }) {
|
|
1011
|
+
const classes = [styles11.textarea, className].filter(Boolean).join(" ");
|
|
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
|
+
] });
|
|
904
1330
|
}
|
|
905
1331
|
|
|
906
1332
|
// src/components/Dialog/Dialog.tsx
|
|
907
|
-
import { useEffect as
|
|
1333
|
+
import { createContext, useContext, useEffect as useEffect4, useRef as useRef3, useState as useState5 } from "react";
|
|
908
1334
|
|
|
909
1335
|
// src/scroll-lock.ts
|
|
910
1336
|
function lockBodyScroll() {
|
|
@@ -918,9 +1344,74 @@ function unlockBodyScroll() {
|
|
|
918
1344
|
if (next === 0) document.body.style.overflowY = "";
|
|
919
1345
|
}
|
|
920
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
|
+
|
|
921
1399
|
// src/components/Dialog/Dialog.tsx
|
|
922
|
-
import
|
|
923
|
-
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
|
+
}
|
|
924
1415
|
var FOCUSABLE = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
925
1416
|
function Dialog({
|
|
926
1417
|
open,
|
|
@@ -930,14 +1421,17 @@ function Dialog({
|
|
|
930
1421
|
title,
|
|
931
1422
|
children
|
|
932
1423
|
}) {
|
|
933
|
-
const panelRef =
|
|
934
|
-
const previouslyFocused =
|
|
935
|
-
|
|
936
|
-
|
|
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;
|
|
937
1431
|
lockBodyScroll();
|
|
938
1432
|
return unlockBodyScroll;
|
|
939
|
-
}, [
|
|
940
|
-
|
|
1433
|
+
}, [mounted]);
|
|
1434
|
+
useEffect4(() => {
|
|
941
1435
|
if (!open) return;
|
|
942
1436
|
previouslyFocused.current = document.activeElement;
|
|
943
1437
|
const panel = panelRef.current;
|
|
@@ -945,7 +1439,7 @@ function Dialog({
|
|
|
945
1439
|
(first ?? panel)?.focus();
|
|
946
1440
|
return () => previouslyFocused.current?.focus();
|
|
947
1441
|
}, [open]);
|
|
948
|
-
|
|
1442
|
+
useEffect4(() => {
|
|
949
1443
|
if (!open) return;
|
|
950
1444
|
function handleKeyDown(e) {
|
|
951
1445
|
if (e.key === "Escape") {
|
|
@@ -975,14 +1469,15 @@ function Dialog({
|
|
|
975
1469
|
document.addEventListener("keydown", handleKeyDown);
|
|
976
1470
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
977
1471
|
}, [open, onClose]);
|
|
978
|
-
if (!
|
|
1472
|
+
if (!mounted) return null;
|
|
1473
|
+
const isOpenPhase = phase === "open";
|
|
979
1474
|
return (
|
|
980
1475
|
// role="presentation" removes the scrim from the AT (it is purely visual).
|
|
981
1476
|
// e.target check lets clicks inside the panel bubble without triggering dismiss.
|
|
982
|
-
/* @__PURE__ */
|
|
1477
|
+
/* @__PURE__ */ jsx46(
|
|
983
1478
|
"div",
|
|
984
1479
|
{
|
|
985
|
-
className:
|
|
1480
|
+
className: [styles17.scrim, isOpenPhase ? styles17.scrimOpen : ""].filter(Boolean).join(" "),
|
|
986
1481
|
role: "presentation",
|
|
987
1482
|
onClick: (e) => {
|
|
988
1483
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -990,7 +1485,7 @@ function Dialog({
|
|
|
990
1485
|
onKeyDown: (e) => {
|
|
991
1486
|
if (e.key === "Escape") onClose();
|
|
992
1487
|
},
|
|
993
|
-
children: /* @__PURE__ */
|
|
1488
|
+
children: /* @__PURE__ */ jsxs32(
|
|
994
1489
|
"div",
|
|
995
1490
|
{
|
|
996
1491
|
ref: panelRef,
|
|
@@ -998,23 +1493,19 @@ function Dialog({
|
|
|
998
1493
|
"aria-modal": "true",
|
|
999
1494
|
"aria-label": ariaLabel ?? title,
|
|
1000
1495
|
tabIndex: -1,
|
|
1001
|
-
className: [
|
|
1496
|
+
className: [styles17.panel, styles17[size], isOpenPhase ? styles17.panelOpen : ""].filter(Boolean).join(" "),
|
|
1002
1497
|
children: [
|
|
1003
|
-
/* @__PURE__ */
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
)
|
|
1015
|
-
] }),
|
|
1016
|
-
/* @__PURE__ */ jsx39("button", { type: "button", className: styles11.close, "aria-label": "Close", onClick: onClose, children: "\xD7" }),
|
|
1017
|
-
/* @__PURE__ */ jsx39("div", { className: styles11.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 }) })
|
|
1018
1509
|
]
|
|
1019
1510
|
}
|
|
1020
1511
|
)
|
|
@@ -1024,19 +1515,30 @@ function Dialog({
|
|
|
1024
1515
|
}
|
|
1025
1516
|
|
|
1026
1517
|
// src/components/Drawer/Drawer.tsx
|
|
1027
|
-
import { useEffect as
|
|
1028
|
-
import
|
|
1029
|
-
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;
|
|
1030
1522
|
var FOCUSABLE2 = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
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;
|
|
1036
1538
|
lockBodyScroll();
|
|
1037
1539
|
return unlockBodyScroll;
|
|
1038
|
-
}, [
|
|
1039
|
-
|
|
1540
|
+
}, [mounted]);
|
|
1541
|
+
useEffect5(() => {
|
|
1040
1542
|
if (!open) return;
|
|
1041
1543
|
previouslyFocused.current = document.activeElement;
|
|
1042
1544
|
const panel = panelRef.current;
|
|
@@ -1044,7 +1546,7 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1044
1546
|
(first ?? panel)?.focus();
|
|
1045
1547
|
return () => previouslyFocused.current?.focus();
|
|
1046
1548
|
}, [open]);
|
|
1047
|
-
|
|
1549
|
+
useEffect5(() => {
|
|
1048
1550
|
if (!open) return;
|
|
1049
1551
|
function handleKeyDown(e) {
|
|
1050
1552
|
if (e.key === "Escape") {
|
|
@@ -1074,14 +1576,40 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1074
1576
|
document.addEventListener("keydown", handleKeyDown);
|
|
1075
1577
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1076
1578
|
}, [open, onClose]);
|
|
1077
|
-
|
|
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";
|
|
1078
1606
|
return (
|
|
1079
1607
|
// role="presentation" removes the scrim from the AT (it is purely visual).
|
|
1080
1608
|
// e.target check lets clicks inside the panel bubble without triggering dismiss.
|
|
1081
|
-
/* @__PURE__ */
|
|
1609
|
+
/* @__PURE__ */ jsx47(
|
|
1082
1610
|
"div",
|
|
1083
1611
|
{
|
|
1084
|
-
className:
|
|
1612
|
+
className: [styles18.scrim, isOpenPhase ? styles18.scrimOpen : ""].filter(Boolean).join(" "),
|
|
1085
1613
|
role: "presentation",
|
|
1086
1614
|
onClick: (e) => {
|
|
1087
1615
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -1089,7 +1617,7 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1089
1617
|
onKeyDown: (e) => {
|
|
1090
1618
|
if (e.key === "Escape") onClose();
|
|
1091
1619
|
},
|
|
1092
|
-
children: /* @__PURE__ */
|
|
1620
|
+
children: /* @__PURE__ */ jsxs33(
|
|
1093
1621
|
"div",
|
|
1094
1622
|
{
|
|
1095
1623
|
ref: panelRef,
|
|
@@ -1097,8 +1625,25 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1097
1625
|
"aria-modal": "true",
|
|
1098
1626
|
"aria-label": ariaLabel,
|
|
1099
1627
|
tabIndex: -1,
|
|
1100
|
-
className:
|
|
1101
|
-
|
|
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
|
+
]
|
|
1102
1647
|
}
|
|
1103
1648
|
)
|
|
1104
1649
|
}
|
|
@@ -1106,37 +1651,391 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1106
1651
|
);
|
|
1107
1652
|
}
|
|
1108
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
|
+
|
|
1109
2008
|
// src/components/Toast/Toast.tsx
|
|
1110
2009
|
import {
|
|
1111
|
-
createContext,
|
|
1112
|
-
useCallback,
|
|
1113
|
-
useContext,
|
|
1114
|
-
useEffect as
|
|
2010
|
+
createContext as createContext2,
|
|
2011
|
+
useCallback as useCallback3,
|
|
2012
|
+
useContext as useContext2,
|
|
2013
|
+
useEffect as useEffect10,
|
|
1115
2014
|
useId,
|
|
1116
|
-
useMemo,
|
|
1117
|
-
useRef as
|
|
1118
|
-
useState
|
|
2015
|
+
useMemo as useMemo2,
|
|
2016
|
+
useRef as useRef9,
|
|
2017
|
+
useState as useState8
|
|
1119
2018
|
} from "react";
|
|
1120
|
-
import
|
|
1121
|
-
import { jsx as
|
|
1122
|
-
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);
|
|
1123
2022
|
function useToast() {
|
|
1124
|
-
const ctx =
|
|
2023
|
+
const ctx = useContext2(ToastContext);
|
|
1125
2024
|
if (!ctx) throw new Error("useToast() must be used inside <ToastProvider>");
|
|
1126
2025
|
return ctx;
|
|
1127
2026
|
}
|
|
1128
2027
|
function ToastProvider({ children }) {
|
|
1129
|
-
const [toasts, setToasts] =
|
|
1130
|
-
const timers =
|
|
2028
|
+
const [toasts, setToasts] = useState8([]);
|
|
2029
|
+
const timers = useRef9(/* @__PURE__ */ new Map());
|
|
1131
2030
|
const seedId = useId();
|
|
1132
|
-
const counter =
|
|
1133
|
-
const dismiss =
|
|
2031
|
+
const counter = useRef9(0);
|
|
2032
|
+
const dismiss = useCallback3((id) => {
|
|
1134
2033
|
setToasts((prev) => prev.map((t) => t.id === id ? { ...t, exiting: true } : t));
|
|
1135
2034
|
setTimeout(() => {
|
|
1136
2035
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
1137
2036
|
}, 220);
|
|
1138
2037
|
}, []);
|
|
1139
|
-
const show =
|
|
2038
|
+
const show = useCallback3(
|
|
1140
2039
|
(item) => {
|
|
1141
2040
|
const id = `${seedId}-${++counter.current}`;
|
|
1142
2041
|
const duration = item.duration ?? 5e3;
|
|
@@ -1149,23 +2048,23 @@ function ToastProvider({ children }) {
|
|
|
1149
2048
|
},
|
|
1150
2049
|
[seedId, dismiss]
|
|
1151
2050
|
);
|
|
1152
|
-
|
|
2051
|
+
useEffect10(() => {
|
|
1153
2052
|
const currentTimers = timers.current;
|
|
1154
2053
|
return () => {
|
|
1155
2054
|
currentTimers.forEach((t) => clearTimeout(t));
|
|
1156
2055
|
};
|
|
1157
2056
|
}, []);
|
|
1158
|
-
const value =
|
|
1159
|
-
return /* @__PURE__ */
|
|
2057
|
+
const value = useMemo2(() => ({ show, dismiss }), [show, dismiss]);
|
|
2058
|
+
return /* @__PURE__ */ jsxs37(ToastContext.Provider, { value, children: [
|
|
1160
2059
|
children,
|
|
1161
|
-
/* @__PURE__ */
|
|
2060
|
+
/* @__PURE__ */ jsx51(
|
|
1162
2061
|
"ol",
|
|
1163
2062
|
{
|
|
1164
|
-
className:
|
|
2063
|
+
className: styles22.region,
|
|
1165
2064
|
"aria-label": "Notifications",
|
|
1166
2065
|
"aria-live": "polite",
|
|
1167
2066
|
"aria-atomic": "false",
|
|
1168
|
-
children: toasts.map((toast) => /* @__PURE__ */
|
|
2067
|
+
children: toasts.map((toast) => /* @__PURE__ */ jsx51(Toast, { ...toast, onDismiss: dismiss }, toast.id))
|
|
1169
2068
|
}
|
|
1170
2069
|
)
|
|
1171
2070
|
] });
|
|
@@ -1186,24 +2085,24 @@ function Toast({
|
|
|
1186
2085
|
exiting,
|
|
1187
2086
|
onDismiss
|
|
1188
2087
|
}) {
|
|
1189
|
-
const categoryClass =
|
|
2088
|
+
const categoryClass = styles22[category] ?? styles22.info;
|
|
1190
2089
|
const iconName = CATEGORY_ICON[category] ?? "info";
|
|
1191
|
-
return /* @__PURE__ */
|
|
2090
|
+
return /* @__PURE__ */ jsxs37(
|
|
1192
2091
|
"li",
|
|
1193
2092
|
{
|
|
1194
2093
|
role: "status",
|
|
1195
|
-
className: `${
|
|
2094
|
+
className: `${styles22.toast} ${String(categoryClass)} ${exiting ? styles22.exiting : ""}`,
|
|
1196
2095
|
children: [
|
|
1197
|
-
/* @__PURE__ */
|
|
1198
|
-
/* @__PURE__ */
|
|
1199
|
-
/* @__PURE__ */
|
|
1200
|
-
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 })
|
|
1201
2100
|
] }),
|
|
1202
|
-
/* @__PURE__ */
|
|
2101
|
+
/* @__PURE__ */ jsx51(
|
|
1203
2102
|
"button",
|
|
1204
2103
|
{
|
|
1205
2104
|
type: "button",
|
|
1206
|
-
className:
|
|
2105
|
+
className: styles22.close,
|
|
1207
2106
|
"aria-label": "Dismiss notification",
|
|
1208
2107
|
onClick: () => onDismiss(id),
|
|
1209
2108
|
children: "\u2715"
|
|
@@ -1215,12 +2114,12 @@ function Toast({
|
|
|
1215
2114
|
}
|
|
1216
2115
|
|
|
1217
2116
|
// src/components/Card/Card.tsx
|
|
1218
|
-
import
|
|
1219
|
-
import { jsx as
|
|
2117
|
+
import styles23 from "./Card.module.css";
|
|
2118
|
+
import { jsx as jsx52 } from "react/jsx-runtime";
|
|
1220
2119
|
var paddingClass = {
|
|
1221
|
-
sm:
|
|
1222
|
-
md:
|
|
1223
|
-
lg:
|
|
2120
|
+
sm: styles23.paddingSm,
|
|
2121
|
+
md: styles23.paddingMd,
|
|
2122
|
+
lg: styles23.paddingLg
|
|
1224
2123
|
};
|
|
1225
2124
|
function Card({
|
|
1226
2125
|
as: Tag = "div",
|
|
@@ -1230,118 +2129,126 @@ function Card({
|
|
|
1230
2129
|
children,
|
|
1231
2130
|
...rest
|
|
1232
2131
|
}) {
|
|
1233
|
-
const cls = [
|
|
1234
|
-
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 });
|
|
1235
2134
|
}
|
|
1236
2135
|
|
|
1237
2136
|
// src/components/FormField/FormField.tsx
|
|
1238
2137
|
import { useId as useId2 } from "react";
|
|
1239
|
-
import
|
|
1240
|
-
import { jsx as
|
|
2138
|
+
import styles24 from "./FormField.module.css";
|
|
2139
|
+
import { jsx as jsx53, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
1241
2140
|
function FormField({
|
|
1242
2141
|
label,
|
|
1243
2142
|
hint,
|
|
1244
2143
|
error,
|
|
1245
|
-
|
|
2144
|
+
id,
|
|
1246
2145
|
required = false,
|
|
1247
|
-
|
|
1248
|
-
|
|
2146
|
+
className,
|
|
2147
|
+
children
|
|
1249
2148
|
}) {
|
|
1250
|
-
const
|
|
1251
|
-
const
|
|
1252
|
-
const
|
|
1253
|
-
|
|
1254
|
-
|
|
2149
|
+
const generatedId = useId2();
|
|
2150
|
+
const fieldId = id ?? generatedId;
|
|
2151
|
+
const hintId = `${fieldId}-hint`;
|
|
2152
|
+
const errorId = `${fieldId}-error`;
|
|
2153
|
+
const describedBy = [hint && !error && hintId, error && errorId].filter(Boolean).join(" ") || void 0;
|
|
2154
|
+
const field = {
|
|
2155
|
+
id: fieldId,
|
|
2156
|
+
"aria-describedby": describedBy,
|
|
2157
|
+
"aria-invalid": error ? true : void 0,
|
|
2158
|
+
required: required || void 0
|
|
2159
|
+
};
|
|
2160
|
+
return /* @__PURE__ */ jsxs38("div", { className: [styles24.field, className].filter(Boolean).join(" "), children: [
|
|
2161
|
+
/* @__PURE__ */ jsxs38("label", { className: styles24.label, htmlFor: fieldId, children: [
|
|
1255
2162
|
label,
|
|
1256
|
-
required && /* @__PURE__ */
|
|
2163
|
+
required && /* @__PURE__ */ jsx53("span", { className: styles24.required, "aria-hidden": "true", children: "*" })
|
|
1257
2164
|
] }),
|
|
1258
|
-
|
|
1259
|
-
hint && !error && /* @__PURE__ */
|
|
1260
|
-
error && /* @__PURE__ */
|
|
2165
|
+
children(field),
|
|
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 })
|
|
1261
2168
|
] });
|
|
1262
2169
|
}
|
|
1263
2170
|
|
|
1264
2171
|
// src/components/PageHeader/PageHeader.tsx
|
|
1265
|
-
import
|
|
1266
|
-
import { jsx as
|
|
2172
|
+
import styles25 from "./PageHeader.module.css";
|
|
2173
|
+
import { jsx as jsx54, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
1267
2174
|
function PageHeader({ title, description, action, className }) {
|
|
1268
|
-
return /* @__PURE__ */
|
|
1269
|
-
/* @__PURE__ */
|
|
1270
|
-
/* @__PURE__ */
|
|
1271
|
-
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 })
|
|
1272
2179
|
] }),
|
|
1273
|
-
action && /* @__PURE__ */
|
|
2180
|
+
action && /* @__PURE__ */ jsx54("div", { className: styles25.action, children: action })
|
|
1274
2181
|
] });
|
|
1275
2182
|
}
|
|
1276
2183
|
|
|
1277
2184
|
// src/components/EmptyState/EmptyState.tsx
|
|
1278
|
-
import
|
|
1279
|
-
import { jsx as
|
|
2185
|
+
import styles26 from "./EmptyState.module.css";
|
|
2186
|
+
import { jsx as jsx55, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
1280
2187
|
function EmptyState({ icon, heading, description, action, className }) {
|
|
1281
|
-
return /* @__PURE__ */
|
|
1282
|
-
icon && /* @__PURE__ */
|
|
1283
|
-
/* @__PURE__ */
|
|
1284
|
-
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 }),
|
|
1285
2192
|
action
|
|
1286
2193
|
] });
|
|
1287
2194
|
}
|
|
1288
2195
|
|
|
1289
2196
|
// src/components/Spinner/Spinner.tsx
|
|
1290
|
-
import
|
|
1291
|
-
import { jsx as
|
|
2197
|
+
import styles27 from "./Spinner.module.css";
|
|
2198
|
+
import { jsx as jsx56 } from "react/jsx-runtime";
|
|
1292
2199
|
var sizeClass = {
|
|
1293
|
-
sm:
|
|
1294
|
-
md:
|
|
1295
|
-
lg:
|
|
2200
|
+
sm: styles27.sm,
|
|
2201
|
+
md: styles27.md,
|
|
2202
|
+
lg: styles27.lg
|
|
1296
2203
|
};
|
|
1297
2204
|
function Spinner({ size = "md", label = "Loading\u2026", className }) {
|
|
1298
|
-
return /* @__PURE__ */
|
|
2205
|
+
return /* @__PURE__ */ jsx56(
|
|
1299
2206
|
"span",
|
|
1300
2207
|
{
|
|
1301
2208
|
role: "status",
|
|
1302
2209
|
"aria-label": label,
|
|
1303
|
-
className: [
|
|
2210
|
+
className: [styles27.spinner, sizeClass[size], className].filter(Boolean).join(" ")
|
|
1304
2211
|
}
|
|
1305
2212
|
);
|
|
1306
2213
|
}
|
|
1307
2214
|
|
|
1308
2215
|
// src/components/Avatar/Avatar.tsx
|
|
1309
|
-
import { useState as
|
|
1310
|
-
import
|
|
1311
|
-
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";
|
|
1312
2219
|
function initials(name) {
|
|
1313
2220
|
const parts = name.trim().split(/\s+/);
|
|
1314
2221
|
if (parts.length === 1) return (parts[0] ?? "").slice(0, 2).toUpperCase();
|
|
1315
2222
|
return ((parts[0]?.[0] ?? "") + (parts[parts.length - 1]?.[0] ?? "")).toUpperCase();
|
|
1316
2223
|
}
|
|
1317
2224
|
var sizeClass2 = {
|
|
1318
|
-
sm:
|
|
1319
|
-
md:
|
|
1320
|
-
lg:
|
|
2225
|
+
sm: styles28.sm,
|
|
2226
|
+
md: styles28.md,
|
|
2227
|
+
lg: styles28.lg
|
|
1321
2228
|
};
|
|
1322
2229
|
function Avatar({ name, src, size = "md", className }) {
|
|
1323
|
-
const [imgFailed, setImgFailed] =
|
|
1324
|
-
const cls = [
|
|
2230
|
+
const [imgFailed, setImgFailed] = useState9(false);
|
|
2231
|
+
const cls = [styles28.avatar, sizeClass2[size], className].filter(Boolean).join(" ");
|
|
1325
2232
|
if (src && !imgFailed) {
|
|
1326
|
-
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) }) });
|
|
1327
2234
|
}
|
|
1328
|
-
return /* @__PURE__ */
|
|
2235
|
+
return /* @__PURE__ */ jsx57("span", { className: cls, "aria-label": name, children: initials(name) });
|
|
1329
2236
|
}
|
|
1330
2237
|
|
|
1331
2238
|
// src/components/NavTabs/NavTabs.tsx
|
|
1332
|
-
import
|
|
1333
|
-
import { jsx as
|
|
2239
|
+
import styles29 from "./NavTabs.module.css";
|
|
2240
|
+
import { jsx as jsx58 } from "react/jsx-runtime";
|
|
1334
2241
|
function NavTabs({ items, className, "aria-label": ariaLabel }) {
|
|
1335
|
-
return /* @__PURE__ */
|
|
2242
|
+
return /* @__PURE__ */ jsx58(
|
|
1336
2243
|
"nav",
|
|
1337
2244
|
{
|
|
1338
|
-
className: [
|
|
2245
|
+
className: [styles29.nav, className].filter(Boolean).join(" "),
|
|
1339
2246
|
"aria-label": ariaLabel ?? "Page navigation",
|
|
1340
|
-
children: items.map((item) => /* @__PURE__ */
|
|
2247
|
+
children: items.map((item) => /* @__PURE__ */ jsx58(
|
|
1341
2248
|
"a",
|
|
1342
2249
|
{
|
|
1343
2250
|
href: item.href,
|
|
1344
|
-
className: [
|
|
2251
|
+
className: [styles29.link, item.active && styles29.active].filter(Boolean).join(" "),
|
|
1345
2252
|
"aria-current": item.active ? "page" : void 0,
|
|
1346
2253
|
children: item.label
|
|
1347
2254
|
},
|
|
@@ -1353,42 +2260,438 @@ function NavTabs({ items, className, "aria-label": ariaLabel }) {
|
|
|
1353
2260
|
|
|
1354
2261
|
// src/components/Tooltip/Tooltip.tsx
|
|
1355
2262
|
import { useId as useId3 } from "react";
|
|
1356
|
-
import
|
|
1357
|
-
import { jsx as
|
|
2263
|
+
import styles30 from "./Tooltip.module.css";
|
|
2264
|
+
import { jsx as jsx59, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
1358
2265
|
var sideClass = {
|
|
1359
|
-
top:
|
|
1360
|
-
bottom:
|
|
1361
|
-
left:
|
|
1362
|
-
right:
|
|
2266
|
+
top: styles30.top,
|
|
2267
|
+
bottom: styles30.bottom,
|
|
2268
|
+
left: styles30.left,
|
|
2269
|
+
right: styles30.right
|
|
1363
2270
|
};
|
|
1364
2271
|
function Tooltip({ content, children, side = "top" }) {
|
|
1365
2272
|
const tipId = useId3();
|
|
1366
|
-
return /* @__PURE__ */
|
|
1367
|
-
/* @__PURE__ */
|
|
1368
|
-
/* @__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 })
|
|
1369
2276
|
] });
|
|
1370
2277
|
}
|
|
2278
|
+
|
|
2279
|
+
// src/components/Checkbox/Checkbox.tsx
|
|
2280
|
+
import styles31 from "./Checkbox.module.css";
|
|
2281
|
+
import { jsx as jsx60, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
2282
|
+
function Checkbox({
|
|
2283
|
+
checked,
|
|
2284
|
+
onChange,
|
|
2285
|
+
label,
|
|
2286
|
+
strikeThrough = false,
|
|
2287
|
+
disabled,
|
|
2288
|
+
className,
|
|
2289
|
+
id,
|
|
2290
|
+
...rest
|
|
2291
|
+
}) {
|
|
2292
|
+
const inputId = id ?? `checkbox-${label.toLowerCase().replace(/\s+/g, "-")}`;
|
|
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(
|
|
2296
|
+
"input",
|
|
2297
|
+
{
|
|
2298
|
+
...rest,
|
|
2299
|
+
id: inputId,
|
|
2300
|
+
type: "checkbox",
|
|
2301
|
+
checked,
|
|
2302
|
+
disabled,
|
|
2303
|
+
className: styles31.input,
|
|
2304
|
+
onChange: (e) => onChange(e.target.checked)
|
|
2305
|
+
}
|
|
2306
|
+
),
|
|
2307
|
+
checked && /* @__PURE__ */ jsx60("svg", { className: styles31.tick, viewBox: "0 0 10 8", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx60(
|
|
2308
|
+
"path",
|
|
2309
|
+
{
|
|
2310
|
+
d: "M1 4l3 3 5-6",
|
|
2311
|
+
stroke: "currentColor",
|
|
2312
|
+
strokeWidth: "1.5",
|
|
2313
|
+
strokeLinecap: "round",
|
|
2314
|
+
strokeLinejoin: "round"
|
|
2315
|
+
}
|
|
2316
|
+
) })
|
|
2317
|
+
] }),
|
|
2318
|
+
label && /* @__PURE__ */ jsx60(
|
|
2319
|
+
"label",
|
|
2320
|
+
{
|
|
2321
|
+
htmlFor: inputId,
|
|
2322
|
+
className: [
|
|
2323
|
+
styles31.label,
|
|
2324
|
+
strikeThrough && checked ? styles31.struck : "",
|
|
2325
|
+
disabled ? styles31.disabled : ""
|
|
2326
|
+
].filter(Boolean).join(" "),
|
|
2327
|
+
children: label
|
|
2328
|
+
}
|
|
2329
|
+
)
|
|
2330
|
+
] });
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
// src/components/DragHandleRow/DragHandleRow.tsx
|
|
2334
|
+
import styles32 from "./DragHandleRow.module.css";
|
|
2335
|
+
import { jsx as jsx61, jsxs as jsxs43 } from "react/jsx-runtime";
|
|
2336
|
+
function DragHandleRow({
|
|
2337
|
+
children,
|
|
2338
|
+
handleProps,
|
|
2339
|
+
isDragging,
|
|
2340
|
+
className,
|
|
2341
|
+
...rest
|
|
2342
|
+
}) {
|
|
2343
|
+
return /* @__PURE__ */ jsxs43(
|
|
2344
|
+
"div",
|
|
2345
|
+
{
|
|
2346
|
+
...rest,
|
|
2347
|
+
className: [styles32.row, isDragging ? styles32.dragging : "", className].filter(Boolean).join(" "),
|
|
2348
|
+
children: [
|
|
2349
|
+
/* @__PURE__ */ jsx61(
|
|
2350
|
+
"button",
|
|
2351
|
+
{
|
|
2352
|
+
type: "button",
|
|
2353
|
+
"aria-label": "Drag to reorder",
|
|
2354
|
+
className: styles32.handle,
|
|
2355
|
+
tabIndex: -1,
|
|
2356
|
+
...handleProps,
|
|
2357
|
+
children: /* @__PURE__ */ jsx61(DragIcon, {})
|
|
2358
|
+
}
|
|
2359
|
+
),
|
|
2360
|
+
/* @__PURE__ */ jsx61("div", { className: styles32.content, children })
|
|
2361
|
+
]
|
|
2362
|
+
}
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2365
|
+
function DragIcon() {
|
|
2366
|
+
return /* @__PURE__ */ jsx61(
|
|
2367
|
+
"svg",
|
|
2368
|
+
{
|
|
2369
|
+
width: "14",
|
|
2370
|
+
height: "14",
|
|
2371
|
+
viewBox: "0 0 14 14",
|
|
2372
|
+
fill: "none",
|
|
2373
|
+
"aria-hidden": "true",
|
|
2374
|
+
className: styles32.icon,
|
|
2375
|
+
children: [3, 7, 11].map(
|
|
2376
|
+
(cy) => [4, 10].map((cx) => /* @__PURE__ */ jsx61("circle", { cx, cy, r: 1.2, fill: "currentColor" }, `${cx}-${cy}`))
|
|
2377
|
+
)
|
|
2378
|
+
}
|
|
2379
|
+
);
|
|
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
|
+
}
|
|
1371
2655
|
export {
|
|
1372
2656
|
Avatar,
|
|
1373
2657
|
Badge,
|
|
1374
2658
|
Button,
|
|
2659
|
+
Calendar,
|
|
1375
2660
|
Card,
|
|
2661
|
+
Checkbox,
|
|
2662
|
+
CodeTextarea,
|
|
2663
|
+
ConfirmDialog,
|
|
2664
|
+
DatePicker,
|
|
1376
2665
|
Dialog,
|
|
2666
|
+
DragHandleRow,
|
|
1377
2667
|
Drawer,
|
|
1378
2668
|
EmptyState,
|
|
1379
2669
|
FormField,
|
|
1380
2670
|
Icon,
|
|
1381
2671
|
Input,
|
|
2672
|
+
MOBILE_BREAKPOINT_PX,
|
|
2673
|
+
Menu,
|
|
1382
2674
|
NavTabs,
|
|
2675
|
+
OverlayHeader,
|
|
1383
2676
|
PageHeader,
|
|
1384
2677
|
Popover,
|
|
1385
2678
|
SegmentedControl,
|
|
1386
2679
|
Select,
|
|
2680
|
+
Sheet,
|
|
1387
2681
|
Spinner,
|
|
2682
|
+
SplitPane,
|
|
2683
|
+
StatusBadge,
|
|
1388
2684
|
SystemBanner,
|
|
1389
2685
|
Tabs,
|
|
2686
|
+
TagInput,
|
|
2687
|
+
Textarea,
|
|
1390
2688
|
ToastProvider,
|
|
1391
2689
|
Toggle,
|
|
1392
2690
|
Tooltip,
|
|
2691
|
+
useDoubleTapHandler,
|
|
2692
|
+
useIsMobile,
|
|
2693
|
+
useLongPress,
|
|
2694
|
+
useOverlaySecondRow,
|
|
2695
|
+
useSingleOrDoubleTap,
|
|
1393
2696
|
useToast
|
|
1394
2697
|
};
|