@devtravelcode/widget-native 1.0.2 → 1.0.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 (32) hide show
  1. package/dist/App.js +8 -5
  2. package/dist/modules/hooks/useHotelMapData.d.ts +1 -1
  3. package/dist/modules/hooks/useHotelMapData.js +26 -32
  4. package/dist/modules/search-form/AgeSelector.js +4 -1
  5. package/dist/modules/search-form/NationalityInput.js +7 -3
  6. package/dist/modules/search-form/TimePicker.js +4 -3
  7. package/dist/modules/search-form/flight/AirlinesSelector.js +7 -3
  8. package/dist/modules/search-form/flight/DatePicker.js +14 -2
  9. package/dist/modules/search-form/flight/FlightSearchForm.js +2 -1
  10. package/dist/modules/search-form/flight/LocationInput.js +6 -2
  11. package/dist/modules/search-form/flight/PassengersPicker.js +9 -4
  12. package/dist/modules/search-form/flight/SwitchLocationBtn.js +4 -1
  13. package/dist/modules/search-form/flight/flight-filters/FiltersContent.d.ts +1 -0
  14. package/dist/modules/search-form/flight/flight-filters/FiltersContent.js +14 -13
  15. package/dist/modules/search-form/flight/flight-filters/FlightFiltersDynamic.js +3 -2
  16. package/dist/modules/search-form/flight/flight-filters/RangeBlock.d.ts +1 -0
  17. package/dist/modules/search-form/flight/flight-filters/RangeBlock.js +5 -3
  18. package/dist/modules/search-form/flight/flight-results/FlightCard.js +3 -2
  19. package/dist/modules/search-form/flight/flight-results/FlightPriceBlock.js +4 -1
  20. package/dist/modules/search-form/flight/flight-results/ProvidersLoader.js +4 -1
  21. package/dist/modules/search-form/hotel/GuestSelector.js +14 -9
  22. package/dist/modules/search-form/hotel/HotelLocation.js +6 -2
  23. package/dist/modules/search-form/hotel/HotelResults.d.ts +1 -0
  24. package/dist/modules/search-form/hotel/HotelResults.js +32 -6
  25. package/dist/modules/search-form/hotel/components/HotelCard.d.ts +1 -0
  26. package/dist/modules/search-form/hotel/components/HotelCard.js +39 -5
  27. package/dist/modules/search-form/hotel/components/HotelList.d.ts +1 -0
  28. package/dist/modules/search-form/hotel/components/HotelList.js +2 -2
  29. package/dist/modules/search-form/hotel/components/HotelMap.js +40 -9
  30. package/dist/modules/search-form/hotel/hotel-filters/HotelFilters.js +12 -5
  31. package/index.ts +7 -0
  32. package/package.json +2 -2
package/dist/App.js CHANGED
@@ -58,7 +58,10 @@ const App = ({ lang, currency, onFlightSelect, onHotelSelect, onCarSelect }) =>
58
58
  return t.banner_car ?? defaultTexts.car;
59
59
  return "";
60
60
  };
61
- const showHeadings = config?.texts?.headings_visible !== false;
61
+ const bannerText = getBannerText();
62
+ const showHeadings = config?.texts?.headings_visible !== false &&
63
+ config?.headings_visible !== false &&
64
+ !!bannerText;
62
65
  const { results: flightResults, filtersData, airlinesData, dictionaries, currencySign, loading: flightLoading, isFiltering: flightIsFiltering, isPageLoading: flightIsPageLoading, error: flightError, status: flightStatus, total: flightTotal, limit: flightLimit, offset: flightOffset, sessionId, startSearch: startFlightSearch, applyFilters: applyFlightFilters, fetchPage: fetchFlightPage, reset: resetFlights, } = useFlightSearch();
63
66
  const [hasFlightSearched, setHasFlightSearched] = useState(false);
