@transferwise/components 0.0.0-experimental-4b93e2b → 0.0.0-experimental-f144cf1
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/Calendar/DateHeader/DateHeader.js +6 -2
- package/build/Calendar/DateHeader/DateHeader.js.map +1 -1
- package/build/Calendar/DateHeader/DateHeader.mjs +7 -3
- package/build/Calendar/DateHeader/DateHeader.mjs.map +1 -1
- package/build/Carousel/Carousel.js +2 -2
- package/build/Carousel/Carousel.js.map +1 -1
- package/build/Carousel/Carousel.mjs +3 -3
- package/build/Carousel/Carousel.mjs.map +1 -1
- package/build/Inputs/SelectInput/OptionContent/SelectInputOptionContent.js +3 -4
- package/build/Inputs/SelectInput/OptionContent/SelectInputOptionContent.js.map +1 -1
- package/build/Inputs/SelectInput/OptionContent/SelectInputOptionContent.mjs +3 -4
- package/build/Inputs/SelectInput/OptionContent/SelectInputOptionContent.mjs.map +1 -1
- package/build/ListItem/Navigation/ListItemNavigation.js +1 -1
- package/build/ListItem/Navigation/ListItemNavigation.js.map +1 -1
- package/build/ListItem/Navigation/ListItemNavigation.mjs +2 -2
- package/build/ListItem/Navigation/ListItemNavigation.mjs.map +1 -1
- package/build/PhoneNumberInput/PhoneNumberInput.js +1 -2
- package/build/PhoneNumberInput/PhoneNumberInput.js.map +1 -1
- package/build/PhoneNumberInput/PhoneNumberInput.mjs +1 -2
- package/build/PhoneNumberInput/PhoneNumberInput.mjs.map +1 -1
- package/build/PromoCard/Indicator/PromoCardIndicator.js +1 -1
- package/build/PromoCard/Indicator/PromoCardIndicator.js.map +1 -1
- package/build/PromoCard/Indicator/PromoCardIndicator.mjs +2 -2
- package/build/PromoCard/Indicator/PromoCardIndicator.mjs.map +1 -1
- package/build/Snackbar/Snackbar.js +212 -108
- package/build/Snackbar/Snackbar.js.map +1 -1
- package/build/Snackbar/Snackbar.messages.js +14 -0
- package/build/Snackbar/Snackbar.messages.js.map +1 -0
- package/build/Snackbar/Snackbar.messages.mjs +10 -0
- package/build/Snackbar/Snackbar.messages.mjs.map +1 -0
- package/build/Snackbar/Snackbar.mjs +213 -107
- package/build/Snackbar/Snackbar.mjs.map +1 -1
- package/build/Snackbar/SnackbarContext.js.map +1 -1
- package/build/Snackbar/SnackbarContext.mjs.map +1 -1
- package/build/Snackbar/SnackbarPortal.js +92 -0
- package/build/Snackbar/SnackbarPortal.js.map +1 -0
- package/build/Snackbar/SnackbarPortal.mjs +89 -0
- package/build/Snackbar/SnackbarPortal.mjs.map +1 -0
- package/build/Snackbar/SnackbarProvider.js +21 -31
- package/build/Snackbar/SnackbarProvider.js.map +1 -1
- package/build/Snackbar/SnackbarProvider.mjs +22 -32
- package/build/Snackbar/SnackbarProvider.mjs.map +1 -1
- package/build/Snackbar/constants.js +14 -0
- package/build/Snackbar/constants.js.map +1 -0
- package/build/Snackbar/constants.mjs +8 -0
- package/build/Snackbar/constants.mjs.map +1 -0
- package/build/Snackbar/useSnackbarAutoDismiss.js +135 -0
- package/build/Snackbar/useSnackbarAutoDismiss.js.map +1 -0
- package/build/Snackbar/useSnackbarAutoDismiss.mjs +131 -0
- package/build/Snackbar/useSnackbarAutoDismiss.mjs.map +1 -0
- package/build/Snackbar/useSnackbarManager.js +164 -0
- package/build/Snackbar/useSnackbarManager.js.map +1 -0
- package/build/Snackbar/useSnackbarManager.mjs +160 -0
- package/build/Snackbar/useSnackbarManager.mjs.map +1 -0
- package/build/Snackbar/useSnackbarSwipeDismiss.js +114 -0
- package/build/Snackbar/useSnackbarSwipeDismiss.js.map +1 -0
- package/build/Snackbar/useSnackbarSwipeDismiss.mjs +110 -0
- package/build/Snackbar/useSnackbarSwipeDismiss.mjs.map +1 -0
- package/build/common/PropsValues/typography.js +0 -6
- package/build/common/PropsValues/typography.js.map +1 -1
- package/build/common/PropsValues/typography.mjs +0 -6
- package/build/common/PropsValues/typography.mjs.map +1 -1
- package/build/i18n/en.json +1 -0
- package/build/i18n/en.json.js +1 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +1 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/index.js +3 -1
- package/build/index.js.map +1 -1
- package/build/index.mjs +2 -1
- package/build/index.mjs.map +1 -1
- package/build/main.css +864 -1509
- package/build/styles/Button/Button.css +3 -0
- package/build/styles/DateInput/DateInput.css +0 -13
- package/build/styles/Inputs/Input/Input.css +24 -0
- package/build/styles/Inputs/TextArea/TextArea.css +24 -0
- package/build/styles/ListItem/ListItem.css +1 -1
- package/build/styles/PhoneNumberInput/PhoneNumberInput.css +0 -3
- package/build/styles/Popover/Popover.css +6 -0
- package/build/styles/PromoCard/PromoCard.css +3 -0
- package/build/styles/Snackbar/Snackbar.css +182 -61
- package/build/styles/_deprecated/Select/Select.css +6 -0
- package/build/styles/css/accordion.css +12 -0
- package/build/styles/css/button-groups.css +6 -0
- package/build/styles/css/buttons.css +2 -0
- package/build/styles/css/decision.css +12 -0
- package/build/styles/css/dropdowns.css +18 -2
- package/build/styles/css/droppable.css +6 -0
- package/build/styles/css/footer.css +12 -0
- package/build/styles/css/input-groups.css +42 -2
- package/build/styles/css/list-group.css +6 -0
- package/build/styles/css/navbar.css +18 -2
- package/build/styles/css/neptune-core.css +512 -4
- package/build/styles/css/neptune.css +640 -1426
- package/build/styles/css/popovers.css +18 -0
- package/build/styles/css/tooltip.css +6 -0
- package/build/styles/less/neptune-tokens.less +0 -8
- package/build/styles/main.css +864 -1509
- package/build/styles/props/neptune-tokens.css +0 -8
- package/build/styles/styles/less/accordion.css +12 -0
- package/build/styles/styles/less/button-groups.css +6 -0
- package/build/styles/styles/less/buttons.css +2 -0
- package/build/styles/styles/less/decision.css +12 -0
- package/build/styles/styles/less/dropdowns.css +18 -2
- package/build/styles/styles/less/droppable.css +6 -0
- package/build/styles/styles/less/footer.css +12 -0
- package/build/styles/styles/less/forms/bootstrap-forms.css +24 -2
- package/build/styles/styles/less/input-groups.css +42 -2
- package/build/styles/styles/less/list-group.css +6 -0
- package/build/styles/styles/less/navbar.css +18 -2
- package/build/styles/styles/less/neptune-core.css +512 -4
- package/build/styles/styles/less/neptune.css +640 -1426
- package/build/styles/styles/less/popovers.css +18 -0
- package/build/styles/styles/less/tooltip.css +6 -0
- package/build/types/Inputs/SelectInput/OptionContent/SelectInputOptionContent.d.ts +1 -2
- package/build/types/Inputs/SelectInput/OptionContent/SelectInputOptionContent.d.ts.map +1 -1
- package/build/types/PhoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
- package/build/types/Snackbar/Snackbar.d.ts +41 -24
- package/build/types/Snackbar/Snackbar.d.ts.map +1 -1
- package/build/types/Snackbar/Snackbar.messages.d.ts +9 -0
- package/build/types/Snackbar/Snackbar.messages.d.ts.map +1 -0
- package/build/types/Snackbar/Snackbar.types.d.ts +21 -0
- package/build/types/Snackbar/Snackbar.types.d.ts.map +1 -0
- package/build/types/Snackbar/SnackbarContext.d.ts +2 -2
- package/build/types/Snackbar/SnackbarContext.d.ts.map +1 -1
- package/build/types/Snackbar/SnackbarPortal.d.ts +5 -0
- package/build/types/Snackbar/SnackbarPortal.d.ts.map +1 -0
- package/build/types/Snackbar/SnackbarProvider.d.ts +6 -0
- package/build/types/Snackbar/SnackbarProvider.d.ts.map +1 -1
- package/build/types/Snackbar/constants.d.ts +6 -0
- package/build/types/Snackbar/constants.d.ts.map +1 -0
- package/build/types/Snackbar/index.d.ts +3 -1
- package/build/types/Snackbar/index.d.ts.map +1 -1
- package/build/types/Snackbar/useSnackbar.d.ts +1 -1
- package/build/types/Snackbar/useSnackbar.d.ts.map +1 -1
- package/build/types/Snackbar/useSnackbarAutoDismiss.d.ts +18 -0
- package/build/types/Snackbar/useSnackbarAutoDismiss.d.ts.map +1 -0
- package/build/types/Snackbar/useSnackbarManager.d.ts +17 -0
- package/build/types/Snackbar/useSnackbarManager.d.ts.map +1 -0
- package/build/types/Snackbar/useSnackbarSwipeDismiss.d.ts +11 -0
- package/build/types/Snackbar/useSnackbarSwipeDismiss.d.ts.map +1 -0
- package/build/types/common/PropsValues/typography.d.ts +1 -13
- package/build/types/common/PropsValues/typography.d.ts.map +1 -1
- package/build/types/index.d.ts +2 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/test-utils/index.d.ts +2 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/Button/Button.css +3 -0
- package/src/Button/Button.less +16 -0
- package/src/Button/_stories/Button.test.story.tsx +12 -12
- package/src/Calendar/DateHeader/DateHeader.tsx +3 -3
- package/src/Carousel/Carousel.tsx +3 -3
- package/src/DateInput/DateInput.css +0 -13
- package/src/DateInput/DateInput.less +2 -10
- package/src/DateInput/_stories/DateInput.test.story.tsx +13 -0
- package/src/Display/_stories/Display.story.tsx +6 -20
- package/src/ExpressiveMoneyInput/_stories/ExpressiveMoneyInput.rtl.test.story.tsx +1 -1
- package/src/InputWithDisplayFormat/_stories/InputWithDisplayFormat.test.story.tsx +0 -52
- package/src/Inputs/Input/Input.css +24 -0
- package/src/Inputs/SelectInput/OptionContent/SelectInputOptionContent.tsx +1 -4
- package/src/Inputs/SelectInput/SelectInput.test.tsx +0 -74
- package/src/Inputs/TextArea/TextArea.css +24 -0
- package/src/ListItem/ListItem.css +1 -1
- package/src/ListItem/ListItem.grid.less +4 -4
- package/src/ListItem/ListItem.less +1 -1
- package/src/ListItem/Navigation/ListItemNavigation.tsx +2 -2
- package/src/PhoneNumberInput/PhoneNumberInput.css +0 -3
- package/src/PhoneNumberInput/PhoneNumberInput.less +2 -10
- package/src/PhoneNumberInput/PhoneNumberInput.tsx +0 -1
- package/src/PhoneNumberInput/_stories/PhoneNumberInput.rtl.test.story.tsx +1 -1
- package/src/Popover/Popover.css +6 -0
- package/src/PromoCard/Indicator/PromoCardIndicator.tsx +2 -2
- package/src/PromoCard/PromoCard.css +3 -0
- package/src/PromoCard/PromoCard.less +5 -0
- package/src/Prompt/CriticalBanner/_stories/CriticalBanner.test.story.tsx +1 -1
- package/src/Snackbar/Snackbar.css +182 -61
- package/src/Snackbar/Snackbar.less +185 -56
- package/src/Snackbar/Snackbar.messages.ts +9 -0
- package/src/Snackbar/Snackbar.test.tsx +697 -0
- package/src/Snackbar/Snackbar.tsx +227 -96
- package/src/Snackbar/Snackbar.types.ts +26 -0
- package/src/Snackbar/SnackbarContext.ts +2 -2
- package/src/Snackbar/SnackbarPortal.tsx +110 -0
- package/src/Snackbar/SnackbarProvider.test.tsx +202 -0
- package/src/Snackbar/SnackbarProvider.tsx +25 -25
- package/src/Snackbar/_stories/Snackbar.accessibility.docs.mdx +73 -0
- package/src/Snackbar/_stories/Snackbar.rtl.test.story.tsx +1 -12
- package/src/Snackbar/_stories/Snackbar.story.tsx +447 -62
- package/src/Snackbar/_stories/Snackbar.test.story.tsx +91 -16
- package/src/Snackbar/constants.ts +5 -0
- package/src/Snackbar/index.ts +3 -1
- package/src/Snackbar/useSnackbarAutoDismiss.ts +155 -0
- package/src/Snackbar/useSnackbarManager.ts +181 -0
- package/src/Snackbar/useSnackbarSwipeDismiss.ts +129 -0
- package/src/_deprecated/Select/Select.css +6 -0
- package/src/common/PropsValues/typography.ts +1 -20
- package/src/i18n/en.json +1 -1
- package/src/index.ts +7 -2
- package/src/main.css +864 -1509
- package/src/styles/less/accordion.css +12 -0
- package/src/styles/less/button-groups.css +6 -0
- package/src/styles/less/buttons.css +2 -0
- package/src/styles/less/core/_typography.less +194 -26
- package/src/styles/less/decision.css +12 -0
- package/src/styles/less/dropdowns.css +18 -2
- package/src/styles/less/droppable.css +6 -0
- package/src/styles/less/footer.css +12 -0
- package/src/styles/less/forms/bootstrap-forms.css +24 -2
- package/src/styles/less/input-groups.css +42 -2
- package/src/styles/less/list-group.css +6 -0
- package/src/styles/less/navbar.css +18 -2
- package/src/styles/less/neptune-core.css +512 -4
- package/src/styles/less/neptune.bundle.less +0 -2
- package/src/styles/less/neptune.css +640 -1426
- package/src/styles/less/popovers.css +18 -0
- package/src/styles/less/tooltip.css +6 -0
- package/src/DateInput/_stories/DateInput.rtl.test.story.tsx +0 -36
|
@@ -1,126 +1,257 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { CSSTransition } from 'react-transition-group';
|
|
3
|
+
import { useIntl } from 'react-intl';
|
|
4
|
+
import { Tags } from '@transferwise/icons';
|
|
3
5
|
|
|
4
6
|
import Button from '../Button';
|
|
7
|
+
import type { ButtonReferenceType } from '../Button/Button.types';
|
|
5
8
|
import Body from '../Body';
|
|
9
|
+
import { LiveRegion } from '../common';
|
|
6
10
|
import type { ThemeDark, ThemeLight } from '../common';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
import ProcessIndicator from '../ProcessIndicator';
|
|
12
|
+
import { PrimitivePrompt } from '../Prompt/PrimitivePrompt';
|
|
13
|
+
import type { Sentiment } from '../SentimentSurface';
|
|
14
|
+
import StatusIcon from '../StatusIcon';
|
|
15
|
+
import { SNACKBAR_ENTER_DURATION, SNACKBAR_EXIT_DURATION } from './constants';
|
|
16
|
+
import messages from './Snackbar.messages';
|
|
17
|
+
import type { SnackbarAction, SnackbarDuration, SnackbarInternalProps } from './Snackbar.types';
|
|
18
|
+
import useSnackbarAutoDismiss from './useSnackbarAutoDismiss';
|
|
19
|
+
import useSnackbarSwipeDismiss from './useSnackbarSwipeDismiss';
|
|
11
20
|
|
|
12
21
|
export interface SnackbarProps {
|
|
13
|
-
action
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
/** One optional action. Supports callbacks and navigation through the Button API. */
|
|
23
|
+
action?: SnackbarAction;
|
|
24
|
+
/**
|
|
25
|
+
* Controls auto-dismiss using the design-defined duration presets.
|
|
26
|
+
* @default 'default'
|
|
27
|
+
*/
|
|
28
|
+
duration?: SnackbarDuration;
|
|
29
|
+
/**
|
|
30
|
+
* Replaces the sentiment-derived default media.
|
|
31
|
+
* Custom media should fit the 24x24px media slot.
|
|
32
|
+
* The asset should include its own accessibility attributes if it conveys meaning.
|
|
33
|
+
*/
|
|
34
|
+
media?: React.ReactNode;
|
|
35
|
+
/**
|
|
36
|
+
* Replaces the media with a progress indicator and uses neutral sentiment styling.
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
loading?: boolean;
|
|
40
|
+
/** Called after the Snackbar completes dismissal. */
|
|
41
|
+
onDismiss?: () => void;
|
|
42
|
+
/**
|
|
43
|
+
* Determines the colour scheme and default media.
|
|
44
|
+
* @default 'neutral'
|
|
45
|
+
*/
|
|
46
|
+
sentiment?: Sentiment;
|
|
47
|
+
/** Short Snackbar message. Plain text is recommended. */
|
|
17
48
|
text: React.ReactNode;
|
|
18
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* @deprecated Theme no longer changes Snackbar appearance. Use `sentiment` for the semantic
|
|
51
|
+
* colour scheme. This prop remains available temporarily for existing implementations.
|
|
52
|
+
*/
|
|
19
53
|
theme?: ThemeLight | ThemeDark;
|
|
20
|
-
|
|
21
|
-
|
|
54
|
+
/**
|
|
55
|
+
* @deprecated Use `duration` instead. Existing millisecond values remain supported during
|
|
56
|
+
* migration: use `default` for 0–5000ms, `extended` above 5000ms, and `indefinite` only when
|
|
57
|
+
* the existing usage is intentionally persistent. `duration` takes precedence when both are
|
|
58
|
+
* provided.
|
|
59
|
+
*/
|
|
60
|
+
timeout?: number;
|
|
22
61
|
}
|
|
23
62
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
63
|
+
export const SnackbarInternal = ({
|
|
64
|
+
action,
|
|
65
|
+
announcementKey,
|
|
66
|
+
duration,
|
|
67
|
+
loading = false,
|
|
68
|
+
media,
|
|
69
|
+
onDismissRequest,
|
|
70
|
+
onExited,
|
|
71
|
+
sentiment = 'neutral',
|
|
72
|
+
text,
|
|
73
|
+
timeout,
|
|
74
|
+
visible,
|
|
75
|
+
}: SnackbarInternalProps) => {
|
|
76
|
+
const { formatMessage } = useIntl();
|
|
77
|
+
const {
|
|
78
|
+
clearSwipePosition,
|
|
79
|
+
containerRef,
|
|
80
|
+
handleClickCapture,
|
|
81
|
+
handlePointerCancel,
|
|
82
|
+
handlePointerDown,
|
|
83
|
+
handlePointerMove,
|
|
84
|
+
handlePointerUp,
|
|
85
|
+
} = useSnackbarSwipeDismiss(onDismissRequest);
|
|
86
|
+
const { handleBlur, handleFocus, handleMouseEnter, handleMouseLeave, surfaceRef } =
|
|
87
|
+
useSnackbarAutoDismiss({
|
|
88
|
+
duration,
|
|
89
|
+
loading,
|
|
90
|
+
onDismissRequest,
|
|
91
|
+
timeout,
|
|
92
|
+
visible,
|
|
93
|
+
resetKey: announcementKey,
|
|
94
|
+
});
|
|
27
95
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
96
|
+
const handleExited = () => {
|
|
97
|
+
clearSwipePosition();
|
|
98
|
+
onExited();
|
|
99
|
+
};
|
|
32
100
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
101
|
+
const handleActionClick: React.MouseEventHandler<ButtonReferenceType> = (event) => {
|
|
102
|
+
const handleClick: React.MouseEventHandler<ButtonReferenceType> | undefined = action?.onClick;
|
|
103
|
+
try {
|
|
104
|
+
handleClick?.(event);
|
|
105
|
+
} finally {
|
|
106
|
+
onDismissRequest();
|
|
107
|
+
}
|
|
108
|
+
};
|
|
40
109
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
110
|
+
const renderMedia = () => {
|
|
111
|
+
if (loading) {
|
|
112
|
+
return (
|
|
113
|
+
<ProcessIndicator
|
|
114
|
+
className="wds-Snackbar__process-indicator"
|
|
115
|
+
data-testid="Snackbar_ProcessIndicator"
|
|
116
|
+
size="xs"
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
45
120
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return false;
|
|
121
|
+
if (media) {
|
|
122
|
+
return <span className="wds-Snackbar__media-asset">{media}</span>;
|
|
49
123
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
124
|
+
|
|
125
|
+
if (sentiment === 'proposition') {
|
|
126
|
+
return (
|
|
127
|
+
<span className="wds-Snackbar__media-asset">
|
|
128
|
+
<Tags aria-hidden size={24} />
|
|
129
|
+
</span>
|
|
130
|
+
);
|
|
56
131
|
}
|
|
57
132
|
|
|
58
|
-
return
|
|
59
|
-
}
|
|
133
|
+
return <StatusIcon sentiment={sentiment} size={24} />;
|
|
134
|
+
};
|
|
60
135
|
|
|
61
|
-
|
|
62
|
-
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (visible) {
|
|
138
|
+
clearSwipePosition();
|
|
139
|
+
}
|
|
140
|
+
}, [clearSwipePosition, visible]);
|
|
63
141
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (!visible) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
68
146
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (!previousProps.text) {
|
|
73
|
-
this.setState({ visible: true, action, text }, () => {
|
|
74
|
-
this.setLeaveTimeout();
|
|
75
|
-
});
|
|
76
|
-
} else if (previousProps.timestamp !== timestamp) {
|
|
77
|
-
window.clearTimeout(this.timeout);
|
|
78
|
-
|
|
79
|
-
if (this.state.visible) {
|
|
80
|
-
this.setState({ visible: false }, () => {
|
|
81
|
-
this.transitionTimeout = window.setTimeout(() => {
|
|
82
|
-
this.setState({ visible: true, action, text });
|
|
83
|
-
this.setLeaveTimeout();
|
|
84
|
-
}, CSS_TRANSITION_DURATION);
|
|
85
|
-
});
|
|
86
|
-
} else {
|
|
87
|
-
this.setState({ visible: true, action, text });
|
|
88
|
-
this.setLeaveTimeout();
|
|
147
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
148
|
+
if (event.key === 'Escape' && !event.defaultPrevented) {
|
|
149
|
+
onDismissRequest();
|
|
89
150
|
}
|
|
90
|
-
}
|
|
91
|
-
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
154
|
+
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
155
|
+
}, [onDismissRequest, visible]);
|
|
92
156
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
157
|
+
return (
|
|
158
|
+
<div ref={containerRef} className="wds-Snackbar">
|
|
159
|
+
<CSSTransition
|
|
160
|
+
appear
|
|
161
|
+
nodeRef={surfaceRef}
|
|
162
|
+
in={visible}
|
|
163
|
+
classNames="wds-Snackbar-container"
|
|
164
|
+
timeout={{
|
|
165
|
+
appear: SNACKBAR_ENTER_DURATION,
|
|
166
|
+
enter: SNACKBAR_ENTER_DURATION,
|
|
167
|
+
exit: SNACKBAR_EXIT_DURATION,
|
|
168
|
+
}}
|
|
169
|
+
unmountOnExit
|
|
170
|
+
onExited={handleExited}
|
|
171
|
+
>
|
|
172
|
+
<PrimitivePrompt
|
|
173
|
+
ref={surfaceRef}
|
|
174
|
+
className="wds-Snackbar-container"
|
|
175
|
+
sentiment={loading ? 'neutral' : sentiment}
|
|
176
|
+
emphasis="elevated"
|
|
177
|
+
media={renderMedia()}
|
|
178
|
+
onDismiss={onDismissRequest}
|
|
179
|
+
onBlur={handleBlur}
|
|
180
|
+
onFocus={handleFocus}
|
|
181
|
+
onMouseEnter={handleMouseEnter}
|
|
182
|
+
onMouseLeave={handleMouseLeave}
|
|
183
|
+
onClickCapture={handleClickCapture}
|
|
184
|
+
onPointerCancel={handlePointerCancel}
|
|
185
|
+
onPointerDown={handlePointerDown}
|
|
186
|
+
onPointerMove={handlePointerMove}
|
|
187
|
+
onPointerUp={handlePointerUp}
|
|
109
188
|
>
|
|
110
|
-
<
|
|
111
|
-
|
|
189
|
+
<div className="wds-Snackbar__content">
|
|
190
|
+
<LiveRegion
|
|
191
|
+
aria-live="polite"
|
|
192
|
+
announceOnChange={announcementKey}
|
|
193
|
+
className="wds-Snackbar__message"
|
|
194
|
+
>
|
|
195
|
+
<Body as="span" className="wds-Snackbar__body">
|
|
196
|
+
{text}
|
|
197
|
+
<span className="sr-only"> {formatMessage(messages.escapeDismissHint)}</span>
|
|
198
|
+
</Body>
|
|
199
|
+
</LiveRegion>
|
|
112
200
|
{action ? (
|
|
113
|
-
<Button
|
|
201
|
+
<Button
|
|
202
|
+
className="wds-Snackbar__action"
|
|
203
|
+
v2
|
|
204
|
+
size="sm"
|
|
205
|
+
priority="secondary"
|
|
206
|
+
href={action.href}
|
|
207
|
+
target={action.target}
|
|
208
|
+
onClick={handleActionClick}
|
|
209
|
+
>
|
|
114
210
|
{action.label}
|
|
115
211
|
</Button>
|
|
116
212
|
) : null}
|
|
117
|
-
</
|
|
118
|
-
</
|
|
119
|
-
</
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
213
|
+
</div>
|
|
214
|
+
</PrimitivePrompt>
|
|
215
|
+
</CSSTransition>
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export const Snackbar = ({ onDismiss, ...props }: SnackbarProps) => {
|
|
221
|
+
const [mounted, setMounted] = useState(true);
|
|
222
|
+
const [visible, setVisible] = useState(true);
|
|
223
|
+
const [announcementKey, setAnnouncementKey] = useState(0);
|
|
224
|
+
const previousText = useRef(props.text);
|
|
225
|
+
const handleDismissRequest = useCallback(() => setVisible(false), []);
|
|
226
|
+
const handleExited = useCallback(() => {
|
|
227
|
+
setMounted(false);
|
|
228
|
+
onDismiss?.();
|
|
229
|
+
}, [onDismiss]);
|
|
230
|
+
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
if (previousText.current === props.text) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
previousText.current = props.text;
|
|
237
|
+
// Direct Snackbars do not have a Provider-generated instance id, so text changes advance this
|
|
238
|
+
// key to schedule a fresh announcement and restart the semantic duration.
|
|
239
|
+
setMounted(true);
|
|
240
|
+
setVisible(true);
|
|
241
|
+
setAnnouncementKey((key) => key + 1);
|
|
242
|
+
}, [props.text]);
|
|
123
243
|
|
|
124
|
-
|
|
244
|
+
if (!mounted) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
125
247
|
|
|
126
|
-
|
|
248
|
+
return (
|
|
249
|
+
<SnackbarInternal
|
|
250
|
+
{...props}
|
|
251
|
+
announcementKey={announcementKey}
|
|
252
|
+
visible={visible}
|
|
253
|
+
onDismissRequest={handleDismissRequest}
|
|
254
|
+
onExited={handleExited}
|
|
255
|
+
/>
|
|
256
|
+
);
|
|
257
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ButtonProps } from '../Button/Button.types';
|
|
2
|
+
import type { SnackbarProps } from './Snackbar';
|
|
3
|
+
|
|
4
|
+
export type { SnackbarProps } from './Snackbar';
|
|
5
|
+
|
|
6
|
+
export type SnackbarAction = Pick<ButtonProps, 'onClick' | 'href' | 'target'> & {
|
|
7
|
+
label: ButtonProps['children'];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type SnackbarDuration = 'default' | 'extended' | 'indefinite';
|
|
11
|
+
|
|
12
|
+
/** Props accepted when creating a Snackbar through SnackbarProvider. */
|
|
13
|
+
export type SnackbarCreationProps = Omit<SnackbarProps, 'timeout'>;
|
|
14
|
+
|
|
15
|
+
export interface SnackbarInternalProps extends Omit<SnackbarProps, 'onDismiss'> {
|
|
16
|
+
announcementKey: string | number;
|
|
17
|
+
onDismissRequest: () => void;
|
|
18
|
+
onExited: () => void;
|
|
19
|
+
visible: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Compatibility props for the existing SnackbarPortal export. */
|
|
23
|
+
export interface SnackbarPortalProps extends SnackbarProps {
|
|
24
|
+
/** @internal Legacy notification identity retained only for migration compatibility. */
|
|
25
|
+
timestamp?: number;
|
|
26
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type { SnackbarCreationProps } from './Snackbar.types';
|
|
4
4
|
|
|
5
5
|
export type SnackbarContextType = {
|
|
6
|
-
createSnackbar: (props:
|
|
6
|
+
createSnackbar: (props: SnackbarCreationProps) => void;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
export const SnackbarContext = createContext<SnackbarContextType>({ createSnackbar: () => {} });
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import withNextPortal from '../WithNextPortal/WithNextPortal';
|
|
4
|
+
import { SnackbarInternal } from './Snackbar';
|
|
5
|
+
import type { SnackbarPortalProps } from './Snackbar.types';
|
|
6
|
+
import useSnackbarManager from './useSnackbarManager';
|
|
7
|
+
|
|
8
|
+
export const PortalledSnackbar = withNextPortal(SnackbarInternal);
|
|
9
|
+
|
|
10
|
+
const hasSnackbarText = (text: SnackbarPortalProps['text']) =>
|
|
11
|
+
text !== null && text !== undefined && text !== '';
|
|
12
|
+
|
|
13
|
+
export const SnackbarPortal = ({
|
|
14
|
+
action,
|
|
15
|
+
duration,
|
|
16
|
+
loading,
|
|
17
|
+
media,
|
|
18
|
+
onDismiss,
|
|
19
|
+
sentiment,
|
|
20
|
+
text,
|
|
21
|
+
theme,
|
|
22
|
+
timeout,
|
|
23
|
+
timestamp,
|
|
24
|
+
}: SnackbarPortalProps) => {
|
|
25
|
+
const hasText = hasSnackbarText(text);
|
|
26
|
+
const previousTimestamp = useRef(timestamp);
|
|
27
|
+
const previousText = useRef(text);
|
|
28
|
+
const isInitialRender = useRef(true);
|
|
29
|
+
const {
|
|
30
|
+
active,
|
|
31
|
+
announcementKey,
|
|
32
|
+
dismissSnackbar,
|
|
33
|
+
handleExited,
|
|
34
|
+
showSnackbar,
|
|
35
|
+
updateSnackbar,
|
|
36
|
+
visible,
|
|
37
|
+
} = useSnackbarManager(
|
|
38
|
+
hasText
|
|
39
|
+
? { action, duration, loading, media, onDismiss, sentiment, text, theme, timeout }
|
|
40
|
+
: undefined,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (isInitialRender.current) {
|
|
45
|
+
isInitialRender.current = false;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!hasText) {
|
|
50
|
+
dismissSnackbar();
|
|
51
|
+
previousTimestamp.current = timestamp;
|
|
52
|
+
previousText.current = text;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const snackbar = {
|
|
57
|
+
action,
|
|
58
|
+
duration,
|
|
59
|
+
loading,
|
|
60
|
+
media,
|
|
61
|
+
onDismiss,
|
|
62
|
+
sentiment,
|
|
63
|
+
text,
|
|
64
|
+
theme,
|
|
65
|
+
timeout,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (timestamp === undefined) {
|
|
69
|
+
// Without the legacy identity prop, update in place so recreated callbacks/media do not
|
|
70
|
+
// repeatedly restart the exit animation or auto-dismiss timer.
|
|
71
|
+
updateSnackbar(snackbar, !hasSnackbarText(previousText.current));
|
|
72
|
+
} else if (!hasSnackbarText(previousText.current) || previousTimestamp.current !== timestamp) {
|
|
73
|
+
showSnackbar(snackbar);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
previousTimestamp.current = timestamp;
|
|
77
|
+
previousText.current = text;
|
|
78
|
+
}, [
|
|
79
|
+
dismissSnackbar,
|
|
80
|
+
showSnackbar,
|
|
81
|
+
action,
|
|
82
|
+
duration,
|
|
83
|
+
hasText,
|
|
84
|
+
loading,
|
|
85
|
+
media,
|
|
86
|
+
onDismiss,
|
|
87
|
+
sentiment,
|
|
88
|
+
text,
|
|
89
|
+
theme,
|
|
90
|
+
timeout,
|
|
91
|
+
timestamp,
|
|
92
|
+
updateSnackbar,
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
if (!active) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<PortalledSnackbar
|
|
101
|
+
{...active.props}
|
|
102
|
+
announcementKey={announcementKey}
|
|
103
|
+
visible={visible}
|
|
104
|
+
onDismissRequest={dismissSnackbar}
|
|
105
|
+
onExited={handleExited}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export default SnackbarPortal;
|