@sanity/ui 2.14.4-canary.3 → 2.14.5

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.3",
3
+ "version": "2.14.5",
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,10 +118,10 @@
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
- "react-compiler-runtime": "19.0.0-beta-e1e972c-20250221",
124
+ "react-compiler-runtime": "19.0.0-beta-40c6c23-20250301",
99
125
  "react-refractor": "^2.2.0",
100
126
  "use-effect-event": "^1.0.2"
101
127
  },
@@ -138,7 +164,7 @@
138
164
  "@typescript-eslint/eslint-plugin": "^7.18.0",
139
165
  "@typescript-eslint/parser": "^7.18.0",
140
166
  "@vitejs/plugin-react": "^4.3.4",
141
- "babel-plugin-react-compiler": "19.0.0-beta-e1e972c-20250221",
167
+ "babel-plugin-react-compiler": "19.0.0-beta-40c6c23-20250301",
142
168
  "babel-plugin-styled-components": "^2.1.4",
143
169
  "commitizen": "^4.3.1",
144
170
  "cypress": "^13.17.0",
@@ -150,7 +176,7 @@
150
176
  "eslint-plugin-import": "^2.31.0",
151
177
  "eslint-plugin-jsx-a11y": "^6.10.2",
152
178
  "eslint-plugin-react": "^7.37.4",
153
- "eslint-plugin-react-compiler": "19.0.0-beta-e1e972c-20250221",
179
+ "eslint-plugin-react-compiler": "19.0.0-beta-40c6c23-20250301",
154
180
  "eslint-plugin-react-hooks": "experimental",
155
181
  "eslint-plugin-storybook": "^0.11.3",
156
182
  "http-server": "^14.1.1",
@@ -161,7 +187,7 @@
161
187
  "lint-staged": "^14.0.1",
162
188
  "module-alias": "^2.2.3",
163
189
  "npm-run-all2": "^5.0.2",
164
- "prettier": "^3.5.2",
190
+ "prettier": "^3.5.3",
165
191
  "react": "^19.0.0",
166
192
  "react-dom": "^19.0.0",
167
193
  "react-is": "^19.0.0",
