@transferwise/components 0.0.0-experimental-5125346 → 0.0.0-experimental-3b84ee4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/build/i18n/en.json +1 -0
  2. package/build/index.js +2442 -2335
  3. package/build/index.js.map +1 -1
  4. package/build/index.mjs +2444 -2338
  5. package/build/index.mjs.map +1 -1
  6. package/build/main.css +54 -65
  7. package/build/styles/main.css +54 -65
  8. package/build/styles/selectOption/SelectOption.css +42 -0
  9. package/build/styles/uploadInput/UploadInput.css +1 -18
  10. package/build/styles/uploadInput/uploadButton/UploadButton.css +0 -4
  11. package/build/styles/uploadInput/uploadItem/UploadItem.css +11 -43
  12. package/build/types/common/responsivePanel/ResponsivePanel.d.ts.map +1 -1
  13. package/build/types/index.d.ts +2 -0
  14. package/build/types/index.d.ts.map +1 -1
  15. package/build/types/inlineAlert/InlineAlert.d.ts +2 -2
  16. package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
  17. package/build/types/selectOption/SelectOption.d.ts +21 -0
  18. package/build/types/selectOption/SelectOption.d.ts.map +1 -0
  19. package/build/types/selectOption/SelectOption.messages.d.ts +8 -0
  20. package/build/types/selectOption/SelectOption.messages.d.ts.map +1 -0
  21. package/build/types/selectOption/index.d.ts +3 -0
  22. package/build/types/selectOption/index.d.ts.map +1 -0
  23. package/package.json +1 -1
  24. package/src/common/responsivePanel/ResponsivePanel.tsx +2 -0
  25. package/src/i18n/en.json +1 -0
  26. package/src/index.ts +2 -0
  27. package/src/inlineAlert/InlineAlert.tsx +1 -1
  28. package/src/main.css +54 -65
  29. package/src/main.less +1 -0
  30. package/src/selectOption/SelectOption.css +42 -0
  31. package/src/selectOption/SelectOption.less +40 -0
  32. package/src/selectOption/SelectOption.messages.ts +8 -0
  33. package/src/selectOption/SelectOption.spec.tsx +101 -0
  34. package/src/selectOption/SelectOption.story.tsx +230 -0
  35. package/src/selectOption/SelectOption.tsx +157 -0
  36. package/src/selectOption/index.ts +2 -0
  37. package/src/ssr.spec.js +1 -0
  38. package/src/uploadInput/UploadInput.css +1 -18
  39. package/src/uploadInput/UploadInput.less +1 -17
  40. package/src/uploadInput/UploadInput.tests.story.tsx +2 -13
  41. package/src/uploadInput/uploadButton/UploadButton.css +0 -4
  42. package/src/uploadInput/uploadButton/UploadButton.less +0 -5
  43. package/src/uploadInput/uploadItem/UploadItem.css +11 -43
  44. package/src/uploadInput/uploadItem/UploadItem.less +17 -61
package/build/index.mjs CHANGED
@@ -1,21 +1,21 @@
1
1
  import * as React from 'react';
2
- import React__default, { forwardRef, useId, cloneElement, useState, useEffect, useRef, useMemo, Component, createContext, useContext, useSyncExternalStore, useCallback, useImperativeHandle, createElement, PureComponent, createRef, isValidElement, Children, Fragment as Fragment$1 } from 'react';
2
+ import React__default, { forwardRef, useId, cloneElement, useState, Component, Children, createContext, useContext, useRef, useEffect, useCallback, useSyncExternalStore, useImperativeHandle, createElement, useMemo, PureComponent, createRef, isValidElement, Fragment as Fragment$1 } from 'react';
3
3
  import classNames from 'classnames';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { ChevronUp, CrossCircleFill, Cross, Check, Info as Info$1, Alert as Alert$1, ClockBorderless, NavigateAway, Briefcase, Person, ArrowRight, Download, ChevronLeft, ChevronRight, AlertCircleFill, ArrowLeft, QuestionMarkCircle, Search, CrossCircle, ChevronDown, CheckCircleFill, ClockFill, Upload as Upload$2, Document, Plus, PlusCircle, Bin } from '@transferwise/icons';
6
- import { defineMessages, useIntl, injectIntl, IntlProvider } from 'react-intl';
7
- import commonmark from 'commonmark';
5
+ import { ChevronUp, NavigateAway, CrossCircleFill, Cross, Plus, Check, Info as Info$1, Alert as Alert$1, ClockBorderless, Briefcase, Person, ArrowRight, Download, ChevronLeft, ChevronRight, AlertCircleFill, ArrowLeft, QuestionMarkCircle, Search, CrossCircle, ChevronDown, CheckCircleFill, ClockFill, Upload as Upload$2, Document, PlusCircle, Bin } from '@transferwise/icons';
8
6
  import { useTheme, ThemeProvider } from '@wise/components-theming';
7
+ import { defineMessages, useIntl, injectIntl, IntlProvider } from 'react-intl';
9
8
  import { CSSTransition } from 'react-transition-group';
10
9
  import { FocusScope } from '@react-aria/focus';
11
10
  import { createPortal } from 'react-dom';
12
11
  import { isUndefined, isNumber, isEmpty, isNull } from '@transferwise/neptune-validation';
12
+ import { usePopper } from 'react-popper';
13
+ import commonmark from 'commonmark';
13
14
  import { formatDate, formatMoney, formatAmount } from '@transferwise/formatting';
14
15
  import { Transition, Listbox } from '@headlessui/react';
15
16
  import mergeProps from 'merge-props';
16
17
  import { useFloating, useDismiss, useRole, useInteractions, FloatingPortal, FloatingFocusManager, offset, flip, shift, size, autoUpdate } from '@floating-ui/react';
17
18
  import { usePreventScroll } from '@react-aria/overlays';
18
- import { usePopper } from 'react-popper';
19
19
  import { Flag, Illustration } from '@wise/art';
20
20
  import PropTypes from 'prop-types';
21
21
  import clamp$2 from 'lodash.clamp';
@@ -663,97 +663,143 @@ const ActionOption = ({
663
663
  });
664
664
  };
665
665
 
