@transferwise/components 0.0.0-experimental-5125346 → 0.0.0-experimental-2220d83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.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,1831 @@ 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
+ showMediaAtAllSizes: true,
1998
+ disabled: disabled,
1999
+ decision: false,
2000
+ media: hasSelected ? selected.media : placeholder.media ?? /*#__PURE__*/jsx(Plus, {
2001
+ size: 24
2002
+ }),
2003
+ title: (hasSelected ? selected : placeholder).title,
2004
+ content: (hasSelected ? selected : placeholder).content,
2005
+ className: classNames('np-select-option', 'clickable', hasSelected ? 'np-select-option-selected' : 'np-select-option-placeholder', props.className),
2006
+ button: /*#__PURE__*/jsx("button", {
2007
+ type: "button",
2008
+ disabled: disabled,
2009
+ "aria-haspopup": "dialog",
2010
+ "aria-expanded": showOptions,
2011
+ "aria-labelledby": ariaLabelledBy,
2012
+ className: hasSelected ? 'btn-unstyled' : 'np-action-btn',
2013
+ "aria-label": hasSelected ? undefined : props['aria-label'],
2014
+ onClick: handleOnClick(true),
2015
+ children: hasSelected ? /*#__PURE__*/jsx(Chevron, {}) : placeholder.actionLabel || intl.formatMessage(messages$c.actionLabel)
2170
2016
  })
2017
+ }), /*#__PURE__*/jsx(ResponsivePanel, {
2018
+ anchorWidth: true,
2019
+ altAxis: true,
2020
+ anchorRef: rootRef,
2021
+ open: showOptions,
2022
+ position: Position.BOTTOM,
2023
+ onClose: handleOnClick(false),
2024
+ children: getOptions(isLargeScreen)
2171
2025
  })]
2172
2026
  });
