@scripso-homepad/ui 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/{chunk-WDSMJWKC.js → chunk-66WR57JJ.js} +35 -3
  2. package/dist/chunk-66WR57JJ.js.map +1 -0
  3. package/dist/index.cjs +2260 -385
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +133 -1
  6. package/dist/index.d.ts +133 -1
  7. package/dist/index.js +1960 -97
  8. package/dist/index.js.map +1 -1
  9. package/dist/web/index.cjs +30650 -1009
  10. package/dist/web/index.cjs.map +1 -1
  11. package/dist/web/index.css +78 -0
  12. package/dist/web/index.css.map +1 -0
  13. package/dist/web/index.d.cts +267 -6
  14. package/dist/web/index.d.ts +267 -6
  15. package/dist/web/index.js +30179 -601
  16. package/dist/web/index.js.map +1 -1
  17. package/package.json +5 -4
  18. package/src/components/Calendar.stories.tsx +337 -0
  19. package/src/components/Calendar.tsx +441 -0
  20. package/src/components/DatePicker.stories.tsx +219 -0
  21. package/src/components/DatePicker.tsx +366 -0
  22. package/src/components/Select.stories.tsx +134 -0
  23. package/src/components/Select.tsx +462 -0
  24. package/src/css.d.ts +1 -0
  25. package/src/icons/CalendarIcon.tsx +28 -0
  26. package/src/icons/CalendarIcon.web.tsx +34 -0
  27. package/src/icons/ChevronDownIcon.tsx +11 -4
  28. package/src/icons/ChevronDownIcon.web.tsx +6 -4
  29. package/src/icons/ChevronNavIcons.tsx +44 -0
  30. package/src/icons/ChevronNavIcons.web.tsx +57 -0
  31. package/src/icons/calendarIconPaths.ts +2 -0
  32. package/src/icons/chevronDownPath.ts +1 -0
  33. package/src/icons/chevronNavPaths.ts +2 -0
  34. package/src/index.ts +41 -0
  35. package/src/theme/calendar.ts +357 -0
  36. package/src/theme/select.ts +249 -0
  37. package/src/utils/calendarDays.ts +273 -0
  38. package/src/utils/calendarLocaleContext.tsx +95 -0
  39. package/src/utils/calendarLocalization.ts +187 -0
  40. package/src/utils/countryDropdownScrollStyles.ts +53 -13
  41. package/src/utils/formatMultiSelectDisplay.ts +56 -0
  42. package/src/web/components/Badge.stories.tsx +43 -0
  43. package/src/web/components/Badge.tsx +35 -0
  44. package/src/web/components/Pagination.stories.tsx +78 -0
  45. package/src/web/components/Pagination.tsx +153 -0
  46. package/src/web/components/StatusBadge.tsx +20 -0
  47. package/src/web/components/Table.stories.tsx +415 -0
  48. package/src/web/components/Table.tsx +3 -0
  49. package/src/web/components/TableActionButton.tsx +67 -0
  50. package/src/web/components/TableActionsMenu.tsx +208 -0
  51. package/src/web/components/TableIconText.tsx +23 -0
  52. package/src/web/components/TableRowStatusActions.tsx +33 -0
  53. package/src/web/components/TableStatusActionsCell.tsx +29 -0
  54. package/src/web/components/pagination-utils.ts +39 -0
  55. package/src/web/components/table/DataTable.tsx +123 -0
  56. package/src/web/components/table/TablePrimitives.tsx +214 -0
  57. package/src/web/components/table/TableScrollArea.tsx +51 -0
  58. package/src/web/components/table/constants.ts +59 -0
  59. package/src/web/components/table/data-table-class-names.ts +26 -0
  60. package/src/web/components/table/index.ts +44 -0
  61. package/src/web/components/table/table-row-context.tsx +19 -0
  62. package/src/web/components/table/use-horizontal-scroll-state.ts +72 -0
  63. package/src/web/components/table-scroll.css +81 -0
  64. package/src/web/hooks/useOnClickOutside.ts +20 -5
  65. package/src/web/icons/TableMenuActivateIcon.tsx +27 -0
  66. package/src/web/icons/TableMenuCheckIcon.tsx +27 -0
  67. package/src/web/icons/TableMenuCloseIcon.tsx +27 -0
  68. package/src/web/icons/TableMenuEditIcon.tsx +27 -0
  69. package/src/web/icons/TableMenuInfoIcon.tsx +27 -0
  70. package/src/web/icons/TableMenuRefreshIcon.tsx +27 -0
  71. package/src/web/icons/TableMenuSuspendIcon.tsx +34 -0
  72. package/src/web/icons/TableMenuTrashIcon.tsx +27 -0
  73. package/src/web/icons/TableMoreIcon.tsx +41 -0
  74. package/src/web/icons/TableSortIcon.tsx +37 -0
  75. package/src/web/index.ts +74 -0
  76. package/src/web/layout/AppHeader.stories.tsx +2 -0
  77. package/src/web/layout/AppHeader.tsx +18 -14
  78. package/src/web/layout/DashboardLayout.stories.tsx +1 -0
  79. package/src/web/layout/DashboardLayout.tsx +4 -4
  80. package/src/web/layout/story-helpers.tsx +10 -2
  81. package/dist/chunk-WDSMJWKC.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  var react = require('react');
4
4
  var reactNative = require('react-native');
5
- var Svg = require('react-native-svg');
5
+ var Svg5 = require('react-native-svg');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
 
8
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
9
 
10
- var Svg__default = /*#__PURE__*/_interopDefault(Svg);
10
+ var Svg5__default = /*#__PURE__*/_interopDefault(Svg5);
11
11
 
12
12
  // src/components/Button.tsx
13
13
 
