@transferwise/components 0.0.0-experimental-ae7816b → 0.0.0-experimental-1718ca8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -5,22 +5,22 @@ var reactId = require('@radix-ui/react-id');
5
5
  var classNames = require('classnames');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var icons = require('@transferwise/icons');
8
- var reactPopper = require('react-popper');
8
+ var reactIntl = require('react-intl');
9
+ var PropTypes = require('prop-types');
10
+ var commonmark = require('commonmark');
9
11
  var componentsTheming = require('@wise/components-theming');
10
12
  var reactTransitionGroup = require('react-transition-group');
11
13
  var focus = require('@react-aria/focus');
12
14
  var reactDom = require('react-dom');
13
- var reactIntl = require('react-intl');
14
- var PropTypes = require('prop-types');
15
- var art = require('@wise/art');
16
15
  var index_js = require('use-sync-external-store/shim/index.js');
17
16
  var neptuneValidation = require('@transferwise/neptune-validation');
18
- var commonmark = require('commonmark');
19
17
  var formatting = require('@transferwise/formatting');
20
18
  var react$1 = require('@headlessui/react');
21
19
  var mergeProps = require('merge-props');
22
20
  var react = require('@floating-ui/react');
23
21
  var overlays = require('@react-aria/overlays');
22
+ var reactPopper = require('react-popper');
23
+ var art = require('@wise/art');
24
24
  var clamp$2 = require('lodash.clamp');
25
25
  var debounce = require('lodash.debounce');
26
26
  var requiredIf = require('react-required-if');
@@ -700,383 +700,590 @@ const ActionOption = ({
700
700
  });
701
701
  };
702
702
 
