@travelswitchhq/flight-search-react 1.1.7 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -54,7 +54,6 @@ var flightSearchWidgetStyles = `
54
54
  -webkit-font-smoothing: antialiased;
55
55
  -moz-osx-font-smoothing: grayscale;
56
56
  -webkit-tap-highlight-color: transparent;
57
- background: var(--sw-widget-bg);
58
57
  *,
59
58
  *::before,
60
59
  *::after {
@@ -160,7 +159,7 @@ var flightSearchWidgetStyles = `
160
159
  .sw-com-cal .p-disabled{
161
160
  opacity: 0.3;
162
161
  }
163
- .sw-form-field {
162
+ .sw-srh-form-field {
164
163
  display: flex;
165
164
  flex-direction: column;
166
165
  flex: 1 1 auto;
@@ -170,10 +169,10 @@ var flightSearchWidgetStyles = `
170
169
  min-width: 0;
171
170
  overflow: visible;
172
171
  }
173
- .sw-form-field.sw-is-invalid {
172
+ .sw-srh-form-field.sw-is-invalid {
174
173
  box-shadow: inset 0 0 0 1px var(--sw-error);
175
174
  }
176
- .sw-form-field.sw-is-invalid label {
175
+ .sw-srh-form-field.sw-is-invalid label {
177
176
  color: var(--sw-error);
178
177
  }
179
178
  .sw-custom-dropdown.sw-has-error .sw-custom-dropdown-trigger {
@@ -182,7 +181,7 @@ var flightSearchWidgetStyles = `
182
181
  .sw-custom-dropdown.sw-has-error .sw-custom-dropdown-trigger::placeholder {
183
182
  color: var(--sw-error) !important;
184
183
  }
185
- .sw-form-field label {
184
+ .sw-srh-form-field label {
186
185
  display: block;
187
186
  width: 100%;
188
187
  margin: 0;
@@ -196,11 +195,18 @@ var flightSearchWidgetStyles = `
196
195
  text-overflow: ellipsis;
197
196
  text-align: initial;
198
197
  }
198
+ .sw-field-error {
199
+ margin: 2px 0 0;
200
+ color: #f60b0b !important;
201
+ font-size: 12px;
202
+ font-weight: 500;
203
+ line-height: 1.2;
204
+ }
199
205
  .p-button-label {
200
206
  flex: unset;
201
207
  }
202
- .sw-form-field input,
203
- .sw-form-field .p-inputtext {
208
+ .sw-srh-form-field input,
209
+ .sw-srh-form-field .p-inputtext {
204
210
  display: block;
205
211
  width: 100%;
206
212
  margin: 0;
@@ -214,8 +220,8 @@ var flightSearchWidgetStyles = `
214
220
  font-size: 14px;
215
221
  font-weight: 500;
216
222
  }
217
- .sw-form-field input::placeholder,
218
- .sw-form-field .p-inputtext::placeholder {
223
+ .sw-srh-form-field input::placeholder,
224
+ .sw-srh-form-field .p-inputtext::placeholder {
219
225
  color: var(--sw-label-color) !important;
220
226
  }
221
227
  .sw-form-swap {
@@ -233,7 +239,7 @@ var flightSearchWidgetStyles = `
233
239
  cursor: pointer;
234
240
  transform: translate(50%, -50%);
235
241
  }
236
- .sw-srh-wrap.sw-srh-box.sw-pos-rel .sw-form-field.sw-pos-rel:last-child {
242
+ .sw-srh-wrap.sw-srh-box.sw-pos-rel .sw-srh-form-field.sw-pos-rel:last-child {
237
243
  padding-inline-start: 25px;
238
244
  }
239
245
  .sw-custom-dropdown {
@@ -291,6 +297,10 @@ padding-inline-start: 25px;
291
297
  border-radius: var(--sw-radius);
292
298
  box-shadow: var(--sw-widget-shadow);
293
299
  }
300
+ .p-autocomplete-panel ul {
301
+ margin-bottom: 0 !important;
302
+ padding-left: 0 !important;
303
+ }
294
304
  .sw-dropdown::-webkit-scrollbar {
295
305
  display: none;
296
306
  }
@@ -849,7 +859,7 @@ padding-inline-start: 25px;
849
859
  min-width: 0;
850
860
  overflow: visible;
851
861
  }
852
- .sw-srh-wrap .sw-form-field {
862
+ .sw-srh-wrap .sw-srh-form-field {
853
863
  flex: 1 1 50%;
854
864
  min-width: 0;
855
865
  justify-content: center;
@@ -979,6 +989,9 @@ padding-inline-start: 25px;
979
989
  font-size: 12px;
980
990
  font-weight: 400;
981
991
  }
992
+ .sw-airline-autocomplete .p-autocomplete-loader {
993
+ display: none !important;
994
+ }
982
995
  .sw-fsrh-adv-cont {
983
996
  display: flex;
984
997
  align-items: center;
@@ -1172,8 +1185,7 @@ padding-inline-start: 25px;
1172
1185
  display: none;
1173
1186
  }
1174
1187
  }
1175
-
1176
- .sw-container .sw-form-field {
1188
+ .sw-container .sw-srh-form-field {
1177
1189
  border-radius: var(--sw-radius);
1178
1190
  }
1179
1191
  .sw-fsrh-adv-cont{
@@ -1204,7 +1216,7 @@ padding-inline-start: 25px;
1204
1216
  line-height: 1;
1205
1217
  }
1206
1218
  }
1207
- .sw-container .sw-srh-wrap.sw-srh-box.sw-pos-rel .sw-form-field.sw-pos-rel:last-child{
1219
+ .sw-container .sw-srh-wrap.sw-srh-box.sw-pos-rel .sw-srh-form-field.sw-pos-rel:last-child{
1208
1220
  padding-inline-start: 15px;
1209
1221
  }
1210
1222
  .sw-container .p-datepicker-group + .p-datepicker-group .mobileHead {
@@ -1462,6 +1474,10 @@ function formatDisplayDate(date) {
1462
1474
  if (!date || !(date instanceof Date) || isNaN(date.getTime())) return "Select date";
1463
1475
  return date.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" });
1464
1476
  }
1477
+ var isEmptyLocationValue = (value, placeholder) => {
1478
+ const trimmed = (value ?? "").trim();
1479
+ return trimmed.length === 0 || trimmed === placeholder;
1480
+ };
1465
1481
  function FlightSearchWidget({
1466
1482
  config,
1467
1483
  ssoUser
@@ -1524,6 +1540,8 @@ function FlightSearchWidget({
1524
1540
  const [isOriginDropdownOpen, setIsOriginDropdownOpen] = useState(false);
1525
1541
  const [isDestinationDropdownOpen, setIsDestinationDropdownOpen] = useState(false);
1526
1542
  const [isRoomsGuestsMenuOpen, setIsRoomsGuestsMenuOpen] = useState(false);
1543
+ const [roundTripMinDate, setRoundTripMinDate] = useState(/* @__PURE__ */ new Date());
1544
+ const [activeRoundTripField, setActiveRoundTripField] = useState("departure");
1527
1545
  const originDropdownRef = useRef(null);
1528
1546
  const firstOriginDesktopInputRef = useRef(null);
1529
1547
  const destinationDropdownRef = useRef(null);
@@ -1733,6 +1751,12 @@ function FlightSearchWidget({
1733
1751
  getAirlineListBySearch(value).then(setAirlineSuggestions);
1734
1752
  }, DEBOUNCE_MS);
1735
1753
  }, [getAirlineListBySearch]);
1754
+ useEffect(() => {
1755
+ if (typeof window === "undefined") return;
1756
+ if (window.matchMedia("(max-width: 768px)").matches) {
1757
+ setIsOriginDropdownOpen(false);
1758
+ }
1759
+ }, []);
1736
1760
  useEffect(() => () => {
1737
1761
  if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current);
1738
1762
  if (airlineSearchDebounceRef.current) clearTimeout(airlineSearchDebounceRef.current);
@@ -1976,7 +2000,12 @@ function FlightSearchWidget({
1976
2000
  };
1977
2001
  const extractAirportCode = (location) => {
1978
2002
  const match = location.match(/\(([A-Z]{3})\)/);
1979
- return match ? match[1] : location;
2003
+ return match ? match[1] : location.trim();
2004
+ };
2005
+ const getComparableLocation = (location) => {
2006
+ const code = extractAirportCode(location).trim().toUpperCase();
2007
+ if (code.length === 3 && /^[A-Z]{3}$/.test(code)) return code;
2008
+ return location.trim().toLowerCase().replace(/\s+/g, " ");
1980
2009
  };
1981
2010
  const MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1982
2011
  const formatDate = (date) => {
@@ -1995,7 +2024,7 @@ function FlightSearchWidget({
1995
2024
  case "multi-city":
1996
2025
  return 3;
1997
2026
  case "custom-search":
1998
- return 4;
2027
+ return 1;
1999
2028
  default:
2000
2029
  return 2;
2001
2030
  }
@@ -2137,13 +2166,22 @@ function FlightSearchWidget({
2137
2166
  const segmentsToValidate = isMultiCityOrCustom ? segments : segments.slice(0, 1);
2138
2167
  const nextErrors = {};
2139
2168
  segmentsToValidate.forEach((segment, index) => {
2140
- const originInvalid = !isValidLocationValue(segment.origin, "Origin");
2141
- const destinationInvalid = !isValidLocationValue(segment.destination, "Destination");
2169
+ const originValue = typeof segment.origin === "string" ? segment.origin : "";
2170
+ const destinationValue = typeof segment.destination === "string" ? segment.destination : "";
2171
+ const originInvalid = isEmptyLocationValue(originValue, "Origin");
2172
+ const destinationInvalid = isEmptyLocationValue(destinationValue, "Destination");
2142
2173
  if (originInvalid || destinationInvalid) {
2143
2174
  nextErrors[index] = {
2144
2175
  origin: originInvalid,
2145
2176
  destination: destinationInvalid
2146
2177
  };
2178
+ return;
2179
+ }
2180
+ if (getComparableLocation(originValue) === getComparableLocation(destinationValue)) {
2181
+ nextErrors[index] = {
2182
+ origin: true,
2183
+ destination: true
2184
+ };
2147
2185
  }
2148
2186
  });
2149
2187
  if (Object.keys(nextErrors).length > 0) {
@@ -2160,7 +2198,7 @@ function FlightSearchWidget({
2160
2198
  const list = isOrigin ? getPredictiveState(segmentIndex).fromAirport : getPredictiveState(segmentIndex).toAirport;
2161
2199
  const selectedIndex = isOrigin ? getPredictiveState(segmentIndex).selectedFromIndex : getPredictiveState(segmentIndex).selectedToIndex;
2162
2200
  const isFirstOrigin = segmentIndex === 0 && isOrigin;
2163
- return /* @__PURE__ */ jsxs("div", { className: `sw-form-field sw-pos-rel${isOrigin ? " br-left" : ""} ${validationErrors[segmentIndex]?.[dropdownType] ? "sw-is-invalid" : ""}`, children: [
2201
+ return /* @__PURE__ */ jsxs("div", { className: `sw-srh-form-field sw-pos-rel${isOrigin ? " br-left" : ""} ${validationErrors[segmentIndex]?.[dropdownType] ? "sw-is-invalid" : ""}`, children: [
2164
2202
  /* @__PURE__ */ jsx("label", { htmlFor: `${dropdownType}-${segmentIndex}`, children: label }),
2165
2203
  /* @__PURE__ */ jsxs("div", { className: `sw-custom-dropdown ${validationErrors[segmentIndex]?.[dropdownType] ? "sw-has-error" : ""}`, ref: dropdownRef, children: [
2166
2204
  /* @__PURE__ */ jsx(
@@ -2250,6 +2288,47 @@ function FlightSearchWidget({
2250
2288
  if (prev instanceof Date && !isNaN(prev.getTime())) return prev;
2251
2289
  return today;
2252
2290
  };
2291
+ const openRoundTripCalendar = (mode) => {
2292
+ const today = /* @__PURE__ */ new Date();
2293
+ const selectedDeparture = segments[0]?.dateRange?.[0] ?? segments[0]?.departureDate ?? null;
2294
+ const validDeparture = selectedDeparture instanceof Date && !isNaN(selectedDeparture.getTime()) ? selectedDeparture : today;
2295
+ setActiveRoundTripField(mode);
2296
+ setRoundTripMinDate(mode === "return" ? validDeparture : today);
2297
+ dateRangeCalendarRef.current?.show?.();
2298
+ mobileDateRangeCalendarRef.current?.show?.();
2299
+ };
2300
+ const applyRoundTripRangeChange = (range) => {
2301
+ setSegments((prev) => {
2302
+ const updated = [...prev];
2303
+ if (!updated[0]) return prev;
2304
+ const current = updated[0];
2305
+ const currentDeparture = current.dateRange?.[0] ?? current.departureDate ?? /* @__PURE__ */ new Date();
2306
+ if (activeRoundTripField === "return") {
2307
+ const pickedReturn = range?.[1] ?? range?.[0] ?? currentDeparture;
2308
+ const safeReturn = pickedReturn instanceof Date && pickedReturn.getTime() >= currentDeparture.getTime() ? pickedReturn : currentDeparture;
2309
+ updated[0] = {
2310
+ ...current,
2311
+ dateRange: [currentDeparture, safeReturn],
2312
+ departureDate: currentDeparture,
2313
+ returnDate: safeReturn
2314
+ };
2315
+ return updated;
2316
+ }
2317
+ updated[0] = {
2318
+ ...current,
2319
+ dateRange: range,
2320
+ departureDate: range?.[0] ?? /* @__PURE__ */ new Date(),
2321
+ returnDate: range?.[1] ?? /* @__PURE__ */ new Date()
2322
+ };
2323
+ return updated;
2324
+ });
2325
+ if (activeRoundTripField === "return") {
2326
+ setTimeout(() => {
2327
+ dateRangeCalendarRef.current?.hide?.();
2328
+ mobileDateRangeCalendarRef.current?.hide?.();
2329
+ }, 50);
2330
+ }
2331
+ };
2253
2332
  const renderFlightSegment = (index, segment, isMultiCityOrCustom) => {
2254
2333
  const canRemove = segments.length > 2 && index > 0 && isMultiCityOrCustom;
2255
2334
  const originRef = (el) => {
@@ -2269,7 +2348,7 @@ function FlightSearchWidget({
2269
2348
  renderAirportDropdown(index, "destination", destinationDropdownRef || destinationRef, isDestOpen, () => toggleSegmentDropdown(index, "destination"))
2270
2349
  ] }, index),
2271
2350
  isMultiCityOrCustom && /* @__PURE__ */ jsxs("div", { className: "sw-srh-wrap sw-pos-rel", children: [
2272
- /* @__PURE__ */ jsxs("div", { className: "sw-form-field sw-com-cal ", children: [
2351
+ /* @__PURE__ */ jsxs("div", { className: "sw-srh-form-field sw-com-cal", children: [
2273
2352
  /* @__PURE__ */ jsx("label", { htmlFor: `departure-${index}`, children: "Departure" }),
2274
2353
  /* @__PURE__ */ jsx(
2275
2354
  Calendar,
@@ -2389,10 +2468,7 @@ function FlightSearchWidget({
2389
2468
  renderAirportDropdown(0, "destination", destinationDropdownRef, isDestinationDropdownOpen, () => setIsDestinationDropdownOpen(true))
2390
2469
  ] }),
2391
2470
  (tripType === "round-trip" || tripType === "one-way") && /* @__PURE__ */ jsx("div", { className: "sw-srh-wrap sw-pos-rel mobiBox", children: tripType === "round-trip" ? /* @__PURE__ */ jsxs(Fragment, { children: [
2392
- /* @__PURE__ */ jsxs("div", { style: { cursor: "pointer" }, onClick: () => {
2393
- dateRangeCalendarRef.current?.show?.();
2394
- mobileDateRangeCalendarRef.current?.show?.();
2395
- }, className: "sw-form-field sw-com-cal", children: [
2471
+ /* @__PURE__ */ jsxs("div", { style: { cursor: "pointer" }, onClick: () => openRoundTripCalendar("departure"), className: "sw-srh-form-field sw-com-cal", children: [
2396
2472
  /* @__PURE__ */ jsxs("div", { children: [
2397
2473
  /* @__PURE__ */ jsx("label", { children: "Departure" }),
2398
2474
  /* @__PURE__ */ jsx("p", { children: segments[0]?.dateRange?.[0]?.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" }) })
@@ -2406,23 +2482,15 @@ function FlightSearchWidget({
2406
2482
  appendTo: "self",
2407
2483
  selectionMode: "range",
2408
2484
  value: segments[0]?.dateRange ?? null,
2409
- minDate: /* @__PURE__ */ new Date(),
2485
+ minDate: roundTripMinDate,
2410
2486
  onChange: (e) => {
2411
2487
  const range = e.value;
2412
- setSegments((prev) => {
2413
- const u = [...prev];
2414
- if (!u[0]) return prev;
2415
- u[0] = {
2416
- ...u[0],
2417
- dateRange: range,
2418
- departureDate: range?.[0] ?? /* @__PURE__ */ new Date(),
2419
- returnDate: range?.[1] ?? /* @__PURE__ */ new Date()
2420
- };
2421
- return u;
2422
- });
2423
- if (range?.[0] && range?.[1]) setTimeout(() => {
2424
- dateRangeCalendarRef.current?.hide?.();
2425
- }, 150);
2488
+ applyRoundTripRangeChange(range);
2489
+ if (activeRoundTripField === "departure" && range?.[0] && range?.[1]) {
2490
+ setTimeout(() => {
2491
+ dateRangeCalendarRef.current?.hide?.();
2492
+ }, 150);
2493
+ }
2426
2494
  },
2427
2495
  dateFormat: "dd M yy",
2428
2496
  placeholder: "Select date range",
@@ -2438,20 +2506,10 @@ function FlightSearchWidget({
2438
2506
  appendTo: "self",
2439
2507
  selectionMode: "range",
2440
2508
  value: segments[0]?.dateRange ?? null,
2441
- minDate: /* @__PURE__ */ new Date(),
2509
+ minDate: roundTripMinDate,
2442
2510
  onChange: (e) => {
2443
2511
  const range = e.value;
2444
- setSegments((prev) => {
2445
- const u = [...prev];
2446
- if (!u[0]) return prev;
2447
- u[0] = {
2448
- ...u[0],
2449
- dateRange: range,
2450
- departureDate: range?.[0] ?? /* @__PURE__ */ new Date(),
2451
- returnDate: range?.[1] ?? /* @__PURE__ */ new Date()
2452
- };
2453
- return u;
2454
- });
2512
+ applyRoundTripRangeChange(range);
2455
2513
  },
2456
2514
  dateFormat: "dd M yy",
2457
2515
  placeholder: "Select date range",
@@ -2481,10 +2539,7 @@ function FlightSearchWidget({
2481
2539
  }
2482
2540
  )
2483
2541
  ] }),
2484
- /* @__PURE__ */ jsx("div", { style: { cursor: "pointer" }, className: "sw-form-field sw-com-cal", onClick: () => {
2485
- dateRangeCalendarRef.current?.show?.();
2486
- mobileDateRangeCalendarRef.current?.show?.();
2487
- }, children: /* @__PURE__ */ jsxs("div", { children: [
2542
+ /* @__PURE__ */ jsx("div", { style: { cursor: "pointer" }, className: "sw-srh-form-field sw-com-cal", onClick: () => openRoundTripCalendar("return"), children: /* @__PURE__ */ jsxs("div", { children: [
2488
2543
  /* @__PURE__ */ jsx("label", { children: "Return" }),
2489
2544
  /* @__PURE__ */ jsx("p", { children: (segments[0]?.dateRange?.[1] ?? segments[0]?.dateRange?.[0])?.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" }) })
2490
2545
  ] }) })
@@ -2492,7 +2547,7 @@ function FlightSearchWidget({
2492
2547
  /* @__PURE__ */ jsxs(
2493
2548
  "div",
2494
2549
  {
2495
- className: "sw-form-field sw-pos-rel sw-com-cal",
2550
+ className: "sw-srh-form-field sw-pos-rel sw-com-cal",
2496
2551
  style: { cursor: "pointer" },
2497
2552
  onClick: () => {
2498
2553
  oneWayDesktopCalendarRef.current?.show?.();
@@ -2570,7 +2625,7 @@ function FlightSearchWidget({
2570
2625
  ]
2571
2626
  }
2572
2627
  ),
2573
- /* @__PURE__ */ jsx("div", { className: "sw-form-field sw-pos-rel sw-com-cal sw-add-return-label", style: { cursor: "pointer" }, onClick: () => {
2628
+ /* @__PURE__ */ jsx("div", { className: "sw-srh-form-field sw-pos-rel sw-com-cal sw-add-return-label", style: { cursor: "pointer" }, onClick: () => {
2574
2629
  setTripType("round-trip");
2575
2630
  setTimeout(() => document.getElementById("dateRange")?.querySelector("input")?.focus?.(), 100);
2576
2631
  }, children: /* @__PURE__ */ jsx("label", { className: "font14", children: "Add return" }) })
@@ -2582,7 +2637,7 @@ function FlightSearchWidget({
2582
2637
  ] }),
2583
2638
  "Add Another Flight"
2584
2639
  ] }) }),
2585
- /* @__PURE__ */ jsxs("div", { className: "sw-form-field sw-traveller sw-pos-rel", children: [
2640
+ /* @__PURE__ */ jsxs("div", { className: "sw-srh-form-field sw-traveller sw-pos-rel", children: [
2586
2641
  /* @__PURE__ */ jsx("label", { htmlFor: "rooms", children: "Travellers and Class" }),
2587
2642
  /* @__PURE__ */ jsxs("div", { className: "sw-custom-dropdown", ref: roomsGuestsMenuRef, children: [
2588
2643
  /* @__PURE__ */ jsx(
@@ -2745,6 +2800,7 @@ function FlightSearchWidget({
2745
2800
  /* @__PURE__ */ jsx(
2746
2801
  AutoComplete,
2747
2802
  {
2803
+ className: "sw-airline-autocomplete",
2748
2804
  value: selectedAirlines,
2749
2805
  suggestions: airlineSuggestions,
2750
2806
  completeMethod: (e) => {