@react-spectrum/toast 3.0.0-beta.12 → 3.0.0-beta.13

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/src/Toast.tsx CHANGED
@@ -15,16 +15,16 @@ import {Button, ClearButton} from '@react-spectrum/button';
15
15
  import {classNames, useDOMRef, useStyleProps} from '@react-spectrum/utils';
16
16
  import CrossMedium from '@spectrum-icons/ui/CrossMedium';
17
17
  import {DOMProps, DOMRef} from '@react-types/shared';
18
- import {filterDOMProps} from '@react-aria/utils';
18
+ import {filterDOMProps, mergeProps} from '@react-aria/utils';
19
19
  import InfoMedium from '@spectrum-icons/ui/InfoMedium';
20
20
  // @ts-ignore
21
21
  import intlMessages from '../intl/*.json';
22
22
  import {QueuedToast, ToastState} from '@react-stately/toast';
23
- import React, {useContext} from 'react';
23
+ import React from 'react';
24
24
  import styles from '@adobe/spectrum-css-temp/components/toast/vars.css';
25
25
  import SuccessMedium from '@spectrum-icons/ui/SuccessMedium';
26
26
  import toastContainerStyles from './toastContainer.css';
27
- import {ToasterContext} from './Toaster';
27
+ import {useFocusRing} from '@react-aria/focus';
28
28
  import {useLocalizedStringFormatter} from '@react-aria/i18n';
29
29
  import {useToast} from '@react-aria/toast';
30
30
 