703
- const FocusBoundary = ({
704
- children
703
+ var messages$d = reactIntl.defineMessages({
704
+ ariaLabel: {
705
+ id: "neptune.CloseButton.ariaLabel"
706
+ }
707
+ });
708
+
709
+ const CloseButton = /*#__PURE__*/React.forwardRef(function CloseButton({
710
+ 'aria-label': ariaLabel,
711
+ size = exports.Size.MEDIUM,
712
+ filled = false,
713
+ className,
714
+ onClick,
715
+ isDisabled,
716
+ testId
717
+ }, reference) {
718
+ const intl = reactIntl.useIntl();
719
+ ariaLabel ??= intl.formatMessage(messages$d.ariaLabel);
720
+ const Icon = filled ? icons.CrossCircleFill : icons.Cross;
721
+ return /*#__PURE__*/jsxRuntime.jsx("button", {
722
+ ref: reference,
723
+ type: "button",
724
+ className: classNames__default.default('np-close-button', 'close btn-link', 'text-no-decoration', {
725
+ 'np-close-button--large': size === exports.Size.MEDIUM,
726
+ 'np-close-button--x-large': size === exports.Size.LARGE
727
+ }, className),
728
+ "aria-label": ariaLabel,
729
+ "aria-disabled": isDisabled,
730
+ disabled: isDisabled,
731
+ "data-testid": testId,
732
+ onClick: onClick,
733
+ children: /*#__PURE__*/jsxRuntime.jsx(Icon, {
734
+ size: size === exports.Size.SMALL ? 16 : 24
735
+ })
736
+ });
737
+ });
738
+
739
+ var messages$c = reactIntl.defineMessages({
740
+ opensInNewTab: {
741
+ id: "neptune.Link.opensInNewTab"
742
+ }
743
+ });
744
+
745
+ const Link = ({
746
+ className,
747
+ children,
748
+ href,
749
+ target,
750
+ type,
751
+ 'aria-label': ariaLabel,
752
+ onClick,
753
+ ...props
705
754
  }) => {
706
- const wrapperReference = React.useRef(null);
707
- React.useEffect(() => {
708
- wrapperReference.current?.focus({
709
- preventScroll: true
710
- });
711
- }, []);
712
- return /*#__PURE__*/jsxRuntime.jsx(focus.FocusScope, {
713
- contain: true,
714
- restoreFocus: true,
715
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
716
- ref: wrapperReference,
717
- tabIndex: -1,
718
- children: children
755
+ const isBlank = target === '_blank';
756
+ const {
757
+ formatMessage
758
+ } = reactIntl.useIntl();
759
+ return /*#__PURE__*/jsxRuntime.jsxs("a", {
760
+ href: href,
761
+ target: target,
762
+ className: classNames__default.default('np-link', type ? `np-text-${type}` : undefined, 'd-inline-flex', className),
763
+ "aria-label": ariaLabel,
764
+ rel: isBlank ? 'noreferrer' : undefined,
765
+ onClick: onClick,
766
+ ...props,
767
+ children: [children, " ", isBlank && /*#__PURE__*/jsxRuntime.jsx(icons.NavigateAway, {
768
+ title: formatMessage(messages$c.opensInNewTab)
769
+ })]
770
+ });
771
+ };
772
+
773
+ const iconTypeMap = {
774
+ positive: icons.Check,
775
+ neutral: icons.Info,
776
+ warning: icons.Alert,
777
+ negative: icons.Cross,
778
+ pending: icons.ClockBorderless,
779
+ info: icons.Info,
780
+ error: icons.Cross,
781
+ success: icons.Check
782
+ };
783
+ const StatusIcon = ({
784
+ sentiment = 'neutral',
785
+ size = 'md'
786
+ }) => {
787
+ const Icon = iconTypeMap[sentiment];
788
+ const iconColor = sentiment === 'warning' || sentiment === 'pending' ? 'dark' : 'light';
789
+ return /*#__PURE__*/jsxRuntime.jsx("span", {
790
+ "data-testid": "status-icon",
791
+ className: classNames__default.default('status-circle', 'status-circle-' + size, sentiment),
792
+ children: /*#__PURE__*/jsxRuntime.jsx(Icon, {
793
+ className: classNames__default.default('status-icon', iconColor)
719
794
  })
720
795
  });
721
796
  };
722
797
 
723
- function withNextPortalWrapper(Component) {
724
- return function (props) {
725
- const [mounted, setMounted] = React.useState(false);
726
- React.useEffect(() => {
727
- setMounted(true);
728
- }, [setMounted]);
729
- return mounted ? /*#__PURE__*/reactDom.createPortal( /*#__PURE__*/jsxRuntime.jsx(Component, {
730
- ...props
731
- }), document.body) : null;
732
- };
798
+ const DEFAULT_TYPE = exports.Typography.TITLE_GROUP;
799
+ const titleTypeMapping = {
800
+ [exports.Typography.TITLE_SCREEN]: 'h1',
801
+ [exports.Typography.TITLE_SECTION]: 'h2',
802
+ [exports.Typography.TITLE_SUBSECTION]: 'h3',
803
+ [exports.Typography.TITLE_BODY]: 'h4',
804
+ [exports.Typography.TITLE_GROUP]: 'h5'
805
+ };
806
+ function Title({
807
+ as,
808
+ type = DEFAULT_TYPE,
809
+ className,
810
+ ...props
811
+ }) {
812
+ const mapping = titleTypeMapping[type];
813
+ const isTypeSupported = mapping !== undefined;
814
+ if (isTypeSupported) {
815
+ const HeaderTag = as ?? mapping;
816
+ return /*#__PURE__*/jsxRuntime.jsx(HeaderTag, {
817
+ ...props,
818
+ className: classNames__default.default(`np-text-${type}`, className)
819
+ });
820
+ }
821
+ const HeaderTag = as ?? titleTypeMapping[DEFAULT_TYPE];
822
+ return /*#__PURE__*/jsxRuntime.jsx(HeaderTag, {
823
+ ...props,
824
+ className: classNames__default.default(`np-text-${DEFAULT_TYPE}`, className)
825
+ });
733
826
  }
734
827
 
735
- /**
736
- * Dimmer state management inspired by Material UI's ModalManager (https://github.com/mui-org/material-ui)
737
- */
738
- class DimmerManager {
739
- /**
740
- * Dimmer refs
741
- */
742
- dimmers;
743
- constructor() {
744
- this.dimmers = [];
745
- }
746
- add(dimmer) {
747
- let dimmerIndex = this.dimmers.indexOf(dimmer);
748
- if (dimmerIndex !== -1) {
749
- return dimmerIndex;
750
- }
751
- dimmerIndex = this.dimmers.length;
752
- this.dimmers.push(dimmer);
753
- return dimmerIndex;
754
- }
755
- remove(dimmer) {
756
- const dimmerIndex = this.dimmers.indexOf(dimmer);
757
- if (dimmerIndex !== -1) {
758
- this.dimmers.splice(dimmerIndex, 1);
759
- }
760
- return dimmerIndex;
828
+ function logActionRequired(message) {
829
+ if (['development', 'test'].includes(process?.env?.NODE_ENV)) {
830
+ // eslint-disable-next-line no-console
831
+ console.warn(message);
761
832
  }
762
- isTop(dimmer) {
763
- return this.dimmers.length > 0 && this.dimmers[this.dimmers.length - 1] === dimmer;
833
+ }
834
+ function logActionRequiredIf(message, conditional) {
835
+ if (conditional) {
836
+ logActionRequired(message);
764
837
  }
765
838
  }
766
839
 
767
- const EXIT_ANIMATION$1 = 350;
768
- const dimmerManager = new DimmerManager();
769
- const handleTouchMove = event => {
770
- const isTouchedElementDimmer = event.target.classList.contains('dimmer');
771
- // disable scroll on iOS devices for Dimmer area
772
- // this is because of bug in WebKit https://bugs.webkit.org/show_bug.cgi?id=220908
773
- // note: scrolling still works for children(s) as expected
774
- if (isIosDevice() && isTouchedElementDimmer) {
775
- event.stopPropagation();
776
- event.preventDefault();
777
- }
778
- };
779
- const Dimmer = ({
780
- children,
840
+ const reader = new commonmark__default.default.Parser();
841
+ const writer = new commonmark__default.default.HtmlRenderer({
842
+ safe: true
843
+ });
844
+ const NODE_TYPE_LIST = Object.values(exports.MarkdownNodeType);
845
+ function Markdown({
846
+ as: Element = 'div',
847
+ allowList,
848
+ blockList,
849
+ config,
781
850
  className,
782
- disableClickToClose = false,
783
- contentPosition,
784
- fadeContentOnEnter = false,
785
- fadeContentOnExit = false,
786
- open = false,
787
- scrollable = false,
788
- transparent = false,
789
- onClose
790
- }) => {
791
- const [hasNotExited, setHasNotExited] = React.useState(false);
792
- const dimmerReference = React.useRef(null);
793
- const closeOnClick = event => {
794
- if (event.target === dimmerReference.current) {
795
- onClose?.(event);
796
- }
851
+ children
852
+ }) {
853
+ if (!children) {
854
+ return null;
855
+ }
856
+ const linkTarget = config?.link?.target ?? '_self';
857
+ const paragraphClass = config?.paragraph?.className ?? '';
858
+ if (allowList != null && blockList != null) {
859
+ logActionRequired('Markdown supports only one of `allowList` or `blockList` to be used at a time. `blockList` will be ignored.');
860
+ }
861
+ const parser = nodes => {
862
+ const parsed = reader.parse(nodes);
863
+ const toExclude = allowList != null ? NODE_TYPE_LIST.filter(type => !allowList.includes(type)) : blockList;
864
+ return toExclude != null ? stripNodes({
865
+ parsed,
866
+ blockList: toExclude
867
+ }) : parsed;
797
868
  };
798
- const handleClick = event => {
799
- if (disableClickToClose || !onClose) {
800
- return;
801
- }
802
- closeOnClick(event);
869
+ const createMarkup = () => {
870
+ const parsed = parser(children);
871
+ return writer.render(parsed).replace(/<a href="/g, `<a target="${linkTarget}" href="`).replace(/<p>/g, `<p class="${paragraphClass}">`);
803
872
  };
804
- const handleKeyDown = React.useCallback(event => {
805
- if (event.key !== 'Escape') {
806
- return;
807
- }
808
- event.stopPropagation();
809
- if (onClose && dimmerReference.current && dimmerManager.isTop(dimmerReference.current)) {
810
- onClose(event);
811
- }
812
- }, [onClose]);
813
- const onEnter = () => {
814
- setHasNotExited(true);
815
- if (dimmerReference.current) {
816
- dimmerManager.add(dimmerReference.current);
817
- }
818
- };
819
- const onExited = () => {
820
- setHasNotExited(false);
821
- if (dimmerReference.current) {
822
- dimmerManager.remove(dimmerReference.current);
873
+ return /*#__PURE__*/jsxRuntime.jsx(Element, {
874
+ className: className,
875
+ dangerouslySetInnerHTML: {
876
+ __html: createMarkup()
823
877
  }
824
- };
825
- React.useEffect(() => {
826
- const localReferenceCopy = dimmerReference.current;
827
- if (open) {
828
- document.addEventListener('keydown', handleKeyDown);
829
- localReferenceCopy?.addEventListener('touchmove', handleTouchMove, {
830
- passive: true
831
- });
878
+ });
879
+ }
880
+ function stripNodes({
881
+ blockList,
882
+ parsed
883
+ }) {
884
+ if (!parsed) {
885
+ return parsed;
886
+ }
887
+ const walker = parsed.walker();
888
+ for (let event = walker.next(); event != null; event = walker.next()) {
889
+ const {
890
+ node
891
+ } = event;
892
+ if (blockList.includes(node.type) && !event.entering) {
893
+ while (node.firstChild != null) {
894
+ node.insertBefore(node.firstChild);
895
+ }
896
+ node.unlink();
832
897
  }
833
- return () => {
834
- document.removeEventListener('keydown', handleKeyDown);
835
- localReferenceCopy?.removeEventListener('touchmove', handleTouchMove);
836
- };
837
- }, [handleKeyDown, open]);
838
- return /*#__PURE__*/jsxRuntime.jsx(DimmerWrapper, {
839
- open: open,
840
- hasNotExited: hasNotExited,
841
- children: /*#__PURE__*/jsxRuntime.jsx(reactTransitionGroup.CSSTransition, {
842
- nodeRef: dimmerReference,
843
- in: open,
844
- appear: true
845
- // Wait for animation to finish before unmount.
846
- ,
847
- timeout: {
848
- enter: 0,
849
- exit: EXIT_ANIMATION$1
850
- },
851
- classNames: {
852
- enter: classNames__default.default({
853
- 'dimmer--enter-fade': fadeContentOnEnter
854
- }),
855
- enterDone: classNames__default.default('dimmer--enter-done', {
856
- 'dimmer--enter-fade': fadeContentOnEnter
857
- }),
858
- exit: classNames__default.default('dimmer--exit', {
859
- 'dimmer--exit-fade': fadeContentOnExit
860
- })
861
- },
862
- unmountOnExit: true,
863
- onEnter: onEnter,
864
- onExited: onExited,
865
- children: /*#__PURE__*/jsxRuntime.jsx(DimmerContentWrapper, {
866
- scrollBody: !transparent,
867
- children: /*#__PURE__*/jsxRuntime.jsx(FocusBoundary, {
868
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
869
- ref: dimmerReference,
870
- className: classNames__default.default('dimmer', {
871
- 'dimmer--scrollable': scrollable,
872
- 'dimmer--transparent': transparent
873
- }, className),
874
- role: "presentation",
875
- onClick: handleClick,
876
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
877
- className: classNames__default.default('dimmer-content-positioner', contentPosition != null && ['d-flex justify-content-center', {
878
- 'align-items-start': contentPosition === 'top',
879
- 'align-items-center': contentPosition === 'center',
880
- 'align-items-end': contentPosition === 'bottom'
881
- }]),
882
- children: children
883
- })
884
- })
885
- })
886
- })
887
- })
898
+ }
899
+ return parsed;
900
+ }
901
+
902
+ const allowList = [exports.MarkdownNodeType.STRONG];
903
+ const InlineMarkdown = props => {
904
+ return /*#__PURE__*/jsxRuntime.jsx(Markdown, {
905
+ ...props,
906
+ as: "span",
907
+ allowList: allowList,
908
+ blockList: undefined
888
909
  });
889
910
  };
890
- const DimmerWrapper = ({
891
- open,
892
- hasNotExited,
893
- children
894
- }) => {
895
- const {
896
- screenMode,
897
- theme
898
- } = componentsTheming.useTheme();
899
- return open || hasNotExited ? /*#__PURE__*/jsxRuntime.jsx(componentsTheming.ThemeProvider, {
900
- theme: "personal",
901
- screenMode: theme === 'personal' ? screenMode : 'light',
902
- isNotRootProvider: true,
903
- children: children
904
- }) : /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
905
- children: children
906
- });
911
+ InlineMarkdown.propTypes = {
912
+ children: PropTypes__default.default.string.isRequired,
913
+ className: PropTypes__default.default.string
907
914
  };
908
- const DimmerContentWrapper = ({
909
- children,
910
- scrollBody
911
- }) => {
912
- React.useEffect(() => {
913
- if (scrollBody) {
914
- addNoScrollClass();
915
- }
916
- return () => {
917
- if (scrollBody) {
918
- removeNoScrollClass();
919
- }
920
- };
921
- }, [scrollBody]);
922
- return children;
915
+ InlineMarkdown.defaultProps = {
916
+ className: undefined
923
917
  };
924
- var Dimmer$1 = withNextPortalWrapper(Dimmer);
925
918
 
926
- const POPOVER_OFFSET = [0, 16];
927
- // By default the flip positioning explores only the opposite alternative. So if left is passed and there's no enough space
928
- // the right one gets chosen. If there's no space on both sides popover goes back to the initially chosen one left.
929
- // This mapping forces popover to try the four available positions before going back to the initial chosen one.
930
- const fallbackPlacements = {
931
- [exports.Position.TOP]: [exports.Position.BOTTOM, exports.Position.RIGHT, exports.Position.LEFT],
932
- [exports.Position.BOTTOM]: [exports.Position.TOP, exports.Position.RIGHT, exports.Position.LEFT],
933
- [exports.Position.LEFT]: [exports.Position.RIGHT, exports.Position.TOP, exports.Position.BOTTOM],
934
- [exports.Position.RIGHT]: [exports.Position.LEFT, exports.Position.TOP, exports.Position.BOTTOM]
935
- };
936
- const Panel = /*#__PURE__*/React.forwardRef(({
937
- arrow = false,
938
- flip = true,
939
- altAxis = false,
940
- children,
941
- open = false,
942
- onClose,
943
- position = exports.Position.BOTTOM,
944
- anchorRef,
945
- anchorWidth = false,
946
- ...rest
947
- }, reference) => {
948
- const [arrowElement, setArrowElement] = React.useState(null);
949
- const [popperElement, setPopperElement] = React.useState(null);
950
- const modifiers = [];
951
- if (altAxis) {
952
- modifiers.push({
953
- // https://popper.js.org/docs/v2/modifiers/prevent-overflow
954
- name: 'preventOverflow',
955
- options: {
956
- altAxis: true,
957
- tether: false
958
- }
959
- });
960
- }
961
- if (arrow) {
962
- modifiers.push({
963
- name: 'arrow',
964
- options: {
965
- element: arrowElement,
966
- options: {
967
- padding: 8 // 8px from the edges of the popper
968
- }
969
- }
970
- });
971
- // This lets you displace a popper element from its reference element.
972
- modifiers.push({
973
- name: 'offset',
974
- options: {
975
- offset: POPOVER_OFFSET
976
- }
977
- });
978
- }
979
- if (flip && fallbackPlacements[position]) {
980
- modifiers.push({
981
- name: 'flip',
982
- options: {
983
- fallbackPlacements: fallbackPlacements[position]
984
- }
985
- });
919
+ exports.AlertArrowPosition = void 0;
920
+ (function (AlertArrowPosition) {
921
+ AlertArrowPosition["TOP_LEFT"] = "up-left";
922
+ AlertArrowPosition["TOP"] = "up-center";
923
+ AlertArrowPosition["TOP_RIGHT"] = "up-right";
924
+ AlertArrowPosition["BOTTOM_LEFT"] = "down-left";
925
+ AlertArrowPosition["BOTTOM"] = "down-center";
926
+ AlertArrowPosition["BOTTOM_RIGHT"] = "down-right";
927
+ })(exports.AlertArrowPosition || (exports.AlertArrowPosition = {}));
928
+ function resolveType(type) {
929
+ switch (type) {
930
+ case 'success':
931
+ return 'positive';
932
+ case 'info':
933
+ return 'neutral';
934
+ case 'error':
935
+ return 'negative';
936
+ default:
937
+ return type;
986
938
  }
987
- const {
988
- styles,
989
- attributes,
990
- forceUpdate
991
- } = reactPopper.usePopper(anchorRef.current, popperElement, {
992
- placement: position,
993
- modifiers
994
- });
995
- // If the trigger is not visible when the position is calculated, it will be incorrect. Because this can happen repeatedly (on resize for example),
996
- // it is most simple just to always position before opening
939
+ }
940
+ function Alert({
941
+ arrow,
942
+ action,
943
+ children,
944
+ className,
945
+ dismissible,
946
+ icon,
947
+ onDismiss,
948
+ message,
949
+ size,
950
+ title,
951
+ type = 'neutral',
952
+ variant = 'desktop'
953
+ }) {
997
954
  React.useEffect(() => {
998
- if (open && forceUpdate) {
999
- forceUpdate();
955
+ if (arrow !== undefined) {
956
+ logActionRequired("Alert component doesn't support 'arrow' anymore, use 'InlineAlert' instead.");
1000
957
  }
1001
- }, [open]);
1002
- const contentStyle = {
1003
- ...(anchorWidth ? {
1004
- width: anchorRef.current?.clientWidth
1005
- } : undefined)
1006
- };
1007
- return /*#__PURE__*/jsxRuntime.jsx(Dimmer$1, {
1008
- open: open,
1009
- transparent: true,
1010
- fadeContentOnEnter: true,
1011
- fadeContentOnExit: true,
1012
- onClose: onClose,
1013
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
1014
- ...rest,
1015
- ref: setPopperElement,
1016
- role: "dialog"
1017
- // eslint-disable-next-line react/forbid-dom-props
1018
- ,
1019
- style: {
1020
- ...styles.popper
1021
- },
1022
- ...attributes.popper,
1023
- className: classNames__default.default('np-panel', {
1024
- 'np-panel--open': open
1025
- }, rest['className']),
1026
- children: /*#__PURE__*/jsxRuntime.jsxs("div", {
1027
- ref: reference
1028
- /* eslint-disable-next-line react/forbid-dom-props */,
1029
- style: contentStyle,
1030
- className: classNames__default.default('np-panel__content'),
1031
- children: [children, arrow && /*#__PURE__*/jsxRuntime.jsx("div", {
1032
- ref: setArrowElement,
1033
- className: classNames__default.default('np-panel__arrow')
1034
- // eslint-disable-next-line react/forbid-dom-props
1035
- ,
1036
- style: styles.arrow
958
+ }, [arrow]);
959
+ React.useEffect(() => {
960
+ if (children !== undefined) {
961
+ logActionRequired("Alert component doesn't support 'children' anymore, use 'message' instead.");
962
+ }
963
+ }, [children]);
964
+ React.useEffect(() => {
965
+ if (dismissible !== undefined) {
966
+ logActionRequired("Alert component doesn't support 'dismissible' anymore, use 'onDismiss' instead.");
967
+ }
968
+ }, [dismissible]);
969
+ React.useEffect(() => {
970
+ if (size !== undefined) {
971
+ logActionRequired("Alert component doesn't support 'size' anymore, please remove that prop.");
972
+ }
973
+ }, [size]);
974
+ const resolvedType = resolveType(type);
975
+ React.useEffect(() => {
976
+ if (resolvedType !== type) {
977
+ logActionRequired(`Alert component has deprecated '${type}' value for the 'type' prop. Please use '${resolvedType}' instead.`);
978
+ }
979
+ }, [resolvedType, type]);
980
+ const [shouldFire, setShouldFire] = React.useState(false);
981
+ const closeButtonReference = React.useRef(null);
982
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
983
+ className: classNames__default.default('alert d-flex', `alert-${resolvedType}`, arrow != null && alertArrowClassNames(arrow), className),
984
+ "data-testid": "alert",
985
+ onTouchStart: () => setShouldFire(true),
986
+ onTouchEnd: event => {
987
+ if (shouldFire && action &&
988
+ // Check if current event is triggered from closeButton
989
+ event.target instanceof Node && closeButtonReference.current && !closeButtonReference.current.contains(event.target)) {
990
+ if (action.target === '_blank') {
991
+ window.top?.open(action.href);
992
+ } else {
993
+ window.top?.location.assign(action.href);
994
+ }
995
+ }
996
+ setShouldFire(false);
997
+ },
998
+ onTouchMove: () => setShouldFire(false),
999
+ children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
1000
+ className: classNames__default.default('alert__content', 'd-flex', 'flex-grow-1', variant),
1001
+ "data-testid": variant,
1002
+ children: [icon ? /*#__PURE__*/jsxRuntime.jsx("div", {
1003
+ className: "alert__icon",
1004
+ children: icon
1005
+ }) : /*#__PURE__*/jsxRuntime.jsx(StatusIcon, {
1006
+ size: exports.Size.LARGE,
1007
+ sentiment: resolvedType
1008
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
1009
+ className: "alert__message",
1010
+ children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
1011
+ role: exports.Sentiment.NEGATIVE === resolvedType ? 'alert' : 'status',
1012
+ children: [title && /*#__PURE__*/jsxRuntime.jsx(Title, {
1013
+ className: "m-b-1",
1014
+ type: exports.Typography.TITLE_BODY,
1015
+ children: title
1016
+ }), /*#__PURE__*/jsxRuntime.jsx(Body, {
1017
+ as: "span",
1018
+ className: "d-block",
1019
+ type: exports.Typography.BODY_LARGE,
1020
+ children: children || /*#__PURE__*/jsxRuntime.jsx(InlineMarkdown, {
1021
+ children: message
1022
+ })
1023
+ })]
1024
+ }), action && /*#__PURE__*/jsxRuntime.jsx(Link, {
1025
+ href: action.href,
1026
+ className: "m-t-1",
1027
+ "aria-label": action['aria-label'],
1028
+ target: action.target,
1029
+ type: exports.Typography.LINK_LARGE,
1030
+ children: action.text
1037
1031
  })]
1038
- })
1039
- })
1032
+ })]
1033
+ }), onDismiss && /*#__PURE__*/jsxRuntime.jsx(CloseButton, {
1034
+ ref: closeButtonReference,
1035
+ className: "m-l-2",
1036
+ onClick: onDismiss
1037
+ })]
1040
1038
  });
1041
- });
1039
+ }
1040
+ function alertArrowClassNames(arrow) {
1041
+ switch (arrow) {
1042
+ case 'down-center':
1043
+ return 'arrow arrow-bottom arrow-center';
1044
+ case 'down-left':
1045
+ return 'arrow arrow-bottom arrow-left';
1046
+ case 'down-right':
1047
+ return 'arrow arrow-bottom arrow-right';
1048
+ case 'up-center':
1049
+ return 'arrow arrow-center';
1050
+ case 'up-right':
1051
+ return 'arrow arrow-right';
1052
+ case 'up-left':
1053
+ default:
1054
+ return 'arrow';
1055
+ }
1056
+ }
1042
1057
 
1043
- const Section = ({
1044
- children,
1058
+ // TODO: consider to move this enum into component file once we migrate it on TypeScript or replace with some common enum
1059
+ exports.AvatarType = void 0;
1060
+ (function (AvatarType) {
1061
+ AvatarType["THUMBNAIL"] = "thumbnail";
1062
+ AvatarType["ICON"] = "icon";
1063
+ AvatarType["EMOJI"] = "emoji";
1064
+ AvatarType["INITIALS"] = "initials";
1065
+ })(exports.AvatarType || (exports.AvatarType = {}));
1066
+
1067
+ /*
1068
+ * The colors we support generating an Avatar background color from a "seed" for.
1069
+ * Changing this array will change the assignment between seeds.
1070
+ * Do not change this array.
1071
+ */
1072
+ const avatarColors = ['--color-bright-blue', '--color-bright-yellow', '--color-bright-pink', '--color-bright-orange'];
1073
+ /*
1074
+ * Takes in a "seed" string and spits out an index for the color we should use.
1075
+ * This implementation has been synced across all three clients so that we consistently
1076
+ * generate branded avatar colors when rendering a list of Avatars.
1077
+ * Do not change this implementation.
1078
+ */
1079
+ const hashSeed = seed => {
1080
+ const base = 31;
1081
+ const modulo = avatarColors.length;
1082
+ let hashValue = 0;
1083
+ let basePow = 1;
1084
+ for (let i = 0; i < seed.length; i += 1) {
1085
+ hashValue = (hashValue + seed.charCodeAt(i) * basePow) % modulo;
1086
+ basePow = basePow * base % modulo;
1087
+ }
1088
+ return hashValue;
1089
+ };
1090
+ const getAvatarColorFromSeed = seed => {
1091
+ return avatarColors[hashSeed(seed)];
1092
+ };
1093
+
1094
+ const backwardsCompatibleSize = size => {
1095
+ switch (size) {
1096
+ case 'sm':
1097
+ return 24;
1098
+ case 'md':
1099
+ return 48;
1100
+ case 'lg':
1101
+ return 72;
1102
+ default:
1103
+ return size;
1104
+ }
1105
+ };
1106
+ const Avatar = ({
1107
+ backgroundColor = null,
1108
+ backgroundColorSeed = null,
1109
+ children = null,
1045
1110
  className,
1046
- withHorizontalPadding = false
1111
+ outlined = false,
1112
+ size: sizeFromProps = 48,
1113
+ theme = exports.Theme.LIGHT,
1114
+ type = 'thumbnail'
1047
1115
  }) => {
1116
+ const backgroundColorFromSeed = React.useMemo(() => !backgroundColor && backgroundColorSeed ? `var(${getAvatarColorFromSeed(backgroundColorSeed)})` : undefined, [backgroundColor, backgroundColorSeed]);
1117
+ const size = backwardsCompatibleSize(sizeFromProps);
1048
1118
  return /*#__PURE__*/jsxRuntime.jsx("div", {
1049
- className: classNames__default.default('np-section', className, {
1050
- 'np-section--with-horizontal-padding': withHorizontalPadding
1119
+ className: classNames__default.default('tw-avatar', className, `tw-avatar--${size}`, `tw-avatar--${type}`, {
1120
+ 'tw-avatar--outlined': outlined,
1121
+ 'tw-avatar--branded': Boolean(backgroundColorFromSeed),
1122
+ 'np-text-title-body': type === 'initials'
1051
1123
  }),
1052
- children: children
1124
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
1125
+ className: "tw-avatar__content",
1126
+ style: {
1127
+ backgroundColor: backgroundColor || backgroundColorFromSeed
1128
+ },
1129
+ children: children
1130
+ })
1053
1131
  });
1054
1132
  };
1055
1133
 
1056
- const radius = {
1057
- xxs: 6,
1058
- xs: 11,
1059
- sm: 22,
1060
- xl: 61
1134
+ const Badge = ({
1135
+ badge,
1136
+ className = undefined,
1137
+ size = exports.Size.SMALL,
1138
+ border = exports.Theme.LIGHT,
1139
+ children
1140
+ }) => {
1141
+ const classes = classNames__default.default('tw-badge', {
1142
+ [`tw-badge-border-${border}`]: border,
1143
+ [`tw-badge-${size}`]: size
1144
+ }, className);
1145
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
1146
+ className: classes,
1147
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
1148
+ className: "tw-badge__children",
1149
+ children: children
1150
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
1151
+ className: "tw-badge__content",
1152
+ children: badge
1153
+ })]
1154
+ });
1061
1155
  };
1062
- const ANIMATION_DURATION_IN_MS = 1500;
1063
- class ProcessIndicator extends React.Component {
1064
- constructor(props) {
1065
- super(props);
1066
- this.state = {
1067
- status: props.status,
1068
- size: props.size
1069
- };
1070
- this.interval = null;
1071
- this.timeout = null;
1072
- }
1073
1156
 
1074
- /**
1075
- * Create interval for animation duration (1500ms)
1076
- * Update state only at the end of every interval
1077
- * (end of animation loop) if props changed before end of animation
1078
- */
1079
- componentDidMount() {
1157
+ const OptionalBadge = ({
1158
+ url,
1159
+ altText,
1160
+ statusIcon,
1161
+ children,
1162
+ ...rest
1163
+ }) => {
1164
+ if (url) {
1165
+ return /*#__PURE__*/jsxRuntime.jsx(Badge, {
1166
+ badge: /*#__PURE__*/jsxRuntime.jsx("img", {
1167
+ src: url,
1168
+ alt: altText
1169
+ }),
1170
+ ...rest,
1171
+ children: children
1172
+ });
1173
+ }
1174
+ if (statusIcon) {
1175
+ return /*#__PURE__*/jsxRuntime.jsx(Badge, {
1176
+ badge: /*#__PURE__*/jsxRuntime.jsx(StatusIcon, {
1177
+ sentiment: statusIcon,
1178
+ size: exports.Size.SMALL
1179
+ }),
1180
+ ...rest,
1181
+ children: children
1182
+ });
1183
+ }
1184
+ return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
1185
+ children: children
1186
+ });
1187
+ };
1188
+ const AvatarWrapper = ({
1189
+ url,
1190
+ profileType,
1191
+ profileId,
1192
+ badgeUrl,
1193
+ badgeAltText,
1194
+ badgeStatusIcon,
1195
+ name,
1196
+ avatarProps,
1197
+ badgeProps
1198
+ }) => {
1199
+ const [hasImageLoadError, setImageLoadError] = React.useState(false);
1200
+ const isBusinessProfile = profileType === exports.ProfileType.BUSINESS;
1201
+ // Reset the errored state when url changes
1202
+ React.useEffect(() => setImageLoadError(false), [url]);
1203
+ const getAvatarProps = () => {
1204
+ if (url && !hasImageLoadError) {
1205
+ return {
1206
+ type: exports.AvatarType.THUMBNAIL,
1207
+ children: /*#__PURE__*/jsxRuntime.jsx("img", {
1208
+ src: url,
1209
+ alt: "",
1210
+ onError: () => setImageLoadError(true)
1211
+ }),
1212
+ ...avatarProps
1213
+ };
1214
+ }
1215
+ if (profileType) {
1216
+ return {
1217
+ type: exports.AvatarType.ICON,
1218
+ children: isBusinessProfile ? /*#__PURE__*/jsxRuntime.jsx(icons.Briefcase, {
1219
+ size: "24"
1220
+ }) : /*#__PURE__*/jsxRuntime.jsx(icons.Person, {
1221
+ size: "24"
1222
+ }),
1223
+ ...avatarProps
1224
+ };
1225
+ }
1226
+ if (name) {
1227
+ return {
1228
+ type: exports.AvatarType.INITIALS,
1229
+ children: /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
1230
+ children: getInitials(name)
1231
+ }),
1232
+ backgroundColorSeed: profileId?.toString(),
1233
+ ...avatarProps
1234
+ };
1235
+ }
1236
+ return {
1237
+ type: exports.AvatarType.ICON,
1238
+ children: /*#__PURE__*/jsxRuntime.jsx(icons.Person, {
1239
+ size: "24"
1240
+ }),
1241
+ ...avatarProps
1242
+ };
1243
+ };
1244
+ return /*#__PURE__*/jsxRuntime.jsx(OptionalBadge, {
1245
+ url: badgeUrl,
1246
+ altText: badgeAltText,
1247
+ statusIcon: badgeStatusIcon,
1248
+ ...badgeProps,
1249
+ children: /*#__PURE__*/jsxRuntime.jsx(Avatar, {
1250
+ size: exports.Size.MEDIUM,
1251
+ ...getAvatarProps()
1252
+ })
1253
+ });
1254
+ };
1255
+ function getInitials(name) {
1256
+ const allInitials = name.split(' ').map(part => part[0]).join('').toUpperCase();
1257
+ if (allInitials.length === 1) {
1258
+ return allInitials[0];
1259
+ }
1260
+ return allInitials[0] + allInitials.slice(-1);
1261
+ }
1262
+
1263
+ const radius = {
1264
+ xxs: 6,
1265
+ xs: 11,
1266
+ sm: 22,
1267
+ xl: 61
1268
+ };
1269
+ const ANIMATION_DURATION_IN_MS = 1500;
1270
+ class ProcessIndicator extends React.Component {
1271
+ constructor(props) {
1272
+ super(props);
1273
+ this.state = {
1274
+ status: props.status,
1275
+ size: props.size
1276
+ };
1277
+ this.interval = null;
1278
+ this.timeout = null;
1279
+ }
1280
+
1281
+ /**
1282
+ * Create interval for animation duration (1500ms)
1283
+ * Update state only at the end of every interval
1284
+ * (end of animation loop) if props changed before end of animation
1285
+ */
1286
+ componentDidMount() {
1080
1287
  this.interval = setInterval(() => {
1081
1288
  const statusFromState = this.state.status;
1082
1289
  const sizeFromState = this.state.size;
@@ -1176,7 +1383,7 @@ ProcessIndicator.defaultProps = {
1176
1383
  };
1177
1384
  var ProcessIndicator$1 = ProcessIndicator;
1178
1385
 
1179
- var messages$d = reactIntl.defineMessages({
1386
+ var messages$b = reactIntl.defineMessages({
1180
1387
  loadingAriaLabel: {
1181
1388
  id: "neptune.Button.loadingAriaLabel"
1182
1389
  }
@@ -1193,18 +1400,6 @@ const priorityClassMap = {
1193
1400
  [exports.Priority.TERTIARY]: 'btn-priority-3'
1194
1401
  };
1195
1402
 
1196
- function logActionRequired(message) {
1197
- if (['development', 'test'].includes(process?.env?.NODE_ENV)) {
1198
- // eslint-disable-next-line no-console
1199
- console.warn(message);
1200
- }
1201
- }
1202
- function logActionRequiredIf(message, conditional) {
1203
- if (conditional) {
1204
- logActionRequired(message);
1205
- }
1206
- }
1207
-
1208
1403
  const deprecatedTypeMap = {
1209
1404
  [exports.Type.PRIMARY]: exports.ControlType.ACCENT,
1210
1405
  [exports.Type.SECONDARY]: exports.ControlType.ACCENT,
@@ -1307,7 +1502,7 @@ const Button = /*#__PURE__*/React.forwardRef(({
1307
1502
  className: classes,
1308
1503
  ...props,
1309
1504
  "aria-live": loading ? 'polite' : 'off',
1310
- "aria-label": loading ? intl.formatMessage(messages$d.loadingAriaLabel) : rest['aria-label'],
1505
+ "aria-label": loading ? intl.formatMessage(messages$b.loadingAriaLabel) : rest['aria-label'],
1311
1506
  children: [children, loading && /*#__PURE__*/jsxRuntime.jsx(ProcessIndicator$1, {
1312
1507
  size: processIndicatorSize(),
1313
1508
  className: "btn-loader"
@@ -1315,1505 +1510,972 @@ const Button = /*#__PURE__*/React.forwardRef(({
1315
1510
  });
1316
1511
  });
1317
1512
 
1318
- var messages$c = reactIntl.defineMessages({
1319
- opensInNewTab: {
1320
- id: "neptune.Link.opensInNewTab"
1321
- }
1322
- });
1323
-
1324
- const Link = ({
1325
- className,
1326
- children,
1327
- href,
1328
- target,
1329
- type,
1330
- 'aria-label': ariaLabel,
1331
- onClick,
1332
- ...props
1333
- }) => {
1334
- const isBlank = target === '_blank';
1513
+ const Card$1 = /*#__PURE__*/React.forwardRef((props, reference) => {
1335
1514
  const {
1336
- formatMessage
1337
- } = reactIntl.useIntl();
1338
- return /*#__PURE__*/jsxRuntime.jsxs("a", {
1339
- href: href,
1340
- target: target,
1341
- className: classNames__default.default('np-link', type ? `np-text-${type}` : undefined, 'd-inline-flex', className),
1342
- "aria-label": ariaLabel,
1343
- rel: isBlank ? 'noreferrer' : undefined,
1344
- onClick: onClick,
1345
- ...props,
1346
- children: [children, " ", isBlank && /*#__PURE__*/jsxRuntime.jsx(icons.NavigateAway, {
1347
- title: formatMessage(messages$c.opensInNewTab)
1515
+ 'aria-label': ariaLabel,
1516
+ as: Element,
1517
+ isExpanded,
1518
+ title,
1519
+ details,
1520
+ children,
1521
+ onClick,
1522
+ icon,
1523
+ id,
1524
+ className,
1525
+ ...rest
1526
+ } = props;
1527
+ const isOpen = !!(isExpanded && children);
1528
+ return /*#__PURE__*/jsxRuntime.jsxs(Element, {
1529
+ ref: reference,
1530
+ className: classNames__default.default('np-card', className, {
1531
+ 'np-card--expanded': isOpen,
1532
+ 'np-card--inactive': !children,
1533
+ 'np-card--has-icon': !!icon
1534
+ }),
1535
+ id: id,
1536
+ "data-testid": rest['data-testid'],
1537
+ children: [/*#__PURE__*/jsxRuntime.jsx(Option$2, {
1538
+ "aria-label": ariaLabel,
1539
+ as: children ? 'button' : 'div',
1540
+ className: classNames__default.default('np-card__button'),
1541
+ media: icon,
1542
+ title: title,
1543
+ content: details,
1544
+ showMediaAtAllSizes: true,
1545
+ button: children && /*#__PURE__*/jsxRuntime.jsx(Chevron, {
1546
+ orientation: isOpen ? exports.Position.TOP : exports.Position.BOTTOM
1547
+ }),
1548
+ onClick: () => children && onClick(!isExpanded)
1549
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
1550
+ className: classNames__default.default('np-card__divider', {
1551
+ 'np-card__divider--expanded': isOpen
1552
+ })
1553
+ }), isOpen && /*#__PURE__*/jsxRuntime.jsx(Body, {
1554
+ as: "span",
1555
+ type: exports.Typography.BODY_LARGE,
1556
+ className: "np-card__content d-block",
1557
+ children: children
1348
1558
  })]
1349
1559
  });
1560
+ });
1561
+ const hasChildren = ({
1562
+ children
1563
+ }) => children;
1564
+ Card$1.propTypes = {
1565
+ 'aria-label': PropTypes__default.default.string,
1566
+ as: PropTypes__default.default.string,
1567
+ isExpanded: requiredIf__default.default(PropTypes__default.default.bool, hasChildren),
1568
+ title: PropTypes__default.default.node.isRequired,
1569
+ details: PropTypes__default.default.node.isRequired,
1570
+ onClick: requiredIf__default.default(PropTypes__default.default.func, hasChildren),
1571
+ icon: PropTypes__default.default.node,
1572
+ children: PropTypes__default.default.node,
1573
+ id: PropTypes__default.default.string,
1574
+ className: PropTypes__default.default.string,
1575
+ 'data-testid': PropTypes__default.default.string
1350
1576
  };
1351
-
1352
- const DEFAULT_TYPE = exports.Typography.TITLE_GROUP;
1353
- const titleTypeMapping = {
1354
- [exports.Typography.TITLE_SCREEN]: 'h1',
1355
- [exports.Typography.TITLE_SECTION]: 'h2',
1356
- [exports.Typography.TITLE_SUBSECTION]: 'h3',
1357
- [exports.Typography.TITLE_BODY]: 'h4',
1358
- [exports.Typography.TITLE_GROUP]: 'h5'
1577
+ Card$1.defaultProps = {
1578
+ 'aria-label': undefined,
1579
+ as: 'div',
1580
+ children: null,
1581
+ id: null,
1582
+ className: null,
1583
+ 'data-testid': null
1359
1584
  };
1360
- function Title({
1361
- as,
1362
- type = DEFAULT_TYPE,
1585
+ var Card$2 = Card$1;
1586
+
1587
+ const CheckboxButton = /*#__PURE__*/React.forwardRef(({
1588
+ checked,
1363
1589
  className,
1364
- ...props
1365
- }) {
1366
- const mapping = titleTypeMapping[type];
1367
- const isTypeSupported = mapping !== undefined;
1368
- if (isTypeSupported) {
1369
- const HeaderTag = as ?? mapping;
1370
- return /*#__PURE__*/jsxRuntime.jsx(HeaderTag, {
1371
- ...props,
1372
- className: classNames__default.default(`np-text-${type}`, className)
1373
- });
1374
- }
1375
- const HeaderTag = as ?? titleTypeMapping[DEFAULT_TYPE];
1376
- return /*#__PURE__*/jsxRuntime.jsx(HeaderTag, {
1377
- ...props,
1378
- className: classNames__default.default(`np-text-${DEFAULT_TYPE}`, className)
1379
- });
1380
- }
1590
+ disabled,
1591
+ onChange,
1592
+ ...rest
1593
+ }, reference) => /*#__PURE__*/jsxRuntime.jsxs("span", {
1594
+ className: classNames__default.default('np-checkbox-button', className, disabled && 'disabled'),
1595
+ children: [/*#__PURE__*/jsxRuntime.jsx("input", {
1596
+ ...rest,
1597
+ ref: reference,
1598
+ type: "checkbox",
1599
+ disabled: disabled,
1600
+ checked: checked,
1601
+ onChange: onChange
1602
+ }), /*#__PURE__*/jsxRuntime.jsx("span", {
1603
+ className: "tw-checkbox-button",
1604
+ children: /*#__PURE__*/jsxRuntime.jsx("span", {
1605
+ className: "tw-checkbox-check"
1606
+ })
1607
+ })]
1608
+ }));
1609
+ var CheckboxButton$1 = CheckboxButton;
1381
1610
 
1382
- const HeaderAction = ({
1383
- action
1611
+ const Checkbox = ({
1612
+ id,
1613
+ checked,
1614
+ required,
1615
+ disabled,
1616
+ readOnly,
1617
+ label,
1618
+ className,
1619
+ secondary,
1620
+ onChange,
1621
+ onFocus,
1622
+ onBlur
1384
1623
  }) => {
1385
1624
  const {
1386
1625
  isModern
1387
1626
  } = componentsTheming.useTheme();
1388
- const props = {
1389
- 'aria-label': action['aria-label']
1390
- };
1391
- if ('href' in action) {
1392
- return /*#__PURE__*/jsxRuntime.jsx(Link, {
1393
- href: action.href,
1394
- target: action.target,
1395
- onClick: action.onClick,
1396
- ...props,
1397
- children: action.text
1398
- });
1399
- }
1400
- return isModern ? /*#__PURE__*/jsxRuntime.jsx(Button, {
1401
- className: "np-header__button",
1402
- priority: "tertiary",
1403
- size: "sm",
1404
- onClick: action.onClick,
1405
- ...props,
1406
- children: action.text
1407
- }) : /*#__PURE__*/jsxRuntime.jsx(ActionButton, {
1408
- onClick: action.onClick,
1409
- ...props,
1410
- children: action.text
1627
+ const classList = classNames__default.default('np-checkbox', {
1628
+ checkbox: true,
1629
+ 'checkbox-lg': secondary,
1630
+ disabled: isModern && disabled
1631
+ }, className);
1632
+ const innerDisabled = disabled || readOnly;
1633
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
1634
+ id: id,
1635
+ className: classList,
1636
+ children: /*#__PURE__*/jsxRuntime.jsxs("label", {
1637
+ className: classNames__default.default({
1638
+ disabled
1639
+ }),
1640
+ children: [/*#__PURE__*/jsxRuntime.jsx(CheckboxButton$1, {
1641
+ className: "p-r-2",
1642
+ checked: checked,
1643
+ disabled: innerDisabled,
1644
+ required: !innerDisabled && required,
1645
+ onFocus: onFocus,
1646
+ onChange: () => onChange(!checked),
1647
+ onBlur: onBlur
1648
+ }), /*#__PURE__*/jsxRuntime.jsxs(Body, {
1649
+ as: "span",
1650
+ className: "np-checkbox__text",
1651
+ type: secondary ? exports.Typography.BODY_LARGE_BOLD : exports.Typography.BODY_LARGE,
1652
+ children: [/*#__PURE__*/jsxRuntime.jsx("span", {
1653
+ className: required ? 'has-required' : undefined,
1654
+ children: label
1655
+ }), secondary && /*#__PURE__*/jsxRuntime.jsx(Body, {
1656
+ as: "span",
1657
+ className: classNames__default.default({
1658
+ secondary: !isModern
1659
+ }),
1660
+ children: secondary
1661
+ })]
1662
+ })]
1663
+ })
1411
1664
  });
1412
1665
  };
1413
- /**
1414
- *
1415
- * Neptune Web: https://transferwise.github.io/neptune-web/components/content/Header
1416
- *
1417
- */
1418
- const Header = ({
1419
- action,
1420
- as = 'h5',
1421
- title,
1422
- className
1423
- }) => {
1424
- if (!action) {
1425
- return /*#__PURE__*/jsxRuntime.jsx(Title, {
1426
- as: as,
1427
- type: exports.Typography.TITLE_GROUP,
1428
- className: classNames__default.default('np-header', 'np-header__title', className),
1429
- children: title
1430
- });
1431
- }
1432
- if (as === 'legend') {
1433
- // eslint-disable-next-line no-console
1434
- console.warn('Legends should be the first child in a fieldset, and this is not possible when including an action');
1435
- }
1436
- return /*#__PURE__*/jsxRuntime.jsxs("div", {
1437
- className: classNames__default.default('np-header', className),
1438
- children: [/*#__PURE__*/jsxRuntime.jsx(Title, {
1439
- as: as,
1440
- type: exports.Typography.TITLE_GROUP,
1441
- className: "np-header__title",
1442
- children: title
1443
- }), /*#__PURE__*/jsxRuntime.jsx(HeaderAction, {
1444
- action: action
1445
- })]
1446
- });
1666
+ Checkbox.propTypes = {
1667
+ id: PropTypes__default.default.string,
1668
+ checked: PropTypes__default.default.bool,
1669
+ required: PropTypes__default.default.bool,
1670
+ disabled: PropTypes__default.default.bool,
1671
+ readOnly: PropTypes__default.default.bool,
1672
+ label: PropTypes__default.default.node.isRequired,
1673
+ secondary: PropTypes__default.default.string,
1674
+ onFocus: PropTypes__default.default.func,
1675
+ onChange: PropTypes__default.default.func.isRequired,
1676
+ onBlur: PropTypes__default.default.func,
1677
+ className: PropTypes__default.default.string
1447
1678
  };
1448
-
1449
- const useConditionalListener = ({
1450
- attachListener,
1451
- callback,
1452
- eventType,
1453
- parent
1454
- }) => {
1455
- React.useEffect(() => {
1456
- if (attachListener && !neptuneValidation.isUndefined(parent)) {
1457
- parent.addEventListener(eventType, callback, true);
1458
- }
1459
- return () => {
1460
- if (!neptuneValidation.isUndefined(parent)) {
1461
- parent.removeEventListener(eventType, callback, true);
1462
- }
1463
- };
1464
- }, [attachListener, callback, eventType, parent]);
1679
+ Checkbox.defaultProps = {
1680
+ id: null,
1681
+ checked: false,
1682
+ required: false,
1683
+ disabled: false,
1684
+ readOnly: false,
1685
+ secondary: null,
1686
+ onFocus: null,
1687
+ onBlur: null,
1688
+ className: undefined
1465
1689
  };
1466
1690
 
1467
- const DirectionContext = /*#__PURE__*/React.createContext(exports.Direction.LTR);
1468
- const DirectionProvider = ({
1469
- direction,
1470
- children
1471
- }) => {
1472
- return /*#__PURE__*/jsxRuntime.jsx(DirectionContext.Provider, {
1473
- value: direction,
1474
- children: children
1691
+ const CheckboxOption = /*#__PURE__*/React.forwardRef(({
1692
+ checked,
1693
+ disabled,
1694
+ onChange,
1695
+ ...rest
1696
+ }, reference) => {
1697
+ return /*#__PURE__*/jsxRuntime.jsx(Option$2, {
1698
+ ...rest,
1699
+ ref: reference,
1700
+ disabled: disabled,
1701
+ button: /*#__PURE__*/jsxRuntime.jsx(CheckboxButton$1, {
1702
+ checked: checked,
1703
+ disabled: disabled,
1704
+ onChange: () => onChange?.(!checked)
1705
+ })
1475
1706
  });
1476
- };
1477
-
1478
- const useDirection = () => {
1479
- const direction = React.useContext(DirectionContext);
1480
- return {
1481
- direction,
1482
- isRTL: direction === 'rtl'
1483
- };
1484
- };
1485
-
1486
- const ObserverParams = {
1487
- threshold: 0.1
1488
- };
1707
+ });
1489
1708
 
1490
- /**
1491
- * useHasIntersected.
1492
- * Use this custom hook to detect when an element has became visible inside the viewport. This hook checks only if the intersection happend.
1493
- * Once the intersection has happened the hook will not return false even if the element gets out of the viewport.
1494
- *
1495
- * @param elRef.elRef
1496
- * @param {object} [elRef] - node object that contains a react reference to the element that needs to be observed.
1497
- * @param {strimng} [loading = 'eager'] - string that contains the type of loading.
1498
- * @param elRef.loading
1499
- * @usage `const [hasIntersected] = useHasIntersected({imageRef,loading});`
1500
- */
1501
- const useHasIntersected = ({
1502
- elRef,
1503
- loading
1709
+ const Chip = ({
1710
+ label,
1711
+ value,
1712
+ onRemove,
1713
+ onClick,
1714
+ onKeyPress,
1715
+ className = undefined,
1716
+ 'aria-label': ariaLabel,
1717
+ 'aria-checked': ariaChecked,
1718
+ role,
1719
+ closeButton
1504
1720
  }) => {
1505
- const [hasIntersected, setHasIntersected] = React.useState(false);
1721
+ const isActionable = onClick || onKeyPress;
1722
+ const defaultRole = isActionable ? 'button' : undefined;
1723
+ const tabIndex = isActionable ? 0 : -1;
1506
1724
  const {
1507
- current
1508
- } = elRef || {};
1509
- const isValidReference = () => {
1510
- return elRef && current;
1511
- };
1512
- const handleOnIntersect = (entries, observer) => {
1513
- entries.forEach(entry => {
1514
- if (entry.isIntersecting) {
1515
- setHasIntersected(true);
1516
- observer.unobserve(current);
1517
- }
1518
- });
1519
- };
1725
+ isModern
1726
+ } = componentsTheming.useTheme();
1727
+ const closeButtonReference = React.useRef(null);
1728
+ const previousCloseButtonShown = React.useRef();
1520
1729
  React.useEffect(() => {
1521
- let observer;
1522
- let didCancel = false;
1523
-
1524
- // Check if window is define for SSR and Old browsers fallback
1525
- if (typeof window === 'undefined' || !window.IntersectionObserver || !isValidReference()) {
1526
- setHasIntersected(true);
1527
- } else if (!didCancel) {
1528
- observer = new IntersectionObserver(handleOnIntersect, ObserverParams);
1529
- observer.observe(current);
1730
+ if (closeButtonReference.current != null && previousCloseButtonShown.current === false) {
1731
+ closeButtonReference.current?.focus();
1530
1732
  }
1531
- return () => {
1532
- didCancel = true;
1533
- if (observer) {
1534
- observer.unobserve(current);
1535
- }
1536
- };
1537
- }, [elRef]);
1538
- if (loading === 'eager') {
1539
- return [false];
1540
- }
1541
- return [hasIntersected];
1542
- };
1543
-
1544
- // eslint-disable-next-line import/extensions
1545
- function useMedia(query) {
1546
- return index_js.useSyncExternalStore(onStoreChange => {
1547
- const mediaQueryList = window.matchMedia(query);
1548
- mediaQueryList.addEventListener('change', onStoreChange);
1549
- return () => {
1550
- mediaQueryList.removeEventListener('change', onStoreChange);
1551
- };
1552
- }, () => typeof window !== 'undefined' ? window.matchMedia(query).matches : undefined, () => undefined);
1553
- }
1554
-
1555
- function useScreenSize(size) {
1556
- return useMedia(`(min-width: ${size}px)`);
1557
- }
1558
-
1559
- /**
1560
- * @deprecated Prefer `useScreenSize` instead.
1561
- */
1562
- const useLayout = () => {
1563
- const screenXs = useScreenSize(exports.Breakpoint.EXTRA_SMALL);
1564
- const screenSm = useScreenSize(exports.Breakpoint.SMALL);
1565
- const screenMd = useScreenSize(exports.Breakpoint.MEDIUM);
1566
- const screenLg = useScreenSize(exports.Breakpoint.LARGE);
1567
- const screenXl = useScreenSize(exports.Breakpoint.EXTRA_LARGE);
1568
- return {
1569
- isMobile: screenSm != null ? !screenSm : undefined,
1570
- isExtraSmall: screenXs,
1571
- isSmall: screenSm,
1572
- isMedium: screenMd,
1573
- isLarge: screenLg,
1574
- isExtraLarge: screenXl
1575
- };
1733
+ previousCloseButtonShown.current = closeButtonReference.current != null;
1734
+ }, [onRemove]);
1735
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
1736
+ role: role ?? defaultRole,
1737
+ tabIndex: tabIndex,
1738
+ "aria-label": ariaLabel,
1739
+ "aria-checked": ariaChecked,
1740
+ className: classNames__default.default('np-chip', 'd-flex', 'align-items-center', 'justify-content-between', onRemove ? 'np-chip--removable' : '', className),
1741
+ ...(isActionable && {
1742
+ onClick,
1743
+ onKeyPress
1744
+ }),
1745
+ children: [isModern ? /*#__PURE__*/jsxRuntime.jsx(Body, {
1746
+ "aria-hidden": !!onRemove,
1747
+ type: exports.Typography.BODY_DEFAULT_BOLD,
1748
+ children: label
1749
+ }) : /*#__PURE__*/jsxRuntime.jsx("span", {
1750
+ "aria-hidden": "false",
1751
+ className: "np-chip-label",
1752
+ children: label
1753
+ }), onRemove ? /*#__PURE__*/jsxRuntime.jsx(CloseButton, {
1754
+ ref: closeButtonReference,
1755
+ className: isModern ? `btn-unstyled` : `btn-unstyled m-l-1`,
1756
+ "aria-label": closeButton && closeButton['aria-label'],
1757
+ size: "sm",
1758
+ filled: isModern,
1759
+ onClick: onRemove
1760
+ }) : null]
1761
+ }, value);
1576
1762
  };
1577
1763
 
1578
- var messages$b = reactIntl.defineMessages({
1764
+ var messages$a = reactIntl.defineMessages({
1579
1765
  ariaLabel: {
1580
- id: "neptune.CloseButton.ariaLabel"
1766
+ id: "neptune.Chips.ariaLabel"
1581
1767
  }
1582
1768
  });
1583
1769
 
1584
- const CloseButton = /*#__PURE__*/React.forwardRef(function CloseButton({
1770
+ const Chips = ({
1771
+ chips,
1772
+ onChange,
1773
+ selected,
1585
1774
  'aria-label': ariaLabel,
1586
- size = exports.Size.MEDIUM,
1587
- filled = false,
1588
1775
  className,
1589
- onClick,
1590
- isDisabled,
1591
- testId
1592
- }, reference) {
1776
+ multiple
1777
+ }) => {
1593
1778
  const intl = reactIntl.useIntl();
1594
- ariaLabel ??= intl.formatMessage(messages$b.ariaLabel);
1595
- const Icon = filled ? icons.CrossCircleFill : icons.Cross;
1596
- return /*#__PURE__*/jsxRuntime.jsx("button", {
1597
- ref: reference,
1598
- type: "button",
1599
- className: classNames__default.default('np-close-button', 'close btn-link', 'text-no-decoration', {
1600
- 'np-close-button--large': size === exports.Size.MEDIUM,
1601
- 'np-close-button--x-large': size === exports.Size.LARGE
1602
- }, className),
1779
+ const isSelected = value => Array.isArray(selected) ? selected.includes(value) : selected === value;
1780
+ const handleOnChange = (selectedValue, isCurrentlyEnabled) => {
1781
+ onChange({
1782
+ isEnabled: !isCurrentlyEnabled,
1783
+ selectedValue
1784
+ });
1785
+ };
1786
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
1787
+ className: classNames__default.default('np-chips d-flex', className),
1603
1788
  "aria-label": ariaLabel,
1604
- "aria-disabled": isDisabled,
1605
- disabled: isDisabled,
1606
- "data-testid": testId,
1607
- onClick: onClick,
1608
- children: /*#__PURE__*/jsxRuntime.jsx(Icon, {
1609
- size: size === exports.Size.SMALL ? 16 : 24
1789
+ role: !multiple ? 'radiogroup' : undefined,
1790
+ children: chips.map(chip => {
1791
+ const chipSelected = isSelected(chip.value);
1792
+ return /*#__PURE__*/jsxRuntime.jsx(Chip, {
1793
+ value: chip.value,
1794
+ label: chip.label,
1795
+ closeButton: {
1796
+ 'aria-label': intl.formatMessage(messages$a.ariaLabel, {
1797
+ choice: chip.label
1798
+ })
1799
+ },
1800
+ className: classNames__default.default('text-xs-nowrap', {
1801
+ 'np-chip--selected': chipSelected,
1802
+ 'p-r-1': multiple && chipSelected
1803
+ }),
1804
+ ...(multiple && chipSelected ? {
1805
+ onRemove: () => handleOnChange(chip.value, chipSelected),
1806
+ 'aria-label': chip.label
1807
+ } : {
1808
+ onClick: () => handleOnChange(chip.value, chipSelected),
1809
+ onKeyPress: () => handleOnChange(chip.value, chipSelected),
1810
+ role: !multiple ? 'radio' : undefined,
1811
+ 'aria-checked': chipSelected
1812
+ })
1813
+ }, chip.value);
1610
1814
  })
1611
1815
  });
1612
- });
1816
+ };
1613
1817
 
1614
- const EXIT_ANIMATION = 350;
1615
- const SlidingPanel = /*#__PURE__*/React.forwardRef(({
1616
- position = 'left',
1617
- open,
1618
- showSlidingPanelBorder,
1619
- slidingPanelPositionFixed,
1818
+ const CircularButton = ({
1620
1819
  className,
1621
1820
  children,
1821
+ disabled,
1822
+ icon,
1823
+ priority = exports.Priority.PRIMARY,
1824
+ type = exports.ControlType.ACCENT,
1622
1825
  ...rest
1623
- }, reference) => {
1624
- const localReference = React.useRef(null);
1625
- React.useImperativeHandle(reference, () => localReference.current, []);
1626
- return /*#__PURE__*/React.createElement(reactTransitionGroup.CSSTransition, {
1627
- ...rest,
1628
- key: `sliding-panel--open-${position}`,
1629
- nodeRef: localReference,
1630
- in: open
1631
- // Wait for animation to finish before unmount.
1632
- ,
1633
- timeout: {
1634
- enter: 0,
1635
- exit: EXIT_ANIMATION
1636
- },
1637
- classNames: "sliding-panel",
1638
- appear: true,
1639
- unmountOnExit: true
1640
- }, /*#__PURE__*/jsxRuntime.jsx("div", {
1641
- ref: localReference,
1642
- className: classNames__default.default('sliding-panel', `sliding-panel--open-${position}`, showSlidingPanelBorder && `sliding-panel--border-${position}`, slidingPanelPositionFixed && 'sliding-panel--fixed', className),
1643
- children: children
1644
- }));
1645
- });
1826
+ }) => {
1827
+ const classes = classNames__default.default('btn np-btn', typeClassMap[type], priorityClassMap[priority]);
1828
+ const iconElement = Number(icon.props.size) !== 24 ? /*#__PURE__*/React.cloneElement(icon, {
1829
+ size: 24
1830
+ }) : icon;
1831
+ return /*#__PURE__*/jsxRuntime.jsxs("label", {
1832
+ className: classNames__default.default('np-circular-btn', priority, type, disabled && 'disabled', className),
1833
+ children: [/*#__PURE__*/jsxRuntime.jsx("input", {
1834
+ type: "button",
1835
+ "aria-label": children,
1836
+ className: classes,
1837
+ disabled: disabled,
1838
+ ...rest
1839
+ }), iconElement, /*#__PURE__*/jsxRuntime.jsx(Body, {
1840
+ as: "span",
1841
+ className: "np-circular-btn__label",
1842
+ type: exports.Typography.BODY_DEFAULT_BOLD,
1843
+ children: children
1844
+ })]
1845
+ });
1846
+ };
1646
1847
 
1647
- const Drawer = ({
1648
- children,
1649
- className,
1650
- footerContent,
1651
- headerTitle,
1652
- onClose,
1653
- open,
1654
- position
1848
+ const FocusBoundary = ({
1849
+ children
1655
1850
  }) => {
1656
- logActionRequiredIf('Drawer now expects `onClose`, and will soon make this prop required. Please update your usage to provide it.', !onClose);
1657
- const {
1658
- isMobile
1659
- } = useLayout();
1660
- const titleId = reactId.useId();
1661
- return /*#__PURE__*/jsxRuntime.jsx(Dimmer$1, {
1662
- open: open,
1663
- onClose: onClose,
1664
- children: /*#__PURE__*/jsxRuntime.jsx(SlidingPanel, {
1665
- open: open,
1666
- position: isMobile ? exports.Position.BOTTOM : position,
1667
- children: /*#__PURE__*/jsxRuntime.jsxs("div", {
1668
- role: "dialog",
1669
- "aria-modal": true,
1670
- "aria-labelledby": headerTitle ? titleId : undefined,
1671
- className: classNames__default.default('np-drawer', className),
1672
- children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
1673
- className: classNames__default.default('np-drawer-header', {
1674
- 'np-drawer-header--withborder': headerTitle
1675
- }),
1676
- children: [headerTitle && /*#__PURE__*/jsxRuntime.jsx(Title, {
1677
- id: titleId,
1678
- type: exports.Typography.TITLE_BODY,
1679
- children: headerTitle
1680
- }), /*#__PURE__*/jsxRuntime.jsx(CloseButton, {
1681
- onClick: onClose
1682
- })]
1683
- }), children && /*#__PURE__*/jsxRuntime.jsx("div", {
1684
- className: classNames__default.default('np-drawer-content'),
1685
- children: children
1686
- }), footerContent && /*#__PURE__*/jsxRuntime.jsx("div", {
1687
- className: classNames__default.default('np-drawer-footer'),
1688
- children: footerContent
1689
- })]
1690
- })
1851
+ const wrapperReference = React.useRef(null);
1852
+ React.useEffect(() => {
1853
+ wrapperReference.current?.focus({
1854
+ preventScroll: true
1855
+ });
1856
+ }, []);
1857
+ return /*#__PURE__*/jsxRuntime.jsx(focus.FocusScope, {
1858
+ contain: true,
1859
+ restoreFocus: true,
1860
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
1861
+ ref: wrapperReference,
1862
+ tabIndex: -1,
1863
+ children: children
1691
1864
  })
1692
1865
  });
1693
1866
  };
1694
- Drawer.propTypes = {
1695
- /** The content to appear in the drawer body. */
1696
- children: PropTypes__default.default.node,
1697
- className: PropTypes__default.default.string,
1698
- /** The content to appear in the drawer footer. */
1699
- footerContent: PropTypes__default.default.node,
1700
- /** The content to appear in the drawer header. */
1701
- headerTitle: PropTypes__default.default.node,
1702
- /** The action to perform on close click. */
1703
- onClose: PropTypes__default.default.func,
1704
- /** The status of Drawer either open or not. */
1705
- open: PropTypes__default.default.bool,
1706
- /** The placement of Drawer on the screen either left or right. On mobile it will default to bottom. */
1707
- position: PropTypes__default.default.oneOf(['left', 'right', 'bottom'])
1708
- };
1709
- Drawer.defaultProps = {
1710
- children: null,
1711
- className: undefined,
1712
- footerContent: null,
1713
- headerTitle: null,
1714
- onClose: null,
1715
- open: false,
1716
- position: exports.Position.RIGHT
1717
- };
1718
- var Drawer$1 = Drawer;
1719
1867
 
1720
- const INITIAL_Y_POSITION = 0;
1721
- const CONTENT_SCROLL_THRESHOLD = 1;
1722
- const MOVE_OFFSET_THRESHOLD = 50;
1868
+ function withNextPortalWrapper(Component) {
1869
+ return function (props) {
1870
+ const [mounted, setMounted] = React.useState(false);
1871
+ React.useEffect(() => {
1872
+ setMounted(true);
1873
+ }, [setMounted]);
1874
+ return mounted ? /*#__PURE__*/reactDom.createPortal( /*#__PURE__*/jsxRuntime.jsx(Component, {
1875
+ ...props
1876
+ }), document.body) : null;
1877
+ };
1878
+ }
1879
+
1723
1880
  /**
1724
- * Neptune: https://transferwise.github.io/neptune/components/bottom-sheet/
1725
- *
1726
- * Neptune Web: https://transferwise.github.io/neptune-web/components/overlays/BottomSheet
1727
- *
1881
+ * Dimmer state management inspired by Material UI's ModalManager (https://github.com/mui-org/material-ui)
1728
1882
  */
1729
- const BottomSheet$1 = props => {
1730
- const bottomSheetReference = React.useRef(null);
1731
- const topBarReference = React.useRef(null);
1732
- const contentReference = React.useRef(null);
1733
- const [pressed, setPressed] = React.useState(false);
1734
- /**
1735
- * Used to track `requestAnimationFrame` requests
1736
- *
1737
- * @see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame#return_value
1738
- */
1739
- const animationId = React.useRef(0);
1883
+ class DimmerManager {
1740
1884
  /**
1741
- * Difference between initial coordinate ({@link initialYCoordinate})
1742
- * and new position (when user moves component), it's get calculated on `onTouchMove` and `onMouseMove` events
1743
- *
1744
- * @see {@link calculateOffsetAfterMove}
1885
+ * Dimmer refs
1745
1886
  */
1746
- const moveOffset = React.useRef(0);
1747
- const initialYCoordinate = React.useRef(0);
1748
- // apply shadow to the bottom of top-bar when scroll over content
1749
- useConditionalListener({
1750
- attachListener: props.open && !isServerSide(),
1751
- callback: () => {
1752
- if (topBarReference.current !== null) {
1753
- const {
1754
- classList
1755
- } = topBarReference.current;
1756
- if (!isContentScrollPositionAtTop()) {
1757
- classList.add('np-bottom-sheet--top-bar--shadow');
1758
- } else {
1759
- classList.remove('np-bottom-sheet--top-bar--shadow');
1760
- }
1761
- }
1762
- },
1763
- eventType: 'scroll',
1764
- parent: isServerSide() ? undefined : document
1765
- });
1766
- function move(newHeight) {
1767
- if (bottomSheetReference.current !== null) {
1768
- bottomSheetReference.current.style.transform = `translateY(${newHeight}px)`;
1769
- }
1887
+ dimmers;
1888
+ constructor() {
1889
+ this.dimmers = [];
1770
1890
  }
1771
- function close(event) {
1772
- setPressed(false);
1773
- moveOffset.current = INITIAL_Y_POSITION;
1774
- if (bottomSheetReference.current !== null) {
1775
- bottomSheetReference.current.style.removeProperty('transform');
1891
+ add(dimmer) {
1892
+ let dimmerIndex = this.dimmers.indexOf(dimmer);
1893
+ if (dimmerIndex !== -1) {
1894
+ return dimmerIndex;
1776
1895
  }
1777
- if (props.onClose) {
1778
- props.onClose(event);
1896
+ dimmerIndex = this.dimmers.length;
1897
+ this.dimmers.push(dimmer);
1898
+ return dimmerIndex;
1899
+ }
1900
+ remove(dimmer) {
1901
+ const dimmerIndex = this.dimmers.indexOf(dimmer);
1902
+ if (dimmerIndex !== -1) {
1903
+ this.dimmers.splice(dimmerIndex, 1);
1779
1904
  }
1905
+ return dimmerIndex;
1780
1906
  }
1781
- const onSwipeStart = event => {
1782
- initialYCoordinate.current = ('touches' in event ? event.touches[0] : event).clientY;
1783
- setPressed(true);
1907
+ isTop(dimmer) {
1908
+ return this.dimmers.length > 0 && this.dimmers[this.dimmers.length - 1] === dimmer;
1909
+ }
1910
+ }
1911
+
1912
+ const EXIT_ANIMATION$1 = 350;
1913
+ const dimmerManager = new DimmerManager();
1914
+ const handleTouchMove = event => {
1915
+ const isTouchedElementDimmer = event.target.classList.contains('dimmer');
1916
+ // disable scroll on iOS devices for Dimmer area
1917
+ // this is because of bug in WebKit https://bugs.webkit.org/show_bug.cgi?id=220908
1918
+ // note: scrolling still works for children(s) as expected
1919
+ if (isIosDevice() && isTouchedElementDimmer) {
1920
+ event.stopPropagation();
1921
+ event.preventDefault();
1922
+ }
1923
+ };
1924
+ const Dimmer = ({
1925
+ children,
1926
+ className,
1927
+ disableClickToClose = false,
1928
+ contentPosition,
1929
+ fadeContentOnEnter = false,
1930
+ fadeContentOnExit = false,
1931
+ open = false,
1932
+ scrollable = false,
1933
+ transparent = false,
1934
+ onClose
1935
+ }) => {
1936
+ const [hasNotExited, setHasNotExited] = React.useState(false);
1937
+ const dimmerReference = React.useRef(null);
1938
+ const closeOnClick = event => {
1939
+ if (event.target === dimmerReference.current) {
1940
+ onClose?.(event);
1941
+ }
1784
1942
  };
1785
- const onSwipeMove = event => {
1786
- if (pressed) {
1787
- const {
1788
- clientY
1789
- } = 'touches' in event ? event.touches[0] : event;
1790
- const offset = calculateOffsetAfterMove(clientY);
1791
- // check whether move is to the bottom only and content scroll position is at the top
1792
- if (offset > INITIAL_Y_POSITION && isContentScrollPositionAtTop()) {
1793
- moveOffset.current = offset;
1794
- animationId.current = requestAnimationFrame(() => {
1795
- if (animationId.current !== undefined && bottomSheetReference.current !== null) {
1796
- move(offset);
1797
- }
1798
- });
1799
- }
1943
+ const handleClick = event => {
1944
+ if (disableClickToClose || !onClose) {
1945
+ return;
1800
1946
  }
1947
+ closeOnClick(event);
1801
1948
  };
1802
- function onSwipeEnd(event) {
1803
- // stop moving component
1804
- cancelAnimationFrame(animationId.current);
1805
- setPressed(false);
1806
- // check whether move down is strong enough
1807
- // and content scroll position is at the top to close the component
1808
- if (moveOffset.current > MOVE_OFFSET_THRESHOLD && isContentScrollPositionAtTop()) {
1809
- close(event);
1949
+ const handleKeyDown = React.useCallback(event => {
1950
+ if (event.key !== 'Escape') {
1951
+ return;
1810
1952
  }
1811
- // otherwise move component back to default (initial) position
1812
- else {
1813
- move(INITIAL_Y_POSITION);
1953
+ event.stopPropagation();
1954
+ if (onClose && dimmerReference.current && dimmerManager.isTop(dimmerReference.current)) {
1955
+ onClose(event);
1814
1956
  }
1815
- moveOffset.current = INITIAL_Y_POSITION;
1816
- }
1817
- function isContentScrollPositionAtTop() {
1818
- return contentReference?.current?.scrollTop !== undefined && contentReference.current.scrollTop <= CONTENT_SCROLL_THRESHOLD;
1819
- }
1820
- /**
1821
- * Calculates how hard user moves component,
1822
- * result value used to determine whether to hide component or re-position to default state
1823
- *
1824
- * @param afterMoveYCoordinate
1825
- */
1826
- function calculateOffsetAfterMove(afterMoveYCoordinate) {
1827
- return afterMoveYCoordinate - initialYCoordinate.current;
1828
- }
1829
- /**
1830
- * Set `max-height` for content part (in order to keep it scrollable for content overflow cases) of the component
1831
- * and ensures space for safe zone (32px) at the top.
1832
- */
1833
- function setContentMaxHeight() {
1834
- const safeZoneHeight = '64px';
1835
- const topbarHeight = '32px';
1836
- const windowHight = isServerSide() ? 0 : window.innerHeight;
1837
- /**
1838
- * Calculate _real_ height of the screen (taking into account parts of browser interface).
1839
- *
1840
- * See https://css-tricks.com/the-trick-to-viewport-units-on-mobile for more details.
1841
- */
1842
- const screenHeight = `${windowHight * 0.01 * 100}px`;
1843
- return {
1844
- maxHeight: `calc(${screenHeight} - ${safeZoneHeight} - ${topbarHeight})`
1957
+ }, [onClose]);
1958
+ const onEnter = () => {
1959
+ setHasNotExited(true);
1960
+ if (dimmerReference.current) {
1961
+ dimmerManager.add(dimmerReference.current);
1962
+ }
1963
+ };
1964
+ const onExited = () => {
1965
+ setHasNotExited(false);
1966
+ if (dimmerReference.current) {
1967
+ dimmerManager.remove(dimmerReference.current);
1968
+ }
1969
+ };
1970
+ React.useEffect(() => {
1971
+ const localReferenceCopy = dimmerReference.current;
1972
+ if (open) {
1973
+ document.addEventListener('keydown', handleKeyDown);
1974
+ localReferenceCopy?.addEventListener('touchmove', handleTouchMove, {
1975
+ passive: true
1976
+ });
1977
+ }
1978
+ return () => {
1979
+ document.removeEventListener('keydown', handleKeyDown);
1980
+ localReferenceCopy?.removeEventListener('touchmove', handleTouchMove);
1845
1981
  };
1846
- }
1847
- const is400Zoom = useMedia(`(max-width: ${exports.Breakpoint.ZOOM_400}px)`);
1848
- return is400Zoom ? /*#__PURE__*/jsxRuntime.jsx(Drawer$1, {
1849
- open: props.open,
1850
- className: props.className,
1851
- onClose: close,
1852
- children: props.children
1853
- }) : /*#__PURE__*/jsxRuntime.jsx(Dimmer$1, {
1854
- open: props.open,
1855
- fadeContentOnEnter: true,
1856
- fadeContentOnExit: true,
1857
- onClose: close,
1858
- children: /*#__PURE__*/jsxRuntime.jsx(SlidingPanel, {
1859
- ref: bottomSheetReference,
1860
- open: props.open,
1861
- position: exports.Position.BOTTOM,
1862
- className: classNames__default.default('np-bottom-sheet', props.className),
1863
- children: /*#__PURE__*/jsxRuntime.jsxs("div", {
1864
- role: "dialog",
1865
- "aria-modal": true,
1866
- onTouchStart: onSwipeStart,
1867
- onTouchMove: onSwipeMove,
1868
- onTouchEnd: onSwipeEnd,
1869
- onMouseDown: onSwipeStart,
1870
- onMouseMove: onSwipeMove,
1871
- onMouseUp: onSwipeEnd,
1872
- children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
1873
- ref: topBarReference,
1874
- className: "np-bottom-sheet--top-bar",
1875
- children: [/*#__PURE__*/jsxRuntime.jsx("div", {
1876
- className: "np-bottom-sheet--handler"
1877
- }), /*#__PURE__*/jsxRuntime.jsx(CloseButton, {
1878
- size: "sm",
1879
- className: "sr-only np-bottom-sheet--close-btn",
1880
- onClick: close
1881
- })]
1882
- }), /*#__PURE__*/jsxRuntime.jsx("div", {
1883
- ref: contentReference,
1884
- style: setContentMaxHeight(),
1885
- className: "np-bottom-sheet--content",
1886
- children: props.children
1887
- })]
1982
+ }, [handleKeyDown, open]);
1983
+ return /*#__PURE__*/jsxRuntime.jsx(DimmerWrapper, {
1984
+ open: open,
1985
+ hasNotExited: hasNotExited,
1986
+ children: /*#__PURE__*/jsxRuntime.jsx(reactTransitionGroup.CSSTransition, {
1987
+ nodeRef: dimmerReference,
1988
+ in: open,
1989
+ appear: true
1990
+ // Wait for animation to finish before unmount.
1991
+ ,
1992
+ timeout: {
1993
+ enter: 0,
1994
+ exit: EXIT_ANIMATION$1
1995
+ },
1996
+ classNames: {
1997
+ enter: classNames__default.default({
1998
+ 'dimmer--enter-fade': fadeContentOnEnter
1999
+ }),
2000
+ enterDone: classNames__default.default('dimmer--enter-done', {
2001
+ 'dimmer--enter-fade': fadeContentOnEnter
2002
+ }),
2003
+ exit: classNames__default.default('dimmer--exit', {
2004
+ 'dimmer--exit-fade': fadeContentOnExit
2005
+ })
2006
+ },
2007
+ unmountOnExit: true,
2008
+ onEnter: onEnter,
2009
+ onExited: onExited,
2010
+ children: /*#__PURE__*/jsxRuntime.jsx(DimmerContentWrapper, {
2011
+ scrollBody: !transparent,
2012
+ children: /*#__PURE__*/jsxRuntime.jsx(FocusBoundary, {
2013
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
2014
+ ref: dimmerReference,
2015
+ className: classNames__default.default('dimmer', {
2016
+ 'dimmer--scrollable': scrollable,
2017
+ 'dimmer--transparent': transparent
2018
+ }, className),
2019
+ role: "presentation",
2020
+ onClick: handleClick,
2021
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
2022
+ className: classNames__default.default('dimmer-content-positioner', contentPosition != null && ['d-flex justify-content-center', {
2023
+ 'align-items-start': contentPosition === 'top',
2024
+ 'align-items-center': contentPosition === 'center',
2025
+ 'align-items-end': contentPosition === 'bottom'
2026
+ }]),
2027
+ children: children
2028
+ })
2029
+ })
2030
+ })
1888
2031
  })
1889
2032
  })
1890
2033
  });
1891
2034
  };
1892
-
1893
- function SelectOption({
1894
- selected: selectedValueProp = undefined,
1895
- ...props
1896
- }) {
1897
- const rootRef = React.useRef(null);
1898
- const [selected, setSelected] = React.useState(selectedValueProp);
1899
- const [showMenu, setShowMenu] = React.useState(false);
1900
- function handleOnClick(status) {
2035
+ const DimmerWrapper = ({
2036
+ open,
2037
+ hasNotExited,
2038
+ children
2039
+ }) => {
2040
+ const {
2041
+ screenMode,
2042
+ theme
2043
+ } = componentsTheming.useTheme();
2044
+ return open || hasNotExited ? /*#__PURE__*/jsxRuntime.jsx(componentsTheming.ThemeProvider, {
2045
+ theme: "personal",
2046
+ screenMode: theme === 'personal' ? screenMode : 'light',
2047
+ isNotRootProvider: true,
2048
+ children: children
2049
+ }) : /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
2050
+ children: children
2051
+ });
2052
+ };
2053
+ const DimmerContentWrapper = ({
2054
+ children,
2055
+ scrollBody
2056
+ }) => {
2057
+ React.useEffect(() => {
2058
+ if (scrollBody) {
2059
+ addNoScrollClass();
2060
+ }
1901
2061
  return () => {
1902
- console.log('handleOnClick', status);
1903
- setShowMenu(status);
2062
+ if (scrollBody) {
2063
+ removeNoScrollClass();
2064
+ }
1904
2065
  };
1905
- }
1906
- function handleOnSelect(data) {
2066
+ }, [scrollBody]);
2067
+ return children;
2068
+ };
2069
+ var Dimmer$1 = withNextPortalWrapper(Dimmer);
2070
+
2071
+ const useConditionalListener = ({
2072
+ attachListener,
2073
+ callback,
2074
+ eventType,
2075
+ parent
2076
+ }) => {
2077
+ React.useEffect(() => {
2078
+ if (attachListener && !neptuneValidation.isUndefined(parent)) {
2079
+ parent.addEventListener(eventType, callback, true);
2080
+ }
1907
2081
  return () => {
1908
- setShowMenu(false);
1909
- setSelected(data);
2082
+ if (!neptuneValidation.isUndefined(parent)) {
2083
+ parent.removeEventListener(eventType, callback, true);
2084
+ }
1910
2085
  };
1911
- }
1912
- const isSelected = selected !== undefined;
1913
- const {
1914
- isMobile
1915
- } = useLayout();
1916
- const options = /*#__PURE__*/jsxRuntime.jsxs(Section, {
1917
- children: [/*#__PURE__*/jsxRuntime.jsx(Header, {
1918
- title: "Payment Methods"
1919
- }), /*#__PURE__*/jsxRuntime.jsx(Option$2, {
1920
- showMediaAtAllSizes: true,
1921
- media: /*#__PURE__*/jsxRuntime.jsx(art.Flag, {
1922
- code: "gbp"
1923
- }),
1924
- title: "Wise GBP balance",
1925
- content: /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1926
- children: [/*#__PURE__*/jsxRuntime.jsx("span", {
1927
- children: "300 GBP available"
1928
- }), /*#__PURE__*/jsxRuntime.jsx("span", {
1929
- children: "0 GBP in fees, should arrive in seconds"
1930
- })]
1931
- }),
1932
- onClick: handleOnSelect({
1933
- media: /*#__PURE__*/jsxRuntime.jsx(art.Flag, {
1934
- code: "gbp"
1935
- }),
1936
- title: 'Wise GBP balance'
1937
- })
1938
- }), /*#__PURE__*/jsxRuntime.jsx(Option$2, {
1939
- media: /*#__PURE__*/jsxRuntime.jsx(icons.Card, {}),
1940
- title: "Credit card",
1941
- content: /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1942
- children: [/*#__PURE__*/jsxRuntime.jsx("div", {
1943
- children: "Transfer the money to Wise using your bank account."
1944
- }), /*#__PURE__*/jsxRuntime.jsx("div", {
1945
- children: "0.32 GBP in fees, should arrive in seconds"
1946
- })]
1947
- }),
1948
- onClick: handleOnSelect({
1949
- media: /*#__PURE__*/jsxRuntime.jsx(icons.Card, {}),
1950
- title: 'Credit card',
1951
- content: 'Pay with your credit card'
1952
- })
1953
- }), /*#__PURE__*/jsxRuntime.jsx(Option$2, {
1954
- media: /*#__PURE__*/jsxRuntime.jsx(icons.Card, {}),
1955
- title: "Debit card",
1956
- content: /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1957
- children: [/*#__PURE__*/jsxRuntime.jsx("span", {
1958
- children: "Send from your Visa or Mastercard."
1959
- }), /*#__PURE__*/jsxRuntime.jsx("span", {
1960
- children: "0.74 GBP in fees, should arrive in seconds"
1961
- })]
1962
- }),
1963
- onClick: handleOnSelect({
1964
- media: /*#__PURE__*/jsxRuntime.jsx(icons.Card, {}),
1965
- title: 'Debit card',
1966
- content: 'Pay with your debit card'
1967
- })
1968
- }), /*#__PURE__*/jsxRuntime.jsx(Option$2, {
1969
- media: /*#__PURE__*/jsxRuntime.jsx(icons.Bank, {}),
1970
- title: "Swift Transfer",
1971
- content: /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1972
- children: [/*#__PURE__*/jsxRuntime.jsx("span", {
1973
- children: "Send money internationally. Your bank will charge you extra fees."
1974
- }), /*#__PURE__*/jsxRuntime.jsx("span", {
1975
- children: "0.32 GBP in fees, should arrive by Thursday"
1976
- })]
1977
- })
1978
- }), /*#__PURE__*/jsxRuntime.jsx(Option$2, {
1979
- media: /*#__PURE__*/jsxRuntime.jsx(icons.BankTransfer, {}),
1980
- title: "Bank Transfer",
1981
- content: /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1982
- children: [/*#__PURE__*/jsxRuntime.jsx("span", {
1983
- children: "Transfer the money to Wise using your bank account."
1984
- }), /*#__PURE__*/jsxRuntime.jsx("span", {
1985
- children: "0.32 GBP in fees, should arrive in seconds"
1986
- })]
1987
- })
1988
- }), /*#__PURE__*/jsxRuntime.jsx(Option$2, {
1989
- media: /*#__PURE__*/jsxRuntime.jsx(icons.Card, {}),
1990
- title: "Debit card"
1991
- }), /*#__PURE__*/jsxRuntime.jsx(Option$2, {
1992
- media: /*#__PURE__*/jsxRuntime.jsx(icons.Bank, {}),
1993
- title: "Swift"
1994
- }), /*#__PURE__*/jsxRuntime.jsx(Option$2, {
1995
- media: /*#__PURE__*/jsxRuntime.jsx(icons.BankTransfer, {}),
1996
- title: "Bank Transfer"
1997
- })]
1998
- });
1999
- return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2000
- children: [/*#__PURE__*/jsxRuntime.jsx(Option$2, {
2001
- ref: rootRef,
2002
- showMediaAtAllSizes: true,
2003
- media: isSelected ? selected.media : props.media,
2004
- title: isSelected ? selected.title : props.title,
2005
- content: isSelected ? selected.content : props.content,
2006
- className: classNames__default.default('np-select-option', {
2007
- 'np-select-option-selected': isSelected
2008
- }, props.className),
2009
- button: isSelected ? /*#__PURE__*/jsxRuntime.jsx(Chevron, {}) : /*#__PURE__*/jsxRuntime.jsx(ActionButton, {
2010
- onClick: handleOnClick(true),
2011
- children: "Choose"
2012
- }),
2013
- onClick: isSelected ? handleOnClick(true) : undefined
2014
- }), isMobile ? /*#__PURE__*/jsxRuntime.jsx(BottomSheet$1, {
2015
- open: showMenu,
2016
- onClose: handleOnClick(false),
2017
- children: options
2018
- }) : /*#__PURE__*/jsxRuntime.jsx(Panel, {
2019
- open: showMenu,
2020
- flip: false,
2021
- altAxis: true,
2022
- anchorRef: rootRef,
2023
- anchorWidth: false,
2024
- position: exports.Position.BOTTOM,
2025
- onClose: handleOnClick(false),
2026
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
2027
- className: "np-select-option-list p-a-2",
2028
- children: options
2029
- })
2030
- })]
2031
- });
2032
- }
2033
-
2034
- const iconTypeMap = {
2035
- positive: icons.Check,
2036
- neutral: icons.Info,
2037
- warning: icons.Alert,
2038
- negative: icons.Cross,
2039
- pending: icons.ClockBorderless,
2040
- info: icons.Info,
2041
- error: icons.Cross,
2042
- success: icons.Check
2086
+ }, [attachListener, callback, eventType, parent]);
2043
2087
  };
2044
- const StatusIcon = ({
2045
- sentiment = 'neutral',
2046
- size = 'md'
2088
+
2089
+ const DirectionContext = /*#__PURE__*/React.createContext(exports.Direction.LTR);
2090
+ const DirectionProvider = ({
2091
+ direction,
2092
+ children
2047
2093
  }) => {
2048
- const Icon = iconTypeMap[sentiment];
2049
- const iconColor = sentiment === 'warning' || sentiment === 'pending' ? 'dark' : 'light';
2050
- return /*#__PURE__*/jsxRuntime.jsx("span", {
2051
- "data-testid": "status-icon",
2052
- className: classNames__default.default('status-circle', 'status-circle-' + size, sentiment),
2053
- children: /*#__PURE__*/jsxRuntime.jsx(Icon, {
2054
- className: classNames__default.default('status-icon', iconColor)
2055
- })
2094
+ return /*#__PURE__*/jsxRuntime.jsx(DirectionContext.Provider, {
2095
+ value: direction,
2096
+ children: children
2056
2097
  });
2057
2098
  };
2058
2099
 
2059
- const reader = new commonmark__default.default.Parser();
2060
- const writer = new commonmark__default.default.HtmlRenderer({
2061
- safe: true
2062
- });
2063
- const NODE_TYPE_LIST = Object.values(exports.MarkdownNodeType);
2064
- function Markdown({
2065
- as: Element = 'div',
2066
- allowList,
2067
- blockList,
2068
- config,
2069
- className,
2070
- children
2071
- }) {
2072
- if (!children) {
2073
- return null;
2074
- }
2075
- const linkTarget = config?.link?.target ?? '_self';
2076
- const paragraphClass = config?.paragraph?.className ?? '';
2077
- if (allowList != null && blockList != null) {
2078
- logActionRequired('Markdown supports only one of `allowList` or `blockList` to be used at a time. `blockList` will be ignored.');
2079
- }
2080
- const parser = nodes => {
2081
- const parsed = reader.parse(nodes);
2082
- const toExclude = allowList != null ? NODE_TYPE_LIST.filter(type => !allowList.includes(type)) : blockList;
2083
- return toExclude != null ? stripNodes({
2084
- parsed,
2085
- blockList: toExclude
2086
- }) : parsed;
2087
- };
2088
- const createMarkup = () => {
2089
- const parsed = parser(children);
2090
- return writer.render(parsed).replace(/<a href="/g, `<a target="${linkTarget}" href="`).replace(/<p>/g, `<p class="${paragraphClass}">`);
2100
+ const useDirection = () => {
2101
+ const direction = React.useContext(DirectionContext);
2102
+ return {
2103
+ direction,
2104
+ isRTL: direction === 'rtl'
2091
2105
  };
2092
- return /*#__PURE__*/jsxRuntime.jsx(Element, {
2093
- className: className,
2094
- dangerouslySetInnerHTML: {
2095
- __html: createMarkup()
2096
- }
2097
- });
2098
- }
2099
- function stripNodes({
2100
- blockList,
2101
- parsed
2102
- }) {
2103
- if (!parsed) {
2104
- return parsed;
2105
- }
2106
- const walker = parsed.walker();
2107
- for (let event = walker.next(); event != null; event = walker.next()) {
2108
- const {
2109
- node
2110
- } = event;
2111
- if (blockList.includes(node.type) && !event.entering) {
2112
- while (node.firstChild != null) {
2113
- node.insertBefore(node.firstChild);
2114
- }
2115
- node.unlink();
2116
- }
2117
- }
2118
- return parsed;
2119
- }
2120
-
2121
- const allowList = [exports.MarkdownNodeType.STRONG];
2122
- const InlineMarkdown = props => {
2123
- return /*#__PURE__*/jsxRuntime.jsx(Markdown, {
2124
- ...props,
2125
- as: "span",
2126
- allowList: allowList,
2127
- blockList: undefined
2128
- });
2129
- };
2130
- InlineMarkdown.propTypes = {
2131
- children: PropTypes__default.default.string.isRequired,
2132
- className: PropTypes__default.default.string
2133
2106
  };
2134
- InlineMarkdown.defaultProps = {
2135
- className: undefined
2107
+
2108
+ const ObserverParams = {
2109
+ threshold: 0.1
2136
2110
  };
2137
2111
 
2138
- exports.AlertArrowPosition = void 0;
2139
- (function (AlertArrowPosition) {
2140
- AlertArrowPosition["TOP_LEFT"] = "up-left";
2141
- AlertArrowPosition["TOP"] = "up-center";
2142
- AlertArrowPosition["TOP_RIGHT"] = "up-right";
2143
- AlertArrowPosition["BOTTOM_LEFT"] = "down-left";
2144
- AlertArrowPosition["BOTTOM"] = "down-center";
2145
- AlertArrowPosition["BOTTOM_RIGHT"] = "down-right";
2146
- })(exports.AlertArrowPosition || (exports.AlertArrowPosition = {}));
2147
- function resolveType(type) {
2148
- switch (type) {
2149
- case 'success':
2150
- return 'positive';
2151
- case 'info':
2152
- return 'neutral';
2153
- case 'error':
2154
- return 'negative';
2155
- default:
2156
- return type;
2157
- }
2158
- }
2159
- function Alert({
2160
- arrow,
2161
- action,
2162
- children,
2163
- className,
2164
- dismissible,
2165
- icon,
2166
- onDismiss,
2167
- message,
2168
- size,
2169
- title,
2170
- type = 'neutral',
2171
- variant = 'desktop'
2172
- }) {
2173
- React.useEffect(() => {
2174
- if (arrow !== undefined) {
2175
- logActionRequired("Alert component doesn't support 'arrow' anymore, use 'InlineAlert' instead.");
2176
- }
2177
- }, [arrow]);
2178
- React.useEffect(() => {
2179
- if (children !== undefined) {
2180
- logActionRequired("Alert component doesn't support 'children' anymore, use 'message' instead.");
2181
- }
2182
- }, [children]);
2183
- React.useEffect(() => {
2184
- if (dismissible !== undefined) {
2185
- logActionRequired("Alert component doesn't support 'dismissible' anymore, use 'onDismiss' instead.");
2186
- }
2187
- }, [dismissible]);
2188
- React.useEffect(() => {
2189
- if (size !== undefined) {
2190
- logActionRequired("Alert component doesn't support 'size' anymore, please remove that prop.");
2191
- }
2192
- }, [size]);
2193
- const resolvedType = resolveType(type);
2112
+ /**
2113
+ * useHasIntersected.
2114
+ * Use this custom hook to detect when an element has became visible inside the viewport. This hook checks only if the intersection happend.
2115
+ * Once the intersection has happened the hook will not return false even if the element gets out of the viewport.
2116
+ *
2117
+ * @param elRef.elRef
2118
+ * @param {object} [elRef] - node object that contains a react reference to the element that needs to be observed.
2119
+ * @param {strimng} [loading = 'eager'] - string that contains the type of loading.
2120
+ * @param elRef.loading
2121
+ * @usage `const [hasIntersected] = useHasIntersected({imageRef,loading});`
2122
+ */
2123
+ const useHasIntersected = ({
2124
+ elRef,
2125
+ loading
2126
+ }) => {
2127
+ const [hasIntersected, setHasIntersected] = React.useState(false);
2128
+ const {
2129
+ current
2130
+ } = elRef || {};
2131
+ const isValidReference = () => {
2132
+ return elRef && current;
2133
+ };
2134
+ const handleOnIntersect = (entries, observer) => {
2135
+ entries.forEach(entry => {
2136
+ if (entry.isIntersecting) {
2137
+ setHasIntersected(true);
2138
+ observer.unobserve(current);
2139
+ }
2140
+ });
2141
+ };
2194
2142
  React.useEffect(() => {
2195
- if (resolvedType !== type) {
2196
- logActionRequired(`Alert component has deprecated '${type}' value for the 'type' prop. Please use '${resolvedType}' instead.`);
2143
+ let observer;
2144
+ let didCancel = false;
2145
+
2146
+ // Check if window is define for SSR and Old browsers fallback
2147
+ if (typeof window === 'undefined' || !window.IntersectionObserver || !isValidReference()) {
2148
+ setHasIntersected(true);
2149
+ } else if (!didCancel) {
2150
+ observer = new IntersectionObserver(handleOnIntersect, ObserverParams);
2151
+ observer.observe(current);
2197
2152
  }
2198
- }, [resolvedType, type]);
2199
- const [shouldFire, setShouldFire] = React.useState(false);
2200
- const closeButtonReference = React.useRef(null);
2201
- return /*#__PURE__*/jsxRuntime.jsxs("div", {
2202
- className: classNames__default.default('alert d-flex', `alert-${resolvedType}`, arrow != null && alertArrowClassNames(arrow), className),
2203
- "data-testid": "alert",
2204
- onTouchStart: () => setShouldFire(true),
2205
- onTouchEnd: event => {
2206
- if (shouldFire && action &&
2207
- // Check if current event is triggered from closeButton
2208
- event.target instanceof Node && closeButtonReference.current && !closeButtonReference.current.contains(event.target)) {
2209
- if (action.target === '_blank') {
2210
- window.top?.open(action.href);
2211
- } else {
2212
- window.top?.location.assign(action.href);
2213
- }
2153
+ return () => {
2154
+ didCancel = true;
2155
+ if (observer) {
2156
+ observer.unobserve(current);
2214
2157
  }
2215
- setShouldFire(false);
2216
- },
2217
- onTouchMove: () => setShouldFire(false),
2218
- children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
2219
- className: classNames__default.default('alert__content', 'd-flex', 'flex-grow-1', variant),
2220
- "data-testid": variant,
2221
- children: [icon ? /*#__PURE__*/jsxRuntime.jsx("div", {
2222
- className: "alert__icon",
2223
- children: icon
2224
- }) : /*#__PURE__*/jsxRuntime.jsx(StatusIcon, {
2225
- size: exports.Size.LARGE,
2226
- sentiment: resolvedType
2227
- }), /*#__PURE__*/jsxRuntime.jsxs("div", {
2228
- className: "alert__message",
2229
- children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
2230
- role: exports.Sentiment.NEGATIVE === resolvedType ? 'alert' : 'status',
2231
- children: [title && /*#__PURE__*/jsxRuntime.jsx(Title, {
2232
- className: "m-b-1",
2233
- type: exports.Typography.TITLE_BODY,
2234
- children: title
2235
- }), /*#__PURE__*/jsxRuntime.jsx(Body, {
2236
- as: "span",
2237
- className: "d-block",
2238
- type: exports.Typography.BODY_LARGE,
2239
- children: children || /*#__PURE__*/jsxRuntime.jsx(InlineMarkdown, {
2240
- children: message
2241
- })
2242
- })]
2243
- }), action && /*#__PURE__*/jsxRuntime.jsx(Link, {
2244
- href: action.href,
2245
- className: "m-t-1",
2246
- "aria-label": action['aria-label'],
2247
- target: action.target,
2248
- type: exports.Typography.LINK_LARGE,
2249
- children: action.text
2250
- })]
2251
- })]
2252
- }), onDismiss && /*#__PURE__*/jsxRuntime.jsx(CloseButton, {
2253
- ref: closeButtonReference,
2254
- className: "m-l-2",
2255
- onClick: onDismiss
2256
- })]
2257
- });
2258
- }
2259
- function alertArrowClassNames(arrow) {
2260
- switch (arrow) {
2261
- case 'down-center':
2262
- return 'arrow arrow-bottom arrow-center';
2263
- case 'down-left':
2264
- return 'arrow arrow-bottom arrow-left';
2265
- case 'down-right':
2266
- return 'arrow arrow-bottom arrow-right';
2267
- case 'up-center':
2268
- return 'arrow arrow-center';
2269
- case 'up-right':
2270
- return 'arrow arrow-right';
2271
- case 'up-left':
2272
- default:
2273
- return 'arrow';
2158
+ };
2159
+ }, [elRef]);
2160
+ if (loading === 'eager') {
2161
+ return [false];
2274
2162
  }
2163
+ return [hasIntersected];
2164
+ };
2165
+
2166
+ // eslint-disable-next-line import/extensions
2167
+ function useMedia(query) {
2168
+ return index_js.useSyncExternalStore(onStoreChange => {
2169
+ const mediaQueryList = window.matchMedia(query);
2170
+ mediaQueryList.addEventListener('change', onStoreChange);
2171
+ return () => {
2172
+ mediaQueryList.removeEventListener('change', onStoreChange);
2173
+ };
2174
+ }, () => typeof window !== 'undefined' ? window.matchMedia(query).matches : undefined, () => undefined);
2275
2175
  }
2276
2176
 
2277
- // TODO: consider to move this enum into component file once we migrate it on TypeScript or replace with some common enum
2278
- exports.AvatarType = void 0;
2279
- (function (AvatarType) {
2280
- AvatarType["THUMBNAIL"] = "thumbnail";
2281
- AvatarType["ICON"] = "icon";
2282
- AvatarType["EMOJI"] = "emoji";
2283
- AvatarType["INITIALS"] = "initials";
2284
- })(exports.AvatarType || (exports.AvatarType = {}));
2177
+ function useScreenSize(size) {
2178
+ return useMedia(`(min-width: ${size}px)`);
2179
+ }
2285
2180
 
2286
- /*
2287
- * The colors we support generating an Avatar background color from a "seed" for.
2288
- * Changing this array will change the assignment between seeds.
2289
- * Do not change this array.
2181
+ /**
2182
+ * @deprecated Prefer `useScreenSize` instead.
2290
2183
  */
2291
- const avatarColors = ['--color-bright-blue', '--color-bright-yellow', '--color-bright-pink', '--color-bright-orange'];
2292
- /*
2293
- * Takes in a "seed" string and spits out an index for the color we should use.
2294
- * This implementation has been synced across all three clients so that we consistently
2295
- * generate branded avatar colors when rendering a list of Avatars.
2296
- * Do not change this implementation.
2297
- */
2298
- const hashSeed = seed => {
2299
- const base = 31;
2300
- const modulo = avatarColors.length;
2301
- let hashValue = 0;
2302
- let basePow = 1;
2303
- for (let i = 0; i < seed.length; i += 1) {
2304
- hashValue = (hashValue + seed.charCodeAt(i) * basePow) % modulo;
2305
- basePow = basePow * base % modulo;
2306
- }
2307
- return hashValue;
2308
- };
2309
- const getAvatarColorFromSeed = seed => {
2310
- return avatarColors[hashSeed(seed)];
2184
+ const useLayout = () => {
2185
+ const screenXs = useScreenSize(exports.Breakpoint.EXTRA_SMALL);
2186
+ const screenSm = useScreenSize(exports.Breakpoint.SMALL);
2187
+ const screenMd = useScreenSize(exports.Breakpoint.MEDIUM);
2188
+ const screenLg = useScreenSize(exports.Breakpoint.LARGE);
2189
+ const screenXl = useScreenSize(exports.Breakpoint.EXTRA_LARGE);
2190
+ return {
2191
+ isMobile: screenSm != null ? !screenSm : undefined,
2192
+ isExtraSmall: screenXs,
2193
+ isSmall: screenSm,
2194
+ isMedium: screenMd,
2195
+ isLarge: screenLg,
2196
+ isExtraLarge: screenXl
2197
+ };
2311
2198
  };
2312
2199
 
2313
- const backwardsCompatibleSize = size => {
2314
- switch (size) {
2315
- case 'sm':
2316
- return 24;
2317
- case 'md':
2318
- return 48;
2319
- case 'lg':
2320
- return 72;
2321
- default:
2322
- return size;
2323
- }
2324
- };
2325
- const Avatar = ({
2326
- backgroundColor = null,
2327
- backgroundColorSeed = null,
2328
- children = null,
2200
+ const EXIT_ANIMATION = 350;
2201
+ const SlidingPanel = /*#__PURE__*/React.forwardRef(({
2202
+ position = 'left',
2203
+ open,
2204
+ showSlidingPanelBorder,
2205
+ slidingPanelPositionFixed,
2329
2206
  className,
2330
- outlined = false,
2331
- size: sizeFromProps = 48,
2332
- theme = exports.Theme.LIGHT,
2333
- type = 'thumbnail'
2334
- }) => {
2335
- const backgroundColorFromSeed = React.useMemo(() => !backgroundColor && backgroundColorSeed ? `var(${getAvatarColorFromSeed(backgroundColorSeed)})` : undefined, [backgroundColor, backgroundColorSeed]);
2336
- const size = backwardsCompatibleSize(sizeFromProps);
2337
- return /*#__PURE__*/jsxRuntime.jsx("div", {
2338
- className: classNames__default.default('tw-avatar', className, `tw-avatar--${size}`, `tw-avatar--${type}`, {
2339
- 'tw-avatar--outlined': outlined,
2340
- 'tw-avatar--branded': Boolean(backgroundColorFromSeed),
2341
- 'np-text-title-body': type === 'initials'
2342
- }),
2343
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
2344
- className: "tw-avatar__content",
2345
- style: {
2346
- backgroundColor: backgroundColor || backgroundColorFromSeed
2347
- },
2348
- children: children
2349
- })
2350
- });
2351
- };
2352
-
2353
- const Badge = ({
2354
- badge,
2355
- className = undefined,
2356
- size = exports.Size.SMALL,
2357
- border = exports.Theme.LIGHT,
2358
- children
2359
- }) => {
2360
- const classes = classNames__default.default('tw-badge', {
2361
- [`tw-badge-border-${border}`]: border,
2362
- [`tw-badge-${size}`]: size
2363
- }, className);
2364
- return /*#__PURE__*/jsxRuntime.jsxs("div", {
2365
- className: classes,
2366
- children: [/*#__PURE__*/jsxRuntime.jsx("div", {
2367
- className: "tw-badge__children",
2368
- children: children
2369
- }), /*#__PURE__*/jsxRuntime.jsx("div", {
2370
- className: "tw-badge__content",
2371
- children: badge
2372
- })]
2373
- });
2374
- };
2375
-
2376
- const OptionalBadge = ({
2377
- url,
2378
- altText,
2379
- statusIcon,
2380
2207
  children,
2381
2208
  ...rest
2382
- }) => {
2383
- if (url) {
2384
- return /*#__PURE__*/jsxRuntime.jsx(Badge, {
2385
- badge: /*#__PURE__*/jsxRuntime.jsx("img", {
2386
- src: url,
2387
- alt: altText
2388
- }),
2389
- ...rest,
2390
- children: children
2391
- });
2392
- }
2393
- if (statusIcon) {
2394
- return /*#__PURE__*/jsxRuntime.jsx(Badge, {
2395
- badge: /*#__PURE__*/jsxRuntime.jsx(StatusIcon, {
2396
- sentiment: statusIcon,
2397
- size: exports.Size.SMALL
2398
- }),
2399
- ...rest,
2400
- children: children
2401
- });
2402
- }
2403
- return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
2209
+ }, reference) => {
2210
+ const localReference = React.useRef(null);
2211
+ React.useImperativeHandle(reference, () => localReference.current, []);
2212
+ return /*#__PURE__*/React.createElement(reactTransitionGroup.CSSTransition, {
2213
+ ...rest,
2214
+ key: `sliding-panel--open-${position}`,
2215
+ nodeRef: localReference,
2216
+ in: open
2217
+ // Wait for animation to finish before unmount.
2218
+ ,
2219
+ timeout: {
2220
+ enter: 0,
2221
+ exit: EXIT_ANIMATION
2222
+ },
2223
+ classNames: "sliding-panel",
2224
+ appear: true,
2225
+ unmountOnExit: true
2226
+ }, /*#__PURE__*/jsxRuntime.jsx("div", {
2227
+ ref: localReference,
2228
+ className: classNames__default.default('sliding-panel', `sliding-panel--open-${position}`, showSlidingPanelBorder && `sliding-panel--border-${position}`, slidingPanelPositionFixed && 'sliding-panel--fixed', className),
2404
2229
  children: children
2405
- });
2406
- };
2407
- const AvatarWrapper = ({
2408
- url,
2409
- profileType,
2410
- profileId,
2411
- badgeUrl,
2412
- badgeAltText,
2413
- badgeStatusIcon,
2414
- name,
2415
- avatarProps,
2416
- badgeProps
2230
+ }));
2231
+ });
2232
+
2233
+ const Drawer = ({
2234
+ children,
2235
+ className,
2236
+ footerContent,
2237
+ headerTitle,
2238
+ onClose,
2239
+ open,
2240
+ position
2417
2241
  }) => {
2418
- const [hasImageLoadError, setImageLoadError] = React.useState(false);
2419
- const isBusinessProfile = profileType === exports.ProfileType.BUSINESS;
2420
- // Reset the errored state when url changes
2421
- React.useEffect(() => setImageLoadError(false), [url]);
2422
- const getAvatarProps = () => {
2423
- if (url && !hasImageLoadError) {
2424
- return {
2425
- type: exports.AvatarType.THUMBNAIL,
2426
- children: /*#__PURE__*/jsxRuntime.jsx("img", {
2427
- src: url,
2428
- alt: "",
2429
- onError: () => setImageLoadError(true)
2430
- }),
2431
- ...avatarProps
2432
- };
2433
- }
2434
- if (profileType) {
2435
- return {
2436
- type: exports.AvatarType.ICON,
2437
- children: isBusinessProfile ? /*#__PURE__*/jsxRuntime.jsx(icons.Briefcase, {
2438
- size: "24"
2439
- }) : /*#__PURE__*/jsxRuntime.jsx(icons.Person, {
2440
- size: "24"
2441
- }),
2442
- ...avatarProps
2443
- };
2444
- }
2445
- if (name) {
2446
- return {
2447
- type: exports.AvatarType.INITIALS,
2448
- children: /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
2449
- children: getInitials(name)
2450
- }),
2451
- backgroundColorSeed: profileId?.toString(),
2452
- ...avatarProps
2453
- };
2454
- }
2455
- return {
2456
- type: exports.AvatarType.ICON,
2457
- children: /*#__PURE__*/jsxRuntime.jsx(icons.Person, {
2458
- size: "24"
2459
- }),
2460
- ...avatarProps
2461
- };
2462
- };
2463
- return /*#__PURE__*/jsxRuntime.jsx(OptionalBadge, {
2464
- url: badgeUrl,
2465
- altText: badgeAltText,
2466
- statusIcon: badgeStatusIcon,
2467
- ...badgeProps,
2468
- children: /*#__PURE__*/jsxRuntime.jsx(Avatar, {
2469
- size: exports.Size.MEDIUM,
2470
- ...getAvatarProps()
2242
+ logActionRequiredIf('Drawer now expects `onClose`, and will soon make this prop required. Please update your usage to provide it.', !onClose);
2243
+ const {
2244
+ isMobile
2245
+ } = useLayout();
2246
+ const titleId = reactId.useId();
2247
+ return /*#__PURE__*/jsxRuntime.jsx(Dimmer$1, {
2248
+ open: open,
2249
+ onClose: onClose,
2250
+ children: /*#__PURE__*/jsxRuntime.jsx(SlidingPanel, {
2251
+ open: open,
2252
+ position: isMobile ? exports.Position.BOTTOM : position,
2253
+ children: /*#__PURE__*/jsxRuntime.jsxs("div", {
2254
+ role: "dialog",
2255
+ "aria-modal": true,
2256
+ "aria-labelledby": headerTitle ? titleId : undefined,
2257
+ className: classNames__default.default('np-drawer', className),
2258
+ children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
2259
+ className: classNames__default.default('np-drawer-header', {
2260
+ 'np-drawer-header--withborder': headerTitle
2261
+ }),
2262
+ children: [headerTitle && /*#__PURE__*/jsxRuntime.jsx(Title, {
2263
+ id: titleId,
2264
+ type: exports.Typography.TITLE_BODY,
2265
+ children: headerTitle
2266
+ }), /*#__PURE__*/jsxRuntime.jsx(CloseButton, {
2267
+ onClick: onClose
2268
+ })]
2269
+ }), children && /*#__PURE__*/jsxRuntime.jsx("div", {
2270
+ className: classNames__default.default('np-drawer-content'),
2271
+ children: children
2272
+ }), footerContent && /*#__PURE__*/jsxRuntime.jsx("div", {
2273
+ className: classNames__default.default('np-drawer-footer'),
2274
+ children: footerContent
2275
+ })]
2276
+ })
2471
2277
  })
2472
2278
  });
2473
2279
  };
2474
- function getInitials(name) {
2475
- const allInitials = name.split(' ').map(part => part[0]).join('').toUpperCase();
2476
- if (allInitials.length === 1) {
2477
- return allInitials[0];
2478
- }
2479
- return allInitials[0] + allInitials.slice(-1);
2480
- }
2481
-
2482
- const Card$1 = /*#__PURE__*/React.forwardRef((props, reference) => {
2483
- const {
2484
- 'aria-label': ariaLabel,
2485
- as: Element,
2486
- isExpanded,
2487
- title,
2488
- details,
2489
- children,
2490
- onClick,
2491
- icon,
2492
- id,
2493
- className,
2494
- ...rest
2495
- } = props;
2496
- const isOpen = !!(isExpanded && children);
2497
- return /*#__PURE__*/jsxRuntime.jsxs(Element, {
2498
- ref: reference,
2499
- className: classNames__default.default('np-card', className, {
2500
- 'np-card--expanded': isOpen,
2501
- 'np-card--inactive': !children,
2502
- 'np-card--has-icon': !!icon
2503
- }),
2504
- id: id,
2505
- "data-testid": rest['data-testid'],
2506
- children: [/*#__PURE__*/jsxRuntime.jsx(Option$2, {
2507
- "aria-label": ariaLabel,
2508
- as: children ? 'button' : 'div',
2509
- className: classNames__default.default('np-card__button'),
2510
- media: icon,
2511
- title: title,
2512
- content: details,
2513
- showMediaAtAllSizes: true,
2514
- button: children && /*#__PURE__*/jsxRuntime.jsx(Chevron, {
2515
- orientation: isOpen ? exports.Position.TOP : exports.Position.BOTTOM
2516
- }),
2517
- onClick: () => children && onClick(!isExpanded)
2518
- }), /*#__PURE__*/jsxRuntime.jsx("div", {
2519
- className: classNames__default.default('np-card__divider', {
2520
- 'np-card__divider--expanded': isOpen
2521
- })
2522
- }), isOpen && /*#__PURE__*/jsxRuntime.jsx(Body, {
2523
- as: "span",
2524
- type: exports.Typography.BODY_LARGE,
2525
- className: "np-card__content d-block",
2526
- children: children
2527
- })]
2528
- });
2529
- });
2530
- const hasChildren = ({
2531
- children
2532
- }) => children;
2533
- Card$1.propTypes = {
2534
- 'aria-label': PropTypes__default.default.string,
2535
- as: PropTypes__default.default.string,
2536
- isExpanded: requiredIf__default.default(PropTypes__default.default.bool, hasChildren),
2537
- title: PropTypes__default.default.node.isRequired,
2538
- details: PropTypes__default.default.node.isRequired,
2539
- onClick: requiredIf__default.default(PropTypes__default.default.func, hasChildren),
2540
- icon: PropTypes__default.default.node,
2280
+ Drawer.propTypes = {
2281
+ /** The content to appear in the drawer body. */
2541
2282
  children: PropTypes__default.default.node,
2542
- id: PropTypes__default.default.string,
2543
2283
  className: PropTypes__default.default.string,
2544
- 'data-testid': PropTypes__default.default.string
2284
+ /** The content to appear in the drawer footer. */
2285
+ footerContent: PropTypes__default.default.node,
2286
+ /** The content to appear in the drawer header. */
2287
+ headerTitle: PropTypes__default.default.node,
2288
+ /** The action to perform on close click. */
2289
+ onClose: PropTypes__default.default.func,
2290
+ /** The status of Drawer either open or not. */
2291
+ open: PropTypes__default.default.bool,
2292
+ /** The placement of Drawer on the screen either left or right. On mobile it will default to bottom. */
2293
+ position: PropTypes__default.default.oneOf(['left', 'right', 'bottom'])
2545
2294
  };
2546
- Card$1.defaultProps = {
2547
- 'aria-label': undefined,
2548
- as: 'div',
2295
+ Drawer.defaultProps = {
2549
2296
  children: null,
2550
- id: null,
2551
- className: null,
2552
- 'data-testid': null
2553
- };
2554
- var Card$2 = Card$1;
2555
-
2556
- const CheckboxButton = /*#__PURE__*/React.forwardRef(({
2557
- checked,
2558
- className,
2559
- disabled,
2560
- onChange,
2561
- ...rest
2562
- }, reference) => /*#__PURE__*/jsxRuntime.jsxs("span", {
2563
- className: classNames__default.default('np-checkbox-button', className, disabled && 'disabled'),
2564
- children: [/*#__PURE__*/jsxRuntime.jsx("input", {
2565
- ...rest,
2566
- ref: reference,
2567
- type: "checkbox",
2568
- disabled: disabled,
2569
- checked: checked,
2570
- onChange: onChange
2571
- }), /*#__PURE__*/jsxRuntime.jsx("span", {
2572
- className: "tw-checkbox-button",
2573
- children: /*#__PURE__*/jsxRuntime.jsx("span", {
2574
- className: "tw-checkbox-check"
2575
- })
2576
- })]
2577
- }));
2578
- var CheckboxButton$1 = CheckboxButton;
2579
-
2580
- const Checkbox = ({
2581
- id,
2582
- checked,
2583
- required,
2584
- disabled,
2585
- readOnly,
2586
- label,
2587
- className,
2588
- secondary,
2589
- onChange,
2590
- onFocus,
2591
- onBlur
2592
- }) => {
2593
- const {
2594
- isModern
2595
- } = componentsTheming.useTheme();
2596
- const classList = classNames__default.default('np-checkbox', {
2597
- checkbox: true,
2598
- 'checkbox-lg': secondary,
2599
- disabled: isModern && disabled
2600
- }, className);
2601
- const innerDisabled = disabled || readOnly;
2602
- return /*#__PURE__*/jsxRuntime.jsx("div", {
2603
- id: id,
2604
- className: classList,
2605
- children: /*#__PURE__*/jsxRuntime.jsxs("label", {
2606
- className: classNames__default.default({
2607
- disabled
2608
- }),
2609
- children: [/*#__PURE__*/jsxRuntime.jsx(CheckboxButton$1, {
2610
- className: "p-r-2",
2611
- checked: checked,
2612
- disabled: innerDisabled,
2613
- required: !innerDisabled && required,
2614
- onFocus: onFocus,
2615
- onChange: () => onChange(!checked),
2616
- onBlur: onBlur
2617
- }), /*#__PURE__*/jsxRuntime.jsxs(Body, {
2618
- as: "span",
2619
- className: "np-checkbox__text",
2620
- type: secondary ? exports.Typography.BODY_LARGE_BOLD : exports.Typography.BODY_LARGE,
2621
- children: [/*#__PURE__*/jsxRuntime.jsx("span", {
2622
- className: required ? 'has-required' : undefined,
2623
- children: label
2624
- }), secondary && /*#__PURE__*/jsxRuntime.jsx(Body, {
2625
- as: "span",
2626
- className: classNames__default.default({
2627
- secondary: !isModern
2628
- }),
2629
- children: secondary
2630
- })]
2631
- })]
2632
- })
2633
- });
2634
- };
2635
- Checkbox.propTypes = {
2636
- id: PropTypes__default.default.string,
2637
- checked: PropTypes__default.default.bool,
2638
- required: PropTypes__default.default.bool,
2639
- disabled: PropTypes__default.default.bool,
2640
- readOnly: PropTypes__default.default.bool,
2641
- label: PropTypes__default.default.node.isRequired,
2642
- secondary: PropTypes__default.default.string,
2643
- onFocus: PropTypes__default.default.func,
2644
- onChange: PropTypes__default.default.func.isRequired,
2645
- onBlur: PropTypes__default.default.func,
2646
- className: PropTypes__default.default.string
2647
- };
2648
- Checkbox.defaultProps = {
2649
- id: null,
2650
- checked: false,
2651
- required: false,
2652
- disabled: false,
2653
- readOnly: false,
2654
- secondary: null,
2655
- onFocus: null,
2656
- onBlur: null,
2657
- className: undefined
2297
+ className: undefined,
2298
+ footerContent: null,
2299
+ headerTitle: null,
2300
+ onClose: null,
2301
+ open: false,
2302
+ position: exports.Position.RIGHT
2658
2303
  };
2304
+ var Drawer$1 = Drawer;
2659
2305
 
2660
- const CheckboxOption = /*#__PURE__*/React.forwardRef(({
2661
- checked,
2662
- disabled,
2663
- onChange,
2664
- ...rest
2665
- }, reference) => {
2666
- return /*#__PURE__*/jsxRuntime.jsx(Option$2, {
2667
- ...rest,
2668
- ref: reference,
2669
- disabled: disabled,
2670
- button: /*#__PURE__*/jsxRuntime.jsx(CheckboxButton$1, {
2671
- checked: checked,
2672
- disabled: disabled,
2673
- onChange: () => onChange?.(!checked)
2674
- })
2306
+ const INITIAL_Y_POSITION = 0;
2307
+ const CONTENT_SCROLL_THRESHOLD = 1;
2308
+ const MOVE_OFFSET_THRESHOLD = 50;
2309
+ /**
2310
+ * Neptune: https://transferwise.github.io/neptune/components/bottom-sheet/
2311
+ *
2312
+ * Neptune Web: https://transferwise.github.io/neptune-web/components/overlays/BottomSheet
2313
+ *
2314
+ */
2315
+ const BottomSheet$1 = props => {
2316
+ const bottomSheetReference = React.useRef(null);
2317
+ const topBarReference = React.useRef(null);
2318
+ const contentReference = React.useRef(null);
2319
+ const [pressed, setPressed] = React.useState(false);
2320
+ /**
2321
+ * Used to track `requestAnimationFrame` requests
2322
+ *
2323
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame#return_value
2324
+ */
2325
+ const animationId = React.useRef(0);
2326
+ /**
2327
+ * Difference between initial coordinate ({@link initialYCoordinate})
2328
+ * and new position (when user moves component), it's get calculated on `onTouchMove` and `onMouseMove` events
2329
+ *
2330
+ * @see {@link calculateOffsetAfterMove}
2331
+ */
2332
+ const moveOffset = React.useRef(0);
2333
+ const initialYCoordinate = React.useRef(0);
2334
+ // apply shadow to the bottom of top-bar when scroll over content
2335
+ useConditionalListener({
2336
+ attachListener: props.open && !isServerSide(),
2337
+ callback: () => {
2338
+ if (topBarReference.current !== null) {
2339
+ const {
2340
+ classList
2341
+ } = topBarReference.current;
2342
+ if (!isContentScrollPositionAtTop()) {
2343
+ classList.add('np-bottom-sheet--top-bar--shadow');
2344
+ } else {
2345
+ classList.remove('np-bottom-sheet--top-bar--shadow');
2346
+ }
2347
+ }
2348
+ },
2349
+ eventType: 'scroll',
2350
+ parent: isServerSide() ? undefined : document
2675
2351
  });
2676
- });
2677
-
2678
- const Chip = ({
2679
- label,
2680
- value,
2681
- onRemove,
2682
- onClick,
2683
- onKeyPress,
2684
- className = undefined,
2685
- 'aria-label': ariaLabel,
2686
- 'aria-checked': ariaChecked,
2687
- role,
2688
- closeButton
2689
- }) => {
2690
- const isActionable = onClick || onKeyPress;
2691
- const defaultRole = isActionable ? 'button' : undefined;
2692
- const tabIndex = isActionable ? 0 : -1;
2693
- const {
2694
- isModern
2695
- } = componentsTheming.useTheme();
2696
- const closeButtonReference = React.useRef(null);
2697
- const previousCloseButtonShown = React.useRef();
2698
- React.useEffect(() => {
2699
- if (closeButtonReference.current != null && previousCloseButtonShown.current === false) {
2700
- closeButtonReference.current?.focus();
2352
+ function move(newHeight) {
2353
+ if (bottomSheetReference.current !== null) {
2354
+ bottomSheetReference.current.style.transform = `translateY(${newHeight}px)`;
2701
2355
  }
2702
- previousCloseButtonShown.current = closeButtonReference.current != null;
2703
- }, [onRemove]);
2704
- return /*#__PURE__*/jsxRuntime.jsxs("div", {
2705
- role: role ?? defaultRole,
2706
- tabIndex: tabIndex,
2707
- "aria-label": ariaLabel,
2708
- "aria-checked": ariaChecked,
2709
- className: classNames__default.default('np-chip', 'd-flex', 'align-items-center', 'justify-content-between', onRemove ? 'np-chip--removable' : '', className),
2710
- ...(isActionable && {
2711
- onClick,
2712
- onKeyPress
2713
- }),
2714
- children: [isModern ? /*#__PURE__*/jsxRuntime.jsx(Body, {
2715
- "aria-hidden": !!onRemove,
2716
- type: exports.Typography.BODY_DEFAULT_BOLD,
2717
- children: label
2718
- }) : /*#__PURE__*/jsxRuntime.jsx("span", {
2719
- "aria-hidden": "false",
2720
- className: "np-chip-label",
2721
- children: label
2722
- }), onRemove ? /*#__PURE__*/jsxRuntime.jsx(CloseButton, {
2723
- ref: closeButtonReference,
2724
- className: isModern ? `btn-unstyled` : `btn-unstyled m-l-1`,
2725
- "aria-label": closeButton && closeButton['aria-label'],
2726
- size: "sm",
2727
- filled: isModern,
2728
- onClick: onRemove
2729
- }) : null]
2730
- }, value);
2731
- };
2732
-
2733
- var messages$a = reactIntl.defineMessages({
2734
- ariaLabel: {
2735
- id: "neptune.Chips.ariaLabel"
2736
2356
  }
2737
- });
2738
-
2739
- const Chips = ({
2740
- chips,
2741
- onChange,
2742
- selected,
2743
- 'aria-label': ariaLabel,
2744
- className,
2745
- multiple
2746
- }) => {
2747
- const intl = reactIntl.useIntl();
2748
- const isSelected = value => Array.isArray(selected) ? selected.includes(value) : selected === value;
2749
- const handleOnChange = (selectedValue, isCurrentlyEnabled) => {
2750
- onChange({
2751
- isEnabled: !isCurrentlyEnabled,
2752
- selectedValue
2753
- });
2357
+ function close(event) {
2358
+ setPressed(false);
2359
+ moveOffset.current = INITIAL_Y_POSITION;
2360
+ if (bottomSheetReference.current !== null) {
2361
+ bottomSheetReference.current.style.removeProperty('transform');
2362
+ }
2363
+ if (props.onClose) {
2364
+ props.onClose(event);
2365
+ }
2366
+ }
2367
+ const onSwipeStart = event => {
2368
+ initialYCoordinate.current = ('touches' in event ? event.touches[0] : event).clientY;
2369
+ setPressed(true);
2754
2370
  };
2755
- return /*#__PURE__*/jsxRuntime.jsx("div", {
2756
- className: classNames__default.default('np-chips d-flex', className),
2757
- "aria-label": ariaLabel,
2758
- role: !multiple ? 'radiogroup' : undefined,
2759
- children: chips.map(chip => {
2760
- const chipSelected = isSelected(chip.value);
2761
- return /*#__PURE__*/jsxRuntime.jsx(Chip, {
2762
- value: chip.value,
2763
- label: chip.label,
2764
- closeButton: {
2765
- 'aria-label': intl.formatMessage(messages$a.ariaLabel, {
2766
- choice: chip.label
2767
- })
2768
- },
2769
- className: classNames__default.default('text-xs-nowrap', {
2770
- 'np-chip--selected': chipSelected,
2771
- 'p-r-1': multiple && chipSelected
2772
- }),
2773
- ...(multiple && chipSelected ? {
2774
- onRemove: () => handleOnChange(chip.value, chipSelected),
2775
- 'aria-label': chip.label
2776
- } : {
2777
- onClick: () => handleOnChange(chip.value, chipSelected),
2778
- onKeyPress: () => handleOnChange(chip.value, chipSelected),
2779
- role: !multiple ? 'radio' : undefined,
2780
- 'aria-checked': chipSelected
2781
- })
2782
- }, chip.value);
2371
+ const onSwipeMove = event => {
2372
+ if (pressed) {
2373
+ const {
2374
+ clientY
2375
+ } = 'touches' in event ? event.touches[0] : event;
2376
+ const offset = calculateOffsetAfterMove(clientY);
2377
+ // check whether move is to the bottom only and content scroll position is at the top
2378
+ if (offset > INITIAL_Y_POSITION && isContentScrollPositionAtTop()) {
2379
+ moveOffset.current = offset;
2380
+ animationId.current = requestAnimationFrame(() => {
2381
+ if (animationId.current !== undefined && bottomSheetReference.current !== null) {
2382
+ move(offset);
2383
+ }
2384
+ });
2385
+ }
2386
+ }
2387
+ };
2388
+ function onSwipeEnd(event) {
2389
+ // stop moving component
2390
+ cancelAnimationFrame(animationId.current);
2391
+ setPressed(false);
2392
+ // check whether move down is strong enough
2393
+ // and content scroll position is at the top to close the component
2394
+ if (moveOffset.current > MOVE_OFFSET_THRESHOLD && isContentScrollPositionAtTop()) {
2395
+ close(event);
2396
+ }
2397
+ // otherwise move component back to default (initial) position
2398
+ else {
2399
+ move(INITIAL_Y_POSITION);
2400
+ }
2401
+ moveOffset.current = INITIAL_Y_POSITION;
2402
+ }
2403
+ function isContentScrollPositionAtTop() {
2404
+ return contentReference?.current?.scrollTop !== undefined && contentReference.current.scrollTop <= CONTENT_SCROLL_THRESHOLD;
2405
+ }
2406
+ /**
2407
+ * Calculates how hard user moves component,
2408
+ * result value used to determine whether to hide component or re-position to default state
2409
+ *
2410
+ * @param afterMoveYCoordinate
2411
+ */
2412
+ function calculateOffsetAfterMove(afterMoveYCoordinate) {
2413
+ return afterMoveYCoordinate - initialYCoordinate.current;
2414
+ }
2415
+ /**
2416
+ * Set `max-height` for content part (in order to keep it scrollable for content overflow cases) of the component
2417
+ * and ensures space for safe zone (32px) at the top.
2418
+ */
2419
+ function setContentMaxHeight() {
2420
+ const safeZoneHeight = '64px';
2421
+ const topbarHeight = '32px';
2422
+ const windowHight = isServerSide() ? 0 : window.innerHeight;
2423
+ /**
2424
+ * Calculate _real_ height of the screen (taking into account parts of browser interface).
2425
+ *
2426
+ * See https://css-tricks.com/the-trick-to-viewport-units-on-mobile for more details.
2427
+ */
2428
+ const screenHeight = `${windowHight * 0.01 * 100}px`;
2429
+ return {
2430
+ maxHeight: `calc(${screenHeight} - ${safeZoneHeight} - ${topbarHeight})`
2431
+ };
2432
+ }
2433
+ const is400Zoom = useMedia(`(max-width: ${exports.Breakpoint.ZOOM_400}px)`);
2434
+ return is400Zoom ? /*#__PURE__*/jsxRuntime.jsx(Drawer$1, {
2435
+ open: props.open,
2436
+ className: props.className,
2437
+ onClose: close,
2438
+ children: props.children
2439
+ }) : /*#__PURE__*/jsxRuntime.jsx(Dimmer$1, {
2440
+ open: props.open,
2441
+ fadeContentOnEnter: true,
2442
+ fadeContentOnExit: true,
2443
+ onClose: close,
2444
+ children: /*#__PURE__*/jsxRuntime.jsx(SlidingPanel, {
2445
+ ref: bottomSheetReference,
2446
+ open: props.open,
2447
+ position: exports.Position.BOTTOM,
2448
+ className: classNames__default.default('np-bottom-sheet', props.className),
2449
+ children: /*#__PURE__*/jsxRuntime.jsxs("div", {
2450
+ role: "dialog",
2451
+ "aria-modal": true,
2452
+ onTouchStart: onSwipeStart,
2453
+ onTouchMove: onSwipeMove,
2454
+ onTouchEnd: onSwipeEnd,
2455
+ onMouseDown: onSwipeStart,
2456
+ onMouseMove: onSwipeMove,
2457
+ onMouseUp: onSwipeEnd,
2458
+ children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
2459
+ ref: topBarReference,
2460
+ className: "np-bottom-sheet--top-bar",
2461
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
2462
+ className: "np-bottom-sheet--handler"
2463
+ }), /*#__PURE__*/jsxRuntime.jsx(CloseButton, {
2464
+ size: "sm",
2465
+ className: "sr-only np-bottom-sheet--close-btn",
2466
+ onClick: close
2467
+ })]
2468
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
2469
+ ref: contentReference,
2470
+ style: setContentMaxHeight(),
2471
+ className: "np-bottom-sheet--content",
2472
+ children: props.children
2473
+ })]
2474
+ })
2783
2475
  })
2784
2476
  });
2785
2477
  };
2786
2478
 
2787
- const CircularButton = ({
2788
- className,
2789
- children,
2790
- disabled,
2791
- icon,
2792
- priority = exports.Priority.PRIMARY,
2793
- type = exports.ControlType.ACCENT,
2794
- ...rest
2795
- }) => {
2796
- const classes = classNames__default.default('btn np-btn', typeClassMap[type], priorityClassMap[priority]);
2797
- const iconElement = Number(icon.props.size) !== 24 ? /*#__PURE__*/React.cloneElement(icon, {
2798
- size: 24
2799
- }) : icon;
2800
- return /*#__PURE__*/jsxRuntime.jsxs("label", {
2801
- className: classNames__default.default('np-circular-btn', priority, type, disabled && 'disabled', className),
2802
- children: [/*#__PURE__*/jsxRuntime.jsx("input", {
2803
- type: "button",
2804
- "aria-label": children,
2805
- className: classes,
2806
- disabled: disabled,
2807
- ...rest
2808
- }), iconElement, /*#__PURE__*/jsxRuntime.jsx(Body, {
2809
- as: "span",
2810
- className: "np-circular-btn__label",
2811
- type: exports.Typography.BODY_DEFAULT_BOLD,
2812
- children: children
2813
- })]
2814
- });
2815
- };
2816
-
2817
2479
  const Card = ({
2818
2480
  className,
2819
2481
  children = null,
@@ -3326,6 +2988,124 @@ function shouldPropagateOnBlur({
3326
2988
  return blurElementParent !== focusElementParent;
3327
2989
  }
3328
2990
 
2991
+ const POPOVER_OFFSET = [0, 16];
2992
+ // By default the flip positioning explores only the opposite alternative. So if left is passed and there's no enough space
2993
+ // the right one gets chosen. If there's no space on both sides popover goes back to the initially chosen one left.
2994
+ // This mapping forces popover to try the four available positions before going back to the initial chosen one.
2995
+ const fallbackPlacements = {
2996
+ [exports.Position.TOP]: [exports.Position.BOTTOM, exports.Position.RIGHT, exports.Position.LEFT],
2997
+ [exports.Position.BOTTOM]: [exports.Position.TOP, exports.Position.RIGHT, exports.Position.LEFT],
2998
+ [exports.Position.LEFT]: [exports.Position.RIGHT, exports.Position.TOP, exports.Position.BOTTOM],
2999
+ [exports.Position.RIGHT]: [exports.Position.LEFT, exports.Position.TOP, exports.Position.BOTTOM]
3000
+ };
3001
+ const Panel = /*#__PURE__*/React.forwardRef(({
3002
+ arrow = false,
3003
+ flip = true,
3004
+ altAxis = false,
3005
+ children,
3006
+ open = false,
3007
+ onClose,
3008
+ position = exports.Position.BOTTOM,
3009
+ anchorRef,
3010
+ anchorWidth = false,
3011
+ ...rest
3012
+ }, reference) => {
3013
+ const [arrowElement, setArrowElement] = React.useState(null);
3014
+ const [popperElement, setPopperElement] = React.useState(null);
3015
+ const modifiers = [];
3016
+ if (altAxis) {
3017
+ modifiers.push({
3018
+ // https://popper.js.org/docs/v2/modifiers/prevent-overflow
3019
+ name: 'preventOverflow',
3020
+ options: {
3021
+ altAxis: true,
3022
+ tether: false
3023
+ }
3024
+ });
3025
+ }
3026
+ if (arrow) {
3027
+ modifiers.push({
3028
+ name: 'arrow',
3029
+ options: {
3030
+ element: arrowElement,
3031
+ options: {
3032
+ padding: 8 // 8px from the edges of the popper
3033
+ }
3034
+ }
3035
+ });
3036
+ // This lets you displace a popper element from its reference element.
3037
+ modifiers.push({
3038
+ name: 'offset',
3039
+ options: {
3040
+ offset: POPOVER_OFFSET
3041
+ }
3042
+ });
3043
+ }
3044
+ if (flip && fallbackPlacements[position]) {
3045
+ modifiers.push({
3046
+ name: 'flip',
3047
+ options: {
3048
+ fallbackPlacements: fallbackPlacements[position]
3049
+ }
3050
+ });
3051
+ }
3052
+ const {
3053
+ styles,
3054
+ attributes,
3055
+ forceUpdate
3056
+ } = reactPopper.usePopper(anchorRef.current, popperElement, {
3057
+ placement: position,
3058
+ modifiers
3059
+ });
3060
+ // If the trigger is not visible when the position is calculated, it will be incorrect. Because this can happen repeatedly (on resize for example),
3061
+ // it is most simple just to always position before opening
3062
+ React.useEffect(() => {
3063
+ if (open && forceUpdate) {
3064
+ forceUpdate();
3065
+ }
3066
+ }, [open]);
3067
+ const contentStyle = {
3068
+ ...(anchorWidth ? {
3069
+ width: anchorRef.current?.clientWidth
3070
+ } : undefined)
3071
+ };
3072
+ return /*#__PURE__*/jsxRuntime.jsx(Dimmer$1, {
3073
+ open: open,
3074
+ transparent: true,
3075
+ fadeContentOnEnter: true,
3076
+ fadeContentOnExit: true,
3077
+ onClose: onClose,
3078
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
3079
+ ...rest,
3080
+ ref: setPopperElement,
3081
+ role: "dialog"
3082
+ // eslint-disable-next-line react/forbid-dom-props
3083
+ ,
3084
+ style: {
3085
+ ...styles.popper
3086
+ },
3087
+ ...attributes.popper,
3088
+ className: classNames__default.default('np-panel', {
3089
+ 'np-panel--open': open
3090
+ }, rest['className']),
3091
+ children: /*#__PURE__*/jsxRuntime.jsxs("div", {
3092
+ ref: reference
3093
+ /* eslint-disable-next-line react/forbid-dom-props */,
3094
+ style: contentStyle,
3095
+ className: classNames__default.default('np-panel__content'),
3096
+ children: [children, arrow && /*#__PURE__*/jsxRuntime.jsx("div", {
3097
+ ref: setArrowElement,
3098
+ className: classNames__default.default('np-panel__arrow')
3099
+ // eslint-disable-next-line react/forbid-dom-props
3100
+ ,
3101
+ style: styles.arrow
3102
+ })]
3103
+ })
3104
+ })
3105
+ });
3106
+ });
3107
+ var Panel$1 = Panel;
3108
+
3329
3109
  const ResponsivePanel = /*#__PURE__*/React.forwardRef(({
3330
3110
  anchorRef,
3331
3111
  arrow = false,
@@ -3347,7 +3127,7 @@ const ResponsivePanel = /*#__PURE__*/React.forwardRef(({
3347
3127
  children: children
3348
3128
  }, "bottomSheet");
3349
3129
  }
3350
- return /*#__PURE__*/jsxRuntime.jsx(Panel, {
3130
+ return /*#__PURE__*/jsxRuntime.jsx(Panel$1, {
3351
3131
  ref: reference,
3352
3132
  flip: flip,
3353
3133
  arrow: arrow,
@@ -5245,6 +5025,73 @@ const Field = ({
5245
5025
  });
5246
5026
  };
5247
5027
 
5028
+ const HeaderAction = ({
5029
+ action
5030
+ }) => {
5031
+ const {
5032
+ isModern
5033
+ } = componentsTheming.useTheme();
5034
+ const props = {
5035
+ 'aria-label': action['aria-label']
5036
+ };
5037
+ if ('href' in action) {
5038
+ return /*#__PURE__*/jsxRuntime.jsx(Link, {
5039
+ href: action.href,
5040
+ target: action.target,
5041
+ onClick: action.onClick,
5042
+ ...props,
5043
+ children: action.text
5044
+ });
5045
+ }
5046
+ return isModern ? /*#__PURE__*/jsxRuntime.jsx(Button, {
5047
+ className: "np-header__button",
5048
+ priority: "tertiary",
5049
+ size: "sm",
5050
+ onClick: action.onClick,
5051
+ ...props,
5052
+ children: action.text
5053
+ }) : /*#__PURE__*/jsxRuntime.jsx(ActionButton, {
5054
+ onClick: action.onClick,
5055
+ ...props,
5056
+ children: action.text
5057
+ });
5058
+ };
5059
+ /**
5060
+ *
5061
+ * Neptune Web: https://transferwise.github.io/neptune-web/components/content/Header
5062
+ *
5063
+ */
5064
+ const Header = ({
5065
+ action,
5066
+ as = 'h5',
5067
+ title,
5068
+ className
5069
+ }) => {
5070
+ if (!action) {
5071
+ return /*#__PURE__*/jsxRuntime.jsx(Title, {
5072
+ as: as,
5073
+ type: exports.Typography.TITLE_GROUP,
5074
+ className: classNames__default.default('np-header', 'np-header__title', className),
5075
+ children: title
5076
+ });
5077
+ }
5078
+ if (as === 'legend') {
5079
+ // eslint-disable-next-line no-console
5080
+ console.warn('Legends should be the first child in a fieldset, and this is not possible when including an action');
5081
+ }
5082
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
5083
+ className: classNames__default.default('np-header', className),
5084
+ children: [/*#__PURE__*/jsxRuntime.jsx(Title, {
5085
+ as: as,
5086
+ type: exports.Typography.TITLE_GROUP,
5087
+ className: "np-header__title",
5088
+ children: title
5089
+ }), /*#__PURE__*/jsxRuntime.jsx(HeaderAction, {
5090
+ action: action
5091
+ })]
5092
+ });
5093
+ };
5094
+
5248
5095
  const EmptyTransparentImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
5249
5096
  const Image = ({
5250
5097
  id,
@@ -9000,6 +8847,12 @@ const PhoneNumberInput = ({
9000
8847
  return explodeNumberModel(cleanValue);
9001
8848
  });
9002
8849
  const [broadcastedValue, setBroadcastedValue] = React.useState(null);
8850
+ const [suffixDirty, setSuffixDirty] = React.useState(false);
8851
+ React.useEffect(() => {
8852
+ if (internalValue.suffix) {
8853
+ setSuffixDirty(true);
8854
+ }
8855
+ }, [internalValue.suffix]);
9003
8856
  const countriesByPrefix = React.useMemo(() => groupCountriesByPrefix(sortArrayByProperty(excludeCountries(countries, disabledCountries), 'iso3')), [disabledCountries]);
9004
8857
  const onSuffixChange = event => {
9005
8858
  const suffix = event.target.value;
@@ -9063,6 +8916,11 @@ const PhoneNumberInput = ({
9063
8916
  format: country?.phoneFormat
9064
8917
  }));
9065
8918
  },
8919
+ onClose: () => {
8920
+ if (suffixDirty) {
8921
+ onBlur?.();
8922
+ }
8923
+ },
9066
8924
  ...selectProps
9067
8925
  })
9068
8926
  }), /*#__PURE__*/jsxRuntime.jsx("div", {
@@ -9082,7 +8940,7 @@ const PhoneNumberInput = ({
9082
8940
  onChange: onSuffixChange,
9083
8941
  onPaste: onPaste,
9084
8942
  onFocus: onFocus,
9085
- onBlur: onBlur
8943
+ onBlur: () => onBlur?.()
9086
8944
  })
9087
8945
  })
9088
8946
  })]
@@ -9692,6 +9550,19 @@ function RadioOption({
9692
9550
  });
9693
9551
  }
9694
9552
 
9553
+ const Section = ({
9554
+ children,
9555
+ className,
9556
+ withHorizontalPadding = false
9557
+ }) => {
9558
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
9559
+ className: classNames__default.default('np-section', className, {
9560
+ 'np-section--with-horizontal-padding': withHorizontalPadding
9561
+ }),
9562
+ children: children
9563
+ });
9564
+ };
9565
+
9695
9566
  const SegmentedControl = ({
9696
9567
  name,
9697
9568
  value,
@@ -10358,7 +10229,7 @@ function Select({
10358
10229
  children: renderOptionsList({
10359
10230
  className: isModern ? '' : 'p-a-1'
10360
10231
  })
10361
- }) : /*#__PURE__*/jsxRuntime.jsx(Panel, {
10232
+ }) : /*#__PURE__*/jsxRuntime.jsx(Panel$1, {
10362
10233
  open: open,
10363
10234
  flip: false,
10364
10235
  altAxis: true,
@@ -14720,7 +14591,6 @@ exports.Select = Select;
14720
14591
  exports.SelectInput = SelectInput;
14721
14592
  exports.SelectInputOptionContent = SelectInputOptionContent;
14722
14593
  exports.SelectInputTriggerButton = SelectInputTriggerButton;
14723
- exports.SelectOption = SelectOption;
14724
14594
  exports.SlidingPanel = SlidingPanel;
14725
14595
  exports.SnackbarConsumer = SnackbarConsumer;
14726
14596
  exports.SnackbarContext = SnackbarContext;