2027
+ }
2028
+
2029
+ const iconTypeMap = {
2030
+ positive: Check,
2031
+ neutral: Info$1,
2032
+ warning: Alert$1,
2033
+ negative: Cross,
2034
+ pending: ClockBorderless,
2035
+ info: Info$1,
2036
+ error: Cross,
2037
+ success: Check
2038
+ };
2039
+ const StatusIcon = ({
2040
+ sentiment = 'neutral',
2041
+ size = 'md'
2042
+ }) => {
2043
+ const Icon = iconTypeMap[sentiment];
2044
+ const iconColor = sentiment === 'warning' || sentiment === 'pending' ? 'dark' : 'light';
2045
+ return /*#__PURE__*/jsx("span", {
2046
+ "data-testid": "status-icon",
2047
+ className: classNames('status-circle', `status-circle-${size}`, sentiment),
2048
+ children: /*#__PURE__*/jsx(Icon, {
2049
+ className: classNames('status-icon', iconColor)
2050
+ })
2051
+ });
2052
+ };
2053
+
2054
+ const reader = new commonmark.Parser();
2055
+ const writer = new commonmark.HtmlRenderer({
2056
+ safe: true
2057
+ });
2058
+ const NODE_TYPE_LIST = Object.values(MarkdownNodeType);
2059
+ function Markdown({
2060
+ as: Element = 'div',
2061
+ allowList,
2062
+ blockList,
2063
+ config,
2064
+ className,
2065
+ children
2066
+ }) {
2067
+ if (!children) {
2068
+ return null;
2069
+ }
2070
+ const linkTarget = config?.link?.target ?? '_self';
2071
+ const paragraphClass = config?.paragraph?.className ?? '';
2072
+ if (allowList != null && blockList != null) {
2073
+ logActionRequired('Markdown supports only one of `allowList` or `blockList` to be used at a time. `blockList` will be ignored.');
2074
+ }
2075
+ const parser = nodes => {
2076
+ const parsed = reader.parse(nodes);
2077
+ const toExclude = allowList != null ? NODE_TYPE_LIST.filter(type => !allowList.includes(type)) : blockList;
2078
+ return toExclude != null ? stripNodes({
2079
+ parsed,
2080
+ blockList: toExclude
2081
+ }) : parsed;
2082
+ };
2083
+ const createMarkup = () => {
2084
+ const parsed = parser(children);
2085
+ return writer.render(parsed).replace(/<a href="/g, `<a target="${linkTarget}" href="`).replace(/<p>/g, `<p class="${paragraphClass}">`);
2086
+ };
2087
+ return /*#__PURE__*/jsx(Element, {
2088
+ className: className,
2089
+ dangerouslySetInnerHTML: {
2090
+ __html: createMarkup()
2091
+ }
2092
+ });
2093
+ }
2094
+ function stripNodes({
2095
+ blockList,
2096
+ parsed
2097
+ }) {
2098
+ if (!parsed) {
2099
+ return parsed;
2100
+ }
2101
+ const walker = parsed.walker();
2102
+ for (let event = walker.next(); event != null; event = walker.next()) {
2103
+ const {
2104
+ node
2105
+ } = event;
2106
+ if (blockList.includes(node.type) && !event.entering) {
2107
+ while (node.firstChild != null) {
2108
+ node.insertBefore(node.firstChild);
2109
+ }
2110
+ node.unlink();
2111
+ }
2112
+ }
2113
+ return parsed;
2114
+ }
2115
+
2116
+ const allowList = [MarkdownNodeType.STRONG];
2117
+ function InlineMarkdown(props) {
2118
+ return /*#__PURE__*/jsx(Markdown, {
2119
+ ...props,
2120
+ as: "span",
2121
+ allowList: allowList,
2122
+ blockList: undefined
2123
+ });
2124
+ }
2125
+
2126
+ function Action({
2127
+ action,
2128
+ className
2129
+ }) {
2130
+ if ('href' in action) {
2131
+ return /*#__PURE__*/jsx(Link, {
2132
+ href: action.href,
2133
+ className: className,
2134
+ "aria-label": action['aria-label'],
2135
+ target: action.target,
2136
+ type: Typography.LINK_LARGE,
2137
+ onClick: action.onClick,
2138
+ children: action.text
2139
+ });
2140
+ }
2141
+ return /*#__PURE__*/jsx("button", {
2142
+ type: "button",
2143
+ "aria-label": action['aria-label'],
2144
+ className: classNames('btn-unstyled np-text-link-large', className),
2145
+ onClick: action.onClick,
2146
+ children: action.text
2147
+ });
2148
+ }
2149
+
2150
+ var AlertArrowPosition;
2151
+ (function (AlertArrowPosition) {
2152
+ AlertArrowPosition["TOP_LEFT"] = "up-left";
2153
+ AlertArrowPosition["TOP"] = "up-center";
2154
+ AlertArrowPosition["TOP_RIGHT"] = "up-right";
2155
+ AlertArrowPosition["BOTTOM_LEFT"] = "down-left";
2156
+ AlertArrowPosition["BOTTOM"] = "down-center";
2157
+ AlertArrowPosition["BOTTOM_RIGHT"] = "down-right";
2158
+ })(AlertArrowPosition || (AlertArrowPosition = {}));
2159
+ function resolveType(type) {
2160
+ switch (type) {
2161
+ case 'success':
2162
+ return 'positive';
2163
+ case 'info':
2164
+ return 'neutral';
2165
+ case 'error':
2166
+ return 'negative';
2167
+ default:
2168
+ return type;
2169
+ }
2170
+ }
2171
+ function Alert({
2172
+ arrow,
2173
+ action,
2174
+ children,
2175
+ className,
2176
+ dismissible,
2177
+ icon,
2178
+ onDismiss,
2179
+ message,
2180
+ size,
2181
+ title,
2182
+ type = 'neutral',
2183
+ variant = 'desktop'
2184
+ }) {
2185
+ useEffect(() => {
2186
+ if (arrow !== undefined) {
2187
+ logActionRequired("Alert component doesn't support 'arrow' anymore, use 'InlineAlert' instead.");
2188
+ }
2189
+ }, [arrow]);
2190
+ useEffect(() => {
2191
+ if (children !== undefined) {
2192
+ logActionRequired("Alert component doesn't support 'children' anymore, use 'message' instead.");
2193
+ }
2194
+ }, [children]);
2195
+ useEffect(() => {
2196
+ if (dismissible !== undefined) {
2197
+ logActionRequired("Alert component doesn't support 'dismissible' anymore, use 'onDismiss' instead.");
2198
+ }
2199
+ }, [dismissible]);
2200
+ useEffect(() => {
2201
+ if (size !== undefined) {
2202
+ logActionRequired("Alert component doesn't support 'size' anymore, please remove that prop.");
2203
+ }
2204
+ }, [size]);
2205
+ const resolvedType = resolveType(type);
2206
+ useEffect(() => {
2207
+ if (resolvedType !== type) {
2208
+ logActionRequired(`Alert component has deprecated '${type}' value for the 'type' prop. Please use '${resolvedType}' instead.`);
2209
+ }
2210
+ }, [resolvedType, type]);
2211
+ const [shouldFire, setShouldFire] = useState(false);
2212
+ const closeButtonReference = useRef(null);
2213
+ return /*#__PURE__*/jsxs("div", {
2214
+ className: classNames('alert d-flex', `alert-${resolvedType}`, arrow != null && alertArrowClassNames(arrow), className),
2215
+ "data-testid": "alert",
2216
+ onTouchStart: () => setShouldFire(true),
2217
+ onTouchEnd: event => {
2218
+ if (shouldFire && action?.href &&
2219
+ // Check if current event is triggered from closeButton
2220
+ event.target instanceof Node && closeButtonReference.current && !closeButtonReference.current.contains(event.target)) {
2221
+ if (action.target === '_blank') {
2222
+ window.top?.open(action.href);
2223
+ } else {
2224
+ window.top?.location.assign(action.href);
2225
+ }
2226
+ }
2227
+ setShouldFire(false);
2228
+ },
2229
+ onTouchMove: () => setShouldFire(false),
2230
+ children: [/*#__PURE__*/jsxs("div", {
2231
+ className: classNames('alert__content', 'd-flex', 'flex-grow-1', variant),
2232
+ "data-testid": variant,
2233
+ children: [icon ? /*#__PURE__*/jsx("div", {
2234
+ className: "alert__icon",
2235
+ children: icon
2236
+ }) : /*#__PURE__*/jsx(StatusIcon, {
2237
+ size: Size.LARGE,
2238
+ sentiment: resolvedType
2239
+ }), /*#__PURE__*/jsxs("div", {
2240
+ className: "alert__message",
2241
+ children: [/*#__PURE__*/jsxs("div", {
2242
+ role: Sentiment.NEGATIVE === resolvedType ? 'alert' : 'status',
2243
+ children: [title && /*#__PURE__*/jsx(Title, {
2244
+ className: "m-b-1",
2245
+ type: Typography.TITLE_BODY,
2246
+ children: title
2247
+ }), /*#__PURE__*/jsx(Body, {
2248
+ as: "span",
2249
+ className: "d-block",
2250
+ type: Typography.BODY_LARGE,
2251
+ children: children || /*#__PURE__*/jsx(InlineMarkdown, {
2252
+ children: message
2253
+ })
2254
+ })]
2255
+ }), action && /*#__PURE__*/jsx(Action, {
2256
+ action: action,
2257
+ className: "m-t-1"
2258
+ })]
2259
+ })]
2260
+ }), onDismiss && /*#__PURE__*/jsx(CloseButton, {
2261
+ ref: closeButtonReference,
2262
+ className: "m-l-2",
2263
+ onClick: onDismiss
2264
+ })]
2265
+ });
2266
+ }
2267
+ function alertArrowClassNames(arrow) {
2268
+ switch (arrow) {
2269
+ case 'down-center':
2270
+ return 'arrow arrow-bottom arrow-center';
2271
+ case 'down-left':
2272
+ return 'arrow arrow-bottom arrow-left';
2273
+ case 'down-right':
2274
+ return 'arrow arrow-bottom arrow-right';
2275
+ case 'up-center':
2276
+ return 'arrow arrow-center';
2277
+ case 'up-right':
2278
+ return 'arrow arrow-right';
2279
+ case 'up-left':
2280
+ default:
2281
+ return 'arrow';
2282
+ }
2283
+ }
2284
+
2285
+ // TODO: consider to move this enum into component file once we migrate it on TypeScript or replace with some common enum
2286
+ var AvatarType;
2287
+ (function (AvatarType) {
2288
+ AvatarType["THUMBNAIL"] = "thumbnail";
2289
+ AvatarType["ICON"] = "icon";
2290
+ AvatarType["EMOJI"] = "emoji";
2291
+ AvatarType["INITIALS"] = "initials";
2292
+ })(AvatarType || (AvatarType = {}));
2293
+
2294
+ /*
2295
+ * The colors we support generating an Avatar background color from a "seed" for.
2296
+ * Changing this array will change the assignment between seeds.
2297
+ * Do not change this array.
2298
+ */
2299
+ const avatarColors = ['--color-bright-blue', '--color-bright-yellow', '--color-bright-pink', '--color-bright-orange'];
2300
+ /*
2301
+ * Takes in a "seed" string and spits out an index for the color we should use.
2302
+ * This implementation has been synced across all three clients so that we consistently
2303
+ * generate branded avatar colors when rendering a list of Avatars.
2304
+ * Do not change this implementation.
2305
+ */
2306
+ const hashSeed = seed => {
2307
+ const base = 31;
2308
+ const modulo = avatarColors.length;
2309
+ let hashValue = 0;
2310
+ let basePow = 1;
2311
+ for (let i = 0; i < seed.length; i += 1) {
2312
+ hashValue = (hashValue + seed.charCodeAt(i) * basePow) % modulo;
2313
+ basePow = basePow * base % modulo;
2314
+ }
2315
+ return hashValue;
2316
+ };
2317
+ const getAvatarColorFromSeed = seed => {
2318
+ return avatarColors[hashSeed(seed)];
2319
+ };
2320
+
2321
+ const backwardsCompatibleSize = size => {
2322
+ switch (size) {
2323
+ case 'sm':
2324
+ return 24;
2325
+ case 'md':
2326
+ return 48;
2327
+ case 'lg':
2328
+ return 72;
2329
+ default:
2330
+ return size;
2331
+ }
2332
+ };
2333
+ const Avatar = ({
2334
+ backgroundColor = null,
2335
+ backgroundColorSeed = null,
2336
+ children = null,
2337
+ className,
2338
+ outlined = false,
2339
+ size: sizeFromProps = 48,
2340
+ theme = Theme.LIGHT,
2341
+ type = 'thumbnail'
2342
+ }) => {
2343
+ const backgroundColorFromSeed = useMemo(() => !backgroundColor && backgroundColorSeed ? `var(${getAvatarColorFromSeed(backgroundColorSeed)})` : undefined, [backgroundColor, backgroundColorSeed]);
2344
+ const size = backwardsCompatibleSize(sizeFromProps);
2345
+ return /*#__PURE__*/jsx("div", {
2346
+ className: classNames('tw-avatar', className, `tw-avatar--${size}`, `tw-avatar--${type}`, {
2347
+ 'tw-avatar--outlined': outlined,
2348
+ 'tw-avatar--branded': Boolean(backgroundColorFromSeed),
2349
+ 'np-text-title-body': type === 'initials'
2350
+ }),
2351
+ children: /*#__PURE__*/jsx("div", {
2352
+ className: "tw-avatar__content",
2353
+ style: {
2354
+ backgroundColor: backgroundColor || backgroundColorFromSeed
2355
+ },
2356
+ children: children
2357
+ })
2358
+ });
2359
+ };
2360
+
2361
+ const Badge = ({
2362
+ badge,
2363
+ className = undefined,
2364
+ size = Size.SMALL,
2365
+ border = Theme.LIGHT,
2366
+ children
2367
+ }) => {
2368
+ const classes = classNames('tw-badge', {
2369
+ [`tw-badge-border-${border}`]: border,
2370
+ [`tw-badge-${size}`]: size
2371
+ }, className);
2372
+ return /*#__PURE__*/jsxs("div", {
2373
+ className: classes,
2374
+ children: [/*#__PURE__*/jsx("div", {
2375
+ className: "tw-badge__children",
2376
+ children: children
2377
+ }), /*#__PURE__*/jsx("div", {
2378
+ className: "tw-badge__content",
2379
+ children: badge
2380
+ })]
2381
+ });
2382
+ };
2383
+
2384
+ const OptionalBadge = ({
2385
+ url,
2386
+ altText,
2387
+ statusIcon,
2388
+ children,
2389
+ ...rest
2390
+ }) => {
2391
+ if (url) {
2392
+ return /*#__PURE__*/jsx(Badge, {
2393
+ badge: /*#__PURE__*/jsx("img", {
2394
+ src: url,
2395
+ alt: altText
2396
+ }),
2397
+ ...rest,
2398
+ children: children
2399
+ });
2400
+ }
2401
+ if (statusIcon) {
2402
+ return /*#__PURE__*/jsx(Badge, {
2403
+ badge: /*#__PURE__*/jsx(StatusIcon, {
2404
+ sentiment: statusIcon,
2405
+ size: Size.SMALL
2406
+ }),
2407
+ ...rest,
2408
+ children: children
2409
+ });
2410
+ }
2411
+ return /*#__PURE__*/jsx(Fragment, {
2412
+ children: children
2413
+ });
2414
+ };
2415
+ const AvatarWrapper = ({
2416
+ url,
2417
+ profileType,
2418
+ profileId,
2419
+ badgeUrl,
2420
+ badgeAltText,
2421
+ badgeStatusIcon,
2422
+ name,
2423
+ avatarProps,
2424
+ badgeProps
2425
+ }) => {
2426
+ const [hasImageLoadError, setImageLoadError] = useState(false);
2427
+ const isBusinessProfile = profileType === ProfileType.BUSINESS;
2428
+ // Reset the errored state when url changes
2429
+ useEffect(() => setImageLoadError(false), [url]);
2430
+ const getAvatarProps = () => {
2431
+ if (url && !hasImageLoadError) {
2432
+ return {
2433
+ type: AvatarType.THUMBNAIL,
2434
+ children: /*#__PURE__*/jsx("img", {
2435
+ src: url,
2436
+ alt: "",
2437
+ onError: () => setImageLoadError(true)
2438
+ }),
2439
+ ...avatarProps
2440
+ };
2441
+ }
2442
+ if (profileType) {
2443
+ return {
2444
+ type: AvatarType.ICON,
2445
+ children: isBusinessProfile ? /*#__PURE__*/jsx(Briefcase, {
2446
+ size: "24"
2447
+ }) : /*#__PURE__*/jsx(Person, {
2448
+ size: "24"
2449
+ }),
2450
+ ...avatarProps
2451
+ };
2452
+ }
2453
+ if (name) {
2454
+ return {
2455
+ type: AvatarType.INITIALS,
2456
+ children: /*#__PURE__*/jsx(Fragment, {
2457
+ children: getInitials(name)
2458
+ }),
2459
+ backgroundColorSeed: profileId?.toString(),
2460
+ ...avatarProps
2461
+ };
2462
+ }
2463
+ return {
2464
+ type: AvatarType.ICON,
2465
+ children: /*#__PURE__*/jsx(Person, {
2466
+ size: "24"
2467
+ }),
2468
+ ...avatarProps
2469
+ };
2470
+ };
2471
+ return /*#__PURE__*/jsx(OptionalBadge, {
2472
+ url: badgeUrl,
2473
+ altText: badgeAltText,
2474
+ statusIcon: badgeStatusIcon,
2475
+ ...badgeProps,
2476
+ children: /*#__PURE__*/jsx(Avatar, {
2477
+ size: Size.MEDIUM,
2478
+ ...getAvatarProps()
2479
+ })
2480
+ });
2173
2481
  };
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
- };
2482
+ function getInitials(name) {
2483
+ const allInitials = name.split(' ').map(part => part[0]).join('').toUpperCase();
2484
+ if (allInitials.length === 1) {
2485
+ return allInitials[0];
2486
+ }
2487
+ return allInitials[0] + allInitials.slice(-1);
2488
+ }
2181
2489
 
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,
2490
+ const Card$1 = /*#__PURE__*/forwardRef(({
2192
2491
  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
- }),
2492
+ children = null,
2493
+ id,
2494
+ isDisabled = false,
2495
+ isSmall = false,
2496
+ onDismiss,
2497
+ testId,
2498
+ ...props
2499
+ }, ref) => {
2500
+ const closeButtonReference = useRef(null);
2501
+ return /*#__PURE__*/jsxs("div", {
2502
+ ref: ref,
2503
+ className: classNames('np-Card', {
2504
+ 'np-Card--small': !!isSmall,
2505
+ 'is-disabled': !!isDisabled
2506
+ }, className),
2203
2507
  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
- })]
2508
+ "data-testid": testId,
2509
+ ...props,
2510
+ children: [onDismiss && /*#__PURE__*/jsx(CloseButton, {
2511
+ ref: closeButtonReference,
2512
+ className: "np-Card-closeButton",
2513
+ size: isSmall ? 'sm' : 'md',
2514
+ isDisabled: isDisabled,
2515
+ testId: "close-button",
2516
+ onClick: e => {
2517
+ stopPropagation$1(e);
2518
+ onDismiss();
2519
+ }
2520
+ }), children]
2227
2521
  });
2228
2522
  });
2523
+ Card$1.displayName = 'Card';
2229
2524
 
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,
2525
+ function Display({
2526
+ as: Heading = 'h1',
2527
+ type = Typography.DISPLAY_LARGE,
2528
+ children,
2260
2529
  className,
2261
- secondary,
2262
- onChange,
2263
- onFocus,
2264
- onBlur
2530
+ id
2265
2531
  }) {
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", {
2532
+ return /*#__PURE__*/jsx(Heading, {
2273
2533
  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
- })
2534
+ className: classNames(`np-text-${type}`, 'text-primary', className),
2535
+ children: children
2300
2536
  });
2301
2537
  }
2302
2538
 
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
- })
2539
+ const EmptyTransparentImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
2540
+ const Image = ({
2541
+ id,
2542
+ src,
2543
+ alt,
2544
+ onLoad,
2545
+ onError,
2546
+ className,
2547
+ loading,
2548
+ stretch = true,
2549
+ role,
2550
+ shrink = true
2551
+ }) => {
2552
+ const elementReference = useRef(null);
2553
+ const [hasIntersected] = useHasIntersected({
2554
+ elRef: elementReference,
2555
+ loading
2556
+ });
2557
+ let imageSource = src;
2558
+ let imageOnLoad = onLoad;
2559
+ if (loading === 'lazy' && !hasIntersected) {
2560
+ imageSource = EmptyTransparentImage;
2561
+ imageOnLoad = undefined;
2562
+ }
2563
+ return /*#__PURE__*/jsx("img", {
2564
+ ref: elementReference,
2565
+ id: id,
2566
+ alt: alt,
2567
+ src: imageSource,
2568
+ className: classNames(['tw-image', {
2569
+ 'tw-image__stretch': stretch,
2570
+ 'tw-image__shrink': shrink
2571
+ }, className]),
2572
+ role: role,
2573
+ onLoad: imageOnLoad,
2574
+ onError: onError
2318
2575
  });
2319
- });
2576
+ };
2320
2577
 
2321
- const Chip = ({
2578
+ const defaultPromoCardContext = {
2579
+ state: '',
2580
+ isDisabled: false,
2581
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2582
+ onChange: () => {}
2583
+ };
2584
+ /**
2585
+ * The PromoCard context object.
2586
+ */
2587
+ const PromoCardContext = /*#__PURE__*/createContext(defaultPromoCardContext);
2588
+ /**
2589
+ * A custom hook for accessing the PromoCard context object.
2590
+ *
2591
+ * The `usePromoCardContext` hook is used to access the PromoCard context object
2592
+ * from within a child PromoCard component. It throws an error if the context
2593
+ * object is not available, which can help with debugging and development.
2594
+ *
2595
+ * @returns {PromoCardContextType} - The PromoCard context object.
2596
+ */
2597
+ const usePromoCardContext = () => {
2598
+ return useContext(PromoCardContext);
2599
+ };
2600
+
2601
+ const PromoCardIndicator = ({
2602
+ className,
2603
+ children,
2322
2604
  label,
2323
- value,
2324
- onRemove,
2325
- onClick,
2326
- onKeyPress,
2327
- className = undefined,
2328
- 'aria-label': ariaLabel,
2329
- 'aria-checked': ariaChecked,
2330
- role,
2331
- closeButton
2605
+ icon,
2606
+ isSmall = false,
2607
+ testid,
2608
+ ...rest
2332
2609
  }) => {
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]);
2610
+ const isIconString = icon && typeof icon === 'string';
2611
+ const IconComponent = isIconString && {
2612
+ check: Check,
2613
+ arrow: ArrowRight,
2614
+ download: Download
2615
+ }[icon];
2347
2616
  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",
2617
+ className: classNames('np-Card-indicator', className),
2618
+ "data-testid": testid,
2619
+ ...rest,
2620
+ children: [label && /*#__PURE__*/jsx(Body, {
2621
+ as: "span",
2622
+ type: Typography.BODY_LARGE_BOLD,
2623
+ className: "np-Card-indicatorText",
2364
2624
  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);
2625
+ }), icon && /*#__PURE__*/jsx(Avatar, {
2626
+ type: AvatarType.ICON,
2627
+ size: isSmall ? 40 : 56,
2628
+ backgroundColor: "var(--Card-indicator-icon-background-color)",
2629
+ className: "np-Card-indicatorIcon",
2630
+ children: IconComponent ? /*#__PURE__*/jsx(IconComponent, {
2631
+ size: 24,
2632
+ "aria-hidden": "true"
2633
+ }) : icon
2634
+ }), children]
2635
+ });
2374
2636
  };
2375
2637
 
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,
2638
+ const PromoCard = /*#__PURE__*/forwardRef(({
2387
2639
  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
2640
+ description,
2641
+ defaultChecked,
2642
+ download,
2643
+ href,
2644
+ hrefLang,
2645
+ id,
2646
+ headingLevel = 'h3',
2647
+ imageAlt,
2648
+ imageClass,
2649
+ imageSource,
2650
+ indicatorLabel,
2651
+ indicatorIcon,
2652
+ isChecked,
2653
+ isDisabled,
2654
+ onClick,
2655
+ onKeyDown,
2656
+ rel,
2657
+ tabIndex,
2658
+ target,
2659
+ testId,
2660
+ title,
2661
+ type,
2662
+ value,
2663
+ isSmall,
2664
+ useDisplayFont = true,
2665
+ anchorRef,
2666
+ anchorId,
2667
+ ...props
2668
+ }, ref) => {
2669
+ // Set the `checked` state to the value of `defaultChecked` if it is truthy,
2670
+ // or the value of `isChecked` if it is truthy, or `false` if neither
2671
+ // is truthy.
2672
+ const {
2673
+ state,
2674
+ onChange,
2675
+ isDisabled: contextIsDisabled
2676
+ } = usePromoCardContext();
2677
+ const [checked, setChecked] = useState(type === 'checkbox' ? defaultChecked ?? isChecked ?? false : false);
2678
+ const handleClick = () => {
2679
+ if (type === 'radio') {
2680
+ onChange(value || ''); // Update the context state for radio
2681
+ } else if (type === 'checkbox') {
2682
+ setChecked(!checked); // Update local state for checkbox
2683
+ }
2684
+ };
2685
+ const fallbackId = useId();
2686
+ const componentId = id || fallbackId;
2687
+ // Set the icon to `'arrow'` if `href` is truthy and `type` is falsy, or
2688
+ // `'download'` if `download` is truthy. If neither condition is true, set
2689
+ // `icon` to `undefined`.
2690
+ // Create a function to get icon type
2691
+ const getIconType = () => {
2692
+ if (indicatorIcon) {
2693
+ return indicatorIcon;
2694
+ }
2695
+ if (download) {
2696
+ return 'download';
2697
+ }
2698
+ if (href && !type) {
2699
+ return 'arrow';
2700
+ }
2701
+ return undefined;
2702
+ };
2703
+ // Define all class names string based on the values of the `href`, `type`,
2704
+ // `checked`, and `className` props.
2705
+ const commonClasses = classNames({
2706
+ 'np-Card--promoCard': true,
2707
+ 'np-Card--checked': !href && type,
2708
+ 'np-Card--link': href && !type,
2709
+ 'is-checked': type === 'radio' ? value === state : type === 'checkbox' ? checked : undefined
2710
+ }, className);
2711
+ // Object with common props that will be passed to the `Card` components
2712
+ const commonProps = {
2713
+ className: commonClasses,
2714
+ id: componentId,
2715
+ isDisabled: isDisabled || contextIsDisabled,
2716
+ onClick,
2717
+ onKeyDown,
2718
+ ref,
2719
+ 'data-testid': testId,
2720
+ isSmall
2721
+ };
2722
+ // Object with Anchor props that will be passed to the `a` element. These
2723
+ // won't be refurned if set to `isDisabled`
2724
+ const anchorProps = href && !isDisabled ? {
2725
+ download,
2726
+ href: href || undefined,
2727
+ hrefLang,
2728
+ rel,
2729
+ target,
2730
+ ref: anchorRef,
2731
+ id: anchorId
2732
+ } : {};
2733
+ // Object of all Checked props that will be passed to the root `Card` component
2734
+ const checkedProps = (type === 'checkbox' || type === 'radio') && !href ? {
2735
+ ...commonProps,
2736
+ 'aria-checked': type === 'radio' ? value === state : type === 'checkbox' ? checked : undefined,
2737
+ 'aria-describedby': `${componentId}-title`,
2738
+ 'aria-disabled': isDisabled,
2739
+ 'data-value': value ?? undefined,
2740
+ role: type === 'checkbox' || type === 'radio' ? type : undefined,
2741
+ onClick: handleClick,
2742
+ onKeyDown: event => {
2743
+ if (event.key === 'Enter' || event.key === ' ') {
2744
+ handleClick();
2745
+ }
2746
+ },
2747
+ ref,
2748
+ tabIndex: 0
2749
+ } : {};
2750
+ const getTitle = () => {
2751
+ const titleContent = href && !type ? /*#__PURE__*/jsx("a", {
2752
+ className: "np-Card-titleLink",
2753
+ ...anchorProps,
2754
+ children: title
2755
+ }) : title;
2756
+ const titleProps = {
2757
+ id: `${componentId}-title`,
2758
+ as: headingLevel,
2759
+ className: 'np-Card-title'
2760
+ };
2761
+ return useDisplayFont ? /*#__PURE__*/jsx(Display, {
2762
+ type: Typography.DISPLAY_SMALL,
2763
+ ...titleProps,
2764
+ children: titleContent
2765
+ }) : /*#__PURE__*/jsx(Title, {
2766
+ type: Typography.TITLE_SUBSECTION,
2767
+ ...titleProps,
2768
+ children: titleContent
2396
2769
  });
2397
2770
  };
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
2771
  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
- })
2772
+ setChecked(defaultChecked ?? isChecked ?? false);
2773
+ }, [defaultChecked, isChecked]);
2774
+ return /*#__PURE__*/jsxs(Card$1, {
2775
+ ...commonProps,
2776
+ ...checkedProps,
2777
+ ...props,
2778
+ children: [(value === state || checked) && /*#__PURE__*/jsx("span", {
2779
+ className: "np-Card-check",
2780
+ children: /*#__PURE__*/jsx(Check, {
2781
+ size: 24,
2782
+ "aria-hidden": "true"
2783
+ })
2784
+ }), getTitle(), /*#__PURE__*/jsx(Body, {
2785
+ className: "np-Card-description",
2786
+ children: description
2787
+ }), imageSource && /*#__PURE__*/jsx("div", {
2788
+ className: classNames('np-Card-image', {
2789
+ imageClass
2790
+ }),
2791
+ children: /*#__PURE__*/jsx(Image, {
2792
+ src: imageSource,
2793
+ alt: imageAlt || '',
2794
+ loading: "lazy"
2795
+ })
2796
+ }), /*#__PURE__*/jsx(PromoCardIndicator, {
2797
+ label: indicatorLabel,
2798
+ icon: getIconType(),
2799
+ isSmall: isSmall
2800
+ })]
2477
2801
  });
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
- }
2802
+ });
2803
+ var PromoCard$1 = /*#__PURE__*/React__default.memo(PromoCard);
2523
2804
 
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,
2805
+ const LEFT_SCROLL_OFFSET = 8;
2806
+ const Carousel = ({
2807
+ header,
2538
2808
  className,
2539
- disableClickToClose = false,
2540
- contentPosition,
2541
- fadeContentOnEnter = false,
2542
- fadeContentOnExit = false,
2543
- open = false,
2544
- scrollable = false,
2545
- transparent = false,
2546
- onClose
2809
+ cards,
2810
+ onClick
2547
2811
  }) => {
2548
- const [hasNotExited, setHasNotExited] = useState(false);
2549
- const dimmerReference = useRef(null);
2550
- const closeOnClick = event => {
2551
- if (event.target === dimmerReference.current) {
2552
- onClose?.(event);
2812
+ const [scrollPosition, setScrollPosition] = useState(0);
2813
+ const [previousScrollPosition, setPreviousScrollPosition] = useState(0);
2814
+ const [scrollIsAtEnd, setScrollIsAtEnd] = useState(false);
2815
+ const [visibleCardOnMobileView, setVisibleCardOnMobileView] = useState('');
2816
+ const carouselElementRef = useRef(null);
2817
+ const carouselCardsRef = useRef([]);
2818
+ const isLeftActionButtonEnabled = scrollPosition > LEFT_SCROLL_OFFSET;
2819
+ const areActionButtonsEnabled = isLeftActionButtonEnabled || !scrollIsAtEnd;
2820
+ const [focusedCard, setFocusedCard] = useState(cards?.[0]?.id);
2821
+ const updateScrollButtonsState = () => {
2822
+ if (carouselElementRef.current) {
2823
+ const {
2824
+ scrollWidth,
2825
+ offsetWidth
2826
+ } = carouselElementRef.current;
2827
+ const scrollAtEnd = scrollWidth - offsetWidth <= scrollPosition + LEFT_SCROLL_OFFSET;
2828
+ setScrollIsAtEnd(scrollAtEnd);
2553
2829
  }
2554
- };
2555
- const handleClick = event => {
2556
- if (disableClickToClose || !onClose) {
2557
- return;
2830
+ const scrollDirecton = scrollPosition > previousScrollPosition ? 'right' : 'left';
2831
+ const cardsInFullViewIds = [];
2832
+ carouselCardsRef.current.forEach(card => {
2833
+ if (isVisible(carouselElementRef.current, card.cardElement)) {
2834
+ // eslint-disable-next-line functional/immutable-data
2835
+ cardsInFullViewIds.push(card.cardElement.getAttribute('id') ?? '');
2836
+ }
2837
+ });
2838
+ if (cardsInFullViewIds.length >= 1) {
2839
+ const visibleCardIndex = scrollDirecton === 'right' ? cardsInFullViewIds.length - 1 : 0;
2840
+ const visibleCardId = cardsInFullViewIds[visibleCardIndex];
2841
+ setVisibleCardOnMobileView(visibleCardId);
2842
+ setFocusedCard(visibleCardId);
2558
2843
  }
2559
- closeOnClick(event);
2844
+ setPreviousScrollPosition(scrollPosition);
2560
2845
  };
2561
- const handleKeyDown = useCallback(event => {
2562
- if (event.key !== 'Escape') {
2563
- return;
2846
+ const scrollCarousel = (direction = 'right') => {
2847
+ if (carouselElementRef.current) {
2848
+ const {
2849
+ scrollWidth
2850
+ } = carouselElementRef.current;
2851
+ const cardWidth = scrollWidth / carouselCardsRef.current.length;
2852
+ const res = Math.floor(cardWidth - cardWidth * 0.05);
2853
+ carouselElementRef.current.scrollBy({
2854
+ left: direction === 'right' ? res : -res,
2855
+ behavior: 'smooth'
2856
+ });
2564
2857
  }
2565
- event.stopPropagation();
2566
- if (onClose && dimmerReference.current && dimmerManager.isTop(dimmerReference.current)) {
2567
- onClose(event);
2858
+ };
2859
+ const handleOnKeyDown = (event, index) => {
2860
+ if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
2861
+ const nextIndex = event.key === 'ArrowRight' ? index + 1 : index - 1;
2862
+ const nextCard = cards[nextIndex];
2863
+ if (nextCard) {
2864
+ const ref = carouselCardsRef.current[nextIndex];
2865
+ if (ref.type === 'promo') {
2866
+ ref.anchorElement?.focus();
2867
+ } else {
2868
+ ref.cardElement?.focus();
2869
+ }
2870
+ scrollCardIntoView(carouselCardsRef.current[nextIndex].cardElement, nextCard);
2871
+ event.preventDefault();
2872
+ }
2568
2873
  }
2569
- }, [onClose]);
2570
- const onEnter = () => {
2571
- setHasNotExited(true);
2572
- if (dimmerReference.current) {
2573
- dimmerManager.add(dimmerReference.current);
2874
+ if (event.key === 'Enter' || event.key === ' ') {
2875
+ event.currentTarget.click();
2574
2876
  }
2575
2877
  };
2576
- const onExited = () => {
2577
- setHasNotExited(false);
2578
- if (dimmerReference.current) {
2579
- dimmerManager.remove(dimmerReference.current);
2580
- }
2878
+ const scrollCardIntoView = (element, card) => {
2879
+ element.scrollIntoView({
2880
+ behavior: 'smooth',
2881
+ block: 'nearest',
2882
+ inline: 'center'
2883
+ });
2884
+ setFocusedCard(card.id);
2581
2885
  };
2582
2886
  useEffect(() => {
2583
- const localReferenceCopy = dimmerReference.current;
2584
- if (open) {
2585
- document.addEventListener('keydown', handleKeyDown);
2586
- localReferenceCopy?.addEventListener('touchmove', handleTouchMove, {
2587
- passive: true
2588
- });
2589
- }
2887
+ updateScrollButtonsState();
2888
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2889
+ }, [scrollPosition]);
2890
+ useEffect(() => {
2891
+ window.addEventListener('resize', updateScrollButtonsState);
2590
2892
  return () => {
2591
- document.removeEventListener('keydown', handleKeyDown);
2592
- localReferenceCopy?.removeEventListener('touchmove', handleTouchMove);
2893
+ window.removeEventListener('resize', updateScrollButtonsState);
2593
2894
  };
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
- })
2895
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2896
+ }, []);
2897
+ const addElementToCardsRefArray = (index, ref) => {
2898
+ if (ref) {
2899
+ // eslint-disable-next-line functional/immutable-data
2900
+ carouselCardsRef.current[index] = {
2901
+ type: ref.type ?? carouselCardsRef.current?.[index]?.type,
2902
+ cardElement: ref.cardElement ?? carouselCardsRef.current?.[index]?.cardElement,
2903
+ anchorElement: ref.anchorElement ?? carouselCardsRef.current?.[index]?.anchorElement
2904
+ };
2905
+ }
2906
+ };
2907
+ return /*#__PURE__*/jsxs("div", {
2908
+ className: classNames('carousel-wrapper', className),
2909
+ children: [/*#__PURE__*/jsxs("div", {
2910
+ className: "d-flex justify-content-between carousel__header",
2911
+ children: [typeof header === 'string' ? /*#__PURE__*/jsx(Title, {
2912
+ as: "span",
2913
+ type: "title-body",
2914
+ children: header
2915
+ }) : header, areActionButtonsEnabled ? /*#__PURE__*/jsxs("div", {
2916
+ className: "hidden-xs",
2917
+ children: [/*#__PURE__*/jsx(ActionButton, {
2918
+ className: "carousel__scroll-button",
2919
+ tabIndex: -1,
2920
+ priority: "secondary",
2921
+ disabled: !isLeftActionButtonEnabled,
2922
+ "aria-hidden": "true",
2923
+ "data-testid": "scroll-carousel-left",
2924
+ onClick: () => scrollCarousel('left'),
2925
+ children: /*#__PURE__*/jsx(ChevronLeft, {})
2926
+ }), /*#__PURE__*/jsx(ActionButton, {
2927
+ tabIndex: -1,
2928
+ className: "carousel__scroll-button m-l-1",
2929
+ priority: "secondary",
2930
+ "aria-hidden": "true",
2931
+ "data-testid": "scroll-carousel-right",
2932
+ disabled: scrollIsAtEnd,
2933
+ onClick: () => scrollCarousel(),
2934
+ children: /*#__PURE__*/jsx(ChevronRight, {})
2935
+ })]
2936
+ }) : null]
2937
+ }), /*#__PURE__*/jsx("div", {
2938
+ ref: carouselElementRef,
2939
+ tabIndex: -1,
2940
+ role: "list",
2941
+ className: "carousel",
2942
+ onScroll: event => {
2943
+ const target = event.target;
2944
+ setScrollPosition(target.scrollLeft);
2618
2945
  },
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,
2946
+ children: cards?.map((card, index) => {
2947
+ const sharedProps = {
2948
+ id: card.id,
2949
+ className: classNames('carousel__card', {
2950
+ 'carousel__card--focused': card.id === focusedCard
2951
+ }),
2952
+ onClick: () => {
2953
+ card.onClick?.();
2954
+ onClick?.(card.id);
2955
+ },
2956
+ onFocus: event => {
2957
+ scrollCardIntoView(event.currentTarget, card);
2958
+ }
2959
+ };
2960
+ const cardContent = card.type !== 'promo' ? /*#__PURE__*/jsx("div", {
2961
+ id: `${card.id}-content`,
2962
+ className: classNames('carousel__card-content', {
2963
+ [card.className ?? '']: !!card.className
2964
+ })
2965
+ // eslint-disable-next-line react/forbid-dom-props
2966
+ ,
2967
+ style: card.styles,
2968
+ children: card.content
2969
+ }) : null;
2970
+ if (card.type === 'button') {
2971
+ return /*#__PURE__*/jsx("div", {
2972
+ "aria-labelledby": `${card.id}-content`,
2973
+ role: "listitem",
2633
2974
  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
2975
+ ...sharedProps,
2976
+ ref: el => {
2977
+ if (el) {
2978
+ // eslint-disable-next-line functional/immutable-data
2979
+ carouselCardsRef.current[index] = {
2980
+ type: 'default',
2981
+ cardElement: el
2982
+ };
2983
+ }
2984
+ },
2985
+ role: "button",
2986
+ tabIndex: 0,
2987
+ onKeyDown: event => handleOnKeyDown(event, index),
2988
+ children: cardContent
2989
+ })
2990
+ }, card.id);
2991
+ }
2992
+ if (card.type === 'promo') {
2993
+ return /*#__PURE__*/jsx("div", {
2994
+ id: card.id,
2995
+ role: "listitem",
2996
+ "aria-labelledby": `${card.id}-anchor`,
2997
+ children: /*#__PURE__*/jsx(PromoCard$1, {
2998
+ ...card,
2999
+ type: undefined,
3000
+ ...sharedProps,
3001
+ ref: el => {
3002
+ if (el) {
3003
+ addElementToCardsRefArray(index, {
3004
+ type: 'promo',
3005
+ cardElement: el
3006
+ });
3007
+ }
3008
+ },
3009
+ anchorRef: el => {
3010
+ if (el) {
3011
+ addElementToCardsRefArray(index, {
3012
+ type: 'promo',
3013
+ anchorElement: el
3014
+ });
3015
+ }
3016
+ },
3017
+ anchorId: `${card.id}-anchor`,
3018
+ onKeyDown: event => handleOnKeyDown(event, index)
2640
3019
  })
3020
+ }, card.id);
3021
+ }
3022
+ return /*#__PURE__*/jsx("div", {
3023
+ "aria-labelledby": `${card.id}-content`,
3024
+ role: "listitem",
3025
+ children: /*#__PURE__*/jsx("a", {
3026
+ ...sharedProps,
3027
+ ref: el => {
3028
+ if (el) {
3029
+ // eslint-disable-next-line functional/immutable-data
3030
+ carouselCardsRef.current[index] = {
3031
+ type: 'default',
3032
+ cardElement: el
3033
+ };
3034
+ }
3035
+ },
3036
+ href: card.href,
3037
+ rel: "noreferrer",
3038
+ onKeyDown: event => handleOnKeyDown(event, index),
3039
+ children: cardContent
2641
3040
  })
2642
- })
3041
+ }, card.id);
2643
3042
  })
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
3043
+ }), /*#__PURE__*/jsx("div", {
3044
+ className: "visible-xs",
3045
+ children: /*#__PURE__*/jsx("div", {
3046
+ className: "carousel__indicators",
3047
+ children: cards?.map((card, index) => /*#__PURE__*/jsx("button", {
3048
+ "data-testid": `${card.id}-indicator`,
3049
+ tabIndex: -1,
3050
+ "aria-hidden": true,
3051
+ type: "button",
3052
+ className: classNames('carousel__indicator', {
3053
+ 'carousel__indicator--selected': card.id === visibleCardOnMobileView
3054
+ }),
3055
+ onClick: () => {
3056
+ scrollCardIntoView(carouselCardsRef.current[index].cardElement, card);
3057
+ }
3058
+ }, `${card.id}-indicator`))
3059
+ })
3060
+ })]
2663
3061
  });
2664
3062
  };
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;
3063
+ const isVisible = (container, el) => {
3064
+ const cWidth = container.offsetWidth;
3065
+ const cScrollOffset = container.scrollLeft;
3066
+ const elemLeft = el.offsetLeft - container.offsetLeft;
3067
+ const elemRight = elemLeft + el.offsetWidth;
3068
+ return elemLeft >= cScrollOffset && elemRight <= cScrollOffset + cWidth;
2680
3069
  };
2681
- var Dimmer$1 = withNextPortalWrapper(Dimmer);
2682
3070
 
2683
- const EXIT_ANIMATION = 350;
2684
- const SlidingPanel = /*#__PURE__*/forwardRef(({
2685
- position = 'left',
2686
- open,
2687
- showSlidingPanelBorder,
2688
- slidingPanelPositionFixed,
2689
- className,
3071
+ const Card = /*#__PURE__*/forwardRef(function Card({
3072
+ 'aria-label': ariaLabel,
3073
+ as: Element = 'div',
3074
+ isExpanded,
3075
+ title,
3076
+ details,
2690
3077
  children,
2691
- testId,
3078
+ onClick,
3079
+ icon,
3080
+ id,
3081
+ className,
3082
+ 'data-testid': dataTestId
3083
+ }, reference) {
3084
+ const isOpen = !!(isExpanded && children);
3085
+ return /*#__PURE__*/jsxs(Element, {
3086
+ ref: reference,
3087
+ className: classNames('np-card', className, {
3088
+ 'np-card--expanded': isOpen,
3089
+ 'np-card--inactive': !children,
3090
+ 'np-card--has-icon': !!icon
3091
+ }),
3092
+ id: id,
3093
+ "data-testid": dataTestId,
3094
+ children: [/*#__PURE__*/jsx(Option$2, {
3095
+ "aria-label": ariaLabel,
3096
+ as: children ? 'button' : 'div',
3097
+ className: classNames('np-card__button'),
3098
+ media: icon,
3099
+ title: title,
3100
+ content: details,
3101
+ showMediaAtAllSizes: true,
3102
+ button: children && /*#__PURE__*/jsx(Chevron, {
3103
+ orientation: isOpen ? Position.TOP : Position.BOTTOM
3104
+ }),
3105
+ onClick: children ? () => onClick?.(!isExpanded) : undefined
3106
+ }), /*#__PURE__*/jsx("div", {
3107
+ className: classNames('np-card__divider', {
3108
+ 'np-card__divider--expanded': isOpen
3109
+ })
3110
+ }), isOpen && /*#__PURE__*/jsx(Body, {
3111
+ as: "span",
3112
+ type: Typography.BODY_LARGE,
3113
+ className: "np-card__content d-block",
3114
+ children: children
3115
+ })]
3116
+ });
3117
+ });
3118
+
3119
+ const CheckboxButton = /*#__PURE__*/forwardRef(({
3120
+ checked,
3121
+ className,
3122
+ disabled,
3123
+ onChange,
2692
3124
  ...rest
2693
- }, reference) => {
2694
- const localReference = useRef(null);
2695
- useImperativeHandle(reference, () => localReference.current, []);
2696
- return /*#__PURE__*/createElement(CSSTransition, {
3125
+ }, reference) => /*#__PURE__*/jsxs("span", {
3126
+ className: classNames('np-checkbox-button', className, disabled && 'disabled'),
3127
+ children: [/*#__PURE__*/jsx("input", {
2697
3128
  ...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
- });
3129
+ ref: reference,
3130
+ type: "checkbox",
3131
+ disabled: disabled,
3132
+ checked: checked,
3133
+ onChange: onChange
3134
+ }), /*#__PURE__*/jsx("span", {
3135
+ className: "tw-checkbox-button",
3136
+ children: /*#__PURE__*/jsx("span", {
3137
+ className: "tw-checkbox-check"
3138
+ })
3139
+ })]
3140
+ }));
2717
3141
 
2718
- function Drawer({
2719
- children,
3142
+ function Checkbox({
3143
+ id,
3144
+ checked,
3145
+ required,
3146
+ disabled,
3147
+ readOnly,
3148
+ label,
2720
3149
  className,
2721
- footerContent,
2722
- headerTitle,
2723
- onClose,
2724
- open = false,
2725
- position = 'right'
3150
+ secondary,
3151
+ onChange,
3152
+ onFocus,
3153
+ onBlur
2726
3154
  }) {
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
3155
+ const classList = classNames('np-checkbox', {
3156
+ checkbox: true,
3157
+ 'checkbox-lg': secondary,
3158
+ disabled
3159
+ }, className);
3160
+ const innerDisabled = disabled || readOnly;
3161
+ return /*#__PURE__*/jsx("div", {
3162
+ id: id,
3163
+ className: classList,
3164
+ children: /*#__PURE__*/jsxs("label", {
3165
+ className: classNames({
3166
+ disabled
3167
+ }),
3168
+ children: [/*#__PURE__*/jsx(CheckboxButton, {
3169
+ className: "p-r-2",
3170
+ checked: checked,
3171
+ disabled: innerDisabled,
3172
+ required: !innerDisabled && required,
3173
+ onFocus: onFocus,
3174
+ onChange: () => onChange(!checked),
3175
+ onBlur: onBlur
3176
+ }), /*#__PURE__*/jsxs(Body, {
3177
+ as: "span",
3178
+ className: "np-checkbox__text",
3179
+ type: secondary ? Typography.BODY_LARGE_BOLD : Typography.BODY_LARGE,
3180
+ children: [/*#__PURE__*/jsx("span", {
3181
+ className: required ? 'has-required' : undefined,
3182
+ children: label
3183
+ }), secondary && /*#__PURE__*/jsx(Body, {
3184
+ as: "span",
3185
+ children: secondary
2760
3186
  })]
2761
- })
3187
+ })]
2762
3188
  })
2763
3189
  });
2764
3190
  }
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
3191
+
3192
+ const CheckboxOption = /*#__PURE__*/forwardRef(({
3193
+ checked,
3194
+ disabled,
3195
+ onChange,
3196
+ ...rest
3197
+ }, reference) => {
3198
+ return /*#__PURE__*/jsx(Option$2, {
3199
+ ...rest,
3200
+ ref: reference,
3201
+ disabled: disabled,
3202
+ button: /*#__PURE__*/jsx(CheckboxButton, {
3203
+ checked: checked,
3204
+ disabled: disabled,
3205
+ onChange: () => onChange?.(!checked)
3206
+ })
2811
3207
  });
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);
3208
+ });
3209
+
3210
+ const Chip = ({
3211
+ label,
3212
+ value,
3213
+ onRemove,
3214
+ onClick,
3215
+ onKeyPress,
3216
+ className = undefined,
3217
+ 'aria-label': ariaLabel,
3218
+ 'aria-checked': ariaChecked,
3219
+ role,
3220
+ closeButton
3221
+ }) => {
3222
+ const isActionable = onClick || onKeyPress;
3223
+ const defaultRole = isActionable ? 'button' : undefined;
3224
+ const tabIndex = isActionable ? 0 : -1;
3225
+ const {
3226
+ isModern
3227
+ } = useTheme();
3228
+ const closeButtonReference = useRef(null);
3229
+ const previousCloseButtonShown = useRef(undefined);
3230
+ useEffect(() => {
3231
+ if (closeButtonReference.current != null && previousCloseButtonShown.current === false) {
3232
+ closeButtonReference.current?.focus();
2825
3233
  }
3234
+ previousCloseButtonShown.current = closeButtonReference.current != null;
3235
+ }, [onRemove]);
3236
+ return /*#__PURE__*/jsxs("div", {
3237
+ role: role ?? defaultRole,
3238
+ tabIndex: tabIndex,
3239
+ "aria-label": ariaLabel,
3240
+ "aria-checked": ariaChecked,
3241
+ className: classNames('np-chip', 'd-flex', 'align-items-center', 'justify-content-between', onRemove ? 'np-chip--removable' : '', className),
3242
+ ...(isActionable && {
3243
+ onClick,
3244
+ onKeyPress
3245
+ }),
3246
+ children: [isModern ? /*#__PURE__*/jsx(Body, {
3247
+ "aria-hidden": !!onRemove,
3248
+ type: Typography.BODY_DEFAULT_BOLD,
3249
+ children: label
3250
+ }) : /*#__PURE__*/jsx("span", {
3251
+ "aria-hidden": "false",
3252
+ className: "np-chip-label",
3253
+ children: label
3254
+ }), onRemove ? /*#__PURE__*/jsx(CloseButton, {
3255
+ ref: closeButtonReference,
3256
+ className: isModern ? `btn-unstyled` : `btn-unstyled m-l-1`,
3257
+ "aria-label": closeButton?.['aria-label'],
3258
+ size: "sm",
3259
+ filled: isModern,
3260
+ onClick: onRemove
3261
+ }) : null]
3262
+ }, value);
3263
+ };
3264
+
3265
+ var messages$a = defineMessages({
3266
+ ariaLabel: {
3267
+ id: "neptune.Chips.ariaLabel"
2826
3268
  }
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
- }
3269
+ });
3270
+
3271
+ const Chips = ({
3272
+ chips,
3273
+ onChange,
3274
+ selected,
3275
+ 'aria-label': ariaLabel,
3276
+ className,
3277
+ multiple
3278
+ }) => {
3279
+ const intl = useIntl();
3280
+ const isSelected = value => Array.isArray(selected) ? selected.includes(value) : selected === value;
3281
+ const handleOnChange = (selectedValue, isCurrentlyEnabled) => {
3282
+ onChange({
3283
+ isEnabled: !isCurrentlyEnabled,
3284
+ selectedValue
3285
+ });
2847
3286
  };
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
- })
3287
+ return /*#__PURE__*/jsx("div", {
3288
+ className: classNames('np-chips d-flex', className),
3289
+ "aria-label": ariaLabel,
3290
+ role: !multiple ? 'radiogroup' : undefined,
3291
+ children: chips.map(chip => {
3292
+ const chipSelected = isSelected(chip.value);
3293
+ return /*#__PURE__*/jsx(Chip, {
3294
+ value: chip.value,
3295
+ label: chip.label,
3296
+ closeButton: {
3297
+ 'aria-label': intl.formatMessage(messages$a.ariaLabel, {
3298
+ choice: chip.label
3299
+ })
3300
+ },
3301
+ className: classNames('text-xs-nowrap', {
3302
+ 'np-chip--selected': chipSelected,
3303
+ 'p-r-1': multiple && chipSelected
3304
+ }),
3305
+ ...(multiple && chipSelected ? {
3306
+ onRemove: () => handleOnChange(chip.value, chipSelected),
3307
+ 'aria-label': chip.label
3308
+ } : {
3309
+ onClick: () => handleOnChange(chip.value, chipSelected),
3310
+ onKeyPress: () => handleOnChange(chip.value, chipSelected),
3311
+ role: !multiple ? 'radio' : undefined,
3312
+ 'aria-checked': chipSelected
3313
+ })
3314
+ }, chip.value);
2935
3315
  })
2936
3316
  });
2937
3317
  };
2938
3318
 
3319
+ const CircularButton = ({
3320
+ className,
3321
+ children,
3322
+ disabled,
3323
+ icon,
3324
+ priority = Priority.PRIMARY,
3325
+ type = ControlType.ACCENT,
3326
+ ...rest
3327
+ }) => {
3328
+ const classes = classNames('btn np-btn', typeClassMap[type], priorityClassMap[priority]);
3329
+ const iconElement = Number(icon.props.size) !== 24 ? /*#__PURE__*/cloneElement(icon, {
3330
+ size: 24
3331
+ }) : icon;
3332
+ return /*#__PURE__*/jsxs("label", {
3333
+ className: classNames('np-circular-btn', priority, type, disabled && 'disabled', className),
3334
+ children: [/*#__PURE__*/jsx("input", {
3335
+ type: "button",
3336
+ "aria-label": children,
3337
+ className: classes,
3338
+ disabled: disabled,
3339
+ ...rest
3340
+ }), iconElement, /*#__PURE__*/jsx(Body, {
3341
+ as: "span",
3342
+ className: "np-circular-btn__label",
3343
+ type: Typography.BODY_DEFAULT_BOLD,
3344
+ children: children
3345
+ })]
3346
+ });
3347
+ };
3348
+
2939
3349
  function CriticalCommsBanner({
2940
3350
  title,
2941
3351
  subtitle,
@@ -3049,36 +3459,6 @@ const isMonthAndYearFormat = dateString => validDateString(dateString) && dateSt
3049
3459
  const MDY = new Set(['en-US']);
3050
3460
  const YMD = new Set(['hu', 'hu-HU', 'zh-HK', 'zh-CN', 'ja', 'ja-JP']);
3051
3461
 
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
3462
  var messages$9 = defineMessages({
3083
3463
  monthLabel: {
3084
3464
  id: "neptune.DateInput.month.label"
@@ -3302,258 +3682,106 @@ const DateInput = ({
3302
3682
  name: "day",
3303
3683
  autoComplete: dayAutoComplete,
3304
3684
  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
3685
+ placeholder: placeholders?.day,
3686
+ disabled: disabled,
3687
+ min: 1,
3688
+ max: 31,
3689
+ maxLength: 2,
3690
+ onChange: event => handleDayChange(event)
3691
+ })
3692
+ })]
3693
+ })
3694
+ });
3695
+ };
3696
+ const getYear = () => {
3697
+ return /*#__PURE__*/jsx("div", {
3698
+ className: "col-sm-4",
3699
+ children: /*#__PURE__*/jsxs("label", {
3700
+ children: [/*#__PURE__*/jsx(Body, {
3701
+ type: Typography.BODY_DEFAULT,
3702
+ children: yearLabel
3703
+ }), /*#__PURE__*/jsx("div", {
3704
+ className: `input-group input-group-${size}`,
3705
+ children: /*#__PURE__*/jsx(Input, {
3706
+ type: "text",
3707
+ inputMode: "numeric",
3708
+ pattern: "[0-9]*",
3709
+ name: "year",
3710
+ autoComplete: yearAutoComplete,
3711
+ placeholder: placeholders?.year,
3712
+ value: displayYear || '',
3713
+ disabled: disabled,
3714
+ min: 0,
3715
+ max: 9999,
3716
+ maxLength: 6,
3717
+ onChange: event => handleYearChange(event)
3718
+ })
3517
3719
  })]
3518
3720
  })
3721
+ });
3722
+ };
3723
+ const monthBeforeDay = MDY.has(locale);
3724
+ const yearFirst = YMD.has(locale);
3725
+ return /*#__PURE__*/jsx("div", {
3726
+ className: "tw-date",
3727
+ ...inputAttributes,
3728
+ id: id,
3729
+ "aria-labelledby": ariaLabelledBy,
3730
+ "aria-label": ariaLabel,
3731
+ role: "group" // Add role attribute to indicate container for interactive elements
3732
+ ,
3733
+ onFocus: event => shouldPropagateOnFocus(event) ? onFocus && onFocus(event) : event.stopPropagation(),
3734
+ onBlur: event => shouldPropagateOnBlur(event) ? onBlur && onBlur(event) : event.stopPropagation(),
3735
+ children: /*#__PURE__*/jsx("div", {
3736
+ className: "row",
3737
+ children: (() => {
3738
+ if (monthYearOnly) {
3739
+ return /*#__PURE__*/jsxs(Fragment, {
3740
+ children: [!yearFirst && getMonth(), getYear(), yearFirst && getMonth()]
3741
+ });
3742
+ }
3743
+ if (monthBeforeDay) {
3744
+ return /*#__PURE__*/jsxs(Fragment, {
3745
+ children: [getMonth(), getDay(), getYear()]
3746
+ });
3747
+ }
3748
+ if (yearFirst) {
3749
+ return /*#__PURE__*/jsxs(Fragment, {
3750
+ children: [getYear(), getMonth(), getDay()]
3751
+ });
3752
+ }
3753
+ return /*#__PURE__*/jsxs(Fragment, {
3754
+ children: [getDay(), getMonth(), getYear()]
3755
+ });
3756
+ })()
3519
3757
  })
3520
3758
  });
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
- });
3759
+ };
3760
+ // Should only propagate if the relatedTarget is not part of this DateInput component.
3761
+ function shouldPropagateOnFocus({
3762
+ target,
3763
+ relatedTarget
3764
+ }) {
3765
+ const targetParent = target.closest('.tw-date');
3766
+ const relatedParent = relatedTarget && relatedTarget.closest('.tw-date');
3767
+ return targetParent !== relatedParent;
3768
+ }
3769
+ // Should only propagate if the relatedTarget or the activeElement is not part of this DateInput component.
3770
+ function shouldPropagateOnBlur({
3771
+ target,
3772
+ relatedTarget
3773
+ }) {
3774
+ const blurElementParent = target.closest('.tw-date');
3775
+ // Even though FocusEvent.relatedTarget is supported by IE
3776
+ // (https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget)
3777
+ // "IE11 sets document.activeElement to the next focused element before the blur event is called."
3778
+ // (https://stackoverflow.com/a/49325196/986241)
3779
+ // Therefore if the relatedTarget is null, we try the document.activeElement,
3780
+ // which may contain the HTML element that is gaining focus
3781
+ const focusElement = relatedTarget || (document.activeElement !== target ? document.activeElement : null);
3782
+ const focusElementParent = focusElement && focusElement.closest('.tw-date');
3783
+ return blurElementParent !== focusElementParent;
3784
+ }
3557
3785
 
3558
3786
  var dateTriggerMessages = defineMessages({
3559
3787
  ariaLabel: {
@@ -4517,39 +4745,6 @@ var DateLookup$1 = withInputAttributes(DateLookup, {
4517
4745
  nonLabelable: true
4518
4746
  });
4519
4747
 
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
4748
  function Tile({
4554
4749
  className,
4555
4750
  description,
@@ -5373,73 +5568,6 @@ const FlowNavigation = ({
5373
5568
  });
5374
5569
  };
5375
5570
 
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
5571
  // TODO: consider to move this enum into component file once we migrate it on TypeScript or replace with some common enum
5444
5572
  var InfoPresentation;
5445
5573
  (function (InfoPresentation) {
@@ -7608,18 +7736,6 @@ var MoneyInput$1 = injectIntl(withInputAttributes(MoneyInput, {
7608
7736
  nonLabelable: true
7609
7737
  }));
7610
7738
 
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
7739
  const STORAGE_NAME = 'dismissedNudges';
7624
7740
  const getLocalStorage = () => {
7625
7741
  try {
@@ -9404,6 +9520,7 @@ var en = {
9404
9520
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Select an option...",
9405
9521
  "neptune.Select.searchPlaceholder": "Search...",
9406
9522
  "neptune.SelectInput.noResultsFound": "No results found",
9523
+ "neptune.SelectOption.action.label": "Choose",
9407
9524
  "neptune.Summary.statusDone": "Item done",
9408
9525
  "neptune.Summary.statusNotDone": "Item to do",
9409
9526
  "neptune.Summary.statusPending": "Item pending",
@@ -9631,19 +9748,6 @@ function RadioOption({
9631
9748
  });
9632
9749
  }
9633
9750
 
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
9751
  const SegmentedControl = ({
9648
9752
  name,
9649
9753
  value,
@@ -14320,5 +14424,5 @@ const translations = {
14320
14424
  'zh-HK': zhHK
14321
14425
  };
14322
14426
 
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 };
14427
+ 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
14428
  //# sourceMappingURL=index.mjs.map