@transferwise/components 45.27.0 → 45.28.0

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.
@@ -20,7 +20,7 @@ import { usePopper } from 'react-popper';
20
20
  import throttle from 'lodash.throttle';
21
21
  import { createPortal } from 'react-dom';
22
22
  import { isUndefined, isKey, isNumber, isEmpty, isNull, isArray } from '@transferwise/neptune-validation';
23
- import { Illustration } from '@wise/art';
23
+ import { Flag, Illustration } from '@wise/art';
24
24
  import clamp$2 from 'lodash.clamp';
25
25
  import debounce from 'lodash.debounce';
26
26
  import requiredIf from 'react-required-if';
@@ -6860,6 +6860,7 @@ function SelectInput({
6860
6860
  size = 'md',
6861
6861
  className,
6862
6862
  onChange,
6863
+ onSearchChange,
6863
6864
  onClear
6864
6865
  }) {
6865
6866
  const [open, setOpen] = useState(false);
@@ -6930,7 +6931,8 @@ function SelectInput({
6930
6931
  filterable: filterable,
6931
6932
  filterPlaceholder: filterPlaceholder,
6932
6933
  searchInputRef: searchInputRef,
6933
- listboxRef: listboxRef
6934
+ listboxRef: listboxRef,
6935
+ onSearchChange: onSearchChange
6934
6936
  })
6935
6937
  })
6936
6938
  });
@@ -6998,7 +7000,8 @@ function SelectInputOptions({
6998
7000
  filterable = false,
6999
7001
  filterPlaceholder,
7000
7002
  searchInputRef,
7001
- listboxRef
7003
+ listboxRef,
7004
+ onSearchChange
7002
7005
  }) {
7003
7006
  const intl = useIntl();
7004
7007
  const controllerRef = filterable ? searchInputRef : listboxRef;
@@ -7050,6 +7053,9 @@ function SelectInputOptions({
7050
7053
  },
7051
7054
  onChange: event => {
7052
7055
  setQuery(event.currentTarget.value);
7056
+ if (onSearchChange) {
7057
+ onSearchChange(event.currentTarget.value);
7058
+ }
7053
7059
  }
7054
7060
  })