64
67
  useEffect(() => {
@@ -133,14 +136,14 @@ const App = ({ lang, currency, onFlightSelect, onHotelSelect, onCarSelect }) =>
133
136
  { color: colors.textHeadings || Colors.white },
134
137
  ];
135
138
  if (activeTab === "hotels") {
136
- const hotelHeader = (_jsxs(_Fragment, { children: [showHeadings && (_jsx(View, { style: bannerStyle, children: _jsx(Text, { style: bannerTextStyle, children: getBannerText() }) })), _jsx(Navbar, { activeTab: activeTab, onTabChange: setActiveTab, translations: translations }), _jsx(HotelSearchForm, { translations: translations, fetchHotelOffers: isPreview ? async () => { } : handleHotelSearch, loading: hotelLoading && !isPreview, onSearchParamsChange: setHotelSearchParams, isPreview: isPreview, expiration: hotelExpiration, hasSearched: hasHotelSearched })] }));
137
- return (_jsx(View, { style: styles.container, children: _jsx(HotelResults, { offers: hotelOffers, allHotels: hotelAllHotels, loading: hotelLoading, isStreaming: hotelIsStreaming, isPageLoading: hotelIsPageLoading, error: hotelError, translations: translations, lang: appLang, searchParams: hotelSearchParams, total: hotelTotal, offset: hotelOffset, limit: hotelLimit, fetchPage: fetchHotelPage, filterSearch: filterHotelSearch, currency: appCurrency, headerComponent: hotelHeader }) }));
139
+ const hotelHeader = (_jsxs(_Fragment, { children: [showHeadings && (_jsx(View, { style: bannerStyle, children: _jsx(Text, { style: bannerTextStyle, children: bannerText }) })), _jsx(Navbar, { activeTab: activeTab, onTabChange: setActiveTab, translations: translations }), _jsx(HotelSearchForm, { translations: translations, fetchHotelOffers: isPreview ? async () => { } : handleHotelSearch, loading: hotelLoading && !isPreview, onSearchParamsChange: setHotelSearchParams, isPreview: isPreview, expiration: hotelExpiration, hasSearched: hasHotelSearched })] }));
140
+ return (_jsx(View, { style: styles.container, children: _jsx(HotelResults, { offers: hotelOffers, allHotels: hotelAllHotels, loading: hotelLoading, isStreaming: hotelIsStreaming, isPageLoading: hotelIsPageLoading, error: hotelError, translations: translations, lang: appLang, searchParams: hotelSearchParams, total: hotelTotal, offset: hotelOffset, limit: hotelLimit, fetchPage: fetchHotelPage, filterSearch: filterHotelSearch, currency: appCurrency, headerComponent: hotelHeader, streamCompleted: hotelStreamCompleted }) }));
138
141
  }
139
142
  if (activeTab === "cars") {
140
- const carHeader = (_jsxs(_Fragment, { children: [showHeadings && (_jsx(View, { style: bannerStyle, children: _jsx(Text, { style: bannerTextStyle, children: getBannerText() }) })), _jsx(Navbar, { activeTab: activeTab, onTabChange: setActiveTab, translations: translations }), _jsx(CarSearchForm, { startSearch: isPreview ? async () => null : handleCarSearch, loading: carLoading && !isPreview, error: carError, onDaysChange: setCarDays, onSearchParamsChange: setCarSearchParams, translations: translations, isPreview: isPreview, expiration: carExpiration, hasSearched: hasCarSearched })] }));
143
+ const carHeader = (_jsxs(_Fragment, { children: [showHeadings && (_jsx(View, { style: bannerStyle, children: _jsx(Text, { style: bannerTextStyle, children: bannerText }) })), _jsx(Navbar, { activeTab: activeTab, onTabChange: setActiveTab, translations: translations }), _jsx(CarSearchForm, { startSearch: isPreview ? async () => null : handleCarSearch, loading: carLoading && !isPreview, error: carError, onDaysChange: setCarDays, onSearchParamsChange: setCarSearchParams, translations: translations, isPreview: isPreview, expiration: carExpiration, hasSearched: hasCarSearched })] }));
141
144
  return (_jsx(View, { style: styles.container, children: _jsx(CarResults, { offers: carOffers, filteredOffers: carFilteredOffers, paginatedOffers: carPaginatedOffers, loading: carLoading, error: carError, isFiltering: carIsFiltering, hasAppliedFilters: carHasAppliedFilters, onFilterChange: () => { }, onFiltersApplied: () => setCarHasAppliedFilters(true), days: carDays, searchParams: carSearchParams, translations: translations, hasCarSearched: hasCarSearched, lang: appLang, currency: appCurrency, currentPage: carCurrentPage, totalPages: carTotalPages, onPageChange: setCarCurrentPage, isPageLoading: false, headerComponent: carHeader }) }));
142
145
  }
143
- const flightHeader = (_jsxs(_Fragment, { children: [showHeadings && (_jsx(View, { style: bannerStyle, children: _jsx(Text, { style: bannerTextStyle, children: getBannerText() }) })), _jsx(Navbar, { activeTab: activeTab, onTabChange: setActiveTab, translations: translations }), _jsx(FlightSearchForm, { startSearch: isPreview ? async () => null : startFlightSearch, loading: flightLoading && !isPreview, error: flightError, currency: appCurrency, translations: translations, isPreview: isPreview, expiration: flightExpiration, hasSearched: hasFlightSearched }), flightLoading && !flightIsFiltering && (_jsx(ProvidersLoader, { active: flightLoading, type: "flights", translations: translations })), hasFlightSearched && !flightLoading && (flightTotal > 0 || hasFlightFilters) && (_jsx(View, { style: styles.totalCount, children: _jsxs(Text, { style: styles.totalCountText, children: [translations["results_found"], " ", flightTotal, " ", flightTotal === 1 ? translations["flight_singular"] : translations["flight_many"]] }) })), isFlightEmptyAfterSearch && (_jsxs(View, { style: styles.emptyState, children: [_jsx(Text, { style: styles.emptyTitle, children: translations["flights_not_found_title"] || "No flights found" }), _jsx(Text, { style: styles.emptyHint, children: translations["flights_not_found_hint"] || "Try adjusting your search parameters" })] })), isFlightEmptyAfterFilters && (_jsxs(_Fragment, { children: [_jsx(FlightFilters, { filters: filtersData, airlines: airlinesData, sessionId: sessionId, onApplyFilters: applyFlightFilters, translations: translations }), _jsxs(View, { style: styles.emptyState, children: [_jsx(Text, { style: styles.emptyTitle, children: translations["flights_not_found_title"] || "No flights found" }), _jsx(Text, { style: styles.emptyHint, children: translations["flights_not_found_hint"] || "Try adjusting your search parameters" })] })] })), !isFlightEmptyAfterSearch && !isFlightEmptyAfterFilters && (_jsx(_Fragment, { children: flightLoading && !flightIsFiltering ? (_jsx(FlightsSkeleton, { translations: translations })) : ((filtersData || flightResults.length > 0) && (_jsx(FlightFilters, { filters: filtersData, airlines: airlinesData, sessionId: sessionId, onApplyFilters: applyFlightFilters, translations: translations }))) }))] }));
146
+ const flightHeader = (_jsxs(_Fragment, { children: [showHeadings && (_jsx(View, { style: bannerStyle, children: _jsx(Text, { style: bannerTextStyle, children: bannerText }) })), _jsx(Navbar, { activeTab: activeTab, onTabChange: setActiveTab, translations: translations }), _jsx(FlightSearchForm, { startSearch: isPreview ? async () => null : startFlightSearch, loading: flightLoading && !isPreview, error: flightError, currency: appCurrency, translations: translations, isPreview: isPreview, expiration: flightExpiration, hasSearched: hasFlightSearched }), flightLoading && !flightIsFiltering && (_jsx(ProvidersLoader, { active: flightLoading, type: "flights", translations: translations })), hasFlightSearched && !flightLoading && (flightTotal > 0 || hasFlightFilters) && (_jsx(View, { style: styles.totalCount, children: _jsxs(Text, { style: styles.totalCountText, children: [translations["results_found"], " ", flightTotal, " ", flightTotal === 1 ? translations["flight_singular"] : translations["flight_many"]] }) })), isFlightEmptyAfterSearch && (_jsxs(View, { style: styles.emptyState, children: [_jsx(Text, { style: styles.emptyTitle, children: translations["flights_not_found_title"] || "No flights found" }), _jsx(Text, { style: styles.emptyHint, children: translations["flights_not_found_hint"] || "Try adjusting your search parameters" })] })), isFlightEmptyAfterFilters && (_jsxs(_Fragment, { children: [_jsx(FlightFilters, { filters: filtersData, airlines: airlinesData, sessionId: sessionId, onApplyFilters: applyFlightFilters, translations: translations }), _jsxs(View, { style: styles.emptyState, children: [_jsx(Text, { style: styles.emptyTitle, children: translations["flights_not_found_title"] || "No flights found" }), _jsx(Text, { style: styles.emptyHint, children: translations["flights_not_found_hint"] || "Try adjusting your search parameters" })] })] })), !isFlightEmptyAfterSearch && !isFlightEmptyAfterFilters && (_jsx(_Fragment, { children: flightLoading && !flightIsFiltering ? (_jsx(FlightsSkeleton, { translations: translations })) : ((filtersData || flightResults.length > 0) && (_jsx(FlightFilters, { filters: filtersData, airlines: airlinesData, sessionId: sessionId, onApplyFilters: applyFlightFilters, translations: translations }))) }))] }));
144
147
  return (_jsx(View, { style: styles.container, children: _jsx(FlightResults, { results: flightResults, loading: flightLoading, isFiltering: flightIsFiltering, dictionaries: dictionaries, currencySign: currencySign, airlines: airlinesData, sessionId: sessionId, filtersData: filtersData, total: flightTotal, limit: flightLimit, offset: flightOffset, isPageLoading: flightIsPageLoading, fetchPage: fetchFlightPage, translations: translations, lang: appLang, onFlightSelect: onFlightSelect, headerComponent: flightHeader }) }));
145
148
  };
