@devtravelcode/widget-native 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/LICENSE +18 -0
  2. package/dist/App.d.ts +10 -0
  3. package/dist/App.js +192 -0
  4. package/dist/TravelHubWidget.d.ts +12 -0
  5. package/dist/TravelHubWidget.js +58 -0
  6. package/dist/components/ErrorBoundary.d.ts +18 -0
  7. package/dist/components/ErrorBoundary.js +63 -0
  8. package/dist/components/ui/Modal.d.ts +11 -0
  9. package/dist/components/ui/Modal.js +66 -0
  10. package/dist/components/ui/Skeleton.d.ts +8 -0
  11. package/dist/components/ui/Skeleton.js +33 -0
  12. package/dist/components/ui/badge.d.ts +11 -0
  13. package/dist/components/ui/badge.js +54 -0
  14. package/dist/components/ui/button.d.ts +14 -0
  15. package/dist/components/ui/button.js +101 -0
  16. package/dist/components/ui/card.d.ts +16 -0
  17. package/dist/components/ui/card.js +51 -0
  18. package/dist/components/ui/input.d.ts +8 -0
  19. package/dist/components/ui/input.js +61 -0
  20. package/dist/components/ui/label.d.ts +8 -0
  21. package/dist/components/ui/label.js +17 -0
  22. package/dist/context/WidgetContext.d.ts +29 -0
  23. package/dist/context/WidgetContext.js +64 -0
  24. package/dist/icons/CarIcon.d.ts +4 -0
  25. package/dist/icons/CarIcon.js +3 -0
  26. package/dist/icons/CloseIcon.d.ts +7 -0
  27. package/dist/icons/CloseIcon.js +3 -0
  28. package/dist/icons/FlightIcon.d.ts +4 -0
  29. package/dist/icons/FlightIcon.js +3 -0
  30. package/dist/icons/HotelIcon.d.ts +4 -0
  31. package/dist/icons/HotelIcon.js +3 -0
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.js +1 -0
  34. package/dist/lib/utils.d.ts +2 -0
  35. package/dist/lib/utils.js +3 -0
  36. package/dist/locales/widgetTranslations.d.ts +1 -0
  37. package/dist/locales/widgetTranslations.js +732 -0
  38. package/dist/modules/hooks/useAirportSearch.d.ts +21 -0
  39. package/dist/modules/hooks/useAirportSearch.js +48 -0
  40. package/dist/modules/hooks/useCarOffers.d.ts +7 -0
  41. package/dist/modules/hooks/useCarOffers.js +47 -0
  42. package/dist/modules/hooks/useCountriesByCodes.d.ts +4 -0
  43. package/dist/modules/hooks/useCountriesByCodes.js +60 -0
  44. package/dist/modules/hooks/useCountriesSearch.d.ts +11 -0
  45. package/dist/modules/hooks/useCountriesSearch.js +55 -0
  46. package/dist/modules/hooks/useDefaultHotelLocation.d.ts +7 -0
  47. package/dist/modules/hooks/useDefaultHotelLocation.js +44 -0
  48. package/dist/modules/hooks/useDetectNationality.d.ts +1 -0
  49. package/dist/modules/hooks/useDetectNationality.js +20 -0
  50. package/dist/modules/hooks/useFlightSearch.d.ts +20 -0
  51. package/dist/modules/hooks/useFlightSearch.js +243 -0
  52. package/dist/modules/hooks/useHotelDetails.d.ts +7 -0
  53. package/dist/modules/hooks/useHotelDetails.js +80 -0
  54. package/dist/modules/hooks/useHotelLocations.d.ts +5 -0
  55. package/dist/modules/hooks/useHotelLocations.js +42 -0
  56. package/dist/modules/hooks/useHotelMapData.d.ts +12 -0
  57. package/dist/modules/hooks/useHotelMapData.js +78 -0
  58. package/dist/modules/hooks/useHotelOffers.d.ts +11 -0
  59. package/dist/modules/hooks/useHotelOffers.js +122 -0
  60. package/dist/modules/hooks/useHotelSearchSSE.d.ts +18 -0
  61. package/dist/modules/hooks/useHotelSearchSSE.js +411 -0
  62. package/dist/modules/hooks/useSearchExpiration.d.ts +6 -0
  63. package/dist/modules/hooks/useSearchExpiration.js +35 -0
  64. package/dist/modules/hooks/useTranslationsHub.d.ts +5 -0
  65. package/dist/modules/hooks/useTranslationsHub.js +48 -0
  66. package/dist/modules/navbar/Navbar.d.ts +8 -0
  67. package/dist/modules/navbar/Navbar.js +69 -0
  68. package/dist/modules/search-form/AgeSelector.d.ts +9 -0
  69. package/dist/modules/search-form/AgeSelector.js +109 -0
  70. package/dist/modules/search-form/CustomSelect.d.ts +14 -0
  71. package/dist/modules/search-form/CustomSelect.js +94 -0
  72. package/dist/modules/search-form/FiltersSkeleton.d.ts +6 -0
  73. package/dist/modules/search-form/FiltersSkeleton.js +35 -0
  74. package/dist/modules/search-form/NationalityInput.d.ts +18 -0
  75. package/dist/modules/search-form/NationalityInput.js +172 -0
  76. package/dist/modules/search-form/Pagination.d.ts +8 -0
  77. package/dist/modules/search-form/Pagination.js +84 -0
  78. package/dist/modules/search-form/SearchExpiredPopup.d.ts +10 -0
  79. package/dist/modules/search-form/SearchExpiredPopup.js +91 -0
  80. package/dist/modules/search-form/SkeletonWidget.d.ts +2 -0
  81. package/dist/modules/search-form/SkeletonWidget.js +33 -0
  82. package/dist/modules/search-form/TimePicker.d.ts +9 -0
  83. package/dist/modules/search-form/TimePicker.js +182 -0
  84. package/dist/modules/search-form/cars/CarEmptyState.d.ts +6 -0
  85. package/dist/modules/search-form/cars/CarEmptyState.js +42 -0
  86. package/dist/modules/search-form/cars/CarFiltersSkeleton.d.ts +6 -0
  87. package/dist/modules/search-form/cars/CarFiltersSkeleton.js +35 -0
  88. package/dist/modules/search-form/cars/CarOffersList.d.ts +43 -0
  89. package/dist/modules/search-form/cars/CarOffersList.js +214 -0
  90. package/dist/modules/search-form/cars/CarResults.d.ts +25 -0
  91. package/dist/modules/search-form/cars/CarResults.js +116 -0
  92. package/dist/modules/search-form/cars/CarSearchForm.d.ts +19 -0
  93. package/dist/modules/search-form/cars/CarSearchForm.js +173 -0
  94. package/dist/modules/search-form/cars/CarSkeleton.d.ts +6 -0
  95. package/dist/modules/search-form/cars/CarSkeleton.js +46 -0
  96. package/dist/modules/search-form/cars/CarTopOffersSlider.d.ts +20 -0
  97. package/dist/modules/search-form/cars/CarTopOffersSlider.js +100 -0
  98. package/dist/modules/search-form/cars/car-filters/CarFilters.d.ts +8 -0
  99. package/dist/modules/search-form/cars/car-filters/CarFilters.js +318 -0
  100. package/dist/modules/search-form/cars/car-filters/FilterSection.d.ts +9 -0
  101. package/dist/modules/search-form/cars/car-filters/FilterSection.js +30 -0
  102. package/dist/modules/search-form/cars/car-filters/index.d.ts +6 -0
  103. package/dist/modules/search-form/cars/car-filters/index.js +3 -0
  104. package/dist/modules/search-form/cars/car-filters/useCarFilters.d.ts +24 -0
  105. package/dist/modules/search-form/cars/car-filters/useCarFilters.js +121 -0
  106. package/dist/modules/search-form/flight/AirlinesSelector.d.ts +8 -0
  107. package/dist/modules/search-form/flight/AirlinesSelector.js +235 -0
  108. package/dist/modules/search-form/flight/CabinClassSelector.d.ts +9 -0
  109. package/dist/modules/search-form/flight/CabinClassSelector.js +63 -0
  110. package/dist/modules/search-form/flight/DatePicker.d.ts +12 -0
  111. package/dist/modules/search-form/flight/DatePicker.js +536 -0
  112. package/dist/modules/search-form/flight/FlightSearchForm.d.ts +18 -0
  113. package/dist/modules/search-form/flight/FlightSearchForm.js +147 -0
  114. package/dist/modules/search-form/flight/LocationInput.d.ts +11 -0
  115. package/dist/modules/search-form/flight/LocationInput.js +294 -0
  116. package/dist/modules/search-form/flight/PassengersPicker.d.ts +16 -0
  117. package/dist/modules/search-form/flight/PassengersPicker.js +203 -0
  118. package/dist/modules/search-form/flight/SwitchLocationBtn.d.ts +9 -0
  119. package/dist/modules/search-form/flight/SwitchLocationBtn.js +48 -0
  120. package/dist/modules/search-form/flight/flight-filters/FiltersContent.d.ts +21 -0
  121. package/dist/modules/search-form/flight/flight-filters/FiltersContent.js +145 -0
  122. package/dist/modules/search-form/flight/flight-filters/FlightFiltersDynamic.d.ts +11 -0
  123. package/dist/modules/search-form/flight/flight-filters/FlightFiltersDynamic.js +213 -0
  124. package/dist/modules/search-form/flight/flight-filters/RangeBlock.d.ts +22 -0
  125. package/dist/modules/search-form/flight/flight-filters/RangeBlock.js +164 -0
  126. package/dist/modules/search-form/flight/flight-filters/index.d.ts +1 -0
  127. package/dist/modules/search-form/flight/flight-filters/index.js +1 -0
  128. package/dist/modules/search-form/flight/flight-results/FlightAdditionalInfo.d.ts +16 -0
  129. package/dist/modules/search-form/flight/flight-results/FlightAdditionalInfo.js +229 -0
  130. package/dist/modules/search-form/flight/flight-results/FlightBaggageToggle.d.ts +15 -0
  131. package/dist/modules/search-form/flight/flight-results/FlightBaggageToggle.js +110 -0
  132. package/dist/modules/search-form/flight/flight-results/FlightCard.d.ts +14 -0
  133. package/dist/modules/search-form/flight/flight-results/FlightCard.js +436 -0
  134. package/dist/modules/search-form/flight/flight-results/FlightInfo.d.ts +12 -0
  135. package/dist/modules/search-form/flight/flight-results/FlightInfo.js +48 -0
  136. package/dist/modules/search-form/flight/flight-results/FlightPriceBlock.d.ts +11 -0
  137. package/dist/modules/search-form/flight/flight-results/FlightPriceBlock.js +36 -0
  138. package/dist/modules/search-form/flight/flight-results/FlightResults.d.ts +22 -0
  139. package/dist/modules/search-form/flight/flight-results/FlightResults.js +109 -0
  140. package/dist/modules/search-form/flight/flight-results/FlightSegments.d.ts +40 -0
  141. package/dist/modules/search-form/flight/flight-results/FlightSegments.js +162 -0
  142. package/dist/modules/search-form/flight/flight-results/FlightsSkeleton.d.ts +6 -0
  143. package/dist/modules/search-form/flight/flight-results/FlightsSkeleton.js +52 -0
  144. package/dist/modules/search-form/flight/flight-results/ProvidersLoader.d.ts +9 -0
  145. package/dist/modules/search-form/flight/flight-results/ProvidersLoader.js +242 -0
  146. package/dist/modules/search-form/hotel/DatePicker.d.ts +1 -0
  147. package/dist/modules/search-form/hotel/DatePicker.js +1 -0
  148. package/dist/modules/search-form/hotel/GuestSelector.d.ts +13 -0
  149. package/dist/modules/search-form/hotel/GuestSelector.js +272 -0
  150. package/dist/modules/search-form/hotel/GuestSelectorMobilePopup.d.ts +2 -0
  151. package/dist/modules/search-form/hotel/GuestSelectorMobilePopup.js +1 -0
  152. package/dist/modules/search-form/hotel/HotelLocation.d.ts +17 -0
  153. package/dist/modules/search-form/hotel/HotelLocation.js +178 -0
  154. package/dist/modules/search-form/hotel/HotelLocationMobilePopup.d.ts +2 -0
  155. package/dist/modules/search-form/hotel/HotelLocationMobilePopup.js +1 -0
  156. package/dist/modules/search-form/hotel/HotelResults.d.ts +21 -0
  157. package/dist/modules/search-form/hotel/HotelResults.js +333 -0
  158. package/dist/modules/search-form/hotel/HotelSearchForm.d.ts +13 -0
  159. package/dist/modules/search-form/hotel/HotelSearchForm.js +130 -0
  160. package/dist/modules/search-form/hotel/HotelsSkeleton.d.ts +6 -0
  161. package/dist/modules/search-form/hotel/HotelsSkeleton.js +40 -0
  162. package/dist/modules/search-form/hotel/PassengersPicker.d.ts +2 -0
  163. package/dist/modules/search-form/hotel/PassengersPicker.js +1 -0
  164. package/dist/modules/search-form/hotel/components/HotelCard.d.ts +11 -0
  165. package/dist/modules/search-form/hotel/components/HotelCard.js +180 -0
  166. package/dist/modules/search-form/hotel/components/HotelEmptyState.d.ts +6 -0
  167. package/dist/modules/search-form/hotel/components/HotelEmptyState.js +34 -0
  168. package/dist/modules/search-form/hotel/components/HotelGallery.d.ts +6 -0
  169. package/dist/modules/search-form/hotel/components/HotelGallery.js +77 -0
  170. package/dist/modules/search-form/hotel/components/HotelList.d.ts +9 -0
  171. package/dist/modules/search-form/hotel/components/HotelList.js +12 -0
  172. package/dist/modules/search-form/hotel/components/HotelMap.d.ts +26 -0
  173. package/dist/modules/search-form/hotel/components/HotelMap.js +707 -0
  174. package/dist/modules/search-form/hotel/components/HotelPin.d.ts +7 -0
  175. package/dist/modules/search-form/hotel/components/HotelPin.js +1 -0
  176. package/dist/modules/search-form/hotel/hotel-filters/HotelFilters.d.ts +29 -0
  177. package/dist/modules/search-form/hotel/hotel-filters/HotelFilters.js +339 -0
  178. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersBar.d.ts +3 -0
  179. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersBar.js +2 -0
  180. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersContent.d.ts +2 -0
  181. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersContent.js +1 -0
  182. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersDynamic.d.ts +2 -0
  183. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersDynamic.js +1 -0
  184. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersMobilePopup.d.ts +2 -0
  185. package/dist/modules/search-form/hotel/hotel-filters/HotelFiltersMobilePopup.js +1 -0
  186. package/dist/modules/search-form/hotel/hotel-filters/RangeBlock.d.ts +1 -0
  187. package/dist/modules/search-form/hotel/hotel-filters/RangeBlock.js +1 -0
  188. package/dist/modules/search-form/hotel/hotel-filters/index.d.ts +1 -0
  189. package/dist/modules/search-form/hotel/hotel-filters/index.js +1 -0
  190. package/dist/modules/tabs/FlightsTab.d.ts +1 -0
  191. package/dist/modules/tabs/FlightsTab.js +1 -0
  192. package/dist/modules/tabs/HotelsTab.d.ts +1 -0
  193. package/dist/modules/tabs/HotelsTab.js +1 -0
  194. package/dist/store/index.d.ts +22 -0
  195. package/dist/store/index.js +19 -0
  196. package/dist/theme/colors.d.ts +56 -0
  197. package/dist/theme/colors.js +56 -0
  198. package/dist/theme/formStyles.d.ts +76 -0
  199. package/dist/theme/formStyles.js +78 -0
  200. package/dist/utils/applyWidgetColors.d.ts +1 -0
  201. package/dist/utils/applyWidgetColors.js +2 -0
  202. package/dist/utils/dateTime.d.ts +2 -0
  203. package/dist/utils/dateTime.js +23 -0
  204. package/dist/utils/fetchSSE.d.ts +23 -0
  205. package/dist/utils/fetchSSE.js +104 -0
  206. package/dist/utils/getToken.d.ts +3 -0
  207. package/dist/utils/getToken.js +12 -0
  208. package/package.json +64 -0