@@ -18,8 +18,8 @@ function ArrowUpRightIcon({
18
18
  color = "#08275d",
19
19
  strokeWidth = 2
20
20
  }) {
21
- return /* @__PURE__ */ jsxRuntime.jsx(Svg__default.default, { width: size, height: size, viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
22
- Svg.Path,
21
+ return /* @__PURE__ */ jsxRuntime.jsx(Svg5__default.default, { width: size, height: size, viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
22
+ Svg5.Path,
23
23
  {
24
24
  d: ARROW_UP_RIGHT_PATH,
25
25
  stroke: color,
@@ -467,9 +467,9 @@ function EyeIcon({
467
467
  color = "#c7cdd1",
468
468
  strokeWidth = 2
469
469
  }) {
470
- return /* @__PURE__ */ jsxRuntime.jsxs(Svg__default.default, { width: size, height: size, viewBox: "0 0 20 20", fill: "none", children: [
470
+ return /* @__PURE__ */ jsxRuntime.jsxs(Svg5__default.default, { width: size, height: size, viewBox: "0 0 20 20", fill: "none", children: [
471
471
  /* @__PURE__ */ jsxRuntime.jsx(
472
- Svg.Path,
472
+ Svg5.Path,
473
473
  {
474
474
  d: EYE_OPEN_OUTLINE_PATH,
475
475
  stroke: color,
@@ -479,7 +479,7 @@ function EyeIcon({
479
479
  }
480
480
  ),
481
481
  /* @__PURE__ */ jsxRuntime.jsx(
482
- Svg.Path,
482
+ Svg5.Path,
483
483
  {
484
484
  d: EYE_OPEN_PUPIL_PATH,
485
485
  stroke: color,
@@ -495,8 +495,8 @@ function EyeOffIcon({
495
495
  color = "#c7cdd1",
496
496
  strokeWidth = 2
497
497
  }) {
498
- return /* @__PURE__ */ jsxRuntime.jsx(Svg__default.default, { width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
499
- Svg.Path,
498
+ return /* @__PURE__ */ jsxRuntime.jsx(Svg5__default.default, { width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
499
+ Svg5.Path,
500
500
  {
501
501
  d: EYE_OFF_PATH,
502
502
  stroke: color,
@@ -817,354 +817,643 @@ var styles3 = reactNative.StyleSheet.create({
817
817
  }
818
818
  });
819
819
 
820
- // src/data/countries.ts
821
- var defaultCountry = {
822
- code: "AM",
823
- name: "Armenia",
824
- dialCode: "+374"
825
- };
826
- var countries = [
827
- defaultCountry,
828
- { code: "AF", name: "Afghanistan", dialCode: "+93" },
829
- { code: "AL", name: "Albania", dialCode: "+355" },
830
- { code: "DZ", name: "Algeria", dialCode: "+213" },
831
- { code: "AR", name: "Argentina", dialCode: "+54" },
832
- { code: "AU", name: "Australia", dialCode: "+61" },
833
- { code: "AT", name: "Austria", dialCode: "+43" },
834
- { code: "AZ", name: "Azerbaijan", dialCode: "+994" },
835
- { code: "BH", name: "Bahrain", dialCode: "+973" },
836
- { code: "BY", name: "Belarus", dialCode: "+375" },
837
- { code: "BE", name: "Belgium", dialCode: "+32" },
838
- { code: "BR", name: "Brazil", dialCode: "+55" },
839
- { code: "BG", name: "Bulgaria", dialCode: "+359" },
840
- { code: "CA", name: "Canada", dialCode: "+1" },
841
- { code: "CL", name: "Chile", dialCode: "+56" },
842
- { code: "CN", name: "China", dialCode: "+86" },
843
- { code: "CO", name: "Colombia", dialCode: "+57" },
844
- { code: "HR", name: "Croatia", dialCode: "+385" },
845
- { code: "CY", name: "Cyprus", dialCode: "+357" },
846
- { code: "CZ", name: "Czech Republic", dialCode: "+420" },
847
- { code: "DK", name: "Denmark", dialCode: "+45" },
848
- { code: "EG", name: "Egypt", dialCode: "+20" },
849
- { code: "EE", name: "Estonia", dialCode: "+372" },
850
- { code: "FI", name: "Finland", dialCode: "+358" },
851
- { code: "FR", name: "France", dialCode: "+33" },
852
- { code: "GE", name: "Georgia", dialCode: "+995" },
853
- { code: "DE", name: "Germany", dialCode: "+49" },
854
- { code: "GR", name: "Greece", dialCode: "+30" },
855
- { code: "HK", name: "Hong Kong", dialCode: "+852" },
856
- { code: "HU", name: "Hungary", dialCode: "+36" },
857
- { code: "IN", name: "India", dialCode: "+91" },
858
- { code: "ID", name: "Indonesia", dialCode: "+62" },
859
- { code: "IR", name: "Iran", dialCode: "+98" },
860
- { code: "IQ", name: "Iraq", dialCode: "+964" },
861
- { code: "IE", name: "Ireland", dialCode: "+353" },
862
- { code: "IL", name: "Israel", dialCode: "+972" },
863
- { code: "IT", name: "Italy", dialCode: "+39" },
864
- { code: "JP", name: "Japan", dialCode: "+81" },
865
- { code: "JO", name: "Jordan", dialCode: "+962" },
866
- { code: "KZ", name: "Kazakhstan", dialCode: "+7" },
867
- { code: "KW", name: "Kuwait", dialCode: "+965" },
868
- { code: "LV", name: "Latvia", dialCode: "+371" },
869
- { code: "LB", name: "Lebanon", dialCode: "+961" },
870
- { code: "LT", name: "Lithuania", dialCode: "+370" },
871
- { code: "LU", name: "Luxembourg", dialCode: "+352" },
872
- { code: "MY", name: "Malaysia", dialCode: "+60" },
873
- { code: "MX", name: "Mexico", dialCode: "+52" },
874
- { code: "MD", name: "Moldova", dialCode: "+373" },
875
- { code: "NL", name: "Netherlands", dialCode: "+31" },
876
- { code: "NZ", name: "New Zealand", dialCode: "+64" },
877
- { code: "NG", name: "Nigeria", dialCode: "+234" },
878
- { code: "NO", name: "Norway", dialCode: "+47" },
879
- { code: "PK", name: "Pakistan", dialCode: "+92" },
880
- { code: "PS", name: "Palestine", dialCode: "+970" },
881
- { code: "PH", name: "Philippines", dialCode: "+63" },
882
- { code: "PL", name: "Poland", dialCode: "+48" },
883
- { code: "PT", name: "Portugal", dialCode: "+351" },
884
- { code: "QA", name: "Qatar", dialCode: "+974" },
885
- { code: "RO", name: "Romania", dialCode: "+40" },
886
- { code: "RU", name: "Russia", dialCode: "+7" },
887
- { code: "SA", name: "Saudi Arabia", dialCode: "+966" },
888
- { code: "RS", name: "Serbia", dialCode: "+381" },
889
- { code: "SG", name: "Singapore", dialCode: "+65" },
890
- { code: "SK", name: "Slovakia", dialCode: "+421" },
891
- { code: "SI", name: "Slovenia", dialCode: "+386" },
892
- { code: "ZA", name: "South Africa", dialCode: "+27" },
893
- { code: "KR", name: "South Korea", dialCode: "+82" },
894
- { code: "ES", name: "Spain", dialCode: "+34" },
895
- { code: "SE", name: "Sweden", dialCode: "+46" },
896
- { code: "CH", name: "Switzerland", dialCode: "+41" },
897
- { code: "SY", name: "Syria", dialCode: "+963" },
898
- { code: "TW", name: "Taiwan", dialCode: "+886" },
899
- { code: "TH", name: "Thailand", dialCode: "+66" },
900
- { code: "TR", name: "Turkey", dialCode: "+90" },
901
- { code: "UA", name: "Ukraine", dialCode: "+380" },
902
- { code: "AE", name: "United Arab Emirates", dialCode: "+971" },
903
- { code: "GB", name: "United Kingdom", dialCode: "+44" },
904
- { code: "US", name: "United States", dialCode: "+1" },
905
- { code: "UZ", name: "Uzbekistan", dialCode: "+998" },
906
- { code: "VN", name: "Vietnam", dialCode: "+84" }
907
- ];
908
- function findCountry(code) {
909
- return countries.find((country) => country.code === code) ?? defaultCountry;
910
- }
911
-
912
820
  // src/icons/chevronDownPath.ts
913
821
  var CHEVRON_DOWN_PATH = "M4 6L8 10L12 6";
822
+ var CHEVRON_DOWN_PATH_20 = "M5 7.5L10 12.5L15 7.5";
914
823
  function ChevronDownIcon({
915
824
  size = 16,
916
825
  color = "#dadde0",
917
826
  strokeWidth = 2
918
827
  }) {
919
- return /* @__PURE__ */ jsxRuntime.jsx(Svg__default.default, { width: size, height: size, viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
920
- Svg.Path,
828
+ const useLarge = size >= 20;
829
+ return /* @__PURE__ */ jsxRuntime.jsx(
830
+ Svg5__default.default,
921
831
  {
922
- d: CHEVRON_DOWN_PATH,
923
- stroke: color,
924
- strokeWidth,
925
- strokeLinecap: "round",
926
- strokeLinejoin: "round"
832
+ width: size,
833
+ height: size,
834
+ viewBox: useLarge ? "0 0 20 20" : "0 0 16 16",
835
+ fill: "none",
836
+ children: /* @__PURE__ */ jsxRuntime.jsx(
837
+ Svg5.Path,
838
+ {
839
+ d: useLarge ? CHEVRON_DOWN_PATH_20 : CHEVRON_DOWN_PATH,
840
+ stroke: color,
841
+ strokeWidth,
842
+ strokeLinecap: "round",
843
+ strokeLinejoin: "round"
844
+ }
845
+ )
927
846
  }
928
- ) });
929
- }
930
-
931
- // src/utils/countryFlag.ts
932
- function countryCodeToFlagEmoji(code) {
933
- const upper = code.trim().toUpperCase();
934
- if (upper.length !== 2) return "";
935
- return String.fromCodePoint(
936
- ...upper.split("").map((char) => 127462 - 65 + char.charCodeAt(0))
937
847
  );
938
848
  }
849
+ var SELECT_HEIGHT = 52;
850
+ var SELECT_PADDING = 16;
851
+ var SELECT_INNER_GAP = 10;
852
+ var SELECT_ICON_SIZE = 20;
853
+ var SELECT_LABEL_GAP = spacing.sm;
854
+ var SELECT_DROPDOWN_GAP = spacing.sm;
855
+ var SELECT_DROPDOWN_PADDING = spacing.sm;
856
+ var SELECT_DROPDOWN_MAX_HEIGHT = 317;
857
+ var SELECT_OPTION_HEIGHT = 43;
858
+ var SELECT_OPTION_GAP = spacing.sm;
859
+ var SELECT_CHEVRON_SIZE = 20;
860
+ var SELECT_CHEVRON_COLOR = colors.stormGray150;
861
+ var SELECT_CHEVRON_ROTATION_MS = 200;
862
+ var selectLabelTypography = {
863
+ fontFamily: fonts.sans,
864
+ fontSize: fontSize.sm,
865
+ fontWeight: fontWeight.medium,
866
+ lineHeight: fontSize.sm,
867
+ letterSpacing: 0,
868
+ color: colors.slateBlue
869
+ };
870
+ var selectValueTypography = {
871
+ fontFamily: fonts.sans,
872
+ fontSize: fontSize.md,
873
+ fontWeight: fontWeight.regular,
874
+ lineHeight: fontSize.md,
875
+ letterSpacing: 0,
876
+ textAlign: "left"
877
+ };
878
+ var INPUT_OUTLINE_WIDTH2 = 2;
879
+ function createOutlineStyle2(ringColor) {
880
+ return {
881
+ borderRadius: radii.lg + INPUT_OUTLINE_WIDTH2,
882
+ borderWidth: INPUT_OUTLINE_WIDTH2,
883
+ borderColor: ringColor,
884
+ padding: 0,
885
+ backgroundColor: colors.transparent,
886
+ width: "100%",
887
+ alignSelf: "stretch",
888
+ ...reactNative.Platform.OS !== "web" ? { overflow: "hidden" } : null
889
+ };
890
+ }
891
+ var defaultOutline2 = createOutlineStyle2(colors.transparent);
892
+ function getSelectFieldStyles(state) {
893
+ const containerBase = {
894
+ borderRadius: radii.lg,
895
+ ...reactNative.Platform.OS !== "web" ? { overflow: "hidden" } : null
896
+ };
897
+ const valueBase = {
898
+ ...selectValueTypography,
899
+ color: colors.slateBlue
900
+ };
901
+ switch (state) {
902
+ case "disabled":
903
+ return {
904
+ outline: defaultOutline2,
905
+ container: {
906
+ ...containerBase,
907
+ borderWidth: 1,
908
+ borderColor: colors.stormGray50,
909
+ backgroundColor: colors.stormGray50
910
+ },
911
+ value: { ...valueBase, color: colors.stormGray300 },
912
+ placeholder: colors.stormGray300,
913
+ icon: colors.stormGray150
914
+ };
915
+ case "error":
916
+ return {
917
+ outline: createOutlineStyle2(colors.inputOutlineError),
918
+ container: {
919
+ ...containerBase,
920
+ borderWidth: 1,
921
+ borderColor: colors.inputError,
922
+ backgroundColor: colors.white
923
+ },
924
+ value: { ...valueBase, color: colors.inputError },
925
+ placeholder: colors.stormGray200,
926
+ icon: colors.inputError
927
+ };
928
+ case "focused":
929
+ return {
930
+ outline: createOutlineStyle2(colors.inputOutlineFocus),
931
+ container: {
932
+ ...containerBase,
933
+ borderWidth: 1,
934
+ borderColor: colors.navy,
935
+ backgroundColor: colors.white
936
+ },
937
+ value: valueBase,
938
+ placeholder: colors.stormGray200,
939
+ icon: colors.navy
940
+ };
941
+ default:
942
+ return {
943
+ outline: defaultOutline2,
944
+ container: {
945
+ ...containerBase,
946
+ borderWidth: 1,
947
+ borderColor: colors.stormGray50,
948
+ backgroundColor: colors.white
949
+ },
950
+ value: valueBase,
951
+ placeholder: colors.stormGray200,
952
+ icon: colors.stormGray150
953
+ };
954
+ }
955
+ }
956
+ var selectFieldMetrics = reactNative.StyleSheet.create({
957
+ container: {
958
+ flexDirection: "row",
959
+ alignItems: "center",
960
+ height: SELECT_HEIGHT,
961
+ minHeight: SELECT_HEIGHT,
962
+ maxHeight: SELECT_HEIGHT,
963
+ borderRadius: radii.lg,
964
+ padding: SELECT_PADDING,
965
+ gap: SELECT_INNER_GAP,
966
+ ...reactNative.Platform.OS === "web" ? {
967
+ boxSizing: "border-box"
968
+ } : null
969
+ },
970
+ value: {
971
+ flex: 1,
972
+ minWidth: 0,
973
+ ...selectValueTypography,
974
+ paddingVertical: 0,
975
+ paddingHorizontal: 0,
976
+ margin: 0,
977
+ ...reactNative.Platform.OS === "android" ? { includeFontPadding: false } : null,
978
+ ...reactNative.Platform.OS === "web" ? {
979
+ outlineStyle: "none"
980
+ } : null
981
+ }
982
+ });
983
+ var selectDropdownMetrics = reactNative.StyleSheet.create({
984
+ menu: {
985
+ borderRadius: radii.lg,
986
+ borderWidth: 1,
987
+ borderColor: colors.stormGray50,
988
+ backgroundColor: colors.white,
989
+ padding: SELECT_DROPDOWN_PADDING,
990
+ overflow: "hidden"
991
+ },
992
+ option: {
993
+ width: "100%",
994
+ height: SELECT_OPTION_HEIGHT,
995
+ minHeight: SELECT_OPTION_HEIGHT,
996
+ maxHeight: SELECT_OPTION_HEIGHT,
997
+ paddingTop: 12,
998
+ paddingRight: spacing.sm,
999
+ paddingBottom: 12,
1000
+ paddingLeft: spacing.sm,
1001
+ justifyContent: "center",
1002
+ alignItems: "flex-start"
1003
+ },
1004
+ optionLabel: {
1005
+ fontFamily: fonts.sans,
1006
+ fontSize: fontSize.md,
1007
+ fontWeight: fontWeight.regular,
1008
+ lineHeight: fontSize.md,
1009
+ letterSpacing: 0,
1010
+ textAlign: "left",
1011
+ color: colors.slateBlue,
1012
+ width: "100%"
1013
+ },
1014
+ optionLabelSelected: {
1015
+ fontWeight: fontWeight.medium,
1016
+ color: colors.slateBlue
1017
+ },
1018
+ optionSelected: {
1019
+ opacity: 1,
1020
+ borderRadius: radii.sm,
1021
+ paddingTop: 12,
1022
+ paddingRight: spacing.sm,
1023
+ paddingBottom: 12,
1024
+ paddingLeft: spacing.sm,
1025
+ backgroundColor: colors.countrySelectorSelectedBg
1026
+ },
1027
+ optionHighlighted: {
1028
+ backgroundColor: colors.stormGray0
1029
+ }
1030
+ });
1031
+ function resolveSelectDropdownHeight(optionCount) {
1032
+ const contentHeight = optionCount * SELECT_OPTION_HEIGHT + Math.max(0, optionCount - 1) * SELECT_OPTION_GAP + SELECT_DROPDOWN_PADDING * 2;
1033
+ return Math.min(contentHeight, SELECT_DROPDOWN_MAX_HEIGHT);
1034
+ }
1035
+ function resolveSelectDropdownTop(anchor, dropdownHeight) {
1036
+ const windowHeight = reactNative.Dimensions.get("window").height;
1037
+ const spaceBelow = windowHeight - (anchor.y + anchor.height + SELECT_DROPDOWN_GAP);
1038
+ const spaceAbove = anchor.y - SELECT_DROPDOWN_GAP;
1039
+ const topBelow = anchor.y + anchor.height + SELECT_DROPDOWN_GAP;
1040
+ const topAbove = anchor.y - SELECT_DROPDOWN_GAP - dropdownHeight;
1041
+ if (spaceBelow >= dropdownHeight) {
1042
+ return topBelow;
1043
+ }
1044
+ if (spaceAbove >= dropdownHeight) {
1045
+ return topAbove;
1046
+ }
1047
+ if (spaceAbove > spaceBelow) {
1048
+ return Math.max(0, topAbove);
1049
+ }
1050
+ return Math.max(0, Math.min(topBelow, windowHeight - dropdownHeight));
1051
+ }
939
1052
  var COUNTRY_DROPDOWN_SCROLL_CLASS = "hp-country-dropdown-scroll";
940
- var STYLE_ID = "hp-country-dropdown-scroll-styles";
941
- var countryDropdownScrollCss = `
942
- .${COUNTRY_DROPDOWN_SCROLL_CLASS} {
1053
+ var SELECT_DROPDOWN_SCROLL_CLASS = "hp-select-dropdown-scroll";
1054
+ var STYLE_ID = "hp-dropdown-scroll-styles";
1055
+ function createDropdownScrollCss(className) {
1056
+ return `
1057
+ .${className} {
943
1058
  scrollbar-width: thin;
944
1059
  scrollbar-color: ${colors.stormGray300} transparent;
945
1060
  overflow-y: auto;
946
1061
  }
947
1062
 
948
- .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar {
1063
+ .${className}::-webkit-scrollbar {
949
1064
  width: 4px;
1065
+ -webkit-appearance: none;
1066
+ appearance: none;
950
1067
  }
951
1068
 
952
- .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-corner {
1069
+ .${className}::-webkit-scrollbar-corner {
953
1070
  background: transparent;
954
1071
  }
955
1072
 
956
- .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-button,
957
- .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-button:single-button,
958
- .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-button:vertical:start:decrement,
959
- .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-button:vertical:end:increment {
1073
+ .${className}::-webkit-scrollbar-button,
1074
+ .${className}::-webkit-scrollbar-button:single-button,
1075
+ .${className}::-webkit-scrollbar-button:double-button,
1076
+ .${className}::-webkit-scrollbar-button:horizontal:decrement,
1077
+ .${className}::-webkit-scrollbar-button:horizontal:increment,
1078
+ .${className}::-webkit-scrollbar-button:horizontal:start:decrement,
1079
+ .${className}::-webkit-scrollbar-button:horizontal:end:increment,
1080
+ .${className}::-webkit-scrollbar-button:vertical:decrement,
1081
+ .${className}::-webkit-scrollbar-button:vertical:increment,
1082
+ .${className}::-webkit-scrollbar-button:vertical:start:decrement,
1083
+ .${className}::-webkit-scrollbar-button:vertical:end:increment,
1084
+ .${className}::-webkit-scrollbar-button:start:decrement,
1085
+ .${className}::-webkit-scrollbar-button:end:increment,
1086
+ .${className}::-webkit-scrollbar-button:decrement,
1087
+ .${className}::-webkit-scrollbar-button:increment {
960
1088
  display: none;
961
1089
  width: 0;
962
1090
  height: 0;
1091
+ max-width: 0;
1092
+ max-height: 0;
1093
+ min-width: 0;
1094
+ min-height: 0;
1095
+ padding: 0;
1096
+ margin: 0;
1097
+ border: 0;
1098
+ background: transparent;
963
1099
  -webkit-appearance: none;
964
1100
  appearance: none;
965
1101
  }
966
1102
 
967
- .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-track {
1103
+ .${className}::-webkit-scrollbar-track {
968
1104
  background: transparent;
969
1105
  }
970
1106
 
971
- .${COUNTRY_DROPDOWN_SCROLL_CLASS}::-webkit-scrollbar-thumb {
1107
+ .${className}::-webkit-scrollbar-thumb {
972
1108
  background-color: ${colors.stormGray300};
973
1109
  border-radius: 9999px;
974
1110
  }
975
1111
  `;
976
- function ensureCountryDropdownScrollStyles() {
1112
+ }
1113
+ var dropdownScrollCss = [
1114
+ createDropdownScrollCss(COUNTRY_DROPDOWN_SCROLL_CLASS),
1115
+ createDropdownScrollCss(SELECT_DROPDOWN_SCROLL_CLASS)
1116
+ ].join("\n");
1117
+ function ensureDropdownScrollStyles() {
977
1118
  if (reactNative.Platform.OS !== "web" || typeof document === "undefined") return;
978
1119
  if (document.getElementById(STYLE_ID)) return;
979
1120
  const style = document.createElement("style");
980
1121
  style.id = STYLE_ID;
981
- style.textContent = countryDropdownScrollCss;
1122
+ style.textContent = dropdownScrollCss;
982
1123
  document.head.appendChild(style);
983
1124
  }
984
- var DROPDOWN_GAP = 10;
985
- var DROPDOWN_HEIGHT = 186;
986
- var DROPDOWN_PADDING = 8;
987
- var OPTION_GAP = 10;
988
- var OPTION_HEIGHT = 35;
989
- var TRIGGER_HEIGHT = 52;
990
- var CHEVRON_SIZE = 16;
991
- function resolveDropdownTop(anchor) {
992
- const windowHeight = reactNative.Dimensions.get("window").height;
993
- const topBelow = anchor.y + anchor.height + DROPDOWN_GAP;
994
- const topAbove = anchor.y - DROPDOWN_GAP - DROPDOWN_HEIGHT;
995
- const fitsBelow = topBelow + DROPDOWN_HEIGHT <= windowHeight;
996
- if (fitsBelow) return topBelow;
997
- if (topAbove >= 0) return topAbove;
998
- return Math.max(0, Math.min(topBelow, windowHeight - DROPDOWN_HEIGHT));
1125
+ function ensureCountryDropdownScrollStyles() {
1126
+ ensureDropdownScrollStyles();
999
1127
  }
1000
- function CountryCodeSelector({
1001
- value = defaultCountry.code,
1002
- onValueChange,
1003
- options = countries,
1004
- disabled = false,
1005
- style,
1006
- className
1007
- }) {
1128
+ function ensureSelectDropdownScrollStyles() {
1129
+ ensureDropdownScrollStyles();
1130
+ }
1131
+
1132
+ // src/utils/formatMultiSelectDisplay.ts
1133
+ var MULTI_SELECT_SUFFIX = " ... + ";
1134
+ var MULTI_SELECT_SUFFIX_UNIT = " items";
1135
+ function estimateTextWidth(text, size = fontSize.md) {
1136
+ return text.length * size * 0.58;
1137
+ }
1138
+ function formatMultiSelectDisplay(labels, maxWidth, textSize = fontSize.md) {
1139
+ if (labels.length === 0) {
1140
+ return "";
1141
+ }
1142
+ if (labels.length === 1) {
1143
+ return labels[0];
1144
+ }
1145
+ const fits = (text) => estimateTextWidth(text, textSize) <= maxWidth;
1146
+ const fullText = labels.join(", ");
1147
+ if (fits(fullText)) {
1148
+ return fullText;
1149
+ }
1150
+ for (let visibleCount = labels.length - 1; visibleCount >= 1; visibleCount -= 1) {
1151
+ const hiddenCount = labels.length - visibleCount;
1152
+ const truncatedText = `${labels.slice(0, visibleCount).join(", ")}${MULTI_SELECT_SUFFIX}${hiddenCount}${MULTI_SELECT_SUFFIX_UNIT}`;
1153
+ if (fits(truncatedText)) {
1154
+ return truncatedText;
1155
+ }
1156
+ }
1157
+ return `... + ${labels.length}${MULTI_SELECT_SUFFIX_UNIT}`;
1158
+ }
1159
+ function resolveSelectedLabels(options, selectedValues) {
1160
+ if (selectedValues.length === 0) {
1161
+ return [];
1162
+ }
1163
+ const labelByValue = new Map(options.map((option) => [option.value, option.label]));
1164
+ return selectedValues.map((value) => labelByValue.get(value)).filter((label) => Boolean(label));
1165
+ }
1166
+ function renderSelectIcon(icon, color) {
1167
+ if (!icon) return null;
1168
+ if (react.isValidElement(icon)) {
1169
+ return react.cloneElement(icon, {
1170
+ size: icon.props.size ?? SELECT_ICON_SIZE,
1171
+ color: icon.props.color ?? color
1172
+ });
1173
+ }
1174
+ return icon;
1175
+ }
1176
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
1177
+ function normalizeSelectedValues(multiple, value) {
1178
+ if (multiple) {
1179
+ if (Array.isArray(value)) {
1180
+ return value;
1181
+ }
1182
+ return value ? [value] : [];
1183
+ }
1184
+ return typeof value === "string" && value.length > 0 ? [value] : [];
1185
+ }
1186
+ function Select({
1187
+ label,
1188
+ placeholder = "placeholder",
1189
+ value,
1190
+ onValueChange,
1191
+ options,
1192
+ leftIcon,
1193
+ multiple = false,
1194
+ disabled = false,
1195
+ error,
1196
+ invalid,
1197
+ hint,
1198
+ containerStyle,
1199
+ className,
1200
+ fieldClassName,
1201
+ menuClassName,
1202
+ labelClassName,
1203
+ errorClassName,
1204
+ hintClassName
1205
+ }) {
1008
1206
  const wrapperRef = react.useRef(null);
1009
1207
  const triggerRef = react.useRef(null);
1208
+ const menuRef = react.useRef(null);
1010
1209
  const scrollRef = react.useRef(null);
1210
+ const helperRef = react.useRef(null);
1211
+ const chevronRotation = react.useRef(new reactNative.Animated.Value(0)).current;
1011
1212
  const [open, setOpen] = react.useState(false);
1012
1213
  const [anchor, setAnchor] = react.useState(null);
1214
+ const [highlightedIndex, setHighlightedIndex] = react.useState(-1);
1215
+ const [valueContainerWidth, setValueContainerWidth] = react.useState(0);
1013
1216
  useApplyWebClassName(wrapperRef, className);
1014
- useApplyWebClassName(
1015
- scrollRef,
1016
- COUNTRY_DROPDOWN_SCROLL_CLASS,
1017
- open && Boolean(anchor)
1018
- );
1019
- react.useLayoutEffect(() => {
1020
- ensureCountryDropdownScrollStyles();
1217
+ useApplyWebClassName(triggerRef, fieldClassName);
1218
+ useApplyWebClassName(menuRef, menuClassName);
1219
+ useApplyWebClassName(scrollRef, SELECT_DROPDOWN_SCROLL_CLASS, open && Boolean(anchor));
1220
+ useApplyWebClassName(helperRef, error ? errorClassName : hintClassName);
1221
+ useIsomorphicLayoutEffect(() => {
1222
+ ensureSelectDropdownScrollStyles();
1021
1223
  }, []);
1022
- const selected = findCountry(value);
1023
- const textColor = disabled ? colors.stormGray200 : colors.slateBlue;
1024
- function closeDropdown() {
1224
+ react.useEffect(() => {
1225
+ reactNative.Animated.timing(chevronRotation, {
1226
+ toValue: open ? 1 : 0,
1227
+ duration: SELECT_CHEVRON_ROTATION_MS,
1228
+ easing: reactNative.Easing.out(reactNative.Easing.ease),
1229
+ useNativeDriver: true
1230
+ }).start();
1231
+ }, [chevronRotation, open]);
1232
+ const chevronRotate = chevronRotation.interpolate({
1233
+ inputRange: [0, 1],
1234
+ outputRange: ["0deg", "180deg"]
1235
+ });
1236
+ const hasError = Boolean(error) || Boolean(invalid);
1237
+ const visualState = resolveInputVisualState({
1238
+ focused: open,
1239
+ error: hasError,
1240
+ disabled
1241
+ });
1242
+ const fieldStyles = getSelectFieldStyles(visualState);
1243
+ const selectedValues = react.useMemo(
1244
+ () => normalizeSelectedValues(multiple, value),
1245
+ [multiple, value]
1246
+ );
1247
+ const selectedValueSet = react.useMemo(() => new Set(selectedValues), [selectedValues]);
1248
+ const selectedLabels = react.useMemo(
1249
+ () => resolveSelectedLabels(options, selectedValues),
1250
+ [options, selectedValues]
1251
+ );
1252
+ const selectableOptions = react.useMemo(
1253
+ () => options.filter((option) => !option.disabled),
1254
+ [options]
1255
+ );
1256
+ const selectableIndexByValue = react.useMemo(
1257
+ () => new Map(
1258
+ selectableOptions.map((option, index) => [option.value, index])
1259
+ ),
1260
+ [selectableOptions]
1261
+ );
1262
+ const displayLabel = react.useMemo(() => {
1263
+ if (selectedLabels.length === 0) {
1264
+ return placeholder;
1265
+ }
1266
+ if (!multiple) {
1267
+ return selectedLabels[0];
1268
+ }
1269
+ if (valueContainerWidth <= 0) {
1270
+ return selectedLabels.join(", ");
1271
+ }
1272
+ return formatMultiSelectDisplay(selectedLabels, valueContainerWidth);
1273
+ }, [multiple, placeholder, selectedLabels, valueContainerWidth]);
1274
+ const isPlaceholder = selectedLabels.length === 0;
1275
+ const dropdownHeight = resolveSelectDropdownHeight(options.length);
1276
+ const helperMessage = error ?? hint;
1277
+ function closeMenu() {
1025
1278
  setOpen(false);
1026
1279
  setAnchor(null);
1280
+ setHighlightedIndex(-1);
1027
1281
  }
1028
- function handleOpen() {
1029
- if (disabled) return;
1282
+ function openMenu() {
1283
+ if (disabled || selectableOptions.length === 0) {
1284
+ return;
1285
+ }
1030
1286
  triggerRef.current?.measureInWindow((x, y, width, height) => {
1287
+ const selectedIndex = selectableOptions.findIndex(
1288
+ (option) => selectedValueSet.has(option.value)
1289
+ );
1290
+ setHighlightedIndex(selectedIndex >= 0 ? selectedIndex : 0);
1031
1291
  setAnchor({ x, y, width, height });
1032
1292
  setOpen(true);
1033
1293
  });
1034
1294
  }
1035
- function handleSelect(code) {
1036
- onValueChange?.(code);
1037
- closeDropdown();
1295
+ function handleSelect(nextValue) {
1296
+ if (multiple) {
1297
+ const nextValues = selectedValues.includes(nextValue) ? selectedValues.filter((item) => item !== nextValue) : [...selectedValues, nextValue];
1298
+ onValueChange?.(nextValues);
1299
+ return;
1300
+ }
1301
+ onValueChange?.(nextValue);
1302
+ closeMenu();
1038
1303
  }
1039
- const dropdownTop = anchor ? resolveDropdownTop(anchor) : 0;
1040
- const optionList = options.map((item, index) => {
1041
- const isSelected = item.code === selected.code;
1304
+ const optionList = options.map((option, index) => {
1305
+ const selectableIndex = selectableIndexByValue.get(option.value) ?? -1;
1306
+ const isSelected = selectedValueSet.has(option.value);
1307
+ const isHighlighted = selectableIndex === highlightedIndex;
1042
1308
  const isLast = index === options.length - 1;
1043
- return /* @__PURE__ */ jsxRuntime.jsxs(
1309
+ return /* @__PURE__ */ jsxRuntime.jsx(
1044
1310
  reactNative.Pressable,
1045
1311
  {
1046
1312
  accessibilityRole: "button",
1047
- onPress: () => handleSelect(item.code),
1313
+ accessibilityState: { disabled: option.disabled, selected: isSelected },
1314
+ disabled: option.disabled,
1315
+ onPress: () => {
1316
+ if (!option.disabled) {
1317
+ handleSelect(option.value);
1318
+ }
1319
+ },
1320
+ onHoverIn: () => {
1321
+ if (!option.disabled && selectableIndex >= 0) {
1322
+ setHighlightedIndex(selectableIndex);
1323
+ }
1324
+ },
1048
1325
  style: [
1049
- styles4.option,
1050
- isSelected && styles4.optionSelected,
1051
- !isLast && styles4.optionSpacing
1326
+ selectDropdownMetrics.option,
1327
+ isSelected && selectDropdownMetrics.optionSelected,
1328
+ isHighlighted && !isSelected && !option.disabled ? selectDropdownMetrics.optionHighlighted : null,
1329
+ !isLast && styles4.optionSpacing,
1330
+ option.disabled && styles4.optionDisabled
1052
1331
  ],
1053
- children: [
1054
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles4.flag, children: countryCodeToFlagEmoji(item.code) }),
1055
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles4.optionDialCode, children: item.dialCode })
1056
- ]
1332
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1333
+ reactNative.Text,
1334
+ {
1335
+ style: [
1336
+ selectDropdownMetrics.optionLabel,
1337
+ isSelected && selectDropdownMetrics.optionLabelSelected,
1338
+ option.disabled && styles4.optionLabelDisabled
1339
+ ],
1340
+ numberOfLines: 1,
1341
+ children: option.label
1342
+ }
1343
+ )
1057
1344
  },
1058
- item.code
1345
+ option.value
1059
1346
  );
1060
1347
  });
1061
- const dropdownList = /* @__PURE__ */ jsxRuntime.jsx(
1062
- reactNative.ScrollView,
1063
- {
1064
- ref: scrollRef,
1065
- style: styles4.dropdownScroll,
1066
- contentContainerStyle: styles4.dropdownContent,
1067
- keyboardShouldPersistTaps: "handled",
1068
- showsVerticalScrollIndicator: true,
1069
- bounces: false,
1070
- nestedScrollEnabled: true,
1071
- children: optionList
1072
- }
1073
- );
1074
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: wrapperRef, style: [styles4.root, style], children: [
1075
- /* @__PURE__ */ jsxRuntime.jsxs(
1348
+ const dropdownTop = anchor ? resolveSelectDropdownTop(anchor, dropdownHeight) : 0;
1349
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: wrapperRef, style: [styles4.wrapper, containerStyle], children: [
1350
+ label ? /* @__PURE__ */ jsxRuntime.jsx(Label, { disabled, style: styles4.label, className: labelClassName, children: label }) : null,
1351
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: fieldStyles.outline, children: /* @__PURE__ */ jsxRuntime.jsxs(
1076
1352
  reactNative.Pressable,
1077
1353
  {
1078
1354
  ref: triggerRef,
1079
1355
  accessibilityRole: "button",
1356
+ accessibilityState: { disabled, expanded: open },
1080
1357
  disabled,
1081
- onPress: handleOpen,
1082
- style: [
1083
- styles4.trigger,
1084
- disabled ? styles4.triggerDisabled : styles4.triggerEnabled,
1085
- reactNative.Platform.OS === "web" ? styles4.triggerWeb : null
1086
- ],
1358
+ onPress: open ? closeMenu : openMenu,
1359
+ style: [selectFieldMetrics.container, fieldStyles.container],
1087
1360
  children: [
1088
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles4.flag, children: countryCodeToFlagEmoji(selected.code) }),
1089
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.dialCode, { color: textColor }], children: selected.dialCode }),
1090
- /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { size: CHEVRON_SIZE, color: colors.stormGray100 })
1361
+ leftIcon ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles4.iconSlot, children: renderSelectIcon(leftIcon, fieldStyles.icon) }) : null,
1362
+ /* @__PURE__ */ jsxRuntime.jsx(
1363
+ reactNative.View,
1364
+ {
1365
+ style: styles4.valueContainer,
1366
+ onLayout: (event) => {
1367
+ setValueContainerWidth(event.nativeEvent.layout.width);
1368
+ },
1369
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1370
+ reactNative.Text,
1371
+ {
1372
+ style: [
1373
+ selectFieldMetrics.value,
1374
+ fieldStyles.value,
1375
+ isPlaceholder && { color: fieldStyles.placeholder }
1376
+ ],
1377
+ numberOfLines: 1,
1378
+ children: displayLabel
1379
+ }
1380
+ )
1381
+ }
1382
+ ),
1383
+ /* @__PURE__ */ jsxRuntime.jsx(
1384
+ reactNative.Animated.View,
1385
+ {
1386
+ style: [styles4.iconSlot, { transform: [{ rotate: chevronRotate }] }],
1387
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1388
+ ChevronDownIcon,
1389
+ {
1390
+ size: SELECT_CHEVRON_SIZE,
1391
+ color: disabled ? colors.stormGray200 : SELECT_CHEVRON_COLOR
1392
+ }
1393
+ )
1394
+ }
1395
+ )
1091
1396
  ]
1092
1397
  }
1093
- ),
1094
- /* @__PURE__ */ jsxRuntime.jsx(
1095
- reactNative.Modal,
1398
+ ) }),
1399
+ helperMessage ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { ref: helperRef, style: error ? styles4.error : styles4.hint, children: helperMessage }) : null,
1400
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible: open, transparent: true, animationType: "fade", onRequestClose: closeMenu, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles4.overlay, onPress: closeMenu, children: anchor ? /* @__PURE__ */ jsxRuntime.jsx(
1401
+ reactNative.View,
1096
1402
  {
1097
- visible: open,
1098
- transparent: true,
1099
- animationType: "fade",
1100
- onRequestClose: closeDropdown,
1101
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles4.overlay, onPress: closeDropdown, children: anchor ? /* @__PURE__ */ jsxRuntime.jsx(
1102
- reactNative.View,
1403
+ ref: menuRef,
1404
+ style: [
1405
+ selectDropdownMetrics.menu,
1406
+ styles4.dropdown,
1103
1407
  {
1104
- style: [
1105
- styles4.dropdown,
1106
- {
1107
- top: dropdownTop,
1108
- left: anchor.x,
1109
- width: anchor.width
1110
- }
1111
- ],
1112
- children: dropdownList
1408
+ top: dropdownTop,
1409
+ left: anchor.x,
1410
+ width: anchor.width,
1411
+ height: dropdownHeight
1113
1412
  }
1114
- ) : null })
1413
+ ],
1414
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1415
+ reactNative.ScrollView,
1416
+ {
1417
+ ref: scrollRef,
1418
+ style: styles4.dropdownScroll,
1419
+ contentContainerStyle: styles4.dropdownContent,
1420
+ keyboardShouldPersistTaps: "handled",
1421
+ showsVerticalScrollIndicator: true,
1422
+ bounces: false,
1423
+ nestedScrollEnabled: true,
1424
+ children: optionList
1425
+ }
1426
+ )
1115
1427
  }
1116
- )
1428
+ ) : null }) })
1117
1429
  ] });
1118
1430
  }
1119
1431
  var styles4 = reactNative.StyleSheet.create({
1120
- root: {
1121
- alignSelf: "flex-start",
1122
- flexShrink: 0
1123
- },
1124
- trigger: {
1125
- flexDirection: "row",
1126
- alignItems: "center",
1127
- alignSelf: "flex-start",
1128
- flexGrow: 0,
1129
- flexShrink: 0,
1130
- height: TRIGGER_HEIGHT,
1131
- borderRadius: radii.lg,
1132
- borderWidth: 1,
1133
- padding: spacing.lg,
1134
- gap: spacing.sm
1135
- },
1136
- triggerEnabled: {
1137
- borderColor: colors.stormGray50,
1138
- backgroundColor: colors.white
1139
- },
1140
- triggerDisabled: {
1141
- borderColor: colors.stormGray50,
1142
- backgroundColor: colors.stormGray50
1432
+ wrapper: {
1433
+ width: "100%",
1434
+ gap: SELECT_LABEL_GAP
1143
1435
  },
1144
- triggerWeb: {
1145
- width: "max-content"
1436
+ label: {
1437
+ ...selectLabelTypography
1146
1438
  },
1147
- flag: {
1148
- fontSize: 18,
1149
- lineHeight: 22
1439
+ iconSlot: {
1440
+ width: SELECT_ICON_SIZE,
1441
+ height: SELECT_ICON_SIZE,
1442
+ alignItems: "center",
1443
+ justifyContent: "center",
1444
+ flexShrink: 0
1150
1445
  },
1151
- dialCode: {
1152
- fontSize: fontSize.md,
1153
- fontWeight: fontWeight.medium
1446
+ valueContainer: {
1447
+ flex: 1,
1448
+ minWidth: 0,
1449
+ justifyContent: "center"
1154
1450
  },
1155
1451
  overlay: {
1156
1452
  flex: 1,
1157
- backgroundColor: "transparent"
1453
+ backgroundColor: colors.transparent
1158
1454
  },
1159
1455
  dropdown: {
1160
- position: "absolute",
1161
- height: DROPDOWN_HEIGHT,
1162
- borderRadius: radii.lg,
1163
- borderWidth: 1,
1164
- borderColor: colors.stormGray50,
1165
- backgroundColor: colors.white,
1166
- padding: DROPDOWN_PADDING,
1167
- overflow: "hidden"
1456
+ position: "absolute"
1168
1457
  },
1169
1458
  dropdownScroll: {
1170
1459
  flex: 1
@@ -1172,149 +1461,1723 @@ var styles4 = reactNative.StyleSheet.create({
1172
1461
  dropdownContent: {
1173
1462
  flexGrow: 1
1174
1463
  },
1175
- option: {
1176
- flexDirection: "row",
1177
- alignItems: "center",
1178
- height: OPTION_HEIGHT,
1179
- borderRadius: radii.sm,
1180
- padding: spacing.sm,
1181
- gap: spacing.sm
1182
- },
1183
1464
  optionSpacing: {
1184
- marginBottom: OPTION_GAP
1465
+ marginBottom: SELECT_OPTION_GAP
1185
1466
  },
1186
- optionSelected: {
1187
- backgroundColor: colors.countrySelectorSelectedBg
1467
+ optionDisabled: {
1468
+ opacity: 0.5
1188
1469
  },
1189
- optionDialCode: {
1190
- fontFamily: fonts.sans,
1191
- fontSize: fontSize.md,
1192
- fontWeight: fontWeight.regular,
1193
- lineHeight: fontSize.md,
1194
- letterSpacing: 0,
1195
- textAlign: "center",
1196
- color: colors.slateBlue
1470
+ optionLabelDisabled: {
1471
+ color: colors.stormGray300
1472
+ },
1473
+ error: {
1474
+ fontSize: 12,
1475
+ lineHeight: 12,
1476
+ color: colors.inputError
1477
+ },
1478
+ hint: {
1479
+ fontSize: 12,
1480
+ lineHeight: 12,
1481
+ color: colors.stormGray300
1197
1482
  }
1198
1483
  });
1199
- function PhoneInput({
1200
- label,
1201
- countryCode,
1202
- onCountryCodeChange,
1203
- error,
1204
- hint,
1205
- containerStyle,
1206
- style,
1207
- className,
1208
- labelClassName,
1209
- inputClassName,
1210
- errorClassName,
1211
- hintClassName,
1212
- editable = true,
1213
- onFocus,
1214
- onBlur,
1215
- ...props
1216
- }) {
1217
- const wrapperRef = react.useRef(null);
1218
- const inputRef = react.useRef(null);
1219
- const helperRef = react.useRef(null);
1220
- const [focused, setFocused] = react.useState(false);
1221
- useApplyWebClassName(wrapperRef, className);
1222
- useApplyWebClassName(inputRef, inputClassName);
1223
- useApplyWebClassName(helperRef, error ? errorClassName : hintClassName);
1224
- const isDisabled = editable === false;
1225
- const phoneVisualState = resolveInputVisualState({
1226
- focused,
1227
- error: Boolean(error),
1228
- disabled: isDisabled
1229
- });
1230
- const phoneFieldStyles = getInputFieldStyles(phoneVisualState);
1231
- function handleFocus(event) {
1232
- setFocused(true);
1233
- onFocus?.(event);
1234
- }
1235
- function handleBlur(event) {
1236
- setFocused(false);
1237
- onBlur?.(event);
1238
- }
1239
- const helperMessage = error ?? hint;
1240
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: wrapperRef, style: [styles5.wrapper, containerStyle], children: [
1241
- label ? /* @__PURE__ */ jsxRuntime.jsx(Label, { disabled: isDisabled, className: labelClassName, children: label }) : null,
1242
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.row, children: [
1243
- /* @__PURE__ */ jsxRuntime.jsx(
1244
- CountryCodeSelector,
1245
- {
1246
- value: countryCode,
1484
+
1485
+ // src/data/countries.ts
1486
+ var defaultCountry = {
1487
+ code: "AM",
1488
+ name: "Armenia",
1489
+ dialCode: "+374"
1490
+ };
1491
+ var countries = [
1492
+ defaultCountry,
1493
+ { code: "AF", name: "Afghanistan", dialCode: "+93" },
1494
+ { code: "AL", name: "Albania", dialCode: "+355" },
1495
+ { code: "DZ", name: "Algeria", dialCode: "+213" },
1496
+ { code: "AR", name: "Argentina", dialCode: "+54" },
1497
+ { code: "AU", name: "Australia", dialCode: "+61" },
1498
+ { code: "AT", name: "Austria", dialCode: "+43" },
1499
+ { code: "AZ", name: "Azerbaijan", dialCode: "+994" },
1500
+ { code: "BH", name: "Bahrain", dialCode: "+973" },
1501
+ { code: "BY", name: "Belarus", dialCode: "+375" },
1502
+ { code: "BE", name: "Belgium", dialCode: "+32" },
1503
+ { code: "BR", name: "Brazil", dialCode: "+55" },
1504
+ { code: "BG", name: "Bulgaria", dialCode: "+359" },
1505
+ { code: "CA", name: "Canada", dialCode: "+1" },
1506
+ { code: "CL", name: "Chile", dialCode: "+56" },
1507
+ { code: "CN", name: "China", dialCode: "+86" },
1508
+ { code: "CO", name: "Colombia", dialCode: "+57" },
1509
+ { code: "HR", name: "Croatia", dialCode: "+385" },
1510
+ { code: "CY", name: "Cyprus", dialCode: "+357" },
1511
+ { code: "CZ", name: "Czech Republic", dialCode: "+420" },
1512
+ { code: "DK", name: "Denmark", dialCode: "+45" },
1513
+ { code: "EG", name: "Egypt", dialCode: "+20" },
1514
+ { code: "EE", name: "Estonia", dialCode: "+372" },
1515
+ { code: "FI", name: "Finland", dialCode: "+358" },
1516
+ { code: "FR", name: "France", dialCode: "+33" },
1517
+ { code: "GE", name: "Georgia", dialCode: "+995" },
1518
+ { code: "DE", name: "Germany", dialCode: "+49" },
1519
+ { code: "GR", name: "Greece", dialCode: "+30" },
1520
+ { code: "HK", name: "Hong Kong", dialCode: "+852" },
1521
+ { code: "HU", name: "Hungary", dialCode: "+36" },
1522
+ { code: "IN", name: "India", dialCode: "+91" },
1523
+ { code: "ID", name: "Indonesia", dialCode: "+62" },
1524
+ { code: "IR", name: "Iran", dialCode: "+98" },
1525
+ { code: "IQ", name: "Iraq", dialCode: "+964" },
1526
+ { code: "IE", name: "Ireland", dialCode: "+353" },
1527
+ { code: "IL", name: "Israel", dialCode: "+972" },
1528
+ { code: "IT", name: "Italy", dialCode: "+39" },
1529
+ { code: "JP", name: "Japan", dialCode: "+81" },
1530
+ { code: "JO", name: "Jordan", dialCode: "+962" },
1531
+ { code: "KZ", name: "Kazakhstan", dialCode: "+7" },
1532
+ { code: "KW", name: "Kuwait", dialCode: "+965" },
1533
+ { code: "LV", name: "Latvia", dialCode: "+371" },
1534
+ { code: "LB", name: "Lebanon", dialCode: "+961" },
1535
+ { code: "LT", name: "Lithuania", dialCode: "+370" },
1536
+ { code: "LU", name: "Luxembourg", dialCode: "+352" },
1537
+ { code: "MY", name: "Malaysia", dialCode: "+60" },
1538
+ { code: "MX", name: "Mexico", dialCode: "+52" },
1539
+ { code: "MD", name: "Moldova", dialCode: "+373" },
1540
+ { code: "NL", name: "Netherlands", dialCode: "+31" },
1541
+ { code: "NZ", name: "New Zealand", dialCode: "+64" },
1542
+ { code: "NG", name: "Nigeria", dialCode: "+234" },
1543
+ { code: "NO", name: "Norway", dialCode: "+47" },
1544
+ { code: "PK", name: "Pakistan", dialCode: "+92" },
1545
+ { code: "PS", name: "Palestine", dialCode: "+970" },
1546
+ { code: "PH", name: "Philippines", dialCode: "+63" },
1547
+ { code: "PL", name: "Poland", dialCode: "+48" },
1548
+ { code: "PT", name: "Portugal", dialCode: "+351" },
1549
+ { code: "QA", name: "Qatar", dialCode: "+974" },
1550
+ { code: "RO", name: "Romania", dialCode: "+40" },
1551
+ { code: "RU", name: "Russia", dialCode: "+7" },
1552
+ { code: "SA", name: "Saudi Arabia", dialCode: "+966" },
1553
+ { code: "RS", name: "Serbia", dialCode: "+381" },
1554
+ { code: "SG", name: "Singapore", dialCode: "+65" },
1555
+ { code: "SK", name: "Slovakia", dialCode: "+421" },
1556
+ { code: "SI", name: "Slovenia", dialCode: "+386" },
1557
+ { code: "ZA", name: "South Africa", dialCode: "+27" },
1558
+ { code: "KR", name: "South Korea", dialCode: "+82" },
1559
+ { code: "ES", name: "Spain", dialCode: "+34" },
1560
+ { code: "SE", name: "Sweden", dialCode: "+46" },
1561
+ { code: "CH", name: "Switzerland", dialCode: "+41" },
1562
+ { code: "SY", name: "Syria", dialCode: "+963" },
1563
+ { code: "TW", name: "Taiwan", dialCode: "+886" },
1564
+ { code: "TH", name: "Thailand", dialCode: "+66" },
1565
+ { code: "TR", name: "Turkey", dialCode: "+90" },
1566
+ { code: "UA", name: "Ukraine", dialCode: "+380" },
1567
+ { code: "AE", name: "United Arab Emirates", dialCode: "+971" },
1568
+ { code: "GB", name: "United Kingdom", dialCode: "+44" },
1569
+ { code: "US", name: "United States", dialCode: "+1" },
1570
+ { code: "UZ", name: "Uzbekistan", dialCode: "+998" },
1571
+ { code: "VN", name: "Vietnam", dialCode: "+84" }
1572
+ ];
1573
+ function findCountry(code) {
1574
+ return countries.find((country) => country.code === code) ?? defaultCountry;
1575
+ }
1576
+
1577
+ // src/utils/countryFlag.ts
1578
+ function countryCodeToFlagEmoji(code) {
1579
+ const upper = code.trim().toUpperCase();
1580
+ if (upper.length !== 2) return "";
1581
+ return String.fromCodePoint(
1582
+ ...upper.split("").map((char) => 127462 - 65 + char.charCodeAt(0))
1583
+ );
1584
+ }
1585
+ var DROPDOWN_GAP = 10;
1586
+ var DROPDOWN_HEIGHT = 186;
1587
+ var DROPDOWN_PADDING = 8;
1588
+ var OPTION_GAP = 10;
1589
+ var OPTION_HEIGHT = 35;
1590
+ var TRIGGER_HEIGHT = 52;
1591
+ var CHEVRON_SIZE = 16;
1592
+ function resolveDropdownTop(anchor) {
1593
+ const windowHeight = reactNative.Dimensions.get("window").height;
1594
+ const topBelow = anchor.y + anchor.height + DROPDOWN_GAP;
1595
+ const topAbove = anchor.y - DROPDOWN_GAP - DROPDOWN_HEIGHT;
1596
+ const fitsBelow = topBelow + DROPDOWN_HEIGHT <= windowHeight;
1597
+ if (fitsBelow) return topBelow;
1598
+ if (topAbove >= 0) return topAbove;
1599
+ return Math.max(0, Math.min(topBelow, windowHeight - DROPDOWN_HEIGHT));
1600
+ }
1601
+ function CountryCodeSelector({
1602
+ value = defaultCountry.code,
1603
+ onValueChange,
1604
+ options = countries,
1605
+ disabled = false,
1606
+ style,
1607
+ className
1608
+ }) {
1609
+ const wrapperRef = react.useRef(null);
1610
+ const triggerRef = react.useRef(null);
1611
+ const scrollRef = react.useRef(null);
1612
+ const [open, setOpen] = react.useState(false);
1613
+ const [anchor, setAnchor] = react.useState(null);
1614
+ useApplyWebClassName(wrapperRef, className);
1615
+ useApplyWebClassName(
1616
+ scrollRef,
1617
+ COUNTRY_DROPDOWN_SCROLL_CLASS,
1618
+ open && Boolean(anchor)
1619
+ );
1620
+ react.useLayoutEffect(() => {
1621
+ ensureCountryDropdownScrollStyles();
1622
+ }, []);
1623
+ const selected = findCountry(value);
1624
+ const textColor = disabled ? colors.stormGray200 : colors.slateBlue;
1625
+ function closeDropdown() {
1626
+ setOpen(false);
1627
+ setAnchor(null);
1628
+ }
1629
+ function handleOpen() {
1630
+ if (disabled) return;
1631
+ triggerRef.current?.measureInWindow((x, y, width, height) => {
1632
+ setAnchor({ x, y, width, height });
1633
+ setOpen(true);
1634
+ });
1635
+ }
1636
+ function handleSelect(code) {
1637
+ onValueChange?.(code);
1638
+ closeDropdown();
1639
+ }
1640
+ const dropdownTop = anchor ? resolveDropdownTop(anchor) : 0;
1641
+ const optionList = options.map((item, index) => {
1642
+ const isSelected = item.code === selected.code;
1643
+ const isLast = index === options.length - 1;
1644
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1645
+ reactNative.Pressable,
1646
+ {
1647
+ accessibilityRole: "button",
1648
+ onPress: () => handleSelect(item.code),
1649
+ style: [
1650
+ styles5.option,
1651
+ isSelected && styles5.optionSelected,
1652
+ !isLast && styles5.optionSpacing
1653
+ ],
1654
+ children: [
1655
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles5.flag, children: countryCodeToFlagEmoji(item.code) }),
1656
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles5.optionDialCode, children: item.dialCode })
1657
+ ]
1658
+ },
1659
+ item.code
1660
+ );
1661
+ });
1662
+ const dropdownList = /* @__PURE__ */ jsxRuntime.jsx(
1663
+ reactNative.ScrollView,
1664
+ {
1665
+ ref: scrollRef,
1666
+ style: styles5.dropdownScroll,
1667
+ contentContainerStyle: styles5.dropdownContent,
1668
+ keyboardShouldPersistTaps: "handled",
1669
+ showsVerticalScrollIndicator: true,
1670
+ bounces: false,
1671
+ nestedScrollEnabled: true,
1672
+ children: optionList
1673
+ }
1674
+ );
1675
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: wrapperRef, style: [styles5.root, style], children: [
1676
+ /* @__PURE__ */ jsxRuntime.jsxs(
1677
+ reactNative.Pressable,
1678
+ {
1679
+ ref: triggerRef,
1680
+ accessibilityRole: "button",
1681
+ disabled,
1682
+ onPress: handleOpen,
1683
+ style: [
1684
+ styles5.trigger,
1685
+ disabled ? styles5.triggerDisabled : styles5.triggerEnabled,
1686
+ reactNative.Platform.OS === "web" ? styles5.triggerWeb : null
1687
+ ],
1688
+ children: [
1689
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles5.flag, children: countryCodeToFlagEmoji(selected.code) }),
1690
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.dialCode, { color: textColor }], children: selected.dialCode }),
1691
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { size: CHEVRON_SIZE, color: colors.stormGray100 })
1692
+ ]
1693
+ }
1694
+ ),
1695
+ /* @__PURE__ */ jsxRuntime.jsx(
1696
+ reactNative.Modal,
1697
+ {
1698
+ visible: open,
1699
+ transparent: true,
1700
+ animationType: "fade",
1701
+ onRequestClose: closeDropdown,
1702
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles5.overlay, onPress: closeDropdown, children: anchor ? /* @__PURE__ */ jsxRuntime.jsx(
1703
+ reactNative.View,
1704
+ {
1705
+ style: [
1706
+ styles5.dropdown,
1707
+ {
1708
+ top: dropdownTop,
1709
+ left: anchor.x,
1710
+ width: anchor.width
1711
+ }
1712
+ ],
1713
+ children: dropdownList
1714
+ }
1715
+ ) : null })
1716
+ }
1717
+ )
1718
+ ] });
1719
+ }
1720
+ var styles5 = reactNative.StyleSheet.create({
1721
+ root: {
1722
+ alignSelf: "flex-start",
1723
+ flexShrink: 0
1724
+ },
1725
+ trigger: {
1726
+ flexDirection: "row",
1727
+ alignItems: "center",
1728
+ alignSelf: "flex-start",
1729
+ flexGrow: 0,
1730
+ flexShrink: 0,
1731
+ height: TRIGGER_HEIGHT,
1732
+ borderRadius: radii.lg,
1733
+ borderWidth: 1,
1734
+ padding: spacing.lg,
1735
+ gap: spacing.sm
1736
+ },
1737
+ triggerEnabled: {
1738
+ borderColor: colors.stormGray50,
1739
+ backgroundColor: colors.white
1740
+ },
1741
+ triggerDisabled: {
1742
+ borderColor: colors.stormGray50,
1743
+ backgroundColor: colors.stormGray50
1744
+ },
1745
+ triggerWeb: {
1746
+ width: "max-content"
1747
+ },
1748
+ flag: {
1749
+ fontSize: 18,
1750
+ lineHeight: 22
1751
+ },
1752
+ dialCode: {
1753
+ fontSize: fontSize.md,
1754
+ fontWeight: fontWeight.medium
1755
+ },
1756
+ overlay: {
1757
+ flex: 1,
1758
+ backgroundColor: "transparent"
1759
+ },
1760
+ dropdown: {
1761
+ position: "absolute",
1762
+ height: DROPDOWN_HEIGHT,
1763
+ borderRadius: radii.lg,
1764
+ borderWidth: 1,
1765
+ borderColor: colors.stormGray50,
1766
+ backgroundColor: colors.white,
1767
+ padding: DROPDOWN_PADDING,
1768
+ overflow: "hidden"
1769
+ },
1770
+ dropdownScroll: {
1771
+ flex: 1
1772
+ },
1773
+ dropdownContent: {
1774
+ flexGrow: 1
1775
+ },
1776
+ option: {
1777
+ flexDirection: "row",
1778
+ alignItems: "center",
1779
+ height: OPTION_HEIGHT,
1780
+ borderRadius: radii.sm,
1781
+ padding: spacing.sm,
1782
+ gap: spacing.sm
1783
+ },
1784
+ optionSpacing: {
1785
+ marginBottom: OPTION_GAP
1786
+ },
1787
+ optionSelected: {
1788
+ backgroundColor: colors.countrySelectorSelectedBg
1789
+ },
1790
+ optionDialCode: {
1791
+ fontFamily: fonts.sans,
1792
+ fontSize: fontSize.md,
1793
+ fontWeight: fontWeight.regular,
1794
+ lineHeight: fontSize.md,
1795
+ letterSpacing: 0,
1796
+ textAlign: "center",
1797
+ color: colors.slateBlue
1798
+ }
1799
+ });
1800
+ function PhoneInput({
1801
+ label,
1802
+ countryCode,
1803
+ onCountryCodeChange,
1804
+ error,
1805
+ hint,
1806
+ containerStyle,
1807
+ style,
1808
+ className,
1809
+ labelClassName,
1810
+ inputClassName,
1811
+ errorClassName,
1812
+ hintClassName,
1813
+ editable = true,
1814
+ onFocus,
1815
+ onBlur,
1816
+ ...props
1817
+ }) {
1818
+ const wrapperRef = react.useRef(null);
1819
+ const inputRef = react.useRef(null);
1820
+ const helperRef = react.useRef(null);
1821
+ const [focused, setFocused] = react.useState(false);
1822
+ useApplyWebClassName(wrapperRef, className);
1823
+ useApplyWebClassName(inputRef, inputClassName);
1824
+ useApplyWebClassName(helperRef, error ? errorClassName : hintClassName);
1825
+ const isDisabled = editable === false;
1826
+ const phoneVisualState = resolveInputVisualState({
1827
+ focused,
1828
+ error: Boolean(error),
1829
+ disabled: isDisabled
1830
+ });
1831
+ const phoneFieldStyles = getInputFieldStyles(phoneVisualState);
1832
+ function handleFocus(event) {
1833
+ setFocused(true);
1834
+ onFocus?.(event);
1835
+ }
1836
+ function handleBlur(event) {
1837
+ setFocused(false);
1838
+ onBlur?.(event);
1839
+ }
1840
+ const helperMessage = error ?? hint;
1841
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: wrapperRef, style: [styles6.wrapper, containerStyle], children: [
1842
+ label ? /* @__PURE__ */ jsxRuntime.jsx(Label, { disabled: isDisabled, className: labelClassName, children: label }) : null,
1843
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.row, children: [
1844
+ /* @__PURE__ */ jsxRuntime.jsx(
1845
+ CountryCodeSelector,
1846
+ {
1847
+ value: countryCode,
1247
1848
  onValueChange: onCountryCodeChange,
1248
1849
  disabled: isDisabled,
1249
- style: styles5.countrySelector
1850
+ style: styles6.countrySelector
1851
+ }
1852
+ ),
1853
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.phoneColumn, children: [
1854
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [phoneFieldStyles.outline, styles6.phoneOutline], children: /* @__PURE__ */ jsxRuntime.jsx(
1855
+ reactNative.View,
1856
+ {
1857
+ style: [
1858
+ inputFieldMetrics.container,
1859
+ phoneFieldStyles.container,
1860
+ styles6.phoneField
1861
+ ],
1862
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1863
+ reactNative.TextInput,
1864
+ {
1865
+ ref: inputRef,
1866
+ style: [
1867
+ inputFieldMetrics.input,
1868
+ phoneFieldStyles.text,
1869
+ style
1870
+ ],
1871
+ keyboardType: "phone-pad",
1872
+ placeholderTextColor: phoneFieldStyles.placeholder,
1873
+ editable,
1874
+ onFocus: handleFocus,
1875
+ onBlur: handleBlur,
1876
+ accessibilityState: { disabled: isDisabled },
1877
+ ...props
1878
+ }
1879
+ )
1880
+ }
1881
+ ) }),
1882
+ helperMessage ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { ref: helperRef, style: error ? styles6.error : styles6.hint, children: helperMessage }) : null
1883
+ ] })
1884
+ ] })
1885
+ ] });
1886
+ }
1887
+ var styles6 = reactNative.StyleSheet.create({
1888
+ wrapper: {
1889
+ width: "100%",
1890
+ gap: spacing.sm
1891
+ },
1892
+ row: {
1893
+ flexDirection: "row",
1894
+ alignItems: "flex-start",
1895
+ gap: INPUT_ICON_GAP
1896
+ },
1897
+ countrySelector: {
1898
+ flexShrink: 0,
1899
+ alignSelf: "flex-start"
1900
+ },
1901
+ phoneColumn: {
1902
+ flex: 1,
1903
+ gap: spacing.sm
1904
+ },
1905
+ phoneOutline: {
1906
+ width: "100%"
1907
+ },
1908
+ phoneField: {
1909
+ flex: 1
1910
+ },
1911
+ error: {
1912
+ fontSize: 12,
1913
+ lineHeight: 16,
1914
+ color: colors.inputError
1915
+ },
1916
+ hint: {
1917
+ fontSize: 12,
1918
+ lineHeight: 16,
1919
+ color: colors.stormGray300
1920
+ }
1921
+ });
1922
+
1923
+ // src/icons/chevronNavPaths.ts
1924
+ var CHEVRON_LEFT_PATH = "M12.5 15L7.5 10L12.5 5";
1925
+ var CHEVRON_RIGHT_PATH = "M7.5 15L12.5 10L7.5 5";
1926
+ function ChevronLeftIcon({
1927
+ size = 20,
1928
+ color = "#151a1e",
1929
+ strokeWidth = 2
1930
+ }) {
1931
+ return /* @__PURE__ */ jsxRuntime.jsx(Svg5__default.default, { width: size, height: size, viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
1932
+ Svg5.Path,
1933
+ {
1934
+ d: CHEVRON_LEFT_PATH,
1935
+ stroke: color,
1936
+ strokeWidth,
1937
+ strokeLinecap: "round",
1938
+ strokeLinejoin: "round"
1939
+ }
1940
+ ) });
1941
+ }
1942
+ function ChevronRightIcon({
1943
+ size = 20,
1944
+ color = "#151a1e",
1945
+ strokeWidth = 2
1946
+ }) {
1947
+ return /* @__PURE__ */ jsxRuntime.jsx(Svg5__default.default, { width: size, height: size, viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
1948
+ Svg5.Path,
1949
+ {
1950
+ d: CHEVRON_RIGHT_PATH,
1951
+ stroke: color,
1952
+ strokeWidth,
1953
+ strokeLinecap: "round",
1954
+ strokeLinejoin: "round"
1955
+ }
1956
+ ) });
1957
+ }
1958
+ var CALENDAR_DROPDOWN_GAP = spacing.sm;
1959
+ var CALENDAR_PANEL_WIDTH = 296;
1960
+ var CALENDAR_PANEL_PADDING = spacing.md;
1961
+ var CALENDAR_PANEL_GAP = spacing.md;
1962
+ var CALENDAR_CONTENT_WIDTH = CALENDAR_PANEL_WIDTH - CALENDAR_PANEL_PADDING * 2;
1963
+ var CALENDAR_HEADER_HEIGHT = 20;
1964
+ var CALENDAR_WEEKDAY_CELL_SIZE = 32;
1965
+ var CALENDAR_WEEKDAY_GAP = spacing.sm;
1966
+ var CALENDAR_WEEKDAY_RADIUS = 6;
1967
+ var CALENDAR_DAY_SIZE = CALENDAR_WEEKDAY_CELL_SIZE;
1968
+ var CALENDAR_DAY_GRID_WIDTH = CALENDAR_CONTENT_WIDTH;
1969
+ var CALENDAR_DAY_GAP_X = spacing.sm;
1970
+ var CALENDAR_DAY_GAP_Y = spacing.lg;
1971
+ var CALENDAR_DAY_ROWS = 6;
1972
+ var CALENDAR_DAY_GRID_HEIGHT = CALENDAR_DAY_SIZE * CALENDAR_DAY_ROWS + CALENDAR_DAY_GAP_Y * (CALENDAR_DAY_ROWS - 1);
1973
+ var CALENDAR_PICKER_COLUMNS = 3;
1974
+ var CALENDAR_YEARS_PER_PAGE = 12;
1975
+ var CALENDAR_PICKER_CELL_WIDTH = (CALENDAR_DAY_GRID_WIDTH - CALENDAR_DAY_GAP_X * (CALENDAR_PICKER_COLUMNS - 1)) / CALENDAR_PICKER_COLUMNS;
1976
+ var CALENDAR_PICKER_CELL_HEIGHT = 36;
1977
+ var CALENDAR_PICKER_BODY_HEIGHT = CALENDAR_WEEKDAY_CELL_SIZE + CALENDAR_WEEKDAY_GAP + CALENDAR_DAY_GRID_HEIGHT;
1978
+ var CALENDAR_DAY_DOT_SIZE = 3;
1979
+ var CALENDAR_TODAY_TEXT_COLOR = "#082640";
1980
+ var CALENDAR_TODAY_DOT_COLOR = "#0033A0";
1981
+ var CALENDAR_NAV_ICON_SIZE = 20;
1982
+ var CALENDAR_NAV_ICON_COLOR = colors.stormGray850;
1983
+ var CALENDAR_FIELD_ICON_SIZE = 20;
1984
+ var CALENDAR_FIELD_WIDTH = CALENDAR_PANEL_WIDTH;
1985
+ var CALENDAR_FIELD_HEIGHT = 52;
1986
+ var CALENDAR_FIELD_PADDING = spacing.lg;
1987
+ var CALENDAR_FIELD_GAP = spacing.sm;
1988
+ var CALENDAR_FIELD_PLACEHOLDER_COLOR = colors.stormGray200;
1989
+ var CALENDAR_PANEL_TOTAL_HEIGHT = CALENDAR_PANEL_PADDING * 2 + CALENDAR_HEADER_HEIGHT + CALENDAR_PANEL_GAP * 2 + CALENDAR_WEEKDAY_CELL_SIZE + CALENDAR_DAY_GRID_HEIGHT;
1990
+ var CALENDAR_PANEL_HEIGHT = CALENDAR_PANEL_TOTAL_HEIGHT;
1991
+ var calendarLabelTypography = {
1992
+ fontFamily: fonts.sans,
1993
+ fontSize: fontSize.sm,
1994
+ fontWeight: fontWeight.medium,
1995
+ lineHeight: fontSize.sm,
1996
+ letterSpacing: 0,
1997
+ color: colors.slateBlue
1998
+ };
1999
+ var calendarFieldTypography = {
2000
+ fontFamily: fonts.sans,
2001
+ fontSize: fontSize.md,
2002
+ fontWeight: fontWeight.regular,
2003
+ lineHeight: fontSize.md,
2004
+ letterSpacing: 0,
2005
+ textAlign: "left"
2006
+ };
2007
+ var calendarFieldPlaceholderTypography = {
2008
+ ...calendarFieldTypography,
2009
+ color: CALENDAR_FIELD_PLACEHOLDER_COLOR
2010
+ };
2011
+ var calendarDropdownMetrics = reactNative.StyleSheet.create({
2012
+ panel: {
2013
+ width: CALENDAR_PANEL_WIDTH,
2014
+ height: CALENDAR_PANEL_HEIGHT,
2015
+ borderRadius: radii.lg,
2016
+ borderWidth: 1,
2017
+ borderColor: colors.stormGray50,
2018
+ backgroundColor: colors.white,
2019
+ padding: CALENDAR_PANEL_PADDING,
2020
+ gap: CALENDAR_PANEL_GAP,
2021
+ overflow: "hidden",
2022
+ flexDirection: "column",
2023
+ opacity: 1,
2024
+ ...reactNative.Platform.OS === "web" ? {
2025
+ boxSizing: "border-box"
2026
+ } : null
2027
+ },
2028
+ header: {
2029
+ flexDirection: "row",
2030
+ alignItems: "center",
2031
+ justifyContent: "space-between",
2032
+ height: CALENDAR_HEADER_HEIGHT,
2033
+ minHeight: CALENDAR_HEADER_HEIGHT,
2034
+ maxHeight: CALENDAR_HEADER_HEIGHT
2035
+ },
2036
+ headerTitle: {
2037
+ textAlign: "center",
2038
+ fontFamily: fonts.sans,
2039
+ fontSize: fontSize.md,
2040
+ fontWeight: fontWeight.bold,
2041
+ lineHeight: 18,
2042
+ letterSpacing: 0,
2043
+ color: colors.stormGray850
2044
+ },
2045
+ headerTitlePressable: {
2046
+ flex: 1,
2047
+ alignItems: "center",
2048
+ justifyContent: "center",
2049
+ minHeight: CALENDAR_HEADER_HEIGHT
2050
+ },
2051
+ navButton: {
2052
+ width: CALENDAR_NAV_ICON_SIZE,
2053
+ height: CALENDAR_NAV_ICON_SIZE,
2054
+ alignItems: "center",
2055
+ justifyContent: "center"
2056
+ },
2057
+ weekdayRow: {
2058
+ flexDirection: "row",
2059
+ gap: CALENDAR_WEEKDAY_GAP,
2060
+ height: CALENDAR_WEEKDAY_CELL_SIZE,
2061
+ minHeight: CALENDAR_WEEKDAY_CELL_SIZE,
2062
+ maxHeight: CALENDAR_WEEKDAY_CELL_SIZE,
2063
+ opacity: 1
2064
+ },
2065
+ weekdayCell: {
2066
+ width: CALENDAR_WEEKDAY_CELL_SIZE,
2067
+ height: CALENDAR_WEEKDAY_CELL_SIZE,
2068
+ borderRadius: CALENDAR_WEEKDAY_RADIUS,
2069
+ padding: spacing.xs,
2070
+ backgroundColor: colors.stormGray0,
2071
+ alignItems: "center",
2072
+ justifyContent: "center",
2073
+ opacity: 1,
2074
+ ...reactNative.Platform.OS === "web" ? {
2075
+ boxSizing: "border-box"
2076
+ } : null
2077
+ },
2078
+ weekdayLabel: {
2079
+ fontFamily: fonts.sans,
2080
+ fontSize: fontSize.sm,
2081
+ fontWeight: fontWeight.regular,
2082
+ lineHeight: fontSize.sm,
2083
+ letterSpacing: 0,
2084
+ textAlign: "center",
2085
+ color: colors.stormGray300,
2086
+ textTransform: "uppercase"
2087
+ },
2088
+ dayGrid: {
2089
+ width: CALENDAR_DAY_GRID_WIDTH,
2090
+ height: CALENDAR_DAY_GRID_HEIGHT,
2091
+ flexDirection: "row",
2092
+ flexWrap: "wrap",
2093
+ columnGap: CALENDAR_DAY_GAP_X,
2094
+ rowGap: CALENDAR_DAY_GAP_Y,
2095
+ alignContent: "flex-start",
2096
+ opacity: 1
2097
+ },
2098
+ dayCell: {
2099
+ width: CALENDAR_DAY_SIZE,
2100
+ height: CALENDAR_DAY_SIZE,
2101
+ minHeight: CALENDAR_DAY_SIZE,
2102
+ maxHeight: CALENDAR_DAY_SIZE,
2103
+ alignItems: "center",
2104
+ justifyContent: "center",
2105
+ borderRadius: CALENDAR_WEEKDAY_RADIUS,
2106
+ padding: spacing.xs,
2107
+ backgroundColor: colors.white,
2108
+ opacity: 1,
2109
+ position: "relative",
2110
+ ...reactNative.Platform.OS === "web" ? {
2111
+ boxSizing: "border-box"
2112
+ } : null
2113
+ },
2114
+ dayCellPast: {
2115
+ backgroundColor: colors.stormGray50
2116
+ },
2117
+ dayCellSelected: {
2118
+ backgroundColor: colors.navy
2119
+ },
2120
+ dayLabel: {
2121
+ fontFamily: fonts.sans,
2122
+ fontSize: fontSize.md,
2123
+ fontWeight: fontWeight.regular,
2124
+ lineHeight: 18,
2125
+ letterSpacing: 0,
2126
+ textAlign: "center",
2127
+ color: colors.navy,
2128
+ includeFontPadding: false,
2129
+ textAlignVertical: "center"
2130
+ },
2131
+ dayLabelToday: {
2132
+ color: CALENDAR_TODAY_TEXT_COLOR
2133
+ },
2134
+ dayLabelSelected: {
2135
+ fontWeight: fontWeight.semibold,
2136
+ color: colors.white
2137
+ },
2138
+ dayLabelFuture: {
2139
+ color: colors.navy
2140
+ },
2141
+ dayLabelMuted: {
2142
+ color: colors.stormGray300
2143
+ },
2144
+ dayLabelOutsideMonth: {
2145
+ color: colors.stormGray300
2146
+ },
2147
+ dayLabelDisabled: {
2148
+ color: colors.stormGray300,
2149
+ opacity: 0.5
2150
+ },
2151
+ todayDot: {
2152
+ position: "absolute",
2153
+ bottom: 5,
2154
+ left: "50%",
2155
+ width: CALENDAR_DAY_DOT_SIZE,
2156
+ height: CALENDAR_DAY_DOT_SIZE,
2157
+ borderRadius: 15,
2158
+ backgroundColor: CALENDAR_TODAY_DOT_COLOR,
2159
+ marginLeft: -1.5,
2160
+ zIndex: 1
2161
+ },
2162
+ pickerBody: {
2163
+ width: CALENDAR_DAY_GRID_WIDTH,
2164
+ height: CALENDAR_PICKER_BODY_HEIGHT,
2165
+ justifyContent: "center"
2166
+ },
2167
+ pickerGrid: {
2168
+ width: CALENDAR_DAY_GRID_WIDTH,
2169
+ flexDirection: "row",
2170
+ flexWrap: "wrap",
2171
+ columnGap: CALENDAR_DAY_GAP_X,
2172
+ rowGap: CALENDAR_DAY_GAP_Y,
2173
+ alignContent: "flex-start"
2174
+ },
2175
+ pickerCell: {
2176
+ width: CALENDAR_PICKER_CELL_WIDTH,
2177
+ minHeight: CALENDAR_PICKER_CELL_HEIGHT,
2178
+ alignItems: "center",
2179
+ justifyContent: "center",
2180
+ borderRadius: CALENDAR_WEEKDAY_RADIUS,
2181
+ paddingHorizontal: spacing.xs,
2182
+ paddingVertical: spacing.xs,
2183
+ backgroundColor: colors.white,
2184
+ ...reactNative.Platform.OS === "web" ? {
2185
+ boxSizing: "border-box"
2186
+ } : null
2187
+ },
2188
+ pickerCellSelected: {
2189
+ backgroundColor: colors.navy
2190
+ },
2191
+ pickerLabel: {
2192
+ fontFamily: fonts.sans,
2193
+ fontSize: fontSize.md,
2194
+ fontWeight: fontWeight.regular,
2195
+ lineHeight: 18,
2196
+ letterSpacing: 0,
2197
+ textAlign: "center",
2198
+ color: colors.navy,
2199
+ includeFontPadding: false,
2200
+ ...reactNative.Platform.OS === "android" ? { textAlignVertical: "center" } : null
2201
+ },
2202
+ pickerLabelSelected: {
2203
+ fontWeight: fontWeight.semibold,
2204
+ color: colors.white
2205
+ },
2206
+ pickerLabelDisabled: {
2207
+ color: colors.stormGray300,
2208
+ opacity: 0.5
2209
+ },
2210
+ pickerLabelCurrent: {
2211
+ color: CALENDAR_TODAY_TEXT_COLOR
2212
+ }
2213
+ });
2214
+ function resolveCalendarDropdownTop(anchor, panelHeight = CALENDAR_PANEL_HEIGHT) {
2215
+ const windowHeight = reactNative.Dimensions.get("window").height;
2216
+ const spaceBelow = windowHeight - (anchor.y + anchor.height + CALENDAR_DROPDOWN_GAP);
2217
+ const spaceAbove = anchor.y - CALENDAR_DROPDOWN_GAP;
2218
+ const topBelow = anchor.y + anchor.height + CALENDAR_DROPDOWN_GAP;
2219
+ const topAbove = anchor.y - CALENDAR_DROPDOWN_GAP - panelHeight;
2220
+ if (spaceBelow >= panelHeight) {
2221
+ return topBelow;
2222
+ }
2223
+ if (spaceAbove >= panelHeight) {
2224
+ return topAbove;
2225
+ }
2226
+ if (spaceAbove > spaceBelow) {
2227
+ return Math.max(0, topAbove);
2228
+ }
2229
+ return Math.max(0, Math.min(topBelow, windowHeight - panelHeight));
2230
+ }
2231
+ function resolveCalendarDropdownLeft(anchor, panelWidth = CALENDAR_PANEL_WIDTH) {
2232
+ const windowWidth = reactNative.Dimensions.get("window").width;
2233
+ const maxLeft = Math.max(0, windowWidth - panelWidth);
2234
+ return Math.max(0, Math.min(anchor.x, maxLeft));
2235
+ }
2236
+ var calendarFieldMetrics = reactNative.StyleSheet.create({
2237
+ container: {
2238
+ flexDirection: "row",
2239
+ alignItems: "center",
2240
+ width: CALENDAR_FIELD_WIDTH,
2241
+ minWidth: CALENDAR_FIELD_WIDTH,
2242
+ maxWidth: CALENDAR_FIELD_WIDTH,
2243
+ height: CALENDAR_FIELD_HEIGHT,
2244
+ minHeight: CALENDAR_FIELD_HEIGHT,
2245
+ maxHeight: CALENDAR_FIELD_HEIGHT,
2246
+ borderRadius: radii.lg,
2247
+ padding: CALENDAR_FIELD_PADDING,
2248
+ gap: CALENDAR_FIELD_GAP,
2249
+ opacity: 1,
2250
+ ...reactNative.Platform.OS === "web" ? {
2251
+ boxSizing: "border-box"
2252
+ } : null
2253
+ },
2254
+ value: {
2255
+ flex: 1,
2256
+ minWidth: 0,
2257
+ ...calendarFieldTypography,
2258
+ paddingVertical: 0,
2259
+ paddingHorizontal: 0,
2260
+ margin: 0,
2261
+ ...reactNative.Platform.OS === "android" ? { includeFontPadding: false } : null,
2262
+ ...reactNative.Platform.OS === "web" ? {
2263
+ outlineStyle: "none"
2264
+ } : null
2265
+ }
2266
+ });
2267
+
2268
+ // src/utils/calendarDays.ts
2269
+ function stripTime(date) {
2270
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
2271
+ }
2272
+ function normalizeDateRange(range) {
2273
+ if (!range?.start) {
2274
+ return {};
2275
+ }
2276
+ const start = stripTime(range.start);
2277
+ if (!range.end) {
2278
+ return { start };
2279
+ }
2280
+ const end = stripTime(range.end);
2281
+ if (isBeforeDay(end, start)) {
2282
+ return { start: end, end: start };
2283
+ }
2284
+ return { start, end };
2285
+ }
2286
+ function isInDateRange(date, start, end) {
2287
+ if (!start) {
2288
+ return false;
2289
+ }
2290
+ const day = stripTime(date);
2291
+ if (!end) {
2292
+ return isSameDay(day, start);
2293
+ }
2294
+ return !isBeforeDay(day, start) && !isAfterDay(day, end);
2295
+ }
2296
+ function selectRangeDate(clicked, current) {
2297
+ const clickedDate = stripTime(clicked);
2298
+ if (!current?.start || current.end) {
2299
+ return { start: clickedDate };
2300
+ }
2301
+ const start = stripTime(current.start);
2302
+ if (isSameDay(clickedDate, start)) {
2303
+ return { start, end: clickedDate };
2304
+ }
2305
+ if (isBeforeDay(clickedDate, start)) {
2306
+ return { start: clickedDate };
2307
+ }
2308
+ return { start, end: clickedDate };
2309
+ }
2310
+ function isSameDay(left, right) {
2311
+ return left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate();
2312
+ }
2313
+ function isBeforeDay(left, right) {
2314
+ return stripTime(left).getTime() < stripTime(right).getTime();
2315
+ }
2316
+ function isAfterDay(left, right) {
2317
+ return stripTime(left).getTime() > stripTime(right).getTime();
2318
+ }
2319
+ function addMonths(date, amount) {
2320
+ return new Date(date.getFullYear(), date.getMonth() + amount, 1);
2321
+ }
2322
+ function isDateDisabled(date, minDate, maxDate) {
2323
+ if (minDate && isBeforeDay(date, minDate)) {
2324
+ return true;
2325
+ }
2326
+ if (maxDate && isAfterDay(date, maxDate)) {
2327
+ return true;
2328
+ }
2329
+ return false;
2330
+ }
2331
+ var CALENDAR_VISIBLE_DAY_COUNT = 42;
2332
+ var MONDAY_WEEK_START = 1;
2333
+ function buildCalendarDays({
2334
+ viewDate,
2335
+ selectedDate,
2336
+ selectedRange,
2337
+ today,
2338
+ minDate,
2339
+ maxDate
2340
+ }) {
2341
+ const { start: rangeStart, end: rangeEnd } = normalizeDateRange(selectedRange);
2342
+ const year = viewDate.getFullYear();
2343
+ const month = viewDate.getMonth();
2344
+ const firstOfMonth = new Date(year, month, 1);
2345
+ const startOffset = (firstOfMonth.getDay() - MONDAY_WEEK_START + 7) % 7;
2346
+ const gridStart = new Date(year, month, 1 - startOffset);
2347
+ const days = [];
2348
+ for (let index = 0; index < CALENDAR_VISIBLE_DAY_COUNT; index += 1) {
2349
+ const date = new Date(
2350
+ gridStart.getFullYear(),
2351
+ gridStart.getMonth(),
2352
+ gridStart.getDate() + index
2353
+ );
2354
+ const inCurrentMonth = date.getMonth() === month;
2355
+ const isPast = inCurrentMonth && isBeforeDay(date, today);
2356
+ const isDisabled = isDateDisabled(date, minDate, maxDate);
2357
+ const isRangeStart = rangeStart ? isSameDay(date, rangeStart) : false;
2358
+ const isRangeEnd = rangeEnd ? isSameDay(date, rangeEnd) : false;
2359
+ const inSelectedRange = rangeStart && rangeEnd ? isInDateRange(date, rangeStart, rangeEnd) : false;
2360
+ const isInRange = inSelectedRange && !isRangeStart && !isRangeEnd;
2361
+ const isSelected = selectedDate ? isSameDay(date, selectedDate) : inSelectedRange || Boolean(rangeStart && !rangeEnd && isRangeStart);
2362
+ days.push({
2363
+ date,
2364
+ inCurrentMonth,
2365
+ isToday: isSameDay(date, today),
2366
+ isSelected,
2367
+ isRangeStart,
2368
+ isRangeEnd,
2369
+ isInRange,
2370
+ isPast,
2371
+ isDisabled
2372
+ });
2373
+ }
2374
+ return days;
2375
+ }
2376
+ function formatCalendarYear(year, locale) {
2377
+ return new Intl.DateTimeFormat(locale, { year: "numeric" }).format(new Date(year, 0, 1));
2378
+ }
2379
+ function getYearPageStart(year, yearsPerPage = 12) {
2380
+ return Math.floor(year / yearsPerPage) * yearsPerPage;
2381
+ }
2382
+ function isMonthDisabled(year, month, minDate, maxDate) {
2383
+ const firstDay = new Date(year, month, 1);
2384
+ const lastDay = new Date(year, month + 1, 0);
2385
+ if (minDate && isBeforeDay(lastDay, minDate)) {
2386
+ return true;
2387
+ }
2388
+ if (maxDate && isAfterDay(firstDay, maxDate)) {
2389
+ return true;
2390
+ }
2391
+ return false;
2392
+ }
2393
+ function isYearDisabled(year, minDate, maxDate) {
2394
+ const firstDay = new Date(year, 0, 1);
2395
+ const lastDay = new Date(year, 11, 31);
2396
+ if (minDate && isBeforeDay(lastDay, minDate)) {
2397
+ return true;
2398
+ }
2399
+ if (maxDate && isAfterDay(firstDay, maxDate)) {
2400
+ return true;
2401
+ }
2402
+ return false;
2403
+ }
2404
+ function formatCalendarDate(date, locale) {
2405
+ return new Intl.DateTimeFormat(locale, {
2406
+ day: "numeric",
2407
+ month: "long",
2408
+ year: "numeric"
2409
+ }).format(date);
2410
+ }
2411
+ function formatCalendarDateRange(range, locale, separator = " \u2013 ") {
2412
+ const { start, end } = normalizeDateRange(range);
2413
+ if (!start) {
2414
+ return "";
2415
+ }
2416
+ if (!end) {
2417
+ return formatCalendarDate(start, locale);
2418
+ }
2419
+ return `${formatCalendarDate(start, locale)}${separator}${formatCalendarDate(end, locale)}`;
2420
+ }
2421
+
2422
+ // src/utils/calendarLocalization.ts
2423
+ var calendarTranslations = {
2424
+ "hy-AM": {
2425
+ weekdays: ["\u0535\u0550", "\u0535\u0554", "\u0549\u0550", "\u0540\u0546", "\u0548\u0552\u0550", "\u0547\u0532", "\u053F\u0550"],
2426
+ monthsShort: [
2427
+ "\u0540\u0576\u057E",
2428
+ "\u0553\u057F\u057E",
2429
+ "\u0544\u0580\u057F",
2430
+ "\u0531\u057A\u0580",
2431
+ "\u0544\u0575\u057D",
2432
+ "\u0540\u0578\u0582\u0576",
2433
+ "\u0540\u0578\u0582\u056C",
2434
+ "\u0555\u0563\u057D",
2435
+ "\u054D\u0565\u057A",
2436
+ "\u0540\u0578\u056F",
2437
+ "\u0546\u0578\u0575",
2438
+ "\u0534\u0565\u056F"
2439
+ ],
2440
+ monthsLong: [
2441
+ "\u0540\u0578\u0582\u0576\u057E\u0561\u0580",
2442
+ "\u0553\u0565\u057F\u0580\u057E\u0561\u0580",
2443
+ "\u0544\u0561\u0580\u057F",
2444
+ "\u0531\u057A\u0580\u056B\u056C",
2445
+ "\u0544\u0561\u0575\u056B\u057D",
2446
+ "\u0540\u0578\u0582\u0576\u056B\u057D",
2447
+ "\u0540\u0578\u0582\u056C\u056B\u057D",
2448
+ "\u0555\u0563\u0578\u057D\u057F\u0578\u057D",
2449
+ "\u054D\u0565\u057A\u057F\u0565\u0574\u0562\u0565\u0580",
2450
+ "\u0540\u0578\u056F\u057F\u0565\u0574\u0562\u0565\u0580",
2451
+ "\u0546\u0578\u0575\u0565\u0574\u0562\u0565\u0580",
2452
+ "\u0534\u0565\u056F\u057F\u0565\u0574\u0562\u0565\u0580"
2453
+ ],
2454
+ datePlaceholder: "\u0538\u0576\u057F\u0580\u0565\u056C \u0561\u0574\u057D\u0561\u0569\u056B\u057E",
2455
+ rangePlaceholder: "\u0538\u0576\u057F\u0580\u0565\u056C \u056A\u0561\u0574\u0561\u0576\u0561\u056F\u0561\u0570\u0561\u057F\u057E\u0561\u056E",
2456
+ previousMonth: "\u0546\u0561\u056D\u0578\u0580\u0564 \u0561\u0574\u056B\u057D",
2457
+ nextMonth: "\u0540\u0561\u057B\u0578\u0580\u0564 \u0561\u0574\u056B\u057D",
2458
+ previousYear: "\u0546\u0561\u056D\u0578\u0580\u0564 \u057F\u0561\u0580\u056B",
2459
+ nextYear: "\u0540\u0561\u057B\u0578\u0580\u0564 \u057F\u0561\u0580\u056B",
2460
+ previousYears: "\u0546\u0561\u056D\u0578\u0580\u0564 \u057F\u0561\u0580\u056B\u0576\u0565\u0580",
2461
+ nextYears: "\u0540\u0561\u057B\u0578\u0580\u0564 \u057F\u0561\u0580\u056B\u0576\u0565\u0580",
2462
+ selectMonth: "\u0538\u0576\u057F\u0580\u0565\u056C \u0561\u0574\u056B\u057D",
2463
+ selectYear: "\u0538\u0576\u057F\u0580\u0565\u056C \u057F\u0561\u0580\u056B",
2464
+ closeCalendar: "\u0553\u0561\u056F\u0565\u056C \u0585\u0580\u0561\u0581\u0578\u0582\u0575\u0581\u0568"
2465
+ },
2466
+ "en-US": {
2467
+ weekdays: ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
2468
+ monthsShort: [
2469
+ "Jan",
2470
+ "Feb",
2471
+ "Mar",
2472
+ "Apr",
2473
+ "May",
2474
+ "Jun",
2475
+ "Jul",
2476
+ "Aug",
2477
+ "Sep",
2478
+ "Oct",
2479
+ "Nov",
2480
+ "Dec"
2481
+ ],
2482
+ monthsLong: [
2483
+ "January",
2484
+ "February",
2485
+ "March",
2486
+ "April",
2487
+ "May",
2488
+ "June",
2489
+ "July",
2490
+ "August",
2491
+ "September",
2492
+ "October",
2493
+ "November",
2494
+ "December"
2495
+ ],
2496
+ datePlaceholder: "Select date",
2497
+ rangePlaceholder: "Select date range",
2498
+ previousMonth: "Previous month",
2499
+ nextMonth: "Next month",
2500
+ previousYear: "Previous year",
2501
+ nextYear: "Next year",
2502
+ previousYears: "Previous years",
2503
+ nextYears: "Next years",
2504
+ selectMonth: "Select month",
2505
+ selectYear: "Select year",
2506
+ closeCalendar: "Close calendar"
2507
+ },
2508
+ "ru-RU": {
2509
+ weekdays: ["\u041F\u041D", "\u0412\u0422", "\u0421\u0420", "\u0427\u0422", "\u041F\u0422", "\u0421\u0411", "\u0412\u0421"],
2510
+ monthsShort: [
2511
+ "\u042F\u043D\u0432",
2512
+ "\u0424\u0435\u0432",
2513
+ "\u041C\u0430\u0440",
2514
+ "\u0410\u043F\u0440",
2515
+ "\u041C\u0430\u0439",
2516
+ "\u0418\u044E\u043D",
2517
+ "\u0418\u044E\u043B",
2518
+ "\u0410\u0432\u0433",
2519
+ "\u0421\u0435\u043D",
2520
+ "\u041E\u043A\u0442",
2521
+ "\u041D\u043E\u044F",
2522
+ "\u0414\u0435\u043A"
2523
+ ],
2524
+ monthsLong: [
2525
+ "\u042F\u043D\u0432\u0430\u0440\u044C",
2526
+ "\u0424\u0435\u0432\u0440\u0430\u043B\u044C",
2527
+ "\u041C\u0430\u0440\u0442",
2528
+ "\u0410\u043F\u0440\u0435\u043B\u044C",
2529
+ "\u041C\u0430\u0439",
2530
+ "\u0418\u044E\u043D\u044C",
2531
+ "\u0418\u044E\u043B\u044C",
2532
+ "\u0410\u0432\u0433\u0443\u0441\u0442",
2533
+ "\u0421\u0435\u043D\u0442\u044F\u0431\u0440\u044C",
2534
+ "\u041E\u043A\u0442\u044F\u0431\u0440\u044C",
2535
+ "\u041D\u043E\u044F\u0431\u0440\u044C",
2536
+ "\u0414\u0435\u043A\u0430\u0431\u0440\u044C"
2537
+ ],
2538
+ datePlaceholder: "\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0434\u0430\u0442\u0443",
2539
+ rangePlaceholder: "\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u043F\u0435\u0440\u0438\u043E\u0434",
2540
+ previousMonth: "\u041F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0438\u0439 \u043C\u0435\u0441\u044F\u0446",
2541
+ nextMonth: "\u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u043C\u0435\u0441\u044F\u0446",
2542
+ previousYear: "\u041F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0438\u0439 \u0433\u043E\u0434",
2543
+ nextYear: "\u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0433\u043E\u0434",
2544
+ previousYears: "\u041F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0438\u0435 \u0433\u043E\u0434\u044B",
2545
+ nextYears: "\u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 \u0433\u043E\u0434\u044B",
2546
+ selectMonth: "\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u043C\u0435\u0441\u044F\u0446",
2547
+ selectYear: "\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0433\u043E\u0434",
2548
+ closeCalendar: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C \u043A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u044C"
2549
+ }
2550
+ };
2551
+ function resolveCalendarLocale(locale) {
2552
+ const normalizedLocale = locale.toLowerCase();
2553
+ if (normalizedLocale.startsWith("hy")) {
2554
+ return "hy-AM";
2555
+ }
2556
+ if (normalizedLocale.startsWith("ru")) {
2557
+ return "ru-RU";
2558
+ }
2559
+ return "en-US";
2560
+ }
2561
+ function getCalendarTranslations(locale) {
2562
+ return calendarTranslations[resolveCalendarLocale(locale)];
2563
+ }
2564
+ function resolveWeekdayLabels(locale) {
2565
+ return getCalendarTranslations(locale).weekdays;
2566
+ }
2567
+ function getCalendarMonthLabels(locale, width = "short") {
2568
+ const translations = getCalendarTranslations(locale);
2569
+ return width === "short" ? translations.monthsShort : translations.monthsLong;
2570
+ }
2571
+ function formatLocalizedCalendarMonthYear(date, locale) {
2572
+ return `${getCalendarMonthLabels(locale)[date.getMonth()]} ${date.getFullYear()}`;
2573
+ }
2574
+ var LOCALE_STORAGE_KEY = "homepad.locale";
2575
+ var LOCALE_CHANGE_EVENT = "homepad:locale-change";
2576
+ var DEFAULT_CALENDAR_LOCALE = "hy-AM";
2577
+ var CalendarLocaleContext = react.createContext(void 0);
2578
+ function CalendarLocaleProvider({ locale, children }) {
2579
+ return /* @__PURE__ */ jsxRuntime.jsx(CalendarLocaleContext.Provider, { value: locale, children });
2580
+ }
2581
+ function normalizeProjectLocale(locale) {
2582
+ if (!locale) {
2583
+ return null;
2584
+ }
2585
+ return resolveCalendarLocale(locale);
2586
+ }
2587
+ function readBrowserLocale() {
2588
+ if (typeof window === "undefined") {
2589
+ return null;
2590
+ }
2591
+ let storedLocale = null;
2592
+ try {
2593
+ storedLocale = normalizeProjectLocale(window.localStorage.getItem(LOCALE_STORAGE_KEY));
2594
+ } catch {
2595
+ storedLocale = null;
2596
+ }
2597
+ if (storedLocale) {
2598
+ return storedLocale;
2599
+ }
2600
+ const documentLocale = normalizeProjectLocale(window.document?.documentElement.lang);
2601
+ if (documentLocale) {
2602
+ return documentLocale;
2603
+ }
2604
+ return normalizeProjectLocale(window.navigator.language);
2605
+ }
2606
+ function useCalendarLocale(locale) {
2607
+ const contextLocale = react.useContext(CalendarLocaleContext);
2608
+ const controlledLocale = locale ?? contextLocale;
2609
+ const [browserLocale, setBrowserLocale] = react.useState(
2610
+ () => readBrowserLocale() ?? DEFAULT_CALENDAR_LOCALE
2611
+ );
2612
+ react.useEffect(() => {
2613
+ if (controlledLocale !== void 0 || typeof window === "undefined") {
2614
+ return;
2615
+ }
2616
+ const syncLocale = () => {
2617
+ setBrowserLocale(readBrowserLocale() ?? DEFAULT_CALENDAR_LOCALE);
2618
+ };
2619
+ const handleLocaleChange = (event) => {
2620
+ const customEvent = event;
2621
+ const nextLocale = normalizeProjectLocale(customEvent.detail);
2622
+ if (nextLocale) {
2623
+ setBrowserLocale(nextLocale);
2624
+ return;
2625
+ }
2626
+ syncLocale();
2627
+ };
2628
+ window.addEventListener(LOCALE_CHANGE_EVENT, handleLocaleChange);
2629
+ window.addEventListener("storage", syncLocale);
2630
+ return () => {
2631
+ window.removeEventListener(LOCALE_CHANGE_EVENT, handleLocaleChange);
2632
+ window.removeEventListener("storage", syncLocale);
2633
+ };
2634
+ }, [controlledLocale]);
2635
+ return react.useMemo(
2636
+ () => normalizeProjectLocale(controlledLocale) ?? browserLocale,
2637
+ [browserLocale, controlledLocale]
2638
+ );
2639
+ }
2640
+ function resolveDayCellStyle(day) {
2641
+ if (day.isDisabled) {
2642
+ return [calendarDropdownMetrics.dayCell];
2643
+ }
2644
+ if (day.isSelected) {
2645
+ return [calendarDropdownMetrics.dayCell, calendarDropdownMetrics.dayCellSelected];
2646
+ }
2647
+ return [
2648
+ calendarDropdownMetrics.dayCell,
2649
+ day.inCurrentMonth && day.isPast && !day.isToday ? calendarDropdownMetrics.dayCellPast : null
2650
+ ];
2651
+ }
2652
+ function resolveDayLabelStyle(day) {
2653
+ if (day.isDisabled) {
2654
+ return [calendarDropdownMetrics.dayLabel, calendarDropdownMetrics.dayLabelDisabled];
2655
+ }
2656
+ if (day.isSelected) {
2657
+ return [calendarDropdownMetrics.dayLabel, calendarDropdownMetrics.dayLabelSelected];
2658
+ }
2659
+ if (!day.inCurrentMonth) {
2660
+ return [calendarDropdownMetrics.dayLabel, calendarDropdownMetrics.dayLabelOutsideMonth];
2661
+ }
2662
+ if (day.isToday) {
2663
+ return [calendarDropdownMetrics.dayLabel, calendarDropdownMetrics.dayLabelToday];
2664
+ }
2665
+ if (day.isPast) {
2666
+ return [calendarDropdownMetrics.dayLabel, calendarDropdownMetrics.dayLabelMuted];
2667
+ }
2668
+ return [calendarDropdownMetrics.dayLabel, calendarDropdownMetrics.dayLabelFuture];
2669
+ }
2670
+ function Calendar(props) {
2671
+ const rangeProps = props.mode === "range" ? props : null;
2672
+ const singleProps = props.mode === "range" ? null : props;
2673
+ const {
2674
+ viewDate,
2675
+ defaultViewDate,
2676
+ onViewDateChange,
2677
+ locale,
2678
+ labels,
2679
+ minDate,
2680
+ maxDate,
2681
+ today = /* @__PURE__ */ new Date(),
2682
+ style,
2683
+ className
2684
+ } = props;
2685
+ const rootRef = react.useRef(null);
2686
+ const [internalViewDate, setInternalViewDate] = react.useState(() => {
2687
+ if (defaultViewDate) {
2688
+ return defaultViewDate;
2689
+ }
2690
+ if (rangeProps) {
2691
+ return rangeProps.value?.start ?? today;
2692
+ }
2693
+ return singleProps?.value ?? today;
2694
+ });
2695
+ const activeViewDate = viewDate ?? internalViewDate;
2696
+ const [pickerView, setPickerView] = react.useState("days");
2697
+ const [pickerYear, setPickerYear] = react.useState(() => activeViewDate.getFullYear());
2698
+ const [yearPageStart, setYearPageStart] = react.useState(
2699
+ () => getYearPageStart(activeViewDate.getFullYear(), CALENDAR_YEARS_PER_PAGE)
2700
+ );
2701
+ const customWeekdayLabels = labels?.weekdays;
2702
+ const formatMonthYearLabel = labels?.formatMonthYear;
2703
+ const resolvedLocale = useCalendarLocale(locale);
2704
+ const translations = react.useMemo(
2705
+ () => getCalendarTranslations(resolvedLocale),
2706
+ [resolvedLocale]
2707
+ );
2708
+ useApplyWebClassName(rootRef, className);
2709
+ const weekdayLabels = react.useMemo(
2710
+ () => customWeekdayLabels ?? resolveWeekdayLabels(resolvedLocale),
2711
+ [customWeekdayLabels, resolvedLocale]
2712
+ );
2713
+ const monthLabels = react.useMemo(() => getCalendarMonthLabels(resolvedLocale), [resolvedLocale]);
2714
+ const monthYearLabel = react.useMemo(() => {
2715
+ if (formatMonthYearLabel) {
2716
+ return formatMonthYearLabel(activeViewDate);
2717
+ }
2718
+ return formatLocalizedCalendarMonthYear(activeViewDate, resolvedLocale);
2719
+ }, [activeViewDate, formatMonthYearLabel, resolvedLocale]);
2720
+ const headerLabel = react.useMemo(() => {
2721
+ if (pickerView === "months") {
2722
+ return formatCalendarYear(pickerYear, resolvedLocale);
2723
+ }
2724
+ if (pickerView === "years") {
2725
+ const endYear = yearPageStart + CALENDAR_YEARS_PER_PAGE - 1;
2726
+ return `${yearPageStart} \u2013 ${endYear}`;
2727
+ }
2728
+ return monthYearLabel;
2729
+ }, [monthYearLabel, pickerView, pickerYear, resolvedLocale, yearPageStart]);
2730
+ const days = react.useMemo(
2731
+ () => buildCalendarDays({
2732
+ viewDate: activeViewDate,
2733
+ selectedDate: singleProps?.value,
2734
+ selectedRange: rangeProps?.value,
2735
+ today,
2736
+ minDate,
2737
+ maxDate
2738
+ }),
2739
+ [activeViewDate, maxDate, minDate, rangeProps?.value, singleProps?.value, today]
2740
+ );
2741
+ const yearOptions = react.useMemo(
2742
+ () => Array.from({ length: CALENDAR_YEARS_PER_PAGE }, (_, index) => yearPageStart + index),
2743
+ [yearPageStart]
2744
+ );
2745
+ function getLabel(key) {
2746
+ return labels?.[key] ?? translations[key];
2747
+ }
2748
+ function setViewDate(nextDate) {
2749
+ if (viewDate === void 0) {
2750
+ setInternalViewDate(nextDate);
2751
+ }
2752
+ onViewDateChange?.(nextDate);
2753
+ }
2754
+ function handlePrevious() {
2755
+ if (pickerView === "months") {
2756
+ setPickerYear((year) => year - 1);
2757
+ return;
2758
+ }
2759
+ if (pickerView === "years") {
2760
+ setYearPageStart((start) => start - CALENDAR_YEARS_PER_PAGE);
2761
+ return;
2762
+ }
2763
+ setViewDate(addMonths(activeViewDate, -1));
2764
+ }
2765
+ function handleNext() {
2766
+ if (pickerView === "months") {
2767
+ setPickerYear((year) => year + 1);
2768
+ return;
2769
+ }
2770
+ if (pickerView === "years") {
2771
+ setYearPageStart((start) => start + CALENDAR_YEARS_PER_PAGE);
2772
+ return;
2773
+ }
2774
+ setViewDate(addMonths(activeViewDate, 1));
2775
+ }
2776
+ function handleHeaderPress() {
2777
+ if (pickerView === "days") {
2778
+ setPickerYear(activeViewDate.getFullYear());
2779
+ setPickerView("months");
2780
+ return;
2781
+ }
2782
+ if (pickerView === "months") {
2783
+ setYearPageStart(getYearPageStart(pickerYear, CALENDAR_YEARS_PER_PAGE));
2784
+ setPickerView("years");
2785
+ }
2786
+ }
2787
+ function handleMonthSelect(month) {
2788
+ setViewDate(new Date(pickerYear, month, 1));
2789
+ setPickerView("days");
2790
+ }
2791
+ function handleYearSelect(year) {
2792
+ setPickerYear(year);
2793
+ setPickerView("months");
2794
+ }
2795
+ function handleDayPress(date) {
2796
+ if (rangeProps) {
2797
+ rangeProps.onChange?.(selectRangeDate(date, rangeProps.value));
2798
+ return;
2799
+ }
2800
+ singleProps?.onChange?.(date);
2801
+ }
2802
+ const headerIsPressable = pickerView === "days" || pickerView === "months";
2803
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: rootRef, style: [calendarDropdownMetrics.panel, style], children: [
2804
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: calendarDropdownMetrics.header, children: [
2805
+ /* @__PURE__ */ jsxRuntime.jsx(
2806
+ reactNative.Pressable,
2807
+ {
2808
+ accessibilityRole: "button",
2809
+ accessibilityLabel: pickerView === "months" ? getLabel("previousYear") : pickerView === "years" ? getLabel("previousYears") : getLabel("previousMonth"),
2810
+ onPress: handlePrevious,
2811
+ style: calendarDropdownMetrics.navButton,
2812
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftIcon, { size: CALENDAR_NAV_ICON_SIZE, color: CALENDAR_NAV_ICON_COLOR })
1250
2813
  }
1251
2814
  ),
1252
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.phoneColumn, children: [
1253
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [phoneFieldStyles.outline, styles5.phoneOutline], children: /* @__PURE__ */ jsxRuntime.jsx(
1254
- reactNative.View,
1255
- {
1256
- style: [
1257
- inputFieldMetrics.container,
1258
- phoneFieldStyles.container,
1259
- styles5.phoneField
1260
- ],
1261
- children: /* @__PURE__ */ jsxRuntime.jsx(
1262
- reactNative.TextInput,
1263
- {
1264
- ref: inputRef,
1265
- style: [
1266
- inputFieldMetrics.input,
1267
- phoneFieldStyles.text,
1268
- style
1269
- ],
1270
- keyboardType: "phone-pad",
1271
- placeholderTextColor: phoneFieldStyles.placeholder,
1272
- editable,
1273
- onFocus: handleFocus,
1274
- onBlur: handleBlur,
1275
- accessibilityState: { disabled: isDisabled },
1276
- ...props
1277
- }
1278
- )
1279
- }
1280
- ) }),
1281
- helperMessage ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { ref: helperRef, style: error ? styles5.error : styles5.hint, children: helperMessage }) : null
1282
- ] })
1283
- ] })
2815
+ headerIsPressable ? /* @__PURE__ */ jsxRuntime.jsx(
2816
+ reactNative.Pressable,
2817
+ {
2818
+ accessibilityRole: "button",
2819
+ accessibilityLabel: pickerView === "days" ? getLabel("selectMonth") : getLabel("selectYear"),
2820
+ onPress: handleHeaderPress,
2821
+ style: calendarDropdownMetrics.headerTitlePressable,
2822
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: calendarDropdownMetrics.headerTitle, children: headerLabel })
2823
+ }
2824
+ ) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: calendarDropdownMetrics.headerTitlePressable, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: calendarDropdownMetrics.headerTitle, children: headerLabel }) }),
2825
+ /* @__PURE__ */ jsxRuntime.jsx(
2826
+ reactNative.Pressable,
2827
+ {
2828
+ accessibilityRole: "button",
2829
+ accessibilityLabel: pickerView === "months" ? getLabel("nextYear") : pickerView === "years" ? getLabel("nextYears") : getLabel("nextMonth"),
2830
+ onPress: handleNext,
2831
+ style: calendarDropdownMetrics.navButton,
2832
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRightIcon, { size: CALENDAR_NAV_ICON_SIZE, color: CALENDAR_NAV_ICON_COLOR })
2833
+ }
2834
+ )
2835
+ ] }),
2836
+ pickerView === "days" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2837
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: calendarDropdownMetrics.weekdayRow, children: weekdayLabels.map((weekday) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: calendarDropdownMetrics.weekdayCell, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: calendarDropdownMetrics.weekdayLabel, children: weekday }) }, weekday)) }),
2838
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: calendarDropdownMetrics.dayGrid, children: days.map((day) => /* @__PURE__ */ jsxRuntime.jsxs(
2839
+ reactNative.Pressable,
2840
+ {
2841
+ accessibilityRole: "button",
2842
+ accessibilityState: {
2843
+ selected: day.isSelected,
2844
+ disabled: day.isDisabled
2845
+ },
2846
+ disabled: day.isDisabled,
2847
+ onPress: () => handleDayPress(day.date),
2848
+ style: resolveDayCellStyle(day),
2849
+ children: [
2850
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: resolveDayLabelStyle(day), children: day.date.getDate() }),
2851
+ day.isToday && day.inCurrentMonth && !day.isSelected ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: calendarDropdownMetrics.todayDot }) : null
2852
+ ]
2853
+ },
2854
+ day.date.toISOString()
2855
+ )) })
2856
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: calendarDropdownMetrics.pickerBody, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: calendarDropdownMetrics.pickerGrid, children: pickerView === "months" ? monthLabels.map((monthLabel, month) => {
2857
+ const isSelected = activeViewDate.getFullYear() === pickerYear && activeViewDate.getMonth() === month;
2858
+ const isCurrent = today.getFullYear() === pickerYear && today.getMonth() === month;
2859
+ const disabled = isMonthDisabled(pickerYear, month, minDate, maxDate);
2860
+ return /* @__PURE__ */ jsxRuntime.jsx(
2861
+ reactNative.Pressable,
2862
+ {
2863
+ accessibilityRole: "button",
2864
+ accessibilityState: { selected: isSelected, disabled },
2865
+ disabled,
2866
+ onPress: () => handleMonthSelect(month),
2867
+ style: [
2868
+ calendarDropdownMetrics.pickerCell,
2869
+ isSelected ? calendarDropdownMetrics.pickerCellSelected : null
2870
+ ],
2871
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2872
+ reactNative.Text,
2873
+ {
2874
+ style: [
2875
+ calendarDropdownMetrics.pickerLabel,
2876
+ isSelected ? calendarDropdownMetrics.pickerLabelSelected : null,
2877
+ !isSelected && isCurrent ? calendarDropdownMetrics.pickerLabelCurrent : null,
2878
+ disabled ? calendarDropdownMetrics.pickerLabelDisabled : null
2879
+ ],
2880
+ children: monthLabel
2881
+ }
2882
+ )
2883
+ },
2884
+ monthLabel
2885
+ );
2886
+ }) : yearOptions.map((year) => {
2887
+ const isSelected = activeViewDate.getFullYear() === year;
2888
+ const isCurrent = today.getFullYear() === year;
2889
+ const disabled = isYearDisabled(year, minDate, maxDate);
2890
+ return /* @__PURE__ */ jsxRuntime.jsx(
2891
+ reactNative.Pressable,
2892
+ {
2893
+ accessibilityRole: "button",
2894
+ accessibilityState: { selected: isSelected, disabled },
2895
+ disabled,
2896
+ onPress: () => handleYearSelect(year),
2897
+ style: [
2898
+ calendarDropdownMetrics.pickerCell,
2899
+ isSelected ? calendarDropdownMetrics.pickerCellSelected : null
2900
+ ],
2901
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2902
+ reactNative.Text,
2903
+ {
2904
+ style: [
2905
+ calendarDropdownMetrics.pickerLabel,
2906
+ isSelected ? calendarDropdownMetrics.pickerLabelSelected : null,
2907
+ !isSelected && isCurrent ? calendarDropdownMetrics.pickerLabelCurrent : null,
2908
+ disabled ? calendarDropdownMetrics.pickerLabelDisabled : null
2909
+ ],
2910
+ children: year
2911
+ }
2912
+ )
2913
+ },
2914
+ year
2915
+ );
2916
+ }) }) })
1284
2917
  ] });
1285
2918
  }
1286
- var styles5 = reactNative.StyleSheet.create({
2919
+
2920
+ // src/icons/calendarIconPaths.ts
2921
+ var CALENDAR_ICON_BODY_PATH = "M6.66667 1.6665V4.99984M13.3333 1.6665V4.99984M2.5 8.33317H17.5M4.16667 3.33317H15.8333C16.7538 3.33317 17.5 4.07936 17.5 4.99984V16.6665C17.5 17.587 16.7538 18.3332 15.8333 18.3332H4.16667C3.24619 18.3332 2.5 17.587 2.5 16.6665V4.99984C2.5 4.07936 3.24619 3.33317 4.16667 3.33317Z";
2922
+ function CalendarIcon({
2923
+ size = 20,
2924
+ color = "#C7CDD1",
2925
+ strokeWidth = 2
2926
+ }) {
2927
+ return /* @__PURE__ */ jsxRuntime.jsx(Svg5__default.default, { width: size, height: size, viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
2928
+ Svg5.Path,
2929
+ {
2930
+ d: CALENDAR_ICON_BODY_PATH,
2931
+ stroke: color,
2932
+ strokeWidth,
2933
+ strokeLinecap: "round",
2934
+ strokeLinejoin: "round"
2935
+ }
2936
+ ) });
2937
+ }
2938
+ function DatePicker(props) {
2939
+ const {
2940
+ label,
2941
+ placeholder,
2942
+ locale,
2943
+ labels,
2944
+ minDate,
2945
+ maxDate,
2946
+ today,
2947
+ viewDate,
2948
+ defaultViewDate,
2949
+ onViewDateChange,
2950
+ disabled = false,
2951
+ error,
2952
+ invalid,
2953
+ hint,
2954
+ containerStyle,
2955
+ className,
2956
+ fieldClassName,
2957
+ panelClassName,
2958
+ labelClassName,
2959
+ errorClassName,
2960
+ hintClassName,
2961
+ defaultOpen = false
2962
+ } = props;
2963
+ const mode = props.mode ?? "single";
2964
+ const closeOnRangeComplete = props.mode === "range" ? props.closeOnRangeComplete ?? true : false;
2965
+ const resolvedLocale = useCalendarLocale(locale);
2966
+ const translations = getCalendarTranslations(resolvedLocale);
2967
+ const resolvedPlaceholder = placeholder ?? (mode === "range" ? translations.rangePlaceholder : translations.datePlaceholder);
2968
+ const wrapperRef = react.useRef(null);
2969
+ const triggerRef = react.useRef(null);
2970
+ const helperRef = react.useRef(null);
2971
+ const [open, setOpen] = react.useState(defaultOpen);
2972
+ const [anchor, setAnchor] = react.useState(null);
2973
+ useApplyWebClassName(wrapperRef, className);
2974
+ useApplyWebClassName(triggerRef, fieldClassName);
2975
+ useApplyWebClassName(helperRef, error ? errorClassName : hintClassName);
2976
+ react.useEffect(() => {
2977
+ if (!defaultOpen || disabled) {
2978
+ return;
2979
+ }
2980
+ const frame = requestAnimationFrame(() => {
2981
+ triggerRef.current?.measureInWindow((x, y, width, height) => {
2982
+ setAnchor({ x, y, width, height });
2983
+ setOpen(true);
2984
+ });
2985
+ });
2986
+ return () => cancelAnimationFrame(frame);
2987
+ }, [defaultOpen, disabled]);
2988
+ const hasError = Boolean(error) || Boolean(invalid);
2989
+ const fieldStyles = getSelectFieldStyles(
2990
+ resolveInputVisualState({
2991
+ error: hasError,
2992
+ disabled
2993
+ })
2994
+ );
2995
+ const hasValue = mode === "single" ? Boolean(props.value) : Boolean(props.mode === "range" && props.value?.start);
2996
+ const displayValue = (() => {
2997
+ if (props.mode === "range") {
2998
+ if (!props.value?.start) {
2999
+ return resolvedPlaceholder;
3000
+ }
3001
+ return props.formatRange?.(props.value) ?? formatCalendarDateRange(props.value, resolvedLocale, props.rangeSeparator);
3002
+ }
3003
+ if (props.value) {
3004
+ return props.formatDate?.(props.value) ?? formatCalendarDate(props.value, resolvedLocale);
3005
+ }
3006
+ return resolvedPlaceholder;
3007
+ })();
3008
+ const isPlaceholder = !hasValue;
3009
+ const valueTextStyle = isPlaceholder ? {
3010
+ ...calendarFieldPlaceholderTypography,
3011
+ color: disabled ? colors.stormGray300 : calendarFieldPlaceholderTypography.color
3012
+ } : {
3013
+ ...calendarFieldTypography,
3014
+ color: fieldStyles.value.color
3015
+ };
3016
+ const helperMessage = error ?? hint;
3017
+ const panelWidth = CALENDAR_PANEL_WIDTH;
3018
+ const panelLeft = anchor ? resolveCalendarDropdownLeft(anchor, panelWidth) : 0;
3019
+ const dropdownTop = anchor ? resolveCalendarDropdownTop(anchor) : 0;
3020
+ const resolvedDefaultViewDate = defaultViewDate ?? (props.mode === "range" ? props.value?.start : props.value) ?? today;
3021
+ function closePicker() {
3022
+ setOpen(false);
3023
+ setAnchor(null);
3024
+ }
3025
+ function openPicker() {
3026
+ if (disabled) {
3027
+ return;
3028
+ }
3029
+ triggerRef.current?.measureInWindow((x, y, width, height) => {
3030
+ setAnchor({ x, y, width, height });
3031
+ setOpen(true);
3032
+ });
3033
+ }
3034
+ function handleSingleSelect(date) {
3035
+ if (props.mode === "range") {
3036
+ return;
3037
+ }
3038
+ props.onChange?.(date);
3039
+ closePicker();
3040
+ }
3041
+ function handleRangeSelect(range) {
3042
+ if (props.mode !== "range") {
3043
+ return;
3044
+ }
3045
+ props.onChange?.(range);
3046
+ if (closeOnRangeComplete && range.end) {
3047
+ closePicker();
3048
+ }
3049
+ }
3050
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ref: wrapperRef, style: [styles7.wrapper, containerStyle], children: [
3051
+ label ? /* @__PURE__ */ jsxRuntime.jsx(Label, { disabled, style: styles7.label, className: labelClassName, children: label }) : null,
3052
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [fieldStyles.outline, styles7.fieldOutline], children: /* @__PURE__ */ jsxRuntime.jsxs(
3053
+ reactNative.Pressable,
3054
+ {
3055
+ ref: triggerRef,
3056
+ accessibilityRole: "button",
3057
+ accessibilityState: { disabled, expanded: open },
3058
+ disabled,
3059
+ onPress: openPicker,
3060
+ style: [calendarFieldMetrics.container, fieldStyles.container],
3061
+ children: [
3062
+ /* @__PURE__ */ jsxRuntime.jsx(
3063
+ reactNative.Text,
3064
+ {
3065
+ style: [calendarFieldMetrics.value, valueTextStyle],
3066
+ numberOfLines: 1,
3067
+ children: displayValue
3068
+ }
3069
+ ),
3070
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.iconSlot, children: /* @__PURE__ */ jsxRuntime.jsx(
3071
+ CalendarIcon,
3072
+ {
3073
+ size: CALENDAR_FIELD_ICON_SIZE,
3074
+ color: disabled ? colors.stormGray200 : colors.stormGray150
3075
+ }
3076
+ ) })
3077
+ ]
3078
+ }
3079
+ ) }),
3080
+ helperMessage ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { ref: helperRef, style: error ? styles7.error : styles7.hint, children: helperMessage }) : null,
3081
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible: open, transparent: true, animationType: "fade", onRequestClose: closePicker, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.modalRoot, children: [
3082
+ /* @__PURE__ */ jsxRuntime.jsx(
3083
+ reactNative.Pressable,
3084
+ {
3085
+ style: styles7.backdrop,
3086
+ onPress: closePicker,
3087
+ accessibilityRole: "button",
3088
+ accessibilityLabel: labels?.closeCalendar ?? translations.closeCalendar
3089
+ }
3090
+ ),
3091
+ anchor ? /* @__PURE__ */ jsxRuntime.jsx(
3092
+ reactNative.View,
3093
+ {
3094
+ style: [
3095
+ styles7.panelPosition,
3096
+ {
3097
+ top: dropdownTop,
3098
+ left: panelLeft,
3099
+ width: panelWidth,
3100
+ height: CALENDAR_PANEL_HEIGHT
3101
+ }
3102
+ ],
3103
+ children: props.mode === "range" ? /* @__PURE__ */ jsxRuntime.jsx(
3104
+ Calendar,
3105
+ {
3106
+ mode: "range",
3107
+ value: props.value,
3108
+ onChange: handleRangeSelect,
3109
+ viewDate,
3110
+ defaultViewDate: resolvedDefaultViewDate,
3111
+ onViewDateChange,
3112
+ locale: resolvedLocale,
3113
+ labels,
3114
+ minDate,
3115
+ maxDate,
3116
+ today,
3117
+ className: panelClassName
3118
+ }
3119
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
3120
+ Calendar,
3121
+ {
3122
+ mode: "single",
3123
+ value: props.value,
3124
+ onChange: handleSingleSelect,
3125
+ viewDate,
3126
+ defaultViewDate: resolvedDefaultViewDate,
3127
+ onViewDateChange,
3128
+ locale: resolvedLocale,
3129
+ labels,
3130
+ minDate,
3131
+ maxDate,
3132
+ today,
3133
+ className: panelClassName
3134
+ }
3135
+ )
3136
+ }
3137
+ ) : null
3138
+ ] }) })
3139
+ ] });
3140
+ }
3141
+ var styles7 = reactNative.StyleSheet.create({
1287
3142
  wrapper: {
1288
- width: "100%",
1289
- gap: spacing.sm
1290
- },
1291
- row: {
1292
- flexDirection: "row",
1293
- alignItems: "flex-start",
1294
- gap: INPUT_ICON_GAP
3143
+ width: CALENDAR_FIELD_WIDTH,
3144
+ maxWidth: "100%",
3145
+ gap: SELECT_LABEL_GAP
1295
3146
  },
1296
- countrySelector: {
1297
- flexShrink: 0,
3147
+ fieldOutline: {
3148
+ width: CALENDAR_FIELD_WIDTH,
3149
+ maxWidth: "100%",
1298
3150
  alignSelf: "flex-start"
1299
3151
  },
1300
- phoneColumn: {
1301
- flex: 1,
1302
- gap: spacing.sm
3152
+ label: {
3153
+ ...calendarLabelTypography
1303
3154
  },
1304
- phoneOutline: {
1305
- width: "100%"
3155
+ iconSlot: {
3156
+ width: CALENDAR_FIELD_ICON_SIZE,
3157
+ height: CALENDAR_FIELD_ICON_SIZE,
3158
+ alignItems: "center",
3159
+ justifyContent: "center",
3160
+ flexShrink: 0
1306
3161
  },
1307
- phoneField: {
3162
+ modalRoot: {
1308
3163
  flex: 1
1309
3164
  },
3165
+ backdrop: {
3166
+ ...reactNative.StyleSheet.absoluteFillObject,
3167
+ backgroundColor: colors.transparent
3168
+ },
3169
+ panelPosition: {
3170
+ position: "absolute",
3171
+ zIndex: 1
3172
+ },
1310
3173
  error: {
1311
3174
  fontSize: 12,
1312
- lineHeight: 16,
3175
+ lineHeight: 12,
1313
3176
  color: colors.inputError
1314
3177
  },
1315
3178
  hint: {
1316
3179
  fontSize: 12,
1317
- lineHeight: 16,
3180
+ lineHeight: 12,
1318
3181
  color: colors.stormGray300
1319
3182
  }
1320
3183
  });
@@ -1326,8 +3189,8 @@ function LockIcon({
1326
3189
  color = "#c7cdd1",
1327
3190
  strokeWidth = 2
1328
3191
  }) {
1329
- return /* @__PURE__ */ jsxRuntime.jsx(Svg__default.default, { width: size, height: size, viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
1330
- Svg.Path,
3192
+ return /* @__PURE__ */ jsxRuntime.jsx(Svg5__default.default, { width: size, height: size, viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
3193
+ Svg5.Path,
1331
3194
  {
1332
3195
  d: LOCK_ICON_PATH,
1333
3196
  stroke: color,
@@ -1339,13 +3202,19 @@ function LockIcon({
1339
3202
  }
1340
3203
 
1341
3204
  exports.Button = Button;
3205
+ exports.Calendar = Calendar;
3206
+ exports.CalendarIcon = CalendarIcon;
3207
+ exports.CalendarLocaleProvider = CalendarLocaleProvider;
1342
3208
  exports.CountryCodeSelector = CountryCodeSelector;
3209
+ exports.DatePicker = DatePicker;
1343
3210
  exports.Input = Input;
1344
3211
  exports.Label = Label;
1345
3212
  exports.LockIcon = LockIcon;
1346
3213
  exports.PhoneInput = PhoneInput;
3214
+ exports.Select = Select;
1347
3215
  exports.brand = brand;
1348
3216
  exports.buttonTypography = buttonTypography;
3217
+ exports.calendarTranslations = calendarTranslations;
1349
3218
  exports.colors = colors;
1350
3219
  exports.countries = countries;
1351
3220
  exports.defaultCountry = defaultCountry;
@@ -1354,12 +3223,18 @@ exports.findCountry = findCountry;
1354
3223
  exports.fontSize = fontSize;
1355
3224
  exports.fontWeight = fontWeight;
1356
3225
  exports.fonts = fonts;
3226
+ exports.formatLocalizedCalendarMonthYear = formatLocalizedCalendarMonthYear;
3227
+ exports.getCalendarMonthLabels = getCalendarMonthLabels;
3228
+ exports.getCalendarTranslations = getCalendarTranslations;
1357
3229
  exports.labelTypography = labelTypography;
1358
3230
  exports.navy = navy;
1359
3231
  exports.radii = radii;
3232
+ exports.resolveCalendarLocale = resolveCalendarLocale;
3233
+ exports.resolveWeekdayLabels = resolveWeekdayLabels;
1360
3234
  exports.rubyRed = rubyRed;
1361
3235
  exports.shadows = shadows;
1362
3236
  exports.spacing = spacing;
1363
3237
  exports.stormGray = stormGray;
3238
+ exports.useCalendarLocale = useCalendarLocale;
1364
3239
  //# sourceMappingURL=index.cjs.map
1365
3240
  //# sourceMappingURL=index.cjs.map