@sanity/ui 2.0.0-beta.2 → 2.0.0-beta.21

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.
Files changed (91) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +6 -3
  3. package/dist/index.cjs.mjs +11 -36
  4. package/dist/index.d.ts +247 -267
  5. package/dist/index.esm.js +477 -326
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +486 -539
  8. package/dist/index.js.map +1 -1
  9. package/dist/theme.cjs.mjs +1 -1
  10. package/dist/theme.d.ts +87 -115
  11. package/dist/theme.esm.js +348 -290
  12. package/dist/theme.esm.js.map +1 -1
  13. package/dist/theme.js +348 -290
  14. package/dist/theme.js.map +1 -1
  15. package/package.json +6 -3
  16. package/src/core/__workshop__/fontsPlugin.tsx +48 -8
  17. package/src/core/_compat.ts +236 -87
  18. package/src/core/components/dialog/dialog.tsx +1 -1
  19. package/src/core/components/dialog/styles.ts +2 -2
  20. package/src/core/components/menu/__workshop__/selectedItem.tsx +7 -2
  21. package/src/core/components/menu/menuButton.tsx +12 -1
  22. package/src/core/components/toast/toast.tsx +17 -2
  23. package/src/core/components/toast/toastProvider.tsx +20 -3
  24. package/src/core/constants.ts +37 -0
  25. package/src/core/hooks/useMediaIndex/useMediaIndex.test.tsx +6 -3
  26. package/src/core/middleware/origin.ts +47 -0
  27. package/src/core/primitives/badge/badge.tsx +1 -3
  28. package/src/core/primitives/button/button.tsx +9 -11
  29. package/src/core/primitives/heading/styles.ts +4 -0
  30. package/src/core/primitives/kbd/kbd.tsx +2 -0
  31. package/src/core/primitives/popover/popover.tsx +67 -13
  32. package/src/core/primitives/popover/popoverCard.tsx +31 -15
  33. package/src/core/primitives/select/select.tsx +1 -1
  34. package/src/core/primitives/switch/styles.ts +6 -1
  35. package/src/core/primitives/textInput/__workshop__/plain.tsx +1 -1
  36. package/src/core/primitives/textInput/__workshop__/states.tsx +10 -2
  37. package/src/core/primitives/tooltip/tooltip.tsx +46 -56
  38. package/src/core/primitives/tooltip/tooltipCard.tsx +110 -0
  39. package/src/core/styles/card/_cardColorStyle.ts +2 -0
  40. package/src/core/styles/input/textInputStyle.ts +33 -2
  41. package/src/core/styles/margin/marginsStyle.test.ts +2 -2
  42. package/src/core/styles/padding/paddingStyle.test.ts +2 -2
  43. package/src/core/theme/__workshop__/build/Root.tsx +20 -3
  44. package/src/core/theme/__workshop__/build/story.tsx +16 -10
  45. package/src/core/theme/__workshop__/canvas.tsx +7 -7
  46. package/src/core/theme/__workshop__/debug/story.tsx +196 -0
  47. package/src/core/theme/__workshop__/index.ts +5 -0
  48. package/src/core/theme/theme.test.tsx +2 -2
  49. package/src/core/types/button.ts +1 -2
  50. package/src/core/{primitives/tooltip → utils/conditionalWrapper}/conditionalWrapper.tsx +3 -0
  51. package/src/core/utils/conditionalWrapper/index.ts +1 -0
  52. package/src/core/utils/index.ts +1 -0
  53. package/src/theme/build/_deprecated/color/factory.ts +3 -3
  54. package/src/theme/build/buildColorTheme.test.ts +2 -2
  55. package/src/theme/build/buildColorTheme.ts +16 -16
  56. package/src/theme/build/colorToken/colorToken.ts +19 -0
  57. package/src/theme/build/colorToken/compileColorToken.ts +21 -0
  58. package/src/theme/build/colorToken/index.ts +2 -0
  59. package/src/theme/build/colorToken/types.ts +6 -0
  60. package/src/theme/build/renderColorTheme.ts +14 -15
  61. package/src/theme/build/renderColorValue.ts +9 -2
  62. package/src/theme/config/system.ts +12 -10
  63. package/src/theme/config/tokens/color/types.ts +12 -1
  64. package/src/theme/config/tokens/parseTokenKey.ts +1 -0
  65. package/src/theme/config/tokens/parseTokenValue.test.ts +13 -16
  66. package/src/theme/config/tokens/parseTokenValue.ts +45 -14
  67. package/src/theme/config/tokens/types.ts +3 -2
  68. package/src/theme/defaults/colorTokens.ts +279 -253
  69. package/src/theme/defaults/config.ts +2 -2
  70. package/src/theme/getScopedTheme.ts +1 -0
  71. package/src/theme/index.ts +0 -7
  72. package/src/theme/system/color/_constants.ts +5 -2
  73. package/src/theme/system/color/_system.ts +2 -1
  74. package/src/theme/system/color/avatar.ts +2 -12
  75. package/src/theme/system/color/button.ts +4 -20
  76. package/src/theme/system/color/input.ts +3 -11
  77. package/src/theme/system/color/selectable.ts +3 -14
  78. package/src/theme/system/shadow.ts +0 -6
  79. package/src/theme/system/styles.ts +0 -6
  80. package/src/theme/system/theme.ts +11 -1
  81. package/src/theme/system/v0/avatar.ts +1 -1
  82. package/src/theme/system/v0/color/button.ts +4 -4
  83. package/src/theme/system/v0/color/card.ts +2 -2
  84. package/src/theme/system/v0/color/color.ts +0 -1
  85. package/src/theme/system/v0/color/input.ts +3 -3
  86. package/src/theme/system/v0/color/muted.ts +2 -2
  87. package/src/theme/system/v0/color/solid.ts +2 -2
  88. package/src/theme/versioning/getTheme_v2.ts +2 -1
  89. package/src/theme/versioning/themeColor_v0_v2.ts +2 -2
  90. package/src/theme/versioning/v2_v0.ts +3 -2
  91. package/src/theme/build/colorToken.ts +0 -39
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ui",
3
- "version": "2.0.0-beta.2",
3
+ "version": "2.0.0-beta.21",
4
4
  "sideEffects": false,
