@react-spectrum/toast 3.0.0-nightly.3114 → 3.0.0
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/dist/Toast.main.js +6 -15
- package/dist/Toast.main.js.map +1 -1
- package/dist/Toast.mjs +6 -15
- package/dist/Toast.module.js +6 -15
- package/dist/Toast.module.js.map +1 -1
- package/dist/ToastContainer.main.js +37 -12
- package/dist/ToastContainer.main.js.map +1 -1
- package/dist/ToastContainer.mjs +38 -13
- package/dist/ToastContainer.module.js +38 -13
- package/dist/ToastContainer.module.js.map +1 -1
- package/dist/Toaster.main.js +14 -4
- package/dist/Toaster.main.js.map +1 -1
- package/dist/Toaster.mjs +15 -5
- package/dist/Toaster.module.js +15 -5
- package/dist/Toaster.module.js.map +1 -1
- package/dist/{toastContainer.da404f80.css → toastContainer.6706a095.css} +69 -45
- package/dist/toastContainer.6706a095.css.map +1 -0
- package/dist/toastContainer_css.main.js +33 -11
- package/dist/toastContainer_css.main.js.map +1 -1
- package/dist/toastContainer_css.mjs +33 -11
- package/dist/toastContainer_css.module.js +33 -11
- package/dist/toastContainer_css.module.js.map +1 -1
- package/dist/toast_vars_css.main.js +10 -10
- package/dist/toast_vars_css.mjs +10 -10
- package/dist/toast_vars_css.module.js +10 -10
- package/dist/types.d.ts +4 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/{vars.c6e65b7e.css → vars.84eec607.css} +16 -16
- package/dist/{vars.c6e65b7e.css.map → vars.84eec607.css.map} +1 -1
- package/package.json +17 -17
- package/src/Toast.tsx +3 -17
- package/src/ToastContainer.tsx +52 -24
- package/src/Toaster.tsx +12 -5
- package/src/toastContainer.css +46 -24
- package/dist/toastContainer.da404f80.css.map +0 -1
package/src/ToastContainer.tsx
CHANGED
|
@@ -14,16 +14,21 @@ import {AriaToastRegionProps} from '@react-aria/toast';
|
|
|
14
14
|
import {classNames} from '@react-spectrum/utils';
|
|
15
15
|
import {DOMProps} from '@react-types/shared';
|
|
16
16
|
import {filterDOMProps} from '@react-aria/utils';
|
|
17
|
-
import
|
|
17
|
+
import {flushSync} from 'react-dom';
|
|
18
|
+
import React, {ReactElement, useEffect, useMemo, useRef} from 'react';
|
|
18
19
|
import {SpectrumToastValue, Toast} from './Toast';
|
|
19
20
|
import toastContainerStyles from './toastContainer.css';
|
|
20
21
|
import {Toaster} from './Toaster';
|
|
21
22
|
import {ToastOptions, ToastQueue, useToastQueue} from '@react-stately/toast';
|
|
22
23
|
import {useSyncExternalStore} from 'use-sync-external-store/shim/index.js';
|
|
23
24
|
|
|
24
|
-
export
|
|
25
|
+
export type ToastPlacement = 'top' | 'top end' | 'bottom' | 'bottom end';
|
|
25
26
|
|
|
26
|
-
export interface
|
|
27
|
+
export interface SpectrumToastContainerProps extends AriaToastRegionProps {
|
|
28
|
+
placement?: ToastPlacement
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SpectrumToastOptions extends ToastOptions, DOMProps {
|
|
27
32
|
/** A label for the action button within the toast. */
|
|
28
33
|
actionLabel?: string,
|
|
29
34
|
/** Handler that is called when the action button is pressed. */
|
|
@@ -34,13 +39,23 @@ export interface SpectrumToastOptions extends Omit<ToastOptions, 'priority'>, DO
|
|
|
34
39
|
|
|
35
40
|
type CloseFunction = () => void;
|
|
36
41
|
|
|
42
|
+
function wrapInViewTransition(fn: () => void): void {
|
|
43
|
+
if ('startViewTransition' in document) {
|
|
44
|
+
document.startViewTransition(() => {
|
|
45
|
+
flushSync(fn);
|
|
46
|
+
}).ready.catch(() => {});
|
|
47
|
+
} else {
|
|
48
|
+
fn();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
// There is a single global toast queue instance for the whole app, initialized lazily.
|
|
38
53
|
let globalToastQueue: ToastQueue<SpectrumToastValue> | null = null;
|
|
39
54
|
function getGlobalToastQueue() {
|
|
40
55
|
if (!globalToastQueue) {
|
|
41
56
|
globalToastQueue = new ToastQueue({
|
|
42
57
|
maxVisibleToasts: Infinity,
|
|
43
|
-
|
|
58
|
+
wrapUpdate: wrapInViewTransition
|
|
44
59
|
});
|
|
45
60
|
}
|
|
46
61
|
|
|
@@ -77,25 +92,19 @@ function useActiveToastContainer() {
|
|
|
77
92
|
* A ToastContainer renders the queued toasts in an application. It should be placed
|
|
78
93
|
* at the root of the app.
|
|
79
94
|
*/
|
|
80
|
-
export function ToastContainer(props: SpectrumToastContainerProps): ReactElement {
|
|
95
|
+
export function ToastContainer(props: SpectrumToastContainerProps): ReactElement | null {
|
|
81
96
|
// Track all toast provider instances in a set.
|
|
82
97
|
// Only the first one will actually render.
|
|
83
98
|
// We use a ref to do this, since it will have a stable identity
|
|
84
99
|
// over the lifetime of the component.
|
|
85
|
-
let ref = useRef(
|
|
100
|
+
let ref = useRef(null);
|
|
101
|
+
|
|
86
102
|
|
|
87
|
-
// eslint-disable-next-line arrow-body-style
|
|
88
103
|
useEffect(() => {
|
|
89
104
|
toastProviders.add(ref);
|
|
90
105
|
triggerSubscriptions();
|
|
91
106
|
|
|
92
107
|
return () => {
|
|
93
|
-
// When this toast provider unmounts, reset all animations so that
|
|
94
|
-
// when the new toast provider renders, it is seamless.
|
|
95
|
-
for (let toast of getGlobalToastQueue().visibleToasts) {
|
|
96
|
-
toast.animation = null;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
108
|
// Remove this toast provider, and call subscriptions.
|
|
100
109
|
// This will cause all other instances to re-render,
|
|
101
110
|
// and the first one to become the new active toast provider.
|
|
@@ -108,19 +117,38 @@ export function ToastContainer(props: SpectrumToastContainerProps): ReactElement
|
|
|
108
117
|
let activeToastContainer = useActiveToastContainer();
|
|
109
118
|
let state = useToastQueue(getGlobalToastQueue());
|
|
110
119
|
|
|
120
|
+
let {placement, isCentered} = useMemo(() => {
|
|
121
|
+
let placements = (props.placement ?? 'bottom').split(' ');
|
|
122
|
+
let placement = placements[placements.length - 1];
|
|
123
|
+
let isCentered = placements.length === 1;
|
|
124
|
+
return {placement, isCentered};
|
|
125
|
+
}, [props.placement]);
|
|
126
|
+
|
|
111
127
|
if (ref === activeToastContainer && state.visibleToasts.length > 0) {
|
|
112
128
|
return (
|
|
113
129
|
<Toaster state={state} {...props}>
|
|
114
130
|
<ol className={classNames(toastContainerStyles, 'spectrum-ToastContainer-list')}>
|
|
115
|
-
{state.visibleToasts.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
131
|
+
{state.visibleToasts.map((toast, index) => {
|
|
132
|
+
let shouldFade = isCentered && index !== 0;
|
|
133
|
+
return (
|
|
134
|
+
<li
|
|
135
|
+
key={toast.key}
|
|
136
|
+
className={classNames(toastContainerStyles, 'spectrum-ToastContainer-listitem')}
|
|
137
|
+
style={{
|
|
138
|
+
viewTransitionName: toast.key,
|
|
139
|
+
viewTransitionClass: classNames(
|
|
140
|
+
toastContainerStyles,
|
|
141
|
+
'toast',
|
|
142
|
+
placement,
|
|
143
|
+
{'fadeOnly': shouldFade}
|
|
144
|
+
)
|
|
145
|
+
}}>
|
|
146
|
+
<Toast
|
|
147
|
+
toast={toast}
|
|
148
|
+
state={state} />
|
|
149
|
+
</li>
|
|
150
|
+
);
|
|
151
|
+
})}
|
|
124
152
|
</ol>
|
|
125
153
|
</Toaster>
|
|
126
154
|
);
|
|
@@ -144,7 +172,7 @@ function addToast(children: string, variant: SpectrumToastValue['variant'], opti
|
|
|
144
172
|
|
|
145
173
|
let shouldContinue = window.dispatchEvent(event);
|
|
146
174
|
if (!shouldContinue) {
|
|
147
|
-
return;
|
|
175
|
+
return () => {};
|
|
148
176
|
}
|
|
149
177
|
}
|
|
150
178
|
|
|
@@ -160,7 +188,7 @@ function addToast(children: string, variant: SpectrumToastValue['variant'], opti
|
|
|
160
188
|
// Minimum time of 5s from https://spectrum.adobe.com/page/toast/#Auto-dismissible
|
|
161
189
|
// Actionable toasts cannot be auto dismissed. That would fail WCAG SC 2.2.1.
|
|
162
190
|
// It is debatable whether non-actionable toasts would also fail.
|
|
163
|
-
let timeout = options.timeout && !options.onAction ? Math.max(options.timeout, 5000) :
|
|
191
|
+
let timeout = options.timeout && !options.onAction ? Math.max(options.timeout, 5000) : undefined;
|
|
164
192
|
let queue = getGlobalToastQueue();
|
|
165
193
|
let key = queue.add(value, {timeout, onClose: options.onClose});
|
|
166
194
|
return () => queue.close(key);
|
package/src/Toaster.tsx
CHANGED
|
@@ -15,15 +15,17 @@ import {classNames} from '@react-spectrum/utils';
|
|
|
15
15
|
import {FocusScope, useFocusRing} from '@react-aria/focus';
|
|
16
16
|
import {mergeProps} from '@react-aria/utils';
|
|
17
17
|
import {Provider} from '@react-spectrum/provider';
|
|
18
|
-
import React, {createContext, ReactElement, ReactNode, useRef} from 'react';
|
|
18
|
+
import React, {createContext, ReactElement, ReactNode, useMemo, useRef} from 'react';
|
|
19
19
|
import ReactDOM from 'react-dom';
|
|
20
20
|
import toastContainerStyles from './toastContainer.css';
|
|
21
|
+
import type {ToastPlacement} from './ToastContainer';
|
|
21
22
|
import {ToastState} from '@react-stately/toast';
|
|
22
23
|
import {useUNSTABLE_PortalContext} from '@react-aria/overlays';
|
|
23
24
|
|
|
24
25
|
interface ToastContainerProps extends AriaToastRegionProps {
|
|
25
26
|
children: ReactNode,
|
|
26
|
-
state: ToastState<unknown
|
|
27
|
+
state: ToastState<unknown>,
|
|
28
|
+
placement?: ToastPlacement
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export const ToasterContext = createContext(false);
|
|
@@ -34,11 +36,16 @@ export function Toaster(props: ToastContainerProps): ReactElement {
|
|
|
34
36
|
state
|
|
35
37
|
} = props;
|
|
36
38
|
|
|
37
|
-
let ref = useRef(
|
|
39
|
+
let ref = useRef(null);
|
|
38
40
|
let {regionProps} = useToastRegion(props, state, ref);
|
|
39
41
|
let {focusProps, isFocusVisible} = useFocusRing();
|
|
40
42
|
let {getContainer} = useUNSTABLE_PortalContext();
|
|
41
43
|
|
|
44
|
+
let [position, placement] = useMemo(() => {
|
|
45
|
+
let [pos = 'bottom', place = 'center'] = props.placement?.split(' ') || [];
|
|
46
|
+
return [pos, place];
|
|
47
|
+
}, [props.placement]);
|
|
48
|
+
|
|
42
49
|
let contents = (
|
|
43
50
|
<Provider UNSAFE_style={{background: 'transparent'}}>
|
|
44
51
|
<FocusScope>
|
|
@@ -46,8 +53,8 @@ export function Toaster(props: ToastContainerProps): ReactElement {
|
|
|
46
53
|
<div
|
|
47
54
|
{...mergeProps(regionProps, focusProps)}
|
|
48
55
|
ref={ref}
|
|
49
|
-
data-position=
|
|
50
|
-
data-placement=
|
|
56
|
+
data-position={position}
|
|
57
|
+
data-placement={placement}
|
|
51
58
|
className={classNames(
|
|
52
59
|
toastContainerStyles,
|
|
53
60
|
'react-spectrum-ToastContainer',
|
package/src/toastContainer.css
CHANGED
|
@@ -38,20 +38,17 @@
|
|
|
38
38
|
top: 0;
|
|
39
39
|
flex-direction: column;
|
|
40
40
|
--slide-from: translateY(-100%);
|
|
41
|
-
--slide-to: translateY(0);
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
&[data-position=bottom] {
|
|
45
44
|
bottom: 0;
|
|
46
45
|
flex-direction: column-reverse;
|
|
47
46
|
--slide-from: translateY(100%);
|
|
48
|
-
--slide-to: translateY(0);
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
&[data-placement=
|
|
49
|
+
&[data-placement=start] {
|
|
52
50
|
align-items: flex-start;
|
|
53
51
|
--slide-from: translateX(-100%);
|
|
54
|
-
--slide-to: translateX(0);
|
|
55
52
|
|
|
56
53
|
&:dir(rtl) {
|
|
57
54
|
--slide-from: translateX(100%);
|
|
@@ -62,10 +59,9 @@
|
|
|
62
59
|
align-items: center;
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
&[data-placement=
|
|
62
|
+
&[data-placement=end] {
|
|
66
63
|
align-items: flex-end;
|
|
67
64
|
--slide-from: translateX(100%);
|
|
68
|
-
--slide-to: translateX(0);
|
|
69
65
|
|
|
70
66
|
&:dir(rtl) {
|
|
71
67
|
--slide-from: translateX(-100%);
|
|
@@ -82,14 +78,6 @@
|
|
|
82
78
|
position: relative;
|
|
83
79
|
outline: none;
|
|
84
80
|
|
|
85
|
-
&[data-animation=entering] {
|
|
86
|
-
animation: slide-in 300ms;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
&[data-animation=exiting] {
|
|
90
|
-
animation: fade-out 300ms forwards;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
81
|
.spectrum-Toast-contentWrapper {
|
|
94
82
|
display: inline-flex;
|
|
95
83
|
}
|
|
@@ -100,33 +88,67 @@
|
|
|
100
88
|
flex-direction: inherit;
|
|
101
89
|
align-items: inherit;
|
|
102
90
|
list-style-type: none;
|
|
103
|
-
margin
|
|
104
|
-
margin-block-end: 0;
|
|
105
|
-
margin-inline-start: 0;
|
|
106
|
-
margin-inline-end: 0;
|
|
91
|
+
margin: 0;
|
|
107
92
|
padding-inline-start: 0;
|
|
108
93
|
}
|
|
109
94
|
|
|
110
95
|
.spectrum-ToastContainer-listitem {
|
|
111
96
|
display: inline-block;
|
|
97
|
+
view-transition-class: toast;
|
|
112
98
|
}
|
|
113
99
|
|
|
114
100
|
@keyframes slide-in {
|
|
115
101
|
from {
|
|
116
|
-
|
|
102
|
+
translate: var(--slideX) var(--slideY);
|
|
103
|
+
opacity: 0;
|
|
117
104
|
}
|
|
105
|
+
}
|
|
118
106
|
|
|
107
|
+
@keyframes slide-out {
|
|
119
108
|
to {
|
|
120
|
-
|
|
109
|
+
translate: var(--slideX) var(--slideY);
|
|
110
|
+
opacity: 0;
|
|
121
111
|
}
|
|
122
112
|
}
|
|
123
113
|
|
|
124
114
|
@keyframes fade-out {
|
|
125
|
-
from {
|
|
126
|
-
opacity: 1;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
115
|
to {
|
|
130
116
|
opacity: 0;
|
|
131
117
|
}
|
|
132
118
|
}
|
|
119
|
+
|
|
120
|
+
::view-transition-group(.toast) {
|
|
121
|
+
animation-duration: 300ms;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
::view-transition-group(.toast.bottom) {
|
|
125
|
+
--slideX: 0;
|
|
126
|
+
--slideY: calc(100% + 12px);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
::view-transition-group(.toast.top) {
|
|
130
|
+
--slideX: 0;
|
|
131
|
+
--slideY: calc(-100% - 12px);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
::view-transition-group(.toast.start) {
|
|
135
|
+
--slideX: calc(-100% - 12px);
|
|
136
|
+
--slideY: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
::view-transition-group(.toast.end) {
|
|
140
|
+
--slideX: calc(100% + 12px);
|
|
141
|
+
--slideY: 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
::view-transition-new(.toast):only-child {
|
|
145
|
+
animation-name: slide-in;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
::view-transition-old(.toast):only-child {
|
|
149
|
+
animation-name: slide-out;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
::view-transition-old(.toast.fadeOnly):only-child {
|
|
153
|
+
animation-name: fade-out;
|
|
154
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"mappings":"AAYA;;;;;;;;AAOE;;;;;;;;;;;AAqBE;;;;;AAQF;;;;AAKE;;;;;AAOJ;EACE;;;;EAIE;;;;;AAMJ;;;;;;;;;;;;;;;AAiBE;;;;;AAKA;;;;;;;AAOA;;;;;;;AAOA;;;;;;AAKE;;;;AAAA;;;;AAKF;;;;AAIA;;;;;;AAKE;;;;AAAA;;;;AAMJ;;;;;;;;AASE;;;;AAIA;;;;AAIA;;;;AAKF;;;;;;;;;;AAYA;;;;AAIA;;;;;;;;;;AAUA","sources":["packages/@react-spectrum/toast/src/toastContainer.css"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n@import \"../../../@adobe/spectrum-css-temp/components/commons/focus-ring.css\";\n\n.react-spectrum-ToastContainer {\n composes: spectrum-FocusRing;\n --spectrum-focus-ring-border-radius: var(--spectrum-toast-border-radius);\n --spectrum-focus-ring-gap: var(--spectrum-alias-focus-ring-gap);\n --spectrum-focus-ring-size: var(--spectrum-alias-focus-ring-size);\n\n position: fixed;\n inset-inline-start: 0;\n inset-inline-end: 0;\n z-index: 100050; /* above modals */\n display: flex;\n pointer-events: none;\n outline: none;\n /* keep focus ring inside the viewport */\n margin-block-end: 8px;\n margin-inline: 8px;\n\n .spectrum-Toast {\n margin: 8px;\n pointer-events: all;\n }\n\n &[data-position=top] {\n top: 0;\n flex-direction: column;\n --slide-from: translateY(-100%);\n --slide-to: translateY(0);\n }\n\n &[data-position=bottom] {\n bottom: 0;\n flex-direction: column-reverse;\n --slide-from: translateY(100%);\n --slide-to: translateY(0);\n }\n\n &[data-placement=left] {\n align-items: flex-start;\n --slide-from: translateX(-100%);\n --slide-to: translateX(0);\n\n &:dir(rtl) {\n --slide-from: translateX(100%);\n }\n }\n\n &[data-placement=center] {\n align-items: center;\n }\n\n &[data-placement=right] {\n align-items: flex-end;\n --slide-from: translateX(100%);\n --slide-to: translateX(0);\n\n &:dir(rtl) {\n --slide-from: translateX(-100%);\n }\n }\n}\n\n.spectrum-Toast {\n composes: spectrum-FocusRing;\n --spectrum-focus-ring-border-radius: var(--spectrum-toast-border-radius);\n --spectrum-focus-ring-gap: var(--spectrum-alias-focus-ring-gap);\n --spectrum-focus-ring-size: var(--spectrum-alias-focus-ring-size);\n\n position: relative;\n outline: none;\n\n &[data-animation=entering] {\n animation: slide-in 300ms;\n }\n\n &[data-animation=exiting] {\n animation: fade-out 300ms forwards;\n }\n\n .spectrum-Toast-contentWrapper {\n display: inline-flex;\n }\n}\n\n.spectrum-ToastContainer-list {\n display: inherit;\n flex-direction: inherit;\n align-items: inherit;\n list-style-type: none;\n margin-block-start: 0;\n margin-block-end: 0;\n margin-inline-start: 0;\n margin-inline-end: 0;\n padding-inline-start: 0;\n}\n\n.spectrum-ToastContainer-listitem {\n display: inline-block;\n}\n\n@keyframes slide-in {\n from {\n transform: var(--slide-from);\n }\n\n to {\n transform: var(--slide-to);\n }\n}\n\n@keyframes fade-out {\n from {\n opacity: 1;\n }\n\n to {\n opacity: 0;\n }\n}\n"],"names":[],"version":3,"file":"toastContainer.da404f80.css.map"}
|