@transferwise/components 0.0.0-experimental-08d336e → 0.0.0-experimental-b069e0c

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