666
- var messages$d = defineMessages({
667
- ariaLabel: {
668
- id: "neptune.CloseButton.ariaLabel"
669
- }
670
- });
671
-
672
- const CloseButton = /*#__PURE__*/forwardRef(function CloseButton({
673
- 'aria-label': ariaLabel,
674
- size = Size.MEDIUM,
675
- filled = false,
666
+ const Section = ({
667
+ children,
676
668
  className,
677
- onClick,
678
- isDisabled,
679
- testId
680
- }, reference) {
681
- const intl = useIntl();
682
- ariaLabel ??= intl.formatMessage(messages$d.ariaLabel);
683
- const Icon = filled ? CrossCircleFill : Cross;
684
- return /*#__PURE__*/jsx("button", {
685
- ref: reference,
686
- type: "button",
687
- className: classNames('np-close-button', 'close btn-link', 'text-no-decoration', {
688
- 'np-close-button--large': size === Size.MEDIUM,
689
- 'np-close-button--x-large': size === Size.LARGE
690
- }, className),
691
- "aria-label": ariaLabel,
692
- "aria-disabled": isDisabled,
693
- disabled: isDisabled,
694
- "data-testid": testId,
695
- onClick: onClick,
696
- children: /*#__PURE__*/jsx(Icon, {
697
- size: size === Size.SMALL ? 16 : 24
698
- })
699
- });
700
- });
701
-
702
- const iconTypeMap = {
703
- positive: Check,
704
- neutral: Info$1,
705
- warning: Alert$1,
706
- negative: Cross,
707
- pending: ClockBorderless,
708
- info: Info$1,
709
- error: Cross,
710
- success: Check
711
- };
712
- const StatusIcon = ({
713
- sentiment = 'neutral',
714
- size = 'md'
669
+ withHorizontalPadding = false
715
670
  }) => {
716
- const Icon = iconTypeMap[sentiment];
717
- const iconColor = sentiment === 'warning' || sentiment === 'pending' ? 'dark' : 'light';
718
- return /*#__PURE__*/jsx("span", {
719
- "data-testid": "status-icon",
720
- className: classNames('status-circle', `status-circle-${size}`, sentiment),
721
- children: /*#__PURE__*/jsx(Icon, {
722
- className: classNames('status-icon', iconColor)
723
- })
671
+ return /*#__PURE__*/jsx("div", {
672
+ className: classNames('np-section', className, {
673
+ 'np-section--with-horizontal-padding': withHorizontalPadding
674
+ }),
675
+ children: children
724
676
  });
725
677
  };
726
678
 
727
- const DEFAULT_TYPE = Typography.TITLE_GROUP;
728
- const titleTypeMapping = {
729
- [Typography.TITLE_SCREEN]: 'h1',
730
- [Typography.TITLE_SECTION]: 'h2',
731
- [Typography.TITLE_SUBSECTION]: 'h3',
732
- [Typography.TITLE_BODY]: 'h4',
733
- [Typography.TITLE_GROUP]: 'h5'
679
+ const radius = {
680
+ xxs: 6,
681
+ xs: 11,
682
+ sm: 22,
683
+ xl: 61
734
684
  };
735
- function Title({
736
- as,
737
- type = DEFAULT_TYPE,
738
- className,
739
- ...props
740
- }) {
741
- const mapping = titleTypeMapping[type];
742
- const isTypeSupported = mapping !== undefined;
743
- if (isTypeSupported) {
744
- const HeaderTag = as ?? mapping;
745
- return /*#__PURE__*/jsx(HeaderTag, {
746
- ...props,
747
- className: classNames(`np-text-${type}`, className)
685
+ const ANIMATION_DURATION_IN_MS = 1500;
686
+ class ProcessIndicator extends Component {
687
+ static defaultProps = {
688
+ status: 'processing',
689
+ size: 'sm'
690
+ };
691
+ interval = 0;
692
+ timeout = 0;
693
+ constructor(props) {
694
+ super(props);
695
+ this.state = {
696
+ status: props.status,
697
+ size: props.size
698
+ };
699
+ }
700
+ /**
701
+ * Create interval for animation duration (1500ms)
702
+ * Update state only at the end of every interval
703
+ * (end of animation loop) if props changed before end of animation
704
+ */
705
+ componentDidMount() {
706
+ this.interval = window.setInterval(() => {
707
+ const {
708
+ status: targetStatus,
709
+ size: targetSize,
710
+ onAnimationCompleted
711
+ } = this.props;
712
+ const {
713
+ status: currentStatus,
714
+ size: currentSize
715
+ } = this.state;
716
+ if (currentStatus !== targetStatus) {
717
+ this.setState({
718
+ status: targetStatus
719
+ }, () => {
720
+ if (onAnimationCompleted) {
721
+ this.timeout = window.setTimeout(() => {
722
+ onAnimationCompleted(targetStatus);
723
+ }, ANIMATION_DURATION_IN_MS);
724
+ }
725
+ });
726
+ }
727
+ if (currentSize !== targetSize) {
728
+ this.setState({
729
+ size: targetSize
730
+ });
731
+ }
732
+ }, ANIMATION_DURATION_IN_MS);
733
+ }
734
+ /**
735
+ * Only trigger render if comopnent's state got
736
+ * updated from interval callback
737
+ */
738
+ shouldComponentUpdate(nextProps, nextState) {
739
+ const isSameStatus = nextProps.status === nextState.status;
740
+ const isSameSize = nextProps.size === nextState.size;
741
+ return isSameStatus && isSameSize;
742
+ }
743
+ // Clear interval before destroying component
744
+ componentWillUnmount() {
745
+ window.clearInterval(this.interval);
746
+ window.clearTimeout(this.timeout);
747
+ }
748
+ render() {
749
+ const {
750
+ className,
751
+ 'data-testid': dataTestId
752
+ } = this.props;
753
+ const {
754
+ size,
755
+ status
756
+ } = this.state;
757
+ const classes = classNames(`process process-${size}`, className, {
758
+ [`process-danger`]: status === Status.FAILED,
759
+ [`process-stopped`]: status === Status.HIDDEN,
760
+ [`process-success`]: status === Status.SUCCEEDED
761
+ });
762
+ return /*#__PURE__*/jsxs("span", {
763
+ className: classes,
764
+ "data-testid": dataTestId,
765
+ children: [/*#__PURE__*/jsxs("span", {
766
+ className: "process-icon-container",
767
+ children: [/*#__PURE__*/jsx("span", {
768
+ className: "process-icon-horizontal"
769
+ }), /*#__PURE__*/jsx("span", {
770
+ className: "process-icon-vertical"
771
+ })]
772
+ }), /*#__PURE__*/jsx("svg", {
773
+ xmlns: "http://www.w3.org/2000/svg",
774
+ children: /*#__PURE__*/jsx("circle", {
775
+ className: "process-circle",
776
+ cx: "50%",
777
+ cy: "50%",
778
+ r: radius[size],
779
+ fillOpacity: "0.0"
780
+ })
781
+ })]
748
782
  });
749
783
  }
750
- const HeaderTag = as ?? titleTypeMapping[DEFAULT_TYPE];
751
- return /*#__PURE__*/jsx(HeaderTag, {
752
- ...props,
753
- className: classNames(`np-text-${DEFAULT_TYPE}`, className)
754
- });
755
784
  }
756
785
 
786
+ var messages$e = defineMessages({
787
+ loadingAriaLabel: {
788
+ id: "neptune.Button.loadingAriaLabel"
789
+ }
790
+ });
791
+
792
+ const typeClassMap = {
793
+ [ControlType.ACCENT]: 'btn-accent',
794
+ [ControlType.POSITIVE]: 'btn-positive',
795
+ [ControlType.NEGATIVE]: 'btn-negative'
796
+ };
797
+ const priorityClassMap = {
798
+ [Priority.PRIMARY]: 'btn-priority-1',
799
+ [Priority.SECONDARY]: 'btn-priority-2',
800
+ [Priority.TERTIARY]: 'btn-priority-3'
801
+ };
802
+
757
803
  function logActionRequired(message) {
758
804
  if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
759
805
  // eslint-disable-next-line no-console
@@ -766,79 +812,117 @@ function logActionRequiredIf(message, conditional) {
766
812
  }
767
813
  }
768
814
 
769
- const reader = new commonmark.Parser();
770
- const writer = new commonmark.HtmlRenderer({
771
- safe: true
772
- });
773
- const NODE_TYPE_LIST = Object.values(MarkdownNodeType);
774
- function Markdown({
775
- as: Element = 'div',
776
- allowList,
777
- blockList,
778
- config,
779
- className,
780
- children
781
- }) {
782
- if (!children) {
783
- return null;
815
+ const deprecatedTypeMap = {
816
+ [Type.PRIMARY]: ControlType.ACCENT,
817
+ [Type.SECONDARY]: ControlType.ACCENT,
818
+ [Type.LINK]: ControlType.ACCENT,
819
+ [Type.PAY]: ControlType.POSITIVE,
820
+ [Type.DANGER]: ControlType.NEGATIVE
821
+ };
822
+ const oldTypePriorityMap = {
823
+ [Type.DANGER]: Priority.SECONDARY,
824
+ [Type.LINK]: Priority.TERTIARY,
825
+ [Type.SECONDARY]: Priority.SECONDARY
826
+ };
827
+ const deprecatedTypeMapMessage = {
828
+ [Type.DANGER]: 'Type.NEGATIVE',
829
+ [Type.LINK]: 'ControlType.ACCENT with priority Priority.TERTIARY',
830
+ [Type.PAY]: 'ControlType.POSITIVE',
831
+ [Type.PRIMARY]: 'ControlType.ACCENT',
832
+ [Type.SECONDARY]: 'ControlType.ACCENT with priority Priority.SECONDARY'
833
+ };
834
+ const deprecatedTypes = Object.keys(deprecatedTypeMap);
835
+ const establishNewType = originalType => deprecatedTypeMap[originalType] || originalType;
836
+ const establishNewPriority = (originalPriority, originalType) => {
837
+ const type = establishNewType(originalType);
838
+
839
+ // The old SECONDARY and LINK types now map to priorities. If they're still using one of
840
+ // these old types, ignore whatever priority they've passed and use this instead.
841
+ if (oldTypePriorityMap[originalType]) {
842
+ return oldTypePriorityMap[originalType];
784
843
  }
785
- const linkTarget = config?.link?.target ?? '_self';
786
- const paragraphClass = config?.paragraph?.className ?? '';
787
- if (allowList != null && blockList != null) {
788
- logActionRequired('Markdown supports only one of `allowList` or `blockList` to be used at a time. `blockList` will be ignored.');
844
+ // Only ControlType.ACCENT supports tertiary styles
845
+ if (originalPriority === Priority.TERTIARY && type !== ControlType.ACCENT) {
846
+ return Priority.SECONDARY;
789
847
  }
790
- const parser = nodes => {
791
- const parsed = reader.parse(nodes);
792
- const toExclude = allowList != null ? NODE_TYPE_LIST.filter(type => !allowList.includes(type)) : blockList;
793
- return toExclude != null ? stripNodes({
794
- parsed,
795
- blockList: toExclude
796
- }) : parsed;
797
- };
798
- const createMarkup = () => {
799
- const parsed = parser(children);
800
- return writer.render(parsed).replace(/<a href="/g, `<a target="${linkTarget}" href="`).replace(/<p>/g, `<p class="${paragraphClass}">`);
801
- };
802
- return /*#__PURE__*/jsx(Element, {
803
- className: className,
804
- dangerouslySetInnerHTML: {
805
- __html: createMarkup()
806
- }
848
+ return originalPriority;
849
+ };
850
+ const logDeprecationNotices = ({
851
+ size,
852
+ type
853
+ }) => {
854
+ logActionRequiredIf('Button has deprecated the `Size.EXTRA_SMALL` value for the `size` prop. Please use Size.SMALL instead.', size === Size.EXTRA_SMALL);
855
+ logActionRequiredIf(`Button has deprecated the ${type} value for the \`type\` prop. Please update to ${deprecatedTypeMapMessage[type]}.`, deprecatedTypes.includes(type));
856
+ };
857
+
858
+ const Button = /*#__PURE__*/forwardRef(({
859
+ as: component,
860
+ block = false,
861
+ children,
862
+ className,
863
+ disabled,
864
+ loading = false,
865
+ priority = Priority.PRIMARY,
866
+ size = Size.MEDIUM,
867
+ type = ControlType.ACCENT,
868
+ ...rest
869
+ }, reference) => {
870
+ const intl = useIntl();
871
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
872
+ logDeprecationNotices({
873
+ size,
874
+ type
807
875
  });
808
- }
809
- function stripNodes({
810
- blockList,
811
- parsed
812
- }) {
813
- if (!parsed) {
814
- return parsed;
876
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
877
+ const newType = establishNewType(type);
878
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
879
+ const newPriority = establishNewPriority(priority, type);
880
+ const classes = classNames(`btn btn-${size}`, `np-btn np-btn-${size}`, {
881
+ 'btn-loading': loading,
882
+ 'btn-block np-btn-block': block
883
+ }, {
884
+ disabled: disabled || loading
885
+ },
886
+ // @ts-expect-error fix when refactor `typeClassMap` to TypeScript
887
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
888
+ typeClassMap[newType],
889
+ // @ts-expect-error fix when refactor `typeClassMap` to TypeScript
890
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
891
+ priorityClassMap[newPriority], className);
892
+ function processIndicatorSize() {
893
+ return ['sm', 'xs'].includes(size) ? 'xxs' : 'xs';
815
894
  }
816
- const walker = parsed.walker();
817
- for (let event = walker.next(); event != null; event = walker.next()) {
895
+ const Element = component ?? 'button';
896
+ let props;
897
+ if (Element === 'button') {
818
898
  const {
819
- node
820
- } = event;
821
- if (blockList.includes(node.type) && !event.entering) {
822
- while (node.firstChild != null) {
823
- node.insertBefore(node.firstChild);
824
- }
825
- node.unlink();
826
- }
899
+ htmlType = 'button',
900
+ ...restProps
901
+ } = rest;
902
+ props = {
903
+ ...restProps,
904
+ disabled: disabled || loading,
905
+ type: htmlType
906
+ };
907
+ } else {
908
+ props = {
909
+ ...rest
910
+ };
827
911
  }
828
- return parsed;
829
- }
830
-
831
- const allowList = [MarkdownNodeType.STRONG];
832
- function InlineMarkdown(props) {
833
- return /*#__PURE__*/jsx(Markdown, {
912
+ return /*#__PURE__*/jsxs(Element, {
913
+ ref: reference,
914
+ className: classes,
834
915
  ...props,
835
- as: "span",
836
- allowList: allowList,
837
- blockList: undefined
916
+ "aria-live": loading ? 'polite' : 'off',
917
+ "aria-label": loading ? intl.formatMessage(messages$e.loadingAriaLabel) : rest['aria-label'],
918
+ children: [children, loading && /*#__PURE__*/jsx(ProcessIndicator, {
919
+ size: processIndicatorSize(),
920
+ className: "btn-loader"
921
+ })]
838
922
  });
839
- }
923
+ });
840
924
 
841
- var messages$c = defineMessages({
925
+ var messages$d = defineMessages({
842
926
  opensInNewTab: {
843
927
  id: "neptune.Link.opensInNewTab"
844
928
  }
@@ -867,657 +951,447 @@ const Link = ({
867
951
  onClick: onClick,
868
952
  ...props,
869
953
  children: [children, " ", isBlank && /*#__PURE__*/jsx(NavigateAway, {
870
- title: formatMessage(messages$c.opensInNewTab)
954
+ title: formatMessage(messages$d.opensInNewTab)
871
955
  })]
872
956
  });
873
957
  };
874
958
 
875
- function Action({
876
- action,
877
- className
959
+ const DEFAULT_TYPE = Typography.TITLE_GROUP;
960
+ const titleTypeMapping = {
961
+ [Typography.TITLE_SCREEN]: 'h1',
962
+ [Typography.TITLE_SECTION]: 'h2',
963
+ [Typography.TITLE_SUBSECTION]: 'h3',
964
+ [Typography.TITLE_BODY]: 'h4',
965
+ [Typography.TITLE_GROUP]: 'h5'
966
+ };
967
+ function Title({
968
+ as,
969
+ type = DEFAULT_TYPE,
970
+ className,
971
+ ...props
878
972
  }) {
973
+ const mapping = titleTypeMapping[type];
974
+ const isTypeSupported = mapping !== undefined;
975
+ if (isTypeSupported) {
976
+ const HeaderTag = as ?? mapping;
977
+ return /*#__PURE__*/jsx(HeaderTag, {
978
+ ...props,
979
+ className: classNames(`np-text-${type}`, className)
980
+ });
981
+ }
982
+ const HeaderTag = as ?? titleTypeMapping[DEFAULT_TYPE];
983
+ return /*#__PURE__*/jsx(HeaderTag, {
984
+ ...props,
985
+ className: classNames(`np-text-${DEFAULT_TYPE}`, className)
986
+ });
987
+ }
988
+
989
+ const HeaderAction = ({
990
+ action
991
+ }) => {
992
+ const {
993
+ isModern
994
+ } = useTheme();
995
+ const props = {
996
+ 'aria-label': action['aria-label']
997
+ };
879
998
  if ('href' in action) {
880
999
  return /*#__PURE__*/jsx(Link, {
881
1000
  href: action.href,
882
- className: className,
883
- "aria-label": action['aria-label'],
884
1001
  target: action.target,
885
- type: Typography.LINK_LARGE,
886
1002
  onClick: action.onClick,
1003
+ ...props,
887
1004
  children: action.text
888
1005
  });
889
1006
  }
890
- return /*#__PURE__*/jsx("button", {
891
- type: "button",
892
- "aria-label": action['aria-label'],
893
- className: classNames('btn-unstyled np-text-link-large', className),
1007
+ return isModern ? /*#__PURE__*/jsx(Button, {
1008
+ className: "np-header__button",
1009
+ priority: "tertiary",
1010
+ size: "sm",
1011
+ onClick: action.onClick,
1012
+ ...props,
1013
+ children: action.text
1014
+ }) : /*#__PURE__*/jsx(ActionButton, {
894
1015
  onClick: action.onClick,
1016
+ ...props,
895
1017
  children: action.text
896
1018
  });
897
- }
898
-
899
- var AlertArrowPosition;
900
- (function (AlertArrowPosition) {
901
- AlertArrowPosition["TOP_LEFT"] = "up-left";
902
- AlertArrowPosition["TOP"] = "up-center";
903
- AlertArrowPosition["TOP_RIGHT"] = "up-right";
904
- AlertArrowPosition["BOTTOM_LEFT"] = "down-left";
905
- AlertArrowPosition["BOTTOM"] = "down-center";
906
- AlertArrowPosition["BOTTOM_RIGHT"] = "down-right";
907
- })(AlertArrowPosition || (AlertArrowPosition = {}));
908
- function resolveType(type) {
909
- switch (type) {
910
- case 'success':
911
- return 'positive';
912
- case 'info':
913
- return 'neutral';
914
- case 'error':
915
- return 'negative';
916
- default:
917
- return type;
918
- }
919
- }
920
- function Alert({
921
- arrow,
1019
+ };
1020
+ /**
1021
+ *
1022
+ * Neptune Web: https://transferwise.github.io/neptune-web/components/content/Header
1023
+ *
1024
+ */
1025
+ const Header = ({
922
1026
  action,
923
- children,
924
- className,
925
- dismissible,
926
- icon,
927
- onDismiss,
928
- message,
929
- size,
1027
+ as = 'h5',
930
1028
  title,
931
- type = 'neutral',
932
- variant = 'desktop'
933
- }) {
934
- useEffect(() => {
935
- if (arrow !== undefined) {
936
- logActionRequired("Alert component doesn't support 'arrow' anymore, use 'InlineAlert' instead.");
937
- }
938
- }, [arrow]);
939
- useEffect(() => {
940
- if (children !== undefined) {
941
- logActionRequired("Alert component doesn't support 'children' anymore, use 'message' instead.");
942
- }
943
- }, [children]);
944
- useEffect(() => {
945
- if (dismissible !== undefined) {
946
- logActionRequired("Alert component doesn't support 'dismissible' anymore, use 'onDismiss' instead.");
947
- }
948
- }, [dismissible]);
949
- useEffect(() => {
950
- if (size !== undefined) {
951
- logActionRequired("Alert component doesn't support 'size' anymore, please remove that prop.");
952
- }
953
- }, [size]);
954
- const resolvedType = resolveType(type);
955
- useEffect(() => {
956
- if (resolvedType !== type) {
957
- logActionRequired(`Alert component has deprecated '${type}' value for the 'type' prop. Please use '${resolvedType}' instead.`);
958
- }
959
- }, [resolvedType, type]);
960
- const [shouldFire, setShouldFire] = useState(false);
961
- const closeButtonReference = useRef(null);
962
- return /*#__PURE__*/jsxs("div", {
963
- className: classNames('alert d-flex', `alert-${resolvedType}`, arrow != null && alertArrowClassNames(arrow), className),
964
- "data-testid": "alert",
965
- onTouchStart: () => setShouldFire(true),
966
- onTouchEnd: event => {
967
- if (shouldFire && action?.href &&
968
- // Check if current event is triggered from closeButton
969
- event.target instanceof Node && closeButtonReference.current && !closeButtonReference.current.contains(event.target)) {
970
- if (action.target === '_blank') {
971
- window.top?.open(action.href);
972
- } else {
973
- window.top?.location.assign(action.href);
974
- }
975
- }
976
- setShouldFire(false);
977
- },
978
- onTouchMove: () => setShouldFire(false),
979
- children: [/*#__PURE__*/jsxs("div", {
980
- className: classNames('alert__content', 'd-flex', 'flex-grow-1', variant),
981
- "data-testid": variant,
982
- children: [icon ? /*#__PURE__*/jsx("div", {
983
- className: "alert__icon",
984
- children: icon
985
- }) : /*#__PURE__*/jsx(StatusIcon, {
986
- size: Size.LARGE,
987
- sentiment: resolvedType
988
- }), /*#__PURE__*/jsxs("div", {
989
- className: "alert__message",
990
- children: [/*#__PURE__*/jsxs("div", {
991
- role: Sentiment.NEGATIVE === resolvedType ? 'alert' : 'status',
992
- children: [title && /*#__PURE__*/jsx(Title, {
993
- className: "m-b-1",
994
- type: Typography.TITLE_BODY,
995
- children: title
996
- }), /*#__PURE__*/jsx(Body, {
997
- as: "span",
998
- className: "d-block",
999
- type: Typography.BODY_LARGE,
1000
- children: children || /*#__PURE__*/jsx(InlineMarkdown, {
1001
- children: message
1002
- })
1003
- })]
1004
- }), action && /*#__PURE__*/jsx(Action, {
1005
- action: action,
1006
- className: "m-t-1"
1007
- })]
1008
- })]
1009
- }), onDismiss && /*#__PURE__*/jsx(CloseButton, {
1010
- ref: closeButtonReference,
1011
- className: "m-l-2",
1012
- onClick: onDismiss
1013
- })]
1014
- });
1015
- }
1016
- function alertArrowClassNames(arrow) {
1017
- switch (arrow) {
1018
- case 'down-center':
1019
- return 'arrow arrow-bottom arrow-center';
1020
- case 'down-left':
1021
- return 'arrow arrow-bottom arrow-left';
1022
- case 'down-right':
1023
- return 'arrow arrow-bottom arrow-right';
1024
- case 'up-center':
1025
- return 'arrow arrow-center';
1026
- case 'up-right':
1027
- return 'arrow arrow-right';
1028
- case 'up-left':
1029
- default:
1030
- return 'arrow';
1029
+ className
1030
+ }) => {
1031
+ if (!action) {
1032
+ return /*#__PURE__*/jsx(Title, {
1033
+ as: as,
1034
+ type: Typography.TITLE_GROUP,
1035
+ className: classNames('np-header', 'np-header__title', className),
1036
+ children: title
1037
+ });
1031
1038
  }
1032
- }
1033
-
1034
- // TODO: consider to move this enum into component file once we migrate it on TypeScript or replace with some common enum
1035
- var AvatarType;
1036
- (function (AvatarType) {
1037
- AvatarType["THUMBNAIL"] = "thumbnail";
1038
- AvatarType["ICON"] = "icon";
1039
- AvatarType["EMOJI"] = "emoji";
1040
- AvatarType["INITIALS"] = "initials";
1041
- })(AvatarType || (AvatarType = {}));
1042
-
1043
- /*
1044
- * The colors we support generating an Avatar background color from a "seed" for.
1045
- * Changing this array will change the assignment between seeds.
1046
- * Do not change this array.
1047
- */
1048
- const avatarColors = ['--color-bright-blue', '--color-bright-yellow', '--color-bright-pink', '--color-bright-orange'];
1049
- /*
1050
- * Takes in a "seed" string and spits out an index for the color we should use.
1051
- * This implementation has been synced across all three clients so that we consistently
1052
- * generate branded avatar colors when rendering a list of Avatars.
1053
- * Do not change this implementation.
1054
- */
1055
- const hashSeed = seed => {
1056
- const base = 31;
1057
- const modulo = avatarColors.length;
1058
- let hashValue = 0;
1059
- let basePow = 1;
1060
- for (let i = 0; i < seed.length; i += 1) {
1061
- hashValue = (hashValue + seed.charCodeAt(i) * basePow) % modulo;
1062
- basePow = basePow * base % modulo;
1039
+ if (as === 'legend') {
1040
+ // eslint-disable-next-line no-console
1041
+ console.warn('Legends should be the first child in a fieldset, and this is not possible when including an action');
1063
1042
  }
1064
- return hashValue;
1065
- };
1066
- const getAvatarColorFromSeed = seed => {
1067
- return avatarColors[hashSeed(seed)];
1043
+ return /*#__PURE__*/jsxs("div", {
1044
+ className: classNames('np-header', className),
1045
+ children: [/*#__PURE__*/jsx(Title, {
1046
+ as: as,
1047
+ type: Typography.TITLE_GROUP,
1048
+ className: "np-header__title",
1049
+ children: title
1050
+ }), /*#__PURE__*/jsx(HeaderAction, {
1051
+ action: action
1052
+ })]
1053
+ });
1068
1054
  };
1069
1055
 
1070
- const backwardsCompatibleSize = size => {
1071
- switch (size) {
1072
- case 'sm':
1073
- return 24;
1074
- case 'md':
1075
- return 48;
1076
- case 'lg':
1077
- return 72;
1078
- default:
1079
- return size;
1080
- }
1081
- };
1082
- const Avatar = ({
1083
- backgroundColor = null,
1084
- backgroundColorSeed = null,
1085
- children = null,
1056
+ const NavigationOption = /*#__PURE__*/forwardRef(({
1057
+ as: component = 'button',
1058
+ disabled = false,
1059
+ onClick,
1086
1060
  className,
1087
- outlined = false,
1088
- size: sizeFromProps = 48,
1089
- theme = Theme.LIGHT,
1090
- type = 'thumbnail'
1091
- }) => {
1092
- const backgroundColorFromSeed = useMemo(() => !backgroundColor && backgroundColorSeed ? `var(${getAvatarColorFromSeed(backgroundColorSeed)})` : undefined, [backgroundColor, backgroundColorSeed]);
1093
- const size = backwardsCompatibleSize(sizeFromProps);
1094
- return /*#__PURE__*/jsx("div", {
1095
- className: classNames('tw-avatar', className, `tw-avatar--${size}`, `tw-avatar--${type}`, {
1096
- 'tw-avatar--outlined': outlined,
1097
- 'tw-avatar--branded': Boolean(backgroundColorFromSeed),
1098
- 'np-text-title-body': type === 'initials'
1061
+ ...rest
1062
+ }, reference) => {
1063
+ if ('href' in rest && rest.href) {
1064
+ component = 'a';
1065
+ }
1066
+ return /*#__PURE__*/jsx(Option$2, {
1067
+ ...rest,
1068
+ ref: reference,
1069
+ as: component,
1070
+ className: classNames('np-navigation-option', className),
1071
+ button: /*#__PURE__*/jsx(Chevron, {
1072
+ orientation: Position.RIGHT,
1073
+ disabled: disabled,
1074
+ className: "d-block"
1099
1075
  }),
1100
- children: /*#__PURE__*/jsx("div", {
1101
- className: "tw-avatar__content",
1102
- style: {
1103
- backgroundColor: backgroundColor || backgroundColorFromSeed
1104
- },
1105
- children: children
1106
- })
1076
+ disabled: disabled,
1077
+ onClick: event => {
1078
+ if (disabled) {
1079
+ event.preventDefault();
1080
+ return;
1081
+ }
1082
+ if (onClick) {
1083
+ onClick(event);
1084
+ }
1085
+ }
1107
1086
  });
1108
- };
1087
+ });
1109
1088
 
1110
- const Badge = ({
1111
- badge,
1112
- className = undefined,
1113
- size = Size.SMALL,
1114
- border = Theme.LIGHT,
1089
+ const NavigationOptionList = ({
1115
1090
  children
1116
1091
  }) => {
1117
- const classes = classNames('tw-badge', {
1118
- [`tw-badge-border-${border}`]: border,
1119
- [`tw-badge-${size}`]: size
1120
- }, className);
1121
- return /*#__PURE__*/jsxs("div", {
1122
- className: classes,
1123
- children: [/*#__PURE__*/jsx("div", {
1124
- className: "tw-badge__children",
1125
- children: children
1126
- }), /*#__PURE__*/jsx("div", {
1127
- className: "tw-badge__content",
1128
- children: badge
1129
- })]
1092
+ return /*#__PURE__*/jsx("ul", {
1093
+ className: "np-navigation-options-list",
1094
+ children: Children.map(children, child => /*#__PURE__*/jsx("li", {
1095
+ className: "np-navigation-options-list__item",
1096
+ children: child
1097
+ }, child?.toString()))
1130
1098
  });
1131
1099
  };
1132
1100
 
1133
- const OptionalBadge = ({
1134
- url,
1135
- altText,
1136
- statusIcon,
1137
- children,
1138
- ...rest
1139
- }) => {
1140
- if (url) {
1141
- return /*#__PURE__*/jsx(Badge, {
1142
- badge: /*#__PURE__*/jsx("img", {
1143
- src: url,
1144
- alt: altText
1145
- }),
1146
- ...rest,
1147
- children: children
1101
+ const FieldLabelIdContext = /*#__PURE__*/createContext(undefined);
1102
+ const FieldLabelIdContextProvider = FieldLabelIdContext.Provider;
1103
+ const InputIdContext = /*#__PURE__*/createContext(undefined);
1104
+ const InputIdContextProvider = InputIdContext.Provider;
1105
+ const InputDescribedByContext = /*#__PURE__*/createContext(undefined);
1106
+ const InputDescribedByProvider = InputDescribedByContext.Provider;
1107
+ const InputInvalidContext = /*#__PURE__*/createContext(undefined);
1108
+ const InputInvalidProvider = InputInvalidContext.Provider;
1109
+ function useInputAttributes({
1110
+ nonLabelable
1111
+ } = {}) {
1112
+ const labelId = useContext(FieldLabelIdContext);
1113
+ return {
1114
+ id: useContext(InputIdContext),
1115
+ 'aria-labelledby': nonLabelable ? labelId : undefined,
1116
+ 'aria-describedby': useContext(InputDescribedByContext),
1117
+ 'aria-invalid': useContext(InputInvalidContext)
1118
+ };
1119
+ }
1120
+ function withInputAttributes(Component, args) {
1121
+ function ComponentWithInputAttributes(props) {
1122
+ return /*#__PURE__*/jsx(Component, {
1123
+ inputAttributes: useInputAttributes(args),
1124
+ ...props
1148
1125
  });
1149
1126
  }
1150
- if (statusIcon) {
1151
- return /*#__PURE__*/jsx(Badge, {
1152
- badge: /*#__PURE__*/jsx(StatusIcon, {
1153
- sentiment: statusIcon,
1154
- size: Size.SMALL
1155
- }),
1156
- ...rest,
1157
- children: children
1158
- });
1127
+ ComponentWithInputAttributes.displayName = `withInputAttributes(${Component.displayName || Component.name || 'Component'})`;
1128
+ return ComponentWithInputAttributes;
1129
+ }
1130
+
1131
+ var messages$c = defineMessages({
1132
+ actionLabel: {
1133
+ id: "neptune.SelectOption.action.label"
1159
1134
  }
1160
- return /*#__PURE__*/jsx(Fragment, {
1161
- children: children
1162
- });
1163
- };
1164
- const AvatarWrapper = ({
1165
- url,
1166
- profileType,
1167
- profileId,
1168
- badgeUrl,
1169
- badgeAltText,
1170
- badgeStatusIcon,
1171
- name,
1172
- avatarProps,
1173
- badgeProps
1135
+ });
1136
+
1137
+ const FocusBoundary = ({
1138
+ children
1174
1139
  }) => {
1175
- const [hasImageLoadError, setImageLoadError] = useState(false);
1176
- const isBusinessProfile = profileType === ProfileType.BUSINESS;
1177
- // Reset the errored state when url changes
1178
- useEffect(() => setImageLoadError(false), [url]);
1179
- const getAvatarProps = () => {
1180
- if (url && !hasImageLoadError) {
1181
- return {
1182
- type: AvatarType.THUMBNAIL,
1183
- children: /*#__PURE__*/jsx("img", {
1184
- src: url,
1185
- alt: "",
1186
- onError: () => setImageLoadError(true)
1187
- }),
1188
- ...avatarProps
1189
- };
1190
- }
1191
- if (profileType) {
1192
- return {
1193
- type: AvatarType.ICON,
1194
- children: isBusinessProfile ? /*#__PURE__*/jsx(Briefcase, {
1195
- size: "24"
1196
- }) : /*#__PURE__*/jsx(Person, {
1197
- size: "24"
1198
- }),
1199
- ...avatarProps
1200
- };
1201
- }
1202
- if (name) {
1203
- return {
1204
- type: AvatarType.INITIALS,
1205
- children: /*#__PURE__*/jsx(Fragment, {
1206
- children: getInitials(name)
1207
- }),
1208
- backgroundColorSeed: profileId?.toString(),
1209
- ...avatarProps
1210
- };
1211
- }
1212
- return {
1213
- type: AvatarType.ICON,
1214
- children: /*#__PURE__*/jsx(Person, {
1215
- size: "24"
1216
- }),
1217
- ...avatarProps
1218
- };
1219
- };
1220
- return /*#__PURE__*/jsx(OptionalBadge, {
1221
- url: badgeUrl,
1222
- altText: badgeAltText,
1223
- statusIcon: badgeStatusIcon,
1224
- ...badgeProps,
1225
- children: /*#__PURE__*/jsx(Avatar, {
1226
- size: Size.MEDIUM,
1227
- ...getAvatarProps()
1140
+ const wrapperReference = useRef(null);
1141
+ useEffect(() => {
1142
+ wrapperReference.current?.focus({
1143
+ preventScroll: true
1144
+ });
1145
+ }, []);
1146
+ return /*#__PURE__*/jsx(FocusScope, {
1147
+ contain: true,
1148
+ restoreFocus: true,
1149
+ children: /*#__PURE__*/jsx("div", {
1150
+ ref: wrapperReference,
1151
+ tabIndex: -1,
1152
+ children: children
1228
1153
  })
1229
1154
  });
1230
1155
  };
1231
- function getInitials(name) {
1232
- const allInitials = name.split(' ').map(part => part[0]).join('').toUpperCase();
1233
- if (allInitials.length === 1) {
1234
- return allInitials[0];
1235
- }
1236
- return allInitials[0] + allInitials.slice(-1);
1237
- }
1238
1156
 
1239
- const radius = {
1240
- xxs: 6,
1241
- xs: 11,
1242
- sm: 22,
1243
- xl: 61
1244
- };
1245
- const ANIMATION_DURATION_IN_MS = 1500;
1246
- class ProcessIndicator extends Component {
1247
- static defaultProps = {
1248
- status: 'processing',
1249
- size: 'sm'
1157
+ function withNextPortalWrapper(Component) {
1158
+ return function (props) {
1159
+ const [mounted, setMounted] = useState(false);
1160
+ useEffect(() => {
1161
+ setMounted(true);
1162
+ }, [setMounted]);
1163
+ return mounted ? /*#__PURE__*/createPortal( /*#__PURE__*/jsx(Component, {
1164
+ ...props
1165
+ }), document.body) : null;
1250
1166
  };
1251
- interval = 0;
1252
- timeout = 0;
1253
- constructor(props) {
1254
- super(props);
1255
- this.state = {
1256
- status: props.status,
1257
- size: props.size
1258
- };
1259
- }
1167
+ }
1168
+
1169
+ /**
1170
+ * Dimmer state management inspired by Material UI's ModalManager (https://github.com/mui-org/material-ui)
1171
+ */
1172
+ class DimmerManager {
1260
1173
  /**
1261
- * Create interval for animation duration (1500ms)
1262
- * Update state only at the end of every interval
1263
- * (end of animation loop) if props changed before end of animation
1174
+ * Dimmer refs
1264
1175
  */
1265
- componentDidMount() {
1266
- this.interval = window.setInterval(() => {
1267
- const {
1268
- status: targetStatus,
1269
- size: targetSize,
1270
- onAnimationCompleted
1271
- } = this.props;
1272
- const {
1273
- status: currentStatus,
1274
- size: currentSize
1275
- } = this.state;
1276
- if (currentStatus !== targetStatus) {
1277
- this.setState({
1278
- status: targetStatus
1279
- }, () => {
1280
- if (onAnimationCompleted) {
1281
- this.timeout = window.setTimeout(() => {
1282
- onAnimationCompleted(targetStatus);
1283
- }, ANIMATION_DURATION_IN_MS);
1284
- }
1285
- });
1286
- }
1287
- if (currentSize !== targetSize) {
1288
- this.setState({
1289
- size: targetSize
1290
- });
1291
- }
1292
- }, ANIMATION_DURATION_IN_MS);
1176
+ dimmers;
1177
+ constructor() {
1178
+ this.dimmers = [];
1293
1179
  }
1294
- /**
1295
- * Only trigger render if comopnent's state got
1296
- * updated from interval callback
1297
- */
1298
- shouldComponentUpdate(nextProps, nextState) {
1299
- const isSameStatus = nextProps.status === nextState.status;
1300
- const isSameSize = nextProps.size === nextState.size;
1301
- return isSameStatus && isSameSize;
1180
+ add(dimmer) {
1181
+ let dimmerIndex = this.dimmers.indexOf(dimmer);
1182
+ if (dimmerIndex !== -1) {
1183
+ return dimmerIndex;
1184
+ }
1185
+ dimmerIndex = this.dimmers.length;
1186
+ this.dimmers.push(dimmer);
1187
+ return dimmerIndex;
1302
1188
  }
1303
- // Clear interval before destroying component
1304
- componentWillUnmount() {
1305
- window.clearInterval(this.interval);
1306
- window.clearTimeout(this.timeout);
1189
+ remove(dimmer) {
1190
+ const dimmerIndex = this.dimmers.indexOf(dimmer);
1191
+ if (dimmerIndex !== -1) {
1192
+ this.dimmers.splice(dimmerIndex, 1);
1193
+ }
1194
+ return dimmerIndex;
1307
1195
  }
1308
- render() {
1309
- const {
1310
- className,
1311
- 'data-testid': dataTestId
1312
- } = this.props;
1313
- const {
1314
- size,
1315
- status
1316
- } = this.state;
1317
- const classes = classNames(`process process-${size}`, className, {
1318
- [`process-danger`]: status === Status.FAILED,
1319
- [`process-stopped`]: status === Status.HIDDEN,
1320
- [`process-success`]: status === Status.SUCCEEDED
1321
- });
1322
- return /*#__PURE__*/jsxs("span", {
1323
- className: classes,
1324
- "data-testid": dataTestId,
1325
- children: [/*#__PURE__*/jsxs("span", {
1326
- className: "process-icon-container",
1327
- children: [/*#__PURE__*/jsx("span", {
1328
- className: "process-icon-horizontal"
1329
- }), /*#__PURE__*/jsx("span", {
1330
- className: "process-icon-vertical"
1331
- })]
1332
- }), /*#__PURE__*/jsx("svg", {
1333
- xmlns: "http://www.w3.org/2000/svg",
1334
- children: /*#__PURE__*/jsx("circle", {
1335
- className: "process-circle",
1336
- cx: "50%",
1337
- cy: "50%",
1338
- r: radius[size],
1339
- fillOpacity: "0.0"
1340
- })
1341
- })]
1342
- });
1196
+ isTop(dimmer) {
1197
+ return this.dimmers.length > 0 && this.dimmers[this.dimmers.length - 1] === dimmer;
1343
1198
  }
1344
1199
  }
1345
1200
 
1346
- var messages$b = defineMessages({
1347
- loadingAriaLabel: {
1348
- id: "neptune.Button.loadingAriaLabel"
1201
+ const EXIT_ANIMATION$1 = 350;
1202
+ const dimmerManager = new DimmerManager();
1203
+ const handleTouchMove = event => {
1204
+ const isTouchedElementDimmer = event.target.classList.contains('dimmer');
1205
+ // disable scroll on iOS devices for Dimmer area
1206
+ // this is because of bug in WebKit https://bugs.webkit.org/show_bug.cgi?id=220908
1207
+ // note: scrolling still works for children(s) as expected
1208
+ if (isIosDevice() && isTouchedElementDimmer) {
1209
+ event.stopPropagation();
1210
+ event.preventDefault();
1349
1211
  }
1350
- });
1351
-
1352
- const typeClassMap = {
1353
- [ControlType.ACCENT]: 'btn-accent',
1354
- [ControlType.POSITIVE]: 'btn-positive',
1355
- [ControlType.NEGATIVE]: 'btn-negative'
1356
- };
1357
- const priorityClassMap = {
1358
- [Priority.PRIMARY]: 'btn-priority-1',
1359
- [Priority.SECONDARY]: 'btn-priority-2',
1360
- [Priority.TERTIARY]: 'btn-priority-3'
1361
1212
  };
1362
-
1363
- const deprecatedTypeMap = {
1364
- [Type.PRIMARY]: ControlType.ACCENT,
1365
- [Type.SECONDARY]: ControlType.ACCENT,
1366
- [Type.LINK]: ControlType.ACCENT,
1367
- [Type.PAY]: ControlType.POSITIVE,
1368
- [Type.DANGER]: ControlType.NEGATIVE
1369
- };
1370
- const oldTypePriorityMap = {
1371
- [Type.DANGER]: Priority.SECONDARY,
1372
- [Type.LINK]: Priority.TERTIARY,
1373
- [Type.SECONDARY]: Priority.SECONDARY
1374
- };
1375
- const deprecatedTypeMapMessage = {
1376
- [Type.DANGER]: 'Type.NEGATIVE',
1377
- [Type.LINK]: 'ControlType.ACCENT with priority Priority.TERTIARY',
1378
- [Type.PAY]: 'ControlType.POSITIVE',
1379
- [Type.PRIMARY]: 'ControlType.ACCENT',
1380
- [Type.SECONDARY]: 'ControlType.ACCENT with priority Priority.SECONDARY'
1381
- };
1382
- const deprecatedTypes = Object.keys(deprecatedTypeMap);
1383
- const establishNewType = originalType => deprecatedTypeMap[originalType] || originalType;
1384
- const establishNewPriority = (originalPriority, originalType) => {
1385
- const type = establishNewType(originalType);
1386
-
1387
- // The old SECONDARY and LINK types now map to priorities. If they're still using one of
1388
- // these old types, ignore whatever priority they've passed and use this instead.
1389
- if (oldTypePriorityMap[originalType]) {
1390
- return oldTypePriorityMap[originalType];
1391
- }
1392
- // Only ControlType.ACCENT supports tertiary styles
1393
- if (originalPriority === Priority.TERTIARY && type !== ControlType.ACCENT) {
1394
- return Priority.SECONDARY;
1395
- }
1396
- return originalPriority;
1213
+ const Dimmer = ({
1214
+ children,
1215
+ className,
1216
+ disableClickToClose = false,
1217
+ contentPosition,
1218
+ fadeContentOnEnter = false,
1219
+ fadeContentOnExit = false,
1220
+ open = false,
1221
+ scrollable = false,
1222
+ transparent = false,
1223
+ onClose
1224
+ }) => {
1225
+ const [hasNotExited, setHasNotExited] = useState(false);
1226
+ const dimmerReference = useRef(null);
1227
+ const closeOnClick = event => {
1228
+ if (event.target === dimmerReference.current) {
1229
+ onClose?.(event);
1230
+ }
1231
+ };
1232
+ const handleClick = event => {
1233
+ if (disableClickToClose || !onClose) {
1234
+ return;
1235
+ }
1236
+ closeOnClick(event);
1237
+ };
1238
+ const handleKeyDown = useCallback(event => {
1239
+ if (event.key !== 'Escape') {
1240
+ return;
1241
+ }
1242
+ event.stopPropagation();
1243
+ if (onClose && dimmerReference.current && dimmerManager.isTop(dimmerReference.current)) {
1244
+ onClose(event);
1245
+ }
1246
+ }, [onClose]);
1247
+ const onEnter = () => {
1248
+ setHasNotExited(true);
1249
+ if (dimmerReference.current) {
1250
+ dimmerManager.add(dimmerReference.current);
1251
+ }
1252
+ };
1253
+ const onExited = () => {
1254
+ setHasNotExited(false);
1255
+ if (dimmerReference.current) {
1256
+ dimmerManager.remove(dimmerReference.current);
1257
+ }
1258
+ };
1259
+ useEffect(() => {
1260
+ const localReferenceCopy = dimmerReference.current;
1261
+ if (open) {
1262
+ document.addEventListener('keydown', handleKeyDown);
1263
+ localReferenceCopy?.addEventListener('touchmove', handleTouchMove, {
1264
+ passive: true
1265
+ });
1266
+ }
1267
+ return () => {
1268
+ document.removeEventListener('keydown', handleKeyDown);
1269
+ localReferenceCopy?.removeEventListener('touchmove', handleTouchMove);
1270
+ };
1271
+ }, [handleKeyDown, open]);
1272
+ return /*#__PURE__*/jsx(DimmerWrapper, {
1273
+ open: open,
1274
+ hasNotExited: hasNotExited,
1275
+ children: /*#__PURE__*/jsx(CSSTransition, {
1276
+ nodeRef: dimmerReference,
1277
+ in: open,
1278
+ appear: true
1279
+ // Wait for animation to finish before unmount.
1280
+ ,
1281
+ timeout: {
1282
+ enter: 0,
1283
+ exit: EXIT_ANIMATION$1
1284
+ },
1285
+ classNames: {
1286
+ enter: classNames({
1287
+ 'dimmer--enter-fade': fadeContentOnEnter
1288
+ }),
1289
+ enterDone: classNames('dimmer--enter-done', {
1290
+ 'dimmer--enter-fade': fadeContentOnEnter
1291
+ }),
1292
+ exit: classNames('dimmer--exit', {
1293
+ 'dimmer--exit-fade': fadeContentOnExit
1294
+ })
1295
+ },
1296
+ unmountOnExit: true,
1297
+ onEnter: onEnter,
1298
+ onExited: onExited,
1299
+ children: /*#__PURE__*/jsx(DimmerContentWrapper, {
1300
+ scrollBody: !transparent,
1301
+ children: /*#__PURE__*/jsx(FocusBoundary, {
1302
+ children: /*#__PURE__*/jsx("div", {
1303
+ ref: dimmerReference,
1304
+ className: classNames('dimmer', {
1305
+ 'dimmer--scrollable': scrollable,
1306
+ 'dimmer--transparent': transparent
1307
+ }, className),
1308
+ role: "presentation",
1309
+ onClick: handleClick,
1310
+ children: /*#__PURE__*/jsx("div", {
1311
+ className: classNames('dimmer-content-positioner', contentPosition != null && ['d-flex justify-content-center', {
1312
+ 'align-items-start': contentPosition === 'top',
1313
+ 'align-items-center': contentPosition === 'center',
1314
+ 'align-items-end': contentPosition === 'bottom'
1315
+ }]),
1316
+ children: children
1317
+ })
1318
+ })
1319
+ })
1320
+ })
1321
+ })
1322
+ });
1397
1323
  };
1398
- const logDeprecationNotices = ({
1399
- size,
1400
- type
1324
+ const DimmerWrapper = ({
1325
+ open,
1326
+ hasNotExited,
1327
+ children
1401
1328
  }) => {
1402
- logActionRequiredIf('Button has deprecated the `Size.EXTRA_SMALL` value for the `size` prop. Please use Size.SMALL instead.', size === Size.EXTRA_SMALL);
1403
- logActionRequiredIf(`Button has deprecated the ${type} value for the \`type\` prop. Please update to ${deprecatedTypeMapMessage[type]}.`, deprecatedTypes.includes(type));
1329
+ const {
1330
+ screenMode,
1331
+ theme
1332
+ } = useTheme();
1333
+ return open || hasNotExited ? /*#__PURE__*/jsx(ThemeProvider, {
1334
+ theme: "personal",
1335
+ screenMode: theme === 'personal' ? screenMode : 'light',
1336
+ isNotRootProvider: true,
1337
+ children: children
1338
+ }) : /*#__PURE__*/jsx(Fragment, {
1339
+ children: children
1340
+ });
1404
1341
  };
1405
-
1406
- const Button = /*#__PURE__*/forwardRef(({
1407
- as: component,
1408
- block = false,
1342
+ const DimmerContentWrapper = ({
1409
1343
  children,
1410
- className,
1411
- disabled,
1412
- loading = false,
1413
- priority = Priority.PRIMARY,
1414
- size = Size.MEDIUM,
1415
- type = ControlType.ACCENT,
1416
- ...rest
1417
- }, reference) => {
1418
- const intl = useIntl();
1419
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1420
- logDeprecationNotices({
1421
- size,
1422
- type
1423
- });
1424
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1425
- const newType = establishNewType(type);
1426
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1427
- const newPriority = establishNewPriority(priority, type);
1428
- const classes = classNames(`btn btn-${size}`, `np-btn np-btn-${size}`, {
1429
- 'btn-loading': loading,
1430
- 'btn-block np-btn-block': block
1431
- }, {
1432
- disabled: disabled || loading
1433
- },
1434
- // @ts-expect-error fix when refactor `typeClassMap` to TypeScript
1435
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1436
- typeClassMap[newType],
1437
- // @ts-expect-error fix when refactor `typeClassMap` to TypeScript
1438
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1439
- priorityClassMap[newPriority], className);
1440
- function processIndicatorSize() {
1441
- return ['sm', 'xs'].includes(size) ? 'xxs' : 'xs';
1442
- }
1443
- const Element = component ?? 'button';
1444
- let props;
1445
- if (Element === 'button') {
1446
- const {
1447
- htmlType = 'button',
1448
- ...restProps
1449
- } = rest;
1450
- props = {
1451
- ...restProps,
1452
- disabled: disabled || loading,
1453
- type: htmlType
1454
- };
1455
- } else {
1456
- props = {
1457
- ...rest
1344
+ scrollBody
1345
+ }) => {
1346
+ useEffect(() => {
1347
+ if (scrollBody) {
1348
+ addNoScrollClass();
1349
+ }
1350
+ return () => {
1351
+ if (scrollBody) {
1352
+ removeNoScrollClass();
1353
+ }
1458
1354
  };
1355
+ }, [scrollBody]);
1356
+ return children;
1357
+ };
1358
+ var Dimmer$1 = withNextPortalWrapper(Dimmer);
1359
+
1360
+ var messages$b = defineMessages({
1361
+ ariaLabel: {
1362
+ id: "neptune.CloseButton.ariaLabel"
1459
1363
  }
1460
- return /*#__PURE__*/jsxs(Element, {
1461
- ref: reference,
1462
- className: classes,
1463
- ...props,
1464
- "aria-live": loading ? 'polite' : 'off',
1465
- "aria-label": loading ? intl.formatMessage(messages$b.loadingAriaLabel) : rest['aria-label'],
1466
- children: [children, loading && /*#__PURE__*/jsx(ProcessIndicator, {
1467
- size: processIndicatorSize(),
1468
- className: "btn-loader"
1469
- })]
1470
- });
1471
1364
  });
1472
1365
 
1473
- const Card$1 = /*#__PURE__*/forwardRef(({
1366
+ const CloseButton = /*#__PURE__*/forwardRef(function CloseButton({
1367
+ 'aria-label': ariaLabel,
1368
+ size = Size.MEDIUM,
1369
+ filled = false,
1474
1370
  className,
1475
- children = null,
1476
- id,
1477
- isDisabled = false,
1478
- isSmall = false,
1479
- onDismiss,
1480
- testId,
1481
- ...props
1482
- }, ref) => {
1483
- const closeButtonReference = useRef(null);
1484
- return /*#__PURE__*/jsxs("div", {
1485
- ref: ref,
1486
- className: classNames('np-Card', {
1487
- 'np-Card--small': !!isSmall,
1488
- 'is-disabled': !!isDisabled
1371
+ onClick,
1372
+ isDisabled,
1373
+ testId
1374
+ }, reference) {
1375
+ const intl = useIntl();
1376
+ ariaLabel ??= intl.formatMessage(messages$b.ariaLabel);
1377
+ const Icon = filled ? CrossCircleFill : Cross;
1378
+ return /*#__PURE__*/jsx("button", {
1379
+ ref: reference,
1380
+ type: "button",
1381
+ className: classNames('np-close-button', 'close btn-link', 'text-no-decoration', {
1382
+ 'np-close-button--large': size === Size.MEDIUM,
1383
+ 'np-close-button--x-large': size === Size.LARGE
1489
1384
  }, className),
1490
- id: id,
1385
+ "aria-label": ariaLabel,
1386
+ "aria-disabled": isDisabled,
1387
+ disabled: isDisabled,
1491
1388
  "data-testid": testId,
1492
- ...props,
1493
- children: [onDismiss && /*#__PURE__*/jsx(CloseButton, {
1494
- ref: closeButtonReference,
1495
- className: "np-Card-closeButton",
1496
- size: isSmall ? 'sm' : 'md',
1497
- isDisabled: isDisabled,
1498
- testId: "close-button",
1499
- onClick: e => {
1500
- stopPropagation$1(e);
1501
- onDismiss();
1502
- }
1503
- }), children]
1389
+ onClick: onClick,
1390
+ children: /*#__PURE__*/jsx(Icon, {
1391
+ size: size === Size.SMALL ? 16 : 24
1392
+ })
1504
1393
  });
1505
1394
  });
1506
- Card$1.displayName = 'Card';
1507
-
1508
- function Display({
1509
- as: Heading = 'h1',
1510
- type = Typography.DISPLAY_LARGE,
1511
- children,
1512
- className,
1513
- id
1514
- }) {
1515
- return /*#__PURE__*/jsx(Heading, {
1516
- id: id,
1517
- className: classNames(`np-text-${type}`, 'text-primary', className),
1518
- children: children
1519
- });
1520
- }
1521
1395
 
1522
1396
  const useConditionalListener = ({
1523
1397
  attachListener,
@@ -1647,1295 +1521,1833 @@ const useLayout = () => {
1647
1521
  };
1648
1522
  };
1649
1523
 
1650
- const EmptyTransparentImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
1651
- const Image = ({
1652
- id,
1653
- src,
1654
- alt,
1655
- onLoad,
1656
- onError,
1657
- className,
1658
- loading,
1659
- stretch = true,
1660
- role,
1661
- shrink = true
1662
- }) => {
1663
- const elementReference = useRef(null);
1664
- const [hasIntersected] = useHasIntersected({
1665
- elRef: elementReference,
1666
- loading
1667
- });
1668
- let imageSource = src;
1669
- let imageOnLoad = onLoad;
1670
- if (loading === 'lazy' && !hasIntersected) {
1671
- imageSource = EmptyTransparentImage;
1672
- imageOnLoad = undefined;
1673
- }
1674
- return /*#__PURE__*/jsx("img", {
1675
- ref: elementReference,
1676
- id: id,
1677
- alt: alt,
1678
- src: imageSource,
1679
- className: classNames(['tw-image', {
1680
- 'tw-image__stretch': stretch,
1681
- 'tw-image__shrink': shrink
1682
- }, className]),
1683
- role: role,
1684
- onLoad: imageOnLoad,
1685
- onError: onError
1686
- });
1687
- };
1688
-
1689
- const defaultPromoCardContext = {
1690
- state: '',
1691
- isDisabled: false,
1692
- // eslint-disable-next-line @typescript-eslint/no-empty-function
1693
- onChange: () => {}
1694
- };
1695
- /**
1696
- * The PromoCard context object.
1697
- */
1698
- const PromoCardContext = /*#__PURE__*/createContext(defaultPromoCardContext);
1699
- /**
1700
- * A custom hook for accessing the PromoCard context object.
1701
- *
1702
- * The `usePromoCardContext` hook is used to access the PromoCard context object
1703
- * from within a child PromoCard component. It throws an error if the context
1704
- * object is not available, which can help with debugging and development.
1705
- *
1706
- * @returns {PromoCardContextType} - The PromoCard context object.
1707
- */
1708
- const usePromoCardContext = () => {
1709
- return useContext(PromoCardContext);
1710
- };
1711
-
1712
- const PromoCardIndicator = ({
1524
+ const EXIT_ANIMATION = 350;
1525
+ const SlidingPanel = /*#__PURE__*/forwardRef(({
1526
+ position = 'left',
1527
+ open,
1528
+ showSlidingPanelBorder,
1529
+ slidingPanelPositionFixed,
1713
1530
  className,
1714
1531
  children,
1715
- label,
1716
- icon,
1717
- isSmall = false,
1718
- testid,
1532
+ testId,
1719
1533
  ...rest
1720
- }) => {
1721
- const isIconString = icon && typeof icon === 'string';
1722
- const IconComponent = isIconString && {
1723
- check: Check,
1724
- arrow: ArrowRight,
1725
- download: Download
1726
- }[icon];
1727
- return /*#__PURE__*/jsxs("div", {
1728
- className: classNames('np-Card-indicator', className),
1729
- "data-testid": testid,
1534
+ }, reference) => {
1535
+ const localReference = useRef(null);
1536
+ useImperativeHandle(reference, () => localReference.current, []);
1537
+ return /*#__PURE__*/createElement(CSSTransition, {
1730
1538
  ...rest,
1731
- children: [label && /*#__PURE__*/jsx(Body, {
1732
- as: "span",
1733
- type: Typography.BODY_LARGE_BOLD,
1734
- className: "np-Card-indicatorText",
1735
- children: label
1736
- }), icon && /*#__PURE__*/jsx(Avatar, {
1737
- type: AvatarType.ICON,
1738
- size: isSmall ? 40 : 56,
1739
- backgroundColor: "var(--Card-indicator-icon-background-color)",
1740
- className: "np-Card-indicatorIcon",
1741
- children: IconComponent ? /*#__PURE__*/jsx(IconComponent, {
1742
- size: 24,
1743
- "aria-hidden": "true"
1744
- }) : icon
1745
- }), children]
1746
- });
1747
- };
1539
+ key: `sliding-panel--open-${position}`,
1540
+ nodeRef: localReference,
1541
+ in: open
1542
+ // Wait for animation to finish before unmount.
1543
+ ,
1544
+ timeout: {
1545
+ enter: 0,
1546
+ exit: EXIT_ANIMATION
1547
+ },
1548
+ classNames: "sliding-panel",
1549
+ appear: true,
1550
+ unmountOnExit: true
1551
+ }, /*#__PURE__*/jsx("div", {
1552
+ ref: localReference,
1553
+ "data-testid": testId,
1554
+ className: classNames('sliding-panel', `sliding-panel--open-${position}`, showSlidingPanelBorder && `sliding-panel--border-${position}`, slidingPanelPositionFixed && 'sliding-panel--fixed', className),
1555
+ children: children
1556
+ }));
1557
+ });
1748
1558
 
1749
- const PromoCard = /*#__PURE__*/forwardRef(({
1559
+ function Drawer({
1560
+ children,
1750
1561
  className,
1751
- description,
1752
- defaultChecked,
1753
- download,
1754
- href,
1755
- hrefLang,
1756
- id,
1757
- headingLevel = 'h3',
1758
- imageAlt,
1759
- imageClass,
1760
- imageSource,
1761
- indicatorLabel,
1762
- indicatorIcon,
1763
- isChecked,
1764
- isDisabled,
1765
- onClick,
1766
- onKeyDown,
1767
- rel,
1768
- tabIndex,
1769
- target,
1770
- testId,
1771
- title,
1772
- type,
1773
- value,
1774
- isSmall,
1775
- useDisplayFont = true,
1776
- anchorRef,
1777
- anchorId,
1778
- ...props
1779
- }, ref) => {
1780
- // Set the `checked` state to the value of `defaultChecked` if it is truthy,
1781
- // or the value of `isChecked` if it is truthy, or `false` if neither
1782
- // is truthy.
1562
+ footerContent,
1563
+ headerTitle,
1564
+ onClose,
1565
+ open = false,
1566
+ position = 'right'
1567
+ }) {
1568
+ logActionRequiredIf('Drawer now expects `onClose`, and will soon make this prop required. Please update your usage to provide it.', !onClose);
1783
1569
  const {
1784
- state,
1785
- onChange,
1786
- isDisabled: contextIsDisabled
1787
- } = usePromoCardContext();
1788
- const [checked, setChecked] = useState(type === 'checkbox' ? defaultChecked ?? isChecked ?? false : false);
1789
- const handleClick = () => {
1790
- if (type === 'radio') {
1791
- onChange(value || ''); // Update the context state for radio
1792
- } else if (type === 'checkbox') {
1793
- setChecked(!checked); // Update local state for checkbox
1794
- }
1795
- };
1796
- const fallbackId = useId();
1797
- const componentId = id || fallbackId;
1798
- // Set the icon to `'arrow'` if `href` is truthy and `type` is falsy, or
1799
- // `'download'` if `download` is truthy. If neither condition is true, set
1800
- // `icon` to `undefined`.
1801
- // Create a function to get icon type
1802
- const getIconType = () => {
1803
- if (indicatorIcon) {
1804
- return indicatorIcon;
1805
- }
1806
- if (download) {
1807
- return 'download';
1808
- }
1809
- if (href && !type) {
1810
- return 'arrow';
1811
- }
1812
- return undefined;
1813
- };
1814
- // Define all class names string based on the values of the `href`, `type`,
1815
- // `checked`, and `className` props.
1816
- const commonClasses = classNames({
1817
- 'np-Card--promoCard': true,
1818
- 'np-Card--checked': !href && type,
1819
- 'np-Card--link': href && !type,
1820
- 'is-checked': type === 'radio' ? value === state : type === 'checkbox' ? checked : undefined
1821
- }, className);
1822
- // Object with common props that will be passed to the `Card` components
1823
- const commonProps = {
1824
- className: commonClasses,
1825
- id: componentId,
1826
- isDisabled: isDisabled || contextIsDisabled,
1827
- onClick,
1828
- onKeyDown,
1829
- ref,
1830
- 'data-testid': testId,
1831
- isSmall
1832
- };
1833
- // Object with Anchor props that will be passed to the `a` element. These
1834
- // won't be refurned if set to `isDisabled`
1835
- const anchorProps = href && !isDisabled ? {
1836
- download,
1837
- href: href || undefined,
1838
- hrefLang,
1839
- rel,
1840
- target,
1841
- ref: anchorRef,
1842
- id: anchorId
1843
- } : {};
1844
- // Object of all Checked props that will be passed to the root `Card` component
1845
- const checkedProps = (type === 'checkbox' || type === 'radio') && !href ? {
1846
- ...commonProps,
1847
- 'aria-checked': type === 'radio' ? value === state : type === 'checkbox' ? checked : undefined,
1848
- 'aria-describedby': `${componentId}-title`,
1849
- 'aria-disabled': isDisabled,
1850
- 'data-value': value ?? undefined,
1851
- role: type === 'checkbox' || type === 'radio' ? type : undefined,
1852
- onClick: handleClick,
1853
- onKeyDown: event => {
1854
- if (event.key === 'Enter' || event.key === ' ') {
1855
- handleClick();
1856
- }
1857
- },
1858
- ref,
1859
- tabIndex: 0
1860
- } : {};
1861
- const getTitle = () => {
1862
- const titleContent = href && !type ? /*#__PURE__*/jsx("a", {
1863
- className: "np-Card-titleLink",
1864
- ...anchorProps,
1865
- children: title
1866
- }) : title;
1867
- const titleProps = {
1868
- id: `${componentId}-title`,
1869
- as: headingLevel,
1870
- className: 'np-Card-title'
1871
- };
1872
- return useDisplayFont ? /*#__PURE__*/jsx(Display, {
1873
- type: Typography.DISPLAY_SMALL,
1874
- ...titleProps,
1875
- children: titleContent
1876
- }) : /*#__PURE__*/jsx(Title, {
1877
- type: Typography.TITLE_SUBSECTION,
1878
- ...titleProps,
1879
- children: titleContent
1880
- });
1881
- };
1882
- useEffect(() => {
1883
- setChecked(defaultChecked ?? isChecked ?? false);
1884
- }, [defaultChecked, isChecked]);
1885
- return /*#__PURE__*/jsxs(Card$1, {
1886
- ...commonProps,
1887
- ...checkedProps,
1888
- ...props,
1889
- children: [(value === state || checked) && /*#__PURE__*/jsx("span", {
1890
- className: "np-Card-check",
1891
- children: /*#__PURE__*/jsx(Check, {
1892
- size: 24,
1893
- "aria-hidden": "true"
1894
- })
1895
- }), getTitle(), /*#__PURE__*/jsx(Body, {
1896
- className: "np-Card-description",
1897
- children: description
1898
- }), imageSource && /*#__PURE__*/jsx("div", {
1899
- className: classNames('np-Card-image', {
1900
- imageClass
1901
- }),
1902
- children: /*#__PURE__*/jsx(Image, {
1903
- src: imageSource,
1904
- alt: imageAlt || '',
1905
- loading: "lazy"
1570
+ isMobile
1571
+ } = useLayout();
1572
+ const titleId = useId();
1573
+ return /*#__PURE__*/jsx(Dimmer$1, {
1574
+ open: open,
1575
+ onClose: onClose,
1576
+ children: /*#__PURE__*/jsx(SlidingPanel, {
1577
+ open: open,
1578
+ position: isMobile ? Position.BOTTOM : position,
1579
+ children: /*#__PURE__*/jsxs("div", {
1580
+ role: "dialog",
1581
+ "aria-modal": true,
1582
+ "aria-labelledby": headerTitle ? titleId : undefined,
1583
+ className: classNames('np-drawer', className),
1584
+ children: [/*#__PURE__*/jsxs("div", {
1585
+ className: classNames('np-drawer-header', {
1586
+ 'np-drawer-header--withborder': headerTitle
1587
+ }),
1588
+ children: [headerTitle && /*#__PURE__*/jsx(Title, {
1589
+ id: titleId,
1590
+ type: Typography.TITLE_BODY,
1591
+ children: headerTitle
1592
+ }), /*#__PURE__*/jsx(CloseButton, {
1593
+ onClick: onClose
1594
+ })]
1595
+ }), children && /*#__PURE__*/jsx("div", {
1596
+ className: classNames('np-drawer-content'),
1597
+ children: children
1598
+ }), footerContent && /*#__PURE__*/jsx("div", {
1599
+ className: classNames('np-drawer-footer'),
1600
+ children: footerContent
1601
+ })]
1906
1602
  })
1907
- }), /*#__PURE__*/jsx(PromoCardIndicator, {
1908
- label: indicatorLabel,
1909
- icon: getIconType(),
1910
- isSmall: isSmall
1911
- })]
1603
+ })
1912
1604
  });
1913
- });
1914
- var PromoCard$1 = /*#__PURE__*/React__default.memo(PromoCard);
1605
+ }
1915
1606
 
1916
- const LEFT_SCROLL_OFFSET = 8;
1917
- const Carousel = ({
1918
- header,
1919
- className,
1920
- cards,
1921
- onClick
1922
- }) => {
1923
- const [scrollPosition, setScrollPosition] = useState(0);
1924
- const [previousScrollPosition, setPreviousScrollPosition] = useState(0);
1925
- const [scrollIsAtEnd, setScrollIsAtEnd] = useState(false);
1926
- const [visibleCardOnMobileView, setVisibleCardOnMobileView] = useState('');
1927
- const carouselElementRef = useRef(null);
1928
- const carouselCardsRef = useRef([]);
1929
- const isLeftActionButtonEnabled = scrollPosition > LEFT_SCROLL_OFFSET;
1930
- const areActionButtonsEnabled = isLeftActionButtonEnabled || !scrollIsAtEnd;
1931
- const [focusedCard, setFocusedCard] = useState(cards?.[0]?.id);
1932
- const updateScrollButtonsState = () => {
1933
- if (carouselElementRef.current) {
1934
- const {
1935
- scrollWidth,
1936
- offsetWidth
1937
- } = carouselElementRef.current;
1938
- const scrollAtEnd = scrollWidth - offsetWidth <= scrollPosition + LEFT_SCROLL_OFFSET;
1939
- setScrollIsAtEnd(scrollAtEnd);
1940
- }
1941
- const scrollDirecton = scrollPosition > previousScrollPosition ? 'right' : 'left';
1942
- const cardsInFullViewIds = [];
1943
- carouselCardsRef.current.forEach(card => {
1944
- if (isVisible(carouselElementRef.current, card.cardElement)) {
1945
- // eslint-disable-next-line functional/immutable-data
1946
- cardsInFullViewIds.push(card.cardElement.getAttribute('id') ?? '');
1947
- }
1948
- });
1949
- if (cardsInFullViewIds.length >= 1) {
1950
- const visibleCardIndex = scrollDirecton === 'right' ? cardsInFullViewIds.length - 1 : 0;
1951
- const visibleCardId = cardsInFullViewIds[visibleCardIndex];
1952
- setVisibleCardOnMobileView(visibleCardId);
1953
- setFocusedCard(visibleCardId);
1954
- }
1955
- setPreviousScrollPosition(scrollPosition);
1956
- };
1957
- const scrollCarousel = (direction = 'right') => {
1958
- if (carouselElementRef.current) {
1959
- const {
1960
- scrollWidth
1961
- } = carouselElementRef.current;
1962
- const cardWidth = scrollWidth / carouselCardsRef.current.length;
1963
- const res = Math.floor(cardWidth - cardWidth * 0.05);
1964
- carouselElementRef.current.scrollBy({
1965
- left: direction === 'right' ? res : -res,
1966
- behavior: 'smooth'
1967
- });
1968
- }
1969
- };
1970
- const handleOnKeyDown = (event, index) => {
1971
- if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
1972
- const nextIndex = event.key === 'ArrowRight' ? index + 1 : index - 1;
1973
- const nextCard = cards[nextIndex];
1974
- if (nextCard) {
1975
- const ref = carouselCardsRef.current[nextIndex];
1976
- if (ref.type === 'promo') {
1977
- ref.anchorElement?.focus();
1607
+ const INITIAL_Y_POSITION = 0;
1608
+ const CONTENT_SCROLL_THRESHOLD = 1;
1609
+ const MOVE_OFFSET_THRESHOLD = 50;
1610
+ /**
1611
+ * Neptune: https://transferwise.github.io/neptune/components/bottom-sheet/
1612
+ *
1613
+ * Neptune Web: https://transferwise.github.io/neptune-web/components/overlays/BottomSheet
1614
+ *
1615
+ */
1616
+ const BottomSheet$1 = props => {
1617
+ const bottomSheetReference = useRef(null);
1618
+ const topBarReference = useRef(null);
1619
+ const contentReference = useRef(null);
1620
+ const [pressed, setPressed] = useState(false);
1621
+ /**
1622
+ * Used to track `requestAnimationFrame` requests
1623
+ *
1624
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame#return_value
1625
+ */
1626
+ const animationId = useRef(0);
1627
+ /**
1628
+ * Difference between initial coordinate ({@link initialYCoordinate})
1629
+ * and new position (when user moves component), it's get calculated on `onTouchMove` and `onMouseMove` events
1630
+ *
1631
+ * @see {@link calculateOffsetAfterMove}
1632
+ */
1633
+ const moveOffset = useRef(0);
1634
+ const initialYCoordinate = useRef(0);
1635
+ // apply shadow to the bottom of top-bar when scroll over content
1636
+ useConditionalListener({
1637
+ attachListener: props.open && !isServerSide(),
1638
+ callback: () => {
1639
+ if (topBarReference.current !== null) {
1640
+ const {
1641
+ classList
1642
+ } = topBarReference.current;
1643
+ if (!isContentScrollPositionAtTop()) {
1644
+ classList.add('np-bottom-sheet--top-bar--shadow');
1978
1645
  } else {
1979
- ref.cardElement?.focus();
1646
+ classList.remove('np-bottom-sheet--top-bar--shadow');
1980
1647
  }
1981
- scrollCardIntoView(carouselCardsRef.current[nextIndex].cardElement, nextCard);
1982
- event.preventDefault();
1983
1648
  }
1649
+ },
1650
+ eventType: 'scroll',
1651
+ parent: isServerSide() ? undefined : document
1652
+ });
1653
+ function move(newHeight) {
1654
+ if (bottomSheetReference.current !== null) {
1655
+ bottomSheetReference.current.style.transform = `translateY(${newHeight}px)`;
1984
1656
  }
1985
- if (event.key === 'Enter' || event.key === ' ') {
1986
- event.currentTarget.click();
1657
+ }
1658
+ function close(event) {
1659
+ setPressed(false);
1660
+ moveOffset.current = INITIAL_Y_POSITION;
1661
+ if (bottomSheetReference.current !== null) {
1662
+ bottomSheetReference.current.style.removeProperty('transform');
1663
+ }
1664
+ if (props.onClose) {
1665
+ props.onClose(event);
1987
1666
  }
1667
+ }
1668
+ const onSwipeStart = event => {
1669
+ initialYCoordinate.current = ('touches' in event ? event.touches[0] : event).clientY;
1670
+ setPressed(true);
1988
1671
  };
1989
- const scrollCardIntoView = (element, card) => {
1990
- element.scrollIntoView({
1991
- behavior: 'smooth',
1992
- block: 'nearest',
1993
- inline: 'center'
1994
- });
1995
- setFocusedCard(card.id);
1672
+ const onSwipeMove = event => {
1673
+ if (pressed) {
1674
+ const {
1675
+ clientY
1676
+ } = 'touches' in event ? event.touches[0] : event;
1677
+ const offset = calculateOffsetAfterMove(clientY);
1678
+ // check whether move is to the bottom only and content scroll position is at the top
1679
+ if (offset > INITIAL_Y_POSITION && isContentScrollPositionAtTop()) {
1680
+ moveOffset.current = offset;
1681
+ animationId.current = requestAnimationFrame(() => {
1682
+ if (animationId.current !== undefined && bottomSheetReference.current !== null) {
1683
+ move(offset);
1684
+ }
1685
+ });
1686
+ }
1687
+ }
1996
1688
  };
1997
- useEffect(() => {
1998
- updateScrollButtonsState();
1999
- // eslint-disable-next-line react-hooks/exhaustive-deps
2000
- }, [scrollPosition]);
2001
- useEffect(() => {
2002
- window.addEventListener('resize', updateScrollButtonsState);
2003
- return () => {
2004
- window.removeEventListener('resize', updateScrollButtonsState);
1689
+ function onSwipeEnd(event) {
1690
+ // stop moving component
1691
+ cancelAnimationFrame(animationId.current);
1692
+ setPressed(false);
1693
+ // check whether move down is strong enough
1694
+ // and content scroll position is at the top to close the component
1695
+ if (moveOffset.current > MOVE_OFFSET_THRESHOLD && isContentScrollPositionAtTop()) {
1696
+ close(event);
1697
+ }
1698
+ // otherwise move component back to default (initial) position
1699
+ else {
1700
+ move(INITIAL_Y_POSITION);
1701
+ }
1702
+ moveOffset.current = INITIAL_Y_POSITION;
1703
+ }
1704
+ function isContentScrollPositionAtTop() {
1705
+ return contentReference?.current?.scrollTop !== undefined && contentReference.current.scrollTop <= CONTENT_SCROLL_THRESHOLD;
1706
+ }
1707
+ /**
1708
+ * Calculates how hard user moves component,
1709
+ * result value used to determine whether to hide component or re-position to default state
1710
+ *
1711
+ * @param afterMoveYCoordinate
1712
+ */
1713
+ function calculateOffsetAfterMove(afterMoveYCoordinate) {
1714
+ return afterMoveYCoordinate - initialYCoordinate.current;
1715
+ }
1716
+ /**
1717
+ * Set `max-height` for content part (in order to keep it scrollable for content overflow cases) of the component
1718
+ * and ensures space for safe zone (32px) at the top.
1719
+ */
1720
+ function setContentMaxHeight() {
1721
+ const safeZoneHeight = '64px';
1722
+ const topbarHeight = '32px';
1723
+ const windowHight = isServerSide() ? 0 : window.innerHeight;
1724
+ /**
1725
+ * Calculate _real_ height of the screen (taking into account parts of browser interface).
1726
+ *
1727
+ * See https://css-tricks.com/the-trick-to-viewport-units-on-mobile for more details.
1728
+ */
1729
+ const screenHeight = `${windowHight * 0.01 * 100}px`;
1730
+ return {
1731
+ maxHeight: `calc(${screenHeight} - ${safeZoneHeight} - ${topbarHeight})`
2005
1732
  };
2006
- // eslint-disable-next-line react-hooks/exhaustive-deps
2007
- }, []);
2008
- const addElementToCardsRefArray = (index, ref) => {
2009
- if (ref) {
2010
- // eslint-disable-next-line functional/immutable-data
2011
- carouselCardsRef.current[index] = {
2012
- type: ref.type ?? carouselCardsRef.current?.[index]?.type,
2013
- cardElement: ref.cardElement ?? carouselCardsRef.current?.[index]?.cardElement,
2014
- anchorElement: ref.anchorElement ?? carouselCardsRef.current?.[index]?.anchorElement
2015
- };
1733
+ }
1734
+ const is400Zoom = useMedia(`(max-width: ${Breakpoint.ZOOM_400}px)`);
1735
+ return is400Zoom ? /*#__PURE__*/jsx(Drawer, {
1736
+ open: props.open,
1737
+ className: props.className,
1738
+ onClose: close,
1739
+ children: props.children
1740
+ }) : /*#__PURE__*/jsx(Dimmer$1, {
1741
+ open: props.open,
1742
+ fadeContentOnEnter: true,
1743
+ fadeContentOnExit: true,
1744
+ onClose: close,
1745
+ children: /*#__PURE__*/jsx(SlidingPanel, {
1746
+ ref: bottomSheetReference,
1747
+ open: props.open,
1748
+ position: Position.BOTTOM,
1749
+ className: classNames('np-bottom-sheet', props.className),
1750
+ children: /*#__PURE__*/jsxs("div", {
1751
+ role: "dialog",
1752
+ "aria-modal": true,
1753
+ onTouchStart: onSwipeStart,
1754
+ onTouchMove: onSwipeMove,
1755
+ onTouchEnd: onSwipeEnd,
1756
+ onMouseDown: onSwipeStart,
1757
+ onMouseMove: onSwipeMove,
1758
+ onMouseUp: onSwipeEnd,
1759
+ children: [/*#__PURE__*/jsxs("div", {
1760
+ ref: topBarReference,
1761
+ className: "np-bottom-sheet--top-bar",
1762
+ children: [/*#__PURE__*/jsx("div", {
1763
+ className: "np-bottom-sheet--handler"
1764
+ }), /*#__PURE__*/jsx(CloseButton, {
1765
+ size: "sm",
1766
+ className: "sr-only np-bottom-sheet--close-btn",
1767
+ onClick: close
1768
+ })]
1769
+ }), /*#__PURE__*/jsx("div", {
1770
+ ref: contentReference,
1771
+ style: setContentMaxHeight(),
1772
+ className: "np-bottom-sheet--content",
1773
+ children: props.children
1774
+ })]
1775
+ })
1776
+ })
1777
+ });
1778
+ };
1779
+
1780
+ const POPOVER_OFFSET = [0, 16];
1781
+ // By default the flip positioning explores only the opposite alternative. So if left is passed and there's no enough space
1782
+ // the right one gets chosen. If there's no space on both sides popover goes back to the initially chosen one left.
1783
+ // This mapping forces popover to try the four available positions before going back to the initial chosen one.
1784
+ const fallbackPlacements = {
1785
+ [Position.TOP]: [Position.BOTTOM, Position.RIGHT, Position.LEFT],
1786
+ [Position.BOTTOM]: [Position.TOP, Position.RIGHT, Position.LEFT],
1787
+ [Position.LEFT]: [Position.RIGHT, Position.TOP, Position.BOTTOM],
1788
+ [Position.RIGHT]: [Position.LEFT, Position.TOP, Position.BOTTOM]
1789
+ };
1790
+ const Panel = /*#__PURE__*/forwardRef(function Panel({
1791
+ arrow = false,
1792
+ flip = true,
1793
+ altAxis = false,
1794
+ children,
1795
+ open = false,
1796
+ onClose,
1797
+ position = Position.BOTTOM,
1798
+ anchorRef,
1799
+ anchorWidth = false,
1800
+ ...rest
1801
+ }, reference) {
1802
+ const [arrowElement, setArrowElement] = useState(null);
1803
+ const [popperElement, setPopperElement] = useState(null);
1804
+ const modifiers = [];
1805
+ if (altAxis) {
1806
+ modifiers.push({
1807
+ // https://popper.js.org/docs/v2/modifiers/prevent-overflow
1808
+ name: 'preventOverflow',
1809
+ options: {
1810
+ altAxis: true,
1811
+ tether: false
1812
+ }
1813
+ });
1814
+ }
1815
+ if (arrow) {
1816
+ modifiers.push({
1817
+ name: 'arrow',
1818
+ options: {
1819
+ element: arrowElement,
1820
+ options: {
1821
+ padding: 8 // 8px from the edges of the popper
1822
+ }
1823
+ }
1824
+ });
1825
+ // This lets you displace a popper element from its reference element.
1826
+ modifiers.push({
1827
+ name: 'offset',
1828
+ options: {
1829
+ offset: POPOVER_OFFSET
1830
+ }
1831
+ });
1832
+ }
1833
+ if (flip && fallbackPlacements[position]) {
1834
+ modifiers.push({
1835
+ name: 'flip',
1836
+ options: {
1837
+ fallbackPlacements: fallbackPlacements[position]
1838
+ }
1839
+ });
1840
+ }
1841
+ const {
1842
+ styles,
1843
+ attributes,
1844
+ forceUpdate
1845
+ } = usePopper(anchorRef.current, popperElement, {
1846
+ placement: position,
1847
+ modifiers
1848
+ });
1849
+ // If the trigger is not visible when the position is calculated, it will be incorrect. Because this can happen repeatedly (on resize for example),
1850
+ // it is most simple just to always position before opening
1851
+ useEffect(() => {
1852
+ if (open && forceUpdate) {
1853
+ forceUpdate();
2016
1854
  }
1855
+ }, [open]);
1856
+ const contentStyle = {
1857
+ ...(anchorWidth ? {
1858
+ width: anchorRef.current?.clientWidth
1859
+ } : undefined)
2017
1860
  };
2018
- return /*#__PURE__*/jsxs("div", {
2019
- className: classNames('carousel-wrapper', className),
2020
- children: [/*#__PURE__*/jsxs("div", {
2021
- className: "d-flex justify-content-between carousel__header",
2022
- children: [typeof header === 'string' ? /*#__PURE__*/jsx(Title, {
2023
- as: "span",
2024
- type: "title-body",
2025
- children: header
2026
- }) : header, areActionButtonsEnabled ? /*#__PURE__*/jsxs("div", {
2027
- className: "hidden-xs",
2028
- children: [/*#__PURE__*/jsx(ActionButton, {
2029
- className: "carousel__scroll-button",
2030
- tabIndex: -1,
2031
- priority: "secondary",
2032
- disabled: !isLeftActionButtonEnabled,
2033
- "aria-hidden": "true",
2034
- "data-testid": "scroll-carousel-left",
2035
- onClick: () => scrollCarousel('left'),
2036
- children: /*#__PURE__*/jsx(ChevronLeft, {})
2037
- }), /*#__PURE__*/jsx(ActionButton, {
2038
- tabIndex: -1,
2039
- className: "carousel__scroll-button m-l-1",
2040
- priority: "secondary",
2041
- "aria-hidden": "true",
2042
- "data-testid": "scroll-carousel-right",
2043
- disabled: scrollIsAtEnd,
2044
- onClick: () => scrollCarousel(),
2045
- children: /*#__PURE__*/jsx(ChevronRight, {})
2046
- })]
2047
- }) : null]
2048
- }), /*#__PURE__*/jsx("div", {
2049
- ref: carouselElementRef,
2050
- tabIndex: -1,
2051
- role: "list",
2052
- className: "carousel",
2053
- onScroll: event => {
2054
- const target = event.target;
2055
- setScrollPosition(target.scrollLeft);
2056
- },
2057
- children: cards?.map((card, index) => {
2058
- const sharedProps = {
2059
- id: card.id,
2060
- className: classNames('carousel__card', {
2061
- 'carousel__card--focused': card.id === focusedCard
2062
- }),
2063
- onClick: () => {
2064
- card.onClick?.();
2065
- onClick?.(card.id);
2066
- },
2067
- onFocus: event => {
2068
- scrollCardIntoView(event.currentTarget, card);
2069
- }
2070
- };
2071
- const cardContent = card.type !== 'promo' ? /*#__PURE__*/jsx("div", {
2072
- id: `${card.id}-content`,
2073
- className: classNames('carousel__card-content', {
2074
- [card.className ?? '']: !!card.className
2075
- })
1861
+ return /*#__PURE__*/jsx(Dimmer$1, {
1862
+ open: open,
1863
+ transparent: true,
1864
+ fadeContentOnEnter: true,
1865
+ fadeContentOnExit: true,
1866
+ onClose: onClose,
1867
+ children: /*#__PURE__*/jsx("div", {
1868
+ ...rest,
1869
+ ref: setPopperElement,
1870
+ role: "dialog"
1871
+ // eslint-disable-next-line react/forbid-dom-props
1872
+ ,
1873
+ style: {
1874
+ ...styles.popper
1875
+ },
1876
+ ...attributes.popper,
1877
+ className: classNames('np-panel', {
1878
+ 'np-panel--open': open
1879
+ }, rest.className),
1880
+ children: /*#__PURE__*/jsxs("div", {
1881
+ ref: reference
1882
+ /* eslint-disable-next-line react/forbid-dom-props */,
1883
+ style: contentStyle,
1884
+ className: classNames('np-panel__content'),
1885
+ children: [children, arrow && /*#__PURE__*/jsx("div", {
1886
+ ref: setArrowElement,
1887
+ className: classNames('np-panel__arrow')
2076
1888
  // eslint-disable-next-line react/forbid-dom-props
2077
1889
  ,
2078
- style: card.styles,
2079
- children: card.content
2080
- }) : null;
2081
- if (card.type === 'button') {
2082
- return /*#__PURE__*/jsx("div", {
2083
- "aria-labelledby": `${card.id}-content`,
2084
- role: "listitem",
2085
- children: /*#__PURE__*/jsx("div", {
2086
- ...sharedProps,
2087
- ref: el => {
2088
- if (el) {
2089
- // eslint-disable-next-line functional/immutable-data
2090
- carouselCardsRef.current[index] = {
2091
- type: 'default',
2092
- cardElement: el
2093
- };
2094
- }
2095
- },
2096
- role: "button",
2097
- tabIndex: 0,
2098
- onKeyDown: event => handleOnKeyDown(event, index),
2099
- children: cardContent
2100
- })
2101
- }, card.id);
2102
- }
2103
- if (card.type === 'promo') {
2104
- return /*#__PURE__*/jsx("div", {
2105
- id: card.id,
2106
- role: "listitem",
2107
- "aria-labelledby": `${card.id}-anchor`,
2108
- children: /*#__PURE__*/jsx(PromoCard$1, {
2109
- ...card,
2110
- type: undefined,
2111
- ...sharedProps,
2112
- ref: el => {
2113
- if (el) {
2114
- addElementToCardsRefArray(index, {
2115
- type: 'promo',
2116
- cardElement: el
2117
- });
2118
- }
2119
- },
2120
- anchorRef: el => {
2121
- if (el) {
2122
- addElementToCardsRefArray(index, {
2123
- type: 'promo',
2124
- anchorElement: el
2125
- });
2126
- }
2127
- },
2128
- anchorId: `${card.id}-anchor`,
2129
- onKeyDown: event => handleOnKeyDown(event, index)
2130
- })
2131
- }, card.id);
2132
- }
2133
- return /*#__PURE__*/jsx("div", {
2134
- "aria-labelledby": `${card.id}-content`,
2135
- role: "listitem",
2136
- children: /*#__PURE__*/jsx("a", {
2137
- ...sharedProps,
2138
- ref: el => {
2139
- if (el) {
2140
- // eslint-disable-next-line functional/immutable-data
2141
- carouselCardsRef.current[index] = {
2142
- type: 'default',
2143
- cardElement: el
2144
- };
2145
- }
2146
- },
2147
- href: card.href,
2148
- rel: "noreferrer",
2149
- onKeyDown: event => handleOnKeyDown(event, index),
2150
- children: cardContent
2151
- })
2152
- }, card.id);
1890
+ style: styles.arrow
1891
+ })]
2153
1892
  })
2154
- }), /*#__PURE__*/jsx("div", {
2155
- className: "visible-xs",
2156
- children: /*#__PURE__*/jsx("div", {
2157
- className: "carousel__indicators",
2158
- children: cards?.map((card, index) => /*#__PURE__*/jsx("button", {
2159
- "data-testid": `${card.id}-indicator`,
2160
- tabIndex: -1,
2161
- "aria-hidden": true,
2162
- type: "button",
2163
- className: classNames('carousel__indicator', {
2164
- 'carousel__indicator--selected': card.id === visibleCardOnMobileView
2165
- }),
2166
- onClick: () => {
2167
- scrollCardIntoView(carouselCardsRef.current[index].cardElement, card);
2168
- }
2169
- }, `${card.id}-indicator`))
1893
+ })
1894
+ });
1895
+ });
1896
+ var Panel$1 = Panel;
1897
+
1898
+ const ResponsivePanel = /*#__PURE__*/forwardRef(function ResponsivePanel({
1899
+ anchorRef,
1900
+ arrow = false,
1901
+ flip = true,
1902
+ children,
1903
+ className = undefined,
1904
+ onClose,
1905
+ open = false,
1906
+ position = Position.BOTTOM,
1907
+ anchorWidth = false
1908
+ }, reference) {
1909
+ const {
1910
+ isMobile
1911
+ } = useLayout();
1912
+ if (isMobile) {
1913
+ return /*#__PURE__*/jsx(BottomSheet$1, {
1914
+ open: open,
1915
+ className: className,
1916
+ onClose: onClose,
1917
+ children: children
1918
+ }, "bottomSheet");
1919
+ }
1920
+ return /*#__PURE__*/jsx(Panel$1, {
1921
+ ref: reference,
1922
+ flip: flip,
1923
+ arrow: arrow,
1924
+ open: open,
1925
+ position: position,
1926
+ anchorWidth: anchorWidth,
1927
+ anchorRef: anchorRef,
1928
+ className: className,
1929
+ onClose: onClose,
1930
+ children: children
1931
+ }, "panel");
1932
+ });
1933
+
1934
+ function SelectOption({
1935
+ selected: selectedValueProp = undefined,
1936
+ options,
1937
+ onChange,
1938
+ placeholder,
1939
+ disabled,
1940
+ ...props
1941
+ }) {
1942
+ const intl = useIntl();
1943
+ const rootRef = useRef(null);
1944
+ const [selected, setSelected] = useState(selectedValueProp);
1945
+ const [showOptions, setShowOptions] = useState(false);
1946
+ const hasSelected = selected !== undefined;
1947
+ const isLargeScreen = useScreenSize(Breakpoint.SMALL);
1948
+ const inputAttributes = useInputAttributes();
1949
+ const ariaLabelledBy = props['aria-labelledby'] ?? inputAttributes?.['aria-labelledby'];
1950
+ function handleOnClick(showOptionsStatus) {
1951
+ return () => {
1952
+ setShowOptions(showOptionsStatus);
1953
+ };
1954
+ }
1955
+ function handleOnChange(data) {
1956
+ return () => {
1957
+ setShowOptions(false);
1958
+ setSelected(data);
1959
+ onChange(data);
1960
+ };
1961
+ }
1962
+ function getOptions(isLargeScreen = false) {
1963
+ return /*#__PURE__*/jsx("div", {
1964
+ className: classNames({
1965
+ 'np-select-option-list': isLargeScreen
1966
+ }),
1967
+ children: options.map(optionsSection =>
1968
+ /*#__PURE__*/
1969
+ // eslint-disable-next-line react/jsx-key
1970
+ jsxs(Section, {
1971
+ className: classNames('np-select-option-section', {
1972
+ 'p-x-2 p-y-1': isLargeScreen
1973
+ }),
1974
+ children: [optionsSection.title && /*#__PURE__*/jsx(Header, {
1975
+ title: optionsSection.title
1976
+ }), /*#__PURE__*/jsx(NavigationOptionList, {
1977
+ children: optionsSection.options.map(option => {
1978
+ return (
1979
+ /*#__PURE__*/
1980
+ // eslint-disable-next-line react/jsx-key
1981
+ jsx(NavigationOption, {
1982
+ isContainerAligned: !isLargeScreen,
1983
+ showMediaCircle: true,
1984
+ showMediaAtAllSizes: true,
1985
+ onClick: handleOnChange(option),
1986
+ ...option
1987
+ })
1988
+ );
1989
+ })
1990
+ })]
1991
+ }))
1992
+ });
1993
+ }
1994
+ return /*#__PURE__*/jsxs(Fragment, {
1995
+ children: [/*#__PURE__*/jsx(Option$2, {
1996
+ ref: rootRef,
1997
+ "aria-haspopup": "dialog",
1998
+ "aria-expanded": showOptions,
1999
+ "aria-labelledby": ariaLabelledBy,
2000
+ showMediaAtAllSizes: true,
2001
+ disabled: disabled,
2002
+ decision: false,
2003
+ media: hasSelected ? selected.media : placeholder.media ?? /*#__PURE__*/jsx(Plus, {
2004
+ size: 24
2005
+ }),
2006
+ title: (hasSelected ? selected : placeholder).title,
2007
+ content: (hasSelected ? selected : placeholder).content,
2008
+ className: classNames('np-select-option', 'clickable', hasSelected ? 'np-select-option-selected' : 'np-select-option-placeholder', props.className),
2009
+ button: /*#__PURE__*/jsx("button", {
2010
+ type: "button",
2011
+ disabled: disabled,
2012
+ className: "btn-unstyled",
2013
+ "aria-label": hasSelected ? undefined : props['aria-label'],
2014
+ onClick: handleOnClick(true),
2015
+ children: hasSelected ? /*#__PURE__*/jsx(Chevron, {}) : /*#__PURE__*/jsx(ActionButton, {
2016
+ children: placeholder.actionLabel || intl.formatMessage(messages$c.actionLabel)
2017
+ })
2170
2018
  })
2019
+ }), /*#__PURE__*/jsx(ResponsivePanel, {
2020
+ anchorWidth: true,
2021
+ altAxis: true,
2022
+ anchorRef: rootRef,
2023
+ open: showOptions,
2024
+ position: Position.BOTTOM,
2025
+ onClose: handleOnClick(false),
2026
+ children: getOptions(isLargeScreen)
2171
2027
  })]
2172
2028
  });
2029
+ }
2030
+
2031
+ const iconTypeMap = {
2032
+ positive: Check,
2033
+ neutral: Info$1,
2034
+ warning: Alert$1,
2035
+ negative: Cross,
2036
+ pending: ClockBorderless,
2037
+ info: Info$1,
2038
+ error: Cross,
2039
+ success: Check
2040
+ };
2041
+ const StatusIcon = ({
2042
+ sentiment = 'neutral',
2043
+ size = 'md'
2044
+ }) => {
2045
+ const Icon = iconTypeMap[sentiment];
2046
+ const iconColor = sentiment === 'warning' || sentiment === 'pending' ? 'dark' : 'light';
2047
+ return /*#__PURE__*/jsx("span", {
2048
+ "data-testid": "status-icon",
2049
+ className: classNames('status-circle', `status-circle-${size}`, sentiment),
2050
+ children: /*#__PURE__*/jsx(Icon, {
2051
+ className: classNames('status-icon', iconColor)
2052
+ })
2053
+ });
2054
+ };
2055
+
2056
+ const reader = new commonmark.Parser();
2057
+ const writer = new commonmark.HtmlRenderer({
2058
+ safe: true
2059
+ });
2060
+ const NODE_TYPE_LIST = Object.values(MarkdownNodeType);
2061
+ function Markdown({
2062
+ as: Element = 'div',
2063
+ allowList,
2064
+ blockList,
2065
+ config,
2066
+ className,
2067
+ children
2068
+ }) {
2069
+ if (!children) {
2070
+ return null;
2071
+ }
2072
+ const linkTarget = config?.link?.target ?? '_self';
2073
+ const paragraphClass = config?.paragraph?.className ?? '';
2074
+ if (allowList != null && blockList != null) {
2075
+ logActionRequired('Markdown supports only one of `allowList` or `blockList` to be used at a time. `blockList` will be ignored.');
2076
+ }
2077
+ const parser = nodes => {
2078
+ const parsed = reader.parse(nodes);
2079
+ const toExclude = allowList != null ? NODE_TYPE_LIST.filter(type => !allowList.includes(type)) : blockList;
2080
+ return toExclude != null ? stripNodes({
2081
+ parsed,
2082
+ blockList: toExclude
2083
+ }) : parsed;
2084
+ };
2085
+ const createMarkup = () => {
2086
+ const parsed = parser(children);
2087
+ return writer.render(parsed).replace(/<a href="/g, `<a target="${linkTarget}" href="`).replace(/<p>/g, `<p class="${paragraphClass}">`);
2088
+ };
2089
+ return /*#__PURE__*/jsx(Element, {
2090
+ className: className,
2091
+ dangerouslySetInnerHTML: {
2092
+ __html: createMarkup()
2093
+ }
2094
+ });
2095
+ }
2096
+ function stripNodes({
2097
+ blockList,
2098
+ parsed
2099
+ }) {
2100
+ if (!parsed) {
2101
+ return parsed;
2102
+ }
2103
+ const walker = parsed.walker();
2104
+ for (let event = walker.next(); event != null; event = walker.next()) {
2105
+ const {
2106
+ node
2107
+ } = event;
2108
+ if (blockList.includes(node.type) && !event.entering) {
2109
+ while (node.firstChild != null) {
2110
+ node.insertBefore(node.firstChild);
2111
+ }
2112
+ node.unlink();
2113
+ }
2114
+ }
2115
+ return parsed;
2116
+ }
2117
+
2118
+ const allowList = [MarkdownNodeType.STRONG];
2119
+ function InlineMarkdown(props) {
2120
+ return /*#__PURE__*/jsx(Markdown, {
2121
+ ...props,
2122
+ as: "span",
2123
+ allowList: allowList,
2124
+ blockList: undefined
2125
+ });
2126
+ }
2127
+
2128
+ function Action({
2129
+ action,
2130
+ className
2131
+ }) {
2132
+ if ('href' in action) {
2133
+ return /*#__PURE__*/jsx(Link, {
2134
+ href: action.href,
2135
+ className: className,
2136
+ "aria-label": action['aria-label'],
2137
+ target: action.target,
2138
+ type: Typography.LINK_LARGE,
2139
+ onClick: action.onClick,
2140
+ children: action.text
2141
+ });
2142
+ }
2143
+ return /*#__PURE__*/jsx("button", {
2144
+ type: "button",
2145
+ "aria-label": action['aria-label'],
2146
+ className: classNames('btn-unstyled np-text-link-large', className),
2147
+ onClick: action.onClick,
2148
+ children: action.text
2149
+ });
2150
+ }
2151
+
2152
+ var AlertArrowPosition;
2153
+ (function (AlertArrowPosition) {
2154
+ AlertArrowPosition["TOP_LEFT"] = "up-left";
2155
+ AlertArrowPosition["TOP"] = "up-center";
2156
+ AlertArrowPosition["TOP_RIGHT"] = "up-right";
2157
+ AlertArrowPosition["BOTTOM_LEFT"] = "down-left";
2158
+ AlertArrowPosition["BOTTOM"] = "down-center";
2159
+ AlertArrowPosition["BOTTOM_RIGHT"] = "down-right";
2160
+ })(AlertArrowPosition || (AlertArrowPosition = {}));
2161
+ function resolveType(type) {
2162
+ switch (type) {
2163
+ case 'success':
2164
+ return 'positive';
2165
+ case 'info':
2166
+ return 'neutral';
2167
+ case 'error':
2168
+ return 'negative';
2169
+ default:
2170
+ return type;
2171
+ }
2172
+ }
2173
+ function Alert({
2174
+ arrow,
2175
+ action,
2176
+ children,
2177
+ className,
2178
+ dismissible,
2179
+ icon,
2180
+ onDismiss,
2181
+ message,
2182
+ size,
2183
+ title,
2184
+ type = 'neutral',
2185
+ variant = 'desktop'
2186
+ }) {
2187
+ useEffect(() => {
2188
+ if (arrow !== undefined) {
2189
+ logActionRequired("Alert component doesn't support 'arrow' anymore, use 'InlineAlert' instead.");
2190
+ }
2191
+ }, [arrow]);
2192
+ useEffect(() => {
2193
+ if (children !== undefined) {
2194
+ logActionRequired("Alert component doesn't support 'children' anymore, use 'message' instead.");
2195
+ }
2196
+ }, [children]);
2197
+ useEffect(() => {
2198
+ if (dismissible !== undefined) {
2199
+ logActionRequired("Alert component doesn't support 'dismissible' anymore, use 'onDismiss' instead.");
2200
+ }
2201
+ }, [dismissible]);
2202
+ useEffect(() => {
2203
+ if (size !== undefined) {
2204
+ logActionRequired("Alert component doesn't support 'size' anymore, please remove that prop.");
2205
+ }
2206
+ }, [size]);
2207
+ const resolvedType = resolveType(type);
2208
+ useEffect(() => {
2209
+ if (resolvedType !== type) {
2210
+ logActionRequired(`Alert component has deprecated '${type}' value for the 'type' prop. Please use '${resolvedType}' instead.`);
2211
+ }
2212
+ }, [resolvedType, type]);
2213
+ const [shouldFire, setShouldFire] = useState(false);
2214
+ const closeButtonReference = useRef(null);
2215
+ return /*#__PURE__*/jsxs("div", {
2216
+ className: classNames('alert d-flex', `alert-${resolvedType}`, arrow != null && alertArrowClassNames(arrow), className),
2217
+ "data-testid": "alert",
2218
+ onTouchStart: () => setShouldFire(true),
2219
+ onTouchEnd: event => {
2220
+ if (shouldFire && action?.href &&
2221
+ // Check if current event is triggered from closeButton
2222
+ event.target instanceof Node && closeButtonReference.current && !closeButtonReference.current.contains(event.target)) {
2223
+ if (action.target === '_blank') {
2224
+ window.top?.open(action.href);
2225
+ } else {
2226
+ window.top?.location.assign(action.href);
2227
+ }
2228
+ }
2229
+ setShouldFire(false);
2230
+ },
2231
+ onTouchMove: () => setShouldFire(false),
2232
+ children: [/*#__PURE__*/jsxs("div", {
2233
+ className: classNames('alert__content', 'd-flex', 'flex-grow-1', variant),
2234
+ "data-testid": variant,
2235
+ children: [icon ? /*#__PURE__*/jsx("div", {
2236
+ className: "alert__icon",
2237
+ children: icon
2238
+ }) : /*#__PURE__*/jsx(StatusIcon, {
2239
+ size: Size.LARGE,
2240
+ sentiment: resolvedType
2241
+ }), /*#__PURE__*/jsxs("div", {
2242
+ className: "alert__message",
2243
+ children: [/*#__PURE__*/jsxs("div", {
2244
+ role: Sentiment.NEGATIVE === resolvedType ? 'alert' : 'status',
2245
+ children: [title && /*#__PURE__*/jsx(Title, {
2246
+ className: "m-b-1",
2247
+ type: Typography.TITLE_BODY,
2248
+ children: title
2249
+ }), /*#__PURE__*/jsx(Body, {
2250
+ as: "span",
2251
+ className: "d-block",
2252
+ type: Typography.BODY_LARGE,
2253
+ children: children || /*#__PURE__*/jsx(InlineMarkdown, {
2254
+ children: message
2255
+ })
2256
+ })]
2257
+ }), action && /*#__PURE__*/jsx(Action, {
2258
+ action: action,
2259
+ className: "m-t-1"
2260
+ })]
2261
+ })]
2262
+ }), onDismiss && /*#__PURE__*/jsx(CloseButton, {
2263
+ ref: closeButtonReference,
2264
+ className: "m-l-2",
2265
+ onClick: onDismiss
2266
+ })]
2267
+ });
2268
+ }
2269
+ function alertArrowClassNames(arrow) {
2270
+ switch (arrow) {
2271
+ case 'down-center':
2272
+ return 'arrow arrow-bottom arrow-center';
2273
+ case 'down-left':
2274
+ return 'arrow arrow-bottom arrow-left';
2275
+ case 'down-right':
2276
+ return 'arrow arrow-bottom arrow-right';
2277
+ case 'up-center':
2278
+ return 'arrow arrow-center';
2279
+ case 'up-right':
2280
+ return 'arrow arrow-right';
2281
+ case 'up-left':
2282
+ default:
2283
+ return 'arrow';
2284
+ }
2285
+ }
2286
+
2287
+ // TODO: consider to move this enum into component file once we migrate it on TypeScript or replace with some common enum
2288
+ var AvatarType;
2289
+ (function (AvatarType) {
2290
+ AvatarType["THUMBNAIL"] = "thumbnail";
2291
+ AvatarType["ICON"] = "icon";
2292
+ AvatarType["EMOJI"] = "emoji";
2293
+ AvatarType["INITIALS"] = "initials";
2294
+ })(AvatarType || (AvatarType = {}));
2295
+
2296
+ /*
2297
+ * The colors we support generating an Avatar background color from a "seed" for.
2298
+ * Changing this array will change the assignment between seeds.
2299
+ * Do not change this array.
2300
+ */
2301
+ const avatarColors = ['--color-bright-blue', '--color-bright-yellow', '--color-bright-pink', '--color-bright-orange'];
2302
+ /*
2303
+ * Takes in a "seed" string and spits out an index for the color we should use.
2304
+ * This implementation has been synced across all three clients so that we consistently
2305
+ * generate branded avatar colors when rendering a list of Avatars.
2306
+ * Do not change this implementation.
2307
+ */
2308
+ const hashSeed = seed => {
2309
+ const base = 31;
2310
+ const modulo = avatarColors.length;
2311
+ let hashValue = 0;
2312
+ let basePow = 1;
2313
+ for (let i = 0; i < seed.length; i += 1) {
2314
+ hashValue = (hashValue + seed.charCodeAt(i) * basePow) % modulo;
2315
+ basePow = basePow * base % modulo;
2316
+ }
2317
+ return hashValue;
2318
+ };
2319
+ const getAvatarColorFromSeed = seed => {
2320
+ return avatarColors[hashSeed(seed)];
2321
+ };
2322
+
2323
+ const backwardsCompatibleSize = size => {
2324
+ switch (size) {
2325
+ case 'sm':
2326
+ return 24;
2327
+ case 'md':
2328
+ return 48;
2329
+ case 'lg':
2330
+ return 72;
2331
+ default:
2332
+ return size;
2333
+ }
2334
+ };
2335
+ const Avatar = ({
2336
+ backgroundColor = null,
2337
+ backgroundColorSeed = null,
2338
+ children = null,
2339
+ className,
2340
+ outlined = false,
2341
+ size: sizeFromProps = 48,
2342
+ theme = Theme.LIGHT,
2343
+ type = 'thumbnail'
2344
+ }) => {
2345
+ const backgroundColorFromSeed = useMemo(() => !backgroundColor && backgroundColorSeed ? `var(${getAvatarColorFromSeed(backgroundColorSeed)})` : undefined, [backgroundColor, backgroundColorSeed]);
2346
+ const size = backwardsCompatibleSize(sizeFromProps);
2347
+ return /*#__PURE__*/jsx("div", {
2348
+ className: classNames('tw-avatar', className, `tw-avatar--${size}`, `tw-avatar--${type}`, {
2349
+ 'tw-avatar--outlined': outlined,
2350
+ 'tw-avatar--branded': Boolean(backgroundColorFromSeed),
2351
+ 'np-text-title-body': type === 'initials'
2352
+ }),
2353
+ children: /*#__PURE__*/jsx("div", {
2354
+ className: "tw-avatar__content",
2355
+ style: {
2356
+ backgroundColor: backgroundColor || backgroundColorFromSeed
2357
+ },
2358
+ children: children
2359
+ })
2360
+ });
2361
+ };
2362
+
2363
+ const Badge = ({
2364
+ badge,
2365
+ className = undefined,
2366
+ size = Size.SMALL,
2367
+ border = Theme.LIGHT,
2368
+ children
2369
+ }) => {
2370
+ const classes = classNames('tw-badge', {
2371
+ [`tw-badge-border-${border}`]: border,
2372
+ [`tw-badge-${size}`]: size
2373
+ }, className);
2374
+ return /*#__PURE__*/jsxs("div", {
2375
+ className: classes,
2376
+ children: [/*#__PURE__*/jsx("div", {
2377
+ className: "tw-badge__children",
2378
+ children: children
2379
+ }), /*#__PURE__*/jsx("div", {
2380
+ className: "tw-badge__content",
2381
+ children: badge
2382
+ })]
2383
+ });
2384
+ };
2385
+
2386
+ const OptionalBadge = ({
2387
+ url,
2388
+ altText,
2389
+ statusIcon,
2390
+ children,
2391
+ ...rest
2392
+ }) => {
2393
+ if (url) {
2394
+ return /*#__PURE__*/jsx(Badge, {
2395
+ badge: /*#__PURE__*/jsx("img", {
2396
+ src: url,
2397
+ alt: altText
2398
+ }),
2399
+ ...rest,
2400
+ children: children
2401
+ });
2402
+ }
2403
+ if (statusIcon) {
2404
+ return /*#__PURE__*/jsx(Badge, {
2405
+ badge: /*#__PURE__*/jsx(StatusIcon, {
2406
+ sentiment: statusIcon,
2407
+ size: Size.SMALL
2408
+ }),
2409
+ ...rest,
2410
+ children: children
2411
+ });
2412
+ }
2413
+ return /*#__PURE__*/jsx(Fragment, {
2414
+ children: children
2415
+ });
2416
+ };
2417
+ const AvatarWrapper = ({
2418
+ url,
2419
+ profileType,
2420
+ profileId,
2421
+ badgeUrl,
2422
+ badgeAltText,
2423
+ badgeStatusIcon,
2424
+ name,
2425
+ avatarProps,
2426
+ badgeProps
2427
+ }) => {
2428
+ const [hasImageLoadError, setImageLoadError] = useState(false);
2429
+ const isBusinessProfile = profileType === ProfileType.BUSINESS;
2430
+ // Reset the errored state when url changes
2431
+ useEffect(() => setImageLoadError(false), [url]);
2432
+ const getAvatarProps = () => {
2433
+ if (url && !hasImageLoadError) {
2434
+ return {
2435
+ type: AvatarType.THUMBNAIL,
2436
+ children: /*#__PURE__*/jsx("img", {
2437
+ src: url,
2438
+ alt: "",
2439
+ onError: () => setImageLoadError(true)
2440
+ }),
2441
+ ...avatarProps
2442
+ };
2443
+ }
2444
+ if (profileType) {
2445
+ return {
2446
+ type: AvatarType.ICON,
2447
+ children: isBusinessProfile ? /*#__PURE__*/jsx(Briefcase, {
2448
+ size: "24"
2449
+ }) : /*#__PURE__*/jsx(Person, {
2450
+ size: "24"
2451
+ }),
2452
+ ...avatarProps
2453
+ };
2454
+ }
2455
+ if (name) {
2456
+ return {
2457
+ type: AvatarType.INITIALS,
2458
+ children: /*#__PURE__*/jsx(Fragment, {
2459
+ children: getInitials(name)
2460
+ }),
2461
+ backgroundColorSeed: profileId?.toString(),
2462
+ ...avatarProps
2463
+ };
2464
+ }
2465
+ return {
2466
+ type: AvatarType.ICON,
2467
+ children: /*#__PURE__*/jsx(Person, {
2468
+ size: "24"
2469
+ }),
2470
+ ...avatarProps
2471
+ };
2472
+ };
2473
+ return /*#__PURE__*/jsx(OptionalBadge, {
2474
+ url: badgeUrl,
2475
+ altText: badgeAltText,
2476
+ statusIcon: badgeStatusIcon,
2477
+ ...badgeProps,
2478
+ children: /*#__PURE__*/jsx(Avatar, {
2479
+ size: Size.MEDIUM,
2480
+ ...getAvatarProps()
2481
+ })
2482
+ });
2173
2483
  };
2174
- const isVisible = (container, el) => {
2175
- const cWidth = container.offsetWidth;
2176
- const cScrollOffset = container.scrollLeft;
2177
- const elemLeft = el.offsetLeft - container.offsetLeft;
2178
- const elemRight = elemLeft + el.offsetWidth;
2179
- return elemLeft >= cScrollOffset && elemRight <= cScrollOffset + cWidth;
2180
- };
2484
+ function getInitials(name) {
2485
+ const allInitials = name.split(' ').map(part => part[0]).join('').toUpperCase();
2486
+ if (allInitials.length === 1) {
2487
+ return allInitials[0];
2488
+ }
2489
+ return allInitials[0] + allInitials.slice(-1);
2490
+ }
2181
2491
 
2182
- const Card = /*#__PURE__*/forwardRef(function Card({
2183
- 'aria-label': ariaLabel,
2184
- as: Element = 'div',
2185
- isExpanded,
2186
- title,
2187
- details,
2188
- children,
2189
- onClick,
2190
- icon,
2191
- id,
2492
+ const Card$1 = /*#__PURE__*/forwardRef(({
2192
2493
  className,
2193
- 'data-testid': dataTestId
2194
- }, reference) {
2195
- const isOpen = !!(isExpanded && children);
2196
- return /*#__PURE__*/jsxs(Element, {
2197
- ref: reference,
2198
- className: classNames('np-card', className, {
2199
- 'np-card--expanded': isOpen,
2200
- 'np-card--inactive': !children,
2201
- 'np-card--has-icon': !!icon
2202
- }),
2494
+ children = null,
2495
+ id,
2496
+ isDisabled = false,
2497
+ isSmall = false,
2498
+ onDismiss,
2499
+ testId,
2500
+ ...props
2501
+ }, ref) => {
2502
+ const closeButtonReference = useRef(null);
2503
+ return /*#__PURE__*/jsxs("div", {
2504
+ ref: ref,
2505
+ className: classNames('np-Card', {
2506
+ 'np-Card--small': !!isSmall,
2507
+ 'is-disabled': !!isDisabled
2508
+ }, className),
2203
2509
  id: id,
2204
- "data-testid": dataTestId,
2205
- children: [/*#__PURE__*/jsx(Option$2, {
2206
- "aria-label": ariaLabel,
2207
- as: children ? 'button' : 'div',
2208
- className: classNames('np-card__button'),
2209
- media: icon,
2210
- title: title,
2211
- content: details,
2212
- showMediaAtAllSizes: true,
2213
- button: children && /*#__PURE__*/jsx(Chevron, {
2214
- orientation: isOpen ? Position.TOP : Position.BOTTOM
2215
- }),
2216
- onClick: children ? () => onClick?.(!isExpanded) : undefined
2217
- }), /*#__PURE__*/jsx("div", {
2218
- className: classNames('np-card__divider', {
2219
- 'np-card__divider--expanded': isOpen
2220
- })
2221
- }), isOpen && /*#__PURE__*/jsx(Body, {
2222
- as: "span",
2223
- type: Typography.BODY_LARGE,
2224
- className: "np-card__content d-block",
2225
- children: children
2226
- })]
2510
+ "data-testid": testId,
2511
+ ...props,
2512
+ children: [onDismiss && /*#__PURE__*/jsx(CloseButton, {
2513
+ ref: closeButtonReference,
2514
+ className: "np-Card-closeButton",
2515
+ size: isSmall ? 'sm' : 'md',
2516
+ isDisabled: isDisabled,
2517
+ testId: "close-button",
2518
+ onClick: e => {
2519
+ stopPropagation$1(e);
2520
+ onDismiss();
2521
+ }
2522
+ }), children]
2227
2523
  });
2228
2524
  });
2525
+ Card$1.displayName = 'Card';
2229
2526
 
2230
- const CheckboxButton = /*#__PURE__*/forwardRef(({
2231
- checked,
2232
- className,
2233
- disabled,
2234
- onChange,
2235
- ...rest
2236
- }, reference) => /*#__PURE__*/jsxs("span", {
2237
- className: classNames('np-checkbox-button', className, disabled && 'disabled'),
2238
- children: [/*#__PURE__*/jsx("input", {
2239
- ...rest,
2240
- ref: reference,
2241
- type: "checkbox",
2242
- disabled: disabled,
2243
- checked: checked,
2244
- onChange: onChange
2245
- }), /*#__PURE__*/jsx("span", {
2246
- className: "tw-checkbox-button",
2247
- children: /*#__PURE__*/jsx("span", {
2248
- className: "tw-checkbox-check"
2249
- })
2250
- })]
2251
- }));
2252
-
2253
- function Checkbox({
2254
- id,
2255
- checked,
2256
- required,
2257
- disabled,
2258
- readOnly,
2259
- label,
2527
+ function Display({
2528
+ as: Heading = 'h1',
2529
+ type = Typography.DISPLAY_LARGE,
2530
+ children,
2260
2531
  className,
2261
- secondary,
2262
- onChange,
2263
- onFocus,
2264
- onBlur
2532
+ id
2265
2533
  }) {
2266
- const classList = classNames('np-checkbox', {
2267
- checkbox: true,
2268
- 'checkbox-lg': secondary,
2269
- disabled
2270
- }, className);
2271
- const innerDisabled = disabled || readOnly;
2272
- return /*#__PURE__*/jsx("div", {
2534
+ return /*#__PURE__*/jsx(Heading, {
2273
2535
  id: id,
2274
- className: classList,
2275
- children: /*#__PURE__*/jsxs("label", {
2276
- className: classNames({
2277
- disabled
2278
- }),
2279
- children: [/*#__PURE__*/jsx(CheckboxButton, {
2280
- className: "p-r-2",
2281
- checked: checked,
2282
- disabled: innerDisabled,
2283
- required: !innerDisabled && required,
2284
- onFocus: onFocus,
2285
- onChange: () => onChange(!checked),
2286
- onBlur: onBlur
2287
- }), /*#__PURE__*/jsxs(Body, {
2288
- as: "span",
2289
- className: "np-checkbox__text",
2290
- type: secondary ? Typography.BODY_LARGE_BOLD : Typography.BODY_LARGE,
2291
- children: [/*#__PURE__*/jsx("span", {
2292
- className: required ? 'has-required' : undefined,
2293
- children: label
2294
- }), secondary && /*#__PURE__*/jsx(Body, {
2295
- as: "span",
2296
- children: secondary
2297
- })]
2298
- })]
2299
- })
2536
+ className: classNames(`np-text-${type}`, 'text-primary', className),
2537
+ children: children
2300
2538
  });
2301
2539
  }
2302
2540
 
2303
- const CheckboxOption = /*#__PURE__*/forwardRef(({
2304
- checked,
2305
- disabled,
2306
- onChange,
2307
- ...rest
2308
- }, reference) => {
2309
- return /*#__PURE__*/jsx(Option$2, {
2310
- ...rest,
2311
- ref: reference,
2312
- disabled: disabled,
2313
- button: /*#__PURE__*/jsx(CheckboxButton, {
2314
- checked: checked,
2315
- disabled: disabled,
2316
- onChange: () => onChange?.(!checked)
2317
- })
2541
+ const EmptyTransparentImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
2542
+ const Image = ({
2543
+ id,
2544
+ src,
2545
+ alt,
2546
+ onLoad,
2547
+ onError,
2548
+ className,
2549
+ loading,
2550
+ stretch = true,
2551
+ role,
2552
+ shrink = true
2553
+ }) => {
2554
+ const elementReference = useRef(null);
2555
+ const [hasIntersected] = useHasIntersected({
2556
+ elRef: elementReference,
2557
+ loading
2558
+ });
2559
+ let imageSource = src;
2560
+ let imageOnLoad = onLoad;
2561
+ if (loading === 'lazy' && !hasIntersected) {
2562
+ imageSource = EmptyTransparentImage;
2563
+ imageOnLoad = undefined;
2564
+ }
2565
+ return /*#__PURE__*/jsx("img", {
2566
+ ref: elementReference,
2567
+ id: id,
2568
+ alt: alt,
2569
+ src: imageSource,
2570
+ className: classNames(['tw-image', {
2571
+ 'tw-image__stretch': stretch,
2572
+ 'tw-image__shrink': shrink
2573
+ }, className]),
2574
+ role: role,
2575
+ onLoad: imageOnLoad,
2576
+ onError: onError
2318
2577
  });
2319
- });
2578
+ };
2320
2579
 
2321
- const Chip = ({
2580
+ const defaultPromoCardContext = {
2581
+ state: '',
2582
+ isDisabled: false,
2583
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2584
+ onChange: () => {}
2585
+ };
2586
+ /**
2587
+ * The PromoCard context object.
2588
+ */
2589
+ const PromoCardContext = /*#__PURE__*/createContext(defaultPromoCardContext);
2590
+ /**
2591
+ * A custom hook for accessing the PromoCard context object.
2592
+ *
2593
+ * The `usePromoCardContext` hook is used to access the PromoCard context object
2594
+ * from within a child PromoCard component. It throws an error if the context
2595
+ * object is not available, which can help with debugging and development.
2596
+ *
2597
+ * @returns {PromoCardContextType} - The PromoCard context object.
2598
+ */
2599
+ const usePromoCardContext = () => {
2600
+ return useContext(PromoCardContext);
2601
+ };
2602
+
2603
+ const PromoCardIndicator = ({
2604
+ className,
2605
+ children,
2322
2606
  label,
2323
- value,
2324
- onRemove,
2325
- onClick,
2326
- onKeyPress,
2327
- className = undefined,
2328
- 'aria-label': ariaLabel,
2329
- 'aria-checked': ariaChecked,
2330
- role,
2331
- closeButton
2607
+ icon,
2608
+ isSmall = false,
2609
+ testid,
2610
+ ...rest
2332
2611
  }) => {
2333
- const isActionable = onClick || onKeyPress;
2334
- const defaultRole = isActionable ? 'button' : undefined;
2335
- const tabIndex = isActionable ? 0 : -1;
2336
- const {
2337
- isModern
2338
- } = useTheme();
2339
- const closeButtonReference = useRef(null);
2340
- const previousCloseButtonShown = useRef(undefined);
2341
- useEffect(() => {
2342
- if (closeButtonReference.current != null && previousCloseButtonShown.current === false) {
2343
- closeButtonReference.current?.focus();
2344
- }
2345
- previousCloseButtonShown.current = closeButtonReference.current != null;
2346
- }, [onRemove]);
2612
+ const isIconString = icon && typeof icon === 'string';
2613
+ const IconComponent = isIconString && {
2614
+ check: Check,
2615
+ arrow: ArrowRight,
2616
+ download: Download
2617
+ }[icon];
2347
2618
  return /*#__PURE__*/jsxs("div", {
2348
- role: role ?? defaultRole,
2349
- tabIndex: tabIndex,
2350
- "aria-label": ariaLabel,
2351
- "aria-checked": ariaChecked,
2352
- className: classNames('np-chip', 'd-flex', 'align-items-center', 'justify-content-between', onRemove ? 'np-chip--removable' : '', className),
2353
- ...(isActionable && {
2354
- onClick,
2355
- onKeyPress
2356
- }),
2357
- children: [isModern ? /*#__PURE__*/jsx(Body, {
2358
- "aria-hidden": !!onRemove,
2359
- type: Typography.BODY_DEFAULT_BOLD,
2360
- children: label
2361
- }) : /*#__PURE__*/jsx("span", {
2362
- "aria-hidden": "false",
2363
- className: "np-chip-label",
2619
+ className: classNames('np-Card-indicator', className),
2620
+ "data-testid": testid,
2621
+ ...rest,
2622
+ children: [label && /*#__PURE__*/jsx(Body, {
2623
+ as: "span",
2624
+ type: Typography.BODY_LARGE_BOLD,
2625
+ className: "np-Card-indicatorText",
2364
2626
  children: label
2365
- }), onRemove ? /*#__PURE__*/jsx(CloseButton, {
2366
- ref: closeButtonReference,
2367
- className: isModern ? `btn-unstyled` : `btn-unstyled m-l-1`,
2368
- "aria-label": closeButton?.['aria-label'],
2369
- size: "sm",
2370
- filled: isModern,
2371
- onClick: onRemove
2372
- }) : null]
2373
- }, value);
2627
+ }), icon && /*#__PURE__*/jsx(Avatar, {
2628
+ type: AvatarType.ICON,
2629
+ size: isSmall ? 40 : 56,
2630
+ backgroundColor: "var(--Card-indicator-icon-background-color)",
2631
+ className: "np-Card-indicatorIcon",
2632
+ children: IconComponent ? /*#__PURE__*/jsx(IconComponent, {
2633
+ size: 24,
2634
+ "aria-hidden": "true"
2635
+ }) : icon
2636
+ }), children]
2637
+ });
2374
2638
  };
2375
2639
 
2376
- var messages$a = defineMessages({
2377
- ariaLabel: {
2378
- id: "neptune.Chips.ariaLabel"
2379
- }
2380
- });
2381
-
2382
- const Chips = ({
2383
- chips,
2384
- onChange,
2385
- selected,
2386
- 'aria-label': ariaLabel,
2640
+ const PromoCard = /*#__PURE__*/forwardRef(({
2387
2641
  className,
2388
- multiple
2389
- }) => {
2390
- const intl = useIntl();
2391
- const isSelected = value => Array.isArray(selected) ? selected.includes(value) : selected === value;
2392
- const handleOnChange = (selectedValue, isCurrentlyEnabled) => {
2393
- onChange({
2394
- isEnabled: !isCurrentlyEnabled,
2395
- selectedValue
2642
+ description,
2643
+ defaultChecked,
2644
+ download,
2645
+ href,
2646
+ hrefLang,
2647
+ id,
2648
+ headingLevel = 'h3',
2649
+ imageAlt,
2650
+ imageClass,
2651
+ imageSource,
2652
+ indicatorLabel,
2653
+ indicatorIcon,
2654
+ isChecked,
2655
+ isDisabled,
2656
+ onClick,
2657
+ onKeyDown,
2658
+ rel,
2659
+ tabIndex,
2660
+ target,
2661
+ testId,
2662
+ title,
2663
+ type,
2664
+ value,
2665
+ isSmall,
2666
+ useDisplayFont = true,
2667
+ anchorRef,
2668
+ anchorId,
2669
+ ...props
2670
+ }, ref) => {
2671
+ // Set the `checked` state to the value of `defaultChecked` if it is truthy,
2672
+ // or the value of `isChecked` if it is truthy, or `false` if neither
2673
+ // is truthy.
2674
+ const {
2675
+ state,
2676
+ onChange,
2677
+ isDisabled: contextIsDisabled
2678
+ } = usePromoCardContext();
2679
+ const [checked, setChecked] = useState(type === 'checkbox' ? defaultChecked ?? isChecked ?? false : false);
2680
+ const handleClick = () => {
2681
+ if (type === 'radio') {
2682
+ onChange(value || ''); // Update the context state for radio
2683
+ } else if (type === 'checkbox') {
2684
+ setChecked(!checked); // Update local state for checkbox
2685
+ }
2686
+ };
2687
+ const fallbackId = useId();
2688
+ const componentId = id || fallbackId;
2689
+ // Set the icon to `'arrow'` if `href` is truthy and `type` is falsy, or
2690
+ // `'download'` if `download` is truthy. If neither condition is true, set
2691
+ // `icon` to `undefined`.
2692
+ // Create a function to get icon type
2693
+ const getIconType = () => {
2694
+ if (indicatorIcon) {
2695
+ return indicatorIcon;
2696
+ }
2697
+ if (download) {
2698
+ return 'download';
2699
+ }
2700
+ if (href && !type) {
2701
+ return 'arrow';
2702
+ }
2703
+ return undefined;
2704
+ };
2705
+ // Define all class names string based on the values of the `href`, `type`,
2706
+ // `checked`, and `className` props.
2707
+ const commonClasses = classNames({
2708
+ 'np-Card--promoCard': true,
2709
+ 'np-Card--checked': !href && type,
2710
+ 'np-Card--link': href && !type,
2711
+ 'is-checked': type === 'radio' ? value === state : type === 'checkbox' ? checked : undefined
2712
+ }, className);
2713
+ // Object with common props that will be passed to the `Card` components
2714
+ const commonProps = {
2715
+ className: commonClasses,
2716
+ id: componentId,
2717
+ isDisabled: isDisabled || contextIsDisabled,
2718
+ onClick,
2719
+ onKeyDown,
2720
+ ref,
2721
+ 'data-testid': testId,
2722
+ isSmall
2723
+ };
2724
+ // Object with Anchor props that will be passed to the `a` element. These
2725
+ // won't be refurned if set to `isDisabled`
2726
+ const anchorProps = href && !isDisabled ? {
2727
+ download,
2728
+ href: href || undefined,
2729
+ hrefLang,
2730
+ rel,
2731
+ target,
2732
+ ref: anchorRef,
2733
+ id: anchorId
2734
+ } : {};
2735
+ // Object of all Checked props that will be passed to the root `Card` component
2736
+ const checkedProps = (type === 'checkbox' || type === 'radio') && !href ? {
2737
+ ...commonProps,
2738
+ 'aria-checked': type === 'radio' ? value === state : type === 'checkbox' ? checked : undefined,
2739
+ 'aria-describedby': `${componentId}-title`,
2740
+ 'aria-disabled': isDisabled,
2741
+ 'data-value': value ?? undefined,
2742
+ role: type === 'checkbox' || type === 'radio' ? type : undefined,
2743
+ onClick: handleClick,
2744
+ onKeyDown: event => {
2745
+ if (event.key === 'Enter' || event.key === ' ') {
2746
+ handleClick();
2747
+ }
2748
+ },
2749
+ ref,
2750
+ tabIndex: 0
2751
+ } : {};
2752
+ const getTitle = () => {
2753
+ const titleContent = href && !type ? /*#__PURE__*/jsx("a", {
2754
+ className: "np-Card-titleLink",
2755
+ ...anchorProps,
2756
+ children: title
2757
+ }) : title;
2758
+ const titleProps = {
2759
+ id: `${componentId}-title`,
2760
+ as: headingLevel,
2761
+ className: 'np-Card-title'
2762
+ };
2763
+ return useDisplayFont ? /*#__PURE__*/jsx(Display, {
2764
+ type: Typography.DISPLAY_SMALL,
2765
+ ...titleProps,
2766
+ children: titleContent
2767
+ }) : /*#__PURE__*/jsx(Title, {
2768
+ type: Typography.TITLE_SUBSECTION,
2769
+ ...titleProps,
2770
+ children: titleContent
2396
2771
  });
2397
2772
  };
2398
- return /*#__PURE__*/jsx("div", {
2399
- className: classNames('np-chips d-flex', className),
2400
- "aria-label": ariaLabel,
2401
- role: !multiple ? 'radiogroup' : undefined,
2402
- children: chips.map(chip => {
2403
- const chipSelected = isSelected(chip.value);
2404
- return /*#__PURE__*/jsx(Chip, {
2405
- value: chip.value,
2406
- label: chip.label,
2407
- closeButton: {
2408
- 'aria-label': intl.formatMessage(messages$a.ariaLabel, {
2409
- choice: chip.label
2410
- })
2411
- },
2412
- className: classNames('text-xs-nowrap', {
2413
- 'np-chip--selected': chipSelected,
2414
- 'p-r-1': multiple && chipSelected
2415
- }),
2416
- ...(multiple && chipSelected ? {
2417
- onRemove: () => handleOnChange(chip.value, chipSelected),
2418
- 'aria-label': chip.label
2419
- } : {
2420
- onClick: () => handleOnChange(chip.value, chipSelected),
2421
- onKeyPress: () => handleOnChange(chip.value, chipSelected),
2422
- role: !multiple ? 'radio' : undefined,
2423
- 'aria-checked': chipSelected
2424
- })
2425
- }, chip.value);
2426
- })
2427
- });
2428
- };
2429
-
2430
- const CircularButton = ({
2431
- className,
2432
- children,
2433
- disabled,
2434
- icon,
2435
- priority = Priority.PRIMARY,
2436
- type = ControlType.ACCENT,
2437
- ...rest
2438
- }) => {
2439
- const classes = classNames('btn np-btn', typeClassMap[type], priorityClassMap[priority]);
2440
- const iconElement = Number(icon.props.size) !== 24 ? /*#__PURE__*/cloneElement(icon, {
2441
- size: 24
2442
- }) : icon;
2443
- return /*#__PURE__*/jsxs("label", {
2444
- className: classNames('np-circular-btn', priority, type, disabled && 'disabled', className),
2445
- children: [/*#__PURE__*/jsx("input", {
2446
- type: "button",
2447
- "aria-label": children,
2448
- className: classes,
2449
- disabled: disabled,
2450
- ...rest
2451
- }), iconElement, /*#__PURE__*/jsx(Body, {
2452
- as: "span",
2453
- className: "np-circular-btn__label",
2454
- type: Typography.BODY_DEFAULT_BOLD,
2455
- children: children
2456
- })]
2457
- });
2458
- };
2459
-
2460
- const FocusBoundary = ({
2461
- children
2462
- }) => {
2463
- const wrapperReference = useRef(null);
2464
2773
  useEffect(() => {
2465
- wrapperReference.current?.focus({
2466
- preventScroll: true
2467
- });
2468
- }, []);
2469
- return /*#__PURE__*/jsx(FocusScope, {
2470
- contain: true,
2471
- restoreFocus: true,
2472
- children: /*#__PURE__*/jsx("div", {
2473
- ref: wrapperReference,
2474
- tabIndex: -1,
2475
- children: children
2476
- })
2774
+ setChecked(defaultChecked ?? isChecked ?? false);
2775
+ }, [defaultChecked, isChecked]);
2776
+ return /*#__PURE__*/jsxs(Card$1, {
2777
+ ...commonProps,
2778
+ ...checkedProps,
2779
+ ...props,
2780
+ children: [(value === state || checked) && /*#__PURE__*/jsx("span", {
2781
+ className: "np-Card-check",
2782
+ children: /*#__PURE__*/jsx(Check, {
2783
+ size: 24,
2784
+ "aria-hidden": "true"
2785
+ })
2786
+ }), getTitle(), /*#__PURE__*/jsx(Body, {
2787
+ className: "np-Card-description",
2788
+ children: description
2789
+ }), imageSource && /*#__PURE__*/jsx("div", {
2790
+ className: classNames('np-Card-image', {
2791
+ imageClass
2792
+ }),
2793
+ children: /*#__PURE__*/jsx(Image, {
2794
+ src: imageSource,
2795
+ alt: imageAlt || '',
2796
+ loading: "lazy"
2797
+ })
2798
+ }), /*#__PURE__*/jsx(PromoCardIndicator, {
2799
+ label: indicatorLabel,
2800
+ icon: getIconType(),
2801
+ isSmall: isSmall
2802
+ })]
2477
2803
  });
2478
- };
2479
-
2480
- function withNextPortalWrapper(Component) {
2481
- return function (props) {
2482
- const [mounted, setMounted] = useState(false);
2483
- useEffect(() => {
2484
- setMounted(true);
2485
- }, [setMounted]);
2486
- return mounted ? /*#__PURE__*/createPortal( /*#__PURE__*/jsx(Component, {
2487
- ...props
2488
- }), document.body) : null;
2489
- };
2490
- }
2491
-
2492
- /**
2493
- * Dimmer state management inspired by Material UI's ModalManager (https://github.com/mui-org/material-ui)
2494
- */
2495
- class DimmerManager {
2496
- /**
2497
- * Dimmer refs
2498
- */
2499
- dimmers;
2500
- constructor() {
2501
- this.dimmers = [];
2502
- }
2503
- add(dimmer) {
2504
- let dimmerIndex = this.dimmers.indexOf(dimmer);
2505
- if (dimmerIndex !== -1) {
2506
- return dimmerIndex;
2507
- }
2508
- dimmerIndex = this.dimmers.length;
2509
- this.dimmers.push(dimmer);
2510
- return dimmerIndex;
2511
- }
2512
- remove(dimmer) {
2513
- const dimmerIndex = this.dimmers.indexOf(dimmer);
2514
- if (dimmerIndex !== -1) {
2515
- this.dimmers.splice(dimmerIndex, 1);
2516
- }
2517
- return dimmerIndex;
2518
- }
2519
- isTop(dimmer) {
2520
- return this.dimmers.length > 0 && this.dimmers[this.dimmers.length - 1] === dimmer;
2521
- }
2522
- }
2804
+ });
2805
+ var PromoCard$1 = /*#__PURE__*/React__default.memo(PromoCard);
2523
2806
 
2524
- const EXIT_ANIMATION$1 = 350;
2525
- const dimmerManager = new DimmerManager();
2526
- const handleTouchMove = event => {
2527
- const isTouchedElementDimmer = event.target.classList.contains('dimmer');
2528
- // disable scroll on iOS devices for Dimmer area
2529
- // this is because of bug in WebKit https://bugs.webkit.org/show_bug.cgi?id=220908
2530
- // note: scrolling still works for children(s) as expected
2531
- if (isIosDevice() && isTouchedElementDimmer) {
2532
- event.stopPropagation();
2533
- event.preventDefault();
2534
- }
2535
- };
2536
- const Dimmer = ({
2537
- children,
2807
+ const LEFT_SCROLL_OFFSET = 8;
2808
+ const Carousel = ({
2809
+ header,
2538
2810
  className,
2539
- disableClickToClose = false,
2540
- contentPosition,
2541
- fadeContentOnEnter = false,
2542
- fadeContentOnExit = false,
2543
- open = false,
2544
- scrollable = false,
2545
- transparent = false,
2546
- onClose
2811
+ cards,
2812
+ onClick
2547
2813
  }) => {
2548
- const [hasNotExited, setHasNotExited] = useState(false);
2549
- const dimmerReference = useRef(null);
2550
- const closeOnClick = event => {
2551
- if (event.target === dimmerReference.current) {
2552
- onClose?.(event);
2814
+ const [scrollPosition, setScrollPosition] = useState(0);
2815
+ const [previousScrollPosition, setPreviousScrollPosition] = useState(0);
2816
+ const [scrollIsAtEnd, setScrollIsAtEnd] = useState(false);
2817
+ const [visibleCardOnMobileView, setVisibleCardOnMobileView] = useState('');
2818
+ const carouselElementRef = useRef(null);
2819
+ const carouselCardsRef = useRef([]);
2820
+ const isLeftActionButtonEnabled = scrollPosition > LEFT_SCROLL_OFFSET;
2821
+ const areActionButtonsEnabled = isLeftActionButtonEnabled || !scrollIsAtEnd;
2822
+ const [focusedCard, setFocusedCard] = useState(cards?.[0]?.id);
2823
+ const updateScrollButtonsState = () => {
2824
+ if (carouselElementRef.current) {
2825
+ const {
2826
+ scrollWidth,
2827
+ offsetWidth
2828
+ } = carouselElementRef.current;
2829
+ const scrollAtEnd = scrollWidth - offsetWidth <= scrollPosition + LEFT_SCROLL_OFFSET;
2830
+ setScrollIsAtEnd(scrollAtEnd);
2553
2831
  }
2554
- };
2555
- const handleClick = event => {
2556
- if (disableClickToClose || !onClose) {
2557
- return;
2832
+ const scrollDirecton = scrollPosition > previousScrollPosition ? 'right' : 'left';
2833
+ const cardsInFullViewIds = [];
2834
+ carouselCardsRef.current.forEach(card => {
2835
+ if (isVisible(carouselElementRef.current, card.cardElement)) {
2836
+ // eslint-disable-next-line functional/immutable-data
2837
+ cardsInFullViewIds.push(card.cardElement.getAttribute('id') ?? '');
2838
+ }
2839
+ });
2840
+ if (cardsInFullViewIds.length >= 1) {
2841
+ const visibleCardIndex = scrollDirecton === 'right' ? cardsInFullViewIds.length - 1 : 0;
2842
+ const visibleCardId = cardsInFullViewIds[visibleCardIndex];
2843
+ setVisibleCardOnMobileView(visibleCardId);
2844
+ setFocusedCard(visibleCardId);
2558
2845
  }
2559
- closeOnClick(event);
2846
+ setPreviousScrollPosition(scrollPosition);
2560
2847
  };
2561
- const handleKeyDown = useCallback(event => {
2562
- if (event.key !== 'Escape') {
2563
- return;
2848
+ const scrollCarousel = (direction = 'right') => {
2849
+ if (carouselElementRef.current) {
2850
+ const {
2851
+ scrollWidth
2852
+ } = carouselElementRef.current;
2853
+ const cardWidth = scrollWidth / carouselCardsRef.current.length;
2854
+ const res = Math.floor(cardWidth - cardWidth * 0.05);
2855
+ carouselElementRef.current.scrollBy({
2856
+ left: direction === 'right' ? res : -res,
2857
+ behavior: 'smooth'
2858
+ });
2564
2859
  }
2565
- event.stopPropagation();
2566
- if (onClose && dimmerReference.current && dimmerManager.isTop(dimmerReference.current)) {
2567
- onClose(event);
2860
+ };
2861
+ const handleOnKeyDown = (event, index) => {
2862
+ if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
2863
+ const nextIndex = event.key === 'ArrowRight' ? index + 1 : index - 1;
2864
+ const nextCard = cards[nextIndex];
2865
+ if (nextCard) {
2866
+ const ref = carouselCardsRef.current[nextIndex];
2867
+ if (ref.type === 'promo') {
2868
+ ref.anchorElement?.focus();
2869
+ } else {
2870
+ ref.cardElement?.focus();
2871
+ }
2872
+ scrollCardIntoView(carouselCardsRef.current[nextIndex].cardElement, nextCard);
2873
+ event.preventDefault();
2874
+ }
2568
2875
  }
2569
- }, [onClose]);
2570
- const onEnter = () => {
2571
- setHasNotExited(true);
2572
- if (dimmerReference.current) {
2573
- dimmerManager.add(dimmerReference.current);
2876
+ if (event.key === 'Enter' || event.key === ' ') {
2877
+ event.currentTarget.click();
2574
2878
  }
2575
2879
  };
2576
- const onExited = () => {
2577
- setHasNotExited(false);
2578
- if (dimmerReference.current) {
2579
- dimmerManager.remove(dimmerReference.current);
2580
- }
2880
+ const scrollCardIntoView = (element, card) => {
2881
+ element.scrollIntoView({
2882
+ behavior: 'smooth',
2883
+ block: 'nearest',
2884
+ inline: 'center'
2885
+ });
2886
+ setFocusedCard(card.id);
2581
2887
  };
2582
2888
  useEffect(() => {
2583
- const localReferenceCopy = dimmerReference.current;
2584
- if (open) {
2585
- document.addEventListener('keydown', handleKeyDown);
2586
- localReferenceCopy?.addEventListener('touchmove', handleTouchMove, {
2587
- passive: true
2588
- });
2589
- }
2889
+ updateScrollButtonsState();
2890
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2891
+ }, [scrollPosition]);
2892
+ useEffect(() => {
2893
+ window.addEventListener('resize', updateScrollButtonsState);
2590
2894
  return () => {
2591
- document.removeEventListener('keydown', handleKeyDown);
2592
- localReferenceCopy?.removeEventListener('touchmove', handleTouchMove);
2895
+ window.removeEventListener('resize', updateScrollButtonsState);
2593
2896
  };
2594
- }, [handleKeyDown, open]);
2595
- return /*#__PURE__*/jsx(DimmerWrapper, {
2596
- open: open,
2597
- hasNotExited: hasNotExited,
2598
- children: /*#__PURE__*/jsx(CSSTransition, {
2599
- nodeRef: dimmerReference,
2600
- in: open,
2601
- appear: true
2602
- // Wait for animation to finish before unmount.
2603
- ,
2604
- timeout: {
2605
- enter: 0,
2606
- exit: EXIT_ANIMATION$1
2607
- },
2608
- classNames: {
2609
- enter: classNames({
2610
- 'dimmer--enter-fade': fadeContentOnEnter
2611
- }),
2612
- enterDone: classNames('dimmer--enter-done', {
2613
- 'dimmer--enter-fade': fadeContentOnEnter
2614
- }),
2615
- exit: classNames('dimmer--exit', {
2616
- 'dimmer--exit-fade': fadeContentOnExit
2617
- })
2897
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2898
+ }, []);
2899
+ const addElementToCardsRefArray = (index, ref) => {
2900
+ if (ref) {
2901
+ // eslint-disable-next-line functional/immutable-data
2902
+ carouselCardsRef.current[index] = {
2903
+ type: ref.type ?? carouselCardsRef.current?.[index]?.type,
2904
+ cardElement: ref.cardElement ?? carouselCardsRef.current?.[index]?.cardElement,
2905
+ anchorElement: ref.anchorElement ?? carouselCardsRef.current?.[index]?.anchorElement
2906
+ };
2907
+ }
2908
+ };
2909
+ return /*#__PURE__*/jsxs("div", {
2910
+ className: classNames('carousel-wrapper', className),
2911
+ children: [/*#__PURE__*/jsxs("div", {
2912
+ className: "d-flex justify-content-between carousel__header",
2913
+ children: [typeof header === 'string' ? /*#__PURE__*/jsx(Title, {
2914
+ as: "span",
2915
+ type: "title-body",
2916
+ children: header
2917
+ }) : header, areActionButtonsEnabled ? /*#__PURE__*/jsxs("div", {
2918
+ className: "hidden-xs",
2919
+ children: [/*#__PURE__*/jsx(ActionButton, {
2920
+ className: "carousel__scroll-button",
2921
+ tabIndex: -1,
2922
+ priority: "secondary",
2923
+ disabled: !isLeftActionButtonEnabled,
2924
+ "aria-hidden": "true",
2925
+ "data-testid": "scroll-carousel-left",
2926
+ onClick: () => scrollCarousel('left'),
2927
+ children: /*#__PURE__*/jsx(ChevronLeft, {})
2928
+ }), /*#__PURE__*/jsx(ActionButton, {
2929
+ tabIndex: -1,
2930
+ className: "carousel__scroll-button m-l-1",
2931
+ priority: "secondary",
2932
+ "aria-hidden": "true",
2933
+ "data-testid": "scroll-carousel-right",
2934
+ disabled: scrollIsAtEnd,
2935
+ onClick: () => scrollCarousel(),
2936
+ children: /*#__PURE__*/jsx(ChevronRight, {})
2937
+ })]
2938
+ }) : null]
2939
+ }), /*#__PURE__*/jsx("div", {
2940
+ ref: carouselElementRef,
2941
+ tabIndex: -1,
2942
+ role: "list",
2943
+ className: "carousel",
2944
+ onScroll: event => {
2945
+ const target = event.target;
2946
+ setScrollPosition(target.scrollLeft);
2618
2947
  },
2619
- unmountOnExit: true,
2620
- onEnter: onEnter,
2621
- onExited: onExited,
2622
- children: /*#__PURE__*/jsx(DimmerContentWrapper, {
2623
- scrollBody: !transparent,
2624
- children: /*#__PURE__*/jsx(FocusBoundary, {
2625
- children: /*#__PURE__*/jsx("div", {
2626
- ref: dimmerReference,
2627
- className: classNames('dimmer', {
2628
- 'dimmer--scrollable': scrollable,
2629
- 'dimmer--transparent': transparent
2630
- }, className),
2631
- role: "presentation",
2632
- onClick: handleClick,
2948
+ children: cards?.map((card, index) => {
2949
+ const sharedProps = {
2950
+ id: card.id,
2951
+ className: classNames('carousel__card', {
2952
+ 'carousel__card--focused': card.id === focusedCard
2953
+ }),
2954
+ onClick: () => {
2955
+ card.onClick?.();
2956
+ onClick?.(card.id);
2957
+ },
2958
+ onFocus: event => {
2959
+ scrollCardIntoView(event.currentTarget, card);
2960
+ }
2961
+ };
2962
+ const cardContent = card.type !== 'promo' ? /*#__PURE__*/jsx("div", {
2963
+ id: `${card.id}-content`,
2964
+ className: classNames('carousel__card-content', {
2965
+ [card.className ?? '']: !!card.className
2966
+ })
2967
+ // eslint-disable-next-line react/forbid-dom-props
2968
+ ,
2969
+ style: card.styles,
2970
+ children: card.content
2971
+ }) : null;
2972
+ if (card.type === 'button') {
2973
+ return /*#__PURE__*/jsx("div", {
2974
+ "aria-labelledby": `${card.id}-content`,
2975
+ role: "listitem",
2633
2976
  children: /*#__PURE__*/jsx("div", {
2634
- className: classNames('dimmer-content-positioner', contentPosition != null && ['d-flex justify-content-center', {
2635
- 'align-items-start': contentPosition === 'top',
2636
- 'align-items-center': contentPosition === 'center',
2637
- 'align-items-end': contentPosition === 'bottom'
2638
- }]),
2639
- children: children
2977
+ ...sharedProps,
2978
+ ref: el => {
2979
+ if (el) {
2980
+ // eslint-disable-next-line functional/immutable-data
2981
+ carouselCardsRef.current[index] = {
2982
+ type: 'default',
2983
+ cardElement: el
2984
+ };
2985
+ }
2986
+ },
2987
+ role: "button",
2988
+ tabIndex: 0,
2989
+ onKeyDown: event => handleOnKeyDown(event, index),
2990
+ children: cardContent
2991
+ })
2992
+ }, card.id);
2993
+ }
2994
+ if (card.type === 'promo') {
2995
+ return /*#__PURE__*/jsx("div", {
2996
+ id: card.id,
2997
+ role: "listitem",
2998
+ "aria-labelledby": `${card.id}-anchor`,
2999
+ children: /*#__PURE__*/jsx(PromoCard$1, {
3000
+ ...card,
3001
+ type: undefined,
3002
+ ...sharedProps,
3003
+ ref: el => {
3004
+ if (el) {
3005
+ addElementToCardsRefArray(index, {
3006
+ type: 'promo',
3007
+ cardElement: el
3008
+ });
3009
+ }
3010
+ },
3011
+ anchorRef: el => {
3012
+ if (el) {
3013
+ addElementToCardsRefArray(index, {
3014
+ type: 'promo',
3015
+ anchorElement: el
3016
+ });
3017
+ }
3018
+ },
3019
+ anchorId: `${card.id}-anchor`,
3020
+ onKeyDown: event => handleOnKeyDown(event, index)
2640
3021
  })
3022
+ }, card.id);
3023
+ }
3024
+ return /*#__PURE__*/jsx("div", {
3025
+ "aria-labelledby": `${card.id}-content`,
3026
+ role: "listitem",
3027
+ children: /*#__PURE__*/jsx("a", {
3028
+ ...sharedProps,
3029
+ ref: el => {
3030
+ if (el) {
3031
+ // eslint-disable-next-line functional/immutable-data
3032
+ carouselCardsRef.current[index] = {
3033
+ type: 'default',
3034
+ cardElement: el
3035
+ };
3036
+ }
3037
+ },
3038
+ href: card.href,
3039
+ rel: "noreferrer",
3040
+ onKeyDown: event => handleOnKeyDown(event, index),
3041
+ children: cardContent
2641
3042
  })
2642
- })
3043
+ }, card.id);
2643
3044
  })
2644
- })
2645
- });
2646
- };
2647
- const DimmerWrapper = ({
2648
- open,
2649
- hasNotExited,
2650
- children
2651
- }) => {
2652
- const {
2653
- screenMode,
2654
- theme
2655
- } = useTheme();
2656
- return open || hasNotExited ? /*#__PURE__*/jsx(ThemeProvider, {
2657
- theme: "personal",
2658
- screenMode: theme === 'personal' ? screenMode : 'light',
2659
- isNotRootProvider: true,
2660
- children: children
2661
- }) : /*#__PURE__*/jsx(Fragment, {
2662
- children: children
3045
+ }), /*#__PURE__*/jsx("div", {
3046
+ className: "visible-xs",
3047
+ children: /*#__PURE__*/jsx("div", {
3048
+ className: "carousel__indicators",
3049
+ children: cards?.map((card, index) => /*#__PURE__*/jsx("button", {
3050
+ "data-testid": `${card.id}-indicator`,
3051
+ tabIndex: -1,
3052
+ "aria-hidden": true,
3053
+ type: "button",
3054
+ className: classNames('carousel__indicator', {
3055
+ 'carousel__indicator--selected': card.id === visibleCardOnMobileView
3056
+ }),
3057
+ onClick: () => {
3058
+ scrollCardIntoView(carouselCardsRef.current[index].cardElement, card);
3059
+ }
3060
+ }, `${card.id}-indicator`))
3061
+ })
3062
+ })]
2663
3063
  });
2664
3064
  };
2665
- const DimmerContentWrapper = ({
2666
- children,
2667
- scrollBody
2668
- }) => {
2669
- useEffect(() => {
2670
- if (scrollBody) {
2671
- addNoScrollClass();
2672
- }
2673
- return () => {
2674
- if (scrollBody) {
2675
- removeNoScrollClass();
2676
- }
2677
- };
2678
- }, [scrollBody]);
2679
- return children;
3065
+ const isVisible = (container, el) => {
3066
+ const cWidth = container.offsetWidth;
3067
+ const cScrollOffset = container.scrollLeft;
3068
+ const elemLeft = el.offsetLeft - container.offsetLeft;
3069
+ const elemRight = elemLeft + el.offsetWidth;
3070
+ return elemLeft >= cScrollOffset && elemRight <= cScrollOffset + cWidth;
2680
3071
  };
2681
- var Dimmer$1 = withNextPortalWrapper(Dimmer);
2682
3072
 
2683
- const EXIT_ANIMATION = 350;
2684
- const SlidingPanel = /*#__PURE__*/forwardRef(({
2685
- position = 'left',
2686
- open,
2687
- showSlidingPanelBorder,
2688
- slidingPanelPositionFixed,
2689
- className,
3073
+ const Card = /*#__PURE__*/forwardRef(function Card({
3074
+ 'aria-label': ariaLabel,
3075
+ as: Element = 'div',
3076
+ isExpanded,
3077
+ title,
3078
+ details,
2690
3079
  children,
2691
- testId,
3080
+ onClick,
3081
+ icon,
3082
+ id,
3083
+ className,
3084
+ 'data-testid': dataTestId
3085
+ }, reference) {
3086
+ const isOpen = !!(isExpanded && children);
3087
+ return /*#__PURE__*/jsxs(Element, {
3088
+ ref: reference,
3089
+ className: classNames('np-card', className, {
3090
+ 'np-card--expanded': isOpen,
3091
+ 'np-card--inactive': !children,
3092
+ 'np-card--has-icon': !!icon
3093
+ }),
3094
+ id: id,
3095
+ "data-testid": dataTestId,
3096
+ children: [/*#__PURE__*/jsx(Option$2, {
3097
+ "aria-label": ariaLabel,
3098
+ as: children ? 'button' : 'div',
3099
+ className: classNames('np-card__button'),
3100
+ media: icon,
3101
+ title: title,
3102
+ content: details,
3103
+ showMediaAtAllSizes: true,
3104
+ button: children && /*#__PURE__*/jsx(Chevron, {
3105
+ orientation: isOpen ? Position.TOP : Position.BOTTOM
3106
+ }),
3107
+ onClick: children ? () => onClick?.(!isExpanded) : undefined
3108
+ }), /*#__PURE__*/jsx("div", {
3109
+ className: classNames('np-card__divider', {
3110
+ 'np-card__divider--expanded': isOpen
3111
+ })
3112
+ }), isOpen && /*#__PURE__*/jsx(Body, {
3113
+ as: "span",
3114
+ type: Typography.BODY_LARGE,
3115
+ className: "np-card__content d-block",
3116
+ children: children
3117
+ })]
3118
+ });
3119
+ });
3120
+
3121
+ const CheckboxButton = /*#__PURE__*/forwardRef(({
3122
+ checked,
3123
+ className,
3124
+ disabled,
3125
+ onChange,
2692
3126
  ...rest
2693
- }, reference) => {
2694
- const localReference = useRef(null);
2695
- useImperativeHandle(reference, () => localReference.current, []);
2696
- return /*#__PURE__*/createElement(CSSTransition, {
3127
+ }, reference) => /*#__PURE__*/jsxs("span", {
3128
+ className: classNames('np-checkbox-button', className, disabled && 'disabled'),
3129
+ children: [/*#__PURE__*/jsx("input", {
2697
3130
  ...rest,
2698
- key: `sliding-panel--open-${position}`,
2699
- nodeRef: localReference,
2700
- in: open
2701
- // Wait for animation to finish before unmount.
2702
- ,
2703
- timeout: {
2704
- enter: 0,
2705
- exit: EXIT_ANIMATION
2706
- },
2707
- classNames: "sliding-panel",
2708
- appear: true,
2709
- unmountOnExit: true
2710
- }, /*#__PURE__*/jsx("div", {
2711
- ref: localReference,
2712
- "data-testid": testId,
2713
- className: classNames('sliding-panel', `sliding-panel--open-${position}`, showSlidingPanelBorder && `sliding-panel--border-${position}`, slidingPanelPositionFixed && 'sliding-panel--fixed', className),
2714
- children: children
2715
- }));
2716
- });
3131
+ ref: reference,
3132
+ type: "checkbox",
3133
+ disabled: disabled,
3134
+ checked: checked,
3135
+ onChange: onChange
3136
+ }), /*#__PURE__*/jsx("span", {
3137
+ className: "tw-checkbox-button",
3138
+ children: /*#__PURE__*/jsx("span", {
3139
+ className: "tw-checkbox-check"
3140
+ })
3141
+ })]
3142
+ }));
2717
3143
 
2718
- function Drawer({
2719
- children,
3144
+ function Checkbox({
3145
+ id,
3146
+ checked,
3147
+ required,
3148
+ disabled,
3149
+ readOnly,
3150
+ label,
2720
3151
  className,
2721
- footerContent,
2722
- headerTitle,
2723
- onClose,
2724
- open = false,
2725
- position = 'right'
3152
+ secondary,
3153
+ onChange,
3154
+ onFocus,
3155
+ onBlur
2726
3156
  }) {
2727
- logActionRequiredIf('Drawer now expects `onClose`, and will soon make this prop required. Please update your usage to provide it.', !onClose);
2728
- const {
2729
- isMobile
2730
- } = useLayout();
2731
- const titleId = useId();
2732
- return /*#__PURE__*/jsx(Dimmer$1, {
2733
- open: open,
2734
- onClose: onClose,
2735
- children: /*#__PURE__*/jsx(SlidingPanel, {
2736
- open: open,
2737
- position: isMobile ? Position.BOTTOM : position,
2738
- children: /*#__PURE__*/jsxs("div", {
2739
- role: "dialog",
2740
- "aria-modal": true,
2741
- "aria-labelledby": headerTitle ? titleId : undefined,
2742
- className: classNames('np-drawer', className),
2743
- children: [/*#__PURE__*/jsxs("div", {
2744
- className: classNames('np-drawer-header', {
2745
- 'np-drawer-header--withborder': headerTitle
2746
- }),
2747
- children: [headerTitle && /*#__PURE__*/jsx(Title, {
2748
- id: titleId,
2749
- type: Typography.TITLE_BODY,
2750
- children: headerTitle
2751
- }), /*#__PURE__*/jsx(CloseButton, {
2752
- onClick: onClose
2753
- })]
2754
- }), children && /*#__PURE__*/jsx("div", {
2755
- className: classNames('np-drawer-content'),
2756
- children: children
2757
- }), footerContent && /*#__PURE__*/jsx("div", {
2758
- className: classNames('np-drawer-footer'),
2759
- children: footerContent
3157
+ const classList = classNames('np-checkbox', {
3158
+ checkbox: true,
3159
+ 'checkbox-lg': secondary,
3160
+ disabled
3161
+ }, className);
3162
+ const innerDisabled = disabled || readOnly;
3163
+ return /*#__PURE__*/jsx("div", {
3164
+ id: id,
3165
+ className: classList,
3166
+ children: /*#__PURE__*/jsxs("label", {
3167
+ className: classNames({
3168
+ disabled
3169
+ }),
3170
+ children: [/*#__PURE__*/jsx(CheckboxButton, {
3171
+ className: "p-r-2",
3172
+ checked: checked,
3173
+ disabled: innerDisabled,
3174
+ required: !innerDisabled && required,
3175
+ onFocus: onFocus,
3176
+ onChange: () => onChange(!checked),
3177
+ onBlur: onBlur
3178
+ }), /*#__PURE__*/jsxs(Body, {
3179
+ as: "span",
3180
+ className: "np-checkbox__text",
3181
+ type: secondary ? Typography.BODY_LARGE_BOLD : Typography.BODY_LARGE,
3182
+ children: [/*#__PURE__*/jsx("span", {
3183
+ className: required ? 'has-required' : undefined,
3184
+ children: label
3185
+ }), secondary && /*#__PURE__*/jsx(Body, {
3186
+ as: "span",
3187
+ children: secondary
2760
3188
  })]
2761
- })
3189
+ })]
2762
3190
  })
2763
3191
  });
2764
3192
  }
2765
-
2766
- const INITIAL_Y_POSITION = 0;
2767
- const CONTENT_SCROLL_THRESHOLD = 1;
2768
- const MOVE_OFFSET_THRESHOLD = 50;
2769
- /**
2770
- * Neptune: https://transferwise.github.io/neptune/components/bottom-sheet/
2771
- *
2772
- * Neptune Web: https://transferwise.github.io/neptune-web/components/overlays/BottomSheet
2773
- *
2774
- */
2775
- const BottomSheet$1 = props => {
2776
- const bottomSheetReference = useRef(null);
2777
- const topBarReference = useRef(null);
2778
- const contentReference = useRef(null);
2779
- const [pressed, setPressed] = useState(false);
2780
- /**
2781
- * Used to track `requestAnimationFrame` requests
2782
- *
2783
- * @see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame#return_value
2784
- */
2785
- const animationId = useRef(0);
2786
- /**
2787
- * Difference between initial coordinate ({@link initialYCoordinate})
2788
- * and new position (when user moves component), it's get calculated on `onTouchMove` and `onMouseMove` events
2789
- *
2790
- * @see {@link calculateOffsetAfterMove}
2791
- */
2792
- const moveOffset = useRef(0);
2793
- const initialYCoordinate = useRef(0);
2794
- // apply shadow to the bottom of top-bar when scroll over content
2795
- useConditionalListener({
2796
- attachListener: props.open && !isServerSide(),
2797
- callback: () => {
2798
- if (topBarReference.current !== null) {
2799
- const {
2800
- classList
2801
- } = topBarReference.current;
2802
- if (!isContentScrollPositionAtTop()) {
2803
- classList.add('np-bottom-sheet--top-bar--shadow');
2804
- } else {
2805
- classList.remove('np-bottom-sheet--top-bar--shadow');
2806
- }
2807
- }
2808
- },
2809
- eventType: 'scroll',
2810
- parent: isServerSide() ? undefined : document
3193
+
3194
+ const CheckboxOption = /*#__PURE__*/forwardRef(({
3195
+ checked,
3196
+ disabled,
3197
+ onChange,
3198
+ ...rest
3199
+ }, reference) => {
3200
+ return /*#__PURE__*/jsx(Option$2, {
3201
+ ...rest,
3202
+ ref: reference,
3203
+ disabled: disabled,
3204
+ button: /*#__PURE__*/jsx(CheckboxButton, {
3205
+ checked: checked,
3206
+ disabled: disabled,
3207
+ onChange: () => onChange?.(!checked)
3208
+ })
2811
3209
  });
2812
- function move(newHeight) {
2813
- if (bottomSheetReference.current !== null) {
2814
- bottomSheetReference.current.style.transform = `translateY(${newHeight}px)`;
2815
- }
2816
- }
2817
- function close(event) {
2818
- setPressed(false);
2819
- moveOffset.current = INITIAL_Y_POSITION;
2820
- if (bottomSheetReference.current !== null) {
2821
- bottomSheetReference.current.style.removeProperty('transform');
2822
- }
2823
- if (props.onClose) {
2824
- props.onClose(event);
3210
+ });
3211
+
3212
+ const Chip = ({
3213
+ label,
3214
+ value,
3215
+ onRemove,
3216
+ onClick,
3217
+ onKeyPress,
3218
+ className = undefined,
3219
+ 'aria-label': ariaLabel,
3220
+ 'aria-checked': ariaChecked,
3221
+ role,
3222
+ closeButton
3223
+ }) => {
3224
+ const isActionable = onClick || onKeyPress;
3225
+ const defaultRole = isActionable ? 'button' : undefined;
3226
+ const tabIndex = isActionable ? 0 : -1;
3227
+ const {
3228
+ isModern
3229
+ } = useTheme();
3230
+ const closeButtonReference = useRef(null);
3231
+ const previousCloseButtonShown = useRef(undefined);
3232
+ useEffect(() => {
3233
+ if (closeButtonReference.current != null && previousCloseButtonShown.current === false) {
3234
+ closeButtonReference.current?.focus();
2825
3235
  }
3236
+ previousCloseButtonShown.current = closeButtonReference.current != null;
3237
+ }, [onRemove]);
3238
+ return /*#__PURE__*/jsxs("div", {
3239
+ role: role ?? defaultRole,
3240
+ tabIndex: tabIndex,
3241
+ "aria-label": ariaLabel,
3242
+ "aria-checked": ariaChecked,
3243
+ className: classNames('np-chip', 'd-flex', 'align-items-center', 'justify-content-between', onRemove ? 'np-chip--removable' : '', className),
3244
+ ...(isActionable && {
3245
+ onClick,
3246
+ onKeyPress
3247
+ }),
3248
+ children: [isModern ? /*#__PURE__*/jsx(Body, {
3249
+ "aria-hidden": !!onRemove,
3250
+ type: Typography.BODY_DEFAULT_BOLD,
3251
+ children: label
3252
+ }) : /*#__PURE__*/jsx("span", {
3253
+ "aria-hidden": "false",
3254
+ className: "np-chip-label",
3255
+ children: label
3256
+ }), onRemove ? /*#__PURE__*/jsx(CloseButton, {
3257
+ ref: closeButtonReference,
3258
+ className: isModern ? `btn-unstyled` : `btn-unstyled m-l-1`,
3259
+ "aria-label": closeButton?.['aria-label'],
3260
+ size: "sm",
3261
+ filled: isModern,
3262
+ onClick: onRemove
3263
+ }) : null]
3264
+ }, value);
3265
+ };
3266
+
3267
+ var messages$a = defineMessages({
3268
+ ariaLabel: {
3269
+ id: "neptune.Chips.ariaLabel"
2826
3270
  }
2827
- const onSwipeStart = event => {
2828
- initialYCoordinate.current = ('touches' in event ? event.touches[0] : event).clientY;
2829
- setPressed(true);
2830
- };
2831
- const onSwipeMove = event => {
2832
- if (pressed) {
2833
- const {
2834
- clientY
2835
- } = 'touches' in event ? event.touches[0] : event;
2836
- const offset = calculateOffsetAfterMove(clientY);
2837
- // check whether move is to the bottom only and content scroll position is at the top
2838
- if (offset > INITIAL_Y_POSITION && isContentScrollPositionAtTop()) {
2839
- moveOffset.current = offset;
2840
- animationId.current = requestAnimationFrame(() => {
2841
- if (animationId.current !== undefined && bottomSheetReference.current !== null) {
2842
- move(offset);
2843
- }
2844
- });
2845
- }
2846
- }
3271
+ });
3272
+
3273
+ const Chips = ({
3274
+ chips,
3275
+ onChange,
3276
+ selected,
3277
+ 'aria-label': ariaLabel,
3278
+ className,
3279
+ multiple
3280
+ }) => {
3281
+ const intl = useIntl();
3282
+ const isSelected = value => Array.isArray(selected) ? selected.includes(value) : selected === value;
3283
+ const handleOnChange = (selectedValue, isCurrentlyEnabled) => {
3284
+ onChange({
3285
+ isEnabled: !isCurrentlyEnabled,
3286
+ selectedValue
3287
+ });
2847
3288
  };
2848
- function onSwipeEnd(event) {
2849
- // stop moving component
2850
- cancelAnimationFrame(animationId.current);
2851
- setPressed(false);
2852
- // check whether move down is strong enough
2853
- // and content scroll position is at the top to close the component
2854
- if (moveOffset.current > MOVE_OFFSET_THRESHOLD && isContentScrollPositionAtTop()) {
2855
- close(event);
2856
- }
2857
- // otherwise move component back to default (initial) position
2858
- else {
2859
- move(INITIAL_Y_POSITION);
2860
- }
2861
- moveOffset.current = INITIAL_Y_POSITION;
2862
- }
2863
- function isContentScrollPositionAtTop() {
2864
- return contentReference?.current?.scrollTop !== undefined && contentReference.current.scrollTop <= CONTENT_SCROLL_THRESHOLD;
2865
- }
2866
- /**
2867
- * Calculates how hard user moves component,
2868
- * result value used to determine whether to hide component or re-position to default state
2869
- *
2870
- * @param afterMoveYCoordinate
2871
- */
2872
- function calculateOffsetAfterMove(afterMoveYCoordinate) {
2873
- return afterMoveYCoordinate - initialYCoordinate.current;
2874
- }
2875
- /**
2876
- * Set `max-height` for content part (in order to keep it scrollable for content overflow cases) of the component
2877
- * and ensures space for safe zone (32px) at the top.
2878
- */
2879
- function setContentMaxHeight() {
2880
- const safeZoneHeight = '64px';
2881
- const topbarHeight = '32px';
2882
- const windowHight = isServerSide() ? 0 : window.innerHeight;
2883
- /**
2884
- * Calculate _real_ height of the screen (taking into account parts of browser interface).
2885
- *
2886
- * See https://css-tricks.com/the-trick-to-viewport-units-on-mobile for more details.
2887
- */
2888
- const screenHeight = `${windowHight * 0.01 * 100}px`;
2889
- return {
2890
- maxHeight: `calc(${screenHeight} - ${safeZoneHeight} - ${topbarHeight})`
2891
- };
2892
- }
2893
- const is400Zoom = useMedia(`(max-width: ${Breakpoint.ZOOM_400}px)`);
2894
- return is400Zoom ? /*#__PURE__*/jsx(Drawer, {
2895
- open: props.open,
2896
- className: props.className,
2897
- onClose: close,
2898
- children: props.children
2899
- }) : /*#__PURE__*/jsx(Dimmer$1, {
2900
- open: props.open,
2901
- fadeContentOnEnter: true,
2902
- fadeContentOnExit: true,
2903
- onClose: close,
2904
- children: /*#__PURE__*/jsx(SlidingPanel, {
2905
- ref: bottomSheetReference,
2906
- open: props.open,
2907
- position: Position.BOTTOM,
2908
- className: classNames('np-bottom-sheet', props.className),
2909
- children: /*#__PURE__*/jsxs("div", {
2910
- role: "dialog",
2911
- "aria-modal": true,
2912
- onTouchStart: onSwipeStart,
2913
- onTouchMove: onSwipeMove,
2914
- onTouchEnd: onSwipeEnd,
2915
- onMouseDown: onSwipeStart,
2916
- onMouseMove: onSwipeMove,
2917
- onMouseUp: onSwipeEnd,
2918
- children: [/*#__PURE__*/jsxs("div", {
2919
- ref: topBarReference,
2920
- className: "np-bottom-sheet--top-bar",
2921
- children: [/*#__PURE__*/jsx("div", {
2922
- className: "np-bottom-sheet--handler"
2923
- }), /*#__PURE__*/jsx(CloseButton, {
2924
- size: "sm",
2925
- className: "sr-only np-bottom-sheet--close-btn",
2926
- onClick: close
2927
- })]
2928
- }), /*#__PURE__*/jsx("div", {
2929
- ref: contentReference,
2930
- style: setContentMaxHeight(),
2931
- className: "np-bottom-sheet--content",
2932
- children: props.children
2933
- })]
2934
- })
3289
+ return /*#__PURE__*/jsx("div", {
3290
+ className: classNames('np-chips d-flex', className),
3291
+ "aria-label": ariaLabel,
3292
+ role: !multiple ? 'radiogroup' : undefined,
3293
+ children: chips.map(chip => {
3294
+ const chipSelected = isSelected(chip.value);
3295
+ return /*#__PURE__*/jsx(Chip, {
3296
+ value: chip.value,
3297
+ label: chip.label,
3298
+ closeButton: {
3299
+ 'aria-label': intl.formatMessage(messages$a.ariaLabel, {
3300
+ choice: chip.label
3301
+ })
3302
+ },
3303
+ className: classNames('text-xs-nowrap', {
3304
+ 'np-chip--selected': chipSelected,
3305
+ 'p-r-1': multiple && chipSelected
3306
+ }),
3307
+ ...(multiple && chipSelected ? {
3308
+ onRemove: () => handleOnChange(chip.value, chipSelected),
3309
+ 'aria-label': chip.label
3310
+ } : {
3311
+ onClick: () => handleOnChange(chip.value, chipSelected),
3312
+ onKeyPress: () => handleOnChange(chip.value, chipSelected),
3313
+ role: !multiple ? 'radio' : undefined,
3314
+ 'aria-checked': chipSelected
3315
+ })
3316
+ }, chip.value);
2935
3317
  })
2936
3318
  });
2937
3319
  };
2938
3320
 
3321
+ const CircularButton = ({
3322
+ className,
3323
+ children,
3324
+ disabled,
3325
+ icon,
3326
+ priority = Priority.PRIMARY,
3327
+ type = ControlType.ACCENT,
3328
+ ...rest
3329
+ }) => {
3330
+ const classes = classNames('btn np-btn', typeClassMap[type], priorityClassMap[priority]);
3331
+ const iconElement = Number(icon.props.size) !== 24 ? /*#__PURE__*/cloneElement(icon, {
3332
+ size: 24
3333
+ }) : icon;
3334
+ return /*#__PURE__*/jsxs("label", {
3335
+ className: classNames('np-circular-btn', priority, type, disabled && 'disabled', className),
3336
+ children: [/*#__PURE__*/jsx("input", {
3337
+ type: "button",
3338
+ "aria-label": children,
3339
+ className: classes,
3340
+ disabled: disabled,
3341
+ ...rest
3342
+ }), iconElement, /*#__PURE__*/jsx(Body, {
3343
+ as: "span",
3344
+ className: "np-circular-btn__label",
3345
+ type: Typography.BODY_DEFAULT_BOLD,
3346
+ children: children
3347
+ })]
3348
+ });
3349
+ };
3350
+
2939
3351
  function CriticalCommsBanner({
2940
3352
  title,
2941
3353
  subtitle,
@@ -3049,36 +3461,6 @@ const isMonthAndYearFormat = dateString => validDateString(dateString) && dateSt
3049
3461
  const MDY = new Set(['en-US']);
3050
3462
  const YMD = new Set(['hu', 'hu-HU', 'zh-HK', 'zh-CN', 'ja', 'ja-JP']);
3051
3463
 
3052
- const FieldLabelIdContext = /*#__PURE__*/createContext(undefined);
3053
- const FieldLabelIdContextProvider = FieldLabelIdContext.Provider;
3054
- const InputIdContext = /*#__PURE__*/createContext(undefined);
3055
- const InputIdContextProvider = InputIdContext.Provider;
3056
- const InputDescribedByContext = /*#__PURE__*/createContext(undefined);
3057
- const InputDescribedByProvider = InputDescribedByContext.Provider;
3058
- const InputInvalidContext = /*#__PURE__*/createContext(undefined);
3059
- const InputInvalidProvider = InputInvalidContext.Provider;
3060
- function useInputAttributes({
3061
- nonLabelable
3062
- } = {}) {
3063
- const labelId = useContext(FieldLabelIdContext);
3064
- return {
3065
- id: useContext(InputIdContext),
3066
- 'aria-labelledby': nonLabelable ? labelId : undefined,
3067
- 'aria-describedby': useContext(InputDescribedByContext),
3068
- 'aria-invalid': useContext(InputInvalidContext)
3069
- };
3070
- }
3071
- function withInputAttributes(Component, args) {
3072
- function ComponentWithInputAttributes(props) {
3073
- return /*#__PURE__*/jsx(Component, {
3074
- inputAttributes: useInputAttributes(args),
3075
- ...props
3076
- });
3077
- }
3078
- ComponentWithInputAttributes.displayName = `withInputAttributes(${Component.displayName || Component.name || 'Component'})`;
3079
- return ComponentWithInputAttributes;
3080
- }
3081
-
3082
3464
  var messages$9 = defineMessages({
3083
3465
  monthLabel: {
3084
3466
  id: "neptune.DateInput.month.label"
@@ -3302,258 +3684,106 @@ const DateInput = ({
3302
3684
  name: "day",
3303
3685
  autoComplete: dayAutoComplete,
3304
3686
  value: displayDay || '',
3305
- placeholder: placeholders?.day,
3306
- disabled: disabled,
3307
- min: 1,
3308
- max: 31,
3309
- maxLength: 2,
3310
- onChange: event => handleDayChange(event)
3311
- })
3312
- })]
3313
- })
3314
- });
3315
- };
3316
- const getYear = () => {
3317
- return /*#__PURE__*/jsx("div", {
3318
- className: "col-sm-4",
3319
- children: /*#__PURE__*/jsxs("label", {
3320
- children: [/*#__PURE__*/jsx(Body, {
3321
- type: Typography.BODY_DEFAULT,
3322
- children: yearLabel
3323
- }), /*#__PURE__*/jsx("div", {
3324
- className: `input-group input-group-${size}`,
3325
- children: /*#__PURE__*/jsx(Input, {
3326
- type: "text",
3327
- inputMode: "numeric",
3328
- pattern: "[0-9]*",
3329
- name: "year",
3330
- autoComplete: yearAutoComplete,
3331
- placeholder: placeholders?.year,
3332
- value: displayYear || '',
3333
- disabled: disabled,
3334
- min: 0,
3335
- max: 9999,
3336
- maxLength: 6,
3337
- onChange: event => handleYearChange(event)
3338
- })
3339
- })]
3340
- })
3341
- });
3342
- };
3343
- const monthBeforeDay = MDY.has(locale);
3344
- const yearFirst = YMD.has(locale);
3345
- return /*#__PURE__*/jsx("div", {
3346
- className: "tw-date",
3347
- ...inputAttributes,
3348
- id: id,
3349
- "aria-labelledby": ariaLabelledBy,
3350
- "aria-label": ariaLabel,
3351
- role: "group" // Add role attribute to indicate container for interactive elements
3352
- ,
3353
- onFocus: event => shouldPropagateOnFocus(event) ? onFocus && onFocus(event) : event.stopPropagation(),
3354
- onBlur: event => shouldPropagateOnBlur(event) ? onBlur && onBlur(event) : event.stopPropagation(),
3355
- children: /*#__PURE__*/jsx("div", {
3356
- className: "row",
3357
- children: (() => {
3358
- if (monthYearOnly) {
3359
- return /*#__PURE__*/jsxs(Fragment, {
3360
- children: [!yearFirst && getMonth(), getYear(), yearFirst && getMonth()]
3361
- });
3362
- }
3363
- if (monthBeforeDay) {
3364
- return /*#__PURE__*/jsxs(Fragment, {
3365
- children: [getMonth(), getDay(), getYear()]
3366
- });
3367
- }
3368
- if (yearFirst) {
3369
- return /*#__PURE__*/jsxs(Fragment, {
3370
- children: [getYear(), getMonth(), getDay()]
3371
- });
3372
- }
3373
- return /*#__PURE__*/jsxs(Fragment, {
3374
- children: [getDay(), getMonth(), getYear()]
3375
- });
3376
- })()
3377
- })
3378
- });
3379
- };
3380
- // Should only propagate if the relatedTarget is not part of this DateInput component.
3381
- function shouldPropagateOnFocus({
3382
- target,
3383
- relatedTarget
3384
- }) {
3385
- const targetParent = target.closest('.tw-date');
3386
- const relatedParent = relatedTarget && relatedTarget.closest('.tw-date');
3387
- return targetParent !== relatedParent;
3388
- }
3389
- // Should only propagate if the relatedTarget or the activeElement is not part of this DateInput component.
3390
- function shouldPropagateOnBlur({
3391
- target,
3392
- relatedTarget
3393
- }) {
3394
- const blurElementParent = target.closest('.tw-date');
3395
- // Even though FocusEvent.relatedTarget is supported by IE
3396
- // (https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget)
3397
- // "IE11 sets document.activeElement to the next focused element before the blur event is called."
3398
- // (https://stackoverflow.com/a/49325196/986241)
3399
- // Therefore if the relatedTarget is null, we try the document.activeElement,
3400
- // which may contain the HTML element that is gaining focus
3401
- const focusElement = relatedTarget || (document.activeElement !== target ? document.activeElement : null);
3402
- const focusElementParent = focusElement && focusElement.closest('.tw-date');
3403
- return blurElementParent !== focusElementParent;
3404
- }
3405
-
3406
- const POPOVER_OFFSET = [0, 16];
3407
- // By default the flip positioning explores only the opposite alternative. So if left is passed and there's no enough space
3408
- // the right one gets chosen. If there's no space on both sides popover goes back to the initially chosen one left.
3409
- // This mapping forces popover to try the four available positions before going back to the initial chosen one.
3410
- const fallbackPlacements = {
3411
- [Position.TOP]: [Position.BOTTOM, Position.RIGHT, Position.LEFT],
3412
- [Position.BOTTOM]: [Position.TOP, Position.RIGHT, Position.LEFT],
3413
- [Position.LEFT]: [Position.RIGHT, Position.TOP, Position.BOTTOM],
3414
- [Position.RIGHT]: [Position.LEFT, Position.TOP, Position.BOTTOM]
3415
- };
3416
- const Panel = /*#__PURE__*/forwardRef(function Panel({
3417
- arrow = false,
3418
- flip = true,
3419
- altAxis = false,
3420
- children,
3421
- open = false,
3422
- onClose,
3423
- position = Position.BOTTOM,
3424
- anchorRef,
3425
- anchorWidth = false,
3426
- ...rest
3427
- }, reference) {
3428
- const [arrowElement, setArrowElement] = useState(null);
3429
- const [popperElement, setPopperElement] = useState(null);
3430
- const modifiers = [];
3431
- if (altAxis) {
3432
- modifiers.push({
3433
- // https://popper.js.org/docs/v2/modifiers/prevent-overflow
3434
- name: 'preventOverflow',
3435
- options: {
3436
- altAxis: true,
3437
- tether: false
3438
- }
3439
- });
3440
- }
3441
- if (arrow) {
3442
- modifiers.push({
3443
- name: 'arrow',
3444
- options: {
3445
- element: arrowElement,
3446
- options: {
3447
- padding: 8 // 8px from the edges of the popper
3448
- }
3449
- }
3450
- });
3451
- // This lets you displace a popper element from its reference element.
3452
- modifiers.push({
3453
- name: 'offset',
3454
- options: {
3455
- offset: POPOVER_OFFSET
3456
- }
3457
- });
3458
- }
3459
- if (flip && fallbackPlacements[position]) {
3460
- modifiers.push({
3461
- name: 'flip',
3462
- options: {
3463
- fallbackPlacements: fallbackPlacements[position]
3464
- }
3465
- });
3466
- }
3467
- const {
3468
- styles,
3469
- attributes,
3470
- forceUpdate
3471
- } = usePopper(anchorRef.current, popperElement, {
3472
- placement: position,
3473
- modifiers
3474
- });
3475
- // If the trigger is not visible when the position is calculated, it will be incorrect. Because this can happen repeatedly (on resize for example),
3476
- // it is most simple just to always position before opening
3477
- useEffect(() => {
3478
- if (open && forceUpdate) {
3479
- forceUpdate();
3480
- }
3481
- }, [open]);
3482
- const contentStyle = {
3483
- ...(anchorWidth ? {
3484
- width: anchorRef.current?.clientWidth
3485
- } : undefined)
3486
- };
3487
- return /*#__PURE__*/jsx(Dimmer$1, {
3488
- open: open,
3489
- transparent: true,
3490
- fadeContentOnEnter: true,
3491
- fadeContentOnExit: true,
3492
- onClose: onClose,
3493
- children: /*#__PURE__*/jsx("div", {
3494
- ...rest,
3495
- ref: setPopperElement,
3496
- role: "dialog"
3497
- // eslint-disable-next-line react/forbid-dom-props
3498
- ,
3499
- style: {
3500
- ...styles.popper
3501
- },
3502
- ...attributes.popper,
3503
- className: classNames('np-panel', {
3504
- 'np-panel--open': open
3505
- }, rest.className),
3506
- children: /*#__PURE__*/jsxs("div", {
3507
- ref: reference
3508
- /* eslint-disable-next-line react/forbid-dom-props */,
3509
- style: contentStyle,
3510
- className: classNames('np-panel__content'),
3511
- children: [children, arrow && /*#__PURE__*/jsx("div", {
3512
- ref: setArrowElement,
3513
- className: classNames('np-panel__arrow')
3514
- // eslint-disable-next-line react/forbid-dom-props
3515
- ,
3516
- style: styles.arrow
3687
+ placeholder: placeholders?.day,
3688
+ disabled: disabled,
3689
+ min: 1,
3690
+ max: 31,
3691
+ maxLength: 2,
3692
+ onChange: event => handleDayChange(event)
3693
+ })
3694
+ })]
3695
+ })
3696
+ });
3697
+ };
3698
+ const getYear = () => {
3699
+ return /*#__PURE__*/jsx("div", {
3700
+ className: "col-sm-4",
3701
+ children: /*#__PURE__*/jsxs("label", {
3702
+ children: [/*#__PURE__*/jsx(Body, {
3703
+ type: Typography.BODY_DEFAULT,
3704
+ children: yearLabel
3705
+ }), /*#__PURE__*/jsx("div", {
3706
+ className: `input-group input-group-${size}`,
3707
+ children: /*#__PURE__*/jsx(Input, {
3708
+ type: "text",
3709
+ inputMode: "numeric",
3710
+ pattern: "[0-9]*",
3711
+ name: "year",
3712
+ autoComplete: yearAutoComplete,
3713
+ placeholder: placeholders?.year,
3714
+ value: displayYear || '',
3715
+ disabled: disabled,
3716
+ min: 0,
3717
+ max: 9999,
3718
+ maxLength: 6,
3719
+ onChange: event => handleYearChange(event)
3720
+ })
3517
3721
  })]
3518
3722
  })
3723
+ });
3724
+ };
3725
+ const monthBeforeDay = MDY.has(locale);
3726
+ const yearFirst = YMD.has(locale);
3727
+ return /*#__PURE__*/jsx("div", {
3728
+ className: "tw-date",
3729
+ ...inputAttributes,
3730
+ id: id,
3731
+ "aria-labelledby": ariaLabelledBy,
3732
+ "aria-label": ariaLabel,
3733
+ role: "group" // Add role attribute to indicate container for interactive elements
3734
+ ,
3735
+ onFocus: event => shouldPropagateOnFocus(event) ? onFocus && onFocus(event) : event.stopPropagation(),
3736
+ onBlur: event => shouldPropagateOnBlur(event) ? onBlur && onBlur(event) : event.stopPropagation(),
3737
+ children: /*#__PURE__*/jsx("div", {
3738
+ className: "row",
3739
+ children: (() => {
3740
+ if (monthYearOnly) {
3741
+ return /*#__PURE__*/jsxs(Fragment, {
3742
+ children: [!yearFirst && getMonth(), getYear(), yearFirst && getMonth()]
3743
+ });
3744
+ }
3745
+ if (monthBeforeDay) {
3746
+ return /*#__PURE__*/jsxs(Fragment, {
3747
+ children: [getMonth(), getDay(), getYear()]
3748
+ });
3749
+ }
3750
+ if (yearFirst) {
3751
+ return /*#__PURE__*/jsxs(Fragment, {
3752
+ children: [getYear(), getMonth(), getDay()]
3753
+ });
3754
+ }
3755
+ return /*#__PURE__*/jsxs(Fragment, {
3756
+ children: [getDay(), getMonth(), getYear()]
3757
+ });
3758
+ })()
3519
3759
  })
3520
3760
  });
3521
- });
3522
- var Panel$1 = Panel;
3523
-
3524
- const ResponsivePanel = /*#__PURE__*/forwardRef(function ResponsivePanel({
3525
- anchorRef,
3526
- arrow = false,
3527
- flip = true,
3528
- children,
3529
- className = undefined,
3530
- onClose,
3531
- open = false,
3532
- position = Position.BOTTOM
3533
- }, reference) {
3534
- const {
3535
- isMobile
3536
- } = useLayout();
3537
- if (isMobile) {
3538
- return /*#__PURE__*/jsx(BottomSheet$1, {
3539
- open: open,
3540
- className: className,
3541
- onClose: onClose,
3542
- children: children
3543
- }, "bottomSheet");
3544
- }
3545
- return /*#__PURE__*/jsx(Panel$1, {
3546
- ref: reference,
3547
- flip: flip,
3548
- arrow: arrow,
3549
- open: open,
3550
- position: position,
3551
- anchorRef: anchorRef,
3552
- className: className,
3553
- onClose: onClose,
3554
- children: children
3555
- }, "panel");
3556
- });
3761
+ };
3762
+ // Should only propagate if the relatedTarget is not part of this DateInput component.
3763
+ function shouldPropagateOnFocus({
3764
+ target,
3765
+ relatedTarget
3766
+ }) {
3767
+ const targetParent = target.closest('.tw-date');
3768
+ const relatedParent = relatedTarget && relatedTarget.closest('.tw-date');
3769
+ return targetParent !== relatedParent;
3770
+ }
3771
+ // Should only propagate if the relatedTarget or the activeElement is not part of this DateInput component.
3772
+ function shouldPropagateOnBlur({
3773
+ target,
3774
+ relatedTarget
3775
+ }) {
3776
+ const blurElementParent = target.closest('.tw-date');
3777
+ // Even though FocusEvent.relatedTarget is supported by IE
3778
+ // (https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget)
3779
+ // "IE11 sets document.activeElement to the next focused element before the blur event is called."
3780
+ // (https://stackoverflow.com/a/49325196/986241)
3781
+ // Therefore if the relatedTarget is null, we try the document.activeElement,
3782
+ // which may contain the HTML element that is gaining focus
3783
+ const focusElement = relatedTarget || (document.activeElement !== target ? document.activeElement : null);
3784
+ const focusElementParent = focusElement && focusElement.closest('.tw-date');
3785
+ return blurElementParent !== focusElementParent;
3786
+ }
3557
3787
 
3558
3788
  var dateTriggerMessages = defineMessages({
3559
3789
  ariaLabel: {
@@ -4517,39 +4747,6 @@ var DateLookup$1 = withInputAttributes(DateLookup, {
4517
4747
  nonLabelable: true
4518
4748
  });
4519
4749
 
4520
- const NavigationOption = /*#__PURE__*/forwardRef(({
4521
- as: component = 'button',
4522
- disabled = false,
4523
- onClick,
4524
- className,
4525
- ...rest
4526
- }, reference) => {
4527
- if ('href' in rest && rest.href) {
4528
- component = 'a';
4529
- }
4530
- return /*#__PURE__*/jsx(Option$2, {
4531
- ...rest,
4532
- ref: reference,
4533
- as: component,
4534
- className: classNames('np-navigation-option', className),
4535
- button: /*#__PURE__*/jsx(Chevron, {
4536
- orientation: Position.RIGHT,
4537
- disabled: disabled,
4538
- className: "d-block"
4539
- }),
4540
- disabled: disabled,
4541
- onClick: event => {
4542
- if (disabled) {
4543
- event.preventDefault();
4544
- return;
4545
- }
4546
- if (onClick) {
4547
- onClick(event);
4548
- }
4549
- }
4550
- });
4551
- });
4552
-
4553
4750
  function Tile({
4554
4751
  className,
4555
4752
  description,
@@ -5373,73 +5570,6 @@ const FlowNavigation = ({
5373
5570
  });
5374
5571
  };
5375
5572
 
5376
- const HeaderAction = ({
5377
- action
5378
- }) => {
5379
- const {
5380
- isModern
5381
- } = useTheme();
5382
- const props = {
5383
- 'aria-label': action['aria-label']
5384
- };
5385
- if ('href' in action) {
5386
- return /*#__PURE__*/jsx(Link, {
5387
- href: action.href,
5388
- target: action.target,
5389
- onClick: action.onClick,
5390
- ...props,
5391
- children: action.text
5392
- });
5393
- }
5394
- return isModern ? /*#__PURE__*/jsx(Button, {
5395
- className: "np-header__button",
5396
- priority: "tertiary",
5397
- size: "sm",
5398
- onClick: action.onClick,
5399
- ...props,
5400
- children: action.text
5401
- }) : /*#__PURE__*/jsx(ActionButton, {
5402
- onClick: action.onClick,
5403
- ...props,
5404
- children: action.text
5405
- });
5406
- };
5407
- /**
5408
- *
5409
- * Neptune Web: https://transferwise.github.io/neptune-web/components/content/Header
5410
- *
5411
- */
5412
- const Header = ({
5413
- action,
5414
- as = 'h5',
5415
- title,
5416
- className
5417
- }) => {
5418
- if (!action) {
5419
- return /*#__PURE__*/jsx(Title, {
5420
- as: as,
5421
- type: Typography.TITLE_GROUP,
5422
- className: classNames('np-header', 'np-header__title', className),
5423
- children: title
5424
- });
5425
- }
5426
- if (as === 'legend') {
5427
- // eslint-disable-next-line no-console
5428
- console.warn('Legends should be the first child in a fieldset, and this is not possible when including an action');
5429
- }
5430
- return /*#__PURE__*/jsxs("div", {
5431
- className: classNames('np-header', className),
5432
- children: [/*#__PURE__*/jsx(Title, {
5433
- as: as,
5434
- type: Typography.TITLE_GROUP,
5435
- className: "np-header__title",
5436
- children: title
5437
- }), /*#__PURE__*/jsx(HeaderAction, {
5438
- action: action
5439
- })]
5440
- });
5441
- };
5442
-
5443
5573
  // TODO: consider to move this enum into component file once we migrate it on TypeScript or replace with some common enum
5444
5574
  var InfoPresentation;
5445
5575
  (function (InfoPresentation) {
@@ -7608,18 +7738,6 @@ var MoneyInput$1 = injectIntl(withInputAttributes(MoneyInput, {
7608
7738
  nonLabelable: true
7609
7739
  }));
7610
7740
 
7611
- const NavigationOptionList = ({
7612
- children
7613
- }) => {
7614
- return /*#__PURE__*/jsx("ul", {
7615
- className: "np-navigation-options-list",
7616
- children: Children.map(children, child => /*#__PURE__*/jsx("li", {
7617
- className: "np-navigation-options-list__item",
7618
- children: child
7619
- }, child?.toString()))
7620
- });
7621
- };
7622
-
7623
7741
  const STORAGE_NAME = 'dismissedNudges';
7624
7742
  const getLocalStorage = () => {
7625
7743
  try {
@@ -9404,6 +9522,7 @@ var en = {
9404
9522
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Select an option...",
9405
9523
  "neptune.Select.searchPlaceholder": "Search...",
9406
9524
  "neptune.SelectInput.noResultsFound": "No results found",
9525
+ "neptune.SelectOption.action.label": "Choose",
9407
9526
  "neptune.Summary.statusDone": "Item done",
9408
9527
  "neptune.Summary.statusNotDone": "Item to do",
9409
9528
  "neptune.Summary.statusPending": "Item pending",
@@ -9631,19 +9750,6 @@ function RadioOption({
9631
9750
  });
9632
9751
  }
9633
9752
 
9634
- const Section = ({
9635
- children,
9636
- className,
9637
- withHorizontalPadding = false
9638
- }) => {
9639
- return /*#__PURE__*/jsx("div", {
9640
- className: classNames('np-section', className, {
9641
- 'np-section--with-horizontal-padding': withHorizontalPadding
9642
- }),
9643
- children: children
9644
- });
9645
- };
9646
-
9647
9753
  const SegmentedControl = ({
9648
9754
  name,
9649
9755
  value,
@@ -14320,5 +14426,5 @@ const translations = {
14320
14426
  'zh-HK': zhHK
14321
14427
  };
14322
14428
 
14323
- export { Accordion, ActionButton, ActionOption, Alert, AlertArrowPosition, Avatar, AvatarType, AvatarWrapper, Badge, Card$1 as BaseCard, Body, BottomSheet$1 as BottomSheet, Breakpoint, Button, Card, Carousel, Checkbox, CheckboxButton, CheckboxOption, Chevron, Chip, Chips, CircularButton, ControlType, CriticalCommsBanner, DEFAULT_LANG, DEFAULT_LOCALE, DateInput, DateLookup$1 as DateLookup, DateMode, Decision, DecisionPresentation, DecisionType, DefinitionList, Dimmer$1 as Dimmer, Direction, DirectionProvider, Display, Drawer, DropFade, Emphasis, Field, FileType, FlowNavigation, Header, Image, Info, InfoPresentation, InlineAlert, Input, InputGroup, InputWithDisplayFormat, InstructionsList, Label, LanguageProvider, Layout, Link, ListItem$1 as ListItem, Loader, Logo, LogoType, Markdown, MarkdownNodeType, Modal, Money, MoneyInput$1 as MoneyInput, MonthFormat, NavigationOption, NavigationOptionList as NavigationOptionsList, Nudge, Option$2 as Option, OverlayHeader, PhoneNumberInput, Popover$1 as Popover, Position, Priority, ProcessIndicator, ProfileType, Progress, ProgressBar, PromoCard$1 as PromoCard, PromoCardGroup$1 as PromoCardGroup, Provider, RTL_LANGUAGES, Radio, RadioGroup, RadioOption, SUPPORTED_LANGUAGES, Scroll, SearchInput, Section, SegmentedControl, Select, SelectInput, SelectInputOptionContent, SelectInputTriggerButton, Sentiment, Size, SlidingPanel, SnackbarConsumer, SnackbarContext, SnackbarPortal, SnackbarProvider, Status, StatusIcon, Stepper, Sticky, Summary, Switch, SwitchOption, Tabs$1 as Tabs, TextArea, TextareaWithDisplayFormat, Theme, Title, Tooltip, Type, Typeahead$1 as Typeahead, Typography, Upload$1 as Upload, UploadInput, UploadStep, Variant, Width, adjustLocale, getCountryFromLocale, getDirectionFromLocale, getLangFromLocale, isBrowser, isServerSide, translations, useDirection, useLayout, useScreenSize, useSnackbar };
14429
+ export { Accordion, ActionButton, ActionOption, Alert, AlertArrowPosition, Avatar, AvatarType, AvatarWrapper, Badge, Card$1 as BaseCard, Body, BottomSheet$1 as BottomSheet, Breakpoint, Button, Card, Carousel, Checkbox, CheckboxButton, CheckboxOption, Chevron, Chip, Chips, CircularButton, ControlType, CriticalCommsBanner, DEFAULT_LANG, DEFAULT_LOCALE, DateInput, DateLookup$1 as DateLookup, DateMode, Decision, DecisionPresentation, DecisionType, DefinitionList, Dimmer$1 as Dimmer, Direction, DirectionProvider, Display, Drawer, DropFade, Emphasis, Field, FileType, FlowNavigation, Header, Image, Info, InfoPresentation, InlineAlert, Input, InputGroup, InputWithDisplayFormat, InstructionsList, Label, LanguageProvider, Layout, Link, ListItem$1 as ListItem, Loader, Logo, LogoType, Markdown, MarkdownNodeType, Modal, Money, MoneyInput$1 as MoneyInput, MonthFormat, NavigationOption, NavigationOptionList as NavigationOptionsList, Nudge, Option$2 as Option, OverlayHeader, PhoneNumberInput, Popover$1 as Popover, Position, Priority, ProcessIndicator, ProfileType, Progress, ProgressBar, PromoCard$1 as PromoCard, PromoCardGroup$1 as PromoCardGroup, Provider, RTL_LANGUAGES, Radio, RadioGroup, RadioOption, SUPPORTED_LANGUAGES, Scroll, SearchInput, Section, SegmentedControl, Select, SelectInput, SelectInputOptionContent, SelectInputTriggerButton, SelectOption, Sentiment, Size, SlidingPanel, SnackbarConsumer, SnackbarContext, SnackbarPortal, SnackbarProvider, Status, StatusIcon, Stepper, Sticky, Summary, Switch, SwitchOption, Tabs$1 as Tabs, TextArea, TextareaWithDisplayFormat, Theme, Title, Tooltip, Type, Typeahead$1 as Typeahead, Typography, Upload$1 as Upload, UploadInput, UploadStep, Variant, Width, adjustLocale, getCountryFromLocale, getDirectionFromLocale, getLangFromLocale, isBrowser, isServerSide, translations, useDirection, useLayout, useScreenSize, useSnackbar };
14324
14430
  //# sourceMappingURL=index.mjs.map