@sanity/ui 2.14.4-canary.2 → 2.14.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ui",
3
- "version": "2.14.4-canary.2",
3
+ "version": "2.14.4",
4
4
  "keywords": [
5
5
  "sanity",
6
6
  "ui",
@@ -61,6 +61,32 @@
61
61
  "_visual-editing.js",
62
62
  "theme.js"
63
63
  ],
64
+ "scripts": {
65
+ "build": "run-s clean pkg:build pkg:check figma:pkg:build",
66
+ "clean": "rimraf .workshop dist",
67
+ "commit": "cz",
68
+ "cypress:dev": "run-p dev cypress:open",
69
+ "cypress:open": "cypress open",
70
+ "cypress:run": "cypress run",
71
+ "dev": "run-p storybook:dev workshop:dev",
72
+ "figma:pkg:build": "cd figma && pnpm build",
73
+ "format": "prettier --write --cache --ignore-unknown .",
74
+ "lint": "eslint . --ext .cjs,.js,.jsx,.mjs,.ts,.tsx",
75
+ "pkg:build": "pkg build --strict",
76
+ "pkg:check": "pkg --strict",
77
+ "prepare": "husky install",
78
+ "prepack": "pnpm build",
79
+ "release": "semantic-release",
80
+ "storybook:build": "storybook build",
81
+ "storybook:dev": "storybook dev -p 6006",
82
+ "test": "jest",
83
+ "test:browser": "start-server-and-test 'run-s workshop:build workshop:start' http://localhost:1337 'run-s cypress:run'",
84
+ "ts:check": "tsc",
85
+ "watch": "pkg watch --strict",
86
+ "workshop:build": "workshop build",
87
+ "workshop:dev": "workshop dev",
88
+ "workshop:start": "http-server -a localhost -c-0 -p 1337 -s -P http://localhost:1337/index.html? dist"
89
+ },
64
90
  "commitlint": {
65
91
  "extends": [
66
92
  "@commitlint/config-conventional"
@@ -92,7 +118,7 @@
92
118
  "@floating-ui/react-dom": "^2.1.2",
93
119
  "@juggle/resize-observer": "^3.4.0",
94
120
  "@sanity/color": "^3.0.6",
95
- "@sanity/icons": "^3.6.0",
121
+ "@sanity/icons": "^3.7.0",
96
122
  "csstype": "^3.1.3",
97
123
  "framer-motion": "^12.4.7",
98
124
  "react-compiler-runtime": "19.0.0-beta-e1e972c-20250221",
@@ -182,37 +208,25 @@
182
208
  "react-is": "^18 || >=19.0.0-0",
183
209
  "styled-components": "^5.2 || ^6"
184
210
  },
211
+ "packageManager": "pnpm@9.15.6",
185
212
  "engines": {
186
213
  "node": ">=14.0.0"
187
214
  },
188
215
  "publishConfig": {
189
216
  "access": "public"
190
217
  },
218
+ "pnpm": {
219
+ "overrides": {
220
+ "@types/react": "$@types/react",
221
+ "@types/react-dom": "$@types/react-dom",
222
+ "@types/react-is": "$@types/react-is",
223
+ "conventional-changelog-conventionalcommits": ">= 8.0.0",
224
+ "react": "$react",
225
+ "react-dom": "$react-dom",
226
+ "react-is": "$react-is"
227
+ }
228
+ },
191
229
  "esm.sh": {
192
230
  "bundle": false
193
- },
194
- "scripts": {
195
- "build": "run-s clean pkg:build pkg:check figma:pkg:build",
196
- "clean": "rimraf .workshop dist",
197
- "commit": "cz",
198
- "cypress:dev": "run-p dev cypress:open",
199
- "cypress:open": "cypress open",
200
- "cypress:run": "cypress run",
201
- "dev": "run-p storybook:dev workshop:dev",
202
- "figma:pkg:build": "cd figma && pnpm build",
203
- "format": "prettier --write --cache --ignore-unknown .",
204
- "lint": "eslint . --ext .cjs,.js,.jsx,.mjs,.ts,.tsx",
205
- "pkg:build": "pkg build --strict",
206
- "pkg:check": "pkg --strict",
207
- "release": "semantic-release",
208
- "storybook:build": "storybook build",
209
- "storybook:dev": "storybook dev -p 6006",
210
- "test": "jest",
211
- "test:browser": "start-server-and-test 'run-s workshop:build workshop:start' http://localhost:1337 'run-s cypress:run'",
212
- "ts:check": "tsc",
213
- "watch": "pkg watch --strict",
214
- "workshop:build": "workshop build",
215
- "workshop:dev": "workshop dev",
216
- "workshop:start": "http-server -a localhost -c-0 -p 1337 -s -P http://localhost:1337/index.html? dist"
217
231
  }
218
- }
232
+ }
@@ -1,68 +1,64 @@
1
- import {styled} from 'styled-components'
1
+ import {styled, keyframes, css} from 'styled-components'
2
2
  import {ThemeColorStateToneKey, getTheme_v2} from '../../../theme'
