@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.cjs CHANGED
@@ -80,7 +80,6 @@ var flightSearchWidgetStyles = `
80
80
  -webkit-font-smoothing: antialiased;
81
81
  -moz-osx-font-smoothing: grayscale;
82
82
  -webkit-tap-highlight-color: transparent;
83
- background: var(--sw-widget-bg);
84
83
  *,
85
84
  *::before,
86
85
  *::after {
@@ -186,7 +185,7 @@ var flightSearchWidgetStyles = `
186
185
  .sw-com-cal .p-disabled{
187
186
  opacity: 0.3;
188
187
  }
189
- .sw-form-field {
188
+ .sw-srh-form-field {
190
189
  display: flex;
191
190
  flex-direction: column;
192
191
  flex: 1 1 auto;
@@ -196,10 +195,10 @@ var flightSearchWidgetStyles = `
196
195
  min-width: 0;
197
196
  overflow: visible;
198
197
  }
199
- .sw-form-field.sw-is-invalid {
198
+ .sw-srh-form-field.sw-is-invalid {
200
199
  box-shadow: inset 0 0 0 1px var(--sw-error);
201
200
  }
202
- .sw-form-field.sw-is-invalid label {
201
+ .sw-srh-form-field.sw-is-invalid label {
203
202
  color: var(--sw-error);
204
203
  }
205
204
  .sw-custom-dropdown.sw-has-error .sw-custom-dropdown-trigger {
@@ -208,7 +207,7 @@ var flightSearchWidgetStyles = `
208
207
  .sw-custom-dropdown.sw-has-error .sw-custom-dropdown-trigger::placeholder {
209
208
  color: var(--sw-error) !important;
210
209
  }
211
- .sw-form-field label {
210
+ .sw-srh-form-field label {
212
211
  display: block;
213
212
  width: 100%;
214
213
  margin: 0;
@@ -222,11 +221,18 @@ var flightSearchWidgetStyles = `
222
221
  text-overflow: ellipsis;
223
222
  text-align: initial;
224
223
  }
224
+ .sw-field-error {
225
+ margin: 2px 0 0;
226
+ color: #f60b0b !important;
227
+ font-size: 12px;
228
+ font-weight: 500;
229
+ line-height: 1.2;
230
+ }
225
231
  .p-button-label {
226
232
  flex: unset;
227
233
  }
228
- .sw-form-field input,
229
- .sw-form-field .p-inputtext {
234
+ .sw-srh-form-field input,
235
+ .sw-srh-form-field .p-inputtext {
230
236
  display: block;
231
237
  width: 100%;
232
238
  margin: 0;
@@ -240,8 +246,8 @@ var flightSearchWidgetStyles = `
240
246
  font-size: 14px;
241
247
  font-weight: 500;
242
248
  }
243
- .sw-form-field input::placeholder,
244
- .sw-form-field .p-inputtext::placeholder {
249
+ .sw-srh-form-field input::placeholder,
250
+ .sw-srh-form-field .p-inputtext::placeholder {
245
251
  color: var(--sw-label-color) !important;
246
252
  }
247
253
  .sw-form-swap {
@@ -259,7 +265,7 @@ var flightSearchWidgetStyles = `
259
265
  cursor: pointer;
260
266
  transform: translate(50%, -50%);
261
267
  }
262
- .sw-srh-wrap.sw-srh-box.sw-pos-rel .sw-form-field.sw-pos-rel:last-child {
268
+ .sw-srh-wrap.sw-srh-box.sw-pos-rel .sw-srh-form-field.sw-pos-rel:last-child {
263
269
  padding-inline-start: 25px;
264
270
  }
265
271
  .sw-custom-dropdown {
@@ -317,6 +323,10 @@ padding-inline-start: 25px;
317
323
  border-radius: var(--sw-radius);
318
324
  box-shadow: var(--sw-widget-shadow);
319
325
  }
326
+ .p-autocomplete-panel ul {
327
+ margin-bottom: 0 !important;
328
+ padding-left: 0 !important;
329
+ }
320
330
  .sw-dropdown::-webkit-scrollbar {
321
331
  display: none;
322
332
  }
@@ -875,7 +885,7 @@ padding-inline-start: 25px;
875
885
  min-width: 0;
876
886
  overflow: visible;
877
887
  }
878
- .sw-srh-wrap .sw-form-field {
888
+ .sw-srh-wrap .sw-srh-form-field {
879
889
  flex: 1 1 50%;
880
890
  min-width: 0;
881
891
  justify-content: center;
@@ -1005,6 +1015,9 @@ padding-inline-start: 25px;
1005
1015
  font-size: 12px;
1006
1016
  font-weight: 400;
1007
1017
  }
1018
+ .sw-airline-autocomplete .p-autocomplete-loader {
1019
+ display: none !important;
1020
+ }
1008
1021
  .sw-fsrh-adv-cont {
1009
1022
  display: flex;
1010
1023
  align-items: center;
@@ -1198,8 +1211,7 @@ padding-inline-start: 25px;
1198
1211
  display: none;
1199
1212
  }
1200
1213
  }
1201
-
1202
- .sw-container .sw-form-field {
1214
+ .sw-container .sw-srh-form-field {
1203
1215
  border-radius: var(--sw-radius);
1204
1216
  }
1205
1217
  .sw-fsrh-adv-cont{
@@ -1230,7 +1242,7 @@ padding-inline-start: 25px;
1230
1242
  line-height: 1;
1231
1243
  }
1232
1244
  }
1233
- .sw-container .sw-srh-wrap.sw-srh-box.sw-pos-rel .sw-form-field.sw-pos-rel:last-child{
1245
+ .sw-container .sw-srh-wrap.sw-srh-box.sw-pos-rel .sw-srh-form-field.sw-pos-rel:last-child{
1234
1246
  padding-inline-start: 15px;
1235
1247
  }
1236
1248
  .sw-container .p-datepicker-group + .p-datepicker-group .mobileHead {
@@ -1488,6 +1500,10 @@ function formatDisplayDate(date) {
1488
1500
  if (!date || !(date instanceof Date) || isNaN(date.getTime())) return "Select date";
1489
1501
  return date.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" });
1490
1502
  }
1503
+ var isEmptyLocationValue = (value, placeholder) => {
1504
+ const trimmed = (value ?? "").trim();
1505
+ return trimmed.length === 0 || trimmed === placeholder;
1506
+ };
1491
1507
  function FlightSearchWidget({
1492
1508
  config,
1493
1509
  ssoUser
@@ -1550,6 +1566,8 @@ function FlightSearchWidget({
1550
1566
  const [isOriginDropdownOpen, setIsOriginDropdownOpen] = (0, import_react.useState)(false);
1551
1567
  const [isDestinationDropdownOpen, setIsDestinationDropdownOpen] = (0, import_react.useState)(false);
1552
1568
  const [isRoomsGuestsMenuOpen, setIsRoomsGuestsMenuOpen] = (0, import_react.useState)(false);
1569
+ const [roundTripMinDate, setRoundTripMinDate] = (0, import_react.useState)(/* @__PURE__ */ new Date());
1570
+ const [activeRoundTripField, setActiveRoundTripField] = (0, import_react.useState)("departure");
1553
1571
  const originDropdownRef = (0, import_react.useRef)(null);
1554
1572
  const firstOriginDesktopInputRef = (0, import_react.useRef)(null);
1555
1573
  const destinationDropdownRef = (0, import_react.useRef)(null);
@@ -1759,6 +1777,12 @@ function FlightSearchWidget({
1759
1777
  getAirlineListBySearch(value).then(setAirlineSuggestions);
1760
1778
  }, DEBOUNCE_MS);
1761
1779
  }, [getAirlineListBySearch]);
1780
+ (0, import_react.useEffect)(() => {
1781
+ if (typeof window === "undefined") return;
1782
+ if (window.matchMedia("(max-width: 768px)").matches) {
1783
+ setIsOriginDropdownOpen(false);
1784
+ }
1785
+ }, []);
1762
1786
  (0, import_react.useEffect)(() => () => {
1763
1787
  if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current);
1764
1788
  if (airlineSearchDebounceRef.current) clearTimeout(airlineSearchDebounceRef.current);
@@ -2002,7 +2026,12 @@ function FlightSearchWidget({
2002
2026
  };
2003
2027
  const extractAirportCode = (location) => {
2004
2028
  const match = location.match(/\(([A-Z]{3})\)/);
2005
- return match ? match[1] : location;
2029
+ return match ? match[1] : location.trim();
2030
+ };
2031
+ const getComparableLocation = (location) => {
2032
+ const code = extractAirportCode(location).trim().toUpperCase();
2033
+ if (code.length === 3 && /^[A-Z]{3}$/.test(code)) return code;
2034
+ return location.trim().toLowerCase().replace(/\s+/g, " ");
2006
2035
  };
2007
2036
  const MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
2008
2037
  const formatDate = (date) => {
@@ -2021,7 +2050,7 @@ function FlightSearchWidget({
2021
2050
  case "multi-city":
2022
2051
  return 3;
2023
2052
  case "custom-search":
2024
- return 4;
2053
+ return 1;
2025
2054
  default:
2026
2055
  return 2;
2027
2056
  }
@@ -2163,13 +2192,22 @@ function FlightSearchWidget({
2163
2192
  const segmentsToValidate = isMultiCityOrCustom ? segments : segments.slice(0, 1);
2164
2193
  const nextErrors = {};
2165
2194
  segmentsToValidate.forEach((segment, index) => {
2166
- const originInvalid = !isValidLocationValue(segment.origin, "Origin");
2167
- const destinationInvalid = !isValidLocationValue(segment.destination, "Destination");
2195
+ const originValue = typeof segment.origin === "string" ? segment.origin : "";
2196
+ const destinationValue = typeof segment.destination === "string" ? segment.destination : "";
2197
+ const originInvalid = isEmptyLocationValue(originValue, "Origin");
2198
+ const destinationInvalid = isEmptyLocationValue(destinationValue, "Destination");
2168
2199
  if (originInvalid || destinationInvalid) {
2169
2200
  nextErrors[index] = {
2170
2201
  origin: originInvalid,
2171
2202
  destination: destinationInvalid
2172
2203
  };
2204
+ return;
2205
+ }
2206
+ if (getComparableLocation(originValue) === getComparableLocation(destinationValue)) {
2207
+ nextErrors[index] = {
2208
+ origin: true,
2209
+ destination: true
2210
+ };
2173
2211
  }
2174
2212
  });
2175
2213
  if (Object.keys(nextErrors).length > 0) {
@@ -2186,7 +2224,7 @@ function FlightSearchWidget({
2186
2224
  const list = isOrigin ? getPredictiveState(segmentIndex).fromAirport : getPredictiveState(segmentIndex).toAirport;
2187
2225
  const selectedIndex = isOrigin ? getPredictiveState(segmentIndex).selectedFromIndex : getPredictiveState(segmentIndex).selectedToIndex;
2188
2226
  const isFirstOrigin = segmentIndex === 0 && isOrigin;
2189
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `sw-form-field sw-pos-rel${isOrigin ? " br-left" : ""} ${validationErrors[segmentIndex]?.[dropdownType] ? "sw-is-invalid" : ""}`, children: [
2227
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `sw-srh-form-field sw-pos-rel${isOrigin ? " br-left" : ""} ${validationErrors[segmentIndex]?.[dropdownType] ? "sw-is-invalid" : ""}`, children: [
2190
2228
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: `${dropdownType}-${segmentIndex}`, children: label }),
2191
2229
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `sw-custom-dropdown ${validationErrors[segmentIndex]?.[dropdownType] ? "sw-has-error" : ""}`, ref: dropdownRef, children: [
2192
2230
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -2276,6 +2314,47 @@ function FlightSearchWidget({
2276
2314
  if (prev instanceof Date && !isNaN(prev.getTime())) return prev;
2277
2315
  return today;
2278
2316
  };
2317
+ const openRoundTripCalendar = (mode) => {
2318
+ const today = /* @__PURE__ */ new Date();
2319
+ const selectedDeparture = segments[0]?.dateRange?.[0] ?? segments[0]?.departureDate ?? null;
2320
+ const validDeparture = selectedDeparture instanceof Date && !isNaN(selectedDeparture.getTime()) ? selectedDeparture : today;
2321
+ setActiveRoundTripField(mode);
2322
+ setRoundTripMinDate(mode === "return" ? validDeparture : today);
2323
+ dateRangeCalendarRef.current?.show?.();
2324
+ mobileDateRangeCalendarRef.current?.show?.();
2325
+ };
2326
+ const applyRoundTripRangeChange = (range) => {
2327
+ setSegments((prev) => {
2328
+ const updated = [...prev];
2329
+ if (!updated[0]) return prev;
2330
+ const current = updated[0];
2331
+ const currentDeparture = current.dateRange?.[0] ?? current.departureDate ?? /* @__PURE__ */ new Date();
2332
+ if (activeRoundTripField === "return") {
2333
+ const pickedReturn = range?.[1] ?? range?.[0] ?? currentDeparture;
2334
+ const safeReturn = pickedReturn instanceof Date && pickedReturn.getTime() >= currentDeparture.getTime() ? pickedReturn : currentDeparture;
2335
+ updated[0] = {
2336
+ ...current,
2337
+ dateRange: [currentDeparture, safeReturn],
2338
+ departureDate: currentDeparture,
2339
+ returnDate: safeReturn
2340
+ };
2341
+ return updated;
2342
+ }
2343
+ updated[0] = {
2344
+ ...current,
2345
+ dateRange: range,
2346
+ departureDate: range?.[0] ?? /* @__PURE__ */ new Date(),
2347
+ returnDate: range?.[1] ?? /* @__PURE__ */ new Date()
2348
+ };
2349
+ return updated;
2350
+ });
2351
+ if (activeRoundTripField === "return") {
2352
+ setTimeout(() => {
2353
+ dateRangeCalendarRef.current?.hide?.();
2354
+ mobileDateRangeCalendarRef.current?.hide?.();
2355
+ }, 50);
2356
+ }
2357
+ };
2279
2358
  const renderFlightSegment = (index, segment, isMultiCityOrCustom) => {
2280
2359
  const canRemove = segments.length > 2 && index > 0 && isMultiCityOrCustom;
2281
2360
  const originRef = (el) => {
@@ -2295,7 +2374,7 @@ function FlightSearchWidget({
2295
2374
  renderAirportDropdown(index, "destination", destinationDropdownRef || destinationRef, isDestOpen, () => toggleSegmentDropdown(index, "destination"))
2296
2375
  ] }, index),
2297
2376
  isMultiCityOrCustom && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sw-srh-wrap sw-pos-rel", children: [
2298
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sw-form-field sw-com-cal ", children: [
2377
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sw-srh-form-field sw-com-cal", children: [
2299
2378
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: `departure-${index}`, children: "Departure" }),
2300
2379
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2301
2380
  import_calendar.Calendar,
@@ -2415,10 +2494,7 @@ function FlightSearchWidget({
2415
2494
  renderAirportDropdown(0, "destination", destinationDropdownRef, isDestinationDropdownOpen, () => setIsDestinationDropdownOpen(true))
2416
2495
  ] }),
2417
2496
  (tripType === "round-trip" || tripType === "one-way") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "sw-srh-wrap sw-pos-rel mobiBox", children: tripType === "round-trip" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2418
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { cursor: "pointer" }, onClick: () => {
2419
- dateRangeCalendarRef.current?.show?.();
2420
- mobileDateRangeCalendarRef.current?.show?.();
2421
- }, className: "sw-form-field sw-com-cal", children: [
2497
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { cursor: "pointer" }, onClick: () => openRoundTripCalendar("departure"), className: "sw-srh-form-field sw-com-cal", children: [
2422
2498
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
2423
2499
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "Departure" }),
2424
2500
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: segments[0]?.dateRange?.[0]?.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" }) })
@@ -2432,23 +2508,15 @@ function FlightSearchWidget({
2432
2508
  appendTo: "self",
2433
2509
  selectionMode: "range",
2434
2510
  value: segments[0]?.dateRange ?? null,
2435
- minDate: /* @__PURE__ */ new Date(),
2511
+ minDate: roundTripMinDate,
2436
2512
  onChange: (e) => {
2437
2513
  const range = e.value;
2438
- setSegments((prev) => {
2439
- const u = [...prev];
2440
- if (!u[0]) return prev;
2441
- u[0] = {
2442
- ...u[0],
2443
- dateRange: range,
2444
- departureDate: range?.[0] ?? /* @__PURE__ */ new Date(),
2445
- returnDate: range?.[1] ?? /* @__PURE__ */ new Date()
2446
- };
2447
- return u;
2448
- });
2449
- if (range?.[0] && range?.[1]) setTimeout(() => {
2450
- dateRangeCalendarRef.current?.hide?.();
2451
- }, 150);
2514
+ applyRoundTripRangeChange(range);
2515
+ if (activeRoundTripField === "departure" && range?.[0] && range?.[1]) {
2516
+ setTimeout(() => {
2517
+ dateRangeCalendarRef.current?.hide?.();
2518
+ }, 150);
2519
+ }
2452
2520
  },
2453
2521
  dateFormat: "dd M yy",
2454
2522
  placeholder: "Select date range",
@@ -2464,20 +2532,10 @@ function FlightSearchWidget({
2464
2532
  appendTo: "self",
2465
2533
  selectionMode: "range",
2466
2534
  value: segments[0]?.dateRange ?? null,
2467
- minDate: /* @__PURE__ */ new Date(),
2535
+ minDate: roundTripMinDate,
2468
2536
  onChange: (e) => {
2469
2537
  const range = e.value;
2470
- setSegments((prev) => {
2471
- const u = [...prev];
2472
- if (!u[0]) return prev;
2473
- u[0] = {
2474
- ...u[0],
2475
- dateRange: range,
2476
- departureDate: range?.[0] ?? /* @__PURE__ */ new Date(),
2477
- returnDate: range?.[1] ?? /* @__PURE__ */ new Date()
2478
- };
2479
- return u;
2480
- });
2538
+ applyRoundTripRangeChange(range);
2481
2539
  },
2482
2540
  dateFormat: "dd M yy",
2483
2541
  placeholder: "Select date range",
@@ -2507,10 +2565,7 @@ function FlightSearchWidget({
2507
2565
  }
2508
2566
  )
2509
2567
  ] }),
2510
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { cursor: "pointer" }, className: "sw-form-field sw-com-cal", onClick: () => {
2511
- dateRangeCalendarRef.current?.show?.();
2512
- mobileDateRangeCalendarRef.current?.show?.();
2513
- }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
2568
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { cursor: "pointer" }, className: "sw-srh-form-field sw-com-cal", onClick: () => openRoundTripCalendar("return"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
2514
2569
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "Return" }),
2515
2570
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: (segments[0]?.dateRange?.[1] ?? segments[0]?.dateRange?.[0])?.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" }) })
2516
2571
  ] }) })
@@ -2518,7 +2573,7 @@ function FlightSearchWidget({
2518
2573
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2519
2574
  "div",
2520
2575
  {
2521
- className: "sw-form-field sw-pos-rel sw-com-cal",
2576
+ className: "sw-srh-form-field sw-pos-rel sw-com-cal",
2522
2577
  style: { cursor: "pointer" },
2523
2578
  onClick: () => {
2524
2579
  oneWayDesktopCalendarRef.current?.show?.();
@@ -2596,7 +2651,7 @@ function FlightSearchWidget({
2596
2651
  ]
2597
2652
  }
2598
2653
  ),
2599
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "sw-form-field sw-pos-rel sw-com-cal sw-add-return-label", style: { cursor: "pointer" }, onClick: () => {
2654
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "sw-srh-form-field sw-pos-rel sw-com-cal sw-add-return-label", style: { cursor: "pointer" }, onClick: () => {
2600
2655
  setTripType("round-trip");
2601
2656
  setTimeout(() => document.getElementById("dateRange")?.querySelector("input")?.focus?.(), 100);
2602
2657
  }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "font14", children: "Add return" }) })
@@ -2608,7 +2663,7 @@ function FlightSearchWidget({
2608
2663
  ] }),
2609
2664
  "Add Another Flight"
2610
2665
  ] }) }),
2611
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sw-form-field sw-traveller sw-pos-rel", children: [
2666
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sw-srh-form-field sw-traveller sw-pos-rel", children: [
2612
2667
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "rooms", children: "Travellers and Class" }),
2613
2668
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sw-custom-dropdown", ref: roomsGuestsMenuRef, children: [
2614
2669
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -2771,6 +2826,7 @@ function FlightSearchWidget({
2771
2826
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2772
2827
  import_autocomplete.AutoComplete,
2773
2828
  {
2829
+ className: "sw-airline-autocomplete",
2774
2830
  value: selectedAirlines,
2775
2831
  suggestions: airlineSuggestions,
2776
2832
  completeMethod: (e) => {