related-ui-components 1.3.4 → 1.3.5

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 (188) hide show
  1. package/lib/commonjs/app.js +39 -20
  2. package/lib/commonjs/app.js.map +1 -1
  3. package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +358 -0
  4. package/lib/commonjs/components/DateRangePicker/DateRangePicker.js.map +1 -0
  5. package/lib/commonjs/components/DateRangePicker/index.js +4 -0
  6. package/lib/commonjs/components/DateRangePicker/index.js.map +1 -0
  7. package/lib/commonjs/components/Filters/Filters.js +1 -1
  8. package/lib/commonjs/components/Filters/Filters.js.map +1 -1
  9. package/lib/commonjs/components/Input/Input.js +2 -2
  10. package/lib/commonjs/components/Input/Input.js.map +1 -1
  11. package/lib/commonjs/components/NumericStepper/NumericStepper.js +103 -0
  12. package/lib/commonjs/components/NumericStepper/NumericStepper.js.map +1 -0
  13. package/lib/commonjs/components/NumericStepper/index.js +29 -0
  14. package/lib/commonjs/components/NumericStepper/index.js.map +1 -0
  15. package/lib/commonjs/components/SelectAmount/SelectAmount.js +189 -0
  16. package/lib/commonjs/components/SelectAmount/SelectAmount.js.map +1 -0
  17. package/lib/commonjs/components/SelectAmount/index.js +4 -0
  18. package/lib/commonjs/components/SelectAmount/index.js.map +1 -0
  19. package/lib/commonjs/components/Suggestions/SuggestionList.js +98 -0
  20. package/lib/commonjs/components/Suggestions/SuggestionList.js.map +1 -0
  21. package/lib/commonjs/components/TravelBooking/CarRentalForm.js +370 -0
  22. package/lib/commonjs/components/TravelBooking/CarRentalForm.js.map +1 -0
  23. package/lib/commonjs/components/TravelBooking/FlightForm.js +347 -0
  24. package/lib/commonjs/components/TravelBooking/FlightForm.js.map +1 -0
  25. package/lib/commonjs/components/TravelBooking/FlightSummary.js +257 -0
  26. package/lib/commonjs/components/TravelBooking/FlightSummary.js.map +1 -0
  27. package/lib/commonjs/components/TravelBooking/HotelForm.js +290 -0
  28. package/lib/commonjs/components/TravelBooking/HotelForm.js.map +1 -0
  29. package/lib/commonjs/components/TravelBooking/HotelSummary.js +246 -0
  30. package/lib/commonjs/components/TravelBooking/HotelSummary.js.map +1 -0
  31. package/lib/commonjs/components/TravelBooking/README.md +421 -0
  32. package/lib/commonjs/components/TravelBooking/SummaryBar.js +181 -0
  33. package/lib/commonjs/components/TravelBooking/SummaryBar.js.map +1 -0
  34. package/lib/commonjs/components/TravelBooking/TabSelector.js +115 -0
  35. package/lib/commonjs/components/TravelBooking/TabSelector.js.map +1 -0
  36. package/lib/commonjs/components/TravelBooking/TravelBooking.js +261 -0
  37. package/lib/commonjs/components/TravelBooking/TravelBooking.js.map +1 -0
  38. package/lib/commonjs/components/TravelBooking/index.js +22 -0
  39. package/lib/commonjs/components/TravelBooking/index.js.map +1 -0
  40. package/lib/commonjs/components/TravelBooking/types.js +15 -0
  41. package/lib/commonjs/components/TravelBooking/types.js.map +1 -0
  42. package/lib/commonjs/components/UnlockRewards/UnlockRewards.js +2 -2
  43. package/lib/commonjs/components/UnlockRewards/UnlockRewards.js.map +1 -1
  44. package/lib/commonjs/index.js +0 -5
  45. package/lib/commonjs/index.js.map +1 -1
  46. package/lib/module/app.js +43 -24
  47. package/lib/module/app.js.map +1 -1
  48. package/lib/module/components/DateRangePicker/DateRangePicker.js +350 -0
  49. package/lib/module/components/DateRangePicker/DateRangePicker.js.map +1 -0
  50. package/lib/module/components/DateRangePicker/index.js +4 -0
  51. package/lib/module/components/DateRangePicker/index.js.map +1 -0
  52. package/lib/module/components/Filters/Filters.js +1 -1
  53. package/lib/module/components/Filters/Filters.js.map +1 -1
  54. package/lib/module/components/Input/Input.js +2 -2
  55. package/lib/module/components/Input/Input.js.map +1 -1
  56. package/lib/module/components/NumericStepper/NumericStepper.js +96 -0
  57. package/lib/module/components/NumericStepper/NumericStepper.js.map +1 -0
  58. package/lib/module/components/NumericStepper/index.js +5 -0
  59. package/lib/module/components/NumericStepper/index.js.map +1 -0
  60. package/lib/module/components/SelectAmount/SelectAmount.js +184 -0
  61. package/lib/module/components/SelectAmount/SelectAmount.js.map +1 -0
  62. package/lib/module/components/SelectAmount/index.js +4 -0
  63. package/lib/module/components/SelectAmount/index.js.map +1 -0
  64. package/lib/module/components/Suggestions/SuggestionList.js +92 -0
  65. package/lib/module/components/Suggestions/SuggestionList.js.map +1 -0
  66. package/lib/module/components/TravelBooking/CarRentalForm.js +363 -0
  67. package/lib/module/components/TravelBooking/CarRentalForm.js.map +1 -0
  68. package/lib/module/components/TravelBooking/FlightForm.js +340 -0
  69. package/lib/module/components/TravelBooking/FlightForm.js.map +1 -0
  70. package/lib/module/components/TravelBooking/FlightSummary.js +254 -0
  71. package/lib/module/components/TravelBooking/FlightSummary.js.map +1 -0
  72. package/lib/module/components/TravelBooking/HotelForm.js +286 -0
  73. package/lib/module/components/TravelBooking/HotelForm.js.map +1 -0
  74. package/lib/module/components/TravelBooking/HotelSummary.js +239 -0
  75. package/lib/module/components/TravelBooking/HotelSummary.js.map +1 -0
  76. package/lib/module/components/TravelBooking/README.md +421 -0
  77. package/lib/module/components/TravelBooking/SummaryBar.js +174 -0
  78. package/lib/module/components/TravelBooking/SummaryBar.js.map +1 -0
  79. package/lib/module/components/TravelBooking/TabSelector.js +110 -0
  80. package/lib/module/components/TravelBooking/TabSelector.js.map +1 -0
  81. package/lib/module/components/TravelBooking/TravelBooking.js +243 -0
  82. package/lib/module/components/TravelBooking/TravelBooking.js.map +1 -0
  83. package/lib/module/components/TravelBooking/index.js +22 -0
  84. package/lib/module/components/TravelBooking/index.js.map +1 -0
  85. package/lib/module/components/TravelBooking/types.js +11 -0
  86. package/lib/module/components/TravelBooking/types.js.map +1 -0
  87. package/lib/module/components/UnlockRewards/UnlockRewards.js +2 -2
  88. package/lib/module/components/UnlockRewards/UnlockRewards.js.map +1 -1
  89. package/lib/module/index.js +7 -4
  90. package/lib/module/index.js.map +1 -1
  91. package/lib/typescript/commonjs/app.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/components/DateRangePicker/DateRangePicker.d.ts +25 -0
  93. package/lib/typescript/commonjs/components/DateRangePicker/DateRangePicker.d.ts.map +1 -0
  94. package/lib/typescript/commonjs/components/DateRangePicker/index.d.ts +1 -0
  95. package/lib/typescript/commonjs/components/DateRangePicker/index.d.ts.map +1 -0
  96. package/lib/typescript/commonjs/components/NumericStepper/NumericStepper.d.ts +19 -0
  97. package/lib/typescript/commonjs/components/NumericStepper/NumericStepper.d.ts.map +1 -0
  98. package/lib/typescript/commonjs/components/NumericStepper/index.d.ts +3 -0
  99. package/lib/typescript/commonjs/components/NumericStepper/index.d.ts.map +1 -0
  100. package/lib/typescript/commonjs/components/SelectAmount/SelectAmount.d.ts +29 -0
  101. package/lib/typescript/commonjs/components/SelectAmount/SelectAmount.d.ts.map +1 -0
  102. package/lib/typescript/commonjs/components/SelectAmount/index.d.ts +1 -0
  103. package/lib/typescript/commonjs/components/SelectAmount/index.d.ts.map +1 -0
  104. package/lib/typescript/commonjs/components/Suggestions/SuggestionList.d.ts +14 -0
  105. package/lib/typescript/commonjs/components/Suggestions/SuggestionList.d.ts.map +1 -0
  106. package/lib/typescript/commonjs/components/TravelBooking/CarRentalForm.d.ts +58 -0
  107. package/lib/typescript/commonjs/components/TravelBooking/CarRentalForm.d.ts.map +1 -0
  108. package/lib/typescript/commonjs/components/TravelBooking/FlightForm.d.ts +57 -0
  109. package/lib/typescript/commonjs/components/TravelBooking/FlightForm.d.ts.map +1 -0
  110. package/lib/typescript/commonjs/components/TravelBooking/FlightSummary.d.ts +41 -0
  111. package/lib/typescript/commonjs/components/TravelBooking/FlightSummary.d.ts.map +1 -0
  112. package/lib/typescript/commonjs/components/TravelBooking/HotelForm.d.ts +52 -0
  113. package/lib/typescript/commonjs/components/TravelBooking/HotelForm.d.ts.map +1 -0
  114. package/lib/typescript/commonjs/components/TravelBooking/HotelSummary.d.ts +44 -0
  115. package/lib/typescript/commonjs/components/TravelBooking/HotelSummary.d.ts.map +1 -0
  116. package/lib/typescript/commonjs/components/TravelBooking/SummaryBar.d.ts +17 -0
  117. package/lib/typescript/commonjs/components/TravelBooking/SummaryBar.d.ts.map +1 -0
  118. package/lib/typescript/commonjs/components/TravelBooking/TabSelector.d.ts +17 -0
  119. package/lib/typescript/commonjs/components/TravelBooking/TabSelector.d.ts.map +1 -0
  120. package/lib/typescript/commonjs/components/TravelBooking/TravelBooking.d.ts +84 -0
  121. package/lib/typescript/commonjs/components/TravelBooking/TravelBooking.d.ts.map +1 -0
  122. package/lib/typescript/commonjs/components/TravelBooking/index.d.ts +1 -0
  123. package/lib/typescript/commonjs/components/TravelBooking/index.d.ts.map +1 -0
  124. package/lib/typescript/commonjs/components/TravelBooking/types.d.ts +26 -0
  125. package/lib/typescript/commonjs/components/TravelBooking/types.d.ts.map +1 -0
  126. package/lib/typescript/commonjs/index.d.ts +0 -1
  127. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  128. package/lib/typescript/module/app.d.ts.map +1 -1
  129. package/lib/typescript/module/components/DateRangePicker/DateRangePicker.d.ts +25 -0
  130. package/lib/typescript/module/components/DateRangePicker/DateRangePicker.d.ts.map +1 -0
  131. package/lib/typescript/module/components/DateRangePicker/index.d.ts +1 -0
  132. package/lib/typescript/module/components/DateRangePicker/index.d.ts.map +1 -0
  133. package/lib/typescript/module/components/NumericStepper/NumericStepper.d.ts +19 -0
  134. package/lib/typescript/module/components/NumericStepper/NumericStepper.d.ts.map +1 -0
  135. package/lib/typescript/module/components/NumericStepper/index.d.ts +3 -0
  136. package/lib/typescript/module/components/NumericStepper/index.d.ts.map +1 -0
  137. package/lib/typescript/module/components/SelectAmount/SelectAmount.d.ts +29 -0
  138. package/lib/typescript/module/components/SelectAmount/SelectAmount.d.ts.map +1 -0
  139. package/lib/typescript/module/components/SelectAmount/index.d.ts +1 -0
  140. package/lib/typescript/module/components/SelectAmount/index.d.ts.map +1 -0
  141. package/lib/typescript/module/components/Suggestions/SuggestionList.d.ts +14 -0
  142. package/lib/typescript/module/components/Suggestions/SuggestionList.d.ts.map +1 -0
  143. package/lib/typescript/module/components/TravelBooking/CarRentalForm.d.ts +58 -0
  144. package/lib/typescript/module/components/TravelBooking/CarRentalForm.d.ts.map +1 -0
  145. package/lib/typescript/module/components/TravelBooking/FlightForm.d.ts +57 -0
  146. package/lib/typescript/module/components/TravelBooking/FlightForm.d.ts.map +1 -0
  147. package/lib/typescript/module/components/TravelBooking/FlightSummary.d.ts +41 -0
  148. package/lib/typescript/module/components/TravelBooking/FlightSummary.d.ts.map +1 -0
  149. package/lib/typescript/module/components/TravelBooking/HotelForm.d.ts +52 -0
  150. package/lib/typescript/module/components/TravelBooking/HotelForm.d.ts.map +1 -0
  151. package/lib/typescript/module/components/TravelBooking/HotelSummary.d.ts +44 -0
  152. package/lib/typescript/module/components/TravelBooking/HotelSummary.d.ts.map +1 -0
  153. package/lib/typescript/module/components/TravelBooking/SummaryBar.d.ts +17 -0
  154. package/lib/typescript/module/components/TravelBooking/SummaryBar.d.ts.map +1 -0
  155. package/lib/typescript/module/components/TravelBooking/TabSelector.d.ts +17 -0
  156. package/lib/typescript/module/components/TravelBooking/TabSelector.d.ts.map +1 -0
  157. package/lib/typescript/module/components/TravelBooking/TravelBooking.d.ts +84 -0
  158. package/lib/typescript/module/components/TravelBooking/TravelBooking.d.ts.map +1 -0
  159. package/lib/typescript/module/components/TravelBooking/index.d.ts +1 -0
  160. package/lib/typescript/module/components/TravelBooking/index.d.ts.map +1 -0
  161. package/lib/typescript/module/components/TravelBooking/types.d.ts +26 -0
  162. package/lib/typescript/module/components/TravelBooking/types.d.ts.map +1 -0
  163. package/lib/typescript/module/index.d.ts +0 -1
  164. package/lib/typescript/module/index.d.ts.map +1 -1
  165. package/package.json +12 -8
  166. package/src/app.tsx +77 -13
  167. package/src/components/DateRangePicker/DateRangePicker.tsx +410 -0
  168. package/src/components/DateRangePicker/index.ts +2 -0
  169. package/src/components/Filters/Filters.tsx +1 -1
  170. package/src/components/Input/Input.tsx +1 -1
  171. package/src/components/NumericStepper/NumericStepper.tsx +117 -0
  172. package/src/components/NumericStepper/index.ts +2 -0
  173. package/src/components/SelectAmount/SelectAmount.tsx +217 -0
  174. package/src/components/SelectAmount/index.ts +2 -0
  175. package/src/components/Suggestions/SuggestionList.tsx +106 -0
  176. package/src/components/TravelBooking/CarRentalForm.tsx +440 -0
  177. package/src/components/TravelBooking/FlightForm.tsx +452 -0
  178. package/src/components/TravelBooking/FlightSummary.tsx +388 -0
  179. package/src/components/TravelBooking/HotelForm.tsx +365 -0
  180. package/src/components/TravelBooking/HotelSummary.tsx +340 -0
  181. package/src/components/TravelBooking/README.md +421 -0
  182. package/src/components/TravelBooking/SummaryBar.tsx +239 -0
  183. package/src/components/TravelBooking/TabSelector.tsx +158 -0
  184. package/src/components/TravelBooking/TravelBooking.tsx +407 -0
  185. package/src/components/TravelBooking/index.ts +20 -0
  186. package/src/components/TravelBooking/types.ts +28 -0
  187. package/src/components/UnlockRewards/UnlockRewards.tsx +2 -2
  188. package/src/index.ts +4 -4