3
- import {Card, Flex} from '../../primitives'
4
- import type {ButtonTone} from '../../types'
5
-
6
- const LOADING_BAR_HEIGHT = 2
7
-
8
- export const STATUS_CARD_TONE = {
9
- error: 'critical',
10
- warning: 'caution',
11
- success: 'positive',
12
- info: 'neutral',
13
- } satisfies {[key: string]: ThemeColorStateToneKey}
14
-
15
- export const BUTTON_TONE = {
16
- error: 'critical',
17
- warning: 'caution',
18
- success: 'positive',
19
- info: 'neutral',
20
- } satisfies {[key: string]: ButtonTone}
3
+ import {POPOVER_MOTION_CONTENT_OPACITY_PROPERTY} from '../../constants'
4
+ import {Flex} from '../../primitives'
5
+ import {ThemeProps} from '../../styles'
21
6
 
22
7
  export const TextBox = styled(Flex)`
23
8
  overflow-x: auto;
24
9
  `
25
10
 
26
- export const StyledToast = styled(Card)`
27
- pointer-events: all;
28
- width: 100%;
29
- position: relative;
30
- overflow: hidden;
31
- overflow: clip;
32
- will-change: opacity, transform;
33
-
34
- &[data-has-duration] {
35
- padding-bottom: calc(${LOADING_BAR_HEIGHT}px / 2);
11
+ const loadingAnimation = keyframes`
12
+ 0% {
13
+ width: 0;
14
+ }
15
+ 100% {
16
+ width: 100%;
36
17
  }