@@ -0,0 +1,536 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState, useMemo, useCallback } from "react";
3
+ import { View, Text, TouchableOpacity, Modal, ScrollView, StyleSheet, Dimensions, } from "react-native";
4
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
5
+ import Svg, { Path } from "react-native-svg";
6
+ import { format, parse, isBefore, isAfter, startOfMonth, endOfMonth, startOfDay, addMonths, addDays, isSameDay, isWithinInterval, } from "date-fns";
7
+ import { ru, enUS } from "date-fns/locale";
8
+ import { useWidget } from "../../../context/WidgetContext";
9
+ import { Colors, Spacing, BorderRadius, FontSize } from "../../../theme/colors";
10
+ import { formStyles } from "../../../theme/formStyles";
11
+ import { CloseIcon } from "../../../store";
12
+ const SCREEN_WIDTH = Dimensions.get("window").width;
13
+ const DAY_SIZE = Math.floor((SCREEN_WIDTH - Spacing.lg * 2) / 7);
14
+ const ChevronLeft = ({ color = Colors.textTertiary }) => (_jsx(Svg, { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", children: _jsx(Path, { d: "M4 7.5 9.5 2l1.25 1.25L6 8l4.75 4.75L9.5 14 4 8.5z", fill: color }) }));
15
+ const ChevronRight = ({ color = Colors.textTertiary }) => (_jsx(Svg, { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", children: _jsx(Path, { d: "M12 8.5 6.5 14l-1.25-1.25L10 8 5.25 3.25 6.5 2 12 7.5z", fill: color }) }));
16
+ const DatePicker = ({ lang, value, onChange, minDate, maxDate, translations, mode = "flight", }) => {
17
+ const { dateTimeFormat } = useWidget();
18
+ const insets = useSafeAreaInsets();
19
+ const [isOpen, setIsOpen] = useState(false);
20
+ const [currentMonth, setCurrentMonth] = useState(new Date());
21
+ const [oneWay, setOneWay] = useState(false);
22
+ const [activeField, setActiveField] = useState(null);
23
+ const [monthSelectionMode, setMonthSelectionMode] = useState(false);
24
+ const today = startOfDay(new Date());
25
+ const displayFormat = useMemo(() => {
26
+ return dateTimeFormat === "usa" ? "MMMM d" : "d MMMM";
27
+ }, [dateTimeFormat]);
28
+ const headerFormat = useMemo(() => (lang === "en" ? "MMMM yyyy" : "LLLL yyyy"), [lang]);
29
+ const locale = useMemo(() => (lang === "en" ? enUS : ru), [lang]);
30
+ const weekdays = useMemo(() => {
31
+ const monday = new Date(2020, 0, 6);
32
+ return Array.from({ length: 7 }).map((_, i) => {
33
+ const name = format(addDays(monday, i), "EEE", { locale });
34
+ return name.slice(0, 2);
35
+ });
36
+ }, [locale]);
37
+ const monthNames = useMemo(() => {
38
+ return Array.from({ length: 12 }).map((_, i) => format(new Date(2020, i, 1), "LLLL", { locale }));
39
+ }, [locale]);
40
+ const min = minDate ? new Date(minDate) : today;
41
+ const max = maxDate ? new Date(maxDate) : null;
42
+ const selectedStart = value[0] ? parse(value[0], "yyyy-MM-dd", new Date()) : null;
43
+ const selectedEnd = value[1] ? parse(value[1], "yyyy-MM-dd", new Date()) : null;
44
+ let depLabelFinal;
45
+ let retLabelFinal;
46
+ if (mode === "hotel") {
47
+ depLabelFinal = translations["check_in"] || (lang === "en" ? "Check-in" : "Check-in");
48
+ retLabelFinal = translations["check_out"] || (lang === "en" ? "Check-out" : "Check-out");
49
+ }
50
+ else if (mode === "car") {
51
+ depLabelFinal = translations["pick-up"] || (lang === "en" ? "Pick-up date" : "Pick-up date");
52
+ retLabelFinal = translations["drop-off"] || (lang === "en" ? "Drop-off date" : "Drop-off date");
53
+ }
54
+ else {
55
+ depLabelFinal = translations["departure_date"] || (lang === "en" ? "Departure" : "Departure");
56
+ retLabelFinal = translations["return_date"] || (lang === "en" ? "Return" : "Return");
57
+ }
58
+ const depPlaceholder = translations["departure_placeholder"] || (lang === "en" ? "Departure" : "When");
59
+ const retPlaceholder = translations["return_placeholder"] || (lang === "en" ? "Return" : "Return");
60
+ const onewayText = oneWay
61
+ ? translations["with_return"] || (lang === "en" ? "Add return trip" : "Add return trip")
62
+ : translations["no_return_ticket"] || (lang === "en" ? "One-way only" : "One-way only");
63
+ const formatDateDisplay = (dateStr) => {
64
+ if (!dateStr)
65
+ return "";
66
+ const parsed = parse(dateStr, "yyyy-MM-dd", new Date());
67
+ return isNaN(parsed.getTime()) ? "" : format(parsed, displayFormat, { locale });
68
+ };
69
+ const formatMobileDisplay = (dateStr) => {
70
+ if (!dateStr)
71
+ return "";
72
+ const parsed = parse(dateStr, "yyyy-MM-dd", new Date());
73
+ const mobileFormat = lang === "en" ? "MMM d" : "d MMMM";
74
+ return isNaN(parsed.getTime()) ? "" : format(parsed, mobileFormat, { locale });
75
+ };
76
+ const openPicker = (field) => {
77
+ if (field === "return" && !value[0])
78
+ return;
79
+ if (field === "return" && oneWay) {
80
+ setOneWay(false);
81
+ }
82
+ setActiveField(field);
83
+ let targetDateStr = null;
84
+ if (field === "return") {
85
+ targetDateStr = value[1] || value[0];
86
+ }
87
+ else {
88
+ targetDateStr = value[0] || null;
89
+ }
90
+ if (targetDateStr) {
91
+ const parsed = parse(targetDateStr, "yyyy-MM-dd", new Date());
92
+ if (!isNaN(parsed.getTime())) {
93
+ setCurrentMonth(startOfMonth(parsed));
94
+ }
95
+ }
96
+ else {
97
+ setCurrentMonth(startOfMonth(new Date()));
98
+ }
99
+ setIsOpen(true);
100
+ setMonthSelectionMode(false);
101
+ };
102
+ const closePicker = () => {
103
+ if (oneWay && value[1]) {
104
+ onChange([value[0] || "", ""]);
105
+ }
106
+ setIsOpen(false);
107
+ setActiveField(null);
108
+ setMonthSelectionMode(false);
109
+ };
110
+ const handleDateClick = useCallback((date) => {
111
+ if (activeField === "departure") {
112
+ onChange([format(date, "yyyy-MM-dd"), ""]);
113
+ if (!oneWay) {
114
+ setActiveField("return");
115
+ }
116
+ else {
117
+ setIsOpen(false);
118
+ setActiveField(null);
119
+ }
120
+ return;
121
+ }
122
+ if (activeField === "return") {
123
+ const depDate = value[0] ? parse(value[0], "yyyy-MM-dd", new Date()) : null;
124
+ if (mode === "hotel" && depDate && isSameDay(date, depDate)) {
125
+ return;
126
+ }
127
+ if (depDate && isBefore(date, depDate)) {
128
+ onChange([format(date, "yyyy-MM-dd"), value[0]]);
129
+ }
130
+ else {
131
+ onChange([value[0], format(date, "yyyy-MM-dd")]);
132
+ }
133
+ setIsOpen(false);
134
+ setActiveField(null);
135
+ }
136
+ }, [activeField, oneWay, value, mode, onChange]);
137
+ const toggleOneWay = () => {
138
+ if (oneWay) {
139
+ setOneWay(false);
140
+ setActiveField("return");
141
+ }
142
+ else {
143
+ onChange([value[0] || "", ""]);
144
+ setOneWay(true);
145
+ setIsOpen(false);
146
+ setActiveField(null);
147
+ }
148
+ };
149
+ const handleClear = () => {
150
+ onChange(["", ""]);
151
+ setOneWay(false);
152
+ setActiveField("departure");
153
+ setMonthSelectionMode(false);
154
+ };
155
+ const renderMonthSelector = () => {
156
+ const currentYear = currentMonth.getFullYear();
157
+ return (_jsxs(View, { style: styles.monthSelectorContainer, children: [_jsxs(View, { style: styles.monthSelectorHeader, children: [_jsx(TouchableOpacity, { onPress: () => setCurrentMonth(addMonths(currentMonth, -12)), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(ChevronLeft, {}) }), _jsx(Text, { style: styles.monthSelectorYear, children: currentYear }), _jsx(TouchableOpacity, { onPress: () => setCurrentMonth(addMonths(currentMonth, 12)), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(ChevronRight, {}) })] }), _jsx(View, { style: styles.monthGrid, children: monthNames.map((name, index) => {
158
+ const monthDate = new Date(currentYear, index, 1);
159
+ const isDisabled = isBefore(monthDate, startOfMonth(today)) &&
160
+ monthDate.getFullYear() === today.getFullYear();
161
+ const isActive = currentMonth.getMonth() === index;
162
+ return (_jsx(TouchableOpacity, { disabled: isDisabled, style: [
163
+ styles.monthBtn,
164
+ isActive && styles.monthBtnActive,
165
+ isDisabled && styles.monthBtnDisabled,
166
+ ], onPress: () => {
167
+ if (isDisabled)
168
+ return;
169
+ setCurrentMonth(new Date(currentYear, index, 1));
170
+ setMonthSelectionMode(false);
171
+ }, activeOpacity: 0.7, children: _jsx(Text, { style: [
172
+ styles.monthBtnText,
173
+ isActive && styles.monthBtnTextActive,
174
+ isDisabled && styles.monthBtnTextDisabled,
175
+ ], children: name }) }, name));
176
+ }) })] }));
177
+ };
178
+ const renderMonth = (monthStart) => {
179
+ const firstDayOfMonth = startOfMonth(monthStart);
180
+ const lastDayOfMonth = endOfMonth(firstDayOfMonth);
181
+ const firstWeekday = (firstDayOfMonth.getDay() + 6) % 7;
182
+ const dayCells = [];
183
+ for (let i = 0; i < firstWeekday; i++) {
184
+ dayCells.push(_jsx(View, { style: styles.dayCell }, `empty-${i}`));
185
+ }
186
+ let day = new Date(firstDayOfMonth);
187
+ while (day <= lastDayOfMonth) {
188
+ const cellDate = new Date(day);
189
+ const isDisabled = isBefore(cellDate, today) ||
190
+ (min && isBefore(cellDate, min)) ||
191
+ (max && isAfter(cellDate, max)) ||
192
+ (selectedStart && activeField === "return" && isBefore(cellDate, selectedStart));
193
+ const isSelectedStart = selectedStart && isSameDay(cellDate, selectedStart);
194
+ const isSelectedEnd = selectedEnd && isSameDay(cellDate, selectedEnd);
195
+ let isInRange = false;
196
+ if (selectedStart && selectedEnd) {
197
+ isInRange = isWithinInterval(cellDate, {
198
+ start: selectedStart,
199
+ end: selectedEnd,
200
+ });
201
+ }
202
+ const isOneDay = selectedStart && !selectedEnd && isSameDay(cellDate, selectedStart);
203
+ dayCells.push(_jsx(TouchableOpacity, { disabled: !!isDisabled, style: [
204
+ styles.dayCell,
205
+ isInRange && !isSelectedStart && !isSelectedEnd && styles.dayInRange,
206
+ isSelectedStart && styles.daySelected,
207
+ isSelectedEnd && styles.daySelected,
208
+ isOneDay && styles.dayOneDay,
209
+ isSelectedStart && selectedEnd && styles.daySelectedStart,
210
+ isSelectedEnd && selectedStart && styles.daySelectedEnd,
211
+ ], onPress: () => !isDisabled && handleDateClick(cellDate), activeOpacity: 0.7, children: _jsx(Text, { style: [
212
+ styles.dayText,
213
+ isDisabled && styles.dayTextDisabled,
214
+ (isSelectedStart || isSelectedEnd) && styles.dayTextSelected,
215
+ ], children: format(cellDate, "d", { locale }) }) }, cellDate.getTime()));
216
+ day = addDays(day, 1);
217
+ }
218
+ return (_jsxs(View, { style: styles.monthContainer, children: [_jsx(TouchableOpacity, { style: styles.monthHeader, onPress: () => setMonthSelectionMode(true), activeOpacity: 0.7, children: _jsx(Text, { style: styles.monthHeaderText, children: format(firstDayOfMonth, headerFormat, { locale }) }) }), _jsx(View, { style: styles.weekdayRow, children: weekdays.map((d, i) => (_jsx(View, { style: styles.weekdayCell, children: _jsx(Text, { style: styles.weekdayText, children: d }) }, `${d}-${i}`))) }), _jsx(View, { style: styles.dayGrid, children: dayCells })] }, firstDayOfMonth.getTime()));
219
+ };
220
+ const renderCalendarContent = () => {
221
+ const months = [];
222
+ for (let i = 0; i < 12; i++) {
223
+ months.push(renderMonth(addMonths(currentMonth, i)));
224
+ }
225
+ return months;
226
+ };
227
+ return (_jsxs(_Fragment, { children: [_jsxs(View, { style: styles.dateInputsWrapper, children: [_jsxs(View, { style: styles.dateInputsRow, children: [_jsxs(TouchableOpacity, { style: [
228
+ styles.dateField,
229
+ activeField === "departure" && styles.dateFieldActive,
230
+ ], onPress: () => openPicker("departure"), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: depLabelFinal }), _jsx(Text, { style: [styles.dateFieldValue, !value[0] && styles.dateFieldPlaceholder], children: value[0] ? formatDateDisplay(value[0]) : depPlaceholder })] }), _jsxs(TouchableOpacity, { style: [
231
+ styles.dateField,
232
+ activeField === "return" && styles.dateFieldActive,
233
+ !value[0] && styles.dateFieldDisabled,
234
+ oneWay && styles.dateFieldSoftDisabled,
235
+ ], onPress: () => openPicker("return"), activeOpacity: value[0] ? 0.8 : 1, children: [_jsx(Text, { style: formStyles.label, children: retLabelFinal }), _jsx(Text, { style: [
236
+ styles.dateFieldValue,
237
+ (!value[1] || oneWay) && styles.dateFieldPlaceholder,
238
+ ], children: oneWay
239
+ ? translations["no_return_short"] || "One-way"
240
+ : value[1]
241
+ ? formatDateDisplay(value[1])
242
+ : retPlaceholder })] })] }), (value[0] || value[1]) && (_jsx(TouchableOpacity, { style: styles.clearButton, onPress: handleClear, hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, {}) }))] }), _jsx(Modal, { visible: isOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: closePicker, children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.modalCloseButton, onPress: closePicker, hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsxs(View, { style: styles.modalDateInputs, children: [_jsxs(TouchableOpacity, { style: [
243
+ styles.modalDateField,
244
+ activeField === "departure" && styles.modalDateFieldActive,
245
+ ], onPress: () => setActiveField("departure"), children: [_jsx(Text, { style: styles.modalDateFieldLabel, children: lang === "en" ? "Departure" : depLabelFinal }), _jsx(Text, { style: styles.modalDateFieldValue, children: value[0] ? formatMobileDisplay(value[0]) : (lang === "en" ? "Choose date" : "Choose") })] }), _jsxs(TouchableOpacity, { style: [
246
+ styles.modalDateField,
247
+ (oneWay || !value[0]) && styles.modalDateFieldDisabled,
248
+ activeField === "return" && styles.modalDateFieldActive,
249
+ ], onPress: () => {
250
+ if (oneWay || !value[0])
251
+ return;
252
+ setActiveField("return");
253
+ }, children: [_jsx(Text, { style: styles.modalDateFieldLabel, children: lang === "en" ? "Return" : retLabelFinal }), _jsx(Text, { style: styles.modalDateFieldValue, children: oneWay
254
+ ? (lang === "en" ? "Not needed" : "One-way")
255
+ : !value[0]
256
+ ? (lang === "en" ? "Choose date" : "Choose")
257
+ : value[1]
258
+ ? formatMobileDisplay(value[1])
259
+ : (lang === "en" ? "Choose date" : "Choose") })] }), (value[0] || value[1]) && (_jsx(TouchableOpacity, { style: styles.modalClearButton, onPress: () => {
260
+ handleClear();
261
+ setActiveField("departure");
262
+ }, hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, {}) }))] }), _jsx(ScrollView, { style: styles.calendarScrollView, showsVerticalScrollIndicator: false, children: monthSelectionMode
263
+ ? renderMonthSelector()
264
+ : (_jsxs(_Fragment, { children: [_jsxs(View, { style: styles.navRow, children: [_jsx(TouchableOpacity, { disabled: isBefore(addMonths(currentMonth, -1), startOfMonth(today)), onPress: () => setCurrentMonth((prev) => startOfMonth(addMonths(prev, -1))), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, style: [
265
+ styles.navButton,
266
+ isBefore(addMonths(currentMonth, -1), startOfMonth(today)) && styles.navButtonDisabled,
267
+ ], children: _jsx(ChevronLeft, {}) }), _jsx(TouchableOpacity, { onPress: () => setCurrentMonth((prev) => startOfMonth(addMonths(prev, 1))), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, style: styles.navButton, children: _jsx(ChevronRight, {}) })] }), renderCalendarContent()] })) }), mode === "flight" && (_jsx(View, { style: styles.onewayContainer, children: _jsx(TouchableOpacity, { style: [styles.onewayBtn, oneWay && styles.onewayBtnActive], onPress: toggleOneWay, activeOpacity: 0.7, children: _jsx(Text, { style: [
268
+ styles.onewayBtnText,
269
+ oneWay && styles.onewayBtnTextActive,
270
+ ], children: onewayText }) }) }))] }) })] }));
271
+ };
272
+ const styles = StyleSheet.create({
273
+ dateInputsWrapper: {
274
+ position: "relative",
275
+ },
276
+ dateInputsRow: {
277
+ flexDirection: "column",
278
+ gap: Spacing.lg,
279
+ },
280
+ dateField: {
281
+ flex: 1,
282
+ backgroundColor: Colors.inputBg,
283
+ borderRadius: BorderRadius.lg,
284
+ paddingHorizontal: Spacing.lg,
285
+ paddingVertical: Spacing.md,
286
+ minHeight: 56,
287
+ },
288
+ dateFieldActive: {
289
+ backgroundColor: Colors.primaryLight,
290
+ },
291
+ dateFieldDisabled: {
292
+ opacity: 0.5,
293
+ },
294
+ dateFieldSoftDisabled: {
295
+ opacity: 0.6,
296
+ },
297
+ dateFieldValue: {
298
+ fontSize: FontSize.md,
299
+ color: Colors.text,
300
+ fontWeight: "400",
301
+ },
302
+ dateFieldPlaceholder: {
303
+ color: Colors.textTertiary,
304
+ fontWeight: "400",
305
+ },
306
+ clearButton: {
307
+ position: "absolute",
308
+ right: 0,
309
+ top: 14,
310
+ width: 28,
311
+ height: 28,
312
+ borderRadius: BorderRadius.full,
313
+ alignItems: "center",
314
+ justifyContent: "center",
315
+ zIndex: 1,
316
+ marginRight: 5,
317
+ },
318
+ modalContainer: {
319
+ flex: 1,
320
+ backgroundColor: Colors.surface,
321
+ },
322
+ modalCloseButton: {
323
+ alignSelf: "flex-end",
324
+ padding: Spacing.lg,
325
+ },
326
+ modalCloseText: {
327
+ fontSize: FontSize.xl,
328
+ color: Colors.textSecondary,
329
+ fontWeight: "600",
330
+ },
331
+ modalDateInputs: {
332
+ flexDirection: "row",
333
+ paddingHorizontal: Spacing.lg,
334
+ paddingBottom: Spacing.md,
335
+ gap: Spacing.sm,
336
+ borderBottomWidth: 1,
337
+ borderBottomColor: Colors.border,
338
+ alignItems: "center",
339
+ },
340
+ modalDateField: {
341
+ flex: 1,
342
+ paddingVertical: Spacing.sm,
343
+ paddingHorizontal: Spacing.md,
344
+ borderRadius: BorderRadius.md,
345
+ borderWidth: 1,
346
+ borderColor: Colors.transparent,
347
+ },
348
+ modalDateFieldActive: {
349
+ borderColor: Colors.primary,
350
+ backgroundColor: Colors.primaryLight,
351
+ },
352
+ modalDateFieldDisabled: {
353
+ opacity: 0.4,
354
+ },
355
+ modalDateFieldLabel: {
356
+ fontSize: FontSize.xs,
357
+ color: Colors.textTertiary,
358
+ fontWeight: "500",
359
+ },
360
+ modalDateFieldValue: {
361
+ fontSize: FontSize.md,
362
+ color: Colors.text,
363
+ fontWeight: "500",
364
+ marginTop: 2,
365
+ },
366
+ modalClearButton: {
367
+ width: 28,
368
+ height: 28,
369
+ alignItems: "center",
370
+ justifyContent: "center",
371
+ },
372
+ calendarScrollView: {
373
+ flex: 1,
374
+ paddingHorizontal: Spacing.lg,
375
+ },
376
+ navRow: {
377
+ flexDirection: "row",
378
+ justifyContent: "space-between",
379
+ paddingVertical: Spacing.md,
380
+ },
381
+ navButton: {
382
+ padding: Spacing.sm,
383
+ },
384
+ navButtonDisabled: {
385
+ opacity: 0.3,
386
+ },
387
+ monthContainer: {
388
+ marginBottom: Spacing.xxl,
389
+ },
390
+ monthHeader: {
391
+ paddingVertical: Spacing.md,
392
+ alignItems: "center",
393
+ },
394
+ monthHeaderText: {
395
+ fontSize: FontSize.lg,
396
+ fontWeight: "600",
397
+ color: Colors.text,
398
+ textTransform: "capitalize",
399
+ },
400
+ weekdayRow: {
401
+ flexDirection: "row",
402
+ marginBottom: Spacing.xs,
403
+ },
404
+ weekdayCell: {
405
+ flex: 1,
406
+ alignItems: "center",
407
+ paddingVertical: Spacing.xs,
408
+ },
409
+ weekdayText: {
410
+ fontSize: FontSize.xs,
411
+ color: Colors.textTertiary,
412
+ fontWeight: "600",
413
+ textTransform: "capitalize",
414
+ },
415
+ dayGrid: {
416
+ flexDirection: "row",
417
+ flexWrap: "wrap",
418
+ },
419
+ dayCell: {
420
+ width: `${100 / 7}%`,
421
+ height: 40,
422
+ aspectRatio: 1,
423
+ alignItems: "center",
424
+ justifyContent: "center",
425
+ },
426
+ dayText: {
427
+ fontSize: FontSize.md,
428
+ color: Colors.text,
429
+ },
430
+ dayTextDisabled: {
431
+ color: Colors.textTertiary,
432
+ opacity: 0.4,
433
+ },
434
+ dayTextSelected: {
435
+ color: Colors.textOnPrimary,
436
+ fontWeight: "600",
437
+ },
438
+ daySelected: {
439
+ backgroundColor: Colors.primary,
440
+ borderRadius: BorderRadius.full,
441
+ },
442
+ dayOneDay: {
443
+ backgroundColor: Colors.primary,
444
+ borderRadius: BorderRadius.full,
445
+ },
446
+ daySelectedStart: {
447
+ borderTopLeftRadius: BorderRadius.full,
448
+ borderBottomLeftRadius: BorderRadius.full,
449
+ borderTopRightRadius: 0,
450
+ borderBottomRightRadius: 0,
451
+ },
452
+ daySelectedEnd: {
453
+ borderTopRightRadius: BorderRadius.full,
454
+ borderBottomRightRadius: BorderRadius.full,
455
+ borderTopLeftRadius: 0,
456
+ borderBottomLeftRadius: 0,
457
+ },
458
+ dayInRange: {
459
+ backgroundColor: Colors.primaryLight,
460
+ },
461
+ monthSelectorContainer: {
462
+ paddingVertical: Spacing.lg,
463
+ },
464
+ monthSelectorHeader: {
465
+ flexDirection: "row",
466
+ alignItems: "center",
467
+ justifyContent: "space-between",
468
+ paddingVertical: Spacing.md,
469
+ },
470
+ monthSelectorYear: {
471
+ fontSize: FontSize.xl,
472
+ fontWeight: "600",
473
+ color: Colors.text,
474
+ },
475
+ monthGrid: {
476
+ flexDirection: "row",
477
+ flexWrap: "wrap",
478
+ gap: Spacing.sm,
479
+ marginTop: Spacing.lg,
480
+ },
481
+ monthBtn: {
482
+ width: "30%",
483
+ paddingVertical: Spacing.md,
484
+ alignItems: "center",
485
+ borderRadius: BorderRadius.md,
486
+ borderWidth: 1,
487
+ borderColor: Colors.border,
488
+ },
489
+ monthBtnActive: {
490
+ backgroundColor: Colors.primary,
491
+ borderColor: Colors.primary,
492
+ },
493
+ monthBtnDisabled: {
494
+ opacity: 0.3,
495
+ },
496
+ monthBtnText: {
497
+ fontSize: FontSize.md,
498
+ color: Colors.text,
499
+ textTransform: "capitalize",
500
+ },
501
+ monthBtnTextActive: {
502
+ color: Colors.textOnPrimary,
503
+ fontWeight: "600",
504
+ },
505
+ monthBtnTextDisabled: {
506
+ color: Colors.textTertiary,
507
+ },
508
+ onewayContainer: {
509
+ paddingTop: Spacing.sm,
510
+ paddingBottom: Spacing.lg,
511
+ paddingHorizontal: Spacing.lg,
512
+ borderTopWidth: 1,
513
+ borderTopColor: Colors.border,
514
+ },
515
+ onewayBtn: {
516
+ width: "100%",
517
+ paddingVertical: Spacing.md,
518
+ paddingHorizontal: Spacing.lg,
519
+ borderRadius: BorderRadius.md,
520
+ backgroundColor: Colors.surfaceSecondary,
521
+ alignItems: "center",
522
+ },
523
+ onewayBtnActive: {
524
+ backgroundColor: Colors.primaryLight,
525
+ },
526
+ onewayBtnText: {
527
+ fontSize: 15,
528
+ color: Colors.text,
529
+ fontWeight: "500",
530
+ },
531
+ onewayBtnTextActive: {
532
+ color: Colors.primary,
533
+ fontWeight: "500",
534
+ },
535
+ });
536
+ export default DatePicker;
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import { useSearchExpiration } from "../../hooks/useSearchExpiration";
3
+ type FlightSearchFormProps = {
4
+ startSearch: (params: any) => Promise<any>;
5
+ loading: boolean;
6
+ error: string | null;
7
+ currency: string;
8
+ translations?: Record<string, string>;
9
+ defaults?: {
10
+ from?: string;
11
+ to?: string;
12
+ } | null;
13
+ isPreview: boolean;
14
+ expiration: ReturnType<typeof useSearchExpiration>;
15
+ hasSearched: boolean;
16
+ };
17
+ declare const FlightSearchForm: React.FC<FlightSearchFormProps>;
18
+ export default FlightSearchForm;