@@ -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,67 +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
-
33
- &[data-has-duration] {
34
- padding-bottom: calc(${LOADING_BAR_HEIGHT}px / 2);
11
+ const loadingAnimation = keyframes`
12
+ 0% {
13
+ width: 0;
14
+ }
15
+ 100% {
16
+ width: 100%;
35
17
  }
36
18
  `
37
19
 
38
- export const LoadingBar = styled.div`
39
- display: flex;
40
- position: absolute;
41
- bottom: 0px;
42
- top: 0px;
43
- left: 0px;
44
- right: 0px;
45
- pointer-events: none;
46
- z-index: -1;
47
- overflow: hidden;
48
- overflow: clip;
49
- background: transparent;
50
- align-items: flex-end;
51
- will-change: opacity;
52
- `
20
+ const LOADING_BAR_HEIGHT = 2
53
21
 
54
- type LoadingBarProgressProps = Omit<React.ComponentProps<typeof Card>, 'tone'> & {
55
- tone: ThemeColorStateToneKey
56
- }
57
- export const LoadingBarProgress = styled<React.ComponentType<LoadingBarProgressProps>>(Card)`
58
- display: block;
59
- height: 100%;
60
- width: 100%;
61
- transform-origin: 0% 50%;
62
- background-color: ${(props) => {
63
- 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)
64
26
 
65
- return color.button.default[props.tone].enabled.bg
66
- }};
67
- `
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,9 +40,9 @@ const ROLES = {
33
40
  info: 'alert',
34
41
  } as const
35
42
 
36
- // Support pattern used by Sanity Studio, that works around the lack of `duration: Infinity` support in older @sanity/ui versions
37
- // https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value
38
- const LONG_ENOUGH_BUT_NOT_TOO_LONG = 1000 * 60 * 60 * 24 * 24
43
+ const StyledToast = styled(Card)<{$duration?: number; tone: ThemeColorStateToneKey} & ThemeProps>(
44
+ rootStyles,
45
+ )
39
46
 
40
47
  /**
41
48
  * The `Toast` component gives feedback to users when an action has taken place.
@@ -45,60 +52,25 @@ const LONG_ENOUGH_BUT_NOT_TOO_LONG = 1000 * 60 * 60 * 24 * 24
45
52
  * @public
46
53
  */
47
54
  export function Toast(
48
- props: ToastProps &
49
- Omit<
50
- React.HTMLProps<HTMLDivElement>,
51
- | 'as'
52
- | 'height'
53
- | 'ref'
54
- | 'title'
55
- | 'onAnimationStart'
56
- | 'onDragStart'
57
- | 'onDragEnd'
58
- | 'onDrag'
59
- >,
55
+ props: ToastProps & Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'height' | 'ref' | 'title'>,
60
56
  ): React.JSX.Element {
61
- const {
62
- closable,
63
- description,
64
- duration,
65
- onClose,
66
- radius = 3,
67
- title,
68
- status,
69
- updatedAt,
70
- ...restProps
71
- } = props
57
+ const {closable, description, duration, onClose, radius = 3, title, status, ...restProps} = props
72
58
  const cardTone = status ? STATUS_CARD_TONE[status] : 'default'
73
59
  const buttonTone = status ? BUTTON_TONE[status] : 'default'
74
60
  const role = status ? ROLES[status] : 'status'
75
61
 
76
- const prefersReducedMotion = usePrefersReducedMotion()
77
-
78
- const visualDuration = prefersReducedMotion ? 0 : 0.26
79
- const transition = visualDuration ? {type: 'spring', visualDuration, bounce: 0.25} : {duration: 0}
80
-
81
- const hasDuration = duration && isFinite(duration) && duration < LONG_ENOUGH_BUT_NOT_TOO_LONG
82
-
83
62
  return (
84
- <MotionToast
63
+ <StyledToast
85
64
  data-ui="Toast"
86
65
  role={role}
87
66
  {...restProps}
88
- data-has-duration={hasDuration ? '' : undefined}
89
- custom={visualDuration satisfies number}
67
+ marginTop={3}
90
68
  radius={radius}
91
69
  shadow={2}
92
70
  tone={cardTone}
93
- forwardedAs="li"
94
- layout="position"
95
- variants={container}
96
- initial={['hidden', 'initial'] satisfies ContainerVariants[]}
97
- animate={['visible', 'slideIn'] satisfies ContainerVariants[]}
98
- exit={['hidden', 'slideOut'] satisfies ContainerVariants[]}
99
- transition={transition}
71
+ $duration={duration}
100
72
  >
101
- <MotionFlex align="flex-start" variants={content} transition={transition}>
73
+ <Flex align="flex-start">
102
74
  <TextBox flex={1} padding={3}>
103
75
  <Stack space={3}>
104
76
  {title && (
@@ -107,9 +79,9 @@ export function Toast(
107
79
  </Text>
108
80
  )}
109
81
  {description && (
110
- <MotionText muted size={1} variants={content} transition={transition}>
82
+ <Text muted size={1}>
111
83
  {description}
112
- </MotionText>
84
+ </Text>
113
85
  )}
114
86
  </Stack>
115
87
  </TextBox>
@@ -127,79 +99,9 @@ export function Toast(
127
99
  />
128
100
  </Box>
129
101
  )}
130
- </MotionFlex>
131
- {hasDuration && (
132
- <MotionLoadiongBar variants={content} transition={transition}>
133
- <Card
134
- tone={cardTone}
135
- radius={radius}
136
- style={{
137
- position: 'absolute',
138
- right: -2,
139
- left: -2,
140
- top: 0,
141
- bottom: 2,
142
- zIndex: 1,
143
- }}
144
- />
145
- <MotionLoadiongBarProgress
146
- key={`progress-${updatedAt}`}
147
- tone={cardTone}
148
- initial={{scaleX: 0}}
149
- animate={{scaleX: 1}}
150
- transition={{delay: visualDuration, duration: duration / 1_000, ease: 'linear'}}
151
- onAnimationComplete={onClose}
152
- />
153
- </MotionLoadiongBar>
154
- )}
155
- </MotionToast>
102
+ </Flex>
103
+ </StyledToast>
156
104
  )
157
105
  }
158
106
 
159
107
  Toast.displayName = 'Toast'
160
-
161
- const container = {
162
- initial: {y: 32, scale: 0.5, zIndex: 1},
163
- hidden: {opacity: 0},
164
- visible: (visualDuration: number) => {
165
- if (!visualDuration) return {opacity: 1}
166
-
167
- return {
168
- opacity: 1,
169
- transition: {
170
- when: 'beforeChildren',
171
- staggerChildren: visualDuration / 3,
172
- duration: visualDuration / 3,
173
- },
174
- }
175
- },
176
- slideIn: {
177
- y: 0,
178
- scale: 1,
179
- },
180
- slideOut: {
181
- zIndex: 0,
182
- scale: 0.75,
183
- },
184
- } satisfies Variants
185
- type ContainerVariants = keyof typeof container
186
-
187
- const content = {
188
- initial: {
189
- willChange: 'transform',
190
- },
191
- hidden: {
192
- opacity: 0,
193
- },
194
- visible: {
195
- opacity: 1,
196
- },
197
- } satisfies Variants
198
-
199
- const MotionToast = motion.create(StyledToast)
200
- const MotionFlex = motion.create(Flex)
201
-
202
- const MotionText = motion.create(Text)
203
-
204
- const MotionLoadiongBar = motion.create(LoadingBar)
205
- const MotionLoadiongBarProgress = motion.create(LoadingBarProgress)