@qite/tide-booking-component 1.4.25 → 1.4.27

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.
@@ -22872,6 +22872,7 @@ var QSMConfigurationContext = React__default['default'].createContext({
22872
22872
  travelTypeIcon: '',
22873
22873
  travelClasses: [],
22874
22874
  travelClassIcon: '',
22875
+ nationalities: [],
22875
22876
  showReturnDate: false,
22876
22877
  datesIcon: '',
22877
22878
  onSubmit: function () {},
@@ -22929,6 +22930,7 @@ var initialState$1 = {
22929
22930
  selectedTravelType: undefined,
22930
22931
  travelClasses: [],
22931
22932
  selectedTravelClass: undefined,
22933
+ selectedNationality: undefined,
22932
22934
  defaultTravelers: 2,
22933
22935
  maxTravelers: 9,
22934
22936
  maxChildAge: 12,
@@ -23014,6 +23016,9 @@ var qsmSlice = toolkit.createSlice({
23014
23016
  setSelectedTravelClass: function (state, action) {
23015
23017
  state.selectedTravelClass = action.payload;
23016
23018
  },
23019
+ setSelectedNationality: function (state, action) {
23020
+ state.selectedNationality = action.payload;
23021
+ },
23017
23022
  setDefaultTravelers: function (state, action) {
23018
23023
  state.defaultTravelers = action.payload;
23019
23024
  },
@@ -23077,7 +23082,8 @@ var setMobileDatePickerMode = _a$1.setMobileDatePickerMode,
23077
23082
  _a$1.setTravelTypes;
23078
23083
  var setSelectedTravelType = _a$1.setSelectedTravelType;
23079
23084
  _a$1.setTravelClasses;
23080
- var setSelectedTravelClass = _a$1.setSelectedTravelClass;
23085
+ var setSelectedTravelClass = _a$1.setSelectedTravelClass,
23086
+ setSelectedNationality = _a$1.setSelectedNationality;
23081
23087
  _a$1.setDefaultTravelers;
23082
23088
  _a$1.setMaxTravelers;
23083
23089
  _a$1.setMaxChildAge;
@@ -23564,11 +23570,11 @@ var SearchInput = function (_a) {
23564
23570
  _a.label;
23565
23571
  var isSecondInput = _a.isSecondInput,
23566
23572
  isDoubleInput = _a.isDoubleInput;
23567
- var highlightMatch = function (text, highlight) {
23573
+ var highlightMatch = function (option, highlight) {
23568
23574
  if (!highlight) {
23569
- return text;
23575
+ return option.value;
23570
23576
  }
23571
- var parts = text.split(new RegExp('('.concat(highlight, ')'), 'gi'));
23577
+ var parts = option.value.split(new RegExp('('.concat(highlight, ')'), 'gi'));
23572
23578
  return React__default['default'].createElement(
23573
23579
  'span',
23574
23580
  null,
@@ -23589,14 +23595,18 @@ var SearchInput = function (_a) {
23589
23595
  .concat(isSecondInput ? ' qsm__double-input-options--second-input' : '')
23590
23596
  .concat(isDoubleInput ? ' qsm__double-input-options--splittable' : '')
23591
23597
  },
23592
- searchResults.map(function (result, index) {
23598
+ searchResults.map(function (option, index) {
23593
23599
  return React__default['default'].createElement(
23594
23600
  'div',
23595
23601
  {
23596
23602
  key: index,
23597
23603
  className: 'qsm__double-input-option',
23598
- onClick: function () {
23599
- return onOptionSelect(result);
23604
+ onMouseDown: function (e) {
23605
+ return e.preventDefault();
23606
+ },
23607
+ onClick: function (e) {
23608
+ e.stopPropagation();
23609
+ onOptionSelect(option);
23600
23610
  },
23601
23611
  role: 'option',
23602
23612
  'aria-selected': false
@@ -23604,15 +23614,16 @@ var SearchInput = function (_a) {
23604
23614
  React__default['default'].createElement(
23605
23615
  'div',
23606
23616
  { className: 'qsm__double-input-option-content' },
23607
- React__default['default'].createElement(Icon$1, { name: 'ui-location', height: 16 }),
23617
+ React__default['default'].createElement(Icon$1, { name: option.type == 'hotel' ? 'ui-hotel' : 'ui-location', height: 16 }),
23608
23618
  React__default['default'].createElement(
23609
23619
  'div',
23610
23620
  { className: 'qsm__double-input-option-content-text' },
23611
- highlightMatch(result, highlightTarget),
23612
- React__default['default'].createElement('span', { className: 'qsm__double-input-option-content-country' }, 'Belgi\u00EB')
23621
+ highlightMatch(option, highlightTarget),
23622
+ option.country && React__default['default'].createElement('span', { className: 'qsm__double-input-option-content-country' }, option.country)
23613
23623
  )
23614
23624
  ),
23615
- React__default['default'].createElement('span', { className: 'qsm__double-input-option-content-airport-label' }, '[BRU]')
23625
+ option.iataCode &&
23626
+ React__default['default'].createElement('span', { className: 'qsm__double-input-option-content-airport-label' }, '[', option.iataCode, ']')
23616
23627
  );
23617
23628
  })
23618
23629
  );
@@ -24609,26 +24620,41 @@ var MobileFilterModal = function () {
24609
24620
  hasTypedRef.current = false;
24610
24621
  dispatch(closeMobileFilter());
24611
24622
  };
24623
+ // const handleLocationChange = (val: string) => {
24624
+ // setInputValue(val);
24625
+ // hasTypedRef.current = true;
24626
+ // if (val.trim() !== '' && activeSearchFieldProps) {
24627
+ // const filtered = activeSearchFieldProps.options.filter((loc) => loc.value.toLowerCase().includes(val.toLowerCase())).map((loc) => loc.value);
24628
+ // setSearchResultsLocal(filtered);
24629
+ // } else {
24630
+ // setSearchResultsLocal([]);
24631
+ // }
24632
+ // };
24612
24633
  var handleLocationChange = function (val) {
24613
24634
  setInputValue(val);
24614
24635
  hasTypedRef.current = true;
24615
24636
  if (val.trim() !== '' && activeSearchFieldProps) {
24616
- var filtered = activeSearchFieldProps.options
24617
- .filter(function (loc) {
24618
- return loc.value.toLowerCase().includes(val.toLowerCase());
24619
- })
24620
- .map(function (loc) {
24621
- return loc.value;
24622
- });
24637
+ var filtered = activeSearchFieldProps.options.filter(function (option) {
24638
+ return option.value.toLowerCase().includes(val.toLowerCase());
24639
+ });
24623
24640
  setSearchResultsLocal(filtered);
24624
24641
  } else {
24625
24642
  setSearchResultsLocal([]);
24626
24643
  }
24627
24644
  };
24628
- var handleLocationSelect = function (val) {
24645
+ // const handleLocationSelect = (val: string) => {
24646
+ // if (activeSearchFieldProps) {
24647
+ // const { fieldKey } = activeSearchFieldProps;
24648
+ // dispatch(setFieldValue({ fieldKey, value: val }));
24649
+ // dispatch(setSearchResultsAction([]));
24650
+ // dispatch(setActiveSearchField(null));
24651
+ // }
24652
+ // dispatch(closeMobileFilter());
24653
+ // };
24654
+ var handleLocationSelect = function (option) {
24629
24655
  if (activeSearchFieldProps) {
24630
24656
  var fieldKey = activeSearchFieldProps.fieldKey;
24631
- dispatch(setFieldValue({ fieldKey: fieldKey, value: val }));
24657
+ dispatch(setFieldValue({ fieldKey: fieldKey, value: option.value }));
24632
24658
  dispatch(setSearchResults([]));
24633
24659
  dispatch(setActiveSearchField(null));
24634
24660
  }
@@ -24818,7 +24844,6 @@ var MobileFilterModal = function () {
24818
24844
  }
24819
24845
  };
24820
24846
 
24821
- // SearchInputGroup.tsx
24822
24847
  var findConfig = function (all, key) {
24823
24848
  for (var _i = 0, all_1 = all; _i < all_1.length; _i++) {
24824
24849
  var config = all_1[_i];
@@ -24859,17 +24884,6 @@ var SearchInputGroup = function (_a) {
24859
24884
  var label = config.label,
24860
24885
  placeholder = config.placeholder,
24861
24886
  options = config.options;
24862
- var lowerOptions = React.useMemo(
24863
- function () {
24864
- return options.map(function (x) {
24865
- return {
24866
- value: x.value,
24867
- lower: x.value.toLowerCase()
24868
- };
24869
- });
24870
- },
24871
- [options]
24872
- );
24873
24887
  var selector = React.useMemo(
24874
24888
  function () {
24875
24889
  return function (state) {
@@ -24886,30 +24900,44 @@ var SearchInputGroup = function (_a) {
24886
24900
  searchResults = _g.searchResults,
24887
24901
  activeSearchField = _g.activeSearchField;
24888
24902
  var match = React.useCallback(
24889
- function (option) {
24890
- if (!option) {
24903
+ function (input) {
24904
+ if (!input) {
24891
24905
  return [];
24892
24906
  }
24893
- var lowered = option.toLowerCase();
24894
- return lowerOptions
24895
- .filter(function (x) {
24896
- return x.lower.includes(lowered);
24897
- })
24898
- .map(function (x) {
24899
- return x.value;
24900
- });
24907
+ var lowered = input.toLowerCase();
24908
+ return options.filter(function (option) {
24909
+ return option.value.toLowerCase().includes(lowered);
24910
+ });
24901
24911
  },
24902
- [lowerOptions]
24912
+ [options]
24903
24913
  );
24904
- var change = React.useCallback(
24905
- function (val) {
24906
- dispatch(setFieldValue({ fieldKey: fieldKey, value: val }));
24914
+ var handleInputChange = React.useCallback(
24915
+ function (input) {
24916
+ dispatch(setFieldValue({ fieldKey: fieldKey, value: input }));
24907
24917
  dispatch(setSearchResults([]));
24908
- if (!small) {
24909
- dispatch(setSearchResults(match(val)));
24918
+ if (small) return;
24919
+ if (input.length === 3) {
24920
+ var exactIataMatch = findExactIataMatch(options, input);
24921
+ if (exactIataMatch) {
24922
+ dispatch(setFieldValue({ fieldKey: fieldKey, value: exactIataMatch.value }));
24923
+ dispatch(setSearchResults([]));
24924
+ dispatch(setActiveSearchField(null));
24925
+ return;
24926
+ }
24910
24927
  }
24928
+ // Normal typeahead behavior
24929
+ dispatch(setActiveSearchField(fieldKey));
24930
+ dispatch(setSearchResults(match(input)));
24931
+ },
24932
+ [dispatch, fieldKey, small, match, options]
24933
+ );
24934
+ var handleOptionSelect = React.useCallback(
24935
+ function (option) {
24936
+ dispatch(setFieldValue({ fieldKey: fieldKey, value: option.value }));
24937
+ dispatch(setSearchResults([]));
24938
+ dispatch(setActiveSearchField(null));
24911
24939
  },
24912
- [dispatch, fieldKey, small, match]
24940
+ [dispatch, fieldKey]
24913
24941
  );
24914
24942
  var click = function () {
24915
24943
  dispatch(setActiveSearchField(fieldKey));
@@ -24929,6 +24957,11 @@ var SearchInputGroup = function (_a) {
24929
24957
  dispatch(setSearchResults(match(value)));
24930
24958
  }
24931
24959
  };
24960
+ var findExactIataMatch = function (options, input) {
24961
+ return options.find(function (option) {
24962
+ return option.iataCode && option.iataCode.toLowerCase() === input.toLowerCase();
24963
+ });
24964
+ };
24932
24965
  React.useEffect(
24933
24966
  function () {
24934
24967
  var outside = function (e) {
@@ -24965,9 +24998,12 @@ var SearchInputGroup = function (_a) {
24965
24998
  name: fieldKey,
24966
24999
  value: value,
24967
25000
  readOnly: small,
24968
- onClick: click,
25001
+ onFocus: click,
25002
+ onClick: function (e) {
25003
+ return e.stopPropagation();
25004
+ },
24969
25005
  onChange: function (e) {
24970
- return !small && !readOnlyForced && change(e.target.value);
25006
+ return !small && !readOnlyForced && handleInputChange(e.target.value);
24971
25007
  },
24972
25008
  className: 'qsm__input'.concat(isSecondInput ? ' qsm__input--splittable' : ' u-ps-2'),
24973
25009
  placeholder: placeholder
@@ -24975,12 +25011,9 @@ var SearchInputGroup = function (_a) {
24975
25011
  !small &&
24976
25012
  activeSearchField === fieldKey &&
24977
25013
  React__default['default'].createElement(SearchInput, {
24978
- onChange: change,
25014
+ onChange: handleInputChange,
24979
25015
  searchResults: searchResults,
24980
- onOptionSelect: function (val) {
24981
- change(val);
24982
- dispatch(setActiveSearchField(null));
24983
- },
25016
+ onOptionSelect: handleOptionSelect,
24984
25017
  highlightTarget: highlightTarget,
24985
25018
  label: label,
24986
25019
  isSecondInput: isSecondInput,
@@ -25148,7 +25181,7 @@ var DoubleSearchInputGroup = function (_a) {
25148
25181
  { className: 'qsm__reverse-wrapper' },
25149
25182
  React__default['default'].createElement(
25150
25183
  'button',
25151
- { type: 'button', onClick: reverse, className: 'qsm__input-line--reverse-button' },
25184
+ { type: 'button', onClick: reverse, className: 'qsm__input-line--reverse-button', tabIndex: -1 },
25152
25185
  React__default['default'].createElement(
25153
25186
  'svg',
25154
25187
  { id: 'qsm-planes-icon', viewBox: '0 0 18 18', width: 18, height: 18 },
@@ -25394,17 +25427,22 @@ var TravelInputGroup = function () {
25394
25427
  });
25395
25428
  }
25396
25429
  };
25397
- React.useEffect(function () {
25398
- var handleClickOutside = function (event) {
25399
- if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
25400
- setIsOpen(false);
25430
+ React.useEffect(
25431
+ function () {
25432
+ var handleClickOutside = function (event) {
25433
+ if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
25434
+ setIsOpen(false);
25435
+ }
25436
+ };
25437
+ if (isOpen) {
25438
+ document.addEventListener('mousedown', handleClickOutside);
25401
25439
  }
25402
- };
25403
- document.addEventListener('mousedown', handleClickOutside);
25404
- return function () {
25405
- return document.removeEventListener('mousedown', handleClickOutside);
25406
- };
25407
- }, []);
25440
+ return function () {
25441
+ document.removeEventListener('mousedown', handleClickOutside);
25442
+ };
25443
+ },
25444
+ [isOpen]
25445
+ );
25408
25446
  React.useEffect(
25409
25447
  function () {
25410
25448
  if (initDone.current) {
@@ -25432,8 +25470,8 @@ var TravelInputGroup = function () {
25432
25470
  [defaultTravelers, maxTravelers]
25433
25471
  );
25434
25472
  return React__default['default'].createElement(
25435
- 'label',
25436
- { className: 'qsm__single-input-wrapper qsm__single-input-wrapper--travel' },
25473
+ 'div',
25474
+ { ref: wrapperRef, className: 'qsm__single-input-wrapper qsm__single-input-wrapper--travel' },
25437
25475
  React__default['default'].createElement(Icon$1, { name: 'ui-user', height: 16 }),
25438
25476
  React__default['default'].createElement('span', { className: 'qsm__label' }, 'Met wie ga je?'),
25439
25477
  React__default['default'].createElement('input', {
@@ -25467,8 +25505,8 @@ var ItemPicker$1 = function (_a) {
25467
25505
  var dropdownMenuRef = React.useRef(null);
25468
25506
  var toggleButtonRef = React.useRef(null);
25469
25507
  var handlePick = function (picked) {
25470
- dispatch(onPick(picked));
25471
25508
  setIsDropdownOpen(false);
25509
+ dispatch(onPick(picked));
25472
25510
  };
25473
25511
  React.useEffect(function () {
25474
25512
  var handleClickOutside = function (event) {
@@ -25495,7 +25533,7 @@ var ItemPicker$1 = function (_a) {
25495
25533
  [isDropdownOpen]
25496
25534
  );
25497
25535
  return React__default['default'].createElement(
25498
- 'label',
25536
+ 'div',
25499
25537
  { className: 'dropdown__input ' + classModifier },
25500
25538
  React__default['default'].createElement('span', { className: 'dropdown__label' }, label),
25501
25539
  React__default['default'].createElement(
@@ -25526,8 +25564,9 @@ var ItemPicker$1 = function (_a) {
25526
25564
  'li',
25527
25565
  {
25528
25566
  key: label,
25529
- onClick: function () {
25530
- return handlePick(label);
25567
+ onClick: function (e) {
25568
+ handlePick(label);
25569
+ e.stopPropagation();
25531
25570
  },
25532
25571
  className: 'dropdown-menu__item'.concat(selection === label ? ' dropdown-menu__item--selected' : '')
25533
25572
  },
@@ -25570,6 +25609,21 @@ var TravelTypePicker = function () {
25570
25609
  });
25571
25610
  };
25572
25611
 
25612
+ var TravelNationalityPicker = function () {
25613
+ var nationalities = React.useContext(QSMConfigurationContext).nationalities;
25614
+ var selectedNationality = reactRedux.useSelector(function (state) {
25615
+ return state.qsm;
25616
+ }).selectedNationality;
25617
+ return React__default['default'].createElement(ItemPicker$1, {
25618
+ items: nationalities,
25619
+ selection: selectedNationality,
25620
+ label: 'Nationaliteit',
25621
+ placeholder: 'Selecteer nationaliteit',
25622
+ classModifier: 'travel-class-picker__items',
25623
+ onPick: setSelectedNationality
25624
+ });
25625
+ };
25626
+
25573
25627
  var QSMContainer = function () {
25574
25628
  var isMobile = useMediaQuery('(max-width: 768px)');
25575
25629
  var mobileFilterType = reactRedux.useSelector(function (state) {
@@ -25731,7 +25785,8 @@ var QSMContainer = function () {
25731
25785
  'div',
25732
25786
  { className: 'qsm__filter__classgroup' },
25733
25787
  React__default['default'].createElement(TravelClassPicker, null),
25734
- React__default['default'].createElement(TravelTypePicker, null)
25788
+ React__default['default'].createElement(TravelTypePicker, null),
25789
+ React__default['default'].createElement(TravelNationalityPicker, null)
25735
25790
  )
25736
25791
  ),
25737
25792
  React__default['default'].createElement(
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
- import { TravelClass, TravelType } from '../../types';
2
+ import { Nationality, TravelClass, TravelType } from '../../types';
3
3
  interface ItemPickerProps {
4
- items: TravelType[] | TravelClass[];
4
+ items: TravelType[] | TravelClass[] | Nationality[];
5
5
  selection: string | undefined;
6
6
  label: string;
7
7
  placeholder: string;
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
+ import { TypeaheadOption } from '../../types';
2
3
  interface SearchInputProps {
3
4
  onChange: (input: string) => void;
4
- searchResults: string[];
5
- onOptionSelect: (val: string) => void;
5
+ searchResults: TypeaheadOption[];
6
+ onOptionSelect: (val: TypeaheadOption) => void;
6
7
  highlightTarget: string;
7
8
  label: string;
8
9
  isSecondInput?: boolean;
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const TravelNationalityPicker: React.FC;
3
+ export default TravelNationalityPicker;
@@ -1,4 +1,4 @@
1
- import { MobileFilterType, Room, TravelerType, TravelType, TravelClass } from '../types';
1
+ import { MobileFilterType, Room, TravelerType, TravelType, TravelClass, TypeaheadOption } from '../types';
2
2
  import { ReactNode } from 'react';
3
3
  export interface QSMState {
4
4
  selectedOrigin?: string;
@@ -9,16 +9,14 @@ export interface QSMState {
9
9
  travelers: any[];
10
10
  travelClass?: string;
11
11
  mobileFilterType: MobileFilterType;
12
- searchResults: string[];
12
+ searchResults: TypeaheadOption[];
13
13
  activeSearchField: string | null;
14
14
  activeSearchFieldProps: {
15
15
  fieldKey: string;
16
16
  label: string;
17
17
  placeholder: string;
18
18
  value: string;
19
- options: {
20
- value: string;
21
- }[];
19
+ options: TypeaheadOption[];
22
20
  } | null;
23
21
  language: 'nl' | 'fr' | 'en';
24
22
  mobileDatePickerMode: 'range' | 'single';
@@ -38,6 +36,7 @@ export interface QSMState {
38
36
  selectedTravelType?: string;
39
37
  travelClasses: TravelClass[];
40
38
  selectedTravelClass?: string;
39
+ selectedNationality?: string;
41
40
  defaultTravelers: number;
42
41
  maxTravelers: number;
43
42
  maxChildAge: number;
@@ -63,13 +62,11 @@ export declare const setOrigin: import('@reduxjs/toolkit').ActionCreatorWithPayl
63
62
  label: string;
64
63
  placeholder: string;
65
64
  value: string;
66
- options: {
67
- value: string;
68
- }[];
65
+ options: TypeaheadOption[];
69
66
  },
70
67
  'qsm/setActiveSearchFieldProps'
71
68
  >,
72
- setSearchResults: import('@reduxjs/toolkit').ActionCreatorWithPayload<string[], 'qsm/setSearchResults'>,
69
+ setSearchResults: import('@reduxjs/toolkit').ActionCreatorWithPayload<TypeaheadOption[], 'qsm/setSearchResults'>,
73
70
  setLanguage: import('@reduxjs/toolkit').ActionCreatorWithPayload<'nl' | 'fr' | 'en', 'qsm/setLanguage'>,
74
71
  setMobileDatePickerMode: import('@reduxjs/toolkit').ActionCreatorWithPayload<'range' | 'single', 'qsm/setMobileDatePickerMode'>,
75
72
  setMinDate: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<string | undefined, 'qsm/setMinDate'>,
@@ -95,6 +92,7 @@ export declare const setOrigin: import('@reduxjs/toolkit').ActionCreatorWithPayl
95
92
  setSelectedTravelType: import('@reduxjs/toolkit').ActionCreatorWithPayload<any, 'qsm/setSelectedTravelType'>,
96
93
  setTravelClasses: import('@reduxjs/toolkit').ActionCreatorWithPayload<TravelType[], 'qsm/setTravelClasses'>,
97
94
  setSelectedTravelClass: import('@reduxjs/toolkit').ActionCreatorWithPayload<any, 'qsm/setSelectedTravelClass'>,
95
+ setSelectedNationality: import('@reduxjs/toolkit').ActionCreatorWithPayload<any, 'qsm/setSelectedNationality'>,
98
96
  setDefaultTravelers: import('@reduxjs/toolkit').ActionCreatorWithPayload<number, 'qsm/setDefaultTravelers'>,
99
97
  setMaxTravelers: import('@reduxjs/toolkit').ActionCreatorWithPayload<number, 'qsm/setMaxTravelers'>,
100
98
  setMaxChildAge: import('@reduxjs/toolkit').ActionCreatorWithPayload<number, 'qsm/setMaxChildAge'>,
@@ -37,6 +37,7 @@ export interface QSMConfiguration {
37
37
  onSubmit: (data: any) => void;
38
38
  submitLabel: string;
39
39
  submitIcon: ReactNode;
40
+ nationalities: Nationality[];
40
41
  }
41
42
  export interface BaseFieldConfig {
42
43
  fieldKey: string;
@@ -47,6 +48,8 @@ export interface BaseFieldConfig {
47
48
  export interface TypeaheadOption {
48
49
  key: string;
49
50
  value: string;
51
+ iataCode?: string;
52
+ country?: string;
50
53
  type: OptionType;
51
54
  }
52
55
  export type OptionType = 'country' | 'region' | 'oord' | 'location' | 'airport' | 'hotel' | 'other';
@@ -89,6 +92,7 @@ export interface Room {
89
92
  babies: number;
90
93
  }
91
94
  export interface TravelType {
95
+ id: number;
92
96
  label: string;
93
97
  icon?: ReactNode;
94
98
  }
@@ -96,3 +100,8 @@ export interface TravelClass {
96
100
  label: string;
97
101
  icon?: ReactNode;
98
102
  }
103
+ export interface Nationality {
104
+ id: number;
105
+ label: string;
106
+ icon?: ReactNode;
107
+ }
@@ -22732,6 +22732,7 @@ var QSMConfigurationContext = React.createContext({
22732
22732
  travelTypeIcon: '',
22733
22733
  travelClasses: [],
22734
22734
  travelClassIcon: '',
22735
+ nationalities: [],
22735
22736
  showReturnDate: false,
22736
22737
  datesIcon: '',
22737
22738
  onSubmit: function () {},
@@ -22789,6 +22790,7 @@ var initialState$1 = {
22789
22790
  selectedTravelType: undefined,
22790
22791
  travelClasses: [],
22791
22792
  selectedTravelClass: undefined,
22793
+ selectedNationality: undefined,
22792
22794
  defaultTravelers: 2,
22793
22795
  maxTravelers: 9,
22794
22796
  maxChildAge: 12,
@@ -22874,6 +22876,9 @@ var qsmSlice = createSlice({
22874
22876
  setSelectedTravelClass: function (state, action) {
22875
22877
  state.selectedTravelClass = action.payload;
22876
22878
  },
22879
+ setSelectedNationality: function (state, action) {
22880
+ state.selectedNationality = action.payload;
22881
+ },
22877
22882
  setDefaultTravelers: function (state, action) {
22878
22883
  state.defaultTravelers = action.payload;
22879
22884
  },
@@ -22937,7 +22942,8 @@ var setMobileDatePickerMode = _a$1.setMobileDatePickerMode,
22937
22942
  _a$1.setTravelTypes;
22938
22943
  var setSelectedTravelType = _a$1.setSelectedTravelType;
22939
22944
  _a$1.setTravelClasses;
22940
- var setSelectedTravelClass = _a$1.setSelectedTravelClass;
22945
+ var setSelectedTravelClass = _a$1.setSelectedTravelClass,
22946
+ setSelectedNationality = _a$1.setSelectedNationality;
22941
22947
  _a$1.setDefaultTravelers;
22942
22948
  _a$1.setMaxTravelers;
22943
22949
  _a$1.setMaxChildAge;
@@ -23422,11 +23428,11 @@ var SearchInput = function (_a) {
23422
23428
  _a.label;
23423
23429
  var isSecondInput = _a.isSecondInput,
23424
23430
  isDoubleInput = _a.isDoubleInput;
23425
- var highlightMatch = function (text, highlight) {
23431
+ var highlightMatch = function (option, highlight) {
23426
23432
  if (!highlight) {
23427
- return text;
23433
+ return option.value;
23428
23434
  }
23429
- var parts = text.split(new RegExp('('.concat(highlight, ')'), 'gi'));
23435
+ var parts = option.value.split(new RegExp('('.concat(highlight, ')'), 'gi'));
23430
23436
  return React.createElement(
23431
23437
  'span',
23432
23438
  null,
@@ -23447,14 +23453,18 @@ var SearchInput = function (_a) {
23447
23453
  .concat(isSecondInput ? ' qsm__double-input-options--second-input' : '')
23448
23454
  .concat(isDoubleInput ? ' qsm__double-input-options--splittable' : '')
23449
23455
  },
23450
- searchResults.map(function (result, index) {
23456
+ searchResults.map(function (option, index) {
23451
23457
  return React.createElement(
23452
23458
  'div',
23453
23459
  {
23454
23460
  key: index,
23455
23461
  className: 'qsm__double-input-option',
23456
- onClick: function () {
23457
- return onOptionSelect(result);
23462
+ onMouseDown: function (e) {
23463
+ return e.preventDefault();
23464
+ },
23465
+ onClick: function (e) {
23466
+ e.stopPropagation();
23467
+ onOptionSelect(option);
23458
23468
  },
23459
23469
  role: 'option',
23460
23470
  'aria-selected': false
@@ -23462,15 +23472,15 @@ var SearchInput = function (_a) {
23462
23472
  React.createElement(
23463
23473
  'div',
23464
23474
  { className: 'qsm__double-input-option-content' },
23465
- React.createElement(Icon$1, { name: 'ui-location', height: 16 }),
23475
+ React.createElement(Icon$1, { name: option.type == 'hotel' ? 'ui-hotel' : 'ui-location', height: 16 }),
23466
23476
  React.createElement(
23467
23477
  'div',
23468
23478
  { className: 'qsm__double-input-option-content-text' },
23469
- highlightMatch(result, highlightTarget),
23470
- React.createElement('span', { className: 'qsm__double-input-option-content-country' }, 'Belgi\u00EB')
23479
+ highlightMatch(option, highlightTarget),
23480
+ option.country && React.createElement('span', { className: 'qsm__double-input-option-content-country' }, option.country)
23471
23481
  )
23472
23482
  ),
23473
- React.createElement('span', { className: 'qsm__double-input-option-content-airport-label' }, '[BRU]')
23483
+ option.iataCode && React.createElement('span', { className: 'qsm__double-input-option-content-airport-label' }, '[', option.iataCode, ']')
23474
23484
  );
23475
23485
  })
23476
23486
  );
@@ -24452,26 +24462,41 @@ var MobileFilterModal = function () {
24452
24462
  hasTypedRef.current = false;
24453
24463
  dispatch(closeMobileFilter());
24454
24464
  };
24465
+ // const handleLocationChange = (val: string) => {
24466
+ // setInputValue(val);
24467
+ // hasTypedRef.current = true;
24468
+ // if (val.trim() !== '' && activeSearchFieldProps) {
24469
+ // const filtered = activeSearchFieldProps.options.filter((loc) => loc.value.toLowerCase().includes(val.toLowerCase())).map((loc) => loc.value);
24470
+ // setSearchResultsLocal(filtered);
24471
+ // } else {
24472
+ // setSearchResultsLocal([]);
24473
+ // }
24474
+ // };
24455
24475
  var handleLocationChange = function (val) {
24456
24476
  setInputValue(val);
24457
24477
  hasTypedRef.current = true;
24458
24478
  if (val.trim() !== '' && activeSearchFieldProps) {
24459
- var filtered = activeSearchFieldProps.options
24460
- .filter(function (loc) {
24461
- return loc.value.toLowerCase().includes(val.toLowerCase());
24462
- })
24463
- .map(function (loc) {
24464
- return loc.value;
24465
- });
24479
+ var filtered = activeSearchFieldProps.options.filter(function (option) {
24480
+ return option.value.toLowerCase().includes(val.toLowerCase());
24481
+ });
24466
24482
  setSearchResultsLocal(filtered);
24467
24483
  } else {
24468
24484
  setSearchResultsLocal([]);
24469
24485
  }
24470
24486
  };
24471
- var handleLocationSelect = function (val) {
24487
+ // const handleLocationSelect = (val: string) => {
24488
+ // if (activeSearchFieldProps) {
24489
+ // const { fieldKey } = activeSearchFieldProps;
24490
+ // dispatch(setFieldValue({ fieldKey, value: val }));
24491
+ // dispatch(setSearchResultsAction([]));
24492
+ // dispatch(setActiveSearchField(null));
24493
+ // }
24494
+ // dispatch(closeMobileFilter());
24495
+ // };
24496
+ var handleLocationSelect = function (option) {
24472
24497
  if (activeSearchFieldProps) {
24473
24498
  var fieldKey = activeSearchFieldProps.fieldKey;
24474
- dispatch(setFieldValue({ fieldKey: fieldKey, value: val }));
24499
+ dispatch(setFieldValue({ fieldKey: fieldKey, value: option.value }));
24475
24500
  dispatch(setSearchResults([]));
24476
24501
  dispatch(setActiveSearchField(null));
24477
24502
  }
@@ -24657,7 +24682,6 @@ var MobileFilterModal = function () {
24657
24682
  }
24658
24683
  };
24659
24684
 
24660
- // SearchInputGroup.tsx
24661
24685
  var findConfig = function (all, key) {
24662
24686
  for (var _i = 0, all_1 = all; _i < all_1.length; _i++) {
24663
24687
  var config = all_1[_i];
@@ -24698,17 +24722,6 @@ var SearchInputGroup = function (_a) {
24698
24722
  var label = config.label,
24699
24723
  placeholder = config.placeholder,
24700
24724
  options = config.options;
24701
- var lowerOptions = useMemo(
24702
- function () {
24703
- return options.map(function (x) {
24704
- return {
24705
- value: x.value,
24706
- lower: x.value.toLowerCase()
24707
- };
24708
- });
24709
- },
24710
- [options]
24711
- );
24712
24725
  var selector = useMemo(
24713
24726
  function () {
24714
24727
  return function (state) {
@@ -24725,30 +24738,44 @@ var SearchInputGroup = function (_a) {
24725
24738
  searchResults = _g.searchResults,
24726
24739
  activeSearchField = _g.activeSearchField;
24727
24740
  var match = useCallback(
24728
- function (option) {
24729
- if (!option) {
24741
+ function (input) {
24742
+ if (!input) {
24730
24743
  return [];
24731
24744
  }
24732
- var lowered = option.toLowerCase();
24733
- return lowerOptions
24734
- .filter(function (x) {
24735
- return x.lower.includes(lowered);
24736
- })
24737
- .map(function (x) {
24738
- return x.value;
24739
- });
24745
+ var lowered = input.toLowerCase();
24746
+ return options.filter(function (option) {
24747
+ return option.value.toLowerCase().includes(lowered);
24748
+ });
24740
24749
  },
24741
- [lowerOptions]
24750
+ [options]
24742
24751
  );
24743
- var change = useCallback(
24744
- function (val) {
24745
- dispatch(setFieldValue({ fieldKey: fieldKey, value: val }));
24752
+ var handleInputChange = useCallback(
24753
+ function (input) {
24754
+ dispatch(setFieldValue({ fieldKey: fieldKey, value: input }));
24746
24755
  dispatch(setSearchResults([]));
24747
- if (!small) {
24748
- dispatch(setSearchResults(match(val)));
24756
+ if (small) return;
24757
+ if (input.length === 3) {
24758
+ var exactIataMatch = findExactIataMatch(options, input);
24759
+ if (exactIataMatch) {
24760
+ dispatch(setFieldValue({ fieldKey: fieldKey, value: exactIataMatch.value }));
24761
+ dispatch(setSearchResults([]));
24762
+ dispatch(setActiveSearchField(null));
24763
+ return;
24764
+ }
24749
24765
  }
24766
+ // Normal typeahead behavior
24767
+ dispatch(setActiveSearchField(fieldKey));
24768
+ dispatch(setSearchResults(match(input)));
24769
+ },
24770
+ [dispatch, fieldKey, small, match, options]
24771
+ );
24772
+ var handleOptionSelect = useCallback(
24773
+ function (option) {
24774
+ dispatch(setFieldValue({ fieldKey: fieldKey, value: option.value }));
24775
+ dispatch(setSearchResults([]));
24776
+ dispatch(setActiveSearchField(null));
24750
24777
  },
24751
- [dispatch, fieldKey, small, match]
24778
+ [dispatch, fieldKey]
24752
24779
  );
24753
24780
  var click = function () {
24754
24781
  dispatch(setActiveSearchField(fieldKey));
@@ -24768,6 +24795,11 @@ var SearchInputGroup = function (_a) {
24768
24795
  dispatch(setSearchResults(match(value)));
24769
24796
  }
24770
24797
  };
24798
+ var findExactIataMatch = function (options, input) {
24799
+ return options.find(function (option) {
24800
+ return option.iataCode && option.iataCode.toLowerCase() === input.toLowerCase();
24801
+ });
24802
+ };
24771
24803
  useEffect(
24772
24804
  function () {
24773
24805
  var outside = function (e) {
@@ -24800,9 +24832,12 @@ var SearchInputGroup = function (_a) {
24800
24832
  name: fieldKey,
24801
24833
  value: value,
24802
24834
  readOnly: small,
24803
- onClick: click,
24835
+ onFocus: click,
24836
+ onClick: function (e) {
24837
+ return e.stopPropagation();
24838
+ },
24804
24839
  onChange: function (e) {
24805
- return !small && !readOnlyForced && change(e.target.value);
24840
+ return !small && !readOnlyForced && handleInputChange(e.target.value);
24806
24841
  },
24807
24842
  className: 'qsm__input'.concat(isSecondInput ? ' qsm__input--splittable' : ' u-ps-2'),
24808
24843
  placeholder: placeholder
@@ -24810,12 +24845,9 @@ var SearchInputGroup = function (_a) {
24810
24845
  !small &&
24811
24846
  activeSearchField === fieldKey &&
24812
24847
  React.createElement(SearchInput, {
24813
- onChange: change,
24848
+ onChange: handleInputChange,
24814
24849
  searchResults: searchResults,
24815
- onOptionSelect: function (val) {
24816
- change(val);
24817
- dispatch(setActiveSearchField(null));
24818
- },
24850
+ onOptionSelect: handleOptionSelect,
24819
24851
  highlightTarget: highlightTarget,
24820
24852
  label: label,
24821
24853
  isSecondInput: isSecondInput,
@@ -24978,7 +25010,7 @@ var DoubleSearchInputGroup = function (_a) {
24978
25010
  { className: 'qsm__reverse-wrapper' },
24979
25011
  React.createElement(
24980
25012
  'button',
24981
- { type: 'button', onClick: reverse, className: 'qsm__input-line--reverse-button' },
25013
+ { type: 'button', onClick: reverse, className: 'qsm__input-line--reverse-button', tabIndex: -1 },
24982
25014
  React.createElement(
24983
25015
  'svg',
24984
25016
  { id: 'qsm-planes-icon', viewBox: '0 0 18 18', width: 18, height: 18 },
@@ -25224,17 +25256,22 @@ var TravelInputGroup = function () {
25224
25256
  });
25225
25257
  }
25226
25258
  };
25227
- useEffect(function () {
25228
- var handleClickOutside = function (event) {
25229
- if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
25230
- setIsOpen(false);
25259
+ useEffect(
25260
+ function () {
25261
+ var handleClickOutside = function (event) {
25262
+ if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
25263
+ setIsOpen(false);
25264
+ }
25265
+ };
25266
+ if (isOpen) {
25267
+ document.addEventListener('mousedown', handleClickOutside);
25231
25268
  }
25232
- };
25233
- document.addEventListener('mousedown', handleClickOutside);
25234
- return function () {
25235
- return document.removeEventListener('mousedown', handleClickOutside);
25236
- };
25237
- }, []);
25269
+ return function () {
25270
+ document.removeEventListener('mousedown', handleClickOutside);
25271
+ };
25272
+ },
25273
+ [isOpen]
25274
+ );
25238
25275
  useEffect(
25239
25276
  function () {
25240
25277
  if (initDone.current) {
@@ -25262,8 +25299,8 @@ var TravelInputGroup = function () {
25262
25299
  [defaultTravelers, maxTravelers]
25263
25300
  );
25264
25301
  return React.createElement(
25265
- 'label',
25266
- { className: 'qsm__single-input-wrapper qsm__single-input-wrapper--travel' },
25302
+ 'div',
25303
+ { ref: wrapperRef, className: 'qsm__single-input-wrapper qsm__single-input-wrapper--travel' },
25267
25304
  React.createElement(Icon$1, { name: 'ui-user', height: 16 }),
25268
25305
  React.createElement('span', { className: 'qsm__label' }, 'Met wie ga je?'),
25269
25306
  React.createElement('input', {
@@ -25297,8 +25334,8 @@ var ItemPicker$1 = function (_a) {
25297
25334
  var dropdownMenuRef = useRef(null);
25298
25335
  var toggleButtonRef = useRef(null);
25299
25336
  var handlePick = function (picked) {
25300
- dispatch(onPick(picked));
25301
25337
  setIsDropdownOpen(false);
25338
+ dispatch(onPick(picked));
25302
25339
  };
25303
25340
  useEffect(function () {
25304
25341
  var handleClickOutside = function (event) {
@@ -25325,7 +25362,7 @@ var ItemPicker$1 = function (_a) {
25325
25362
  [isDropdownOpen]
25326
25363
  );
25327
25364
  return React.createElement(
25328
- 'label',
25365
+ 'div',
25329
25366
  { className: 'dropdown__input ' + classModifier },
25330
25367
  React.createElement('span', { className: 'dropdown__label' }, label),
25331
25368
  React.createElement(
@@ -25356,8 +25393,9 @@ var ItemPicker$1 = function (_a) {
25356
25393
  'li',
25357
25394
  {
25358
25395
  key: label,
25359
- onClick: function () {
25360
- return handlePick(label);
25396
+ onClick: function (e) {
25397
+ handlePick(label);
25398
+ e.stopPropagation();
25361
25399
  },
25362
25400
  className: 'dropdown-menu__item'.concat(selection === label ? ' dropdown-menu__item--selected' : '')
25363
25401
  },
@@ -25400,6 +25438,21 @@ var TravelTypePicker = function () {
25400
25438
  });
25401
25439
  };
25402
25440
 
25441
+ var TravelNationalityPicker = function () {
25442
+ var nationalities = useContext(QSMConfigurationContext).nationalities;
25443
+ var selectedNationality = useSelector(function (state) {
25444
+ return state.qsm;
25445
+ }).selectedNationality;
25446
+ return React.createElement(ItemPicker$1, {
25447
+ items: nationalities,
25448
+ selection: selectedNationality,
25449
+ label: 'Nationaliteit',
25450
+ placeholder: 'Selecteer nationaliteit',
25451
+ classModifier: 'travel-class-picker__items',
25452
+ onPick: setSelectedNationality
25453
+ });
25454
+ };
25455
+
25403
25456
  var QSMContainer = function () {
25404
25457
  var isMobile = useMediaQuery('(max-width: 768px)');
25405
25458
  var mobileFilterType = useSelector(function (state) {
@@ -25529,7 +25582,8 @@ var QSMContainer = function () {
25529
25582
  'div',
25530
25583
  { className: 'qsm__filter__classgroup' },
25531
25584
  React.createElement(TravelClassPicker, null),
25532
- React.createElement(TravelTypePicker, null)
25585
+ React.createElement(TravelTypePicker, null),
25586
+ React.createElement(TravelNationalityPicker, null)
25533
25587
  )
25534
25588
  ),
25535
25589
  React.createElement(
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
- import { TravelClass, TravelType } from '../../types';
2
+ import { Nationality, TravelClass, TravelType } from '../../types';
3
3
  interface ItemPickerProps {
4
- items: TravelType[] | TravelClass[];
4
+ items: TravelType[] | TravelClass[] | Nationality[];
5
5
  selection: string | undefined;
6
6
  label: string;
7
7
  placeholder: string;
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
+ import { TypeaheadOption } from '../../types';
2
3
  interface SearchInputProps {
3
4
  onChange: (input: string) => void;
4
- searchResults: string[];
5
- onOptionSelect: (val: string) => void;
5
+ searchResults: TypeaheadOption[];
6
+ onOptionSelect: (val: TypeaheadOption) => void;
6
7
  highlightTarget: string;
7
8
  label: string;
8
9
  isSecondInput?: boolean;
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const TravelNationalityPicker: React.FC;
3
+ export default TravelNationalityPicker;
@@ -1,4 +1,4 @@
1
- import { MobileFilterType, Room, TravelerType, TravelType, TravelClass } from '../types';
1
+ import { MobileFilterType, Room, TravelerType, TravelType, TravelClass, TypeaheadOption } from '../types';
2
2
  import { ReactNode } from 'react';
3
3
  export interface QSMState {
4
4
  selectedOrigin?: string;
@@ -9,16 +9,14 @@ export interface QSMState {
9
9
  travelers: any[];
10
10
  travelClass?: string;
11
11
  mobileFilterType: MobileFilterType;
12
- searchResults: string[];
12
+ searchResults: TypeaheadOption[];
13
13
  activeSearchField: string | null;
14
14
  activeSearchFieldProps: {
15
15
  fieldKey: string;
16
16
  label: string;
17
17
  placeholder: string;
18
18
  value: string;
19
- options: {
20
- value: string;
21
- }[];
19
+ options: TypeaheadOption[];
22
20
  } | null;
23
21
  language: 'nl' | 'fr' | 'en';
24
22
  mobileDatePickerMode: 'range' | 'single';
@@ -38,6 +36,7 @@ export interface QSMState {
38
36
  selectedTravelType?: string;
39
37
  travelClasses: TravelClass[];
40
38
  selectedTravelClass?: string;
39
+ selectedNationality?: string;
41
40
  defaultTravelers: number;
42
41
  maxTravelers: number;
43
42
  maxChildAge: number;
@@ -63,13 +62,11 @@ export declare const setOrigin: import('@reduxjs/toolkit').ActionCreatorWithPayl
63
62
  label: string;
64
63
  placeholder: string;
65
64
  value: string;
66
- options: {
67
- value: string;
68
- }[];
65
+ options: TypeaheadOption[];
69
66
  },
70
67
  'qsm/setActiveSearchFieldProps'
71
68
  >,
72
- setSearchResults: import('@reduxjs/toolkit').ActionCreatorWithPayload<string[], 'qsm/setSearchResults'>,
69
+ setSearchResults: import('@reduxjs/toolkit').ActionCreatorWithPayload<TypeaheadOption[], 'qsm/setSearchResults'>,
73
70
  setLanguage: import('@reduxjs/toolkit').ActionCreatorWithPayload<'nl' | 'fr' | 'en', 'qsm/setLanguage'>,
74
71
  setMobileDatePickerMode: import('@reduxjs/toolkit').ActionCreatorWithPayload<'range' | 'single', 'qsm/setMobileDatePickerMode'>,
75
72
  setMinDate: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<string | undefined, 'qsm/setMinDate'>,
@@ -95,6 +92,7 @@ export declare const setOrigin: import('@reduxjs/toolkit').ActionCreatorWithPayl
95
92
  setSelectedTravelType: import('@reduxjs/toolkit').ActionCreatorWithPayload<any, 'qsm/setSelectedTravelType'>,
96
93
  setTravelClasses: import('@reduxjs/toolkit').ActionCreatorWithPayload<TravelType[], 'qsm/setTravelClasses'>,
97
94
  setSelectedTravelClass: import('@reduxjs/toolkit').ActionCreatorWithPayload<any, 'qsm/setSelectedTravelClass'>,
95
+ setSelectedNationality: import('@reduxjs/toolkit').ActionCreatorWithPayload<any, 'qsm/setSelectedNationality'>,
98
96
  setDefaultTravelers: import('@reduxjs/toolkit').ActionCreatorWithPayload<number, 'qsm/setDefaultTravelers'>,
99
97
  setMaxTravelers: import('@reduxjs/toolkit').ActionCreatorWithPayload<number, 'qsm/setMaxTravelers'>,
100
98
  setMaxChildAge: import('@reduxjs/toolkit').ActionCreatorWithPayload<number, 'qsm/setMaxChildAge'>,
@@ -37,6 +37,7 @@ export interface QSMConfiguration {
37
37
  onSubmit: (data: any) => void;
38
38
  submitLabel: string;
39
39
  submitIcon: ReactNode;
40
+ nationalities: Nationality[];
40
41
  }
41
42
  export interface BaseFieldConfig {
42
43
  fieldKey: string;
@@ -47,6 +48,8 @@ export interface BaseFieldConfig {
47
48
  export interface TypeaheadOption {
48
49
  key: string;
49
50
  value: string;
51
+ iataCode?: string;
52
+ country?: string;
50
53
  type: OptionType;
51
54
  }
52
55
  export type OptionType = 'country' | 'region' | 'oord' | 'location' | 'airport' | 'hotel' | 'other';
@@ -89,6 +92,7 @@ export interface Room {
89
92
  babies: number;
90
93
  }
91
94
  export interface TravelType {
95
+ id: number;
92
96
  label: string;
93
97
  icon?: ReactNode;
94
98
  }
@@ -96,3 +100,8 @@ export interface TravelClass {
96
100
  label: string;
97
101
  icon?: ReactNode;
98
102
  }
103
+ export interface Nationality {
104
+ id: number;
105
+ label: string;
106
+ icon?: ReactNode;
107
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qite/tide-booking-component",
3
- "version": "1.4.25",
3
+ "version": "1.4.27",
4
4
  "description": "React Booking wizard & Booking product component for Tide",
5
5
  "main": "build/build-cjs/index.js",
6
6
  "module": "build/build-esm/index.js",
@@ -4,7 +4,7 @@ import { TideLogo, language, languages, topLinks, navItems } from '../../../../s
4
4
  import Icon from '../../components/icon';
5
5
  import ContactForm from '../../components/contact';
6
6
  import Slider from '../../components/slider';
7
- import { QSMConfiguration, TypeaheadOption } from '../../../qsm/types';
7
+ import { Nationality, QSMConfiguration, TypeaheadOption } from '../../../qsm/types';
8
8
  import QSM from '../../../qsm';
9
9
 
10
10
  interface ContentPageSelfContainedProps {
@@ -38,6 +38,15 @@ const destinations: TypeaheadOption[] = [
38
38
  { key: 'SIN', value: 'Singapore Changi', type: 'location' }
39
39
  ];
40
40
 
41
+ const nationalities: Nationality[] = [
42
+ { id: 1, label: 'Nederland' },
43
+ { id: 2, label: 'België' },
44
+ { id: 3, label: 'Duitsland' },
45
+ { id: 4, label: 'Frankrijk' },
46
+ { id: 5, label: 'Verenigd Koninkrijk' },
47
+ { id: 6, label: 'Spanje' }
48
+ ];
49
+
41
50
  const configuration: QSMConfiguration = {
42
51
  type: 'hotel',
43
52
  askTravelers: true,
@@ -135,6 +144,8 @@ const configuration: QSMConfiguration = {
135
144
  travelClasses: [],
136
145
  travelClassIcon: '',
137
146
 
147
+ nationalities: nationalities,
148
+
138
149
  dateFlexibility: [
139
150
  {
140
151
  name: '1 dag voor en na',
@@ -11,6 +11,7 @@ import TravelInputGroup from '../travel-input-group';
11
11
  import TravelClassPicker from '../travel-class-picker';
12
12
  import TravelTypePicker from '../travel-type-picker';
13
13
  import Icon from '../icon';
14
+ import TravelNationalityPicker from '../travel-nationality-picker';
14
15
 
15
16
  const QSMContainer: React.FC = () => {
16
17
  const isMobile = useMediaQuery('(max-width: 768px)');
@@ -120,6 +121,7 @@ const QSMContainer: React.FC = () => {
120
121
  <div className="qsm__filter__classgroup">
121
122
  <TravelClassPicker />
122
123
  <TravelTypePicker />
124
+ <TravelNationalityPicker />
123
125
  </div>
124
126
  </div>
125
127
  <div className="qsm__input-group">
@@ -1,9 +1,9 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
2
  import { useDispatch } from 'react-redux';
3
- import { TravelClass, TravelType } from '../../types';
3
+ import { Nationality, TravelClass, TravelType } from '../../types';
4
4
 
5
5
  interface ItemPickerProps {
6
- items: TravelType[] | TravelClass[];
6
+ items: TravelType[] | TravelClass[] | Nationality[];
7
7
  selection: string | undefined;
8
8
  label: string;
9
9
  placeholder: string;
@@ -21,8 +21,8 @@ const ItemPicker: React.FC<ItemPickerProps> = ({ items, selection, label, placeh
21
21
  const toggleButtonRef = useRef<HTMLButtonElement | null>(null);
22
22
 
23
23
  const handlePick = (picked: string) => {
24
- dispatch(onPick(picked));
25
24
  setIsDropdownOpen(false);
25
+ dispatch(onPick(picked));
26
26
  };
27
27
 
28
28
  useEffect(() => {
@@ -49,7 +49,7 @@ const ItemPicker: React.FC<ItemPickerProps> = ({ items, selection, label, placeh
49
49
  }, [isDropdownOpen]);
50
50
 
51
51
  return (
52
- <label className={'dropdown__input ' + classModifier}>
52
+ <div className={'dropdown__input ' + classModifier}>
53
53
  <span className="dropdown__label">{label}</span>
54
54
  <div className="dropdown">
55
55
  <button
@@ -62,7 +62,13 @@ const ItemPicker: React.FC<ItemPickerProps> = ({ items, selection, label, placeh
62
62
  {isDropdownOpen && (
63
63
  <ul className={`dropdown-menu dropdown-menu--${openDirection}`} ref={dropdownMenuRef}>
64
64
  {items.map(({ label, icon }) => (
65
- <li key={label} onClick={() => handlePick(label)} className={`dropdown-menu__item${selection === label ? ' dropdown-menu__item--selected' : ''}`}>
65
+ <li
66
+ key={label}
67
+ onClick={(e) => {
68
+ handlePick(label);
69
+ e.stopPropagation();
70
+ }}
71
+ className={`dropdown-menu__item${selection === label ? ' dropdown-menu__item--selected' : ''}`}>
66
72
  {icon && <span className="travel-class-icon">{icon}</span>}
67
73
  {label}
68
74
  </li>
@@ -70,7 +76,7 @@ const ItemPicker: React.FC<ItemPickerProps> = ({ items, selection, label, placeh
70
76
  </ul>
71
77
  )}
72
78
  </div>
73
- </label>
79
+ </div>
74
80
  );
75
81
  };
76
82
 
@@ -0,0 +1,24 @@
1
+ import React, { useContext } from 'react';
2
+ import { useSelector } from 'react-redux';
3
+ import { QSMRootState } from '../../store/qsm-store';
4
+ import { setSelectedNationality } from '../../store/qsm-slice';
5
+ import QSMConfigurationContext from '../../qsm-configuration-context';
6
+ import ItemPicker from '../item-picker';
7
+
8
+ const TravelNationalityPicker: React.FC = () => {
9
+ const { nationalities } = useContext(QSMConfigurationContext);
10
+ const { selectedNationality } = useSelector((state: QSMRootState) => state.qsm);
11
+
12
+ return (
13
+ <ItemPicker
14
+ items={nationalities}
15
+ selection={selectedNationality}
16
+ label="Nationaliteit"
17
+ placeholder="Selecteer nationaliteit"
18
+ classModifier="travel-class-picker__items"
19
+ onPick={setSelectedNationality}
20
+ />
21
+ );
22
+ };
23
+
24
+ export default TravelNationalityPicker;
@@ -28,6 +28,8 @@ const QSMConfigurationContext = React.createContext<QSMConfiguration>({
28
28
  travelClasses: [],
29
29
  travelClassIcon: '',
30
30
 
31
+ nationalities: [],
32
+
31
33
  showReturnDate: false,
32
34
  datesIcon: '',
33
35
 
@@ -36,6 +36,7 @@ export interface QSMState {
36
36
  selectedTravelType?: string;
37
37
  travelClasses: TravelClass[];
38
38
  selectedTravelClass?: string;
39
+ selectedNationality?: string;
39
40
  defaultTravelers: number;
40
41
  maxTravelers: number;
41
42
  maxChildAge: number;
@@ -75,6 +76,7 @@ const initialState: QSMState = {
75
76
  selectedTravelType: undefined,
76
77
  travelClasses: [],
77
78
  selectedTravelClass: undefined,
79
+ selectedNationality: undefined,
78
80
  defaultTravelers: 2,
79
81
  maxTravelers: 9,
80
82
  maxChildAge: 12,
@@ -174,6 +176,9 @@ const qsmSlice = createSlice({
174
176
  setSelectedTravelClass: (state, action) => {
175
177
  state.selectedTravelClass = action.payload;
176
178
  },
179
+ setSelectedNationality: (state, action) => {
180
+ state.selectedNationality = action.payload;
181
+ },
177
182
  setDefaultTravelers: (state, action: PayloadAction<number>) => {
178
183
  state.defaultTravelers = action.payload;
179
184
  },
@@ -245,6 +250,7 @@ export const {
245
250
  setSelectedTravelType,
246
251
  setTravelClasses,
247
252
  setSelectedTravelClass,
253
+ setSelectedNationality,
248
254
  setDefaultTravelers,
249
255
  setMaxTravelers,
250
256
  setMaxChildAge,
package/src/qsm/types.ts CHANGED
@@ -54,6 +54,8 @@ export interface QSMConfiguration {
54
54
  onSubmit: (data: any) => void;
55
55
  submitLabel: string;
56
56
  submitIcon: ReactNode;
57
+
58
+ nationalities: Nationality[];
57
59
  }
58
60
 
59
61
  export interface BaseFieldConfig {
@@ -132,3 +134,9 @@ export interface TravelClass {
132
134
  label: string;
133
135
  icon?: ReactNode;
134
136
  }
137
+
138
+ export interface Nationality {
139
+ id: number;
140
+ label: string;
141
+ icon?: ReactNode;
142
+ }