37
18
  `
38
19
 
39
- export const LoadingBar = styled.div`
40
- display: flex;
41
- position: absolute;
42
- bottom: 0px;
43
- top: 0px;
44
- left: 0px;
45
- right: 0px;
46
- pointer-events: none;
47
- z-index: -1;
48
- overflow: hidden;
49
- overflow: clip;
50
- background: transparent;
51
- align-items: flex-end;
52
- will-change: opacity;
53
- `
20
+ const LOADING_BAR_HEIGHT = 2
54
21
 
55
- type LoadingBarProgressProps = Omit<React.ComponentProps<typeof Card>, 'tone'> & {
56
- tone: ThemeColorStateToneKey
57
- }
58
- export const LoadingBarProgress = styled<React.ComponentType<LoadingBarProgressProps>>(Card)`
59
- display: block;
60
- height: 100%;
61
- width: 100%;
62
- transform-origin: 0% 50%;
63
- background-color: ${(props) => {
64
- const {color} = getTheme_v2(props.theme)
22
+ export function rootStyles(
23
+ props: {$duration?: number; tone: ThemeColorStateToneKey} & ThemeProps,
24
+ ): ReturnType<typeof css> {
25
+ const {color} = getTheme_v2(props.theme)
65
26
 
66
- return color.button.default[props.tone].enabled.bg
67
- }};
68
- `
27
+ const loadingBarColor = color.button.default[props.tone].enabled.bg
28
+
29
+ if (!props.$duration)
30
+ return css`
31
+ pointer-events: all;
32
+ & > * {
33
+ opacity: var(${POPOVER_MOTION_CONTENT_OPACITY_PROPERTY}, 1);
34
+ will-change: opacity;
35
+ }
36
+ `
37
+
38
+ return css`
39
+ pointer-events: all;
40
+ width: 100%;
41
+ position: relative;
42
+ overflow: hidden;
43
+ overflow: clip;
44
+ padding-bottom: ${LOADING_BAR_HEIGHT}px;
45
+ &::before {
46
+ content: '';
47
+ position: absolute;
48
+ bottom: 0px;
49
+ height: ${LOADING_BAR_HEIGHT}px;
50
+ background: ${loadingBarColor};
51
+ animation-name: ${loadingAnimation};
52
+ animation-duration: ${props.$duration}ms;
53
+ animation-fill-mode: both;
54
+ animation-timing-function: linear;
55
+ opacity: var(${POPOVER_MOTION_CONTENT_OPACITY_PROPERTY}, 1);
56
+ will-change: width;
57
+ }
58
+
59
+ & > * {
60
+ opacity: var(${POPOVER_MOTION_CONTENT_OPACITY_PROPERTY}, 1);
61
+ will-change: opacity;
62
+ }
63
+ `
64
+ }
@@ -1,16 +1,10 @@
1
1
  import {CloseIcon} from '@sanity/icons'
2
- import {motion, type Variants} from 'framer-motion'
3
- import {usePrefersReducedMotion} from '../../hooks/usePrefersReducedMotion'
2
+ import {ThemeColorStateToneKey} from '@sanity/ui/theme'
3
+ import {styled} from 'styled-components'
4
4
  import {Box, Button, Flex, Stack, Text, Card} from '../../primitives'
5
-
6
- import {
7
- LoadingBar,
8
- LoadingBarProgress,
9
- BUTTON_TONE,
10
- STATUS_CARD_TONE,
11
- TextBox,
12
- StyledToast,
13
- } from './styles'
5
+ import {ThemeProps} from '../../styles'
6
+ import type {ButtonTone} from '../../types'
7
+ import {rootStyles, TextBox} from './styles'
14
8
 
15
9
  /**
16
10
  * @public
@@ -18,14 +12,27 @@ import {
18
12
  export interface ToastProps {
19
13
  closable?: boolean
20
14
  description?: React.ReactNode
21
- onClose: () => void
15
+ onClose?: () => void
22
16
  radius?: number | number[]
23
17
  title?: React.ReactNode
24
18
  status?: 'error' | 'warning' | 'success' | 'info'
25
19
  duration?: number
26
- updatedAt?: number
27
20
  }
28
21
 
22
+ const STATUS_CARD_TONE: {[key: string]: ThemeColorStateToneKey} = {
23
+ error: 'critical',
24
+ warning: 'caution',
25
+ success: 'positive',
26
+ info: 'neutral',
27
+ } as const
28
+
29
+ const BUTTON_TONE = {
30
+ error: 'critical',
31
+ warning: 'caution',
32
+ success: 'positive',
33
+ info: 'neutral',
34
+ } satisfies {[key: string]: ButtonTone}
35
+
29
36
  const ROLES = {
30
37
  error: 'alert',
31
38
  warning: 'alert',
@@ -33,6 +40,10 @@ const ROLES = {
33
40
  info: 'alert',
34
41
  } as const
35
42
 
43
+ const StyledToast = styled(Card)<{$duration?: number; tone: ThemeColorStateToneKey} & ThemeProps>(
44
+ rootStyles,
45
+ )
46
+
36
47
  /**
37
48
  * The `Toast` component gives feedback to users when an action has taken place.
38
49
  *
@@ -41,62 +52,25 @@ const ROLES = {
41
52
  * @public
42
53
  */
43
54
  export function Toast(
44
- props: ToastProps &
45
- Omit<
46
- React.HTMLProps<HTMLDivElement>,
47
- | 'as'
48
- | 'height'
49
- | 'ref'
50
- | 'title'
51
- | 'onAnimationStart'
52
- | 'onDragStart'
53
- | 'onDragEnd'
54
- | 'onDrag'
55
- >,
55
+ props: ToastProps & Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'height' | 'ref' | 'title'>,
56
56
  ): React.JSX.Element {
57
- const {
58
- closable,
59
- description,
60
- duration,
61
- onClose,
62
- radius = 3,
63
- title,
64
- status,
65
- updatedAt,
66
- ...restProps
67
- } = props
57
+ const {closable, description, duration, onClose, radius = 3, title, status, ...restProps} = props
68
58
  const cardTone = status ? STATUS_CARD_TONE[status] : 'default'
69
59
  const buttonTone = status ? BUTTON_TONE[status] : 'default'
70
60
  const role = status ? ROLES[status] : 'status'
71
61
 
72
- const prefersReducedMotion = usePrefersReducedMotion()
73
-
74
- // eslint-disable-next-line prefer-const
75
- let visualDuration = prefersReducedMotion ? 0 : 0.26
76
- // visualDuration = 5
77
- const transition = visualDuration ? {type: 'spring', visualDuration, bounce: 0.25} : {duration: 0}
78
-
79
- const hasDuration = duration && isFinite(duration)
80
-
81
62
  return (
82
- <MotionToast
63
+ <StyledToast
83
64
  data-ui="Toast"
84
65
  role={role}
85
66
  {...restProps}
86
- data-has-duration={hasDuration ? '' : undefined}
87
- custom={visualDuration satisfies number}
67
+ marginTop={3}
88
68
  radius={radius}
89
69
  shadow={2}
90
70
  tone={cardTone}
91
- forwardedAs="li"
92
- layout="position"
93
- variants={container}
94
- initial={['hidden', 'initial'] satisfies ContainerVariants[]}
95
- animate={['visible', 'slideIn'] satisfies ContainerVariants[]}
96
- exit={['hidden', 'slideOut'] satisfies ContainerVariants[]}
97
- transition={transition}
71
+ $duration={duration}
98
72
  >
99
- <MotionFlex align="flex-start" variants={content} transition={transition}>
73
+ <Flex align="flex-start">
100
74
  <TextBox flex={1} padding={3}>
101
75
  <Stack space={3}>
102
76
  {title && (
@@ -105,9 +79,9 @@ export function Toast(
105
79
  </Text>
106
80
  )}
107
81
  {description && (
108
- <MotionText muted size={1} variants={content} transition={transition}>
82
+ <Text muted size={1}>
109
83
  {description}
110
- </MotionText>
84
+ </Text>
111
85
  )}
112
86
  </Stack>
113
87
  </TextBox>
@@ -125,76 +99,9 @@ export function Toast(
125
99
  />
126
100
  </Box>
127
101
  )}
128
- </MotionFlex>
129
- {hasDuration && (
130
- <MotionLoadiongBar variants={content} transition={transition}>
131
- <Card
132
- tone={cardTone}
133
- radius={radius}
134
- style={{
135
- position: 'absolute',
136
- right: -2,
137
- left: -2,
138
- top: 0,
139
- bottom: 2,
140
- zIndex: 1,
141
- }}
142
- />
143
- <MotionLoadiongBarProgress
144
- key={`${duration}-${updatedAt}`}
145
- tone={cardTone}
146
- initial={{scaleX: 0}}
147
- animate={{scaleX: 1}}
148
- transition={{delay: visualDuration, duration: duration / 1_000, ease: 'linear'}}
149
- onAnimationComplete={onClose}
150
- />
151
- </MotionLoadiongBar>
152
- )}
153
- </MotionToast>
102
+ </Flex>
103
+ </StyledToast>
154
104
  )
155
105
  }
156
106
 
157
107
  Toast.displayName = 'Toast'
158
-
159
- const container = {
160
- initial: {y: 32, scale: 0.5, zIndex: 1},
161
- hidden: {opacity: 0},
162
- visible: (visualDuration: number) => {
163
- if (!visualDuration) return {opacity: 1}
164
-
165
- return {
166
- opacity: 1,
167
- transition: {
168
- when: 'beforeChildren',
169
- staggerChildren: visualDuration / 3,
170
- duration: visualDuration / 3,
171
- },
172
- }
173
- },
174
- slideIn: {
175
- y: 0,
176
- scale: 1,
177
- },
178
- slideOut: {
179
- zIndex: 0,
180
- scale: 0.75,
181
- },
182
- } satisfies Variants
183
- type ContainerVariants = keyof typeof container
184
-
185
- const content = {
186
- hidden: {
187
- opacity: 0,
188
- },
189
- visible: {
190
- opacity: 1,
191
- },
192
- } satisfies Variants
193
-
194
- const MotionToast = motion.create(StyledToast)
195
- const MotionFlex = motion.create(Flex)
196
-
197
- const MotionText = motion.create(Text)
198
-
199
- const MotionLoadiongBar = motion.create(LoadingBar)
200
- const MotionLoadiongBarProgress = motion.create(LoadingBarProgress)
@@ -1,102 +1,195 @@
1
- import {AnimatePresence} from 'framer-motion'
2
- import {startTransition, useMemo, useState} from 'react'
1
+ import {AnimatePresence, motion, type Variants} from 'framer-motion'
2
+ import {useMemo, useRef, useState, startTransition, useEffect} from 'react'
3
+ import {styled} from 'styled-components'
4
+ import {POPOVER_MOTION_CONTENT_OPACITY_PROPERTY} from '../../constants'
3
5
  import {useMounted} from '../../hooks/useMounted'
4
- import {LayerProvider} from '../../utils'
6
+ import {usePrefersReducedMotion} from '../../hooks/usePrefersReducedMotion'
7
+ import {Box} from '../../primitives'
8
+ import {Layer} from '../../utils'
5
9
  import {Toast} from './toast'
6
10
  import {ToastContext} from './toastContext'
7
- import {ToastLayer, type ToastLayerProps} from './toastLayer'
8
11
  import {generateToastId} from './toastState'
9
12
  import {ToastContextValue, ToastParams} from './types'
10
13
 
11
14
  type ToastState = {
12
15
  dismiss: () => void
13
16
  id: string
14
- updatedAt: number
15
17
  params: ToastParams
16
18
  }[]
17
19
 
18
20
  /**
19
21
  * @public
20
22
  */
21
- export interface ToastProviderProps extends Omit<ToastLayerProps, 'children'> {
23
+ export interface ToastProviderProps {
22
24
  children?: React.ReactNode
25
+ padding?: number | number[]
26
+ paddingX?: number | number[]
27
+ paddingY?: number | number[]
23
28
  zOffset?: number | number[]
24
29
  }
25
30
 
31
+ const StyledToastProvider = styled(Layer)`
32
+ position: fixed;
33
+ top: 0;
34
+ left: 0;
35
+ right: 0;
36
+ bottom: 0;
37
+ pointer-events: none;
38
+ `
39
+
40
+ const ToastContainer = styled.div`
41
+ box-sizing: border-box;
42
+ position: absolute;
43
+ right: 0;
44
+ bottom: 0;
45
+ max-width: 420px;
46
+ width: 100%;
47
+ `
48
+
26
49
  /**
27
50
  * @public
28
51
  */
29
52
  export function ToastProvider(props: ToastProviderProps): React.JSX.Element {
30
- const {children, padding, paddingX, paddingY, gap, zOffset = 1} = props
31
- const [state, setState] = useState<ToastState>([])
53
+ const {children, padding = 4, paddingX, paddingY, zOffset} = props
54
+ const [state, _setState] = useState<ToastState>([])
55
+ const toastsRef = useRef<{[key: string]: {timeoutId: NodeJS.Timeout}}>({})
32
56
  const mounted = useMounted()
57
+ const prefersReducedMotion = usePrefersReducedMotion()
58
+ const variants = useMemo<Variants>(
59
+ () => ({
60
+ /**
61
+ * These variants makes use of special timing, by using a negative opacity as a starting position,
62
+ * as well as double opacity as the end position.
63
+ * The purpose of this is to make the tooltip/popover container appear before the content, and when exiting
64
+ * we want the content to disappear faster than the container.
65
+ */
66
+ initial: {
67
+ opacity: 0,
68
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY]: -1,
69
+ y: 32,
70
+ scale: 0.25,
71
+ willChange: 'transform',
72
+ },
73
+ animate: {
74
+ opacity: 2,
75
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY]: 1,
76
+ y: 0,
77
+ scale: 1,
78
+ },
79
+ exit: {
80
+ opacity: 0,
81
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY]: -1,
82
+ scale: 0.5,
83
+ },
84
+ transition: {duration: prefersReducedMotion ? 0 : 0.2},
85
+ }),
86
+ [prefersReducedMotion],
87
+ )
33
88
 
34
89
  const value: ToastContextValue = useMemo(() => {
35
90
  const push = (params: ToastParams) => {
91
+ // Wrap setState in startTransition to allow React to give input state updates higher priority
92
+ const setState: typeof _setState = (state) => startTransition(() => _setState(state))
93
+
36
94
  const id = params.id || generateToastId()
37
95
  const duration = params.duration || 5000
38
96
 
39
- startTransition(() => {
40
- setState((prevState): ToastState => {
41
- const dismiss = () => {
42
- startTransition(() => {
43
- setState((prevState): ToastState => {
44
- const idx = prevState.findIndex((t) => t.id === id)
97
+ const dismiss = () => {
98
+ const timeoutId = toastsRef.current[id]?.timeoutId
45
99
 
46
- if (idx > -1) {
47
- const toasts = prevState.slice(0)
100
+ setState((prevState): ToastState => {
101
+ const idx = prevState.findIndex((t) => t.id === id)
48
102
 
49
- toasts.splice(idx, 1)
103
+ if (idx > -1) {
104
+ const toasts = prevState.slice(0)
50
105
 
51
- return toasts
52
- }
106
+ toasts.splice(idx, 1)
53
107
 
54
- return prevState
55
- })
56
- })
108
+ return toasts
57
109
  }
58
110
 
59
111
  return prevState
60
- .filter((t) => t.id !== id)
61
- .concat([
62
- {
63
- dismiss,
64
- id,
65
- updatedAt: Date.now(),
66
- params: {...params, duration},
67
- },
68
- ])
69
112
  })
113
+
114
+ if (timeoutId !== undefined) {
115
+ clearTimeout(timeoutId)
116
+ delete toastsRef.current[id]
117
+ }
118
+ }
119
+
120
+ setState((prevState): ToastState => {
121
+ return prevState
122
+ .filter((t) => t.id !== id)
123
+ .concat([
124
+ {
125
+ dismiss,
126
+ id,
127
+ params: {...params, duration},
128
+ },
129
+ ])
70
130
  })
71
131
 
132
+ if (toastsRef.current[id]) {
133
+ clearTimeout(toastsRef.current[id].timeoutId)
134
+ delete toastsRef.current[id]
135
+ }
136
+
137
+ toastsRef.current[id] = {timeoutId: setTimeout(dismiss, duration)}
138
+
72
139
  return id
73
140
  }
74
141
 
75
142
  return {version: 0.0, push}
76
143
  }, [])
77
144
 
145
+ // clear timeouts on unmount
146
+ useEffect(
147
+ () => () => {
148
+ for (const {timeoutId} of Object.values(toastsRef.current)) {
149
+ clearTimeout(timeoutId)
150
+ }
151
+
152
+ toastsRef.current = {}
153
+ },
154
+ [],
155
+ )
156
+
78
157
  return (
79
158
  <ToastContext.Provider value={value}>
80
159
  {children}
81
160
  {mounted && (
82
- <LayerProvider zOffset={zOffset}>
83
- <ToastLayer padding={padding} paddingX={paddingX} paddingY={paddingY} gap={gap}>
84
- <AnimatePresence initial={false} mode="popLayout">
85
- {state.map(({dismiss, id, params, updatedAt}) => (
86
- <Toast
87
- key={id}
88
- closable={params.closable}
89
- description={params.description}
90
- onClose={dismiss}
91
- status={params.status}
92
- title={params.title}
93
- duration={params.duration}
94
- updatedAt={updatedAt}
95
- />
96
- ))}
97
- </AnimatePresence>
98
- </ToastLayer>
99
- </LayerProvider>
161
+ <StyledToastProvider data-ui="ToastProvider" zOffset={zOffset}>
162
+ <ToastContainer>
163
+ <Box padding={padding} paddingX={paddingX} paddingY={paddingY}>
164
+ <AnimatePresence initial={false}>
165
+ {state.map(({dismiss, id, params}) => (
166
+ <motion.div
167
+ key={id}
168
+ layout="position"
169
+ initial="initial"
170
+ animate="animate"
171
+ exit="exit"
172
+ variants={variants}
173
+ transition={
174
+ prefersReducedMotion
175
+ ? {duration: 0}
176
+ : {type: 'spring', damping: 30, stiffness: 400}
177
+ }
178
+ >
179
+ <Toast
180
+ closable={params.closable}
181
+ description={params.description}
182
+ onClose={dismiss}
183
+ status={params.status}
184
+ title={params.title}
185
+ duration={params.duration}
186
+ />
187
+ </motion.div>
188
+ ))}
189
+ </AnimatePresence>
190
+ </Box>
191
+ </ToastContainer>
192
+ </StyledToastProvider>
100
193
  )}
101
194
  </ToastContext.Provider>
102
195
  )