@@ -68,14 +68,15 @@ function Toast(props: SpectrumToastProps, ref: DOMRef<HTMLDivElement>) {
68
68
  let {
69
69
  closeButtonProps,
70
70
  titleProps,
71
- toastProps
71
+ toastProps,
72
+ contentProps
72
73
  } = useToast(props, state, domRef);
73
74
  let {styleProps} = useStyleProps(otherProps);
74
75
 
75
76
  let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/toast');
76
77
  let iconLabel = variant && variant !== 'neutral' ? stringFormatter.format(variant) : null;
77
78
  let Icon = ICONS[variant];
78
- let isFocusVisible = useContext(ToasterContext);
79
+ let {isFocusVisible, focusProps} = useFocusRing();
79
80
 
80
81
  const handleAction = () => {
81
82
  if (onAction) {
@@ -90,7 +91,7 @@ function Toast(props: SpectrumToastProps, ref: DOMRef<HTMLDivElement>) {
90
91
  return (
91
92
  <div
92
93
  {...styleProps}
93
- {...toastProps}
94
+ {...mergeProps(toastProps, focusProps)}
94
95
  {...filterDOMProps(props.toast.content)}
95
96
  ref={domRef}
96
97
  className={classNames(styles,
@@ -100,7 +101,7 @@ function Toast(props: SpectrumToastProps, ref: DOMRef<HTMLDivElement>) {
100
101
  classNames(
101
102
  toastContainerStyles,
102
103
  'spectrum-Toast',
103
- {'focus-ring': props.toast.key === state.visibleToasts[0]?.key && isFocusVisible}
104
+ {'focus-ring': isFocusVisible}
104
105
  )
105
106
  )}
106
107
  style={{
@@ -113,22 +114,26 @@ function Toast(props: SpectrumToastProps, ref: DOMRef<HTMLDivElement>) {
113
114
  state.remove(key);
114
115
  }
115
116
  }}>
116
- {Icon &&
117
- <Icon
118
- aria-label={iconLabel}
119
- UNSAFE_className={classNames(styles, 'spectrum-Toast-typeIcon')} />
120
- }
121
- <div className={classNames(styles, 'spectrum-Toast-body')}>
122
- <div className={classNames(styles, 'spectrum-Toast-content')} {...titleProps}>{children}</div>
123
- {actionLabel &&
124
- <Button
125
- onPress={handleAction}
126
- UNSAFE_className={classNames(styles, 'spectrum-Button')}
127
- variant="secondary"
128
- staticColor="white">
129
- {actionLabel}
130
- </Button>
117
+ <div
118
+ {...contentProps}
119
+ className={classNames(toastContainerStyles, 'spectrum-Toast-contentWrapper')}>
120
+ {Icon &&
121
+ <Icon
122
+ aria-label={iconLabel}
123
+ UNSAFE_className={classNames(styles, 'spectrum-Toast-typeIcon')} />
131
124
  }
125
+ <div className={classNames(styles, 'spectrum-Toast-body')} role="presentation">
126
+ <div className={classNames(styles, 'spectrum-Toast-content')} role="presentation" {...titleProps}>{children}</div>
127
+ {actionLabel &&
128
+ <Button
129
+ onPress={handleAction}
130
+ UNSAFE_className={classNames(styles, 'spectrum-Button')}
131
+ variant="secondary"
132
+ staticColor="white">
133
+ {actionLabel}
134
+ </Button>
135
+ }
136
+ </div>
132
137
  </div>
133
138
  <div className={classNames(styles, 'spectrum-Toast-buttons')}>
134
139
  <ClearButton {...closeButtonProps} variant="overBackground">
@@ -11,10 +11,12 @@
11
11
  */
12
12
 
13
13
  import {AriaToastRegionProps} from '@react-aria/toast';
14
+ import {classNames} from '@react-spectrum/utils';
14
15
  import {DOMProps} from '@react-types/shared';
15
16
  import {filterDOMProps} from '@react-aria/utils';
16
17
  import React, {ReactElement, useEffect, useRef} from 'react';
17
18
  import {SpectrumToastValue, Toast} from './Toast';
19
+ import toastContainerStyles from './toastContainer.css';
18
20
  import {Toaster} from './Toaster';
19
21
  import {ToastOptions, ToastQueue, useToastQueue} from '@react-stately/toast';
20
22
  import {useSyncExternalStore} from 'use-sync-external-store/shim/index.js';
@@ -80,7 +82,7 @@ export function ToastContainer(props: SpectrumToastContainerProps): ReactElement
80
82
  // Only the first one will actually render.
81
83
  // We use a ref to do this, since it will have a stable identity
82
84
  // over the lifetime of the component.
83
- let ref = useRef();
85
+ let ref = useRef(undefined);
84
86
 
85
87
  // eslint-disable-next-line arrow-body-style
86
88
  useEffect(() => {
@@ -105,15 +107,21 @@ export function ToastContainer(props: SpectrumToastContainerProps): ReactElement
105
107
  // Only render if this is the active toast provider instance, and there are visible toasts.
106
108
  let activeToastContainer = useActiveToastContainer();
107
109
  let state = useToastQueue(getGlobalToastQueue());
110
+
108
111
  if (ref === activeToastContainer && state.visibleToasts.length > 0) {
109
112
  return (
110
113
  <Toaster state={state} {...props}>
111
- {state.visibleToasts.map((toast) => (
112
- <Toast
113
- key={toast.key}
114
- toast={toast}
115
- state={state} />
116
- ))}
114
+ <ol className={classNames(toastContainerStyles, 'spectrum-ToastContainer-list')}>
115
+ {state.visibleToasts.slice().reverse().map((toast) => (
116
+ <li
117
+ key={toast.key}
118
+ className={classNames(toastContainerStyles, 'spectrum-ToastContainer-listitem')}>
119
+ <Toast
120
+ toast={toast}
121
+ state={state} />
122
+ </li>
123
+ ))}
124
+ </ol>
117
125
  </Toaster>
118
126
  );
119
127
  }
package/src/Toaster.tsx CHANGED
@@ -12,13 +12,13 @@
12
12
 
13
13
  import {AriaToastRegionProps, useToastRegion} from '@react-aria/toast';
14
14
  import {classNames} from '@react-spectrum/utils';
15
+ import {FocusScope, useFocusRing} from '@react-aria/focus';
15
16
  import {mergeProps} from '@react-aria/utils';
16
17
  import {Provider} from '@react-spectrum/provider';
17
18
  import React, {createContext, ReactElement, ReactNode, useRef} from 'react';
18
19
  import ReactDOM from 'react-dom';
19
20
  import toastContainerStyles from './toastContainer.css';
20
21
  import {ToastState} from '@react-stately/toast';
21
- import {useFocusRing} from '@react-aria/focus';
22
22
  import {useUNSTABLE_PortalContext} from '@react-aria/overlays';
23
23
 
24
24
  interface ToastContainerProps extends AriaToastRegionProps {
@@ -34,26 +34,29 @@ export function Toaster(props: ToastContainerProps): ReactElement {
34
34
  state
35
35
  } = props;
36
36
 
37
- let ref = useRef();
37
+ let ref = useRef(undefined);
38
38
  let {regionProps} = useToastRegion(props, state, ref);
39
39
  let {focusProps, isFocusVisible} = useFocusRing();
40
40
  let {getContainer} = useUNSTABLE_PortalContext();
41
41
 
42
42
  let contents = (
43
43
  <Provider UNSAFE_style={{background: 'transparent'}}>
44
- <ToasterContext.Provider value={isFocusVisible}>
45
- <div
46
- {...mergeProps(regionProps, focusProps)}
47
- ref={ref}
48
- data-position="bottom"
49
- data-placement="center"
50
- className={classNames(
51
- toastContainerStyles,
52
- 'react-spectrum-ToastContainer'
53
- )}>
54
- {children}
55
- </div>
56
- </ToasterContext.Provider>
44
+ <FocusScope>
45
+ <ToasterContext.Provider value={isFocusVisible}>
46
+ <div
47
+ {...mergeProps(regionProps, focusProps)}
48
+ ref={ref}
49
+ data-position="bottom"
50
+ data-placement="center"
51
+ className={classNames(
52
+ toastContainerStyles,
53
+ 'react-spectrum-ToastContainer',
54
+ {'focus-ring': isFocusVisible}
55
+ )}>
56
+ {children}
57
+ </div>
58
+ </ToasterContext.Provider>
59
+ </FocusScope>
57
60
  </Provider>
58
61
  );
59
62
 
@@ -13,6 +13,11 @@
13
13
  @import "../../../@adobe/spectrum-css-temp/components/commons/focus-ring.css";
14
14
 
15
15
  .react-spectrum-ToastContainer {
16
+ composes: spectrum-FocusRing;
17
+ --spectrum-focus-ring-border-radius: var(--spectrum-toast-border-radius);
18
+ --spectrum-focus-ring-gap: var(--spectrum-alias-focus-ring-gap);
19
+ --spectrum-focus-ring-size: var(--spectrum-alias-focus-ring-size);
20
+
16
21
  position: fixed;
17
22
  inset-inline-start: 0;
18
23
  inset-inline-end: 0;
@@ -20,7 +25,9 @@
20
25
  display: flex;
21
26
  pointer-events: none;
22
27
  outline: none;
28
+ /* keep focus ring inside the viewport */
23
29
  margin-block-end: 8px;
30
+ margin-inline: 8px;
24
31
 
25
32
  .spectrum-Toast {
26
33
  margin: 8px;
@@ -29,14 +36,14 @@
29
36
 
30
37
  &[data-position=top] {
31
38
  top: 0;
32
- flex-direction: column-reverse;
39
+ flex-direction: column;
33
40
  --slide-from: translateY(-100%);
34
41
  --slide-to: translateY(0);
35
42
  }
36
43
 
37
44
  &[data-position=bottom] {
38
45
  bottom: 0;
39
- flex-direction: column;
46
+ flex-direction: column-reverse;
40
47
  --slide-from: translateY(100%);
41
48
  --slide-to: translateY(0);
42
49
  }
@@ -72,6 +79,9 @@
72
79
  --spectrum-focus-ring-gap: var(--spectrum-alias-focus-ring-gap);
73
80
  --spectrum-focus-ring-size: var(--spectrum-alias-focus-ring-size);
74
81
 
82
+ position: relative;
83
+ outline: none;
84
+
75
85
  &[data-animation=entering] {
76
86
  animation: slide-in 300ms;
77
87
  }
@@ -79,6 +89,26 @@
79
89
  &[data-animation=exiting] {
80
90
  animation: fade-out 300ms forwards;
81
91
  }
92
+
93
+ .spectrum-Toast-contentWrapper {
94
+ display: inline-flex;
95
+ }
96
+ }
97
+
98
+ .spectrum-ToastContainer-list {
99
+ display: inherit;
100
+ flex-direction: inherit;
101
+ align-items: inherit;
102
+ list-style-type: none;
103
+ margin-block-start: 0;
104
+ margin-block-end: 0;
105
+ margin-inline-start: 0;
106
+ margin-inline-end: 0;
107
+ padding-inline-start: 0;
108
+ }
109
+
110
+ .spectrum-ToastContainer-listitem {
111
+ display: inline-block;
82
112
  }
83
113
 
84
114
  @keyframes slide-in {
@@ -1 +0,0 @@
1
- {"mappings":"AAYA;;;;;;;;AAOE;;;;;;;;;;;AAqBE;;;;;AAQF;;;;AAKE;;;;;AAOJ;EACE;;;;EAIE;;;;;AAMJ;;;;;;;;;;;AAUE;;;;;AAKA;;;;;;;AAOA;;;;;;;AAOA;;;;;;AAKE;;;;AAAA;;;;AAKF;;;;AAIA;;;;;;AAKE;;;;AAAA;;;;AAMJ;;;;;;AAME;;;;AAIA;;;;AAKF;;;;;;;;;;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 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 margin-block-end: 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-reverse;\n --slide-from: translateY(-100%);\n --slide-to: translateY(0);\n }\n\n &[data-position=bottom] {\n bottom: 0;\n flex-direction: column;\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 &[data-animation=entering] {\n animation: slide-in 300ms;\n }\n\n &[data-animation=exiting] {\n animation: fade-out 300ms forwards;\n }\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.b596792c.css.map"}
@@ -1 +0,0 @@
1
- {"mappings":"AAwBA;;;;;;;;;;;;;;;AAkBA;;;;;;;;;AAUA;;;;;;;;;AASA;;;;;;AAOI;;;;AAOJ;;;;;;;;;;AAWE;;;;;;AAoBF;;;;;AAKA;;;;AAIA;;;;AAIA;;;;AAIA;;;;AAIA;;;;AAIA;;;;AAIA;EACE","sources":["packages/@adobe/spectrum-css-temp/components/toast/vars.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 './index.css';\n@import './skin.css';\n"],"names":[],"version":3,"file":"vars.85cde188.css.map"}