@@ -0,0 +1,365 @@
1
+ import { ThemeType, useTheme } from "@/theme";
2
+ import React, { useCallback, useMemo, useRef, useState } from "react";
3
+ import {
4
+ View,
5
+ Text,
6
+ TouchableOpacity,
7
+ StyleSheet,
8
+ StyleProp,
9
+ ViewStyle,
10
+ TextStyle,
11
+ // Added ItemStyle for Picker
12
+ Platform,
13
+ } from "react-native";
14
+ import { Ionicons } from "@expo/vector-icons";
15
+ import CustomInput from "../Input/Input";
16
+ import DateRangePicker from "../DateRangePicker/DateRangePicker";
17
+ import { Picker } from "@react-native-picker/picker";
18
+ import SuggestionList from "../Suggestions/SuggestionList";
19
+ import { FormInputType, HotelSuggestions } from "./types";
20
+ import { TextInput } from "react-native-gesture-handler";
21
+
22
+ // --- Helper Types ---
23
+ type LocationData = string;
24
+
25
+ // --- Component Props ---
26
+ export interface HotelFormProps {
27
+ // Initial values
28
+ initialDestination?: LocationData;
29
+ initialCheckinDate?: string; // Expect YYYY-MM-DD string
30
+ initialCheckoutDate?: string; // Expect YYYY-MM-DD string
31
+ initialNationality?: string; // Added initial nationality
32
+
33
+ // Callbacks
34
+ onTextChange?: (text: string) => void;
35
+ onInputFocus?: (input: FormInputType) => void;
36
+ onSearchPress?: (details: {
37
+ destination: LocationData | undefined; // Renamed from destintation
38
+ checkin: string | undefined; // Send YYYY-MM-DD
39
+ checkout: string | undefined; // Send YYYY-MM-DD
40
+ nationality: string | undefined;
41
+ }) => void;
42
+
43
+ // Labels & Placeholders
44
+ destinationLabel?: string;
45
+ destinationPlaceholder?: string;
46
+ checkinLabel?: string;
47
+ checkoutLabel?: string;
48
+ nationalityLabel?: string; // Added nationality label
49
+
50
+ // Icons
51
+ searchIconName?: keyof typeof Ionicons.glyphMap;
52
+
53
+ // Data & Config
54
+ suggestionData?: HotelSuggestions[];
55
+ nationalityData?: { label: string; value: string }[];
56
+ calendarThemeOverrides?: object;
57
+
58
+ // --- Style Props ---
59
+ containerStyle?: StyleProp<ViewStyle>;
60
+ inputWrapperStyle?: StyleProp<ViewStyle>;
61
+ customInputContainerStyle?: StyleProp<ViewStyle>;
62
+ labelStyle?: StyleProp<TextStyle>;
63
+ checkoutRowStyle?: StyleProp<ViewStyle>;
64
+ searchButtonStyle?: StyleProp<ViewStyle>;
65
+ searchIconStyle?: StyleProp<TextStyle>;
66
+
67
+ // DateRangePicker specific styles (passed down)
68
+ dateRangePickerOuterContainerStyle?: StyleProp<ViewStyle>;
69
+ dateRangePickerLabelStyle?: StyleProp<TextStyle>;
70
+ dateRangePickerInputGroupStyle?: StyleProp<ViewStyle>;
71
+ dateRangePickerCalendarContainerStyle?: StyleProp<ViewStyle>;
72
+
73
+ // SuggestionList specific styles
74
+ suggestionListContainerStyle?: StyleProp<ViewStyle>;
75
+ suggestionItemStyle?: StyleProp<ViewStyle>;
76
+ suggestionItemTextStyle?: StyleProp<TextStyle>;
77
+
78
+ // Nationality Picker specific styles
79
+ nationalityPickerWrapperStyle?: StyleProp<ViewStyle>;
80
+ nationalityPickerLabelStyle?: StyleProp<TextStyle>;
81
+ pickerContainerStyle?: StyleProp<ViewStyle>;
82
+ pickerStyle?: StyleProp<ViewStyle>;
83
+ }
84
+
85
+ // Default Nationality Data (can be overridden by prop)
86
+ const defaultNationalities = [
87
+ { label: "Select Nationality", value: undefined }, // Placeholder
88
+ { label: "United States", value: "US" },
89
+ { label: "Canada", value: "CA" },
90
+ { label: "United Kingdom", value: "GB" }, // Corrected UK code
91
+ { label: "India", value: "IN" },
92
+ { label: "Australia", value: "AU" },
93
+ { label: "Germany", value: "DE" },
94
+ { label: "France", value: "FR" },
95
+ // Add more common nationalities or let user provide full list via props
96
+ ];
97
+
98
+ // --- Component ---
99
+ const HotelForm: React.FC<HotelFormProps> = ({
100
+ // Initial values
101
+ initialDestination,
102
+ initialCheckinDate,
103
+ initialCheckoutDate,
104
+ initialNationality,
105
+ // Callbacks
106
+ onSearchPress,
107
+ onInputFocus,
108
+ onTextChange,
109
+ // Labels & Placeholders
110
+ destinationLabel = "DESTINATION",
111
+ destinationPlaceholder = "Enter city or hotel name",
112
+ checkinLabel = "CHECK-IN",
113
+ checkoutLabel = "CHECK-OUT",
114
+ nationalityLabel = "NATIONALITY", // Default label
115
+ // Icons
116
+ searchIconName = "search",
117
+ // Data & Config
118
+ suggestionData,
119
+ nationalityData = defaultNationalities, // Use default or provided data
120
+ calendarThemeOverrides = {},
121
+ // Style Props
122
+ containerStyle,
123
+ inputWrapperStyle,
124
+ customInputContainerStyle,
125
+ labelStyle,
126
+ checkoutRowStyle,
127
+ searchButtonStyle,
128
+ searchIconStyle,
129
+ dateRangePickerOuterContainerStyle,
130
+ dateRangePickerLabelStyle,
131
+ dateRangePickerInputGroupStyle,
132
+ dateRangePickerCalendarContainerStyle,
133
+ suggestionListContainerStyle,
134
+ suggestionItemStyle,
135
+ suggestionItemTextStyle,
136
+ nationalityPickerWrapperStyle,
137
+ nationalityPickerLabelStyle,
138
+ pickerContainerStyle,
139
+ pickerStyle,
140
+ }) => {
141
+ const { theme, isRTL } = useTheme();
142
+ const styles = useMemo(() => themedStyles(theme, isRTL), [theme, isRTL]);
143
+ const destinationInputRef = useRef<TextInput>(null);
144
+
145
+ // --- State ---
146
+ const [destination, setDestination] = useState<LocationData | undefined>(
147
+ initialDestination
148
+ );
149
+ // Note: DateRangePicker uses departure/return internally. Map them to checkin/checkout.
150
+ const [checkinDate, setCheckinDate] = useState<string | undefined>(
151
+ initialCheckinDate
152
+ );
153
+ const [checkoutDate, setCheckoutDate] = useState<string | undefined>(
154
+ initialCheckoutDate
155
+ );
156
+ const [selectedNationality, setSelectedNationality] = useState<
157
+ string | undefined
158
+ >(initialNationality);
159
+ const [focusedInput, setFocusedInput] = useState<FormInputType>();
160
+
161
+ // --- Handlers ---
162
+ const handleSearch = useCallback(() => {
163
+ onSearchPress?.({
164
+ destination: destination, // Corrected name
165
+ checkin: checkinDate,
166
+ checkout: checkoutDate,
167
+ nationality: selectedNationality,
168
+ });
169
+ }, [
170
+ destination,
171
+ checkinDate,
172
+ checkoutDate,
173
+ selectedNationality,
174
+ onSearchPress,
175
+ ]);
176
+
177
+ // --- Shared Suggestion Render Item ---
178
+ const suggestionRenderItem = (item: HotelSuggestions) => (
179
+ <TouchableOpacity
180
+ key={item.id}
181
+ onPress={() => {
182
+ setDestination(item.value);
183
+ destinationInputRef.current?.blur();
184
+ setFocusedInput(undefined); // Hide suggestions
185
+ }}
186
+ style={[styles.suggestionItem, suggestionItemStyle]} // Apply style prop
187
+ >
188
+ <Text style={[styles.suggestionItemText, suggestionItemTextStyle]}>
189
+ {item.value}
190
+ </Text>
191
+ </TouchableOpacity>
192
+ );
193
+
194
+ return (
195
+ <View style={[styles.container, containerStyle]}>
196
+ <View style={[styles.inputWrapper, inputWrapperStyle]}>
197
+ <CustomInput
198
+ ref={destinationInputRef}
199
+ onChangeText={(t) => {
200
+ onTextChange?.(t);
201
+ setDestination(t); // Correctly update state
202
+ }}
203
+ value={destination}
204
+ label={destinationLabel}
205
+ labelStyle={[styles.label, labelStyle]}
206
+ placeholder={destinationPlaceholder}
207
+ onFocus={() => {
208
+ setFocusedInput(FormInputType.HOTEL_DEST);
209
+ onInputFocus?.(FormInputType.HOTEL_DEST);
210
+ }}
211
+ onBlur={() => {
212
+ setTimeout(() => {
213
+ if (focusedInput === FormInputType.HOTEL_DEST) {
214
+ setFocusedInput(undefined);
215
+ }
216
+ }, 150);
217
+ }}
218
+ containerStyle={customInputContainerStyle}
219
+ />
220
+ {suggestionData && (
221
+ <SuggestionList
222
+ data={suggestionData}
223
+ visible={focusedInput === FormInputType.HOTEL_DEST}
224
+ renderItemContent={suggestionRenderItem}
225
+ containerStyle={suggestionListContainerStyle}
226
+ />
227
+ )}
228
+ </View>
229
+
230
+ <DateRangePicker
231
+ departureLabel={checkinLabel}
232
+ returnLabel={checkoutLabel}
233
+ outerContainerStyle={[
234
+ styles.dateRangePickerOuterContainer,
235
+ dateRangePickerOuterContainerStyle,
236
+ ]}
237
+ labelStyle={[labelStyle, dateRangePickerLabelStyle]}
238
+ inputGroupStyle={dateRangePickerInputGroupStyle}
239
+ calendarContainerStyle={dateRangePickerCalendarContainerStyle}
240
+ calendarThemeOverrides={calendarThemeOverrides}
241
+ onDatesChange={(d) => {
242
+ setCheckinDate(d.departure);
243
+ setCheckoutDate(d.return);
244
+ }}
245
+ initialDepartureDate={initialCheckinDate}
246
+ initialReturnDate={initialCheckoutDate}
247
+ isOneWay={false}
248
+ />
249
+
250
+ <View style={[styles.checkoutRow, checkoutRowStyle]}>
251
+ <View
252
+ style={[styles.nationalityPickerWrapper, nationalityPickerWrapperStyle]}
253
+ >
254
+ <Text
255
+ style={[
256
+ styles.label,
257
+ labelStyle,
258
+ nationalityPickerLabelStyle,
259
+ ]}
260
+ >
261
+ {nationalityLabel}
262
+ </Text>
263
+ <View style={[styles.pickerContainer, pickerContainerStyle]}>
264
+ <Picker
265
+ selectedValue={selectedNationality}
266
+ onValueChange={setSelectedNationality}
267
+ style={[styles.picker, pickerStyle]}
268
+ dropdownIconColor={theme.onSurface}
269
+ >
270
+ {nationalityData.map((nat) => (
271
+ <Picker.Item
272
+ key={nat.value || "placeholder"}
273
+ label={nat.label}
274
+ value={nat.value}
275
+ enabled={nat.value !== undefined}
276
+ />
277
+ ))}
278
+ </Picker>
279
+ </View>
280
+ </View>
281
+
282
+ <TouchableOpacity
283
+ style={[styles.searchButton, searchButtonStyle]}
284
+ onPress={handleSearch}
285
+ activeOpacity={0.8}
286
+ >
287
+ <Ionicons
288
+ name={searchIconName}
289
+ size={24}
290
+ color={theme.onPrimary}
291
+ style={searchIconStyle}
292
+ />
293
+ </TouchableOpacity>
294
+ </View>
295
+ </View>
296
+ );
297
+ };
298
+
299
+ // --- Styles ---
300
+ const themedStyles = (theme: ThemeType, isRTL: boolean) =>
301
+ StyleSheet.create({
302
+ container: {
303
+ padding: 16,
304
+ backgroundColor: theme.surface,
305
+ borderBottomRightRadius: 8,
306
+ borderBottomLeftRadius: 8,
307
+ },
308
+ inputWrapper: {
309
+ marginBottom: 15,
310
+ position: "relative",
311
+ },
312
+ label: {
313
+ color: theme.labelText,
314
+ fontSize: 12,
315
+ fontWeight: "600",
316
+ textTransform: "uppercase",
317
+ textAlign: isRTL ? "right" : "left",
318
+ marginBottom: 6,
319
+ },
320
+ dateRangePickerOuterContainer: {
321
+ marginTop: 10,
322
+ },
323
+ checkoutRow: {
324
+ flexDirection: isRTL ? "row-reverse" : "row",
325
+ alignItems: "flex-end",
326
+ marginTop: 20,
327
+ gap: 10,
328
+ },
329
+ nationalityPickerWrapper: {
330
+ flex: 1,
331
+ },
332
+ pickerContainer: {
333
+ borderWidth: 1,
334
+ borderColor: theme.border,
335
+ borderRadius: 8,
336
+ backgroundColor: theme.surface,
337
+ justifyContent: "center",
338
+ height: 50,
339
+ },
340
+ picker: {
341
+ width: "100%",
342
+ color: theme.onSurface,
343
+ },
344
+ searchButton: {
345
+ backgroundColor: theme.primary,
346
+ width: 50,
347
+ height: 50,
348
+ borderRadius: 8,
349
+ justifyContent: "center",
350
+ alignItems: "center",
351
+ },
352
+ suggestionItem: {
353
+ paddingVertical: 10,
354
+ paddingHorizontal: 12,
355
+ borderBottomWidth: StyleSheet.hairlineWidth,
356
+ borderBottomColor: theme.border,
357
+ backgroundColor: theme.surface,
358
+ },
359
+ suggestionItemText: {
360
+ fontSize: 14,
361
+ color: theme.onSurface,
362
+ },
363
+ });
364
+
365
+ export default HotelForm;
@@ -0,0 +1,340 @@
1
+ import { ThemeType, useTheme } from "@/theme";
2
+ import React, { useCallback, useEffect, useMemo, useState } from "react";
3
+ import {
4
+ StyleProp,
5
+ StyleSheet,
6
+ Text,
7
+ TouchableOpacity,
8
+ View,
9
+ ViewStyle,
10
+ TextStyle,
11
+ } from "react-native";
12
+ import { NumericStepper, NumericStepperProps } from "../NumericStepper";
13
+ // import { ScrollView } from "react-native-gesture-handler";
14
+
15
+ export interface RoomState {
16
+ id: string;
17
+ [key: string]: any;
18
+ }
19
+
20
+ export interface GuestConfigType {
21
+ key: string;
22
+ name: string;
23
+ description: string;
24
+ defaultValue: number;
25
+ minValue: number;
26
+ maxValue?: number;
27
+ }
28
+
29
+ const DEFAULT_GUEST_CONFIG: GuestConfigType[] = [
30
+ {
31
+ key: "adults",
32
+ name: "Adults",
33
+ description: "17+ years old",
34
+ defaultValue: 2,
35
+ minValue: 1,
36
+ maxValue: 5,
37
+ },
38
+ {
39
+ key: "children",
40
+ name: "Children",
41
+ description: "0 - 17 years old",
42
+ defaultValue: 0,
43
+ minValue: 0,
44
+ maxValue: 4,
45
+ },
46
+ ];
47
+
48
+ const createNewRoom = (guestConfig: GuestConfigType[]): RoomState => {
49
+ const newRoom: RoomState = {
50
+ id: Date.now().toString() + Math.random().toString(36).substring(2, 9),
51
+ };
52
+ guestConfig.forEach((guest) => {
53
+ newRoom[guest.key] = guest.defaultValue;
54
+ });
55
+ return newRoom;
56
+ };
57
+
58
+ export interface HotelSummaryProps {
59
+ guestConfigData?: GuestConfigType[];
60
+ selection?: RoomState[];
61
+ onSelectionChange?: (rooms: RoomState[]) => void;
62
+ maxRooms?: number;
63
+ minRooms?: number;
64
+ roomTitlePrefix?: string;
65
+ removeRoomText?: string;
66
+ addRoomText?: string;
67
+ containerStyle?: StyleProp<ViewStyle>;
68
+ roomContainerStyle?: StyleProp<ViewStyle>;
69
+ roomHeaderStyle?: StyleProp<ViewStyle>;
70
+ roomTitleStyle?: StyleProp<TextStyle>;
71
+ removeRoomButtonStyle?: StyleProp<ViewStyle>;
72
+ removeRoomTextStyle?: StyleProp<TextStyle>;
73
+ guestRowStyle?: StyleProp<ViewStyle>;
74
+ guestLabelContainerStyle?: StyleProp<ViewStyle>;
75
+ guestNameStyle?: StyleProp<TextStyle>;
76
+ guestDescriptionStyle?: StyleProp<TextStyle>;
77
+ addRoomButtonStyle?: StyleProp<ViewStyle>;
78
+ addRoomButtonTextStyle?: StyleProp<TextStyle>;
79
+ stepperContainerStyle?: NumericStepperProps["style"];
80
+ stepperButtonStyle?: NumericStepperProps["buttonStyle"];
81
+ stepperButtonTextStyle?: NumericStepperProps["buttonTextStyle"];
82
+ stepperValueTextStyle?: NumericStepperProps["valueTextStyle"];
83
+ }
84
+
85
+ const HotelSummary: React.FC<HotelSummaryProps> = ({
86
+ guestConfigData: guestConfigProp = DEFAULT_GUEST_CONFIG,
87
+ selection,
88
+ onSelectionChange,
89
+ maxRooms = 5,
90
+ minRooms = 1,
91
+ roomTitlePrefix = "Room",
92
+ removeRoomText = "Remove",
93
+ addRoomText = "+ Add Room",
94
+ containerStyle,
95
+ roomContainerStyle,
96
+ roomHeaderStyle,
97
+ roomTitleStyle,
98
+ removeRoomButtonStyle,
99
+ removeRoomTextStyle,
100
+ guestRowStyle,
101
+ guestLabelContainerStyle,
102
+ guestNameStyle,
103
+ guestDescriptionStyle,
104
+ addRoomButtonStyle,
105
+ addRoomButtonTextStyle,
106
+ stepperContainerStyle,
107
+ stepperButtonStyle,
108
+ stepperButtonTextStyle,
109
+ stepperValueTextStyle,
110
+ }) => {
111
+ const { theme, isRTL } = useTheme();
112
+ const styles = useMemo(() => themedStyles(theme, isRTL), [theme, isRTL]);
113
+
114
+ const guestConfig = useMemo(() => guestConfigProp, [guestConfigProp]);
115
+
116
+ const [rooms, setRooms] = useState<RoomState[]>(
117
+ selection && selection.length >= minRooms
118
+ ? selection
119
+ : Array.from({ length: minRooms }, () => createNewRoom(guestConfig))
120
+ );
121
+
122
+ const triggerSelectionChange = useCallback(
123
+ (updatedRooms: RoomState[]) => {
124
+ onSelectionChange?.(updatedRooms);
125
+ },
126
+ [onSelectionChange]
127
+ );
128
+
129
+ const addRoom = useCallback(() => {
130
+ setRooms((prevRooms) => {
131
+ if (prevRooms.length >= maxRooms) {
132
+ return prevRooms;
133
+ }
134
+ const updatedRooms = [...prevRooms, createNewRoom(guestConfig)];
135
+ triggerSelectionChange(updatedRooms);
136
+ return updatedRooms;
137
+ });
138
+ }, [maxRooms, triggerSelectionChange, guestConfig]);
139
+
140
+ const removeRoom = useCallback(
141
+ (roomIdToRemove: string) => {
142
+ setRooms((prevRooms) => {
143
+ if (prevRooms.length <= minRooms) {
144
+ return prevRooms;
145
+ }
146
+ const updatedRooms = prevRooms.filter(
147
+ (room) => room.id !== roomIdToRemove
148
+ );
149
+ triggerSelectionChange(updatedRooms);
150
+ return updatedRooms;
151
+ });
152
+ },
153
+ [triggerSelectionChange, minRooms]
154
+ );
155
+
156
+ const handleGuestChange = useCallback(
157
+ (
158
+ roomId: string,
159
+ guestKey: string,
160
+ action: "increment" | "decrement"
161
+ ) => {
162
+ setRooms((prevRooms) => {
163
+ const guestInfo = guestConfig.find((g) => g.key === guestKey);
164
+ if (!guestInfo) return prevRooms;
165
+
166
+ const minValue = guestInfo.minValue;
167
+ const maxValue = guestInfo.maxValue ?? 99;
168
+ let valueChanged = false;
169
+
170
+ const updatedRooms = prevRooms.map((room) => {
171
+ if (room.id === roomId) {
172
+ const currentVal = room[guestKey];
173
+ let newValue = currentVal;
174
+
175
+ if (action === "increment") {
176
+ newValue = Math.min(maxValue, currentVal + 1);
177
+ } else if (action === "decrement") {
178
+ newValue = Math.max(minValue, currentVal - 1);
179
+ }
180
+
181
+ if (newValue !== currentVal) {
182
+ valueChanged = true;
183
+ return { ...room, [guestKey]: newValue };
184
+ }
185
+ }
186
+ return room;
187
+ });
188
+
189
+ if (valueChanged) {
190
+ triggerSelectionChange(updatedRooms);
191
+ }
192
+ return updatedRooms;
193
+ });
194
+ },
195
+ [triggerSelectionChange, guestConfig]
196
+ );
197
+
198
+ useEffect(() => {
199
+ onSelectionChange?.(rooms);
200
+ }, []);
201
+
202
+ return (
203
+ <View style={[styles.container, containerStyle]}>
204
+ {rooms.map((room, index) => (
205
+ <View key={room.id} style={[styles.roomContainer, roomContainerStyle]}>
206
+ <View style={[styles.roomHeader, roomHeaderStyle]}>
207
+ <Text style={[styles.roomTitle, roomTitleStyle]}>
208
+ {`${roomTitlePrefix} ${index + 1}`}
209
+ </Text>
210
+ {( rooms.length > minRooms) && (
211
+ <TouchableOpacity
212
+ style={removeRoomButtonStyle}
213
+ onPress={() => removeRoom(room.id)}
214
+ >
215
+ <Text style={[styles.removeRoomText, removeRoomTextStyle]}>
216
+ {removeRoomText}
217
+ </Text>
218
+ </TouchableOpacity>
219
+ )}
220
+ </View>
221
+
222
+ {guestConfig.map((guest) => (
223
+ <View key={guest.key} style={[styles.guestRow, guestRowStyle]}>
224
+ <View
225
+ style={[
226
+ styles.guestLabelContainer,
227
+ guestLabelContainerStyle,
228
+ ]}
229
+ >
230
+ <Text style={[styles.guestName, guestNameStyle]}>
231
+ {guest.name}
232
+ </Text>
233
+ <Text
234
+ style={[
235
+ styles.guestDescription,
236
+ guestDescriptionStyle,
237
+ ]}
238
+ >
239
+ {guest.description}
240
+ </Text>
241
+ </View>
242
+ <NumericStepper
243
+ value={room[guest.key]}
244
+ minValue={guest.minValue}
245
+ maxValue={guest.maxValue}
246
+ onIncrement={() =>
247
+ handleGuestChange(room.id, guest.key, "increment")
248
+ }
249
+ onDecrement={() =>
250
+ handleGuestChange(room.id, guest.key, "decrement")
251
+ }
252
+ style={stepperContainerStyle}
253
+ buttonStyle={stepperButtonStyle}
254
+ buttonTextStyle={stepperButtonTextStyle}
255
+ valueTextStyle={stepperValueTextStyle}
256
+ />
257
+ </View>
258
+ ))}
259
+ </View>
260
+ ))}
261
+
262
+ {rooms.length < maxRooms && (
263
+ <TouchableOpacity
264
+ style={[styles.addRoomButton, addRoomButtonStyle]}
265
+ onPress={addRoom}
266
+ >
267
+ <Text style={[styles.addRoomButtonText, addRoomButtonTextStyle]}>
268
+ {addRoomText}
269
+ </Text>
270
+ </TouchableOpacity>
271
+ )}
272
+ </View>
273
+ );
274
+ };
275
+
276
+ const themedStyles = (theme: ThemeType, isRTL: boolean) =>
277
+ StyleSheet.create({
278
+ container: {
279
+ backgroundColor: theme.surface,
280
+ padding: 16,
281
+ // borderRadius: 8,
282
+ width: "100%",
283
+ },
284
+ roomContainer: {
285
+ marginBottom: 20,
286
+ paddingBottom: 10,
287
+ borderBottomWidth: StyleSheet.hairlineWidth,
288
+ borderBottomColor: theme.border,
289
+ },
290
+ roomHeader: {
291
+ flexDirection: isRTL ? "row-reverse" : "row",
292
+ justifyContent: "space-between",
293
+ alignItems: "center",
294
+ marginBottom: 10,
295
+ },
296
+ roomTitle: {
297
+ fontSize: 18,
298
+ fontWeight: "bold",
299
+ color: theme.onSurface,
300
+ },
301
+ removeRoomText: {
302
+ fontSize: 14,
303
+ color: theme.error,
304
+ fontWeight: "500",
305
+ },
306
+ guestRow: {
307
+ flexDirection: isRTL ? "row-reverse" : "row",
308
+ justifyContent: "space-between",
309
+ alignItems: "center",
310
+ paddingVertical: 12,
311
+ },
312
+ guestLabelContainer: {
313
+ flexDirection: "column",
314
+ alignItems: isRTL ? "flex-end" : "flex-start",
315
+ flexShrink: 1,
316
+ marginRight: isRTL ? 0 : 8,
317
+ marginLeft: isRTL ? 8 : 0,
318
+ },
319
+ guestName: {
320
+ fontSize: 16,
321
+ fontWeight: "bold",
322
+ color: theme.onSurface,
323
+ },
324
+ guestDescription: {
325
+ fontSize: 12,
326
+ color: theme.secondaryText || theme.onSurface,
327
+ opacity: 0.8,
328
+ },
329
+ addRoomButton: {
330
+ marginTop: 10,
331
+ alignSelf: isRTL ? "flex-end" : "flex-start",
332
+ },
333
+ addRoomButtonText: {
334
+ fontSize: 16,
335
+ fontWeight: "bold",
336
+ color: theme.primary,
337
+ },
338
+ });
339
+
340
+ export default HotelSummary;