7055
7061
  }) : null, /*#__PURE__*/jsxs("section", {
@@ -7779,1139 +7785,526 @@ Money.propTypes = {
7779
7785
  var Money$1 = Money;
7780
7786
 
7781
7787
  var messages$3 = defineMessages({
7782
- searchPlaceholder: {
7783
- id: "neptune.Select.searchPlaceholder"
7788
+ selectPlaceholder: {
7789
+ id: "neptune.MoneyInput.Select.placeholder"
7784
7790
  }
7785
7791
  });
7786
7792
 
7787
- // Option.tsx NEW
7788
- function Option$1({
7789
- label,
7790
- currency = '',
7791
- note = '',
7792
- secondary = '',
7793
- icon,
7794
- classNames = {},
7795
- selected = false
7796
- }) {
7797
- const {
7798
- isModern
7799
- } = useTheme();
7800
- const style = classes => classes.map(className => classNames[className] || className).join(' ');
7801
- const currencyClassNames = currency ? style(['currency-flag', `currency-flag-${currency.toLowerCase()}`]) : undefined;
7802
- const iconElement = icon ? /*#__PURE__*/cloneElement(icon, {
7803
- size: 24,
7804
- className: 'tw-icon'
7805
- }) : currency && /*#__PURE__*/jsx("i", {
7806
- className: currencyClassNames
7807
- });
7808
- const titleAndNoteElement = /*#__PURE__*/jsxs(Body, {
7809
- as: "span",
7810
- type: Typography.BODY_LARGE,
7811
- className: selected ? 'text-ellipsis' : undefined,
7812
- children: [label, note && /*#__PURE__*/jsx(Body, {
7813
- as: "span",
7814
- className: isModern ? 'm-l-1' : 'm-l-1 body-2',
7815
- children: note
7816
- })]
7817
- });
7818
- const secondaryElementClassNames = () => {
7819
- let classes = undefined;
7820
- if (selected) {
7821
- classes = 'text-ellipsis';
7822
- }
7823
- if (isModern) {
7824
- return classes;
7825
- }
7826
- return `${classes ? classes + ' ' : ''}body-2`;
7827
- };
7828
- const secondaryElement = secondary && /*#__PURE__*/jsx(Body, {
7829
- className: secondaryElementClassNames(),
7830
- children: secondary
7831
- });
7832
- return iconElement ? /*#__PURE__*/jsxs("div", {
7833
- className: "d-flex np-option-content",
7834
- children: [/*#__PURE__*/jsx("div", {
7835
- className: `d-flex flex-column${selected ? ' justify-content-center' : ''}`,
7836
- children: iconElement
7837
- }), /*#__PURE__*/jsxs("div", {
7838
- className: "d-flex flex-column justify-content-center",
7839
- children: [titleAndNoteElement, secondaryElement]
7840
- })]
7841
- }) : /*#__PURE__*/jsxs(Fragment, {
7842
- children: [iconElement, titleAndNoteElement, secondaryElement]
7843
- });
7793
+ // TODO: do not duplicate this between formatting and components
7794
+ const currencyDecimals = {
7795
+ BIF: 0,
7796
+ BYR: 0,
7797
+ CLP: 0,
7798
+ DJF: 0,
7799
+ GNF: 0,
7800
+ JPY: 0,
7801
+ KMF: 0,
7802
+ KRW: 0,
7803
+ MGA: 0,
7804
+ PYG: 0,
7805
+ RWF: 0,
7806
+ VND: 0,
7807
+ VUV: 0,
7808
+ XAF: 0,
7809
+ XOF: 0,
7810
+ XPF: 0,
7811
+ // technically HUF does have decimals, but due to the exchange rate banks
7812
+ // do not accept decimal amounts
7813
+ HUF: 0,
7814
+ BHD: 3,
7815
+ JOD: 3,
7816
+ KWD: 3,
7817
+ OMR: 3,
7818
+ TND: 3
7819
+ };
7820
+ const DEFAULT_CURRENCY_DECIMALS = 2;
7821
+ function isNumberLocaleSupported() {
7822
+ const number = 1234;
7823
+ const numberString = number.toLocaleString && number.toLocaleString(DEFAULT_LOCALE);
7824
+ return numberString === '1,234';
7844
7825
  }
7845
-
7846
- const SearchBox = /*#__PURE__*/forwardRef(({
7847
- id,
7848
- classNames: classNames$1 = {},
7849
- focusedOptionId,
7850
- onChange,
7851
- onClick,
7852
- placeholder = undefined,
7853
- value = ''
7854
- }, reference) => {
7855
- const style = className => classNames$1[className] || className;
7856
- return /*#__PURE__*/jsx("li", {
7857
- className: style('border-bottom'),
7858
- children: /*#__PURE__*/jsx("a", {
7859
- className: `${style('np-select-filter-link')} ${style('p-a-0')}`,
7860
- children: /*#__PURE__*/jsxs("div", {
7861
- className: style('input-group'),
7862
- children: [/*#__PURE__*/jsx("span", {
7863
- className: classNames('input-group-addon', 'input-group-addon--search'),
7864
- children: /*#__PURE__*/jsx(Search, {
7865
- className: classNames(style('tw-icon'), style('tw-icon-search')),
7866
- size: 24
7867
- })
7868
- }), /*#__PURE__*/jsx(Input, {
7869
- ref: reference,
7870
- id: id,
7871
- role: "searchbox",
7872
- inputMode: "search",
7873
- className: classNames(style('np-select-filter')),
7874
- placeholder: placeholder,
7875
- value: value,
7876
- spellCheck: "false",
7877
- "aria-activedescendant": focusedOptionId,
7878
- onChange: onChange,
7879
- onClick: onClick
7880
- })]
7881
- })
7882
- })
7883
- });
7884
- });
7885
- var SearchBox$1 = SearchBox;
7886
-
7887
- const DEFAULT_SEARCH_VALUE = '';
7888
- const DEFAULT_OPTIONS_PAGE_SIZE = 1000;
7889
- const includesString = (string1, string2) => string1.toLowerCase().includes(string2.toLowerCase());
7890
- function defaultFilterFunction(option, searchValue) {
7891
- if (isPlaceholderOption(option)) {
7892
- return true;
7826
+ function getValidLocale(locale) {
7827
+ try {
7828
+ const noUnderscoreLocale = locale.replace(/_/, '-');
7829
+ Intl.NumberFormat(noUnderscoreLocale);
7830
+ return noUnderscoreLocale;
7831
+ } catch {
7832
+ return 'en-GB';
7893
7833
  }
7894
- const {
7895
- label,
7896
- note,
7897
- secondary,
7898
- currency,
7899
- searchStrings
7900
- } = option;
7901
- return !!label && includesString(label, searchValue) || !!note && includesString(note, searchValue) || !!secondary && includesString(secondary, searchValue) || !!currency && includesString(currency, searchValue) || !!searchStrings && searchStrings.some(string => includesString(string, searchValue));
7902
- }
7903
- function isActionableOption(option) {
7904
- return !option.header && !option.separator && !option.disabled;
7905
7834
  }
7906
- function isHeaderOption(option) {
7907
- return option != null && 'header' in option;
7835
+ function getCurrencyDecimals(currency = '') {
7836
+ const upperCaseCurrency = currency.toUpperCase();
7837
+ if (Object.prototype.hasOwnProperty.call(currencyDecimals, upperCaseCurrency)) {
7838
+ return currencyDecimals[upperCaseCurrency];
7839
+ }
7840
+ return DEFAULT_CURRENCY_DECIMALS;
7908
7841
  }
7909
- function isSeparatorOption(option) {
7910
- return option != null && 'separator' in option;
7842
+ function getDecimalSeparator(locale) {
7843
+ return isNumberLocaleSupported() ? 1.1.toLocaleString(locale)[1] : '.';
7911
7844
  }
7912
- function clamp(from, to, value) {
7913
- return Math.max(Math.min(to, value), from);
7845
+ function parseAmount(number, currency, locale) {
7846
+ const validLocale = getValidLocale(locale);
7847
+ const precision = getCurrencyDecimals(currency);
7848
+ const groupSeparator = isNumberLocaleSupported() ? 10000 .toLocaleString(validLocale)[2] : ',';
7849
+ const decimalSeparator = getDecimalSeparator(validLocale);
7850
+ const numberWithStandardDecimalSeparator = (number ? `${number}` : '').replace(new RegExp(`\\${groupSeparator}`, 'g'), '').replace(new RegExp(`\\${decimalSeparator}`, 'g'), '.').replace(/[^0-9.]/g, '');
7851
+ const parsedAmount = parseFloat(parseFloat(numberWithStandardDecimalSeparator).toFixed(precision));
7852
+ return Math.abs(parsedAmount);
7914
7853
  }
7915
7854
 
7916
- /**
7917
- * No option or placeholder option is selected
7918
- */
7919
- const DEFAULT_SELECTED_OPTION = null;
7920
- function isPlaceholderOption(option) {
7921
- return option === DEFAULT_SELECTED_OPTION || 'placeholder' in option;
7922
- }
7923
- function isSearchableOption(option) {
7924
- return !isHeaderOption(option) && !isSeparatorOption(option) && !isPlaceholderOption(option);
7925
- }
7926
- const getUniqueIdForOption = (parentId = '', option) => {
7927
- if (option == null) {
7928
- return undefined;
7855
+ const Currency = PropTypes.shape({
7856
+ header: PropTypes.string,
7857
+ value: PropTypes.string,
7858
+ label: PropTypes.string,
7859
+ currency: PropTypes.string,
7860
+ note: PropTypes.string,
7861
+ searchable: PropTypes.string
7862
+ });
7863
+ const isNumberOrNull = v => isNumber(v) || isNull(v);
7864
+ const formatAmountIfSet = (amount, currency, locale, maxLengthOverride) => {
7865
+ if (maxLengthOverride) {
7866
+ return amount || '';
7867
+ } else {
7868
+ return typeof amount === 'number' ? formatAmount(amount, currency, locale) : '';
7929
7869
  }
7930
- const uniqueOptionId = option.value || (option.label?.replace(/\s/g, '') ?? '');
7931
- return `option-${parentId}-${uniqueOptionId}`;
7932
7870
  };
7933
-
7934
- /**
7935
- * @deprecated Use `SelectInput` instead (https://neptune.wise.design/blog/2023-11-28-adopting-our-new-selectinput)
7936
- */
7937
- function Select({
7938
- placeholder,
7939
- id,
7940
- required,
7941
- disabled,
7942
- inverse,
7943
- dropdownWidth,
7944
- size,
7945
- block,
7946
- selected,
7947
- search,
7948
- onChange,
7949
- onFocus,
7950
- onBlur,
7951
- options: defaultOptions,
7952
- onSearchChange,
7953
- searchValue: initSearchValue,
7954
- searchPlaceholder,
7955
- // eslint-disable-next-line unicorn/prevent-abbreviations
7956
- classNames: classNamesProp,
7957
- dropdownUp,
7958
- dropdownProps,
7959
- buttonProps
7960
- }) {
7961
- const {
7962
- formatMessage
7963
- } = useIntl();
7964
- const {
7965
- isModern
7966
- } = useTheme();
7967
- const s = className => classNamesProp[className] || className;
7968
- const [open, setOpen] = useState(false);
7969
- const [searchValue, setSearchValue] = useState(DEFAULT_SEARCH_VALUE);
7970
- const [keyboardFocusedOptionIndex, setKeyboardFocusedOptionIndex] = useState(null);
7971
- const keyboardFocusedReference = useRef();
7972
- const previousKeyboardFocusedOptionIndex = useRef();
7973
- const [numberOfOptionsShown, setNumberOfOptionsShown] = useState(DEFAULT_OPTIONS_PAGE_SIZE);
7974
- const searchBoxReference = useRef(null);
7975
- const selectReference = useRef(null);
7976
- const dropdownButtonReference = useRef(null);
7977
- const optionsListReference = useRef(null);
7978
- const isSearchEnabled = !!onSearchChange || !!search;
7979
- const isDropdownAutoWidth = dropdownWidth == null;
7980
- const fallbackButtonId = useMemo(() => getSimpleRandomId('np-select-'), []);
7981
- const options = useMemo(() => {
7982
- if (!search || !searchValue) {
7983
- return defaultOptions;
7984
- }
7985
- return defaultOptions.filter(isSearchableOption).filter(option => {
7986
- if (typeof search === 'function') {
7987
- return search(option, searchValue);
7988
- } else {
7989
- return defaultFilterFunction(option, searchValue);
7990
- }
7871
+ const parseNumber = (amount, currency, locale, maxLengthOverride) => {
7872
+ if (!maxLengthOverride) {
7873
+ return parseAmount(amount, currency, locale);
7874
+ }
7875
+ if (maxLengthOverride && amount.length > maxLengthOverride) {
7876
+ return 0;
7877
+ }
7878
+ return +amount;
7879
+ };
7880
+ const inputKeyCodeAllowlist = new Set([KeyCodes.BACKSPACE, KeyCodes.DELETE, KeyCodes.COMMA, KeyCodes.PERIOD, KeyCodes.DOWN, KeyCodes.UP, KeyCodes.LEFT, KeyCodes.RIGHT, KeyCodes.ENTER, KeyCodes.ESCAPE, KeyCodes.TAB]);
7881
+ const inputKeyAllowlist = new Set([Key.PERIOD, Key.COMMA]);
7882
+ class MoneyInput extends Component {
7883
+ constructor(props) {
7884
+ super(props);
7885
+ const {
7886
+ locale
7887
+ } = this.props.intl;
7888
+ this.formatMessage = this.props.intl.formatMessage;
7889
+ this.state = {
7890
+ searchQuery: '',
7891
+ selectedOption: this.props.selectedCurrency,
7892
+ formattedAmount: formatAmountIfSet(props.amount, props.selectedCurrency.currency, locale, props.maxLengthOverride),
7893
+ locale
7894
+ };
7895
+ }
7896
+ UNSAFE_componentWillReceiveProps(nextProps) {
7897
+ this.setState({
7898
+ locale: nextProps?.intl?.locale
7991
7899
  });
7992
- }, [defaultOptions, search, searchValue]);
7993
- const selectableOptions = useMemo(() => options.filter(isActionableOption), [options]);
7994
- const focusedOption = selectableOptions[keyboardFocusedOptionIndex];
7995
- const computedId = id || fallbackButtonId;
7996
- const listboxId = `${computedId}-listbox`;
7997
- const searchBoxId = `${computedId}-searchbox`;
7998
- const {
7999
- isMobile
8000
- } = useLayout();
8001
- useEffect(() => {
8002
- let cancelled;
8003
- if (keyboardFocusedOptionIndex >= 0) {
8004
- requestAnimationFrame(() => {
8005
- if (!cancelled) {
8006
- if (isSearchEnabled) {
8007
- keyboardFocusedReference.current?.scrollIntoView?.({
8008
- block: 'center'
8009
- });
8010
- } else {
8011
- keyboardFocusedReference.current?.focus();
8012
- }
8013
- }
7900
+ if (!this.amountFocused) {
7901
+ this.setState({
7902
+ formattedAmount: formatAmountIfSet(nextProps.amount, nextProps.selectedCurrency.currency, nextProps?.intl?.locale, nextProps.maxLengthOverride)
8014
7903
  });
8015
- return () => {
8016
- cancelled = true;
8017
- };
8018
7904
  }
8019
- }, [keyboardFocusedOptionIndex, isSearchEnabled]);
8020
- const handleOnClick = () => {
8021
- setOpen(true);
7905
+ }
7906
+ isInputAllowedForKeyEvent = event => {
7907
+ const {
7908
+ keyCode,
7909
+ metaKey,
7910
+ key,
7911
+ ctrlKey
7912
+ } = event;
7913
+ const isNumberKey = isNumber(parseInt(key, 10));
7914
+ return isNumberKey || metaKey || ctrlKey || inputKeyCodeAllowlist.has(keyCode) || inputKeyAllowlist.has(key);
8022
7915
  };
8023
- const handleTouchStart = event => {
8024
- if (event.currentTarget === event.target && open) {
8025
- handleCloseOptions();
7916
+ handleKeyDown = event => {
7917
+ if (!this.isInputAllowedForKeyEvent(event)) {
7918
+ event.preventDefault();
8026
7919
  }
8027
7920
  };
8028
- const handleOnFocus = event => {
8029
- if (onFocus) {
8030
- onFocus(event);
7921
+ handlePaste = event => {
7922
+ const paste = (event.clipboardData || window.clipboardData).getData('text');
7923
+ const {
7924
+ locale
7925
+ } = this.state;
7926
+ const parsed = isEmpty(paste) ? null : parseNumber(paste, this.props.selectedCurrency.currency, locale, this.props.maxLengthOverride);
7927
+ if (isNumberOrNull(parsed)) {
7928
+ this.setState({
7929
+ formattedAmount: formatAmountIfSet(parsed, this.props.selectedCurrency.currency, locale, this.props.maxLengthOverride)
7930
+ });
7931
+ this.props.onAmountChange(parsed);
8031
7932
  }
7933
+ event.preventDefault();
8032
7934
  };
8033
- const handleOnBlur = event => {
7935
+ onAmountChange = event => {
8034
7936
  const {
8035
- nativeEvent
8036
- } = event;
8037
- if (nativeEvent) {
8038
- const elementReceivingFocus = nativeEvent.relatedTarget;
8039
- const select = event.currentTarget;
8040
- if (select && elementReceivingFocus && select.contains(elementReceivingFocus)) {
8041
- return;
8042
- }
8043
- }
8044
- if (onBlur) {
8045
- onBlur(event);
7937
+ value
7938
+ } = event.target;
7939
+ this.setState({
7940
+ formattedAmount: value
7941
+ });
7942
+ const parsed = isEmpty(value) ? null : parseNumber(value, this.props.selectedCurrency.currency, this.state.locale, this.props.maxLengthOverride);
7943
+ if (isNumberOrNull(parsed)) {
7944
+ this.props.onAmountChange(parsed);
8046
7945
  }
8047
7946
  };
8048
- const handleSearchChange = event => {
8049
- setNumberOfOptionsShown(DEFAULT_OPTIONS_PAGE_SIZE);
8050
- setSearchValue(event.target.value);
8051
- if (onSearchChange) {
8052
- onSearchChange(event.target.value);
8053
- }
7947
+ onAmountBlur = () => {
7948
+ this.amountFocused = false;
7949
+ this.setAmount();
8054
7950
  };
8055
- const handleKeyDown = event => {
8056
- switch (event.keyCode) {
8057
- case KeyCodes.UP:
8058
- case KeyCodes.DOWN:
8059
- if (open) {
8060
- moveFocusWithDifference(event.keyCode === KeyCodes.UP ? -1 : 1);
8061
- } else {
8062
- setOpen(true);
8063
- }
8064
- stopPropagation$1(event);
8065
- break;
8066
- case KeyCodes.SPACE:
8067
- if (event.target !== searchBoxReference.current) {
8068
- if (open) {
8069
- selectKeyboardFocusedOption();
8070
- } else {
8071
- setOpen(true);
8072
- }
8073
- stopPropagation$1(event);
8074
- }
8075
- break;
8076
- case KeyCodes.ENTER:
8077
- if (open) {
8078
- selectKeyboardFocusedOption();
8079
- } else {
8080
- setOpen(true);
8081
- }
8082
- stopPropagation$1(event);
8083
- break;
8084
- case KeyCodes.ESCAPE:
8085
- handleCloseOptions();
8086
- stopPropagation$1(event);
8087
- break;
8088
- case KeyCodes.TAB:
8089
- if (open) {
8090
- selectKeyboardFocusedOption();
8091
- }
8092
- break;
8093
- }
7951
+ onAmountFocus = () => {
7952
+ this.amountFocused = true;
8094
7953
  };
8095
- function selectKeyboardFocusedOption() {
8096
- if (keyboardFocusedOptionIndex != null) {
8097
- selectableOptions.length > 0 && selectOption(selectableOptions[keyboardFocusedOptionIndex]);
8098
- }
8099
- }
8100
- function moveFocusWithDifference(difference) {
8101
- const selectedOptionIndex = selectableOptions.reduce((optionIndex, current, index) => {
8102
- if (optionIndex != null) {
8103
- return optionIndex;
8104
- }
8105
- if (isOptionSelected(selected, current)) {
8106
- return index;
8107
- }
8108
- return null;
8109
- }, null);
8110
- const previousFocusedIndex = previousKeyboardFocusedOptionIndex.current ?? -1;
8111
- let indexToStartMovingFrom = previousFocusedIndex;
8112
- if (previousFocusedIndex === -1) {
8113
- if (selectedOptionIndex == null) {
8114
- setKeyboardFocusedOptionIndex(0);
7954
+ mapOption = item => {
7955
+ return {
7956
+ type: 'option',
7957
+ value: item,
7958
+ filterMatchers: [item.value, item.label, item.note, item.searchable]
7959
+ };
7960
+ };
7961
+ getSelectOptions() {
7962
+ const selectOptions = [...filterOptionsForQuery(this.props.currencies, this.state.searchQuery)];
7963
+ let formattedOptions = [];
7964
+ let groupIndex = null;
7965
+ selectOptions.forEach(item => {
7966
+ if (item.header) {
7967
+ formattedOptions.push({
7968
+ type: 'group',
7969
+ label: item.header,
7970
+ options: []
7971
+ });
7972
+ groupIndex = formattedOptions.length - 1;
8115
7973
  } else {
8116
- indexToStartMovingFrom = selectedOptionIndex;
7974
+ if (groupIndex === null) {
7975
+ formattedOptions.push(this.mapOption(item));
7976
+ } else {
7977
+ formattedOptions[groupIndex]?.options.push(this.mapOption(item));
7978
+ }
8117
7979
  }
8118
- }
8119
- const unClampedNewIndex = indexToStartMovingFrom + difference;
8120
- const newIndex = clamp(0, selectableOptions.length - 1, unClampedNewIndex);
8121
- setKeyboardFocusedOptionIndex(newIndex);
7980
+ });
7981
+ return formattedOptions;
8122
7982
  }
8123
- useEffect(() => {
8124
- if (open) {
8125
- if (!isMobile || searchValue) {
8126
- if (isSearchEnabled && !!searchBoxReference.current) {
8127
- searchBoxReference.current.focus();
8128
- }
8129
- if (!isSearchEnabled && optionsListReference.current && (previousKeyboardFocusedOptionIndex.current == null || Number.isNaN(previousKeyboardFocusedOptionIndex.current))) {
8130
- optionsListReference.current.focus();
8131
- }
7983
+ setAmount() {
7984
+ this.setState(previousState => {
7985
+ const parsed = parseNumber(previousState.formattedAmount, this.props.selectedCurrency.currency, previousState.locale, this.props.maxLengthOverride);
7986
+ if (!isNumberOrNull(parsed)) {
7987
+ return {
7988
+ formattedAmount: previousState.formattedAmount
7989
+ };
8132
7990
  }
8133
- previousKeyboardFocusedOptionIndex.current = keyboardFocusedOptionIndex;
8134
- } else {
8135
- previousKeyboardFocusedOptionIndex.current = null;
7991
+ return {
7992
+ formattedAmount: formatAmountIfSet(parsed, this.props.selectedCurrency.currency, previousState.locale, this.props.maxLengthOverride)
7993
+ };
7994
+ });
7995
+ }
7996
+ handleSelectChange = value => {
7997
+ this.handleSearchChange('');
7998
+ this.setState({
7999
+ selectedOption: value
8000
+ });
8001
+ this.props.onCurrencyChange(value);
8002
+ };
8003
+ handleCustomAction = () => {
8004
+ this.handleSearchChange('');
8005
+ if (this.props.onCustomAction) {
8006
+ this.props.onCustomAction();
8136
8007
  }
8137
- }, [open, searchValue, isSearchEnabled, isMobile, keyboardFocusedOptionIndex]);
8138
- const handleCloseOptions = () => {
8139
- setOpen(false);
8140
- setKeyboardFocusedOptionIndex(null);
8141
- if (dropdownButtonReference.current) {
8142
- dropdownButtonReference.current.focus();
8008
+ };
8009
+ handleSearchChange = searchQuery => {
8010
+ this.setState({
8011
+ searchQuery
8012
+ });
8013
+ if (this.props.onSearchChange) {
8014
+ this.props.onSearchChange({
8015
+ searchQuery,
8016
+ filteredOptions: filterOptionsForQuery(this.props.currencies, searchQuery)
8017
+ });
8143
8018
  }
8144
8019
  };
8145
- function createSelectHandlerForOption(option) {
8146
- return event => {
8147
- stopPropagation$1(event);
8148
- selectOption(option);
8149
- };
8150
- }
8151
- function selectOption(option) {
8152
- onChange(isPlaceholderOption(option) ? DEFAULT_SELECTED_OPTION : option);
8153
- handleCloseOptions();
8154
- }
8155
- function renderOptionsList({
8156
- className
8157
- } = {}) {
8158
- const dropdownClass = classNames(s('np-dropdown-menu'), {
8159
- [s('np-dropdown-menu-desktop')]: !isMobile,
8160
- [s(`np-dropdown-menu-${dropdownWidth}`)]: !isMobile && !isDropdownAutoWidth
8161
- }, s(className));
8162
- const showPlaceholder = !required && !isSearchEnabled && Boolean(placeholder);
8163
- return /*#__PURE__*/jsxs("ul", {
8164
- ref: optionsListReference,
8165
- id: listboxId,
8166
- role: "listbox",
8167
- "aria-orientation": "vertical",
8168
- "aria-activedescendant": getUniqueIdForOption(id, selected),
8169
- tabIndex: "-1",
8170
- className: dropdownClass,
8171
- ...dropdownProps,
8172
- children: [showPlaceholder && /*#__PURE__*/jsx(PlaceHolderOption, {}), isSearchEnabled && /*#__PURE__*/jsx(SearchBox$1, {
8173
- ref: searchBoxReference,
8174
- id: searchBoxId,
8175
- classNames: classNamesProp,
8176
- value: initSearchValue || searchValue,
8177
- placeholder: searchPlaceholder || formatMessage(messages$3.searchPlaceholder),
8178
- focusedOptionId: getUniqueIdForOption(id, focusedOption),
8179
- onChange: handleSearchChange,
8180
- onClick: stopPropagation$1
8181
- }), options.slice(0, numberOfOptionsShown).map(renderOption), numberOfOptionsShown < options.length && /*#__PURE__*/jsx(ShowMoreOption, {})]
8182
- });
8183
- }
8184
- function ShowMoreOption() {
8185
- function handleOnClick(event) {
8186
- stopPropagation$1(event);
8187
- setNumberOfOptionsShown(numberOfOptionsShown + DEFAULT_OPTIONS_PAGE_SIZE);
8188
- }
8189
- return (
8190
- /*#__PURE__*/
8191
- /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */
8192
- jsx("li", {
8193
- className: classNames(s('clickable'), s('border-bottom'), s('show-more')),
8194
- onClick: handleOnClick,
8195
- onKeyPress: handleOnClick,
8196
- children: /*#__PURE__*/jsx("a", {
8197
- children: "..."
8198
- })
8199
- })
8200
- );
8201
- }
8202
- function PlaceHolderOption() {
8203
- const placeholderOption = {
8204
- placeholder
8020
+ style = className => this.props.classNames[className] || className;
8021
+ render() {
8022
+ const {
8023
+ selectedCurrency,
8024
+ onCurrencyChange,
8025
+ size,
8026
+ addon,
8027
+ id,
8028
+ selectProps,
8029
+ maxLengthOverride
8030
+ } = this.props;
8031
+ const selectOptions = this.getSelectOptions();
8032
+ const getFirstOption = () => {
8033
+ if (selectOptions.length !== 0) {
8034
+ const firstOption = selectOptions[0];
8035
+ if (firstOption.type === 'option') {
8036
+ return firstOption.value;
8037
+ }
8038
+ if (firstOption.type === 'group' && firstOption.options.length !== 0) {
8039
+ return firstOption.options[0].value;
8040
+ }
8041
+ }
8042
+ return null;
8205
8043
  };
8206
- return (
8207
- /*#__PURE__*/
8208
- /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */
8209
- jsx("li", {
8210
- className: classNames(s('clickable'), s('border-bottom')),
8211
- onClick: createSelectHandlerForOption(placeholderOption),
8212
- onKeyPress: createSelectHandlerForOption(placeholderOption),
8213
- children: /*#__PURE__*/jsx("a", {
8214
- children: placeholder
8044
+ const firstOption = getFirstOption();
8045
+ const isFixedCurrency = !this.state.searchQuery && (selectOptions.length === 1 && firstOption.currency === selectedCurrency.currency || !onCurrencyChange);
8046
+ const disabled = !this.props.onAmountChange;
8047
+ return /*#__PURE__*/jsxs("div", {
8048
+ className: classNames(this.style('tw-money-input'), this.style('input-group'), this.style(`input-group-${size}`)),
8049
+ children: [/*#__PURE__*/jsx(Input, {
8050
+ id: id,
8051
+ value: this.state.formattedAmount,
8052
+ inputMode: "decimal",
8053
+ disabled: disabled,
8054
+ maxLength: maxLengthOverride,
8055
+ placeholder: formatAmountIfSet(this.props.placeholder, this.props.selectedCurrency.currency, this.state.locale, this.props.maxLengthOverride),
8056
+ autoComplete: "off",
8057
+ onKeyDown: this.handleKeyDown,
8058
+ onChange: this.onAmountChange,
8059
+ onFocus: this.onAmountFocus,
8060
+ onBlur: this.onAmountBlur,
8061
+ onPaste: this.handlePaste
8062
+ }), addon && /*#__PURE__*/jsx("span", {
8063
+ className: classNames(this.style('input-group-addon'), this.style(`input-${size}`), disabled ? this.style('disabled') : ''),
8064
+ children: addon
8065
+ }), isFixedCurrency ? /*#__PURE__*/jsxs("div", {
8066
+ className: classNames(this.style('input-group-addon'), this.style(`input-${size}`), this.style('tw-money-input__fixed-currency'), disabled ? this.style('disabled') : ''),
8067
+ children: [(size === 'lg' || size === 'md') && /*#__PURE__*/jsx("span", {
8068
+ className: classNames(this.style('money-input-currency-flag'), this.style('m-r-2')),
8069
+ children: /*#__PURE__*/jsx(Flag, {
8070
+ code: selectedCurrency.currency.toLowerCase(),
8071
+ intrinsicSize: 24
8072
+ })
8073
+ }), /*#__PURE__*/jsx(Title, {
8074
+ as: "span",
8075
+ type: Typography.TITLE_SUBSECTION,
8076
+ className: size === 'lg' ? this.style('m-r-1') : '',
8077
+ children: selectedCurrency.currency.toUpperCase()
8078
+ })]
8079
+ }) : /*#__PURE__*/jsx("div", {
8080
+ className: classNames(this.style('input-group-btn'), this.style('amount-currency-select-btn')),
8081
+ children: /*#__PURE__*/jsx(SelectInput, {
8082
+ items: selectOptions,
8083
+ value: this.state.selectedOption,
8084
+ compareValues: "currency",
8085
+ renderValue: (currency, withinTrigger) => {
8086
+ return /*#__PURE__*/jsx(SelectInputOptionContent, {
8087
+ title: currency.label,
8088
+ note: withinTrigger ? undefined : currency.note,
8089
+ icon: /*#__PURE__*/jsx(Flag, {
8090
+ code: currency.currency,
8091
+ intrinsicSize: 24
8092
+ })
8093
+ });
8094
+ },
8095
+ renderFooter: this.props.onCustomAction ? () =>
8096
+ /*#__PURE__*/
8097
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events
8098
+ jsx("div", {
8099
+ role: "button",
8100
+ tabIndex: "0",
8101
+ onClick: this.handleCustomAction,
8102
+ children: this.props.customActionLabel
8103
+ }) : null,
8104
+ placeholder: this.formatMessage(messages$3.selectPlaceholder),
8105
+ filterable: true,
8106
+ filterPlaceholder: this.props.searchPlaceholder,
8107
+ disabled: disabled,
8108
+ size: size,
8109
+ onChange: this.handleSelectChange,
8110
+ onSearchChange: this.handleSearchChange,
8111
+ ...selectProps
8215
8112
  })
8216
- })
8217
- );
8218
- }
8219
-
8220
- // eslint-disable-next-line react/prop-types
8221
- function SeparatorOption() {
8222
- return /*#__PURE__*/jsx("li", {
8223
- className: s('np-separator'),
8224
- "aria-hidden": true
8225
- });
8226
- }
8227
-
8228
- // eslint-disable-next-line react/prop-types
8229
- function HeaderOption({
8230
- children
8231
- }) {
8232
- return /*#__PURE__*/jsx("li", {
8233
- // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
8234
- className: classNames(s('np-dropdown-header'), s('np-text-title-group')),
8235
- onClick: stopPropagation$1,
8236
- onKeyPress: stopPropagation$1,
8237
- children: children
8113
+ })]
8238
8114
  });
8239
8115
  }
8240
- function isOptionSelected(selected, option) {
8241
- return selected?.value === option?.value;
8116
+ }
8117
+ function filterOptionsForQuery(options, query) {
8118
+ if (!query) {
8119
+ return options;
8242
8120
  }
8243
- const renderOption = (option, index) => {
8244
- const separatorOption = option;
8245
- if (isSeparatorOption(separatorOption) && separatorOption?.separator) {
8246
- return /*#__PURE__*/jsx(SeparatorOption, {}, index);
8247
- }
8248
- const headerOption = option;
8249
- if (isHeaderOption(headerOption) && headerOption.header) {
8250
- return /*#__PURE__*/jsx(HeaderOption, {
8251
- children: headerOption.header
8252
- }, index);
8121
+ const filteredOptions = removeDuplicateValueOptions(options).filter(option => isCurrencyOptionAndFitsQuery(option, query));
8122
+ return sortOptionsLabelsToFirst(filteredOptions, query);
8123
+ }
8124
+ function removeDuplicateValueOptions(options) {
8125
+ const result = [];
8126
+ const resultValues = [];
8127
+ options.forEach(option => {
8128
+ if (option.value && !resultValues.includes(option.value)) {
8129
+ result.push(option);
8130
+ resultValues.push(option.value);
8253
8131
  }
8254
- const isActive = isOptionSelected(selected, option);
8255
- const selectOption = option;
8256
- const isFocusedWithKeyboard = !selectOption.disabled && keyboardFocusedOptionIndex === getIndexWithoutHeadersForIndexWithHeaders(index);
8257
- const className = classNames(s('np-dropdown-item'), selectOption.disabled ? [s('disabled')] : s('clickable'), {
8258
- [s('active')]: isActive,
8259
- [s('np-dropdown-item--focused')]: isFocusedWithKeyboard
8260
- });
8261
- const handleOnClick = selectOption.disabled ? stopPropagation$1 : createSelectHandlerForOption(selectOption);
8262
- return (
8263
- /*#__PURE__*/
8264
- /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */
8265
- jsx("li", {
8266
- ref: isFocusedWithKeyboard ? keyboardFocusedReference : undefined,
8267
- id: getUniqueIdForOption(id, option),
8268
- "aria-selected": isActive,
8269
- "aria-disabled": option.disabled,
8270
- role: "option",
8271
- tabIndex: "-1",
8272
- className: className,
8273
- onClick: handleOnClick,
8274
- onKeyPress: handleOnClick,
8275
- children: /*#__PURE__*/jsx("a", {
8276
- disabled: selectOption.disabled,
8277
- children: /*#__PURE__*/jsx(Option$1, {
8278
- ...selectOption,
8279
- classNames: classNamesProp
8280
- })
8281
- })
8282
- }, index)
8283
- );
8284
- };
8285
- function getIndexWithoutHeadersForIndexWithHeaders(index) {
8286
- return options.reduce((sum, option, currentIndex) => {
8287
- if (currentIndex < index && isActionableOption(option)) {
8288
- return sum + 1;
8289
- }
8290
- return sum;
8291
- }, 0);
8132
+ });
8133
+ return result;
8134
+ }
8135
+ function isCurrencyOptionAndFitsQuery(option, query) {
8136
+ if (!option.value) {
8137
+ return false;
8292
8138
  }
8293
- const hasActiveOptions = !!defaultOptions.length;
8294
- if (open && (initSearchValue || searchValue)) {
8295
- if (hasActiveOptions && keyboardFocusedOptionIndex == null) {
8296
- setKeyboardFocusedOptionIndex(0);
8139
+ return contains(option.label, query) || contains(option.searchable, query) || contains(option.note, query);
8140
+ }
8141
+ function contains(property, query) {
8142
+ return property && property.toLowerCase().includes(query.toLowerCase());
8143
+ }
8144
+ function sortOptionsLabelsToFirst(options, query) {
8145
+ return options.sort((first, second) => {
8146
+ const firstContains = contains(first.label, query);
8147
+ const secondContains = contains(second.label, query);
8148
+ if (firstContains && secondContains) {
8149
+ return 0;
8297
8150
  }
8298
- if (!hasActiveOptions && keyboardFocusedOptionIndex != null) {
8299
- setKeyboardFocusedOptionIndex(null);
8151
+ if (firstContains) {
8152
+ return -1;
8300
8153
  }
8301
- }
8302
- return /*#__PURE__*/jsxs("div", {
8303
- // eslint-disable-line jsx-a11y/no-static-element-interactions
8304
- ref: selectReference,
8305
- className: classNames(s('np-select'), block ? s('btn-block') : null, s('btn-group')),
8306
- onKeyDown: handleKeyDown,
8307
- onTouchMove: handleTouchStart,
8308
- onFocus: handleOnFocus,
8309
- onBlur: handleOnBlur,
8310
- children: [/*#__PURE__*/jsxs(Button, {
8311
- ref: dropdownButtonReference,
8312
- id: computedId,
8313
- block: block,
8314
- size: size,
8315
- htmlType: "button",
8316
- className: classNames(s('np-dropdown-toggle'), s('np-text-body-large'), inverse ? s('np-dropdown-toggle-navy') : null)
8317
- // reset Button's styles
8318
- ,
8319
- type: null,
8320
- priority: null,
8321
- disabled: disabled,
8322
- "aria-controls": listboxId,
8323
- "aria-expanded": open,
8324
- "aria-autocomplete": "none",
8325
- onClick: handleOnClick,
8326
- ...buttonProps,
8327
- children: [selected ? /*#__PURE__*/jsx(Option$1, {
8328
- ...selected,
8329
- classNames: classNamesProp,
8330
- selected: true
8331
- }) : /*#__PURE__*/jsx("span", {
8332
- className: s('form-control-placeholder'),
8333
- children: placeholder
8334
- }), /*#__PURE__*/jsx(Chevron$1
8335
- // disabled={disabled}
8336
- , {
8337
- className: classNames(s('tw-icon'), s('tw-chevron-up-icon'), s('tw-chevron'), s('bottom'), s('np-select-chevron'))
8338
- })]
8339
- }), isMobile ? isSearchEnabled ? /*#__PURE__*/jsx(Drawer$1, {
8340
- open: open,
8341
- headerTitle: searchPlaceholder || formatMessage(messages$3.searchPlaceholder),
8342
- onClose: handleCloseOptions,
8343
- children: renderOptionsList()
8344
- }) : /*#__PURE__*/jsx(BottomSheet$2, {
8345
- open: open,
8346
- onClose: handleCloseOptions,
8347
- children: renderOptionsList({
8348
- className: isModern ? '' : 'p-a-1'
8349
- })
8350
- }) : /*#__PURE__*/jsx(Panel$1, {
8351
- open: open,
8352
- flip: false,
8353
- altAxis: true,
8354
- anchorRef: selectReference,
8355
- anchorWidth: isDropdownAutoWidth,
8356
- position: dropdownUp ? Position.TOP : Position.BOTTOM,
8357
- onClose: handleCloseOptions,
8358
- children: renderOptionsList({
8359
- className: 'p-a-1'
8360
- })
8361
- })]
8154
+ if (secondContains) {
8155
+ return 1;
8156
+ }
8157
+ return 0;
8362
8158
  });
8363
8159
  }
8364
- Select.propTypes = {
8365
- placeholder: PropTypes.string,
8160
+ MoneyInput.propTypes = {
8366
8161
  id: PropTypes.string,
8367
- required: PropTypes.bool,
8368
- disabled: PropTypes.bool,
8369
- inverse: PropTypes.bool,
8370
- dropdownRight: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
8371
- dropdownWidth: PropTypes.oneOf(['sm', 'md', 'lg']),
8162
+ currencies: PropTypes.arrayOf(Currency).isRequired,
8163
+ selectedCurrency: Currency.isRequired,
8164
+ onCurrencyChange: PropTypes.func,
8165
+ placeholder: PropTypes.number,
8166
+ amount: PropTypes.number,
8372
8167
  size: PropTypes.oneOf(['sm', 'md', 'lg']),
8373
- block: PropTypes.bool,
8374
- selected: PropTypes.shape({
8375
- value: PropTypes.any.isRequired,
8376
- label: PropTypes.node,
8377
- icon: PropTypes.node,
8378
- currency: PropTypes.string,
8379
- note: PropTypes.node,
8380
- secondary: PropTypes.node
8381
- }),
8382
- /**
8383
- * Search toggle
8384
- * if `true` default search functionality being enabled (not case sensitive search in option labels & currency props)
8385
- * if `function` you can define your own search function to implement custom search experience. This search function used while filtering the options array. The custom search function takes two parameters. First is the option the second is the keyword.
8386
- */
8387
- search: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
8388
- onChange: PropTypes.func.isRequired,
8389
- onFocus: PropTypes.func,
8390
- onBlur: PropTypes.func,
8391
- options: PropTypes.arrayOf(PropTypes.shape({
8392
- value: PropTypes.any,
8393
- label: PropTypes.node,
8394
- header: PropTypes.node,
8395
- icon: PropTypes.node,
8396
- currency: PropTypes.string,
8397
- note: PropTypes.node,
8398
- secondary: PropTypes.node,
8399
- separator: PropTypes.bool,
8400
- disabled: PropTypes.bool,
8401
- searchStrings: PropTypes.arrayOf(PropTypes.string)
8402
- })).isRequired,
8168
+ onAmountChange: PropTypes.func,
8169
+ addon: PropTypes.node,
8170
+ searchPlaceholder: PropTypes.string,
8403
8171
  /**
8404
- * To have full control of your search value and response use `onSearchChange` function combined with `searchValue` and custom filtering on the options array.
8405
- * DO NOT USE TOGETHER WITH `search` PROPERTY
8172
+ * Allows the consumer to react to searching, while the search itself is handled internally. Called with `{ searchQuery: string, filteredOptions: Currency[] }`
8406
8173
  */
8407
8174
  onSearchChange: PropTypes.func,
8408
- searchValue: PropTypes.string,
8409
- searchPlaceholder: PropTypes.string,
8175
+ customActionLabel: PropTypes.node,
8176
+ onCustomAction: PropTypes.func,
8410
8177
  classNames: PropTypes.objectOf(PropTypes.string),
8411
- dropdownUp: PropTypes.bool,
8412
- buttonProps: PropTypes.object,
8413
- dropdownProps: PropTypes.object
8178
+ selectProps: PropTypes.object,
8179
+ maxLengthOverride: PropTypes.number
8414
8180
  };
8415
- Select.defaultProps = {
8416
- id: undefined,
8417
- placeholder: undefined,
8418
- size: 'md',
8419
- dropdownRight: null,
8420
- dropdownWidth: null,
8421
- inverse: false,
8422
- required: false,
8423
- disabled: false,
8424
- block: true,
8425
- selected: null,
8426
- onFocus: null,
8427
- onBlur: null,
8181
+ MoneyInput.defaultProps = {
8182
+ id: null,
8183
+ size: Size.LARGE,
8184
+ addon: null,
8185
+ searchPlaceholder: '',
8428
8186
  onSearchChange: undefined,
8429
- search: false,
8430
- searchValue: '',
8431
- searchPlaceholder: undefined,
8187
+ onCurrencyChange: null,
8188
+ placeholder: null,
8189
+ amount: null,
8190
+ onAmountChange: null,
8191
+ customActionLabel: '',
8192
+ onCustomAction: null,
8432
8193
  classNames: {},
8433
- dropdownUp: false,
8434
- buttonProps: {},
8435
- dropdownProps: {}
8194
+ selectProps: {},
8195
+ maxLengthOverride: null
8436
8196
  };
8197
+ var MoneyInput$1 = injectIntl(MoneyInput);
8437
8198
 
8438
- var messages$2 = defineMessages({
8439
- selectPlaceholder: {
8440
- id: "neptune.MoneyInput.Select.placeholder"
8441
- }
8442
- });
8443
-
8444
- // TODO: do not duplicate this between formatting and components
8445
- const currencyDecimals = {
8446
- BIF: 0,
8447
- BYR: 0,
8448
- CLP: 0,
8449
- DJF: 0,
8450
- GNF: 0,
8451
- JPY: 0,
8452
- KMF: 0,
8453
- KRW: 0,
8454
- MGA: 0,
8455
- PYG: 0,
8456
- RWF: 0,
8457
- VND: 0,
8458
- VUV: 0,
8459
- XAF: 0,
8460
- XOF: 0,
8461
- XPF: 0,
8462
- // technically HUF does have decimals, but due to the exchange rate banks
8463
- // do not accept decimal amounts
8464
- HUF: 0,
8465
- BHD: 3,
8466
- JOD: 3,
8467
- KWD: 3,
8468
- OMR: 3,
8469
- TND: 3
8199
+ const NavigationOptionList = ({
8200
+ children
8201
+ }) => {
8202
+ return /*#__PURE__*/jsx("ul", {
8203
+ className: "np-navigation-options-list",
8204
+ children: Children.map(children, child => /*#__PURE__*/jsx("li", {
8205
+ className: "np-navigation-options-list__item",
8206
+ children: child
8207
+ }, child.key))
8208
+ });
8470
8209
  };
8471
- const DEFAULT_CURRENCY_DECIMALS = 2;
8472
- function isNumberLocaleSupported() {
8473
- const number = 1234;
8474
- const numberString = number.toLocaleString && number.toLocaleString(DEFAULT_LOCALE);
8475
- return numberString === '1,234';
8476
- }
8477
- function getValidLocale(locale) {
8478
- try {
8479
- const noUnderscoreLocale = locale.replace(/_/, '-');
8480
- Intl.NumberFormat(noUnderscoreLocale);
8481
- return noUnderscoreLocale;
8482
- } catch {
8483
- return 'en-GB';
8484
- }
8485
- }
8486
- function getCurrencyDecimals(currency = '') {
8487
- const upperCaseCurrency = currency.toUpperCase();
8488
- if (Object.prototype.hasOwnProperty.call(currencyDecimals, upperCaseCurrency)) {
8489
- return currencyDecimals[upperCaseCurrency];
8490
- }
8491
- return DEFAULT_CURRENCY_DECIMALS;
8492
- }
8493
- function getDecimalSeparator(locale) {
8494
- return isNumberLocaleSupported() ? 1.1.toLocaleString(locale)[1] : '.';
8495
- }
8496
- function parseAmount(number, currency, locale) {
8497
- const validLocale = getValidLocale(locale);
8498
- const precision = getCurrencyDecimals(currency);
8499
- const groupSeparator = isNumberLocaleSupported() ? 10000 .toLocaleString(validLocale)[2] : ',';
8500
- const decimalSeparator = getDecimalSeparator(validLocale);
8501
- const numberWithStandardDecimalSeparator = (number ? `${number}` : '').replace(new RegExp(`\\${groupSeparator}`, 'g'), '').replace(new RegExp(`\\${decimalSeparator}`, 'g'), '.').replace(/[^0-9.]/g, '');
8502
- const parsedAmount = parseFloat(parseFloat(numberWithStandardDecimalSeparator).toFixed(precision));
8503
- return Math.abs(parsedAmount);
8504
- }
8505
-
8506
- const Currency = PropTypes.shape({
8507
- header: PropTypes.string,
8508
- value: PropTypes.string,
8509
- label: PropTypes.string,
8510
- currency: PropTypes.string,
8511
- note: PropTypes.string,
8512
- searchable: PropTypes.string
8513
- });
8514
- const CUSTOM_ACTION = 'CUSTOM_ACTION';
8515
- const isNumberOrNull = v => isNumber(v) || isNull(v);
8516
- const formatAmountIfSet = (amount, currency, locale, maxLengthOverride) => {
8517
- if (maxLengthOverride) {
8518
- return amount || '';
8519
- } else {
8520
- return typeof amount === 'number' ? formatAmount(amount, currency, locale) : '';
8521
- }
8210
+ NavigationOptionList.propTypes = {
8211
+ children: PropTypes.node.isRequired
8522
8212
  };
8523
- const parseNumber = (amount, currency, locale, maxLengthOverride) => {
8524
- if (!maxLengthOverride) {
8525
- return parseAmount(amount, currency, locale);
8526
- }
8527
- if (maxLengthOverride && amount.length > maxLengthOverride) {
8528
- return 0;
8529
- }
8530
- return +amount;
8213
+ var NavigationOptionList$1 = NavigationOptionList;
8214
+
8215
+ const STORAGE_NAME = 'dismissedNudges';
8216
+ const getLocalStorage = () => {
8217
+ try {
8218
+ const storageItem = localStorage.getItem(STORAGE_NAME);
8219
+ if (storageItem) {
8220
+ const storage = JSON.parse(storageItem);
8221
+ if (Array.isArray(storage)) {
8222
+ return storage;
8223
+ }
8224
+ }
8225
+ // eslint-disable-next-line unicorn/prefer-optional-catch-binding, no-empty
8226
+ } catch (error) {}
8227
+ return [];
8531
8228
  };
8532
- const inputKeyCodeAllowlist = new Set([KeyCodes.BACKSPACE, KeyCodes.DELETE, KeyCodes.COMMA, KeyCodes.PERIOD, KeyCodes.DOWN, KeyCodes.UP, KeyCodes.LEFT, KeyCodes.RIGHT, KeyCodes.ENTER, KeyCodes.ESCAPE, KeyCodes.TAB]);
8533
- const inputKeyAllowlist = new Set([Key.PERIOD, Key.COMMA]);
8534
- class MoneyInput extends Component {
8535
- constructor(props) {
8536
- super(props);
8537
- const {
8538
- locale
8539
- } = this.props.intl;
8540
- this.formatMessage = this.props.intl.formatMessage;
8541
- this.state = {
8542
- searchQuery: '',
8543
- formattedAmount: formatAmountIfSet(props.amount, props.selectedCurrency.currency, locale, props.maxLengthOverride),
8544
- locale
8545
- };
8546
- }
8547
- UNSAFE_componentWillReceiveProps(nextProps) {
8548
- this.setState({
8549
- locale: nextProps?.intl?.locale
8550
- });
8551
- if (!this.amountFocused) {
8552
- this.setState({
8553
- formattedAmount: formatAmountIfSet(nextProps.amount, nextProps.selectedCurrency.currency, nextProps?.intl?.locale, nextProps.maxLengthOverride)
8554
- });
8555
- }
8556
- }
8557
- isInputAllowedForKeyEvent = event => {
8558
- const {
8559
- keyCode,
8560
- metaKey,
8561
- key,
8562
- ctrlKey
8563
- } = event;
8564
- const isNumberKey = isNumber(parseInt(key, 10));
8565
- return isNumberKey || metaKey || ctrlKey || inputKeyCodeAllowlist.has(keyCode) || inputKeyAllowlist.has(key);
8566
- };
8567
- handleKeyDown = event => {
8568
- if (!this.isInputAllowedForKeyEvent(event)) {
8569
- event.preventDefault();
8570
- }
8571
- };
8572
- handlePaste = event => {
8573
- const paste = (event.clipboardData || window.clipboardData).getData('text');
8574
- const {
8575
- locale
8576
- } = this.state;
8577
- const parsed = isEmpty(paste) ? null : parseNumber(paste, this.props.selectedCurrency.currency, locale, this.props.maxLengthOverride);
8578
- if (isNumberOrNull(parsed)) {
8579
- this.setState({
8580
- formattedAmount: formatAmountIfSet(parsed, this.props.selectedCurrency.currency, locale, this.props.maxLengthOverride)
8581
- });
8582
- this.props.onAmountChange(parsed);
8229
+ const Nudge = ({
8230
+ media,
8231
+ mediaName,
8232
+ title,
8233
+ link,
8234
+ href,
8235
+ onClick,
8236
+ onDismiss,
8237
+ persistDismissal,
8238
+ isPreviouslyDismissed,
8239
+ id,
8240
+ className
8241
+ }) => {
8242
+ const [isDismissed, setIsDismissed] = useState(false);
8243
+ const [isMounted, setIsMounted] = useState(false);
8244
+ const handleOnDismiss = () => {
8245
+ const dismissedNudgesStorage = getLocalStorage();
8246
+ if (persistDismissal && id) {
8247
+ try {
8248
+ localStorage.setItem(STORAGE_NAME, JSON.stringify([...(dismissedNudgesStorage ? dismissedNudgesStorage : []), id]));
8249
+ // eslint-disable-next-line unicorn/prefer-optional-catch-binding, no-empty
8250
+ } catch (error) {}
8251
+ setIsDismissed(true);
8583
8252
  }
8584
- event.preventDefault();
8585
- };
8586
- onAmountChange = event => {
8587
- const {
8588
- value
8589
- } = event.target;
8590
- this.setState({
8591
- formattedAmount: value
8592
- });
8593
- const parsed = isEmpty(value) ? null : parseNumber(value, this.props.selectedCurrency.currency, this.state.locale, this.props.maxLengthOverride);
8594
- if (isNumberOrNull(parsed)) {
8595
- this.props.onAmountChange(parsed);
8253
+ if (onDismiss) {
8254
+ onDismiss();
8596
8255
  }
8597
8256
  };
8598
- onAmountBlur = () => {
8599
- this.amountFocused = false;
8600
- this.setAmount();
8601
- };
8602
- onAmountFocus = () => {
8603
- this.amountFocused = true;
8604
- };
8605
- getSelectOptions() {
8606
- const selectOptions = [...filterOptionsForQuery(this.props.currencies, this.state.searchQuery)];
8607
- if (this.props.onCustomAction) {
8608
- selectOptions.push({
8609
- value: CUSTOM_ACTION,
8610
- label: this.props.customActionLabel
8611
- });
8612
- }
8613
- return selectOptions;
8614
- }
8615
- setAmount() {
8616
- this.setState(previousState => {
8617
- const parsed = parseNumber(previousState.formattedAmount, this.props.selectedCurrency.currency, previousState.locale, this.props.maxLengthOverride);
8618
- if (!isNumberOrNull(parsed)) {
8619
- return {
8620
- formattedAmount: previousState.formattedAmount
8621
- };
8257
+ useEffect(() => {
8258
+ if (persistDismissal && id) {
8259
+ const dismissedNudgesStorage = getLocalStorage();
8260
+ let isDismissed = false;
8261
+ if (dismissedNudgesStorage && dismissedNudgesStorage.find(item => item === id)) {
8262
+ setIsDismissed(true);
8263
+ isDismissed = true;
8264
+ }
8265
+ if (isPreviouslyDismissed) {
8266
+ isPreviouslyDismissed(isDismissed);
8622
8267
  }
8623
- return {
8624
- formattedAmount: formatAmountIfSet(parsed, this.props.selectedCurrency.currency, previousState.locale, this.props.maxLengthOverride)
8625
- };
8626
- });
8627
- }
8628
- handleSelectChange = value => {
8629
- this.handleSearchChange('');
8630
- if (this.props.onCustomAction && value.value === CUSTOM_ACTION) {
8631
- this.props.onCustomAction();
8632
- } else {
8633
- this.props.onCurrencyChange(value);
8634
- }
8635
- };
8636
- handleSearchChange = searchQuery => {
8637
- this.setState({
8638
- searchQuery
8639
- });
8640
- if (this.props.onSearchChange) {
8641
- this.props.onSearchChange({
8642
- searchQuery,
8643
- filteredOptions: filterOptionsForQuery(this.props.currencies, searchQuery)
8644
- });
8645
8268
  }
8646
- };
8647
- style = className => this.props.classNames[className] || className;
8648
- render() {
8649
- const {
8650
- selectedCurrency,
8651
- onCurrencyChange,
8652
- size,
8653
- addon,
8654
- id,
8655
- selectProps,
8656
- maxLengthOverride
8657
- } = this.props;
8658
- const selectOptions = this.getSelectOptions();
8659
- const isFixedCurrency = !this.state.searchQuery && (selectOptions.length === 1 && selectOptions[0].currency === selectedCurrency.currency || !onCurrencyChange);
8660
- const disabled = !this.props.onAmountChange;
8661
- return /*#__PURE__*/jsxs("div", {
8662
- className: classNames(this.style('tw-money-input'), this.style('input-group'), this.style(`input-group-${size}`)),
8663
- children: [/*#__PURE__*/jsx(Input, {
8664
- id: id,
8665
- value: this.state.formattedAmount,
8666
- inputMode: "decimal",
8667
- disabled: disabled,
8668
- maxLength: maxLengthOverride,
8669
- placeholder: formatAmountIfSet(this.props.placeholder, this.props.selectedCurrency.currency, this.state.locale, this.props.maxLengthOverride),
8670
- autoComplete: "off",
8671
- onKeyDown: this.handleKeyDown,
8672
- onChange: this.onAmountChange,
8673
- onFocus: this.onAmountFocus,
8674
- onBlur: this.onAmountBlur,
8675
- onPaste: this.handlePaste
8676
- }), addon && /*#__PURE__*/jsx("span", {
8677
- className: classNames(this.style('input-group-addon'), this.style(`input-${size}`), disabled ? this.style('disabled') : ''),
8678
- children: addon
8679
- }), isFixedCurrency ? /*#__PURE__*/jsxs("div", {
8680
- className: classNames(this.style('input-group-addon'), this.style(`input-${size}`), this.style('tw-money-input__fixed-currency'), disabled ? this.style('disabled') : ''),
8681
- children: [(size === 'lg' || size === 'md') && /*#__PURE__*/jsxs(Fragment, {
8682
- children: [/*#__PURE__*/jsx("i", {
8683
- className: classNames(this.style('tw-money-input__keyline'))
8684
- }), /*#__PURE__*/jsx("i", {
8685
- className: classNames(this.style('currency-flag'), this.style(`currency-flag-${selectedCurrency.currency.toLowerCase()}`), this.style('m-r-2'))
8686
- })]
8687
- }), /*#__PURE__*/jsx(Title, {
8688
- as: "span",
8689
- type: Typography.TITLE_SUBSECTION,
8690
- className: size === 'lg' ? this.style('m-r-1') : '',
8691
- children: selectedCurrency.currency.toUpperCase()
8692
- })]
8693
- }) : /*#__PURE__*/jsx("div", {
8694
- className: classNames(this.style('input-group-btn'), this.style('amount-currency-select-btn')),
8695
- children: /*#__PURE__*/jsx(Select, {
8696
- id: id ? `${id}-select` : undefined,
8697
- classNames: this.props.classNames,
8698
- options: selectOptions,
8699
- selected: {
8700
- ...selectedCurrency,
8701
- label: /*#__PURE__*/jsx(Title, {
8702
- as: "span",
8703
- type: Typography.TITLE_SUBSECTION,
8704
- className: "tw-money-input__text",
8705
- children: selectedCurrency.label
8706
- }),
8707
- note: null
8708
- },
8709
- placeholder: this.formatMessage(messages$2.selectPlaceholder),
8710
- searchPlaceholder: this.props.searchPlaceholder,
8711
- searchValue: this.state.searchQuery,
8712
- size: size,
8713
- required: true,
8714
- dropdownWidth: Size.LARGE,
8715
- inverse: true,
8716
- onChange: this.handleSelectChange,
8717
- onSearchChange: this.handleSearchChange,
8718
- ...selectProps
8719
- })
8720
- })]
8721
- });
8722
- }
8723
- }
8724
- function filterOptionsForQuery(options, query) {
8725
- if (!query) {
8726
- return options;
8269
+ setIsMounted(true);
8270
+ // eslint-disable-next-line react-hooks/exhaustive-deps
8271
+ }, [id, persistDismissal]);
8272
+ if (persistDismissal && (isDismissed || !isMounted)) {
8273
+ return null;
8727
8274
  }
8728
- const filteredOptions = removeDuplicateValueOptions(options).filter(option => isCurrencyOptionAndFitsQuery(option, query));
8729
- return sortOptionsLabelsToFirst(filteredOptions, query);
8730
- }
8731
- function removeDuplicateValueOptions(options) {
8732
- const result = [];
8733
- const resultValues = [];
8734
- options.forEach(option => {
8735
- if (option.value && !resultValues.includes(option.value)) {
8736
- result.push(option);
8737
- resultValues.push(option.value);
8738
- }
8739
- });
8740
- return result;
8741
- }
8742
- function isCurrencyOptionAndFitsQuery(option, query) {
8743
- if (!option.value) {
8744
- return false;
8745
- }
8746
- return contains(option.label, query) || contains(option.searchable, query) || contains(option.note, query);
8747
- }
8748
- function contains(property, query) {
8749
- return property && property.toLowerCase().includes(query.toLowerCase());
8750
- }
8751
- function sortOptionsLabelsToFirst(options, query) {
8752
- return options.sort((first, second) => {
8753
- const firstContains = contains(first.label, query);
8754
- const secondContains = contains(second.label, query);
8755
- if (firstContains && secondContains) {
8756
- return 0;
8757
- }
8758
- if (firstContains) {
8759
- return -1;
8760
- }
8761
- if (secondContains) {
8762
- return 1;
8763
- }
8764
- return 0;
8765
- });
8766
- }
8767
- MoneyInput.propTypes = {
8768
- id: PropTypes.string,
8769
- currencies: PropTypes.arrayOf(Currency).isRequired,
8770
- selectedCurrency: Currency.isRequired,
8771
- onCurrencyChange: PropTypes.func,
8772
- placeholder: PropTypes.number,
8773
- amount: PropTypes.number,
8774
- size: PropTypes.oneOf(['sm', 'md', 'lg']),
8775
- onAmountChange: PropTypes.func,
8776
- addon: PropTypes.node,
8777
- searchPlaceholder: PropTypes.string,
8778
- /**
8779
- * Allows the consumer to react to searching, while the search itself is handled internally. Called with `{ searchQuery: string, filteredOptions: Currency[] }`
8780
- */
8781
- onSearchChange: PropTypes.func,
8782
- customActionLabel: PropTypes.node,
8783
- onCustomAction: PropTypes.func,
8784
- classNames: PropTypes.objectOf(PropTypes.string),
8785
- selectProps: PropTypes.object,
8786
- maxLengthOverride: PropTypes.number
8787
- };
8788
- MoneyInput.defaultProps = {
8789
- id: null,
8790
- size: Size.LARGE,
8791
- addon: null,
8792
- searchPlaceholder: '',
8793
- onSearchChange: undefined,
8794
- onCurrencyChange: null,
8795
- placeholder: null,
8796
- amount: null,
8797
- onAmountChange: null,
8798
- customActionLabel: '',
8799
- onCustomAction: null,
8800
- classNames: {},
8801
- selectProps: {},
8802
- maxLengthOverride: null
8803
- };
8804
- var MoneyInput$1 = injectIntl(MoneyInput);
8805
-
8806
- const NavigationOptionList = ({
8807
- children
8808
- }) => {
8809
- return /*#__PURE__*/jsx("ul", {
8810
- className: "np-navigation-options-list",
8811
- children: Children.map(children, child => /*#__PURE__*/jsx("li", {
8812
- className: "np-navigation-options-list__item",
8813
- children: child
8814
- }, child.key))
8815
- });
8816
- };
8817
- NavigationOptionList.propTypes = {
8818
- children: PropTypes.node.isRequired
8819
- };
8820
- var NavigationOptionList$1 = NavigationOptionList;
8821
-
8822
- const STORAGE_NAME = 'dismissedNudges';
8823
- const getLocalStorage = () => {
8824
- try {
8825
- const storageItem = localStorage.getItem(STORAGE_NAME);
8826
- if (storageItem) {
8827
- const storage = JSON.parse(storageItem);
8828
- if (Array.isArray(storage)) {
8829
- return storage;
8830
- }
8831
- }
8832
- // eslint-disable-next-line unicorn/prefer-optional-catch-binding, no-empty
8833
- } catch (error) {}
8834
- return [];
8835
- };
8836
- const Nudge = ({
8837
- media,
8838
- mediaName,
8839
- title,
8840
- link,
8841
- href,
8842
- onClick,
8843
- onDismiss,
8844
- persistDismissal,
8845
- isPreviouslyDismissed,
8846
- id,
8847
- className
8848
- }) => {
8849
- const [isDismissed, setIsDismissed] = useState(false);
8850
- const [isMounted, setIsMounted] = useState(false);
8851
- const handleOnDismiss = () => {
8852
- const dismissedNudgesStorage = getLocalStorage();
8853
- if (persistDismissal && id) {
8854
- try {
8855
- localStorage.setItem(STORAGE_NAME, JSON.stringify([...(dismissedNudgesStorage ? dismissedNudgesStorage : []), id]));
8856
- // eslint-disable-next-line unicorn/prefer-optional-catch-binding, no-empty
8857
- } catch (error) {}
8858
- setIsDismissed(true);
8859
- }
8860
- if (onDismiss) {
8861
- onDismiss();
8862
- }
8863
- };
8864
- useEffect(() => {
8865
- if (persistDismissal && id) {
8866
- const dismissedNudgesStorage = getLocalStorage();
8867
- let isDismissed = false;
8868
- if (dismissedNudgesStorage && dismissedNudgesStorage.find(item => item === id)) {
8869
- setIsDismissed(true);
8870
- isDismissed = true;
8871
- }
8872
- if (isPreviouslyDismissed) {
8873
- isPreviouslyDismissed(isDismissed);
8874
- }
8875
- }
8876
- setIsMounted(true);
8877
- // eslint-disable-next-line react-hooks/exhaustive-deps
8878
- }, [id, persistDismissal]);
8879
- if (persistDismissal && (isDismissed || !isMounted)) {
8880
- return null;
8881
- }
8882
- return /*#__PURE__*/jsxs("div", {
8883
- className: classNames('wds-nudge', className),
8884
- id: id,
8885
- children: [!!mediaName && /*#__PURE__*/jsx("div", {
8886
- className: "wds-nudge-media",
8887
- children: /*#__PURE__*/jsx(Illustration, {
8888
- name: mediaName,
8889
- className: classNames(`wds-nudge-media-${mediaName}`),
8890
- size: "small",
8891
- disablePadding: true,
8892
- alt: ""
8893
- })
8894
- }), /*#__PURE__*/jsxs("div", {
8895
- className: "wds-nudge-container",
8896
- children: [/*#__PURE__*/jsxs("div", {
8897
- className: "wds-nudge-content",
8898
- children: [/*#__PURE__*/jsx(Body, {
8899
- type: Typography.BODY_LARGE,
8900
- className: classNames('wds-nudge-body'),
8901
- children: title
8902
- }), link && /*#__PURE__*/jsx(Link, {
8903
- href: href,
8904
- type: Typography.LINK_LARGE,
8905
- className: "wds-nudge-link",
8906
- onClick: onClick,
8907
- children: link
8908
- })]
8909
- }), onDismiss || persistDismissal ? /*#__PURE__*/jsx(CloseButton, {
8910
- className: "wds-nudge-control",
8911
- size: "sm",
8912
- onClick: handleOnDismiss
8913
- }) : null]
8914
- })]
8275
+ return /*#__PURE__*/jsxs("div", {
8276
+ className: classNames('wds-nudge', className),
8277
+ id: id,
8278
+ children: [!!mediaName && /*#__PURE__*/jsx("div", {
8279
+ className: "wds-nudge-media",
8280
+ children: /*#__PURE__*/jsx(Illustration, {
8281
+ name: mediaName,
8282
+ className: classNames(`wds-nudge-media-${mediaName}`),
8283
+ size: "small",
8284
+ disablePadding: true,
8285
+ alt: ""
8286
+ })
8287
+ }), /*#__PURE__*/jsxs("div", {
8288
+ className: "wds-nudge-container",
8289
+ children: [/*#__PURE__*/jsxs("div", {
8290
+ className: "wds-nudge-content",
8291
+ children: [/*#__PURE__*/jsx(Body, {
8292
+ type: Typography.BODY_LARGE,
8293
+ className: classNames('wds-nudge-body'),
8294
+ children: title
8295
+ }), link && /*#__PURE__*/jsx(Link, {
8296
+ href: href,
8297
+ type: Typography.LINK_LARGE,
8298
+ className: "wds-nudge-link",
8299
+ onClick: onClick,
8300
+ children: link
8301
+ })]
8302
+ }), onDismiss || persistDismissal ? /*#__PURE__*/jsx(CloseButton, {
8303
+ className: "wds-nudge-control",
8304
+ size: "sm",
8305
+ onClick: handleOnDismiss
8306
+ }) : null]
8307
+ })]
8915
8308
  });
8916
8309
  };
8917
8310
 
@@ -11024,179 +10417,836 @@ const Radio = ({
11024
10417
  })]
11025
10418
  })
11026
10419
  });
11027
- };
11028
- Radio.propTypes = {
11029
- avatar: PropTypes.element,
11030
- checked: PropTypes.bool,
11031
- disabled: PropTypes.bool,
10420
+ };
10421
+ Radio.propTypes = {
10422
+ avatar: PropTypes.element,
10423
+ checked: PropTypes.bool,
10424
+ disabled: PropTypes.bool,
10425
+ id: PropTypes.string,
10426
+ label: PropTypes.string.isRequired,
10427
+ name: PropTypes.string.isRequired,
10428
+ onChange: PropTypes.func.isRequired,
10429
+ secondary: PropTypes.string,
10430
+ value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
10431
+ className: PropTypes.string
10432
+ };
10433
+ Radio.defaultProps = {
10434
+ avatar: undefined,
10435
+ checked: false,
10436
+ disabled: false,
10437
+ id: null,
10438
+ secondary: null,
10439
+ value: '',
10440
+ className: undefined
10441
+ };
10442
+ var Radio$1 = Radio;
10443
+
10444
+ class RadioGroup extends Component {
10445
+ constructor(props) {
10446
+ super(props);
10447
+ this.state = {
10448
+ selectedValue: props.selectedValue
10449
+ };
10450
+ }
10451
+ handleOnChange = selectedValue => {
10452
+ const {
10453
+ onChange
10454
+ } = this.props;
10455
+ this.setState({
10456
+ selectedValue
10457
+ }, onChange && onChange(selectedValue));
10458
+ };
10459
+ render() {
10460
+ const {
10461
+ radios,
10462
+ name
10463
+ } = this.props;
10464
+ const {
10465
+ selectedValue
10466
+ } = this.state;
10467
+ return radios && radios.length > 0 ? /*#__PURE__*/jsx(Fragment, {
10468
+ children: radios.map(({
10469
+ id,
10470
+ avatar,
10471
+ value,
10472
+ label,
10473
+ disabled,
10474
+ secondary,
10475
+ readOnly
10476
+ }, index) => /*#__PURE__*/jsx(Radio$1
10477
+ // eslint-disable-next-line react/no-array-index-key
10478
+ , {
10479
+ id: id,
10480
+ value: value,
10481
+ label: label,
10482
+ name: name,
10483
+ disabled: disabled,
10484
+ checked: selectedValue === value,
10485
+ secondary: secondary,
10486
+ readOnly: readOnly,
10487
+ avatar: avatar,
10488
+ onChange: value_ => this.handleOnChange(value_)
10489
+ }, index))
10490
+ }) : null;
10491
+ }
10492
+ }
10493
+ RadioGroup.propTypes = {
10494
+ radios: PropTypes.arrayOf(PropTypes.shape({
10495
+ id: PropTypes.string,
10496
+ avatar: PropTypes.element,
10497
+ value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
10498
+ secondary: PropTypes.string,
10499
+ label: PropTypes.string.isRequired,
10500
+ disabled: PropTypes.bool,
10501
+ readOnly: PropTypes.bool
10502
+ })).isRequired,
10503
+ onChange: PropTypes.func.isRequired,
10504
+ selectedValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
10505
+ name: PropTypes.string.isRequired
10506
+ };
10507
+ RadioGroup.defaultProps = {
10508
+ selectedValue: null
10509
+ };
10510
+ var RadioGroup$1 = RadioGroup;
10511
+
10512
+ const RadioOption = ({
10513
+ 'aria-label': ariaLabel,
10514
+ media,
10515
+ title,
10516
+ content,
10517
+ id,
10518
+ name,
10519
+ checked,
10520
+ onChange,
10521
+ complex,
10522
+ disabled,
10523
+ value,
10524
+ showMediaCircle,
10525
+ showMediaAtAllSizes,
10526
+ isContainerAligned
10527
+ }) => {
10528
+ const sharedProps = {
10529
+ 'aria-label': ariaLabel,
10530
+ media,
10531
+ title,
10532
+ content,
10533
+ name,
10534
+ complex,
10535
+ disabled,
10536
+ showMediaCircle,
10537
+ showMediaAtAllSizes,
10538
+ isContainerAligned
10539
+ };
10540
+ return /*#__PURE__*/jsx(Option$2, {
10541
+ ...sharedProps,
10542
+ button: /*#__PURE__*/jsx(RadioButton$1, {
10543
+ id: id,
10544
+ name: name,
10545
+ checked: checked,
10546
+ disabled: disabled,
10547
+ value: value,
10548
+ onChange: onChange
10549
+ })
10550
+ });
10551
+ };
10552
+ RadioOption.propTypes = {
10553
+ 'aria-label': PropTypes.string,
10554
+ media: PropTypes.node,
10555
+ id: PropTypes.string.isRequired,
10556
+ name: PropTypes.string.isRequired,
10557
+ title: PropTypes.node.isRequired,
10558
+ content: PropTypes.node,
10559
+ checked: PropTypes.bool,
10560
+ onChange: PropTypes.func.isRequired,
10561
+ complex: PropTypes.bool,
10562
+ disabled: PropTypes.bool,
10563
+ value: PropTypes.string,
10564
+ showMediaCircle: PropTypes.bool,
10565
+ showMediaAtAllSizes: PropTypes.bool,
10566
+ isContainerAligned: PropTypes.bool
10567
+ };
10568
+ RadioOption.defaultProps = {
10569
+ 'aria-label': undefined,
10570
+ media: null,
10571
+ content: null,
10572
+ checked: false,
10573
+ complex: false,
10574
+ disabled: false,
10575
+ showMediaCircle: true,
10576
+ showMediaAtAllSizes: false,
10577
+ isContainerAligned: false,
10578
+ value: ''
10579
+ };
10580
+ var RadioOption$1 = RadioOption;
10581
+
10582
+ const Section = ({
10583
+ children,
10584
+ className,
10585
+ withHorizontalPadding = false
10586
+ }) => {
10587
+ return /*#__PURE__*/jsx("div", {
10588
+ className: classNames('np-section', className, {
10589
+ 'np-section--with-horizontal-padding': withHorizontalPadding
10590
+ }),
10591
+ children: children
10592
+ });
10593
+ };
10594
+
10595
+ var messages$2 = defineMessages({
10596
+ searchPlaceholder: {
10597
+ id: "neptune.Select.searchPlaceholder"
10598
+ }
10599
+ });
10600
+
10601
+ // Option.tsx NEW
10602
+ function Option$1({
10603
+ label,
10604
+ currency = '',
10605
+ note = '',
10606
+ secondary = '',
10607
+ icon,
10608
+ classNames = {},
10609
+ selected = false
10610
+ }) {
10611
+ const {
10612
+ isModern
10613
+ } = useTheme();
10614
+ const style = classes => classes.map(className => classNames[className] || className).join(' ');
10615
+ const currencyClassNames = currency ? style(['currency-flag', `currency-flag-${currency.toLowerCase()}`]) : undefined;
10616
+ const iconElement = icon ? /*#__PURE__*/cloneElement(icon, {
10617
+ size: 24,
10618
+ className: 'tw-icon'
10619
+ }) : currency && /*#__PURE__*/jsx("i", {
10620
+ className: currencyClassNames
10621
+ });
10622
+ const titleAndNoteElement = /*#__PURE__*/jsxs(Body, {
10623
+ as: "span",
10624
+ type: Typography.BODY_LARGE,
10625
+ className: selected ? 'text-ellipsis' : undefined,
10626
+ children: [label, note && /*#__PURE__*/jsx(Body, {
10627
+ as: "span",
10628
+ className: isModern ? 'm-l-1' : 'm-l-1 body-2',
10629
+ children: note
10630
+ })]
10631
+ });
10632
+ const secondaryElementClassNames = () => {
10633
+ let classes = undefined;
10634
+ if (selected) {
10635
+ classes = 'text-ellipsis';
10636
+ }
10637
+ if (isModern) {
10638
+ return classes;
10639
+ }
10640
+ return `${classes ? classes + ' ' : ''}body-2`;
10641
+ };
10642
+ const secondaryElement = secondary && /*#__PURE__*/jsx(Body, {
10643
+ className: secondaryElementClassNames(),
10644
+ children: secondary
10645
+ });
10646
+ return iconElement ? /*#__PURE__*/jsxs("div", {
10647
+ className: "d-flex np-option-content",
10648
+ children: [/*#__PURE__*/jsx("div", {
10649
+ className: `d-flex flex-column${selected ? ' justify-content-center' : ''}`,
10650
+ children: iconElement
10651
+ }), /*#__PURE__*/jsxs("div", {
10652
+ className: "d-flex flex-column justify-content-center",
10653
+ children: [titleAndNoteElement, secondaryElement]
10654
+ })]
10655
+ }) : /*#__PURE__*/jsxs(Fragment, {
10656
+ children: [iconElement, titleAndNoteElement, secondaryElement]
10657
+ });
10658
+ }
10659
+
10660
+ const SearchBox = /*#__PURE__*/forwardRef(({
10661
+ id,
10662
+ classNames: classNames$1 = {},
10663
+ focusedOptionId,
10664
+ onChange,
10665
+ onClick,
10666
+ placeholder = undefined,
10667
+ value = ''
10668
+ }, reference) => {
10669
+ const style = className => classNames$1[className] || className;
10670
+ return /*#__PURE__*/jsx("li", {
10671
+ className: style('border-bottom'),
10672
+ children: /*#__PURE__*/jsx("a", {
10673
+ className: `${style('np-select-filter-link')} ${style('p-a-0')}`,
10674
+ children: /*#__PURE__*/jsxs("div", {
10675
+ className: style('input-group'),
10676
+ children: [/*#__PURE__*/jsx("span", {
10677
+ className: classNames('input-group-addon', 'input-group-addon--search'),
10678
+ children: /*#__PURE__*/jsx(Search, {
10679
+ className: classNames(style('tw-icon'), style('tw-icon-search')),
10680
+ size: 24
10681
+ })
10682
+ }), /*#__PURE__*/jsx(Input, {
10683
+ ref: reference,
10684
+ id: id,
10685
+ role: "searchbox",
10686
+ inputMode: "search",
10687
+ className: classNames(style('np-select-filter')),
10688
+ placeholder: placeholder,
10689
+ value: value,
10690
+ spellCheck: "false",
10691
+ "aria-activedescendant": focusedOptionId,
10692
+ onChange: onChange,
10693
+ onClick: onClick
10694
+ })]
10695
+ })
10696
+ })
10697
+ });
10698
+ });
10699
+ var SearchBox$1 = SearchBox;
10700
+
10701
+ const DEFAULT_SEARCH_VALUE = '';
10702
+ const DEFAULT_OPTIONS_PAGE_SIZE = 1000;
10703
+ const includesString = (string1, string2) => string1.toLowerCase().includes(string2.toLowerCase());
10704
+ function defaultFilterFunction(option, searchValue) {
10705
+ if (isPlaceholderOption(option)) {
10706
+ return true;
10707
+ }
10708
+ const {
10709
+ label,
10710
+ note,
10711
+ secondary,
10712
+ currency,
10713
+ searchStrings
10714
+ } = option;
10715
+ return !!label && includesString(label, searchValue) || !!note && includesString(note, searchValue) || !!secondary && includesString(secondary, searchValue) || !!currency && includesString(currency, searchValue) || !!searchStrings && searchStrings.some(string => includesString(string, searchValue));
10716
+ }
10717
+ function isActionableOption(option) {
10718
+ return !option.header && !option.separator && !option.disabled;
10719
+ }
10720
+ function isHeaderOption(option) {
10721
+ return option != null && 'header' in option;
10722
+ }
10723
+ function isSeparatorOption(option) {
10724
+ return option != null && 'separator' in option;
10725
+ }
10726
+ function clamp(from, to, value) {
10727
+ return Math.max(Math.min(to, value), from);
10728
+ }
10729
+
10730
+ /**
10731
+ * No option or placeholder option is selected
10732
+ */
10733
+ const DEFAULT_SELECTED_OPTION = null;
10734
+ function isPlaceholderOption(option) {
10735
+ return option === DEFAULT_SELECTED_OPTION || 'placeholder' in option;
10736
+ }
10737
+ function isSearchableOption(option) {
10738
+ return !isHeaderOption(option) && !isSeparatorOption(option) && !isPlaceholderOption(option);
10739
+ }
10740
+ const getUniqueIdForOption = (parentId = '', option) => {
10741
+ if (option == null) {
10742
+ return undefined;
10743
+ }
10744
+ const uniqueOptionId = option.value || (option.label?.replace(/\s/g, '') ?? '');
10745
+ return `option-${parentId}-${uniqueOptionId}`;
10746
+ };
10747
+
10748
+ /**
10749
+ * @deprecated Use `SelectInput` instead (https://neptune.wise.design/blog/2023-11-28-adopting-our-new-selectinput)
10750
+ */
10751
+ function Select({
10752
+ placeholder,
10753
+ id,
10754
+ required,
10755
+ disabled,
10756
+ inverse,
10757
+ dropdownWidth,
10758
+ size,
10759
+ block,
10760
+ selected,
10761
+ search,
10762
+ onChange,
10763
+ onFocus,
10764
+ onBlur,
10765
+ options: defaultOptions,
10766
+ onSearchChange,
10767
+ searchValue: initSearchValue,
10768
+ searchPlaceholder,
10769
+ // eslint-disable-next-line unicorn/prevent-abbreviations
10770
+ classNames: classNamesProp,
10771
+ dropdownUp,
10772
+ dropdownProps,
10773
+ buttonProps
10774
+ }) {
10775
+ const {
10776
+ formatMessage
10777
+ } = useIntl();
10778
+ const {
10779
+ isModern
10780
+ } = useTheme();
10781
+ const s = className => classNamesProp[className] || className;
10782
+ const [open, setOpen] = useState(false);
10783
+ const [searchValue, setSearchValue] = useState(DEFAULT_SEARCH_VALUE);
10784
+ const [keyboardFocusedOptionIndex, setKeyboardFocusedOptionIndex] = useState(null);
10785
+ const keyboardFocusedReference = useRef();
10786
+ const previousKeyboardFocusedOptionIndex = useRef();
10787
+ const [numberOfOptionsShown, setNumberOfOptionsShown] = useState(DEFAULT_OPTIONS_PAGE_SIZE);
10788
+ const searchBoxReference = useRef(null);
10789
+ const selectReference = useRef(null);
10790
+ const dropdownButtonReference = useRef(null);
10791
+ const optionsListReference = useRef(null);
10792
+ const isSearchEnabled = !!onSearchChange || !!search;
10793
+ const isDropdownAutoWidth = dropdownWidth == null;
10794
+ const fallbackButtonId = useMemo(() => getSimpleRandomId('np-select-'), []);
10795
+ const options = useMemo(() => {
10796
+ if (!search || !searchValue) {
10797
+ return defaultOptions;
10798
+ }
10799
+ return defaultOptions.filter(isSearchableOption).filter(option => {
10800
+ if (typeof search === 'function') {
10801
+ return search(option, searchValue);
10802
+ } else {
10803
+ return defaultFilterFunction(option, searchValue);
10804
+ }
10805
+ });
10806
+ }, [defaultOptions, search, searchValue]);
10807
+ const selectableOptions = useMemo(() => options.filter(isActionableOption), [options]);
10808
+ const focusedOption = selectableOptions[keyboardFocusedOptionIndex];
10809
+ const computedId = id || fallbackButtonId;
10810
+ const listboxId = `${computedId}-listbox`;
10811
+ const searchBoxId = `${computedId}-searchbox`;
10812
+ const {
10813
+ isMobile
10814
+ } = useLayout();
10815
+ useEffect(() => {
10816
+ let cancelled;
10817
+ if (keyboardFocusedOptionIndex >= 0) {
10818
+ requestAnimationFrame(() => {
10819
+ if (!cancelled) {
10820
+ if (isSearchEnabled) {
10821
+ keyboardFocusedReference.current?.scrollIntoView?.({
10822
+ block: 'center'
10823
+ });
10824
+ } else {
10825
+ keyboardFocusedReference.current?.focus();
10826
+ }
10827
+ }
10828
+ });
10829
+ return () => {
10830
+ cancelled = true;
10831
+ };
10832
+ }
10833
+ }, [keyboardFocusedOptionIndex, isSearchEnabled]);
10834
+ const handleOnClick = () => {
10835
+ setOpen(true);
10836
+ };
10837
+ const handleTouchStart = event => {
10838
+ if (event.currentTarget === event.target && open) {
10839
+ handleCloseOptions();
10840
+ }
10841
+ };
10842
+ const handleOnFocus = event => {
10843
+ if (onFocus) {
10844
+ onFocus(event);
10845
+ }
10846
+ };
10847
+ const handleOnBlur = event => {
10848
+ const {
10849
+ nativeEvent
10850
+ } = event;
10851
+ if (nativeEvent) {
10852
+ const elementReceivingFocus = nativeEvent.relatedTarget;
10853
+ const select = event.currentTarget;
10854
+ if (select && elementReceivingFocus && select.contains(elementReceivingFocus)) {
10855
+ return;
10856
+ }
10857
+ }
10858
+ if (onBlur) {
10859
+ onBlur(event);
10860
+ }
10861
+ };
10862
+ const handleSearchChange = event => {
10863
+ setNumberOfOptionsShown(DEFAULT_OPTIONS_PAGE_SIZE);
10864
+ setSearchValue(event.target.value);
10865
+ if (onSearchChange) {
10866
+ onSearchChange(event.target.value);
10867
+ }
10868
+ };
10869
+ const handleKeyDown = event => {
10870
+ switch (event.keyCode) {
10871
+ case KeyCodes.UP:
10872
+ case KeyCodes.DOWN:
10873
+ if (open) {
10874
+ moveFocusWithDifference(event.keyCode === KeyCodes.UP ? -1 : 1);
10875
+ } else {
10876
+ setOpen(true);
10877
+ }
10878
+ stopPropagation$1(event);
10879
+ break;
10880
+ case KeyCodes.SPACE:
10881
+ if (event.target !== searchBoxReference.current) {
10882
+ if (open) {
10883
+ selectKeyboardFocusedOption();
10884
+ } else {
10885
+ setOpen(true);
10886
+ }
10887
+ stopPropagation$1(event);
10888
+ }
10889
+ break;
10890
+ case KeyCodes.ENTER:
10891
+ if (open) {
10892
+ selectKeyboardFocusedOption();
10893
+ } else {
10894
+ setOpen(true);
10895
+ }
10896
+ stopPropagation$1(event);
10897
+ break;
10898
+ case KeyCodes.ESCAPE:
10899
+ handleCloseOptions();
10900
+ stopPropagation$1(event);
10901
+ break;
10902
+ case KeyCodes.TAB:
10903
+ if (open) {
10904
+ selectKeyboardFocusedOption();
10905
+ }
10906
+ break;
10907
+ }
10908
+ };
10909
+ function selectKeyboardFocusedOption() {
10910
+ if (keyboardFocusedOptionIndex != null) {
10911
+ selectableOptions.length > 0 && selectOption(selectableOptions[keyboardFocusedOptionIndex]);
10912
+ }
10913
+ }
10914
+ function moveFocusWithDifference(difference) {
10915
+ const selectedOptionIndex = selectableOptions.reduce((optionIndex, current, index) => {
10916
+ if (optionIndex != null) {
10917
+ return optionIndex;
10918
+ }
10919
+ if (isOptionSelected(selected, current)) {
10920
+ return index;
10921
+ }
10922
+ return null;
10923
+ }, null);
10924
+ const previousFocusedIndex = previousKeyboardFocusedOptionIndex.current ?? -1;
10925
+ let indexToStartMovingFrom = previousFocusedIndex;
10926
+ if (previousFocusedIndex === -1) {
10927
+ if (selectedOptionIndex == null) {
10928
+ setKeyboardFocusedOptionIndex(0);
10929
+ } else {
10930
+ indexToStartMovingFrom = selectedOptionIndex;
10931
+ }
10932
+ }
10933
+ const unClampedNewIndex = indexToStartMovingFrom + difference;
10934
+ const newIndex = clamp(0, selectableOptions.length - 1, unClampedNewIndex);
10935
+ setKeyboardFocusedOptionIndex(newIndex);
10936
+ }
10937
+ useEffect(() => {
10938
+ if (open) {
10939
+ if (!isMobile || searchValue) {
10940
+ if (isSearchEnabled && !!searchBoxReference.current) {
10941
+ searchBoxReference.current.focus();
10942
+ }
10943
+ if (!isSearchEnabled && optionsListReference.current && (previousKeyboardFocusedOptionIndex.current == null || Number.isNaN(previousKeyboardFocusedOptionIndex.current))) {
10944
+ optionsListReference.current.focus();
10945
+ }
10946
+ }
10947
+ previousKeyboardFocusedOptionIndex.current = keyboardFocusedOptionIndex;
10948
+ } else {
10949
+ previousKeyboardFocusedOptionIndex.current = null;
10950
+ }
10951
+ }, [open, searchValue, isSearchEnabled, isMobile, keyboardFocusedOptionIndex]);
10952
+ const handleCloseOptions = () => {
10953
+ setOpen(false);
10954
+ setKeyboardFocusedOptionIndex(null);
10955
+ if (dropdownButtonReference.current) {
10956
+ dropdownButtonReference.current.focus();
10957
+ }
10958
+ };
10959
+ function createSelectHandlerForOption(option) {
10960
+ return event => {
10961
+ stopPropagation$1(event);
10962
+ selectOption(option);
10963
+ };
10964
+ }
10965
+ function selectOption(option) {
10966
+ onChange(isPlaceholderOption(option) ? DEFAULT_SELECTED_OPTION : option);
10967
+ handleCloseOptions();
10968
+ }
10969
+ function renderOptionsList({
10970
+ className
10971
+ } = {}) {
10972
+ const dropdownClass = classNames(s('np-dropdown-menu'), {
10973
+ [s('np-dropdown-menu-desktop')]: !isMobile,
10974
+ [s(`np-dropdown-menu-${dropdownWidth}`)]: !isMobile && !isDropdownAutoWidth
10975
+ }, s(className));
10976
+ const showPlaceholder = !required && !isSearchEnabled && Boolean(placeholder);
10977
+ return /*#__PURE__*/jsxs("ul", {
10978
+ ref: optionsListReference,
10979
+ id: listboxId,
10980
+ role: "listbox",
10981
+ "aria-orientation": "vertical",
10982
+ "aria-activedescendant": getUniqueIdForOption(id, selected),
10983
+ tabIndex: "-1",
10984
+ className: dropdownClass,
10985
+ ...dropdownProps,
10986
+ children: [showPlaceholder && /*#__PURE__*/jsx(PlaceHolderOption, {}), isSearchEnabled && /*#__PURE__*/jsx(SearchBox$1, {
10987
+ ref: searchBoxReference,
10988
+ id: searchBoxId,
10989
+ classNames: classNamesProp,
10990
+ value: initSearchValue || searchValue,
10991
+ placeholder: searchPlaceholder || formatMessage(messages$2.searchPlaceholder),
10992
+ focusedOptionId: getUniqueIdForOption(id, focusedOption),
10993
+ onChange: handleSearchChange,
10994
+ onClick: stopPropagation$1
10995
+ }), options.slice(0, numberOfOptionsShown).map(renderOption), numberOfOptionsShown < options.length && /*#__PURE__*/jsx(ShowMoreOption, {})]
10996
+ });
10997
+ }
10998
+ function ShowMoreOption() {
10999
+ function handleOnClick(event) {
11000
+ stopPropagation$1(event);
11001
+ setNumberOfOptionsShown(numberOfOptionsShown + DEFAULT_OPTIONS_PAGE_SIZE);
11002
+ }
11003
+ return (
11004
+ /*#__PURE__*/
11005
+ /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */
11006
+ jsx("li", {
11007
+ className: classNames(s('clickable'), s('border-bottom'), s('show-more')),
11008
+ onClick: handleOnClick,
11009
+ onKeyPress: handleOnClick,
11010
+ children: /*#__PURE__*/jsx("a", {
11011
+ children: "..."
11012
+ })
11013
+ })
11014
+ );
11015
+ }
11016
+ function PlaceHolderOption() {
11017
+ const placeholderOption = {
11018
+ placeholder
11019
+ };
11020
+ return (
11021
+ /*#__PURE__*/
11022
+ /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */
11023
+ jsx("li", {
11024
+ className: classNames(s('clickable'), s('border-bottom')),
11025
+ onClick: createSelectHandlerForOption(placeholderOption),
11026
+ onKeyPress: createSelectHandlerForOption(placeholderOption),
11027
+ children: /*#__PURE__*/jsx("a", {
11028
+ children: placeholder
11029
+ })
11030
+ })
11031
+ );
11032
+ }
11033
+
11034
+ // eslint-disable-next-line react/prop-types
11035
+ function SeparatorOption() {
11036
+ return /*#__PURE__*/jsx("li", {
11037
+ className: s('np-separator'),
11038
+ "aria-hidden": true
11039
+ });
11040
+ }
11041
+
11042
+ // eslint-disable-next-line react/prop-types
11043
+ function HeaderOption({
11044
+ children
11045
+ }) {
11046
+ return /*#__PURE__*/jsx("li", {
11047
+ // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
11048
+ className: classNames(s('np-dropdown-header'), s('np-text-title-group')),
11049
+ onClick: stopPropagation$1,
11050
+ onKeyPress: stopPropagation$1,
11051
+ children: children
11052
+ });
11053
+ }
11054
+ function isOptionSelected(selected, option) {
11055
+ return selected?.value === option?.value;
11056
+ }
11057
+ const renderOption = (option, index) => {
11058
+ const separatorOption = option;
11059
+ if (isSeparatorOption(separatorOption) && separatorOption?.separator) {
11060
+ return /*#__PURE__*/jsx(SeparatorOption, {}, index);
11061
+ }
11062
+ const headerOption = option;
11063
+ if (isHeaderOption(headerOption) && headerOption.header) {
11064
+ return /*#__PURE__*/jsx(HeaderOption, {
11065
+ children: headerOption.header
11066
+ }, index);
11067
+ }
11068
+ const isActive = isOptionSelected(selected, option);
11069
+ const selectOption = option;
11070
+ const isFocusedWithKeyboard = !selectOption.disabled && keyboardFocusedOptionIndex === getIndexWithoutHeadersForIndexWithHeaders(index);
11071
+ const className = classNames(s('np-dropdown-item'), selectOption.disabled ? [s('disabled')] : s('clickable'), {
11072
+ [s('active')]: isActive,
11073
+ [s('np-dropdown-item--focused')]: isFocusedWithKeyboard
11074
+ });
11075
+ const handleOnClick = selectOption.disabled ? stopPropagation$1 : createSelectHandlerForOption(selectOption);
11076
+ return (
11077
+ /*#__PURE__*/
11078
+ /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */
11079
+ jsx("li", {
11080
+ ref: isFocusedWithKeyboard ? keyboardFocusedReference : undefined,
11081
+ id: getUniqueIdForOption(id, option),
11082
+ "aria-selected": isActive,
11083
+ "aria-disabled": option.disabled,
11084
+ role: "option",
11085
+ tabIndex: "-1",
11086
+ className: className,
11087
+ onClick: handleOnClick,
11088
+ onKeyPress: handleOnClick,
11089
+ children: /*#__PURE__*/jsx("a", {
11090
+ disabled: selectOption.disabled,
11091
+ children: /*#__PURE__*/jsx(Option$1, {
11092
+ ...selectOption,
11093
+ classNames: classNamesProp
11094
+ })
11095
+ })
11096
+ }, index)
11097
+ );
11098
+ };
11099
+ function getIndexWithoutHeadersForIndexWithHeaders(index) {
11100
+ return options.reduce((sum, option, currentIndex) => {
11101
+ if (currentIndex < index && isActionableOption(option)) {
11102
+ return sum + 1;
11103
+ }
11104
+ return sum;
11105
+ }, 0);
11106
+ }
11107
+ const hasActiveOptions = !!defaultOptions.length;
11108
+ if (open && (initSearchValue || searchValue)) {
11109
+ if (hasActiveOptions && keyboardFocusedOptionIndex == null) {
11110
+ setKeyboardFocusedOptionIndex(0);
11111
+ }
11112
+ if (!hasActiveOptions && keyboardFocusedOptionIndex != null) {
11113
+ setKeyboardFocusedOptionIndex(null);
11114
+ }
11115
+ }
11116
+ return /*#__PURE__*/jsxs("div", {
11117
+ // eslint-disable-line jsx-a11y/no-static-element-interactions
11118
+ ref: selectReference,
11119
+ className: classNames(s('np-select'), block ? s('btn-block') : null, s('btn-group')),
11120
+ onKeyDown: handleKeyDown,
11121
+ onTouchMove: handleTouchStart,
11122
+ onFocus: handleOnFocus,
11123
+ onBlur: handleOnBlur,
11124
+ children: [/*#__PURE__*/jsxs(Button, {
11125
+ ref: dropdownButtonReference,
11126
+ id: computedId,
11127
+ block: block,
11128
+ size: size,
11129
+ htmlType: "button",
11130
+ className: classNames(s('np-dropdown-toggle'), s('np-text-body-large'), inverse ? s('np-dropdown-toggle-navy') : null)
11131
+ // reset Button's styles
11132
+ ,
11133
+ type: null,
11134
+ priority: null,
11135
+ disabled: disabled,
11136
+ "aria-controls": listboxId,
11137
+ "aria-expanded": open,
11138
+ "aria-autocomplete": "none",
11139
+ onClick: handleOnClick,
11140
+ ...buttonProps,
11141
+ children: [selected ? /*#__PURE__*/jsx(Option$1, {
11142
+ ...selected,
11143
+ classNames: classNamesProp,
11144
+ selected: true
11145
+ }) : /*#__PURE__*/jsx("span", {
11146
+ className: s('form-control-placeholder'),
11147
+ children: placeholder
11148
+ }), /*#__PURE__*/jsx(Chevron$1
11149
+ // disabled={disabled}
11150
+ , {
11151
+ className: classNames(s('tw-icon'), s('tw-chevron-up-icon'), s('tw-chevron'), s('bottom'), s('np-select-chevron'))
11152
+ })]
11153
+ }), isMobile ? isSearchEnabled ? /*#__PURE__*/jsx(Drawer$1, {
11154
+ open: open,
11155
+ headerTitle: searchPlaceholder || formatMessage(messages$2.searchPlaceholder),
11156
+ onClose: handleCloseOptions,
11157
+ children: renderOptionsList()
11158
+ }) : /*#__PURE__*/jsx(BottomSheet$2, {
11159
+ open: open,
11160
+ onClose: handleCloseOptions,
11161
+ children: renderOptionsList({
11162
+ className: isModern ? '' : 'p-a-1'
11163
+ })
11164
+ }) : /*#__PURE__*/jsx(Panel$1, {
11165
+ open: open,
11166
+ flip: false,
11167
+ altAxis: true,
11168
+ anchorRef: selectReference,
11169
+ anchorWidth: isDropdownAutoWidth,
11170
+ position: dropdownUp ? Position.TOP : Position.BOTTOM,
11171
+ onClose: handleCloseOptions,
11172
+ children: renderOptionsList({
11173
+ className: 'p-a-1'
11174
+ })
11175
+ })]
11176
+ });
11177
+ }
11178
+ Select.propTypes = {
11179
+ placeholder: PropTypes.string,
11032
11180
  id: PropTypes.string,
11033
- label: PropTypes.string.isRequired,
11034
- name: PropTypes.string.isRequired,
11181
+ required: PropTypes.bool,
11182
+ disabled: PropTypes.bool,
11183
+ inverse: PropTypes.bool,
11184
+ dropdownRight: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
11185
+ dropdownWidth: PropTypes.oneOf(['sm', 'md', 'lg']),
11186
+ size: PropTypes.oneOf(['sm', 'md', 'lg']),
11187
+ block: PropTypes.bool,
11188
+ selected: PropTypes.shape({
11189
+ value: PropTypes.any.isRequired,
11190
+ label: PropTypes.node,
11191
+ icon: PropTypes.node,
11192
+ currency: PropTypes.string,
11193
+ note: PropTypes.node,
11194
+ secondary: PropTypes.node
11195
+ }),
11196
+ /**
11197
+ * Search toggle
11198
+ * if `true` default search functionality being enabled (not case sensitive search in option labels & currency props)
11199
+ * if `function` you can define your own search function to implement custom search experience. This search function used while filtering the options array. The custom search function takes two parameters. First is the option the second is the keyword.
11200
+ */
11201
+ search: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
11035
11202
  onChange: PropTypes.func.isRequired,
11036
- secondary: PropTypes.string,
11037
- value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
11038
- className: PropTypes.string
11039
- };
11040
- Radio.defaultProps = {
11041
- avatar: undefined,
11042
- checked: false,
11043
- disabled: false,
11044
- id: null,
11045
- secondary: null,
11046
- value: '',
11047
- className: undefined
11048
- };
11049
- var Radio$1 = Radio;
11050
-
11051
- class RadioGroup extends Component {
11052
- constructor(props) {
11053
- super(props);
11054
- this.state = {
11055
- selectedValue: props.selectedValue
11056
- };
11057
- }
11058
- handleOnChange = selectedValue => {
11059
- const {
11060
- onChange
11061
- } = this.props;
11062
- this.setState({
11063
- selectedValue
11064
- }, onChange && onChange(selectedValue));
11065
- };
11066
- render() {
11067
- const {
11068
- radios,
11069
- name
11070
- } = this.props;
11071
- const {
11072
- selectedValue
11073
- } = this.state;
11074
- return radios && radios.length > 0 ? /*#__PURE__*/jsx(Fragment, {
11075
- children: radios.map(({
11076
- id,
11077
- avatar,
11078
- value,
11079
- label,
11080
- disabled,
11081
- secondary,
11082
- readOnly
11083
- }, index) => /*#__PURE__*/jsx(Radio$1
11084
- // eslint-disable-next-line react/no-array-index-key
11085
- , {
11086
- id: id,
11087
- value: value,
11088
- label: label,
11089
- name: name,
11090
- disabled: disabled,
11091
- checked: selectedValue === value,
11092
- secondary: secondary,
11093
- readOnly: readOnly,
11094
- avatar: avatar,
11095
- onChange: value_ => this.handleOnChange(value_)
11096
- }, index))
11097
- }) : null;
11098
- }
11099
- }
11100
- RadioGroup.propTypes = {
11101
- radios: PropTypes.arrayOf(PropTypes.shape({
11102
- id: PropTypes.string,
11103
- avatar: PropTypes.element,
11104
- value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
11105
- secondary: PropTypes.string,
11106
- label: PropTypes.string.isRequired,
11203
+ onFocus: PropTypes.func,
11204
+ onBlur: PropTypes.func,
11205
+ options: PropTypes.arrayOf(PropTypes.shape({
11206
+ value: PropTypes.any,
11207
+ label: PropTypes.node,
11208
+ header: PropTypes.node,
11209
+ icon: PropTypes.node,
11210
+ currency: PropTypes.string,
11211
+ note: PropTypes.node,
11212
+ secondary: PropTypes.node,
11213
+ separator: PropTypes.bool,
11107
11214
  disabled: PropTypes.bool,
11108
- readOnly: PropTypes.bool
11215
+ searchStrings: PropTypes.arrayOf(PropTypes.string)
11109
11216
  })).isRequired,
11110
- onChange: PropTypes.func.isRequired,
11111
- selectedValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
11112
- name: PropTypes.string.isRequired
11113
- };
11114
- RadioGroup.defaultProps = {
11115
- selectedValue: null
11116
- };
11117
- var RadioGroup$1 = RadioGroup;
11118
-
11119
- const RadioOption = ({
11120
- 'aria-label': ariaLabel,
11121
- media,
11122
- title,
11123
- content,
11124
- id,
11125
- name,
11126
- checked,
11127
- onChange,
11128
- complex,
11129
- disabled,
11130
- value,
11131
- showMediaCircle,
11132
- showMediaAtAllSizes,
11133
- isContainerAligned
11134
- }) => {
11135
- const sharedProps = {
11136
- 'aria-label': ariaLabel,
11137
- media,
11138
- title,
11139
- content,
11140
- name,
11141
- complex,
11142
- disabled,
11143
- showMediaCircle,
11144
- showMediaAtAllSizes,
11145
- isContainerAligned
11146
- };
11147
- return /*#__PURE__*/jsx(Option$2, {
11148
- ...sharedProps,
11149
- button: /*#__PURE__*/jsx(RadioButton$1, {
11150
- id: id,
11151
- name: name,
11152
- checked: checked,
11153
- disabled: disabled,
11154
- value: value,
11155
- onChange: onChange
11156
- })
11157
- });
11158
- };
11159
- RadioOption.propTypes = {
11160
- 'aria-label': PropTypes.string,
11161
- media: PropTypes.node,
11162
- id: PropTypes.string.isRequired,
11163
- name: PropTypes.string.isRequired,
11164
- title: PropTypes.node.isRequired,
11165
- content: PropTypes.node,
11166
- checked: PropTypes.bool,
11167
- onChange: PropTypes.func.isRequired,
11168
- complex: PropTypes.bool,
11169
- disabled: PropTypes.bool,
11170
- value: PropTypes.string,
11171
- showMediaCircle: PropTypes.bool,
11172
- showMediaAtAllSizes: PropTypes.bool,
11173
- isContainerAligned: PropTypes.bool
11217
+ /**
11218
+ * To have full control of your search value and response use `onSearchChange` function combined with `searchValue` and custom filtering on the options array.
11219
+ * DO NOT USE TOGETHER WITH `search` PROPERTY
11220
+ */
11221
+ onSearchChange: PropTypes.func,
11222
+ searchValue: PropTypes.string,
11223
+ searchPlaceholder: PropTypes.string,
11224
+ classNames: PropTypes.objectOf(PropTypes.string),
11225
+ dropdownUp: PropTypes.bool,
11226
+ buttonProps: PropTypes.object,
11227
+ dropdownProps: PropTypes.object
11174
11228
  };
11175
- RadioOption.defaultProps = {
11176
- 'aria-label': undefined,
11177
- media: null,
11178
- content: null,
11179
- checked: false,
11180
- complex: false,
11229
+ Select.defaultProps = {
11230
+ id: undefined,
11231
+ placeholder: undefined,
11232
+ size: 'md',
11233
+ dropdownRight: null,
11234
+ dropdownWidth: null,
11235
+ inverse: false,
11236
+ required: false,
11181
11237
  disabled: false,
11182
- showMediaCircle: true,
11183
- showMediaAtAllSizes: false,
11184
- isContainerAligned: false,
11185
- value: ''
11186
- };
11187
- var RadioOption$1 = RadioOption;
11188
-
11189
- const Section = ({
11190
- children,
11191
- className,
11192
- withHorizontalPadding = false
11193
- }) => {
11194
- return /*#__PURE__*/jsx("div", {
11195
- className: classNames('np-section', className, {
11196
- 'np-section--with-horizontal-padding': withHorizontalPadding
11197
- }),
11198
- children: children
11199
- });
11238
+ block: true,
11239
+ selected: null,
11240
+ onFocus: null,
11241
+ onBlur: null,
11242
+ onSearchChange: undefined,
11243
+ search: false,
11244
+ searchValue: '',
11245
+ searchPlaceholder: undefined,
11246
+ classNames: {},
11247
+ dropdownUp: false,
11248
+ buttonProps: {},
11249
+ dropdownProps: {}
11200
11250
  };
11201
11251
 
11202
11252
  const CSS_TRANSITION_DURATION = 400;