146
149
  const styles = StyleSheet.create({
@@ -5,7 +5,7 @@ export type HotelPin = {
5
5
  total: number | null;
6
6
  soldOut: boolean;
7
7
  };
8
- export declare const useHotelMapData: (params: Record<string, any>, shouldFetch?: boolean) => {
8
+ export declare const useHotelMapData: (params: Record<string, any> | null, shouldFetch?: boolean) => {
9
9
  pins: HotelPin[];
10
10
  loading: boolean;
11
11
  fetchFailed: boolean;
@@ -1,14 +1,13 @@
1
1
  import { useState, useEffect, useRef, useCallback } from "react";
2
2
  import { useApiToken } from "../../utils/getToken";
3
3
  const API_BASE = "https://api.travel-code.com/v1/search/hotels/map";
4
- export const useHotelMapData = (params, shouldFetch = true) => {
4
+ export const useHotelMapData = (params, shouldFetch = false) => {
5
5
  const token = useApiToken();
6
6
  const [pins, setPins] = useState([]);
7
7
  const [loading, setLoading] = useState(false);
8
8
  const [fetchFailed, setFetchFailed] = useState(false);
9
9
  const lastParamsRef = useRef("");
10
- const fetchPins = useCallback(async () => {
11
- console.log("%c[useHotelMapData] 🚀 СТАРТ ЗАПРОСА", "color:#fff;background:#43a047;padding:3px 6px;border-radius:4px", { params });
10
+ const fetchPins = useCallback(async (searchParams) => {
12
11
  setLoading(true);
13
12
  setFetchFailed(false);
14
13
  try {
@@ -18,61 +17,56 @@ export const useHotelMapData = (params, shouldFetch = true) => {
18
17
  "Content-Type": "application/json",
19
18
  Authorization: `Bearer ${token}`,
20
19
  },
21
- body: JSON.stringify(params),
20
+ body: JSON.stringify(searchParams),
22
21
  });
23
- console.log("%c[useHotelMapData] 📥 ОТВЕТ ПОЛУЧЕН", "color:#fff;background:#5e35b1;padding:3px 6px;border-radius:4px", { status: response.status });
24
22
  if (!response.ok)
25
- throw new Error(`Ошибка ${response.status}`);
23
+ throw new Error(`Error ${response.status}`);
26
24
  const result = await response.json();
27
- console.log("%c[useHotelMapData] 🟢 pins:", "color:#fff;background:#00897b;padding:3px 6px;border-radius:4px", result?.data?.length);
28
- if (result?.data?.length) {
29
- const formatted = result.data
25
+ const hotels = Array.isArray(result?.data) ? result.data : Array.isArray(result?.hotels) ? result.hotels : [];
26
+ if (hotels.length) {
27
+ const formatted = hotels
30
28
  .filter((item) => {
31
29
  const lat = parseFloat(item.latitude);
32
30
  const lng = parseFloat(item.longitude);
33
31
  return lat && lng && lat !== 0 && lng !== 0;
34
32
  })
35
- .map((item) => ({
36
- id: String(item.propertyId),
37
- latitude: parseFloat(item.latitude),
38
- longitude: parseFloat(item.longitude),
39
- total: typeof item.total === "number" ? item.total : null,
40
- soldOut: item.soldOut === true ||
41
- item.total === null,
42
- }));
33
+ .map((item) => {
34
+ const price = item.total ?? item.price ?? null;
35
+ return {
36
+ id: String(item.propertyId || item.id),
37
+ latitude: parseFloat(item.latitude),
38
+ longitude: parseFloat(item.longitude),
39
+ total: typeof price === "number" ? price : null,
40
+ soldOut: item.soldOut === true || price == null,
41
+ };
42
+ });
43
43
  setPins(formatted);
44
44
  }
45
45
  else {
46
- console.log("%c[useHotelMapData] ⚠️ Нет данных", "color:#fb8c00");
47
46
  setPins([]);
48
47
  }
49
48
  }
50
- catch (err) {
51
- console.error("%c[useHotelMapData] 🔥 ОШИБКА", "background:red;color:white", err);
49
+ catch {
52
50
  setFetchFailed(true);
53
51
  }
54
52
  finally {
55
- console.log("%c[useHotelMapData] ⏹ ЗАПРОС ЗАВЕРШЕН", "color:#fff;background:#6d4c41;padding:3px 6px;border-radius:4px");
56
53
  setLoading(false);
57
54
  }
58
- }, [params, token]);
55
+ }, [token]);
59
56
  useEffect(() => {
60
- console.log("%c[useHotelMapData] EFFECT TRIGGERED", "color:#fff;background:#1e88e5;padding:3px 6px;border-radius:4px", { params, shouldFetch });
61
57
  if (!shouldFetch) {
62
- console.log("%c[useHotelMapData] ❌ shouldFetch = false → запрос НЕ отправлен", "color:#ef5350");
63
- return;
58
+ setPins([]);
59
+ lastParamsRef.current = "";
64
60
  }
65
- if (!params || Object.keys(params).length === 0) {
66
- console.log("%c[useHotelMapData] Пустые params → запрос НЕ отправлен", "color:#ef5350");
61
+ }, [shouldFetch]);
62
+ useEffect(() => {
63
+ if (!shouldFetch || !params || Object.keys(params).length === 0)
67
64
  return;
68
- }
69
65
  const key = JSON.stringify(params);
70
- if (key === lastParamsRef.current) {
71
- console.log("%c[useHotelMapData] 🔁 Те же params → запрос пропущен", "color:#ffa726");
66
+ if (key === lastParamsRef.current)
72
67
  return;
73
- }
74
68
  lastParamsRef.current = key;
75
- fetchPins();
69
+ fetchPins(params);
76
70
  }, [params, shouldFetch, fetchPins]);
77
71
  return { pins, loading, fetchFailed };
78
72
  };
@@ -6,6 +6,7 @@ import Svg, { Path } from "react-native-svg";
6
6
  import { Colors, Spacing, FontSize } from "../../theme/colors";
7
7
  import { CloseIcon } from "../../store";
8
8
  import { formStyles } from "../../theme/formStyles";
9
+ import { useWidget } from "../../context/WidgetContext";
9
10
  const ChevronDown = () => (_jsx(Svg, { width: 20, height: 20, viewBox: "0 0 20 20", fill: "none", children: _jsx(Path, { d: "M5 8L10 12.5L15 8", stroke: Colors.primary, strokeWidth: 2, strokeLinecap: "round" }) }));
10
11
  const AGE_OPTIONS = [
11
12
  ...Array.from({ length: 12 }, (_, i) => ({
@@ -20,6 +21,8 @@ const AGE_OPTIONS = [
20
21
  { id: "80+", label: "80+" },
21
22
  ];
22
23
  const AgeSelector = ({ label, value, onChange, translations, }) => {
24
+ const { colors } = useWidget();
25
+ const inputBg = colors.input || Colors.inputBg;
23
26
  const insets = useSafeAreaInsets();
24
27
  const [isModalOpen, setIsModalOpen] = useState(false);
25
28
  const [displayText, setDisplayText] = useState("");
@@ -40,7 +43,7 @@ const AgeSelector = ({ label, value, onChange, translations, }) => {
40
43
  const isActive = item.id === value;
41
44
  return (_jsxs(TouchableOpacity, { style: [styles.optionItem, isActive && styles.optionItemActive], onPress: () => handleSelect(item), activeOpacity: 0.7, children: [_jsx(Text, { style: [styles.optionText, isActive && styles.optionTextActive], children: item.label }), isActive && _jsx(Text, { style: styles.checkMark, children: "\u2713" })] }));
42
45
  };
43
- return (_jsxs(View, { children: [_jsx(View, { style: styles.inputContainer, children: _jsxs(TouchableOpacity, { style: formStyles.inputTrigger, onPress: () => setIsModalOpen(true), activeOpacity: 0.7, children: [_jsx(Text, { style: formStyles.label, children: translations["age_selector_label"] || label }), _jsxs(View, { style: formStyles.inputTriggerRow, children: [_jsx(Text, { style: [
46
+ return (_jsxs(View, { children: [_jsx(View, { style: styles.inputContainer, children: _jsxs(TouchableOpacity, { style: [formStyles.inputTrigger, { backgroundColor: inputBg }], onPress: () => setIsModalOpen(true), activeOpacity: 0.7, children: [_jsx(Text, { style: formStyles.label, children: translations["age_selector_label"] || label }), _jsxs(View, { style: formStyles.inputTriggerRow, children: [_jsx(Text, { style: [
44
47
  formStyles.inputTriggerText,
45
48
  !displayText && formStyles.inputTriggerPlaceholder,
46
49
  ], children: displayText ||
@@ -6,10 +6,11 @@ import Svg, { Circle, Path } from "react-native-svg";
6
6
  import { SvgUri } from "react-native-svg";
7
7
  import { useCountriesSearch } from "../hooks/useCountriesSearch";
8
8
  import { useCountriesByCodes } from "../hooks/useCountriesByCodes";
9
+ import { useWidget } from "../../context/WidgetContext";
9
10
  import { Colors, Spacing, FontSize } from "../../theme/colors";
10
11
  import { CloseIcon } from "../../store";
11
12
  import { formStyles } from "../../theme/formStyles";
12
- const SearchIcon = () => (_jsxs(Svg, { width: 19, height: 18, viewBox: "0 0 19 18", fill: "none", children: [_jsx(Circle, { cx: 7, cy: 7, r: 6, stroke: Colors.primary, strokeWidth: 2 }), _jsx(Path, { d: "M12 11L17.2857 16.0902", stroke: Colors.primary, strokeWidth: 2, strokeLinecap: "round" })] }));
13
+ const SearchIcon = ({ color = Colors.primary }) => (_jsxs(Svg, { width: 19, height: 18, viewBox: "0 0 19 18", fill: "none", children: [_jsx(Circle, { cx: 7, cy: 7, r: 6, stroke: color, strokeWidth: 2 }), _jsx(Path, { d: "M12 11L17.2857 16.0902", stroke: color, strokeWidth: 2, strokeLinecap: "round" })] }));
13
14
  const FLAG_BASE = "https://cdn.travel-code.com/images/remaster/flags";
14
15
  const POPULAR_FALLBACK = [
15
16
  { code: "PL", title: "\u041F\u043E\u043B\u044C\u0448\u0430", titleEn: "Poland", flag: `${FLAG_BASE}/PL.svg` },
@@ -19,6 +20,9 @@ const POPULAR_FALLBACK = [
19
20
  { code: "TR", title: "\u0422\u0443\u0440\u0446\u0438\u044F", titleEn: "Turkey", flag: `${FLAG_BASE}/TR.svg` },
20
21
  ];
21
22
  const NationalityInput = ({ label, value, onChange, translations, defaultNationality, defaultNationalities, lang = "ru", }) => {
23
+ const { colors } = useWidget();
24
+ const btnColor = colors.primary || Colors.primary;
25
+ const inputBg = colors.input || Colors.inputBg;
22
26
  const insets = useSafeAreaInsets();
23
27
  const [query, setQuery] = useState("");
24
28
  const [selectedCountry, setSelectedCountry] = useState(null);
@@ -78,10 +82,10 @@ const NationalityInput = ({ label, value, onChange, translations, defaultNationa
78
82
  const isActive = query.trim().toLowerCase() === countryLabel.toLowerCase();
79
83
  return (_jsxs(TouchableOpacity, { style: [styles.countryItem, isActive && styles.countryItemActive], onPress: () => handleSelect(item), activeOpacity: 0.7, children: [_jsx(View, { style: styles.flagContainer, children: _jsx(SvgUri, { uri: item.flag || `${FLAG_BASE}/${item.code}.svg`, width: 24, height: 18 }) }), _jsxs(View, { style: styles.countryInfo, children: [_jsx(Text, { style: styles.countryName, children: countryLabel }), _jsx(Text, { style: styles.countryCode, children: item.code })] })] }));
80
84
  };
81
- return (_jsxs(View, { children: [_jsx(View, { style: styles.inputContainer, children: _jsxs(TouchableOpacity, { style: formStyles.inputTrigger, onPress: openModal, activeOpacity: 0.7, children: [_jsx(Text, { style: formStyles.label, children: label }), _jsxs(View, { style: formStyles.inputTriggerRow, children: [_jsx(Text, { style: [
85
+ return (_jsxs(View, { children: [_jsx(View, { style: styles.inputContainer, children: _jsxs(TouchableOpacity, { style: [formStyles.inputTrigger, { backgroundColor: inputBg }], onPress: openModal, activeOpacity: 0.7, children: [_jsx(Text, { style: formStyles.label, children: label }), _jsxs(View, { style: formStyles.inputTriggerRow, children: [_jsx(Text, { style: [
82
86
  formStyles.inputTriggerText,
83
87
  !query && formStyles.inputTriggerPlaceholder,
84
- ], numberOfLines: 1, children: query || label }), selectedCountry?.code && (_jsx(Text, { style: formStyles.inputTriggerCode, children: selectedCountry.code }))] })] }) }), _jsx(Modal, { visible: isModalOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setIsModalOpen(false), children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: () => setIsModalOpen(false), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsxs(View, { style: styles.searchHeader, children: [_jsx(SearchIcon, {}), _jsx(TextInput, { style: styles.searchInput, value: modalQuery, onChangeText: (val) => {
88
+ ], numberOfLines: 1, children: query || label }), selectedCountry?.code && (_jsx(Text, { style: formStyles.inputTriggerCode, children: selectedCountry.code }))] })] }) }), _jsx(Modal, { visible: isModalOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setIsModalOpen(false), children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: () => setIsModalOpen(false), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsxs(View, { style: styles.searchHeader, children: [_jsx(SearchIcon, { color: btnColor }), _jsx(TextInput, { style: styles.searchInput, value: modalQuery, onChangeText: (val) => {
85
89
  setModalQuery(val);
86
90
  if (!val.trim()) {
87
91
  setSelectedCountry(null);
@@ -10,7 +10,8 @@ const TimePicker = ({ value, onChange, lang = "ru", translations, }) => {
10
10
  const [hours, setHours] = useState(10);
11
11
  const [minutes, setMinutes] = useState(0);
12
12
  const [period, setPeriod] = useState("AM");
13
- const { dateTimeFormat } = useWidget();
13
+ const { dateTimeFormat, colors } = useWidget();
14
+ const inputBg = colors.input || Colors.inputBg;
14
15
  const to24h = (h12, p) => {
15
16
  if (p === "AM")
16
17
  return h12 === 12 ? 0 : h12;
@@ -95,8 +96,8 @@ const TimePicker = ({ value, onChange, lang = "ru", translations, }) => {
95
96
  setMinutes(m);
96
97
  applyTime(h, m);
97
98
  };
98
- return (_jsxs(View, { children: [_jsxs(View, { style: styles.fieldsRow, children: [_jsx(View, { style: styles.fieldContainer, children: _jsxs(TouchableOpacity, { style: formStyles.inputTrigger, onPress: () => openPicker("from"), activeOpacity: 0.7, children: [_jsx(Text, { style: formStyles.label, children: translations.timepicker_pickup_label ||
99
- (lang === "en" ? "Pick-up time" : "\u0412\u0440\u0435\u043C\u044F \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u044F") }), _jsx(Text, { style: styles.fieldValue, children: formatInputTime(value[0]) })] }) }), _jsx(View, { style: styles.fieldContainer, children: _jsxs(TouchableOpacity, { style: formStyles.inputTrigger, onPress: () => openPicker("to"), activeOpacity: 0.7, children: [_jsx(Text, { style: formStyles.label, children: translations.timepicker_dropoff_label ||
99
+ return (_jsxs(View, { children: [_jsxs(View, { style: styles.fieldsRow, children: [_jsx(View, { style: styles.fieldContainer, children: _jsxs(TouchableOpacity, { style: [formStyles.inputTrigger, { backgroundColor: inputBg }], onPress: () => openPicker("from"), activeOpacity: 0.7, children: [_jsx(Text, { style: formStyles.label, children: translations.timepicker_pickup_label ||
100
+ (lang === "en" ? "Pick-up time" : "\u0412\u0440\u0435\u043C\u044F \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u044F") }), _jsx(Text, { style: styles.fieldValue, children: formatInputTime(value[0]) })] }) }), _jsx(View, { style: styles.fieldContainer, children: _jsxs(TouchableOpacity, { style: [formStyles.inputTrigger, { backgroundColor: inputBg }], onPress: () => openPicker("to"), activeOpacity: 0.7, children: [_jsx(Text, { style: formStyles.label, children: translations.timepicker_dropoff_label ||
100
101
  (lang === "en" ? "Drop-off time" : "\u0412\u0440\u0435\u043C\u044F \u0432\u043E\u0437\u0432\u0440\u0430\u0442\u0430") }), _jsx(Text, { style: styles.fieldValue, children: formatInputTime(value[1]) })] }) })] }), _jsx(Modal, { visible: isOpen, transparent: true, animationType: "fade", onRequestClose: () => setIsOpen(false), children: _jsx(TouchableOpacity, { style: styles.overlay, activeOpacity: 1, onPress: () => setIsOpen(false), children: _jsxs(View, { style: styles.pickerContainer, onStartShouldSetResponder: () => true, children: [_jsxs(View, { style: styles.pickerContent, children: [_jsxs(View, { style: styles.timeColumn, children: [_jsx(TouchableOpacity, { style: styles.arrowButton, onPress: () => adjustHours(1), children: _jsx(Text, { style: styles.arrowText, children: "\u25B2" }) }), _jsx(Text, { style: styles.timeValue, children: String(hours).padStart(2, "0") }), _jsx(TouchableOpacity, { style: styles.arrowButton, onPress: () => adjustHours(-1), children: _jsx(Text, { style: styles.arrowText, children: "\u25BC" }) })] }), _jsx(Text, { style: styles.timeSeparator, children: ":" }), _jsxs(View, { style: styles.timeColumn, children: [_jsx(TouchableOpacity, { style: styles.arrowButton, onPress: () => adjustMinutes(15), children: _jsx(Text, { style: styles.arrowText, children: "\u25B2" }) }), _jsx(Text, { style: styles.timeValue, children: String(minutes).padStart(2, "0") }), _jsx(TouchableOpacity, { style: styles.arrowButton, onPress: () => adjustMinutes(-15), children: _jsx(Text, { style: styles.arrowText, children: "\u25BC" }) })] }), dateTimeFormat === "usa" && (_jsxs(_Fragment, { children: [_jsx(View, { style: styles.timeSeparatorSpace }), _jsxs(View, { style: styles.timeColumn, children: [_jsx(TouchableOpacity, { style: styles.arrowButton, onPress: () => {
101
102
  const p = period === "AM" ? "PM" : "AM";
102
103
  setPeriod(p);
@@ -4,6 +4,7 @@ import { View, Text, TextInput, TouchableOpacity, Modal, FlatList, StyleSheet, A
4
4
  import { useSafeAreaInsets } from "react-native-safe-area-context";
5
5
  import Svg, { Circle, Path } from "react-native-svg";
6
6
  import { useApiToken } from "../../../utils/getToken";
7
+ import { useWidget } from "../../../context/WidgetContext";
7
8
  import { Colors, Spacing, BorderRadius, FontSize } from "../../../theme/colors";
8
9
  import { CloseIcon } from "../../../store";
9
10
  import { formStyles } from "../../../theme/formStyles";
@@ -16,8 +17,11 @@ const POPULAR_AIRLINES = [
16
17
  { id: 6, code: "W6", title: "Wizz Air" },
17
18
  ];
18
19
  const API_URL = "https://api.travel-code.com/v1/data/airlines";
19
- const SearchIcon = () => (_jsxs(Svg, { width: 19, height: 18, viewBox: "0 0 19 18", fill: "none", children: [_jsx(Circle, { cx: 7, cy: 7, r: 6, stroke: Colors.primary, strokeWidth: 2 }), _jsx(Path, { d: "M12 11L17.2857 16.0902", stroke: Colors.primary, strokeWidth: 2, strokeLinecap: "round" })] }));
20
+ const SearchIcon = ({ color = Colors.primary }) => (_jsxs(Svg, { width: 19, height: 18, viewBox: "0 0 19 18", fill: "none", children: [_jsx(Circle, { cx: 7, cy: 7, r: 6, stroke: color, strokeWidth: 2 }), _jsx(Path, { d: "M12 11L17.2857 16.0902", stroke: color, strokeWidth: 2, strokeLinecap: "round" })] }));
20
21
  const AirlinesSelector = ({ value, onChange, translations, }) => {
22
+ const { colors } = useWidget();
23
+ const btnColor = colors.primary || Colors.primary;
24
+ const inputBg = colors.input || Colors.inputBg;
21
25
  const token = useApiToken();
22
26
  const insets = useSafeAreaInsets();
23
27
  const [query, setQuery] = useState("");
@@ -82,11 +86,11 @@ const AirlinesSelector = ({ value, onChange, translations, }) => {
82
86
  isSelected && styles.checkboxSelected,
83
87
  ], children: isSelected && _jsx(Text, { style: styles.checkmark, children: "\u2713" }) })] }) }));
84
88
  };
85
- return (_jsxs(_Fragment, { children: [_jsxs(TouchableOpacity, { style: formStyles.inputTrigger, onPress: () => setIsModalOpen(true), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: translations["airlines_label"] || "Airlines" }), value.length > 0 ? (_jsx(View, { style: styles.chipsRow, children: value.map((code) => {
89
+ return (_jsxs(_Fragment, { children: [_jsxs(TouchableOpacity, { style: [formStyles.inputTrigger, { backgroundColor: inputBg }], onPress: () => setIsModalOpen(true), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: translations["airlines_label"] || "Airlines" }), value.length > 0 ? (_jsx(View, { style: styles.chipsRow, children: value.map((code) => {
86
90
  const airline = POPULAR_AIRLINES.find((a) => a.code === code) ||
87
91
  airlines.find((a) => a.code === code);
88
92
  return (_jsxs(View, { style: styles.chip, children: [_jsx(Text, { style: styles.chipText, children: airline?.title || code }), _jsx(TouchableOpacity, { onPress: () => toggleAirline(code), hitSlop: { top: 4, bottom: 4, left: 4, right: 4 }, children: _jsx(Text, { style: styles.chipRemove, children: "x" }) })] }, code));
89
- }) })) : (_jsx(Text, { style: formStyles.inputTriggerPlaceholder, children: translations["all_airlines_placeholder"] || "All airlines" }))] }), _jsx(Modal, { visible: isModalOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setIsModalOpen(false), children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: () => setIsModalOpen(false), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsxs(View, { style: styles.searchHeader, children: [_jsx(SearchIcon, {}), _jsx(TextInput, { autoFocus: true, style: styles.searchInput, value: query, onChangeText: setQuery, placeholder: translations["airlines_popup_placeholder"] ||
93
+ }) })) : (_jsx(Text, { style: formStyles.inputTriggerPlaceholder, children: translations["all_airlines_placeholder"] || "All airlines" }))] }), _jsx(Modal, { visible: isModalOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setIsModalOpen(false), children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: () => setIsModalOpen(false), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsxs(View, { style: styles.searchHeader, children: [_jsx(SearchIcon, { color: btnColor }), _jsx(TextInput, { autoFocus: true, style: styles.searchInput, value: query, onChangeText: setQuery, placeholder: translations["airlines_popup_placeholder"] ||
90
94
  "Search airlines", placeholderTextColor: Colors.textTertiary, returnKeyType: "search" })] }), selectedAirlines.length > 0 && (_jsx(View, { style: styles.selectedChipsContainer, children: selectedAirlines.map((airline) => (_jsxs(View, { style: styles.chip, children: [_jsx(Text, { style: styles.chipText, children: airline.title }), _jsx(TouchableOpacity, { onPress: () => toggleAirline(airline.code), hitSlop: { top: 4, bottom: 4, left: 4, right: 4 }, children: _jsx(Text, { style: styles.chipRemove, children: "x" }) })] }, airline.code))) })), _jsxs(View, { style: styles.modalContent, children: [loading && (_jsxs(View, { style: styles.loadingContainer, children: [_jsx(ActivityIndicator, { size: "small", color: Colors.primary }), _jsx(Text, { style: styles.loadingText, children: translations["airlines_popup_loading"] || "Loading..." })] })), !loading &&
91
95
  !showPopular &&
92
96
  airlines.length === 0 &&
@@ -14,7 +14,9 @@ const DAY_SIZE = Math.floor((SCREEN_WIDTH - Spacing.lg * 2) / 7);
14
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
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
16
  const DatePicker = ({ lang, value, onChange, minDate, maxDate, translations, mode = "flight", }) => {
17
- const { dateTimeFormat } = useWidget();
17
+ const { dateTimeFormat, colors } = useWidget();
18
+ const btnColor = colors.primary || Colors.primary;
19
+ const inputBg = colors.input || Colors.inputBg;
18
20
  const insets = useSafeAreaInsets();
19
21
  const [isOpen, setIsOpen] = useState(false);
20
22
  const [currentMonth, setCurrentMonth] = useState(new Date());
@@ -162,6 +164,7 @@ const DatePicker = ({ lang, value, onChange, minDate, maxDate, translations, mod
162
164
  return (_jsx(TouchableOpacity, { disabled: isDisabled, style: [
163
165
  styles.monthBtn,
164
166
  isActive && styles.monthBtnActive,
167
+ isActive && { backgroundColor: btnColor, borderColor: btnColor },
165
168
  isDisabled && styles.monthBtnDisabled,
166
169
  ], onPress: () => {
167
170
  if (isDisabled)
@@ -203,11 +206,13 @@ const DatePicker = ({ lang, value, onChange, minDate, maxDate, translations, mod
203
206
  dayCells.push(_jsx(TouchableOpacity, { disabled: !!isDisabled, style: [
204
207
  styles.dayCell,
205
208
  isInRange && !isSelectedStart && !isSelectedEnd && styles.dayInRange,
209
+ isInRange && !isSelectedStart && !isSelectedEnd && { backgroundColor: btnColor + '1A' },
206
210
  isSelectedStart && styles.daySelected,
207
211
  isSelectedEnd && styles.daySelected,
208
212
  isOneDay && styles.dayOneDay,
209
213
  isSelectedStart && selectedEnd && styles.daySelectedStart,
210
214
  isSelectedEnd && selectedStart && styles.daySelectedEnd,
215
+ (isSelectedStart || isSelectedEnd || isOneDay) && { backgroundColor: btnColor },
211
216
  ], onPress: () => !isDisabled && handleDateClick(cellDate), activeOpacity: 0.7, children: _jsx(Text, { style: [
212
217
  styles.dayText,
213
218
  isDisabled && styles.dayTextDisabled,
@@ -226,10 +231,14 @@ const DatePicker = ({ lang, value, onChange, minDate, maxDate, translations, mod
226
231
  };
227
232
  return (_jsxs(_Fragment, { children: [_jsxs(View, { style: styles.dateInputsWrapper, children: [_jsxs(View, { style: styles.dateInputsRow, children: [_jsxs(TouchableOpacity, { style: [
228
233
  styles.dateField,
234
+ { backgroundColor: inputBg },
229
235
  activeField === "departure" && styles.dateFieldActive,
236
+ activeField === "departure" && { backgroundColor: btnColor + '1A' },
230
237
  ], 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
238
  styles.dateField,
239
+ { backgroundColor: inputBg },
232
240
  activeField === "return" && styles.dateFieldActive,
241
+ activeField === "return" && { backgroundColor: btnColor + '1A' },
233
242
  !value[0] && styles.dateFieldDisabled,
234
243
  oneWay && styles.dateFieldSoftDisabled,
235
244
  ], onPress: () => openPicker("return"), activeOpacity: value[0] ? 0.8 : 1, children: [_jsx(Text, { style: formStyles.label, children: retLabelFinal }), _jsx(Text, { style: [
@@ -242,10 +251,12 @@ const DatePicker = ({ lang, value, onChange, minDate, maxDate, translations, mod
242
251
  : 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
252
  styles.modalDateField,
244
253
  activeField === "departure" && styles.modalDateFieldActive,
254
+ activeField === "departure" && { borderColor: btnColor, backgroundColor: btnColor + '1A' },
245
255
  ], 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
256
  styles.modalDateField,
247
257
  (oneWay || !value[0]) && styles.modalDateFieldDisabled,
248
258
  activeField === "return" && styles.modalDateFieldActive,
259
+ activeField === "return" && { borderColor: btnColor, backgroundColor: btnColor + '1A' },
249
260
  ], onPress: () => {
250
261
  if (oneWay || !value[0])
251
262
  return;
@@ -264,9 +275,10 @@ const DatePicker = ({ lang, value, onChange, minDate, maxDate, translations, mod
264
275
  : (_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
276
  styles.navButton,
266
277
  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: [
278
+ ], 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, oneWay && { backgroundColor: btnColor + '1A' }], onPress: toggleOneWay, activeOpacity: 0.7, children: _jsx(Text, { style: [
268
279
  styles.onewayBtnText,
269
280
  oneWay && styles.onewayBtnTextActive,
281
+ oneWay && { color: btnColor },
270
282
  ], children: onewayText }) }) }))] }) })] }));
271
283
  };
272
284
  const styles = StyleSheet.create({
@@ -13,6 +13,7 @@ import { Colors, Spacing, BorderRadius, FontSize } from "../../../theme/colors";
13
13
  import { formStyles } from "../../../theme/formStyles";
14
14
  const FlightSearchForm = ({ startSearch, loading, error, translations = {}, defaults = null, isPreview, expiration, hasSearched, }) => {
15
15
  const { lang, currency, config, colors } = useWidget();
16
+ const inputBg = colors.input || Colors.inputBg;
16
17
  const defaultFromLocation = config?.defaults?.flights?.from || "VNO";
17
18
  const defaultToLocation = config?.defaults?.flights?.to || "BCN";
18
19
  const today = new Date();
@@ -92,7 +93,7 @@ const FlightSearchForm = ({ startSearch, loading, error, translations = {}, defa
92
93
  setLocationFrom(from);
93
94
  setLocationTo(to);
94
95
  }, [config?.defaults?.flights]);
95
- return (_jsxs(ScrollView, { style: local.container, showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "handled", children: [_jsxs(View, { style: [formStyles.formCard, { backgroundColor: colors.form || Colors.surface }], children: [_jsxs(View, { children: [_jsxs(View, { style: local.locationCard, children: [_jsx(Text, { style: local.labelInner, children: translations["from_label_flight"] || "From" }), _jsx(LocationInput, { label: translations["from_label_flight"] || "From", value: locationFrom, onChange: (val) => {
96
+ return (_jsxs(ScrollView, { style: local.container, showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "handled", children: [_jsxs(View, { style: [formStyles.formCard, { backgroundColor: colors.form || Colors.surface }], children: [_jsxs(View, { children: [_jsxs(View, { style: [local.locationCard, { backgroundColor: inputBg }], children: [_jsx(Text, { style: local.labelInner, children: translations["from_label_flight"] || "From" }), _jsx(LocationInput, { label: translations["from_label_flight"] || "From", value: locationFrom, onChange: (val) => {
96
97
  setLocationFrom(val);
97
98
  setUserEdited(true);
98
99
  if (formErrors.locationFrom)
@@ -5,6 +5,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
5
5
  import Svg, { Circle, Path } from "react-native-svg";
6
6
  import { useAirportSearch } from "../../hooks/useAirportSearch";
7
7
  import { useApiToken } from "../../../utils/getToken";
8
+ import { useWidget } from "../../../context/WidgetContext";
8
9
  import { Colors, Spacing, FontSize } from "../../../theme/colors";
9
10
  import { formStyles } from "../../../theme/formStyles";
10
11
  import { CloseIcon } from "../../../store";
@@ -51,8 +52,11 @@ const POPULAR_LOCATIONS = [
51
52
  },
52
53
  ];
53
54
  const API_URL = "https://api.travel-code.com/v1/data/airports";
54
- const SearchIcon = () => (_jsxs(Svg, { width: 19, height: 18, viewBox: "0 0 19 18", fill: "none", children: [_jsx(Circle, { cx: 7, cy: 7, r: 6, stroke: Colors.primary, strokeWidth: 2 }), _jsx(Path, { d: "M12 11L17.2857 16.0902", stroke: Colors.primary, strokeWidth: 2, strokeLinecap: "round" })] }));
55
+ const SearchIcon = ({ color = Colors.primary }) => (_jsxs(Svg, { width: 19, height: 18, viewBox: "0 0 19 18", fill: "none", children: [_jsx(Circle, { cx: 7, cy: 7, r: 6, stroke: color, strokeWidth: 2 }), _jsx(Path, { d: "M12 11L17.2857 16.0902", stroke: color, strokeWidth: 2, strokeLinecap: "round" })] }));
55
56
  const LocationInput = ({ label, value, onChange, lang, translations, minimal = false, }) => {
57
+ const { colors } = useWidget();
58
+ const btnColor = colors.primary || Colors.primary;
59
+ const inputBg = colors.input || Colors.inputBg;
56
60
  const token = useApiToken();
57
61
  const insets = useSafeAreaInsets();
58
62
  const getLabel = (loc) => lang === "en" ? loc.titleEn || loc.title : loc.title;
@@ -182,7 +186,7 @@ const LocationInput = ({ label, value, onChange, lang, translations, minimal = f
182
186
  minimal ? styles.minimalText : formStyles.inputTriggerText,
183
187
  !query && formStyles.inputTriggerPlaceholder,
184
188
  ], numberOfLines: 1, children: query || label }), savedValue.code ? (_jsx(Text, { style: formStyles.inputTriggerCode, children: savedValue.code })) : null] }));
185
- return (_jsxs(_Fragment, { children: [minimal ? (_jsx(TouchableOpacity, { style: styles.minimalContainer, onPress: () => setIsPopupOpen(true), activeOpacity: 0.8, children: triggerContent })) : (_jsxs(TouchableOpacity, { style: formStyles.inputTrigger, onPress: () => setIsPopupOpen(true), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: label }), triggerContent] })), _jsx(Modal, { visible: isPopupOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: handleClose, children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: handleClose, hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsxs(View, { style: styles.searchHeader, children: [_jsx(SearchIcon, {}), _jsx(TextInput, { autoFocus: true, style: styles.searchInput, value: query, onChangeText: (text) => {
189
+ return (_jsxs(_Fragment, { children: [minimal ? (_jsx(TouchableOpacity, { style: styles.minimalContainer, onPress: () => setIsPopupOpen(true), activeOpacity: 0.8, children: triggerContent })) : (_jsxs(TouchableOpacity, { style: [formStyles.inputTrigger, { backgroundColor: inputBg }], onPress: () => setIsPopupOpen(true), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: label }), triggerContent] })), _jsx(Modal, { visible: isPopupOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: handleClose, children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsx(TouchableOpacity, { style: styles.closeButton, onPress: handleClose, hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(CloseIcon, { size: 16 }) }), _jsxs(View, { style: styles.searchHeader, children: [_jsx(SearchIcon, { color: btnColor }), _jsx(TextInput, { autoFocus: true, style: styles.searchInput, value: query, onChangeText: (text) => {
186
190
  setQuery(text);
187
191
  setAllowSearch(true);
188
192
  }, placeholder: translations["location_popup_placeholder"] ||
@@ -3,6 +3,7 @@ import { useState } from "react";
3
3
  import { View, Text, TouchableOpacity, Modal, StyleSheet, ScrollView, } from "react-native";
4
4
  import { useSafeAreaInsets } from "react-native-safe-area-context";
5
5
  import Svg, { Path } from "react-native-svg";
6
+ import { useWidget } from "../../../context/WidgetContext";
6
7
  import { Colors, Spacing, BorderRadius, FontSize } from "../../../theme/colors";
7
8
  import { formStyles } from "../../../theme/formStyles";
8
9
  const MAX_COUNT = 10;
@@ -16,8 +17,10 @@ const CABIN_OPTIONS = [
16
17
  { value: "economy_premium", labelKey: "cabin_premium_label", fallback: "Premium Economy" },
17
18
  { value: "business", labelKey: "cabin_business_label", fallback: "Business" },
18
19
  ];
19
- const ChevronDown = () => (_jsx(Svg, { width: 20, height: 20, viewBox: "0 0 20 20", fill: "none", children: _jsx(Path, { d: "M5 8L10 12.5L15 8", stroke: Colors.primary, strokeWidth: 2, strokeLinecap: "round" }) }));
20
+ const ChevronDown = ({ color = Colors.primary }) => (_jsx(Svg, { width: 20, height: 20, viewBox: "0 0 20 20", fill: "none", children: _jsx(Path, { d: "M5 8L10 12.5L15 8", stroke: color, strokeWidth: 2, strokeLinecap: "round" }) }));
20
21
  const PassengersPicker = ({ label, value, onChange, cabinClass, onCabinClassChange, translations, }) => {
22
+ const { colors } = useWidget();
23
+ const btnColor = colors.primary || Colors.primary;
21
24
  const [isModalOpen, setIsModalOpen] = useState(false);
22
25
  const insets = useSafeAreaInsets();
23
26
  const updateCount = (key, delta, min, max) => {
@@ -34,25 +37,27 @@ const PassengersPicker = ({ label, value, onChange, cabinClass, onCabinClassChan
34
37
  : cabinClass === "economy_premium"
35
38
  ? translations["cabin_premium"] || "Premium Economy"
36
39
  : translations["cabin_economy"] || "Economy";
37
- return (_jsxs(_Fragment, { children: [_jsxs(TouchableOpacity, { style: formStyles.inputTrigger, onPress: () => setIsModalOpen(true), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: labelFinal }), _jsxs(View, { style: formStyles.inputTriggerRow, children: [_jsxs(Text, { style: styles.triggerText, children: [_jsxs(Text, { style: styles.triggerCount, children: [total, " "] }), _jsxs(Text, { children: [labelText, ", "] }), _jsx(Text, { children: cabinLabel })] }), _jsx(ChevronDown, {})] })] }), _jsx(Modal, { visible: isModalOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setIsModalOpen(false), children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsxs(View, { style: styles.modalHeader, children: [_jsx(TouchableOpacity, { onPress: () => setIsModalOpen(false), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(Svg, { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", children: _jsx(Path, { d: "M15 18L9 12L15 6", stroke: Colors.textSecondary, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsxs(View, { style: styles.modalHeaderInfo, children: [_jsx(Text, { style: styles.modalTitle, children: translations["passengers_popup_title"] || "Passengers & Class" }), _jsxs(Text, { style: styles.modalSubtitle, children: [total, " ", labelText, ", ", cabinLabel] })] })] }), _jsxs(ScrollView, { style: styles.modalContent, showsVerticalScrollIndicator: false, children: [_jsx(Text, { style: styles.sectionTitle, children: translations["passengers_label"] || "Passengers" }), PASSENGER_TYPES.map(({ key, labelKey, fallback, min }) => (_jsxs(View, { style: styles.counterRow, children: [_jsx(Text, { style: styles.counterLabel, children: translations[labelKey] || fallback }), _jsxs(View, { style: styles.counterControls, children: [_jsx(TouchableOpacity, { style: [
40
+ return (_jsxs(_Fragment, { children: [_jsxs(TouchableOpacity, { style: [formStyles.inputTrigger, { backgroundColor: colors.input || Colors.inputBg }], onPress: () => setIsModalOpen(true), activeOpacity: 0.8, children: [_jsx(Text, { style: formStyles.label, children: labelFinal }), _jsxs(View, { style: formStyles.inputTriggerRow, children: [_jsxs(Text, { style: styles.triggerText, children: [_jsxs(Text, { style: styles.triggerCount, children: [total, " "] }), _jsxs(Text, { children: [labelText, ", "] }), _jsx(Text, { children: cabinLabel })] }), _jsx(ChevronDown, { color: btnColor })] })] }), _jsx(Modal, { visible: isModalOpen, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: () => setIsModalOpen(false), children: _jsxs(View, { style: [styles.modalContainer, { paddingTop: insets.top }], children: [_jsxs(View, { style: styles.modalHeader, children: [_jsx(TouchableOpacity, { onPress: () => setIsModalOpen(false), hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsx(Svg, { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", children: _jsx(Path, { d: "M15 18L9 12L15 6", stroke: Colors.textSecondary, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsxs(View, { style: styles.modalHeaderInfo, children: [_jsx(Text, { style: styles.modalTitle, children: translations["passengers_popup_title"] || "Passengers & Class" }), _jsxs(Text, { style: styles.modalSubtitle, children: [total, " ", labelText, ", ", cabinLabel] })] })] }), _jsxs(ScrollView, { style: styles.modalContent, showsVerticalScrollIndicator: false, children: [_jsx(Text, { style: styles.sectionTitle, children: translations["passengers_label"] || "Passengers" }), PASSENGER_TYPES.map(({ key, labelKey, fallback, min }) => (_jsxs(View, { style: styles.counterRow, children: [_jsx(Text, { style: styles.counterLabel, children: translations[labelKey] || fallback }), _jsxs(View, { style: styles.counterControls, children: [_jsx(TouchableOpacity, { style: [
38
41
  styles.counterButton,
39
42
  value[key] <= min && styles.counterButtonDisabled,
40
43
  ], onPress: () => updateCount(key, -1, min, MAX_COUNT), disabled: value[key] <= min, activeOpacity: 0.7, children: _jsx(Text, { style: [
41
44
  styles.counterButtonText,
45
+ { color: btnColor },
42
46
  value[key] <= min && styles.counterButtonTextDisabled,
43
47
  ], children: "-" }) }), _jsx(Text, { style: styles.counterValue, children: value[key] }), _jsx(TouchableOpacity, { style: [
44
48
  styles.counterButton,
45
49
  value[key] >= MAX_COUNT && styles.counterButtonDisabled,
46
50
  ], onPress: () => updateCount(key, 1, min, MAX_COUNT), disabled: value[key] >= MAX_COUNT, activeOpacity: 0.7, children: _jsx(Text, { style: [
47
51
  styles.counterButtonText,
52
+ { color: btnColor },
48
53
  value[key] >= MAX_COUNT && styles.counterButtonTextDisabled,
49
54
  ], children: "+" }) })] })] }, key))), _jsx(Text, { style: [styles.sectionTitle, { marginTop: Spacing.xxl }], children: translations["cabin_label"] || "Class" }), CABIN_OPTIONS.map((option) => {
50
55
  const isSelected = cabinClass === option.value;
51
- return (_jsxs(TouchableOpacity, { style: styles.radioRow, onPress: () => onCabinClassChange(option.value), activeOpacity: 0.7, children: [_jsx(View, { style: [styles.radio, isSelected && styles.radioSelected], children: isSelected && _jsx(View, { style: styles.radioInner }) }), _jsx(Text, { style: [
56
+ return (_jsxs(TouchableOpacity, { style: styles.radioRow, onPress: () => onCabinClassChange(option.value), activeOpacity: 0.7, children: [_jsx(View, { style: [styles.radio, isSelected && styles.radioSelected, isSelected && { borderColor: btnColor }], children: isSelected && _jsx(View, { style: [styles.radioInner, { backgroundColor: btnColor }] }) }), _jsx(Text, { style: [
52
57
  styles.radioLabel,
53
58
  isSelected && styles.radioLabelSelected,
54
59
  ], children: translations[option.labelKey] || option.fallback })] }, option.value));
55
- })] }), _jsx(View, { style: styles.modalFooter, children: _jsx(TouchableOpacity, { style: styles.saveButton, onPress: () => setIsModalOpen(false), activeOpacity: 0.8, children: _jsx(Text, { style: styles.saveButtonText, children: translations["save_button"] || "Save" }) }) })] }) })] }));
60
+ })] }), _jsx(View, { style: styles.modalFooter, children: _jsx(TouchableOpacity, { style: [styles.saveButton, { backgroundColor: btnColor }], onPress: () => setIsModalOpen(false), activeOpacity: 0.8, children: _jsx(Text, { style: styles.saveButtonText, children: translations["save_button"] || "Save" }) }) })] }) })] }));
56
61
  };
57
62
  const styles = StyleSheet.create({
58
63
  triggerText: {
@@ -2,8 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useRef } from "react";
3
3
  import { TouchableOpacity, StyleSheet, Animated, Easing, } from "react-native";
4
4
  import Svg, { Path } from "react-native-svg";
5
+ import { useWidget } from "../../../context/WidgetContext";
5
6
  import { Colors, BorderRadius } from "../../../theme/colors";
6
7
  const SwitchLocationBtn = ({ from, to, setFrom, setTo, }) => {
8
+ const { colors } = useWidget();
9
+ const btnColor = colors.primary || Colors.primary;
7
10
  const rotation = useRef(new Animated.Value(0)).current;
8
11
  const handleSwitch = () => {
9
12
  const prevFrom = from;
@@ -25,7 +28,7 @@ const SwitchLocationBtn = ({ from, to, setFrom, setTo, }) => {
25
28
  return (_jsx(Animated.View, { style: [
26
29
  styles.container,
27
30
  { transform: [{ rotate: rotateInterpolate }] },
28
- ], children: _jsx(TouchableOpacity, { onPress: handleSwitch, style: styles.button, activeOpacity: 0.7, hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsxs(Svg, { width: 20, height: 20, viewBox: "0 0 20 20", fill: "none", children: [_jsx(Path, { opacity: 0.3, d: "M13.3334 14.1667C13.7937 14.1667 14.1667 13.7936 14.1667 13.3333C14.1667 12.8731 13.7937 12.5 13.3334 12.5L5.00008 12.5C4.53984 12.5 4.16675 12.8731 4.16675 13.3333C4.16675 13.7936 4.53984 14.1667 5.00008 14.1667L13.3334 14.1667Z", fill: Colors.primary }), _jsx(Path, { d: "M11.9108 11.4224C11.5854 11.097 11.5854 10.5694 11.9108 10.2439C12.2363 9.91848 12.7639 9.91848 13.0893 10.2439L15.5893 12.7439C15.9148 13.0694 15.9148 13.597 15.5893 13.9224L13.0893 16.4224C12.7639 16.7479 12.2363 16.7479 11.9108 16.4224C11.5854 16.097 11.5854 15.5694 11.9108 15.2439L13.8216 13.3332L11.9108 11.4224Z", fill: Colors.primary }), _jsx(Path, { opacity: 0.3, d: "M6.66659 7.50016C6.20635 7.50016 5.83325 7.12707 5.83325 6.66683C5.83325 6.20659 6.20635 5.8335 6.66659 5.8335L14.9999 5.8335C15.4602 5.8335 15.8333 6.20659 15.8333 6.66683C15.8333 7.12707 15.4602 7.50016 14.9999 7.50016L6.66659 7.50016Z", fill: Colors.primary }), _jsx(Path, { d: "M8.08917 4.75592C8.41461 4.43049 8.41461 3.90285 8.08917 3.57741C7.76374 3.25197 7.2361 3.25197 6.91066 3.57741L4.41066 6.07741C4.08523 6.40285 4.08523 6.93049 4.41066 7.25592L6.91066 9.75592C7.2361 10.0814 7.76374 10.0814 8.08917 9.75592C8.41461 9.43049 8.41461 8.90285 8.08917 8.57741L6.17843 6.66667L8.08917 4.75592Z", fill: Colors.primary })] }) }) }));
31
+ ], children: _jsx(TouchableOpacity, { onPress: handleSwitch, style: styles.button, activeOpacity: 0.7, hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }, children: _jsxs(Svg, { width: 20, height: 20, viewBox: "0 0 20 20", fill: "none", children: [_jsx(Path, { opacity: 0.3, d: "M13.3334 14.1667C13.7937 14.1667 14.1667 13.7936 14.1667 13.3333C14.1667 12.8731 13.7937 12.5 13.3334 12.5L5.00008 12.5C4.53984 12.5 4.16675 12.8731 4.16675 13.3333C4.16675 13.7936 4.53984 14.1667 5.00008 14.1667L13.3334 14.1667Z", fill: btnColor }), _jsx(Path, { d: "M11.9108 11.4224C11.5854 11.097 11.5854 10.5694 11.9108 10.2439C12.2363 9.91848 12.7639 9.91848 13.0893 10.2439L15.5893 12.7439C15.9148 13.0694 15.9148 13.597 15.5893 13.9224L13.0893 16.4224C12.7639 16.7479 12.2363 16.7479 11.9108 16.4224C11.5854 16.097 11.5854 15.5694 11.9108 15.2439L13.8216 13.3332L11.9108 11.4224Z", fill: btnColor }), _jsx(Path, { opacity: 0.3, d: "M6.66659 7.50016C6.20635 7.50016 5.83325 7.12707 5.83325 6.66683C5.83325 6.20659 6.20635 5.8335 6.66659 5.8335L14.9999 5.8335C15.4602 5.8335 15.8333 6.20659 15.8333 6.66683C15.8333 7.12707 15.4602 7.50016 14.9999 7.50016L6.66659 7.50016Z", fill: btnColor }), _jsx(Path, { d: "M8.08917 4.75592C8.41461 4.43049 8.41461 3.90285 8.08917 3.57741C7.76374 3.25197 7.2361 3.25197 6.91066 3.57741L4.41066 6.07741C4.08523 6.40285 4.08523 6.93049 4.41066 7.25592L6.91066 9.75592C7.2361 10.0814 7.76374 10.0814 8.08917 9.75592C8.41461 9.43049 8.41461 8.90285 8.08917 8.57741L6.17843 6.66667L8.08917 4.75592Z", fill: btnColor })] }) }) }));
29
32
  };
30
33
  const styles = StyleSheet.create({
31
34
  container: {
@@ -16,6 +16,7 @@ type FiltersContentProps = {
16
16
  maxDayMinutes: number;
17
17
  onRangeDragStart?: () => void;
18
18
  onRangeDragEnd?: () => void;
19
+ accentColor?: string;
19
20
  };
20
21
  declare const FiltersContent: React.FC<FiltersContentProps>;
21
22
  export default FiltersContent;