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