@transferwise/components 45.26.3 → 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';
@@ -5575,19 +5575,16 @@ const Logo = ({
5575
5575
  inverse,
5576
5576
  type
5577
5577
  }) => {
5578
- const [clientWidth] = useClientWidth({
5579
- ref: isServerSide() ? undefined : window
5580
- });
5581
- const isSmall = clientWidth < Breakpoint.SMALL;
5582
- const LogoSvg = isSmall ? svgPaths[`WISE_FLAG${inverse ? '_INVERSE' : ''}`] : svgPaths[`${type}${inverse ? '_INVERSE' : ''}`];
5583
-
5584
- // If clientWidth is not defined, don't render anything
5585
- if (clientWidth === null) {
5586
- return null;
5587
- }
5588
- return /*#__PURE__*/jsx(LogoSvg, {
5589
- className: classNames('np-logo-svg', className),
5590
- alt: type === LogoType.WISE ? 'Wise' : 'Wise business'
5578
+ const LogoSm = svgPaths[`WISE_FLAG${inverse ? '_INVERSE' : ''}`];
5579
+ const LogoMd = svgPaths[`${type}${inverse ? '_INVERSE' : ''}`];
5580
+ return /*#__PURE__*/jsxs("span", {
5581
+ "aria-label": type === LogoType.WISE ? 'Wise' : 'Wise business',
5582
+ className: classNames(className, 'np-logo'),
5583
+ children: [/*#__PURE__*/jsx(LogoSm, {
5584
+ className: "np-logo-svg np-logo-svg--size-sm"
5585
+ }), /*#__PURE__*/jsx(LogoMd, {
5586
+ className: "np-logo-svg np-logo-svg--size-md"
5587
+ })]
5591
5588
  });
5592
5589
  };
5593
5590
  Logo.propTypes = {
@@ -6445,6 +6442,7 @@ const Input = /*#__PURE__*/forwardRef(function Input({
6445
6442
 
6446
6443
  const SearchInput = /*#__PURE__*/forwardRef(function SearchInput({
6447
6444
  shape = 'pill',
6445
+ size = Size.MEDIUM,
6448
6446
  disabled,
6449
6447
  className,
6450
6448
  ...restProps
@@ -6452,9 +6450,9 @@ const SearchInput = /*#__PURE__*/forwardRef(function SearchInput({
6452
6450
  return /*#__PURE__*/jsx(InputGroup, {
6453
6451
  addonStart: {
6454
6452
  content: /*#__PURE__*/jsx(Search, {
6455
- size: 24
6453
+ size: size === Size.SMALL ? 16 : 24
6456
6454
  }),
6457
- initialContentWidth: 24
6455
+ initialContentWidth: size === Size.SMALL ? 16 : 24
6458
6456
  },
6459
6457
  disabled: disabled,
6460
6458
  className: className,
@@ -6463,6 +6461,7 @@ const SearchInput = /*#__PURE__*/forwardRef(function SearchInput({
6463
6461
  role: "searchbox",
6464
6462
  inputMode: "search",
6465
6463
  shape: shape,
6464
+ size: size,
6466
6465
  ...restProps
6467
6466
  })
6468
6467
  });
@@ -6861,6 +6860,7 @@ function SelectInput({
6861
6860
  size = 'md',
6862
6861
  className,
6863
6862
  onChange,
6863
+ onSearchChange,
6864
6864
  onClear
6865
6865
  }) {
6866
6866
  const [open, setOpen] = useState(false);
@@ -6931,7 +6931,8 @@ function SelectInput({
6931
6931
  filterable: filterable,
6932
6932
  filterPlaceholder: filterPlaceholder,
6933
6933
  searchInputRef: searchInputRef,
6934
- listboxRef: listboxRef
6934
+ listboxRef: listboxRef,
6935
+ onSearchChange: onSearchChange
6935
6936
  })
6936
6937
  })
6937
6938
  });
@@ -6999,7 +7000,8 @@ function SelectInputOptions({
6999
7000
  filterable = false,
7000
7001
  filterPlaceholder,
7001
7002
  searchInputRef,
7002
- listboxRef
7003
+ listboxRef,
7004
+ onSearchChange
7003
7005
  }) {
7004
7006
  const intl = useIntl();
7005
7007
  const controllerRef = filterable ? searchInputRef : listboxRef;
@@ -7051,6 +7053,9 @@ function SelectInputOptions({
7051
7053
  },
7052
7054
  onChange: event => {
7053
7055
  setQuery(event.currentTarget.value);
7056
+ if (onSearchChange) {
7057
+ onSearchChange(event.currentTarget.value);
7058
+ }
7054
7059
  }
7055
7060
  })
7056
7061
  }) : null, /*#__PURE__*/jsxs("section", {
@@ -7780,1139 +7785,526 @@ Money.propTypes = {
7780
7785
  var Money$1 = Money;
7781
7786
 
7782
7787
  var messages$3 = defineMessages({
7783
- searchPlaceholder: {
7784
- id: "neptune.Select.searchPlaceholder"
7788
+ selectPlaceholder: {
7789
+ id: "neptune.MoneyInput.Select.placeholder"
7785
7790
  }
7786
7791
  });
7787
7792
 
7788
- // Option.tsx NEW
7789
- function Option$1({
7790
- label,
7791
- currency = '',
7792
- note = '',
7793
- secondary = '',
7794
- icon,
7795
- classNames = {},
7796
- selected = false
7797
- }) {
7798
- const {
7799
- isModern
7800
- } = useTheme();
7801
- const style = classes => classes.map(className => classNames[className] || className).join(' ');
7802
- const currencyClassNames = currency ? style(['currency-flag', `currency-flag-${currency.toLowerCase()}`]) : undefined;
7803
- const iconElement = icon ? /*#__PURE__*/cloneElement(icon, {
7804
- size: 24,
7805
- className: 'tw-icon'
7806
- }) : currency && /*#__PURE__*/jsx("i", {
7807
- className: currencyClassNames
7808
- });
7809
- const titleAndNoteElement = /*#__PURE__*/jsxs(Body, {
7810
- as: "span",
7811
- type: Typography.BODY_LARGE,
7812
- className: selected ? 'text-ellipsis' : undefined,
7813
- children: [label, note && /*#__PURE__*/jsx(Body, {
7814
- as: "span",
7815
- className: isModern ? 'm-l-1' : 'm-l-1 body-2',
7816
- children: note
7817
- })]
7818
- });
7819
- const secondaryElementClassNames = () => {
7820
- let classes = undefined;
7821
- if (selected) {
7822
- classes = 'text-ellipsis';
7823
- }
7824
- if (isModern) {
7825
- return classes;
7826
- }
7827
- return `${classes ? classes + ' ' : ''}body-2`;
7828
- };
7829
- const secondaryElement = secondary && /*#__PURE__*/jsx(Body, {
7830
- className: secondaryElementClassNames(),
7831
- children: secondary
7832
- });
7833
- return iconElement ? /*#__PURE__*/jsxs("div", {
7834
- className: "d-flex np-option-content",
7835
- children: [/*#__PURE__*/jsx("div", {
7836
- className: `d-flex flex-column${selected ? ' justify-content-center' : ''}`,
7837
- children: iconElement
7838
- }), /*#__PURE__*/jsxs("div", {
7839
- className: "d-flex flex-column justify-content-center",
7840
- children: [titleAndNoteElement, secondaryElement]
7841
- })]
7842
- }) : /*#__PURE__*/jsxs(Fragment, {
7843
- children: [iconElement, titleAndNoteElement, secondaryElement]
7844
- });
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';
7845
7825
  }
7846
-
7847
- const SearchBox = /*#__PURE__*/forwardRef(({
7848
- id,
7849
- classNames: classNames$1 = {},
7850
- focusedOptionId,
7851
- onChange,
7852
- onClick,
7853
- placeholder = undefined,
7854
- value = ''
7855
- }, reference) => {
7856
- const style = className => classNames$1[className] || className;
7857
- return /*#__PURE__*/jsx("li", {
7858
- className: style('border-bottom'),
7859
- children: /*#__PURE__*/jsx("a", {
7860
- className: `${style('np-select-filter-link')} ${style('p-a-0')}`,
7861
- children: /*#__PURE__*/jsxs("div", {
7862
- className: style('input-group'),
7863
- children: [/*#__PURE__*/jsx("span", {
7864
- className: classNames('input-group-addon', 'input-group-addon--search'),
7865
- children: /*#__PURE__*/jsx(Search, {
7866
- className: classNames(style('tw-icon'), style('tw-icon-search')),
7867
- size: 24
7868
- })
7869
- }), /*#__PURE__*/jsx(Input, {
7870
- ref: reference,
7871
- id: id,
7872
- role: "searchbox",
7873
- inputMode: "search",
7874
- className: classNames(style('np-select-filter')),
7875
- placeholder: placeholder,
7876
- value: value,
7877
- spellCheck: "false",
7878
- "aria-activedescendant": focusedOptionId,
7879
- onChange: onChange,
7880
- onClick: onClick
7881
- })]
7882
- })
7883
- })
7884
- });
7885
- });
7886
- var SearchBox$1 = SearchBox;
7887
-
7888
- const DEFAULT_SEARCH_VALUE = '';
7889
- const DEFAULT_OPTIONS_PAGE_SIZE = 1000;
7890
- const includesString = (string1, string2) => string1.toLowerCase().includes(string2.toLowerCase());
7891
- function defaultFilterFunction(option, searchValue) {
7892
- if (isPlaceholderOption(option)) {
7893
- 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';
7894
7833
  }
7895
- const {
7896
- label,
7897
- note,
7898
- secondary,
7899
- currency,
7900
- searchStrings
7901
- } = option;
7902
- return !!label && includesString(label, searchValue) || !!note && includesString(note, searchValue) || !!secondary && includesString(secondary, searchValue) || !!currency && includesString(currency, searchValue) || !!searchStrings && searchStrings.some(string => includesString(string, searchValue));
7903
7834
  }
7904
- function isActionableOption(option) {
7905
- return !option.header && !option.separator && !option.disabled;
7906
- }
7907
- function isHeaderOption(option) {
7908
- 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;
7909
7841
  }
7910
- function isSeparatorOption(option) {
7911
- return option != null && 'separator' in option;
7842
+ function getDecimalSeparator(locale) {
7843
+ return isNumberLocaleSupported() ? 1.1.toLocaleString(locale)[1] : '.';
7912
7844
  }
7913
- function clamp(from, to, value) {
7914
- 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);
7915
7853
  }
7916
7854
 
7917
- /**
7918
- * No option or placeholder option is selected
7919
- */
7920
- const DEFAULT_SELECTED_OPTION = null;
7921
- function isPlaceholderOption(option) {
7922
- return option === DEFAULT_SELECTED_OPTION || 'placeholder' in option;
7923
- }
7924
- function isSearchableOption(option) {
7925
- return !isHeaderOption(option) && !isSeparatorOption(option) && !isPlaceholderOption(option);
7926
- }
7927
- const getUniqueIdForOption = (parentId = '', option) => {
7928
- if (option == null) {
7929
- 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) : '';
7930
7869
  }
7931
- const uniqueOptionId = option.value || (option.label?.replace(/\s/g, '') ?? '');
7932
- return `option-${parentId}-${uniqueOptionId}`;
7933
7870
  };
7934
-
7935
- /**
7936
- * @deprecated Use `SelectInput` instead (https://neptune.wise.design/blog/2023-11-28-adopting-our-new-selectinput)
7937
- */
7938
- function Select({
7939
- placeholder,
7940
- id,
7941
- required,
7942
- disabled,
7943
- inverse,
7944
- dropdownWidth,
7945
- size,
7946
- block,
7947
- selected,
7948
- search,
7949
- onChange,
7950
- onFocus,
7951
- onBlur,
7952
- options: defaultOptions,
7953
- onSearchChange,
7954
- searchValue: initSearchValue,
7955
- searchPlaceholder,
7956
- // eslint-disable-next-line unicorn/prevent-abbreviations
7957
- classNames: classNamesProp,
7958
- dropdownUp,
7959
- dropdownProps,
7960
- buttonProps
7961
- }) {
7962
- const {
7963
- formatMessage
7964
- } = useIntl();
7965
- const {
7966
- isModern
7967
- } = useTheme();
7968
- const s = className => classNamesProp[className] || className;
7969
- const [open, setOpen] = useState(false);
7970
- const [searchValue, setSearchValue] = useState(DEFAULT_SEARCH_VALUE);
7971
- const [keyboardFocusedOptionIndex, setKeyboardFocusedOptionIndex] = useState(null);
7972
- const keyboardFocusedReference = useRef();
7973
- const previousKeyboardFocusedOptionIndex = useRef();
7974
- const [numberOfOptionsShown, setNumberOfOptionsShown] = useState(DEFAULT_OPTIONS_PAGE_SIZE);
7975
- const searchBoxReference = useRef(null);
7976
- const selectReference = useRef(null);
7977
- const dropdownButtonReference = useRef(null);
7978
- const optionsListReference = useRef(null);
7979
- const isSearchEnabled = !!onSearchChange || !!search;
7980
- const isDropdownAutoWidth = dropdownWidth == null;
7981
- const fallbackButtonId = useMemo(() => getSimpleRandomId('np-select-'), []);
7982
- const options = useMemo(() => {
7983
- if (!search || !searchValue) {
7984
- return defaultOptions;
7985
- }
7986
- return defaultOptions.filter(isSearchableOption).filter(option => {
7987
- if (typeof search === 'function') {
7988
- return search(option, searchValue);
7989
- } else {
7990
- return defaultFilterFunction(option, searchValue);
7991
- }
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
7992
7899
  });
7993
- }, [defaultOptions, search, searchValue]);
7994
- const selectableOptions = useMemo(() => options.filter(isActionableOption), [options]);
7995
- const focusedOption = selectableOptions[keyboardFocusedOptionIndex];
7996
- const computedId = id || fallbackButtonId;
7997
- const listboxId = `${computedId}-listbox`;
7998
- const searchBoxId = `${computedId}-searchbox`;
7999
- const {
8000
- isMobile
8001
- } = useLayout();
8002
- useEffect(() => {
8003
- let cancelled;
8004
- if (keyboardFocusedOptionIndex >= 0) {
8005
- requestAnimationFrame(() => {
8006
- if (!cancelled) {
8007
- if (isSearchEnabled) {
8008
- keyboardFocusedReference.current?.scrollIntoView?.({
8009
- block: 'center'
8010
- });
8011
- } else {
8012
- keyboardFocusedReference.current?.focus();
8013
- }
8014
- }
7900
+ if (!this.amountFocused) {
7901
+ this.setState({
7902
+ formattedAmount: formatAmountIfSet(nextProps.amount, nextProps.selectedCurrency.currency, nextProps?.intl?.locale, nextProps.maxLengthOverride)
8015
7903
  });
8016
- return () => {
8017
- cancelled = true;
8018
- };
8019
7904
  }
8020
- }, [keyboardFocusedOptionIndex, isSearchEnabled]);
8021
- const handleOnClick = () => {
8022
- 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);
8023
7915
  };
8024
- const handleTouchStart = event => {
8025
- if (event.currentTarget === event.target && open) {
8026
- handleCloseOptions();
7916
+ handleKeyDown = event => {
7917
+ if (!this.isInputAllowedForKeyEvent(event)) {
7918
+ event.preventDefault();
8027
7919
  }
8028
7920
  };
8029
- const handleOnFocus = event => {
8030
- if (onFocus) {
8031
- 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);
8032
7932
  }
7933
+ event.preventDefault();
8033
7934
  };
8034
- const handleOnBlur = event => {
7935
+ onAmountChange = event => {
8035
7936
  const {
8036
- nativeEvent
8037
- } = event;
8038
- if (nativeEvent) {
8039
- const elementReceivingFocus = nativeEvent.relatedTarget;
8040
- const select = event.currentTarget;
8041
- if (select && elementReceivingFocus && select.contains(elementReceivingFocus)) {
8042
- return;
8043
- }
8044
- }
8045
- if (onBlur) {
8046
- 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);
8047
7945
  }
8048
7946
  };
8049
- const handleSearchChange = event => {
8050
- setNumberOfOptionsShown(DEFAULT_OPTIONS_PAGE_SIZE);
8051
- setSearchValue(event.target.value);
8052
- if (onSearchChange) {
8053
- onSearchChange(event.target.value);
8054
- }
7947
+ onAmountBlur = () => {
7948
+ this.amountFocused = false;
7949
+ this.setAmount();
8055
7950
  };
8056
- const handleKeyDown = event => {
8057
- switch (event.keyCode) {
8058
- case KeyCodes.UP:
8059
- case KeyCodes.DOWN:
8060
- if (open) {
8061
- moveFocusWithDifference(event.keyCode === KeyCodes.UP ? -1 : 1);
8062
- } else {
8063
- setOpen(true);
8064
- }
8065
- stopPropagation$1(event);
8066
- break;
8067
- case KeyCodes.SPACE:
8068
- if (event.target !== searchBoxReference.current) {
8069
- if (open) {
8070
- selectKeyboardFocusedOption();
8071
- } else {
8072
- setOpen(true);
8073
- }
8074
- stopPropagation$1(event);
8075
- }
8076
- break;
8077
- case KeyCodes.ENTER:
8078
- if (open) {
8079
- selectKeyboardFocusedOption();
7951
+ onAmountFocus = () => {
7952
+ this.amountFocused = true;
7953
+ };
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;
7973
+ } else {
7974
+ if (groupIndex === null) {
7975
+ formattedOptions.push(this.mapOption(item));
8080
7976
  } else {
8081
- setOpen(true);
8082
- }
8083
- stopPropagation$1(event);
8084
- break;
8085
- case KeyCodes.ESCAPE:
8086
- handleCloseOptions();
8087
- stopPropagation$1(event);
8088
- break;
8089
- case KeyCodes.TAB:
8090
- if (open) {
8091
- selectKeyboardFocusedOption();
7977
+ formattedOptions[groupIndex]?.options.push(this.mapOption(item));
8092
7978
  }
8093
- break;
7979
+ }
7980
+ });
7981
+ return formattedOptions;
7982
+ }
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
+ };
7990
+ }
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();
8094
8007
  }
8095
8008
  };
8096
- function selectKeyboardFocusedOption() {
8097
- if (keyboardFocusedOptionIndex != null) {
8098
- selectableOptions.length > 0 && selectOption(selectableOptions[keyboardFocusedOptionIndex]);
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
+ });
8099
8018
  }
8100
- }
8101
- function moveFocusWithDifference(difference) {
8102
- const selectedOptionIndex = selectableOptions.reduce((optionIndex, current, index) => {
8103
- if (optionIndex != null) {
8104
- return optionIndex;
8105
- }
8106
- if (isOptionSelected(selected, current)) {
8107
- return index;
8019
+ };
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
+ }
8108
8041
  }
8109
8042
  return null;
8110
- }, null);
8111
- const previousFocusedIndex = previousKeyboardFocusedOptionIndex.current ?? -1;
8112
- let indexToStartMovingFrom = previousFocusedIndex;
8113
- if (previousFocusedIndex === -1) {
8114
- if (selectedOptionIndex == null) {
8115
- setKeyboardFocusedOptionIndex(0);
8116
- } else {
8117
- indexToStartMovingFrom = selectedOptionIndex;
8118
- }
8119
- }
8120
- const unClampedNewIndex = indexToStartMovingFrom + difference;
8121
- const newIndex = clamp(0, selectableOptions.length - 1, unClampedNewIndex);
8122
- setKeyboardFocusedOptionIndex(newIndex);
8123
- }
8124
- useEffect(() => {
8125
- if (open) {
8126
- if (!isMobile || searchValue) {
8127
- if (isSearchEnabled && !!searchBoxReference.current) {
8128
- searchBoxReference.current.focus();
8129
- }
8130
- if (!isSearchEnabled && optionsListReference.current && (previousKeyboardFocusedOptionIndex.current == null || Number.isNaN(previousKeyboardFocusedOptionIndex.current))) {
8131
- optionsListReference.current.focus();
8132
- }
8133
- }
8134
- previousKeyboardFocusedOptionIndex.current = keyboardFocusedOptionIndex;
8135
- } else {
8136
- previousKeyboardFocusedOptionIndex.current = null;
8137
- }
8138
- }, [open, searchValue, isSearchEnabled, isMobile, keyboardFocusedOptionIndex]);
8139
- const handleCloseOptions = () => {
8140
- setOpen(false);
8141
- setKeyboardFocusedOptionIndex(null);
8142
- if (dropdownButtonReference.current) {
8143
- dropdownButtonReference.current.focus();
8144
- }
8145
- };
8146
- function createSelectHandlerForOption(option) {
8147
- return event => {
8148
- stopPropagation$1(event);
8149
- selectOption(option);
8150
- };
8151
- }
8152
- function selectOption(option) {
8153
- onChange(isPlaceholderOption(option) ? DEFAULT_SELECTED_OPTION : option);
8154
- handleCloseOptions();
8155
- }
8156
- function renderOptionsList({
8157
- className
8158
- } = {}) {
8159
- const dropdownClass = classNames(s('np-dropdown-menu'), {
8160
- [s('np-dropdown-menu-desktop')]: !isMobile,
8161
- [s(`np-dropdown-menu-${dropdownWidth}`)]: !isMobile && !isDropdownAutoWidth
8162
- }, s(className));
8163
- const showPlaceholder = !required && !isSearchEnabled && Boolean(placeholder);
8164
- return /*#__PURE__*/jsxs("ul", {
8165
- ref: optionsListReference,
8166
- id: listboxId,
8167
- role: "listbox",
8168
- "aria-orientation": "vertical",
8169
- "aria-activedescendant": getUniqueIdForOption(id, selected),
8170
- tabIndex: "-1",
8171
- className: dropdownClass,
8172
- ...dropdownProps,
8173
- children: [showPlaceholder && /*#__PURE__*/jsx(PlaceHolderOption, {}), isSearchEnabled && /*#__PURE__*/jsx(SearchBox$1, {
8174
- ref: searchBoxReference,
8175
- id: searchBoxId,
8176
- classNames: classNamesProp,
8177
- value: initSearchValue || searchValue,
8178
- placeholder: searchPlaceholder || formatMessage(messages$3.searchPlaceholder),
8179
- focusedOptionId: getUniqueIdForOption(id, focusedOption),
8180
- onChange: handleSearchChange,
8181
- onClick: stopPropagation$1
8182
- }), options.slice(0, numberOfOptionsShown).map(renderOption), numberOfOptionsShown < options.length && /*#__PURE__*/jsx(ShowMoreOption, {})]
8183
- });
8184
- }
8185
- function ShowMoreOption() {
8186
- function handleOnClick(event) {
8187
- stopPropagation$1(event);
8188
- setNumberOfOptionsShown(numberOfOptionsShown + DEFAULT_OPTIONS_PAGE_SIZE);
8189
- }
8190
- return (
8191
- /*#__PURE__*/
8192
- /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */
8193
- jsx("li", {
8194
- className: classNames(s('clickable'), s('border-bottom'), s('show-more')),
8195
- onClick: handleOnClick,
8196
- onKeyPress: handleOnClick,
8197
- children: /*#__PURE__*/jsx("a", {
8198
- children: "..."
8199
- })
8200
- })
8201
- );
8202
- }
8203
- function PlaceHolderOption() {
8204
- const placeholderOption = {
8205
- placeholder
8206
8043
  };
8207
- return (
8208
- /*#__PURE__*/
8209
- /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */
8210
- jsx("li", {
8211
- className: classNames(s('clickable'), s('border-bottom')),
8212
- onClick: createSelectHandlerForOption(placeholderOption),
8213
- onKeyPress: createSelectHandlerForOption(placeholderOption),
8214
- children: /*#__PURE__*/jsx("a", {
8215
- 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
8216
8112
  })
8217
- })
8218
- );
8219
- }
8220
-
8221
- // eslint-disable-next-line react/prop-types
8222
- function SeparatorOption() {
8223
- return /*#__PURE__*/jsx("li", {
8224
- className: s('np-separator'),
8225
- "aria-hidden": true
8226
- });
8227
- }
8228
-
8229
- // eslint-disable-next-line react/prop-types
8230
- function HeaderOption({
8231
- children
8232
- }) {
8233
- return /*#__PURE__*/jsx("li", {
8234
- // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
8235
- className: classNames(s('np-dropdown-header'), s('np-text-title-group')),
8236
- onClick: stopPropagation$1,
8237
- onKeyPress: stopPropagation$1,
8238
- children: children
8113
+ })]
8239
8114
  });
8240
8115
  }
8241
- function isOptionSelected(selected, option) {
8242
- return selected?.value === option?.value;
8116
+ }
8117
+ function filterOptionsForQuery(options, query) {
8118
+ if (!query) {
8119
+ return options;
8243
8120
  }
8244
- const renderOption = (option, index) => {
8245
- const separatorOption = option;
8246
- if (isSeparatorOption(separatorOption) && separatorOption?.separator) {
8247
- return /*#__PURE__*/jsx(SeparatorOption, {}, index);
8248
- }
8249
- const headerOption = option;
8250
- if (isHeaderOption(headerOption) && headerOption.header) {
8251
- return /*#__PURE__*/jsx(HeaderOption, {
8252
- children: headerOption.header
8253
- }, 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);
8254
8131
  }
8255
- const isActive = isOptionSelected(selected, option);
8256
- const selectOption = option;
8257
- const isFocusedWithKeyboard = !selectOption.disabled && keyboardFocusedOptionIndex === getIndexWithoutHeadersForIndexWithHeaders(index);
8258
- const className = classNames(s('np-dropdown-item'), selectOption.disabled ? [s('disabled')] : s('clickable'), {
8259
- [s('active')]: isActive,
8260
- [s('np-dropdown-item--focused')]: isFocusedWithKeyboard
8261
- });
8262
- const handleOnClick = selectOption.disabled ? stopPropagation$1 : createSelectHandlerForOption(selectOption);
8263
- return (
8264
- /*#__PURE__*/
8265
- /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */
8266
- jsx("li", {
8267
- ref: isFocusedWithKeyboard ? keyboardFocusedReference : undefined,
8268
- id: getUniqueIdForOption(id, option),
8269
- "aria-selected": isActive,
8270
- "aria-disabled": option.disabled,
8271
- role: "option",
8272
- tabIndex: "-1",
8273
- className: className,
8274
- onClick: handleOnClick,
8275
- onKeyPress: handleOnClick,
8276
- children: /*#__PURE__*/jsx("a", {
8277
- disabled: selectOption.disabled,
8278
- children: /*#__PURE__*/jsx(Option$1, {
8279
- ...selectOption,
8280
- classNames: classNamesProp
8281
- })
8282
- })
8283
- }, index)
8284
- );
8285
- };
8286
- function getIndexWithoutHeadersForIndexWithHeaders(index) {
8287
- return options.reduce((sum, option, currentIndex) => {
8288
- if (currentIndex < index && isActionableOption(option)) {
8289
- return sum + 1;
8290
- }
8291
- return sum;
8292
- }, 0);
8132
+ });
8133
+ return result;
8134
+ }
8135
+ function isCurrencyOptionAndFitsQuery(option, query) {
8136
+ if (!option.value) {
8137
+ return false;
8293
8138
  }
8294
- const hasActiveOptions = !!defaultOptions.length;
8295
- if (open && (initSearchValue || searchValue)) {
8296
- if (hasActiveOptions && keyboardFocusedOptionIndex == null) {
8297
- 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;
8298
8150
  }
8299
- if (!hasActiveOptions && keyboardFocusedOptionIndex != null) {
8300
- setKeyboardFocusedOptionIndex(null);
8151
+ if (firstContains) {
8152
+ return -1;
8301
8153
  }
8302
- }
8303
- return /*#__PURE__*/jsxs("div", {
8304
- // eslint-disable-line jsx-a11y/no-static-element-interactions
8305
- ref: selectReference,
8306
- className: classNames(s('np-select'), block ? s('btn-block') : null, s('btn-group')),
8307
- onKeyDown: handleKeyDown,
8308
- onTouchMove: handleTouchStart,
8309
- onFocus: handleOnFocus,
8310
- onBlur: handleOnBlur,
8311
- children: [/*#__PURE__*/jsxs(Button, {
8312
- ref: dropdownButtonReference,
8313
- id: computedId,
8314
- block: block,
8315
- size: size,
8316
- htmlType: "button",
8317
- className: classNames(s('np-dropdown-toggle'), s('np-text-body-large'), inverse ? s('np-dropdown-toggle-navy') : null)
8318
- // reset Button's styles
8319
- ,
8320
- type: null,
8321
- priority: null,
8322
- disabled: disabled,
8323
- "aria-controls": listboxId,
8324
- "aria-expanded": open,
8325
- "aria-autocomplete": "none",
8326
- onClick: handleOnClick,
8327
- ...buttonProps,
8328
- children: [selected ? /*#__PURE__*/jsx(Option$1, {
8329
- ...selected,
8330
- classNames: classNamesProp,
8331
- selected: true
8332
- }) : /*#__PURE__*/jsx("span", {
8333
- className: s('form-control-placeholder'),
8334
- children: placeholder
8335
- }), /*#__PURE__*/jsx(Chevron$1
8336
- // disabled={disabled}
8337
- , {
8338
- className: classNames(s('tw-icon'), s('tw-chevron-up-icon'), s('tw-chevron'), s('bottom'), s('np-select-chevron'))
8339
- })]
8340
- }), isMobile ? isSearchEnabled ? /*#__PURE__*/jsx(Drawer$1, {
8341
- open: open,
8342
- headerTitle: searchPlaceholder || formatMessage(messages$3.searchPlaceholder),
8343
- onClose: handleCloseOptions,
8344
- children: renderOptionsList()
8345
- }) : /*#__PURE__*/jsx(BottomSheet$2, {
8346
- open: open,
8347
- onClose: handleCloseOptions,
8348
- children: renderOptionsList({
8349
- className: isModern ? '' : 'p-a-1'
8350
- })
8351
- }) : /*#__PURE__*/jsx(Panel$1, {
8352
- open: open,
8353
- flip: false,
8354
- altAxis: true,
8355
- anchorRef: selectReference,
8356
- anchorWidth: isDropdownAutoWidth,
8357
- position: dropdownUp ? Position.TOP : Position.BOTTOM,
8358
- onClose: handleCloseOptions,
8359
- children: renderOptionsList({
8360
- className: 'p-a-1'
8361
- })
8362
- })]
8154
+ if (secondContains) {
8155
+ return 1;
8156
+ }
8157
+ return 0;
8363
8158
  });
8364
8159
  }
8365
- Select.propTypes = {
8366
- placeholder: PropTypes.string,
8160
+ MoneyInput.propTypes = {
8367
8161
  id: PropTypes.string,
8368
- required: PropTypes.bool,
8369
- disabled: PropTypes.bool,
8370
- inverse: PropTypes.bool,
8371
- dropdownRight: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
8372
- 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,
8373
8167
  size: PropTypes.oneOf(['sm', 'md', 'lg']),
8374
- block: PropTypes.bool,
8375
- selected: PropTypes.shape({
8376
- value: PropTypes.any.isRequired,
8377
- label: PropTypes.node,
8378
- icon: PropTypes.node,
8379
- currency: PropTypes.string,
8380
- note: PropTypes.node,
8381
- secondary: PropTypes.node
8382
- }),
8383
- /**
8384
- * Search toggle
8385
- * if `true` default search functionality being enabled (not case sensitive search in option labels & currency props)
8386
- * 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.
8387
- */
8388
- search: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
8389
- onChange: PropTypes.func.isRequired,
8390
- onFocus: PropTypes.func,
8391
- onBlur: PropTypes.func,
8392
- options: PropTypes.arrayOf(PropTypes.shape({
8393
- value: PropTypes.any,
8394
- label: PropTypes.node,
8395
- header: PropTypes.node,
8396
- icon: PropTypes.node,
8397
- currency: PropTypes.string,
8398
- note: PropTypes.node,
8399
- secondary: PropTypes.node,
8400
- separator: PropTypes.bool,
8401
- disabled: PropTypes.bool,
8402
- searchStrings: PropTypes.arrayOf(PropTypes.string)
8403
- })).isRequired,
8168
+ onAmountChange: PropTypes.func,
8169
+ addon: PropTypes.node,
8170
+ searchPlaceholder: PropTypes.string,
8404
8171
  /**
8405
- * To have full control of your search value and response use `onSearchChange` function combined with `searchValue` and custom filtering on the options array.
8406
- * 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[] }`
8407
8173
  */
8408
8174
  onSearchChange: PropTypes.func,
8409
- searchValue: PropTypes.string,
8410
- searchPlaceholder: PropTypes.string,
8175
+ customActionLabel: PropTypes.node,
8176
+ onCustomAction: PropTypes.func,
8411
8177
  classNames: PropTypes.objectOf(PropTypes.string),
8412
- dropdownUp: PropTypes.bool,
8413
- buttonProps: PropTypes.object,
8414
- dropdownProps: PropTypes.object
8178
+ selectProps: PropTypes.object,
8179
+ maxLengthOverride: PropTypes.number
8415
8180
  };
8416
- Select.defaultProps = {
8417
- id: undefined,
8418
- placeholder: undefined,
8419
- size: 'md',
8420
- dropdownRight: null,
8421
- dropdownWidth: null,
8422
- inverse: false,
8423
- required: false,
8424
- disabled: false,
8425
- block: true,
8426
- selected: null,
8427
- onFocus: null,
8428
- onBlur: null,
8181
+ MoneyInput.defaultProps = {
8182
+ id: null,
8183
+ size: Size.LARGE,
8184
+ addon: null,
8185
+ searchPlaceholder: '',
8429
8186
  onSearchChange: undefined,
8430
- search: false,
8431
- searchValue: '',
8432
- searchPlaceholder: undefined,
8187
+ onCurrencyChange: null,
8188
+ placeholder: null,
8189
+ amount: null,
8190
+ onAmountChange: null,
8191
+ customActionLabel: '',
8192
+ onCustomAction: null,
8433
8193
  classNames: {},
8434
- dropdownUp: false,
8435
- buttonProps: {},
8436
- dropdownProps: {}
8194
+ selectProps: {},
8195
+ maxLengthOverride: null
8437
8196
  };
8197
+ var MoneyInput$1 = injectIntl(MoneyInput);
8438
8198
 
8439
- var messages$2 = defineMessages({
8440
- selectPlaceholder: {
8441
- id: "neptune.MoneyInput.Select.placeholder"
8442
- }
8443
- });
8444
-
8445
- // TODO: do not duplicate this between formatting and components
8446
- const currencyDecimals = {
8447
- BIF: 0,
8448
- BYR: 0,
8449
- CLP: 0,
8450
- DJF: 0,
8451
- GNF: 0,
8452
- JPY: 0,
8453
- KMF: 0,
8454
- KRW: 0,
8455
- MGA: 0,
8456
- PYG: 0,
8457
- RWF: 0,
8458
- VND: 0,
8459
- VUV: 0,
8460
- XAF: 0,
8461
- XOF: 0,
8462
- XPF: 0,
8463
- // technically HUF does have decimals, but due to the exchange rate banks
8464
- // do not accept decimal amounts
8465
- HUF: 0,
8466
- BHD: 3,
8467
- JOD: 3,
8468
- KWD: 3,
8469
- OMR: 3,
8470
- 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
+ });
8471
8209
  };
8472
- const DEFAULT_CURRENCY_DECIMALS = 2;
8473
- function isNumberLocaleSupported() {
8474
- const number = 1234;
8475
- const numberString = number.toLocaleString && number.toLocaleString(DEFAULT_LOCALE);
8476
- return numberString === '1,234';
8477
- }
8478
- function getValidLocale(locale) {
8479
- try {
8480
- const noUnderscoreLocale = locale.replace(/_/, '-');
8481
- Intl.NumberFormat(noUnderscoreLocale);
8482
- return noUnderscoreLocale;
8483
- } catch {
8484
- return 'en-GB';
8485
- }
8486
- }
8487
- function getCurrencyDecimals(currency = '') {
8488
- const upperCaseCurrency = currency.toUpperCase();
8489
- if (Object.prototype.hasOwnProperty.call(currencyDecimals, upperCaseCurrency)) {
8490
- return currencyDecimals[upperCaseCurrency];
8491
- }
8492
- return DEFAULT_CURRENCY_DECIMALS;
8493
- }
8494
- function getDecimalSeparator(locale) {
8495
- return isNumberLocaleSupported() ? 1.1.toLocaleString(locale)[1] : '.';
8496
- }
8497
- function parseAmount(number, currency, locale) {
8498
- const validLocale = getValidLocale(locale);
8499
- const precision = getCurrencyDecimals(currency);
8500
- const groupSeparator = isNumberLocaleSupported() ? 10000 .toLocaleString(validLocale)[2] : ',';
8501
- const decimalSeparator = getDecimalSeparator(validLocale);
8502
- const numberWithStandardDecimalSeparator = (number ? `${number}` : '').replace(new RegExp(`\\${groupSeparator}`, 'g'), '').replace(new RegExp(`\\${decimalSeparator}`, 'g'), '.').replace(/[^0-9.]/g, '');
8503
- const parsedAmount = parseFloat(parseFloat(numberWithStandardDecimalSeparator).toFixed(precision));
8504
- return Math.abs(parsedAmount);
8505
- }
8210
+ NavigationOptionList.propTypes = {
8211
+ children: PropTypes.node.isRequired
8212
+ };
8213
+ var NavigationOptionList$1 = NavigationOptionList;
8506
8214
 
8507
- const Currency = PropTypes.shape({
8508
- header: PropTypes.string,
8509
- value: PropTypes.string,
8510
- label: PropTypes.string,
8511
- currency: PropTypes.string,
8512
- note: PropTypes.string,
8513
- searchable: PropTypes.string
8514
- });
8515
- const CUSTOM_ACTION = 'CUSTOM_ACTION';
8516
- const isNumberOrNull = v => isNumber(v) || isNull(v);
8517
- const formatAmountIfSet = (amount, currency, locale, maxLengthOverride) => {
8518
- if (maxLengthOverride) {
8519
- return amount || '';
8520
- } else {
8521
- return typeof amount === 'number' ? formatAmount(amount, currency, locale) : '';
8522
- }
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 [];
8523
8228
  };
8524
- const parseNumber = (amount, currency, locale, maxLengthOverride) => {
8525
- if (!maxLengthOverride) {
8526
- return parseAmount(amount, currency, locale);
8527
- }
8528
- if (maxLengthOverride && amount.length > maxLengthOverride) {
8529
- return 0;
8530
- }
8531
- return +amount;
8532
- };
8533
- 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]);
8534
- const inputKeyAllowlist = new Set([Key.PERIOD, Key.COMMA]);
8535
- class MoneyInput extends Component {
8536
- constructor(props) {
8537
- super(props);
8538
- const {
8539
- locale
8540
- } = this.props.intl;
8541
- this.formatMessage = this.props.intl.formatMessage;
8542
- this.state = {
8543
- searchQuery: '',
8544
- formattedAmount: formatAmountIfSet(props.amount, props.selectedCurrency.currency, locale, props.maxLengthOverride),
8545
- locale
8546
- };
8547
- }
8548
- UNSAFE_componentWillReceiveProps(nextProps) {
8549
- this.setState({
8550
- locale: nextProps?.intl?.locale
8551
- });
8552
- if (!this.amountFocused) {
8553
- this.setState({
8554
- formattedAmount: formatAmountIfSet(nextProps.amount, nextProps.selectedCurrency.currency, nextProps?.intl?.locale, nextProps.maxLengthOverride)
8555
- });
8556
- }
8557
- }
8558
- isInputAllowedForKeyEvent = event => {
8559
- const {
8560
- keyCode,
8561
- metaKey,
8562
- key,
8563
- ctrlKey
8564
- } = event;
8565
- const isNumberKey = isNumber(parseInt(key, 10));
8566
- return isNumberKey || metaKey || ctrlKey || inputKeyCodeAllowlist.has(keyCode) || inputKeyAllowlist.has(key);
8567
- };
8568
- handleKeyDown = event => {
8569
- if (!this.isInputAllowedForKeyEvent(event)) {
8570
- event.preventDefault();
8571
- }
8572
- };
8573
- handlePaste = event => {
8574
- const paste = (event.clipboardData || window.clipboardData).getData('text');
8575
- const {
8576
- locale
8577
- } = this.state;
8578
- const parsed = isEmpty(paste) ? null : parseNumber(paste, this.props.selectedCurrency.currency, locale, this.props.maxLengthOverride);
8579
- if (isNumberOrNull(parsed)) {
8580
- this.setState({
8581
- formattedAmount: formatAmountIfSet(parsed, this.props.selectedCurrency.currency, locale, this.props.maxLengthOverride)
8582
- });
8583
- 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);
8584
8252
  }
8585
- event.preventDefault();
8586
- };
8587
- onAmountChange = event => {
8588
- const {
8589
- value
8590
- } = event.target;
8591
- this.setState({
8592
- formattedAmount: value
8593
- });
8594
- const parsed = isEmpty(value) ? null : parseNumber(value, this.props.selectedCurrency.currency, this.state.locale, this.props.maxLengthOverride);
8595
- if (isNumberOrNull(parsed)) {
8596
- this.props.onAmountChange(parsed);
8253
+ if (onDismiss) {
8254
+ onDismiss();
8597
8255
  }
8598
8256
  };
8599
- onAmountBlur = () => {
8600
- this.amountFocused = false;
8601
- this.setAmount();
8602
- };
8603
- onAmountFocus = () => {
8604
- this.amountFocused = true;
8605
- };
8606
- getSelectOptions() {
8607
- const selectOptions = [...filterOptionsForQuery(this.props.currencies, this.state.searchQuery)];
8608
- if (this.props.onCustomAction) {
8609
- selectOptions.push({
8610
- value: CUSTOM_ACTION,
8611
- label: this.props.customActionLabel
8612
- });
8613
- }
8614
- return selectOptions;
8615
- }
8616
- setAmount() {
8617
- this.setState(previousState => {
8618
- const parsed = parseNumber(previousState.formattedAmount, this.props.selectedCurrency.currency, previousState.locale, this.props.maxLengthOverride);
8619
- if (!isNumberOrNull(parsed)) {
8620
- return {
8621
- formattedAmount: previousState.formattedAmount
8622
- };
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);
8623
8267
  }
8624
- return {
8625
- formattedAmount: formatAmountIfSet(parsed, this.props.selectedCurrency.currency, previousState.locale, this.props.maxLengthOverride)
8626
- };
8627
- });
8628
- }
8629
- handleSelectChange = value => {
8630
- this.handleSearchChange('');
8631
- if (this.props.onCustomAction && value.value === CUSTOM_ACTION) {
8632
- this.props.onCustomAction();
8633
- } else {
8634
- this.props.onCurrencyChange(value);
8635
- }
8636
- };
8637
- handleSearchChange = searchQuery => {
8638
- this.setState({
8639
- searchQuery
8640
- });
8641
- if (this.props.onSearchChange) {
8642
- this.props.onSearchChange({
8643
- searchQuery,
8644
- filteredOptions: filterOptionsForQuery(this.props.currencies, searchQuery)
8645
- });
8646
8268
  }
8647
- };
8648
- style = className => this.props.classNames[className] || className;
8649
- render() {
8650
- const {
8651
- selectedCurrency,
8652
- onCurrencyChange,
8653
- size,
8654
- addon,
8655
- id,
8656
- selectProps,
8657
- maxLengthOverride
8658
- } = this.props;
8659
- const selectOptions = this.getSelectOptions();
8660
- const isFixedCurrency = !this.state.searchQuery && (selectOptions.length === 1 && selectOptions[0].currency === selectedCurrency.currency || !onCurrencyChange);
8661
- const disabled = !this.props.onAmountChange;
8662
- return /*#__PURE__*/jsxs("div", {
8663
- className: classNames(this.style('tw-money-input'), this.style('input-group'), this.style(`input-group-${size}`)),
8664
- children: [/*#__PURE__*/jsx(Input, {
8665
- id: id,
8666
- value: this.state.formattedAmount,
8667
- inputMode: "decimal",
8668
- disabled: disabled,
8669
- maxLength: maxLengthOverride,
8670
- placeholder: formatAmountIfSet(this.props.placeholder, this.props.selectedCurrency.currency, this.state.locale, this.props.maxLengthOverride),
8671
- autoComplete: "off",
8672
- onKeyDown: this.handleKeyDown,
8673
- onChange: this.onAmountChange,
8674
- onFocus: this.onAmountFocus,
8675
- onBlur: this.onAmountBlur,
8676
- onPaste: this.handlePaste
8677
- }), addon && /*#__PURE__*/jsx("span", {
8678
- className: classNames(this.style('input-group-addon'), this.style(`input-${size}`), disabled ? this.style('disabled') : ''),
8679
- children: addon
8680
- }), isFixedCurrency ? /*#__PURE__*/jsxs("div", {
8681
- className: classNames(this.style('input-group-addon'), this.style(`input-${size}`), this.style('tw-money-input__fixed-currency'), disabled ? this.style('disabled') : ''),
8682
- children: [(size === 'lg' || size === 'md') && /*#__PURE__*/jsxs(Fragment, {
8683
- children: [/*#__PURE__*/jsx("i", {
8684
- className: classNames(this.style('tw-money-input__keyline'))
8685
- }), /*#__PURE__*/jsx("i", {
8686
- className: classNames(this.style('currency-flag'), this.style(`currency-flag-${selectedCurrency.currency.toLowerCase()}`), this.style('m-r-2'))
8687
- })]
8688
- }), /*#__PURE__*/jsx(Title, {
8689
- as: "span",
8690
- type: Typography.TITLE_SUBSECTION,
8691
- className: size === 'lg' ? this.style('m-r-1') : '',
8692
- children: selectedCurrency.currency.toUpperCase()
8693
- })]
8694
- }) : /*#__PURE__*/jsx("div", {
8695
- className: classNames(this.style('input-group-btn'), this.style('amount-currency-select-btn')),
8696
- children: /*#__PURE__*/jsx(Select, {
8697
- id: id ? `${id}-select` : undefined,
8698
- classNames: this.props.classNames,
8699
- options: selectOptions,
8700
- selected: {
8701
- ...selectedCurrency,
8702
- label: /*#__PURE__*/jsx(Title, {
8703
- as: "span",
8704
- type: Typography.TITLE_SUBSECTION,
8705
- className: "tw-money-input__text",
8706
- children: selectedCurrency.label
8707
- }),
8708
- note: null
8709
- },
8710
- placeholder: this.formatMessage(messages$2.selectPlaceholder),
8711
- searchPlaceholder: this.props.searchPlaceholder,
8712
- searchValue: this.state.searchQuery,
8713
- size: size,
8714
- required: true,
8715
- dropdownWidth: Size.LARGE,
8716
- inverse: true,
8717
- onChange: this.handleSelectChange,
8718
- onSearchChange: this.handleSearchChange,
8719
- ...selectProps
8720
- })
8721
- })]
8722
- });
8723
- }
8724
- }
8725
- function filterOptionsForQuery(options, query) {
8726
- if (!query) {
8727
- 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;
8728
8274
  }
8729
- const filteredOptions = removeDuplicateValueOptions(options).filter(option => isCurrencyOptionAndFitsQuery(option, query));
8730
- return sortOptionsLabelsToFirst(filteredOptions, query);
8731
- }
8732
- function removeDuplicateValueOptions(options) {
8733
- const result = [];
8734
- const resultValues = [];
8735
- options.forEach(option => {
8736
- if (option.value && !resultValues.includes(option.value)) {
8737
- result.push(option);
8738
- resultValues.push(option.value);
8739
- }
8740
- });
8741
- return result;
8742
- }
8743
- function isCurrencyOptionAndFitsQuery(option, query) {
8744
- if (!option.value) {
8745
- return false;
8746
- }
8747
- return contains(option.label, query) || contains(option.searchable, query) || contains(option.note, query);
8748
- }
8749
- function contains(property, query) {
8750
- return property && property.toLowerCase().includes(query.toLowerCase());
8751
- }
8752
- function sortOptionsLabelsToFirst(options, query) {
8753
- return options.sort((first, second) => {
8754
- const firstContains = contains(first.label, query);
8755
- const secondContains = contains(second.label, query);
8756
- if (firstContains && secondContains) {
8757
- return 0;
8758
- }
8759
- if (firstContains) {
8760
- return -1;
8761
- }
8762
- if (secondContains) {
8763
- return 1;
8764
- }
8765
- return 0;
8766
- });
8767
- }
8768
- MoneyInput.propTypes = {
8769
- id: PropTypes.string,
8770
- currencies: PropTypes.arrayOf(Currency).isRequired,
8771
- selectedCurrency: Currency.isRequired,
8772
- onCurrencyChange: PropTypes.func,
8773
- placeholder: PropTypes.number,
8774
- amount: PropTypes.number,
8775
- size: PropTypes.oneOf(['sm', 'md', 'lg']),
8776
- onAmountChange: PropTypes.func,
8777
- addon: PropTypes.node,
8778
- searchPlaceholder: PropTypes.string,
8779
- /**
8780
- * Allows the consumer to react to searching, while the search itself is handled internally. Called with `{ searchQuery: string, filteredOptions: Currency[] }`
8781
- */
8782
- onSearchChange: PropTypes.func,
8783
- customActionLabel: PropTypes.node,
8784
- onCustomAction: PropTypes.func,
8785
- classNames: PropTypes.objectOf(PropTypes.string),
8786
- selectProps: PropTypes.object,
8787
- maxLengthOverride: PropTypes.number
8788
- };
8789
- MoneyInput.defaultProps = {
8790
- id: null,
8791
- size: Size.LARGE,
8792
- addon: null,
8793
- searchPlaceholder: '',
8794
- onSearchChange: undefined,
8795
- onCurrencyChange: null,
8796
- placeholder: null,
8797
- amount: null,
8798
- onAmountChange: null,
8799
- customActionLabel: '',
8800
- onCustomAction: null,
8801
- classNames: {},
8802
- selectProps: {},
8803
- maxLengthOverride: null
8804
- };
8805
- var MoneyInput$1 = injectIntl(MoneyInput);
8806
-
8807
- const NavigationOptionList = ({
8808
- children
8809
- }) => {
8810
- return /*#__PURE__*/jsx("ul", {
8811
- className: "np-navigation-options-list",
8812
- children: Children.map(children, child => /*#__PURE__*/jsx("li", {
8813
- className: "np-navigation-options-list__item",
8814
- children: child
8815
- }, child.key))
8816
- });
8817
- };
8818
- NavigationOptionList.propTypes = {
8819
- children: PropTypes.node.isRequired
8820
- };
8821
- var NavigationOptionList$1 = NavigationOptionList;
8822
-
8823
- const STORAGE_NAME = 'dismissedNudges';
8824
- const getLocalStorage = () => {
8825
- try {
8826
- const storageItem = localStorage.getItem(STORAGE_NAME);
8827
- if (storageItem) {
8828
- const storage = JSON.parse(storageItem);
8829
- if (Array.isArray(storage)) {
8830
- return storage;
8831
- }
8832
- }
8833
- // eslint-disable-next-line unicorn/prefer-optional-catch-binding, no-empty
8834
- } catch (error) {}
8835
- return [];
8836
- };
8837
- const Nudge = ({
8838
- media,
8839
- mediaName,
8840
- title,
8841
- link,
8842
- href,
8843
- onClick,
8844
- onDismiss,
8845
- persistDismissal,
8846
- isPreviouslyDismissed,
8847
- id,
8848
- className
8849
- }) => {
8850
- const [isDismissed, setIsDismissed] = useState(false);
8851
- const [isMounted, setIsMounted] = useState(false);
8852
- const handleOnDismiss = () => {
8853
- const dismissedNudgesStorage = getLocalStorage();
8854
- if (persistDismissal && id) {
8855
- try {
8856
- localStorage.setItem(STORAGE_NAME, JSON.stringify([...(dismissedNudgesStorage ? dismissedNudgesStorage : []), id]));
8857
- // eslint-disable-next-line unicorn/prefer-optional-catch-binding, no-empty
8858
- } catch (error) {}
8859
- setIsDismissed(true);
8860
- }
8861
- if (onDismiss) {
8862
- onDismiss();
8863
- }
8864
- };
8865
- useEffect(() => {
8866
- if (persistDismissal && id) {
8867
- const dismissedNudgesStorage = getLocalStorage();
8868
- let isDismissed = false;
8869
- if (dismissedNudgesStorage && dismissedNudgesStorage.find(item => item === id)) {
8870
- setIsDismissed(true);
8871
- isDismissed = true;
8872
- }
8873
- if (isPreviouslyDismissed) {
8874
- isPreviouslyDismissed(isDismissed);
8875
- }
8876
- }
8877
- setIsMounted(true);
8878
- // eslint-disable-next-line react-hooks/exhaustive-deps
8879
- }, [id, persistDismissal]);
8880
- if (persistDismissal && (isDismissed || !isMounted)) {
8881
- return null;
8882
- }
8883
- return /*#__PURE__*/jsxs("div", {
8884
- className: classNames('wds-nudge', className),
8885
- id: id,
8886
- children: [!!mediaName && /*#__PURE__*/jsx("div", {
8887
- className: "wds-nudge-media",
8888
- children: /*#__PURE__*/jsx(Illustration, {
8889
- name: mediaName,
8890
- className: classNames(`wds-nudge-media-${mediaName}`),
8891
- size: "small",
8892
- disablePadding: true,
8893
- alt: ""
8894
- })
8895
- }), /*#__PURE__*/jsxs("div", {
8896
- className: "wds-nudge-container",
8897
- children: [/*#__PURE__*/jsxs("div", {
8898
- className: "wds-nudge-content",
8899
- children: [/*#__PURE__*/jsx(Body, {
8900
- type: Typography.BODY_LARGE,
8901
- className: classNames('wds-nudge-body'),
8902
- children: title
8903
- }), link && /*#__PURE__*/jsx(Link, {
8904
- href: href,
8905
- type: Typography.LINK_LARGE,
8906
- className: "wds-nudge-link",
8907
- onClick: onClick,
8908
- children: link
8909
- })]
8910
- }), onDismiss || persistDismissal ? /*#__PURE__*/jsx(CloseButton, {
8911
- className: "wds-nudge-control",
8912
- size: "sm",
8913
- onClick: handleOnDismiss
8914
- }) : null]
8915
- })]
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
+ })]
8916
8308
  });