5
5
  "types": "./dist/index.d.ts",
6
6
  "source": "./exports/index.ts",
@@ -72,7 +72,7 @@
72
72
  },
73
73
  "dependencies": {
74
74
  "@floating-ui/react-dom": "^2.0.4",
75
- "@sanity/color": "3.0.0-beta.8",
75
+ "@sanity/color": "^3.0.0",
76
76
  "@sanity/icons": "^2.7.0",
77
77
  "csstype": "^3.1.2",
78
78
  "framer-motion": "^10.16.5",
@@ -171,7 +171,10 @@
171
171
  "keywords": [
172
172
  "sanity",
173
173
  "ui",
174
- "components"
174
+ "primitives",
175
+ "react",
176
+ "components",
177
+ "design-system"
175
178
  ],
176
179
  "author": "Sanity.io <hello@sanity.io>",
177
180
  "homepage": "https://www.sanity.io/",
@@ -3,20 +3,60 @@ import {createGlobalStyle} from 'styled-components'
3
3
 
4
4
  const GlobalStyle = createGlobalStyle`
5
5
  @font-face {
6
- font-family: 'Inter';
6
+ font-family: Inter;
7
7
  font-style: normal;
8
- font-weight: 100 900;
8
+ font-weight: 400;
9
9
  font-display: swap;
10
- src: url('https://rsms.me/inter/font-files/InterVariable.woff2?v=4.0') format('woff2');
11
- font-named-instance: 'Regular';
10
+ src: url('https://studio-static.sanity.io/Inter-Regular.woff2') format('woff2');
12
11
  }
13
12
  @font-face {
14
- font-family: 'Inter';
13
+ font-family: Inter;
15
14
  font-style: italic;
16
- font-weight: 100 900;
15
+ font-weight: 400;
17
16
  font-display: swap;
18
- src: url('https://rsms.me/inter/font-files/InterVariable-Italic.woff2?v=4.0') format('woff2');
19
- font-named-instance: 'Italic';
17
+ src: url('https://studio-static.sanity.io/Inter-Italic.woff2') format('woff2');
18
+ }
19
+ @font-face {
20
+ font-family: Inter;
21
+ font-style: normal;
22
+ font-weight: 500;
23
+ font-display: swap;
24
+ src: url('https://studio-static.sanity.io/Inter-Medium.woff2') format('woff2');
25
+ }
26
+ @font-face {
27
+ font-family: Inter;
28
+ font-style: italic;
29
+ font-weight: 500;
30
+ font-display: swap;
31
+ src: url('https://studio-static.sanity.io/Inter-MediumItalic.woff2') format('woff2');
32
+ }
33
+ @font-face {
34
+ font-family: Inter;
35
+ font-style: normal;
36
+ font-weight: 600;
37
+ font-display: swap;
38
+ src: url('https://studio-static.sanity.io/Inter-SemiBold.woff2') format('woff2');
39
+ }
40
+ @font-face {
41
+ font-family: Inter;
42
+ font-style: italic;
43
+ font-weight: 600;
44
+ font-display: swap;
45
+ src: url('https://studio-static.sanity.io/Inter-SemiBoldItalic.woff2') format('woff2');
46
+ }
47
+ @font-face {
48
+ font-family: Inter;
49
+ font-style: normal;
50
+ font-weight: 700;
51
+ font-display: swap;
52
+ src: url('https://studio-static.sanity.io/Inter-Bold.woff2') format('woff2');
53
+ }
54
+ @font-face {
55
+ font-family: Inter;
56
+ font-style: italic;
57
+ font-weight: 700;
58
+ font-display: swap;
59
+ src: url('https://studio-static.sanity.io/Inter-BoldItalic.woff2') format('woff2');
20
60
  }
21
61
  `
22
62
 
@@ -1,73 +1,23 @@
1
- // re-export for backwards compatibility
2
- export {
3
- COLOR_CONFIG_CARD_KEYS,
4
- COLOR_CONFIG_CARD_TONES,
5
- COLOR_CONFIG_BLEND_KEYS,
6
- COLOR_CONFIG_STATE_KEYS,
7
- COLOR_CONFIG_STATE_TONES,
8
- THEME_COLOR_CARD_TONES,
9
- THEME_COLOR_BLEND_MODES,
10
- THEME_COLOR_BUTTON_MODES,
11
- THEME_COLOR_STATES,
12
- THEME_COLOR_STATE_TONES,
13
- buildTheme,
14
- createColorTheme,
15
- hexToRgb,
16
- hslToRgb,
17
- isColorBlendModeValue,
18
- isColorButtonMode,
19
- isColorConfigBaseKey,
20
- isColorConfigBaseTone,
21
- isColorConfigBlendKey,
22
- isColorConfigStateKey,
23
- isColorConfigStateTone,
24
- isColorHueKey,
25
- isColorOpacityValue,
26
- isColorTintKey,
27
- isColorTokenValue,
28
- isColorValue,
29
- multiply,
30
- parseColor,
31
- parseTokenKey,
32
- parseTokenValue,
33
- rgbToHex,
34
- rgbToHsl,
35
- rgba,
36
- rgbaToRGBA,
37
- screen,
38
- defaultTheme as studioTheme,
39
- type BaseTheme,
40
- type BoxShadow,
41
- type CSSObject,
42
- type ColorBlendModeTokenValue,
43
- type ColorConfigCardKey,
44
- type ColorConfigCardTone,
45
- type ColorConfigBlendKey,
46
- type ColorConfigOpacityValue,
47
- type ColorConfigStateKey,
48
- type ColorConfigStateTone,
49
- type ColorConfigValue,
50
- type HSL,
1
+ // This file re-exports API members which existed in the `@sanity/ui` export in v1.
2
+
3
+ import {
4
+ type BaseTheme as _BaseTheme,
5
+ type ThemeBoxShadow,
6
+ type HSL as _HSL,
51
7
  type PartialThemeColorBuilderOpts,
52
- type RGB,
53
- type RGBA,
54
- type RootTheme,
55
- type Theme,
8
+ type RGB as _RGB,
9
+ type RootTheme as _RootTheme,
10
+ type ThemeStyles,
11
+ type Theme as _Theme,
56
12
  type ThemeAvatar,
57
13
  type ThemeColor,
58
14
  type ThemeColorBase,
59
- type ThemeColorBaseTokens,
60
- type ThemeColorCardToneKey,
61
- type ThemeColorBlendModeKey,
62
15
  type ThemeColorBuilderOpts,
63
16
  type ThemeColorButton,
64
- type ThemeColorButtonModeKey,
65
- type ThemeColorButtonState,
17
+ type ThemeColorButtonModeKey as _ThemeColorButtonModeKey,
66
18
  type ThemeColorButtonStates,
67
- type ThemeColorButtonTokens,
68
19
  type ThemeColorButtonTones,
69
20
  type ThemeColorCard,
70
- type ThemeColorCardState,
71
21
  type ThemeColorGenericState,
72
22
  type ThemeColorInput,
73
23
  type ThemeColorInputState,
@@ -76,40 +26,239 @@ export {
76
26
  type ThemeColorMutedTone,
77
27
  type ThemeColorName,
78
28
  type ThemeColorScheme,
79
- type ThemeColorSchemeKey,
29
+ type ThemeColorSchemeKey as _ThemeColorSchemeKey,
80
30
  type ThemeColorSchemes,
81
31
  type ThemeColorSelectable,
32
+ type ThemeColorSelectableStates,
33
+ type ThemeColorSolid,
34
+ type ThemeColorSolidTone,
35
+ type ThemeColorSpot,
36
+ type ThemeColorSpotKey,
37
+ type ThemeColorSyntax as _ThemeColorSyntax,
38
+ type ThemeColorToneKey,
39
+ type ThemeFont as _ThemeFont,
40
+ type ThemeFontKey as _ThemeFontKey,
41
+ type ThemeFontSize as _ThemeFontSize,
42
+ type ThemeFontWeight as _ThemeFontWeight,
43
+ type ThemeFontWeightKey as _ThemeFontWeightKey,
44
+ type ThemeFonts as _ThemeFonts,
45
+ type ThemeInput,
46
+ type ThemeLayer as _ThemeLayer,
47
+ type ThemeShadow as _ThemeShadow,
48
+ type ThemeColorButtonState,
49
+ type ThemeColorCardState,
82
50
  type ThemeColorSelectableState,
51
+ buildTheme,
52
+ } from '@sanity/ui/theme'
53
+
54
+ /**
55
+ * @public
56
+ * @deprecated Use `BaseTheme` from `@sanity/ui/theme` instead.
57
+ */
58
+ export type BaseTheme = _BaseTheme
59
+
60
+ /**
61
+ * @public
62
+ * @deprecated Use `ThemeBoxShadow` from `@sanity/ui/theme` instead.
63
+ */
64
+ export type BoxShadow = ThemeBoxShadow
65
+
66
+ /**
67
+ * @public
68
+ * @deprecated Use `HSL` from `@sanity/ui/theme` instead.
69
+ */
70
+ export type HSL = _HSL
71
+
72
+ /**
73
+ * @public
74
+ * @deprecated Use `RGB` from `@sanity/ui/theme` instead.
75
+ */
76
+ export type RGB = _RGB
77
+
78
+ /**
79
+ * @public
80
+ * @deprecated Use `RootTheme` from `@sanity/ui/theme` instead.
81
+ */
82
+ export type RootTheme = _RootTheme
83
+
84
+ /**
85
+ * @public
86
+ * @deprecated Use `ThemeStyles` from `@sanity/ui/theme` instead.
87
+ */
88
+ export type Styles = ThemeStyles
89
+
90
+ /**
91
+ * @public
92
+ * @deprecated Use `Theme` from `@sanity/ui/theme` instead.
93
+ */
94
+ export type Theme = _Theme
95
+
96
+ /**
97
+ * @public
98
+ * @deprecated Use `ThemeColorButtonModeKey` from `@sanity/ui/theme` instead.
99
+ */
100
+ export type ThemeColorButtonModeKey = _ThemeColorButtonModeKey
101
+
102
+ /**
103
+ * @public
104
+ * @deprecated Use `ThemeColorSchemeKey` from `@sanity/ui/theme` instead.
105
+ */
106
+ export type ThemeColorSchemeKey = _ThemeColorSchemeKey
107
+
108
+ /**
109
+ * @public
110
+ * @deprecated Use `ThemeColorSyntax` from `@sanity/ui/theme` instead.
111
+ */
112
+ export type ThemeColorSyntax = _ThemeColorSyntax
113
+
114
+ /**
115
+ * @public
116
+ * @deprecated Use `ThemeFont` from `@sanity/ui/theme` instead.
117
+ */
118
+ export type ThemeFont = _ThemeFont
119
+
120
+ /**
121
+ * @public
122
+ * @deprecated Use `ThemeFontKey` from `@sanity/ui/theme` instead.
123
+ */
124
+ export type ThemeFontKey = _ThemeFontKey
125
+
126
+ /**
127
+ * @public
128
+ * @deprecated Use `ThemeFontSize` from `@sanity/ui/theme` instead.
129
+ */
130
+ export type ThemeFontSize = _ThemeFontSize
131
+
132
+ /**
133
+ * @public
134
+ * @deprecated Use `ThemeFontWeight` from `@sanity/ui/theme` instead.
135
+ */
136
+ export type ThemeFontWeight = _ThemeFontWeight
137
+
138
+ /**
139
+ * @public
140
+ * @deprecated Use `ThemeFontWeightKey` from `@sanity/ui/theme` instead.
141
+ */
142
+ export type ThemeFontWeightKey = _ThemeFontWeightKey
143
+
144
+ /**
145
+ * @public
146
+ * @deprecated Use `ThemeFonts` from `@sanity/ui/theme` instead.
147
+ */
148
+ export type ThemeFonts = _ThemeFonts
149
+
150
+ /**
151
+ * @public
152
+ * @deprecated Use `ThemeLayer` from `@sanity/ui/theme` instead.
153
+ */
154
+ export type ThemeLayer = _ThemeLayer
155
+
156
+ /**
157
+ * @public
158
+ * @deprecated Use `ThemeShadow` from `@sanity/ui/theme` instead.
159
+ */
160
+ export type ThemeShadow = _ThemeShadow
161
+
162
+ export {
163
+ type PartialThemeColorBuilderOpts,
164
+ type ThemeAvatar,
165
+ type ThemeColor,
166
+ type ThemeColorBase,
167
+ type ThemeColorBuilderOpts,
168
+ type ThemeColorButton,
169
+ type ThemeColorButtonStates,
170
+ type ThemeColorButtonTones,
171
+ type ThemeColorCard,
172
+ type ThemeColorGenericState,
173
+ type ThemeColorInput,
174
+ type ThemeColorInputState,
175
+ type ThemeColorInputStates,
176
+ type ThemeColorMuted,
177
+ type ThemeColorMutedTone,
178
+ type ThemeColorName,
179
+ type ThemeColorScheme,
180
+ type ThemeColorSchemes,
181
+ type ThemeColorSelectable,
83
182
  type ThemeColorSelectableStates,
84
183
  type ThemeColorSolid,
85
184
  type ThemeColorSolidTone,
86
185
  type ThemeColorSpot,
87
186
  type ThemeColorSpotKey,
88
- type ThemeColorStateKey,
89
- type ThemeColorStateTokens,
90
- type ThemeColorStateToneKey,
91
- type ThemeColorStatesTokens,
92
- type ThemeColorSyntax,
93
- type ThemeColorTokenValue,
94
- type ThemeColorTokens,
95
187
  type ThemeColorToneKey,
96
- type ThemeConfig,
97
- type ThemeFocusRing,
98
- type ThemeFont,
99
- type ThemeFontKey,
100
- type ThemeFontSize,
101
- type ThemeFontWeight,
102
- type ThemeFontWeightKey,
103
- type ThemeFonts,
104
188
  type ThemeInput,
105
- type ThemeLayer,
106
- type ThemeShadow,
107
- type ThemeStyles,
108
- type TokenBaseKeyNode,
109
- type TokenBlendModeValueNode,
110
- type TokenButtonKeyNode,
111
- type TokenColorValueNode,
112
- type TokenHueValueNode,
113
- type TokenKeyNode,
114
- type TokenValueNode,
189
+ type ThemeColorButtonState,
190
+ type ThemeColorCardState,
191
+ type ThemeColorSelectableState,
192
+ }
193
+
194
+ import {
195
+ createColorTheme as _createColorTheme,
196
+ hexToRgb as _hexToRgb,
197
+ hslToRgb as _hslToRgb,
198
+ multiply as _multiply,
199
+ parseColor as _parseColor,
200
+ rgbToHex as _rgbToHex,
201
+ rgbToHsl as _rgbToHsl,
202
+ rgba as _rgba,
203
+ screen as _screen,
115
204
  } from '@sanity/ui/theme'
205
+
206
+ /**
207
+ * @public
208
+ * @deprecated Use `createColorTheme` from `@sanity/ui/theme` instead.
209
+ */
210
+ export const createColorTheme = _createColorTheme
211
+
212
+ /**
213
+ * @public
214
+ * @deprecated Use `hexToRgb` from `@sanity/ui/theme` instead.
215
+ */
216
+ export const hexToRgb = _hexToRgb
217
+
218
+ /**
219
+ * @public
220
+ * @deprecated Use `hslToRgb` from `@sanity/ui/theme` instead.
221
+ */
222
+ export const hslToRgb = _hslToRgb
223
+
224
+ /**
225
+ * @public
226
+ * @deprecated Use `multiply` from `@sanity/ui/theme` instead.
227
+ */
228
+ export const multiply = _multiply
229
+
230
+ /**
231
+ * @public
232
+ * @deprecated Use `parseColor` from `@sanity/ui/theme` instead.
233
+ */
234
+ export const parseColor = _parseColor
235
+
236
+ /**
237
+ * @public
238
+ * @deprecated Use `rgbToHex` from `@sanity/ui/theme` instead.
239
+ */
240
+ export const rgbToHex = _rgbToHex
241
+
242
+ /**
243
+ * @public
244
+ * @deprecated Use `rgbToHsl` from `@sanity/ui/theme` instead.
245
+ */
246
+ export const rgbToHsl = _rgbToHsl
247
+
248
+ /**
249
+ * @public
250
+ * @deprecated Use `rgba` from `@sanity/ui/theme` instead.
251
+ */
252
+ export const rgba = _rgba
253
+
254
+ /**
255
+ * @public
256
+ * @deprecated Use `screen` from `@sanity/ui/theme` instead.
257
+ */
258
+ export const screen = _screen
259
+
260
+ /**
261
+ * @public
262
+ * @deprecated Use `buildTheme` from `@sanity/ui/theme` instead.
263
+ */
264
+ export const studioTheme = buildTheme()
@@ -394,6 +394,7 @@ export const Dialog = forwardRef(function Dialog(
394
394
  <Portal __unstable_name={portalProp}>
395
395
  <Root
396
396
  {...restProps}
397
+ $animate={animate}
397
398
  $padding={padding}
398
399
  $position={position}
399
400
  aria-labelledby={labelId}
@@ -406,7 +407,6 @@ export const Dialog = forwardRef(function Dialog(
406
407
  ref={ref}
407
408
  role="dialog"
408
409
  zOffset={zOffset}
409
- animate={animate}
410
410
  >
411
411
  <div ref={preDivRef} tabIndex={0} />
412
412
  <DialogCard
@@ -41,11 +41,11 @@ export function responsiveDialogPositionStyle(
41
41
  * @internal
42
42
  */
43
43
  export interface AnimationDialogStyleProps {
44
- animate: boolean
44
+ $animate: boolean
45
45
  }
46
46
 
47
47
  export function animationDialogStyle(props: AnimationDialogStyleProps): ReturnType<typeof css> {
48
- if (!props.animate) return css``
48
+ if (!props.$animate) return css``
49
49
 
50
50
  return css`
51
51
  @keyframes zoomIn {
@@ -16,8 +16,10 @@ const POPOVER_PROPS: MenuButtonProps['popover'] = {
16
16
  matchReferenceWidth: true,
17
17
  }
18
18
 
19
+ const INITIAL_INDEX = 1
20
+
19
21
  export default function SelectedItemStory() {
20
- const [selectedIndex, setSelectedIndex] = useState(0)
22
+ const [selectedIndex, setSelectedIndex] = useState(INITIAL_INDEX)
21
23
 
22
24
  return (
23
25
  <Box padding={[4, 5, 6]}>
@@ -26,12 +28,13 @@ export default function SelectedItemStory() {
26
28
 
27
29
  <MenuButton
28
30
  button={<Button text="Open menu" />}
29
- id="selected-item-example"
31
+ id="menu-button"
30
32
  menu={
31
33
  <Menu>
32
34
  <MenuItem
33
35
  icon={SearchIcon}
34
36
  iconRight={selectedIndex === 0 ? CheckmarkIcon : undefined}
37
+ id="menu-item-1"
35
38
  onClick={() => setSelectedIndex(0)}
36
39
  pressed={selectedIndex === 0}
37
40
  selected={selectedIndex === 0}
@@ -40,6 +43,7 @@ export default function SelectedItemStory() {
40
43
  <MenuItem
41
44
  icon={ClockIcon}
42
45
  iconRight={selectedIndex === 1 ? CheckmarkIcon : undefined}
46
+ id="menu-item-2"
43
47
  onClick={() => setSelectedIndex(1)}
44
48
  pressed={selectedIndex === 1}
45
49
  selected={selectedIndex === 1}
@@ -49,6 +53,7 @@ export default function SelectedItemStory() {
49
53
  <MenuItem
50
54
  icon={ExpandIcon}
51
55
  iconRight={selectedIndex === 2 ? CheckmarkIcon : undefined}
56
+ id="menu-item-3"
52
57
  onClick={() => setSelectedIndex(2)}
53
58
  pressed={selectedIndex === 2}
54
59
  selected={selectedIndex === 2}
@@ -99,6 +99,16 @@ export const MenuButton = forwardRef(function MenuButton(
99
99
  setShouldFocus(null)
100
100
  }, [])
101
101
 
102
+ // Prevent mouse event propagation when the menu is open.
103
+ // This is to ensure that `handleBlur` isn't triggered when clicking the menu button whilst open,
104
+ // which can lead to `setOpen` being triggered multiple times (once by `handleBlur`, and again by `handleButtonClick`).
105
+ const handleMouseDown = useCallback(
106
+ (event: PointerEvent) => {
107
+ if (open) event.preventDefault()
108
+ },
109
+ [open],
110
+ )
111
+
102
112
  const handleButtonKeyDown = useCallback((event: React.KeyboardEvent<HTMLButtonElement>) => {
103
113
  // On `ArrowDown`, `Enter` and `Space`
104
114
  // - Opens menu and moves focus to first menuitem
@@ -231,13 +241,14 @@ export const MenuButton = forwardRef(function MenuButton(
231
241
  id,
232
242
  onClick: handleButtonClick,
233
243
  onKeyDown: handleButtonKeyDown,
244
+ onMouseDown: handleMouseDown,
234
245
  'aria-haspopup': true,
235
246
  'aria-expanded': open,
236
247
  ref: setButtonRef,
237
248
  selected: open,
238
249
  })
239
250
  : null,
240
- [buttonProp, handleButtonClick, handleButtonKeyDown, id, open, setButtonRef],
251
+ [buttonProp, handleButtonClick, handleButtonKeyDown, handleMouseDown, id, open, setButtonRef],
241
252
  )
242
253
 
243
254
  const popoverProps: MenuButtonProps['popover'] = useMemo(
@@ -1,7 +1,9 @@
1
1
  import {CloseIcon} from '@sanity/icons'
2
2
  import {ThemeColorToneKey} from '@sanity/ui/theme'
3
3
  import styled from 'styled-components'
4
+ import {POPOVER_MOTION_CONTENT_OPACITY_PROPERTY} from '../../constants'
4
5
  import {Box, Button, Card, Flex, Stack, Text} from '../../primitives'
6
+ import type {ButtonTone} from '../../types'
5
7
 
6
8
  /**
7
9
  * @public
@@ -20,17 +22,28 @@ const STATUS_CARD_TONE: {[key: string]: ThemeColorToneKey} = {
20
22
  warning: 'caution',
21
23
  success: 'positive',
22
24
  info: 'primary',
23
- }
25
+ } as const
26
+
27
+ const BUTTON_TONE = {
28
+ error: 'critical',
29
+ warning: 'caution',
30
+ success: 'positive',
31
+ info: 'primary',
32
+ } satisfies {[key: string]: ButtonTone}
24
33
 
25
34
  const ROLES = {
26
35
  error: 'alert',
27
36
  warning: 'alert',
28
37
  success: 'alert',
29
38
  info: 'alert',
30
- }
39
+ } as const
31
40
 
32
41
  const Root = styled(Card)`
33
42
  pointer-events: all;
43
+ & > * {
44
+ opacity: var(${POPOVER_MOTION_CONTENT_OPACITY_PROPERTY}, 1);
45
+ will-change: opacity;
46
+ }
34
47
  `
35
48
 
36
49
  const TextBox = styled(Flex)`
@@ -49,6 +62,7 @@ export function Toast(
49
62
  ): React.ReactElement {
50
63
  const {closable, description, onClose, radius = 3, title, status, ...restProps} = props
51
64
  const cardTone = status ? STATUS_CARD_TONE[status] : 'default'
65
+ const buttonTone = status ? BUTTON_TONE[status] : 'default'
52
66
  const role = status ? ROLES[status] : 'status'
53
67
 
54
68
  return (
@@ -84,6 +98,7 @@ export function Toast(
84
98
  icon={CloseIcon}
85
99
  mode="bleed"
86
100
  padding={2}
101
+ tone={buttonTone}
87
102
  onClick={onClose}
88
103
  style={{verticalAlign: 'top'}}
89
104
  />
@@ -1,6 +1,7 @@
1
1
  import {AnimatePresence, motion} from 'framer-motion'
2
2
  import {useCallback, useEffect, useMemo, useRef, useState, startTransition} from 'react'
3
3
  import styled from 'styled-components'
4
+ import {POPOVER_MOTION_CONTENT_OPACITY_PROPERTY} from '../../constants'
4
5
  import {useMounted} from '../../hooks/useMounted'
5
6
  import {Box} from '../../primitives'
6
7
  import {Layer} from '../../utils'
@@ -132,9 +133,25 @@ export function ToastProvider(props: ToastProviderProps): React.ReactElement {
132
133
  <AnimatePresence initial={false}>
133
134
  {state.map(({dismiss, id, params}) => (
134
135
  <motion.div
135
- animate={{opacity: 1, y: 0, scale: 1}}
136
- exit={{opacity: 0, scale: 0.5, transition: {duration: 0.2}}}
137
- initial={{opacity: 0, y: 32, scale: 0.25}}
136
+ animate={{
137
+ opacity: [0, 1, 1],
138
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: [0, 0, 1],
139
+ y: 0,
140
+ scale: 1,
141
+ }}
142
+ exit={{
143
+ opacity: [1, 1, 0],
144
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: [1, 0, 0],
145
+ scale: 0.5,
146
+ transition: {duration: 0.2},
147
+ }}
148
+ initial={{
149
+ opacity: 0,
150
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: 0,
151
+ y: 32,
152
+ scale: 0.25,
153
+ willChange: 'transform',
154
+ }}
138
155
  key={id}
139
156
  layout="position"
140
157
  transition={{type: 'spring', damping: 30, stiffness: 400}}
@@ -1,3 +1,5 @@
1
+ import {Transition, Variant} from 'framer-motion'
2
+
1
3
  /**
2
4
  * @internal
3
5
  */
@@ -8,6 +10,41 @@ export const EMPTY_ARRAY: never[] = []
8
10
  */
9
11
  export const EMPTY_RECORD: Record<string, never> = {}
10
12
 
13
+ /**
14
+ * @internal
15
+ */
16
+ export const POPOVER_MOTION_CONTENT_OPACITY_PROPERTY = '--motion-content-opacity'
17
+
18
+ /**
19
+ * Shared `framer-motion` variants used by `Popover` and `Tooltip` components.
20
+ * @internal
21
+ */
22
+ export const POPOVER_MOTION_PROPS: {
23
+ animate: Variant
24
+ initial: Variant
25
+ exit: Variant
26
+ transition: Transition
27
+ } = {
28
+ initial: {
29
+ opacity: 0.5,
30
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: 0,
31
+ scale: 0.97,
32
+ willChange: 'transform',
33
+ },
34
+ animate: {
35
+ opacity: [null, 1, 1],
36
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: [null, null, 1],
37
+ scale: 1,
38
+ },
39
+ exit: {
40
+ // @ts-expect-error -- passing null a second time is valid: https://github.com/framer/motion/blob/b9ce4c42914c3916ea523609c5b032dfc72718bb/packages/framer-motion/src/animation/utils/keyframes.ts#L34C22-L34C22
41
+ opacity: [null, null, 0],
42
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: [null, 0, 0],
43
+ scale: 0.97,
44
+ },
45
+ transition: {duration: 0.4, type: 'spring'},
46
+ }
47
+
11
48
  /**
12
49
  * @internal
13
50
  * @deprecated No longer used.