8917
8309
  };
8918
8310
 
@@ -11025,179 +10417,836 @@ const Radio = ({
11025
10417
  })]
11026
10418
  })
11027
10419
  });
11028
- };
11029
- Radio.propTypes = {
11030
- avatar: PropTypes.element,
11031
- checked: PropTypes.bool,
11032
- 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,
11033
11180
  id: PropTypes.string,
11034
- label: PropTypes.string.isRequired,
11035
- 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]),
11036
11202
  onChange: PropTypes.func.isRequired,
11037
- secondary: PropTypes.string,
11038
- value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
11039
- className: PropTypes.string
11040
- };
11041
- Radio.defaultProps = {
11042
- avatar: undefined,
11043
- checked: false,
11044
- disabled: false,
11045
- id: null,
11046
- secondary: null,
11047
- value: '',
11048
- className: undefined
11049
- };
11050
- var Radio$1 = Radio;
11051
-
11052
- class RadioGroup extends Component {
11053
- constructor(props) {
11054
- super(props);
11055
- this.state = {
11056
- selectedValue: props.selectedValue
11057
- };
11058
- }
11059
- handleOnChange = selectedValue => {
11060
- const {
11061
- onChange
11062
- } = this.props;
11063
- this.setState({
11064
- selectedValue
11065
- }, onChange && onChange(selectedValue));
11066
- };
11067
- render() {
11068
- const {
11069
- radios,
11070
- name
11071
- } = this.props;
11072
- const {
11073
- selectedValue
11074
- } = this.state;
11075
- return radios && radios.length > 0 ? /*#__PURE__*/jsx(Fragment, {
11076
- children: radios.map(({
11077
- id,
11078
- avatar,
11079
- value,
11080
- label,
11081
- disabled,
11082
- secondary,
11083
- readOnly
11084
- }, index) => /*#__PURE__*/jsx(Radio$1
11085
- // eslint-disable-next-line react/no-array-index-key
11086
- , {
11087
- id: id,
11088
- value: value,
11089
- label: label,
11090
- name: name,
11091
- disabled: disabled,
11092
- checked: selectedValue === value,
11093
- secondary: secondary,
11094
- readOnly: readOnly,
11095
- avatar: avatar,
11096
- onChange: value_ => this.handleOnChange(value_)
11097
- }, index))
11098
- }) : null;
11099
- }
11100
- }
11101
- RadioGroup.propTypes = {
11102
- radios: PropTypes.arrayOf(PropTypes.shape({
11103
- id: PropTypes.string,
11104
- avatar: PropTypes.element,
11105
- value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
11106
- secondary: PropTypes.string,
11107
- 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,
11108
11214
  disabled: PropTypes.bool,
11109
- readOnly: PropTypes.bool
11215
+ searchStrings: PropTypes.arrayOf(PropTypes.string)
11110
11216
  })).isRequired,
11111
- onChange: PropTypes.func.isRequired,
11112
- selectedValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
11113
- name: PropTypes.string.isRequired
11114
- };
11115
- RadioGroup.defaultProps = {
11116
- selectedValue: null
11117
- };
11118
- var RadioGroup$1 = RadioGroup;
11119
-
11120
- const RadioOption = ({
11121
- 'aria-label': ariaLabel,
11122
- media,
11123
- title,
11124
- content,
11125
- id,
11126
- name,
11127
- checked,
11128
- onChange,
11129
- complex,
11130
- disabled,
11131
- value,
11132
- showMediaCircle,
11133
- showMediaAtAllSizes,
11134
- isContainerAligned
11135
- }) => {
11136
- const sharedProps = {
11137
- 'aria-label': ariaLabel,
11138
- media,
11139
- title,
11140
- content,
11141
- name,
11142
- complex,
11143
- disabled,
11144
- showMediaCircle,
11145
- showMediaAtAllSizes,
11146
- isContainerAligned
11147
- };
11148
- return /*#__PURE__*/jsx(Option$2, {
11149
- ...sharedProps,
11150
- button: /*#__PURE__*/jsx(RadioButton$1, {
11151
- id: id,
11152
- name: name,
11153
- checked: checked,
11154
- disabled: disabled,
11155
- value: value,
11156
- onChange: onChange
11157
- })
11158
- });
11159
- };
11160
- RadioOption.propTypes = {
11161
- 'aria-label': PropTypes.string,
11162
- media: PropTypes.node,
11163
- id: PropTypes.string.isRequired,
11164
- name: PropTypes.string.isRequired,
11165
- title: PropTypes.node.isRequired,
11166
- content: PropTypes.node,
11167
- checked: PropTypes.bool,
11168
- onChange: PropTypes.func.isRequired,
11169
- complex: PropTypes.bool,
11170
- disabled: PropTypes.bool,
11171
- value: PropTypes.string,
11172
- showMediaCircle: PropTypes.bool,
11173
- showMediaAtAllSizes: PropTypes.bool,
11174
- 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
11175
11228
  };
11176
- RadioOption.defaultProps = {
11177
- 'aria-label': undefined,
11178
- media: null,
11179
- content: null,
11180
- checked: false,
11181
- 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,
11182
11237
  disabled: false,
11183
- showMediaCircle: true,
11184
- showMediaAtAllSizes: false,
11185
- isContainerAligned: false,
11186
- value: ''
11187
- };
11188
- var RadioOption$1 = RadioOption;
11189
-
11190
- const Section = ({
11191
- children,
11192
- className,
11193
- withHorizontalPadding = false
11194
- }) => {
11195
- return /*#__PURE__*/jsx("div", {
11196
- className: classNames('np-section', className, {
11197
- 'np-section--with-horizontal-padding': withHorizontalPadding
11198
- }),
11199
- children: children
11200
- });
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: {}
11201
11250
  };
11202
11251
 
11203
11252
  const CSS_TRANSITION_DURATION = 400;