@sanity/ui 2.10.0-corel.0 → 2.10.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.
Files changed (179) hide show
  1. package/dist/_chunks-cjs/getTheme_v2.js +110 -54
  2. package/dist/_chunks-cjs/getTheme_v2.js.map +1 -1
  3. package/dist/_chunks-es/getTheme_v2.mjs +110 -54
  4. package/dist/_chunks-es/getTheme_v2.mjs.map +1 -1
  5. package/dist/_legacy/getTheme_v2.esm.js +110 -54
  6. package/dist/_legacy/getTheme_v2.esm.js.map +1 -1
  7. package/dist/index.d.mts +43 -54
  8. package/dist/index.d.ts +43 -54
  9. package/dist/index.esm.js +3323 -3046
  10. package/dist/index.esm.js.map +1 -1
  11. package/dist/index.js +3322 -3046
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +3323 -3046
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/theme.d.mts +9 -9
  16. package/dist/theme.d.ts +9 -9
  17. package/dist/theme.esm.js +1144 -592
  18. package/dist/theme.esm.js.map +1 -1
  19. package/dist/theme.js +1144 -592
  20. package/dist/theme.js.map +1 -1
  21. package/dist/theme.mjs +1144 -592
  22. package/dist/theme.mjs.map +1 -1
  23. package/package.json +73 -59
  24. package/src/core/__workshop__/constants.ts +15 -19
  25. package/src/core/components/autocomplete/__workshop__/constrainedHeight.tsx +1 -1
  26. package/src/core/components/autocomplete/autocomplete.tsx +4 -3
  27. package/src/core/components/autocomplete/autocompleteOption.tsx +1 -1
  28. package/src/core/components/breadcrumbs/__workshop__/example.tsx +1 -3
  29. package/src/core/components/breadcrumbs/__workshop__/index.ts +0 -5
  30. package/src/core/components/breadcrumbs/breadcrumbs.styles.ts +13 -1
  31. package/src/core/components/breadcrumbs/breadcrumbs.tsx +31 -39
  32. package/src/core/components/dialog/dialog.tsx +5 -3
  33. package/src/core/components/dialog/dialogProvider.tsx +1 -1
  34. package/src/core/components/menu/__workshop__/asComponent.tsx +1 -1
  35. package/src/core/components/menu/__workshop__/tones.tsx +5 -6
  36. package/src/core/components/menu/menu.test.tsx +0 -2
  37. package/src/core/components/menu/menu.tsx +15 -11
  38. package/src/core/components/menu/menuButton.tsx +16 -38
  39. package/src/core/components/menu/menuGroup.tsx +4 -3
  40. package/src/core/components/menu/menuItem.tsx +5 -2
  41. package/src/core/components/tab/tabList.tsx +4 -12
  42. package/src/core/components/toast/__workshop__/hook.tsx +1 -1
  43. package/src/core/components/toast/toast.test.tsx +0 -1
  44. package/src/core/components/toast/toast.tsx +3 -3
  45. package/src/core/components/toast/toastProvider.tsx +8 -8
  46. package/src/core/components/toast/toastState.ts +6 -0
  47. package/src/core/components/tree/tree.tsx +1 -1
  48. package/src/core/components/tree/treeGroup.tsx +1 -1
  49. package/src/core/components/tree/treeItem.tsx +1 -1
  50. package/src/core/constants.ts +1 -1
  51. package/src/core/hooks/useArrayProp.ts +14 -9
  52. package/src/core/hooks/useDelayedState.ts +1 -1
  53. package/src/core/hooks/useElementRect/__workshop__/example.tsx +2 -2
  54. package/src/core/hooks/usePrefersDark.hydration.test.tsx +0 -1
  55. package/src/core/hooks/usePrefersReducedMotion.hydration.test.tsx +0 -1
  56. package/src/core/lib/globalScope.ts +0 -1
  57. package/src/core/primitives/avatar/avatar.tsx +1 -1
  58. package/src/core/primitives/avatar/avatarStack.tsx +2 -5
  59. package/src/core/primitives/badge/__workshop__/tones.tsx +6 -13
  60. package/src/core/primitives/badge/badge.tsx +1 -1
  61. package/src/core/primitives/box/box.tsx +1 -1
  62. package/src/core/primitives/button/__workshop__/custom.tsx +4 -3
  63. package/src/core/primitives/button/__workshop__/props.tsx +1 -1
  64. package/src/core/primitives/button/button.tsx +7 -12
  65. package/src/core/primitives/button/styles.ts +1 -1
  66. package/src/core/primitives/card/__workshop__/allTones.tsx +6 -8
  67. package/src/core/primitives/card/__workshop__/asButton.tsx +42 -24
  68. package/src/core/primitives/card/__workshop__/asComponent.tsx +2 -2
  69. package/src/core/primitives/card/__workshop__/checkered.tsx +17 -7
  70. package/src/core/primitives/card/__workshop__/interactive.tsx +10 -3
  71. package/src/core/primitives/card/__workshop__/listNavigation.tsx +5 -3
  72. package/src/core/primitives/card/__workshop__/props.tsx +7 -5
  73. package/src/core/primitives/card/__workshop__/selected.tsx +4 -1
  74. package/src/core/primitives/checkbox/__workshop__/tones.tsx +6 -0
  75. package/src/core/primitives/code/__workshop__/props.tsx +1 -1
  76. package/src/core/primitives/code/code.tsx +1 -2
  77. package/src/core/primitives/container/__workshop__/example.tsx +1 -1
  78. package/src/core/primitives/flex/__workshop__/example.tsx +3 -3
  79. package/src/core/primitives/flex/flex.tsx +1 -5
  80. package/src/core/primitives/grid/__workshop__/responsive.tsx +12 -12
  81. package/src/core/primitives/heading/__workshop__/opticalAlignment.tsx +13 -25
  82. package/src/core/primitives/heading/heading.tsx +2 -2
  83. package/src/core/primitives/inline/__workshop__/plain.tsx +3 -3
  84. package/src/core/primitives/inline/inline.tsx +0 -1
  85. package/src/core/primitives/kbd/kbd.tsx +1 -1
  86. package/src/core/primitives/label/__workshop__/opticalAlignment.tsx +6 -6
  87. package/src/core/primitives/label/label.tsx +1 -1
  88. package/src/core/primitives/popover/__workshop__/AlignedStory.tsx +1 -1
  89. package/src/core/primitives/popover/__workshop__/MatchReferenceWidthStory.tsx +3 -3
  90. package/src/core/primitives/popover/__workshop__/OpenOnMountStory.tsx +1 -1
  91. package/src/core/primitives/popover/__workshop__/PlainStory.tsx +1 -1
  92. package/src/core/primitives/popover/__workshop__/SidePanelStory.tsx +1 -1
  93. package/src/core/primitives/popover/__workshop__/TestStory.tsx +2 -2
  94. package/src/core/primitives/popover/popover.tsx +10 -10
  95. package/src/core/primitives/radio/__workshop__/plain.tsx +2 -2
  96. package/src/core/primitives/select/__workshop__/readOnly.tsx +1 -1
  97. package/src/core/primitives/stack/stack.tsx +1 -1
  98. package/src/core/primitives/text/__workshop__/example.tsx +6 -6
  99. package/src/core/primitives/text/__workshop__/opticalAlignment.tsx +11 -21
  100. package/src/core/primitives/text/styles.ts +0 -1
  101. package/src/core/primitives/text/text.tsx +3 -5
  102. package/src/core/primitives/textArea/__workshop__/plain.tsx +1 -1
  103. package/src/core/primitives/textInput/__workshop__/index.ts +0 -5
  104. package/src/core/primitives/textInput/__workshop__/plain.tsx +1 -2
  105. package/src/core/primitives/textInput/__workshop__/readOnly.tsx +6 -2
  106. package/src/core/primitives/textInput/__workshop__/tones.tsx +38 -0
  107. package/src/core/primitives/tooltip/tooltip.tsx +1 -2
  108. package/src/core/primitives/tooltip/tooltipDelayGroup/tooltipDelayGroupProvider.tsx +1 -1
  109. package/src/core/styles/card/_cardColorStyle.ts +8 -8
  110. package/src/core/styles/flex/flexStyle.ts +0 -18
  111. package/src/core/styles/flex/types.ts +0 -2
  112. package/src/core/theme/__workshop__/build/story.tsx +1 -3
  113. package/src/core/theme/__workshop__/debug/story.tsx +18 -13
  114. package/src/core/theme/theme.test.tsx +0 -1
  115. package/src/core/theme/themeColorProvider.tsx +1 -1
  116. package/src/core/theme/themeProvider.tsx +5 -7
  117. package/src/core/utils/arrow/arrow.tsx +1 -1
  118. package/src/core/utils/boundaryElement/boundaryElement.test.tsx +0 -1
  119. package/src/core/utils/boundaryElement/boundaryElementProvider.tsx +3 -1
  120. package/src/core/utils/elementQuery/elementQuery.tsx +16 -17
  121. package/src/core/utils/layer/layer.test.tsx +0 -1
  122. package/src/core/utils/layer/layer.tsx +9 -1
  123. package/src/core/utils/layer/layerProvider.tsx +2 -2
  124. package/src/core/utils/portal/portal.test.tsx +0 -1
  125. package/src/core/utils/portal/portalProvider.tsx +1 -1
  126. package/src/core/utils/srOnly/srOnly.tsx +1 -1
  127. package/src/core/utils/virtualList/virtualList.tsx +10 -3
  128. package/src/theme/build/_deprecated/color/muted/createMuted.ts +2 -2
  129. package/src/theme/build/buildColorTheme.ts +12 -8
  130. package/src/theme/build/merge.ts +0 -2
  131. package/src/theme/build/renderColorTheme.ts +54 -232
  132. package/src/theme/defaults/colorTokens.ts +13 -18
  133. package/src/theme/system/color/_constants.ts +6 -10
  134. package/src/theme/system/css.ts +1 -2
  135. package/src/theme/versioning/themeColor_v0_v2.ts +26 -26
  136. package/src/theme/versioning/v0_v2.ts +4 -4
  137. package/src/core/components/autocomplete/__workshop__/.eslintrc +0 -5
  138. package/src/core/components/breadcrumbs/__workshop__/.eslintrc +0 -5
  139. package/src/core/components/breadcrumbs/__workshop__/buttons.tsx +0 -58
  140. package/src/core/components/dialog/__workshop__/.eslintrc +0 -5
  141. package/src/core/components/hotkeys/__workshop__/.eslintrc +0 -5
  142. package/src/core/components/menu/__workshop__/.eslintrc +0 -5
  143. package/src/core/components/skeleton/__workshop__/.eslintrc +0 -5
  144. package/src/core/components/tab/__workshop__/.eslintrc +0 -5
  145. package/src/core/components/toast/__workshop__/.eslintrc +0 -5
  146. package/src/core/components/tree/__workshop__/.eslintrc +0 -5
  147. package/src/core/hooks/useElementRect/__workshop__/.eslintrc +0 -5
  148. package/src/core/hooks/useMediaIndex/__workshop__/.eslintrc +0 -5
  149. package/src/core/primitives/avatar/__workshop__/.eslintrc +0 -5
  150. package/src/core/primitives/badge/__workshop__/.eslintrc +0 -5
  151. package/src/core/primitives/box/__workshop__/.eslintrc +0 -5
  152. package/src/core/primitives/button/__workshop__/.eslintrc +0 -5
  153. package/src/core/primitives/card/__workshop__/.eslintrc +0 -5
  154. package/src/core/primitives/checkbox/__workshop__/.eslintrc +0 -5
  155. package/src/core/primitives/code/__workshop__/.eslintrc +0 -5
  156. package/src/core/primitives/container/__workshop__/.eslintrc +0 -5
  157. package/src/core/primitives/flex/__workshop__/.eslintrc +0 -5
  158. package/src/core/primitives/grid/__workshop__/.eslintrc +0 -5
  159. package/src/core/primitives/heading/__workshop__/.eslintrc +0 -5
  160. package/src/core/primitives/inline/__workshop__/.eslintrc +0 -5
  161. package/src/core/primitives/kbd/__workshop__/.eslintrc +0 -5
  162. package/src/core/primitives/label/__workshop__/.eslintrc +0 -5
  163. package/src/core/primitives/popover/__workshop__/.eslintrc +0 -5
  164. package/src/core/primitives/radio/__workshop__/.eslintrc +0 -5
  165. package/src/core/primitives/select/__workshop__/.eslintrc +0 -5
  166. package/src/core/primitives/spinner/__workshop__/.eslintrc +0 -5
  167. package/src/core/primitives/stack/__workshop__/.eslintrc +0 -5
  168. package/src/core/primitives/switch/__workshop__/.eslintrc +0 -5
  169. package/src/core/primitives/text/__workshop__/.eslintrc +0 -5
  170. package/src/core/primitives/textArea/__workshop__/.eslintrc +0 -5
  171. package/src/core/primitives/textInput/__workshop__/.eslintrc +0 -5
  172. package/src/core/primitives/textInput/__workshop__/multipleTones.tsx +0 -20
  173. package/src/core/primitives/tooltip/__workshop__/.eslintrc +0 -5
  174. package/src/core/theme/__workshop__/.eslintrc +0 -5
  175. package/src/core/utils/boundaryElement/__workshop__/.eslintrc +0 -5
  176. package/src/core/utils/elementQuery/__workshop__/.eslintrc +0 -5
  177. package/src/core/utils/layer/__workshop__/.eslintrc +0 -5
  178. package/src/core/utils/portal/__workshop__/.eslintrc +0 -5
  179. package/src/core/utils/virtualList/__workshop__/.eslintrc +0 -5
@@ -25,9 +25,9 @@ export interface MenuButtonProps {
25
25
  * @deprecated Use `popover={{boundaryElement: element}}` instead.
26
26
  */
27
27
  boundaryElement?: HTMLElement
28
- button: React.ReactElement
28
+ button: React.ReactElement<any>
29
29
  id: string
30
- menu?: React.ReactElement
30
+ menu?: React.ReactElement<any>
31
31
  onClose?: () => void
32
32
  onOpen?: () => void
33
33
  /**
@@ -169,7 +169,7 @@ export const MenuButton = forwardRef(function MenuButton(
169
169
  }, [buttonElement, disableRestoreFocusOnClose])
170
170
 
171
171
  const handleBlur = useCallback(
172
- (event: React.FocusEvent<HTMLButtonElement>) => {
172
+ (event: FocusEvent) => {
173
173
  const target = event.relatedTarget
174
174
 
175
175
  if (!(target instanceof Node)) {
@@ -199,32 +199,19 @@ export const MenuButton = forwardRef(function MenuButton(
199
199
  return () => setChildMenuElements((els) => els.filter((_el) => _el !== el))
200
200
  }, [])
201
201
 
202
- const menuProps: MenuProps = useMemo(
203
- () => ({
204
- 'aria-labelledby': id,
205
- 'onBlurCapture': handleBlur,
206
- 'onClickOutside': handleMenuClickOutside,
207
- 'onEscape': handleMenuEscape,
208
- 'onItemClick': handleItemClick,
209
- 'originElement': buttonElement,
210
- registerElement,
211
- shouldFocus,
212
- }),
213
- [
214
- buttonElement,
215
- handleMenuClickOutside,
216
- handleMenuEscape,
217
- handleItemClick,
218
- id,
219
- handleBlur,
220
- registerElement,
221
- shouldFocus,
222
- ],
223
- )
202
+ const menuProps: MenuProps = {
203
+ 'aria-labelledby': id,
204
+ 'onBlurCapture': handleBlur,
205
+ 'onClickOutside': handleMenuClickOutside,
206
+ 'onEscape': handleMenuEscape,
207
+ 'onItemClick': handleItemClick,
208
+ 'originElement': buttonElement,
209
+ registerElement,
210
+ shouldFocus,
211
+ }
224
212
 
225
213
  const menu = menuProp && cloneElement(menuProp, menuProps)
226
214
 
227
- const ref = useRef<HTMLButtonElement | null>(null)
228
215
  const button = useMemo(
229
216
  () =>
230
217
  buttonProp &&
@@ -236,7 +223,7 @@ export const MenuButton = forwardRef(function MenuButton(
236
223
  'onMouseDown': handleMouseDown,
237
224
  'aria-haspopup': true,
238
225
  'aria-expanded': open,
239
- 'ref': ref,
226
+ 'ref': setButtonElement,
240
227
  'selected': buttonProp.props.selected ?? open,
241
228
  }),
242
229
  [buttonProp, handleButtonClick, handleButtonKeyDown, handleMouseDown, id, open],
@@ -245,19 +232,10 @@ export const MenuButton = forwardRef(function MenuButton(
245
232
  // Forward button ref to parent
246
233
  useImperativeHandle<HTMLButtonElement | null, HTMLButtonElement | null>(
247
234
  forwardedRef,
248
- () => ref.current,
235
+ () => buttonElement,
236
+ [buttonElement],
249
237
  )
250
238
 
251
- // If there's a button then we need to set the reference element to the cloned button ref
252
- // and if button changes we make sure to update or remove the reference element.
253
- useEffect(() => {
254
- if (!button) return undefined
255
-
256
- setButtonElement(ref.current)
257
-
258
- return () => setButtonElement(null)
259
- }, [button])
260
-
261
239
  const popoverProps: MenuButtonProps['popover'] = useMemo(
262
240
  () => ({
263
241
  boundaryElement: deprecated_boundaryElement,
@@ -1,5 +1,5 @@
1
1
  import {ChevronRightIcon} from '@sanity/icons'
2
- import {isValidElement, useCallback, useEffect, useState} from 'react'
2
+ import {isValidElement, useCallback, useEffect, useState, type JSX} from 'react'
3
3
  import {isValidElementType} from 'react-is'
4
4
  import {useArrayProp} from '../../hooks'
5
5
  import {Box, Flex, Popover, PopoverProps, Text} from '../../primitives'
@@ -30,7 +30,7 @@ export interface MenuGroupProps {
30
30
  export function MenuGroup(
31
31
  props: Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'height' | 'popover' | 'ref' | 'tabIndex'> &
32
32
  MenuGroupProps,
33
- ): React.ReactElement {
33
+ ): React.ReactElement<any> {
34
34
  const {
35
35
  as = 'button',
36
36
  children,
@@ -53,9 +53,10 @@ export function MenuGroup(
53
53
  onClickOutside,
54
54
  onEscape,
55
55
  onItemClick,
56
- onItemMouseEnter = menu.onMouseEnter,
56
+ onItemMouseEnter: _onItemMouseEnter,
57
57
  registerElement,
58
58
  } = menu
59
+ const onItemMouseEnter = _onItemMouseEnter ?? menu.onMouseEnter
59
60
  const [rootElement, setRootElement] = useState<HTMLButtonElement | HTMLDivElement | null>(null)
60
61
  const [open, setOpen] = useState(false)
61
62
  const [shouldFocus, setShouldFocus] = useState<'first' | 'last' | null>(null)
@@ -7,6 +7,7 @@ import {
7
7
  useMemo,
8
8
  useRef,
9
9
  useState,
10
+ type JSX,
10
11
  } from 'react'
11
12
  import {isValidElementType} from 'react-is'
12
13
  import {useArrayProp} from '../../hooks'
@@ -72,9 +73,11 @@ export const MenuItem = forwardRef(function MenuItem(
72
73
  activeElement,
73
74
  mount,
74
75
  onItemClick,
75
- onItemMouseEnter = menu.onMouseEnter,
76
- onItemMouseLeave = menu.onMouseLeave,
76
+ onItemMouseEnter: _onItemMouseEnter,
77
+ onItemMouseLeave: _onItemMouseLeave,
77
78
  } = menu
79
+ const onItemMouseEnter = _onItemMouseEnter ?? menu.onMouseEnter
80
+ const onItemMouseLeave = _onItemMouseLeave ?? menu.onMouseLeave
78
81
  const [rootElement, setRootElement] = useState<HTMLDivElement | null>(null)
79
82
  const active = Boolean(activeElement) && activeElement === rootElement
80
83
  const ref = useRef<HTMLDivElement | null>(null)
@@ -1,4 +1,4 @@
1
- import {cloneElement, forwardRef, useCallback, useMemo, useState} from 'react'
1
+ import {cloneElement, forwardRef, useCallback, useState, Children, isValidElement} from 'react'
2
2
  import {styled} from 'styled-components'
3
3
  import {Inline, InlineProps} from '../../primitives'
4
4
 
@@ -6,11 +6,7 @@ import {Inline, InlineProps} from '../../primitives'
6
6
  * @public
7
7
  */
8
8
  export interface TabListProps extends Omit<InlineProps, 'as' | 'height'> {
9
- children: Array<React.ReactElement | null | undefined | false>
10
- }
11
-
12
- function _isReactElement(node: unknown): node is React.ReactElement {
13
- return Boolean(node)
9
+ children: Array<React.ReactElement<any> | null | undefined | false>
14
10
  }
15
11
 
16
12
  //Limits the width of tabs in tablist
@@ -33,22 +29,18 @@ export const TabList = forwardRef(function TabList(
33
29
  const {children: childrenProp, ...restProps} = props
34
30
  const [focusedIndex, setFocusedIndex] = useState(-1)
35
31
 
36
- const children = useMemo(() => childrenProp.filter(_isReactElement), [childrenProp])
32
+ const children: React.ReactElement<any>[] = Children.toArray(childrenProp).filter(isValidElement)
37
33
 
38
34
  const tabs = children.map((child, childIndex) =>
39
35
  cloneElement(child, {
40
36
  focused: focusedIndex === childIndex,
41
37
  key: childIndex,
42
- onFocus: () => handleTabFocus(childIndex),
38
+ onFocus: () => setFocusedIndex(childIndex),
43
39
  }),
44
40
  )
45
41
 
46
42
  const numTabs = tabs.length
47
43
 
48
- const handleTabFocus = useCallback((tabIdx: number) => {
49
- setFocusedIndex(tabIdx)
50
- }, [])
51
-
52
44
  const handleKeyDown = useCallback(
53
45
  (event: React.KeyboardEvent<HTMLDivElement>) => {
54
46
  if (event.key === 'ArrowLeft') {
@@ -24,7 +24,7 @@ export default function HookStory() {
24
24
  })
25
25
  }
26
26
  text="Push info"
27
- tone="primary"
27
+ tone="neutral"
28
28
  />
29
29
 
30
30
  <Button
@@ -1,5 +1,4 @@
1
1
  /** @jest-environment jsdom */
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
2
 
4
3
  import {render} from '../../../../test'
5
4
  import {ToastContext} from './toastContext'
@@ -23,14 +23,14 @@ const STATUS_CARD_TONE: {[key: string]: ThemeColorStateToneKey} = {
23
23
  error: 'critical',
24
24
  warning: 'caution',
25
25
  success: 'positive',
26
- info: 'primary',
26
+ info: 'neutral',
27
27
  } as const
28
28
 
29
29
  const BUTTON_TONE = {
30
30
  error: 'critical',
31
31
  warning: 'caution',
32
32
  success: 'positive',
33
- info: 'primary',
33
+ info: 'neutral',
34
34
  } satisfies {[key: string]: ButtonTone}
35
35
 
36
36
  const ROLES = {
@@ -53,7 +53,7 @@ const Root = styled(Card)<{$duration?: number; tone: ThemeColorStateToneKey} & T
53
53
  */
54
54
  export function Toast(
55
55
  props: ToastProps & Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'height' | 'ref' | 'title'>,
56
- ): React.ReactElement {
56
+ ): React.ReactElement<any> {
57
57
  const {closable, description, duration, onClose, radius = 3, title, status, ...restProps} = props
58
58
  const cardTone = status ? STATUS_CARD_TONE[status] : 'default'
59
59
  const buttonTone = status ? BUTTON_TONE[status] : 'default'
@@ -8,6 +8,7 @@ import {Box} from '../../primitives'
8
8
  import {Layer} from '../../utils'
9
9
  import {Toast} from './toast'
10
10
  import {ToastContext} from './toastContext'
11
+ import {generateToastId} from './toastState'
11
12
  import {ToastContextValue, ToastParams} from './types'
12
13
 
13
14
  type ToastState = {
@@ -45,12 +46,10 @@ const ToastContainer = styled.div`
45
46
  width: 100%;
46
47
  `
47
48
 
48
- let toastId = 0
49
-
50
49
  /**
51
50
  * @public
52
51
  */
53
- export function ToastProvider(props: ToastProviderProps): React.ReactElement {
52
+ export function ToastProvider(props: ToastProviderProps): React.ReactElement<any> {
54
53
  const {children, padding = 4, paddingX, paddingY, zOffset} = props
55
54
  const [state, _setState] = useState<ToastState>([])
56
55
  const toastsRef = useRef<{[key: string]: {timeoutId: NodeJS.Timeout}}>({})
@@ -60,22 +59,23 @@ export function ToastProvider(props: ToastProviderProps): React.ReactElement {
60
59
  () => ({
61
60
  initial: {
62
61
  opacity: 0,
63
- [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: 0,
62
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY]: 0,
64
63
  y: 32,
65
64
  scale: 0.25,
66
65
  willChange: 'transform',
67
66
  },
68
67
  animate: {
69
68
  opacity: [0, 1, 1],
70
- [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: [0, 0, 1],
69
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY]: [0, 0, 1],
71
70
  y: 0,
72
71
  scale: 1,
73
72
  },
73
+ // @ts-expect-error fix later
74
74
  exit: {
75
75
  opacity: [1, 1, 0],
76
- [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: [1, 0, 0],
76
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY]: [1, 0, 0],
77
77
  scale: 0.5,
78
- transition: prefersReducedMotion ? {duration: 0} : {duration: 0.2},
78
+ transition: {duration: prefersReducedMotion ? 0 : 0.2},
79
79
  },
80
80
  }),
81
81
  [prefersReducedMotion],
@@ -86,7 +86,7 @@ export function ToastProvider(props: ToastProviderProps): React.ReactElement {
86
86
  // Wrap setState in startTransition to allow React to give input state updates higher priority
87
87
  const setState: typeof _setState = (state) => startTransition(() => _setState(state))
88
88
 
89
- const id = params.id || String(toastId++)
89
+ const id = params.id || generateToastId()
90
90
  const duration = params.duration || 5000
91
91
 
92
92
  const dismiss = () => {
@@ -0,0 +1,6 @@
1
+ let toastId = 0
2
+
3
+ /** @internal */
4
+ export function generateToastId(): string {
5
+ return String(toastId++)
6
+ }
@@ -30,7 +30,7 @@ export const Tree = memo(
30
30
  props: TreeProps &
31
31
  Omit<React.HTMLProps<HTMLDivElement>, 'align' | 'as' | 'height' | 'ref' | 'role' | 'wrap'>,
32
32
  forwardedRef: React.ForwardedRef<HTMLDivElement>,
33
- ): React.ReactElement {
33
+ ): React.ReactElement<any> {
34
34
  const {children, space = 1, onFocus, ...restProps} = props
35
35
  const ref = useRef<HTMLDivElement | null>(null)
36
36
  const [focusedElement, setFocusedElement] = useState<HTMLElement | null>(null)
@@ -9,7 +9,7 @@ export interface TreeGroupProps {
9
9
  export const TreeGroup = memo(function TreeGroup(
10
10
  props: TreeGroupProps &
11
11
  Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'height' | 'ref' | 'role' | 'wrap'>,
12
- ): React.ReactElement {
12
+ ): React.ReactElement<any> {
13
13
  const {children, expanded = false, ...restProps} = props
14
14
  const tree = useTree()
15
15
 
@@ -46,7 +46,7 @@ const ToggleArrowText = styled(Text)`
46
46
  */
47
47
  export const TreeItem = memo(function TreeItem(
48
48
  props: TreeItemProps & Omit<React.HTMLProps<HTMLLIElement>, 'as' | 'ref' | 'role'>,
49
- ): React.ReactElement {
49
+ ): React.ReactElement<any> {
50
50
  const {
51
51
  children,
52
52
  expanded: expandedProp = false,
@@ -13,7 +13,7 @@ export const EMPTY_RECORD: Record<string, never> = {}
13
13
  /**
14
14
  * @internal
15
15
  */
16
- export const POPOVER_MOTION_CONTENT_OPACITY_PROPERTY = '--motion-content-opacity'
16
+ export const POPOVER_MOTION_CONTENT_OPACITY_PROPERTY = '--motion-content-opacity' as string
17
17
 
18
18
  /**
19
19
  * Shared `framer-motion` variants used by `Popover` and `Tooltip` components.
@@ -1,4 +1,4 @@
1
- import {useMemo} from 'react'
1
+ import {useState} from 'react'
2
2
  import {_getArrayProp} from '../styles'
3
3
 
4
4
  /** @beta */
@@ -12,14 +12,19 @@ export function useArrayProp<T extends ArrayPropPrimitive = ArrayPropPrimitive>(
12
12
  val: T | T[] | undefined,
13
13
  defaultVal?: T[],
14
14
  ): T[] {
15
- // JSON.stringify is fast, but it's not faster than useMemo's referencial equality check
16
- const __perf_hash__ = useMemo(() => JSON.stringify(val ?? defaultVal), [defaultVal, val])
15
+ const [[cachedVal, cachedHash], setCache] = useState<[T[], string]>(() => [
16
+ _getArrayProp(val, defaultVal),
17
+ JSON.stringify(val ?? defaultVal),
18
+ ])
17
19
 
18
- return useMemo(
19
- () => _getArrayProp(val, defaultVal),
20
+ const hash = JSON.stringify(val ?? defaultVal)
20
21
 
21
- // Improve performance: Keep object identify for a given hash of the value
22
- // eslint-disable-next-line react-hooks/exhaustive-deps
23
- [__perf_hash__],
24
- )
22
+ if (hash !== cachedHash) {
23
+ // If the cached hash has changed, update the cache right away.
24
+ // Calling setState during render is fine in this case, and preferred over a useEffect loop
25
+ // https://19.react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
26
+ setCache([_getArrayProp(val, defaultVal), hash])
27
+ }
28
+
29
+ return cachedVal
25
30
  }
@@ -8,7 +8,7 @@ export function useDelayedState<S>(
8
8
  initialState: S | (() => S),
9
9
  ): [S, (nextState: SetStateAction<S>, delay?: number) => void] {
10
10
  const [state, setState] = useState(initialState)
11
- const delayedAction = useRef<NodeJS.Timeout | undefined>()
11
+ const delayedAction = useRef<NodeJS.Timeout | undefined>(undefined)
12
12
 
13
13
  const onStateChange = useCallback((nextState: SetStateAction<S>, delay?: number) => {
14
14
  const action = () => {
@@ -1,11 +1,11 @@
1
1
  import {Box, Card, Code, Container, Stack, Text, useElementRect} from '@sanity/ui'
2
- import {useMemo, useState} from 'react'
2
+ import {useState} from 'react'
3
3
  import {Grid} from '../../../primitives'
4
4
 
5
5
  export default function ExampleStory() {
6
6
  const [element, setElement] = useState<HTMLElement | null>(null)
7
7
  const rect = useElementRect(element)
8
- const size = useMemo(() => ({width: rect?.width || 0, height: rect?.height || 0}), [rect])
8
+ const size = {width: rect?.width || 0, height: rect?.height || 0}
9
9
 
10
10
  return (
11
11
  <Box padding={[3, 4, 5]}>
@@ -1,5 +1,4 @@
1
1
  /** @jest-environment jsdom */
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
2
  /**
4
3
  * As this hook is used for top-level theming it's likely to be called while server-rendering
5
4
  * and that's why it's worth it to have a testing suite for hydration
@@ -1,5 +1,4 @@
1
1
  /** @jest-environment jsdom */
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
2
  /**
4
3
  * As this hook is used for top-level theming it's likely to be called while server-rendering
5
4
  * and that's why it's worth it to have a testing suite for hydration
@@ -1,4 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
1
  /**
3
2
  * Gets the global scope instance in a given environment.
4
3
  *
@@ -1,5 +1,5 @@
1
1
  import {ThemeColorAvatarColorKey} from '@sanity/ui/theme'
2
- import {forwardRef, useCallback, useEffect, useId, useMemo, useState} from 'react'
2
+ import {forwardRef, useCallback, useEffect, useId, useMemo, useState, type JSX} from 'react'
3
3
  import ReactIs from 'react-is'
4
4
  import {styled} from 'styled-components'
5
5
  import {useArrayProp} from '../../hooks'
@@ -1,5 +1,5 @@
1
1
  import {getTheme_v2} from '@sanity/ui/theme'
2
- import {Children, cloneElement, forwardRef, isValidElement, useMemo} from 'react'
2
+ import {Children, cloneElement, forwardRef, isValidElement} from 'react'
3
3
  import {styled, css} from 'styled-components'
4
4
  import {EMPTY_RECORD} from '../../constants'
5
5
  import {useArrayProp} from '../../hooks'
@@ -65,10 +65,7 @@ export const AvatarStack = forwardRef(function AvatarStack(
65
65
  size: sizeProp = 1,
66
66
  ...restProps
67
67
  } = props
68
- const children = useMemo<React.ReactElement[]>(
69
- () => Children.toArray(childrenProp).filter(isValidElement),
70
- [childrenProp],
71
- )
68
+ const children: React.ReactElement<any>[] = Children.toArray(childrenProp).filter(isValidElement)
72
69
  const maxLength = Math.max(maxLengthProp, 0)
73
70
  const size = useArrayProp(sizeProp)
74
71
 
@@ -1,4 +1,5 @@
1
1
  import {Badge, Flex, Inline} from '@sanity/ui'
2
+ import {THEME_COLOR_STATE_TONES} from '@sanity/ui/theme'
2
3
  import {useSelect} from '@sanity/ui-workshop'
3
4
  import {WORKSHOP_BADGE_MODE_OPTIONS} from '../../../__workshop__/constants'
4
5
 
@@ -8,19 +9,11 @@ export default function Tones() {
8
9
  return (
9
10
  <Flex align="center" height="fill" justify="center" padding={4} sizing="border">
10
11
  <Inline space={2}>
11
- <Badge mode={mode}>Default</Badge>
12
- <Badge mode={mode} tone="primary">
13
- Primary
14
- </Badge>
15
- <Badge mode={mode} tone="positive">
16
- Positive
17
- </Badge>
18
- <Badge mode={mode} tone="caution">
19
- Caution
20
- </Badge>
21
- <Badge mode={mode} tone="critical">
22
- Critical
23
- </Badge>
12
+ {THEME_COLOR_STATE_TONES.map((tone) => (
13
+ <Badge key={tone} mode={mode} tone={tone}>
14
+ {tone}
15
+ </Badge>
16
+ ))}
24
17
  </Inline>
25
18
  </Flex>
26
19
  )
@@ -1,4 +1,4 @@
1
- import {forwardRef} from 'react'
1
+ import {forwardRef, type JSX} from 'react'
2
2
  import {styled} from 'styled-components'
3
3
  import {useArrayProp} from '../../hooks'
4
4
  import {responsiveRadiusStyle, ResponsiveRadiusStyleProps} from '../../styles/internal'
@@ -1,4 +1,4 @@
1
- import {forwardRef} from 'react'
1
+ import {forwardRef, type JSX} from 'react'
2
2
  import {styled} from 'styled-components'
3
3
  import {useArrayProp} from '../../hooks'
4
4
  import {
@@ -3,11 +3,12 @@ import {WORKSHOP_BUTTON_TONE_OPTIONS} from '../../../__workshop__/constants'
3
3
 
4
4
  export default function CustomStory() {
5
5
  const tones = Object.entries(WORKSHOP_BUTTON_TONE_OPTIONS)
6
+ const len = tones.length
6
7
 
7
8
  return (
8
- <Flex align="center" height="fill" justify="center">
9
+ <Flex align="center" height="fill" justify="center" padding={4} sizing="border">
9
10
  <Stack space={2}>
10
- <Grid columns={5} gap={1}>
11
+ <Grid columns={len} gap={1}>
11
12
  {tones.map(([title, tone]) => (
12
13
  <Button key={tone} mode="ghost" padding={3} tone={tone}>
13
14
  <Stack space={2}>
@@ -24,7 +25,7 @@ export default function CustomStory() {
24
25
  </Button>
25
26
  ))}
26
27
  </Grid>
27
- <Grid columns={5} gap={1}>
28
+ <Grid columns={len} gap={1}>
28
29
  {tones.map(([title, tone]) => (
29
30
  <Button key={tone} mode="default" padding={3} tone={tone}>
30
31
  <Stack space={2}>
@@ -13,7 +13,7 @@ import {
13
13
 
14
14
  export default function ButtonStory() {
15
15
  const disabled = useBoolean('Disabled', false, 'Props')
16
- const fontSize = useSelect('Font size', WORKSHOP_TEXT_SIZE_OPTIONS, 1, 'Props')
16
+ const fontSize = useSelect('Font size', WORKSHOP_TEXT_SIZE_OPTIONS, 2, 'Props')
17
17
  const icon = useSelect('Icon', WORKSHOP_ICON_SYMBOL_OPTIONS, 'add-circle', 'Props')
18
18
  const iconRight = useSelect('Icon (right)', WORKSHOP_ICON_SYMBOL_OPTIONS, '', 'Props')
19
19
  const justify = useSelect('Justify', WORKSHOP_FLEX_JUSTIFY_OPTIONS, 'center', 'Props')
@@ -1,4 +1,4 @@
1
- import {forwardRef, isValidElement, useMemo} from 'react'
1
+ import {forwardRef, isValidElement, useMemo, type JSX} from 'react'
2
2
  import {isValidElementType} from 'react-is'
3
3
  import {styled} from 'styled-components'
4
4
  import {useArrayProp} from '../../hooks'
@@ -9,7 +9,7 @@ import {ButtonMode, ButtonTextAlign, ButtonTone, ButtonWidth, FlexJustify} from
9
9
  import {Box} from '../box'
10
10
  import {Flex} from '../flex'
11
11
  import {Spinner} from '../spinner'
12
- import {Text, TextProps} from '../text'
12
+ import {Text} from '../text'
13
13
  import {ResponsivePaddingProps, ResponsiveRadiusProps} from '../types'
14
14
  import {buttonBaseStyles, buttonColorStyles} from './styles'
15
15
 
@@ -32,8 +32,6 @@ export interface ButtonProps extends ResponsivePaddingProps, ResponsiveRadiusPro
32
32
  textAlign?: ButtonTextAlign
33
33
  muted?: boolean
34
34
  text?: React.ReactNode
35
- textOverflow?: TextProps['textOverflow']
36
- textWeight?: TextProps['weight']
37
35
  tone?: ButtonTone
38
36
  type?: 'button' | 'reset' | 'submit'
39
37
  width?: ButtonWidth
@@ -87,8 +85,6 @@ export const Button = forwardRef(function Button(
87
85
  space: spaceProp = 3,
88
86
  text,
89
87
  textAlign,
90
- textOverflow = 'ellipsis',
91
- textWeight,
92
88
  tone = 'default',
93
89
  type = 'button',
94
90
  muted = false,
@@ -146,21 +142,20 @@ export const Button = forwardRef(function Button(
146
142
  <Box as="span" {...boxProps}>
147
143
  <Flex as="span" justify={justify} gap={space}>
148
144
  {IconComponent && (
149
- <Text as="span" size={fontSize} style={{flex: 'none'}}>
145
+ <Text size={fontSize}>
150
146
  {isValidElement(IconComponent) && IconComponent}
151
147
  {isValidElementType(IconComponent) && <IconComponent />}
152
148
  </Text>
153
149
  )}
154
150
 
155
151
  {text && (
156
- <Box as="span">
152
+ <Box>
157
153
  <Text
158
- as="span"
159
154
  muted={muted}
160
155
  align={textAlign}
161
156
  size={fontSize}
162
- textOverflow={textOverflow}
163
- weight={textWeight ?? button.textWeight}
157
+ textOverflow="ellipsis"
158
+ weight={button.textWeight}
164
159
  >
165
160
  {text}
166
161
  </Text>
@@ -168,7 +163,7 @@ export const Button = forwardRef(function Button(
168
163
  )}
169
164
 
170
165
  {IconRightComponent && (
171
- <Text as="span" size={fontSize} style={{flex: 'none'}}>
166
+ <Text size={fontSize}>
172
167
  {isValidElement(IconRightComponent) && IconRightComponent}
173
168
  {isValidElementType(IconRightComponent) && <IconRightComponent />}
174
169
  </Text>
@@ -29,8 +29,8 @@ export function buttonBaseStyles(
29
29
  border: 0;
30
30
  box-sizing: border-box;
31
31
  padding: 0;
32
- overflow: hidden;
33
32
  margin: 0;
33
+ white-space: nowrap;
34
34
  text-align: left;
35
35
  position: relative;
36
36
  vertical-align: top;
@@ -1,23 +1,21 @@
1
1
  import {Card, Container, Flex, Stack, Text} from '@sanity/ui'
2
- import {WORKSHOP_CARD_TONE_OPTIONS} from '../../../__workshop__/constants'
2
+ import {THEME_COLOR_CARD_TONES} from '@sanity/ui/theme'
3
3
 
4
4
  export default function AllTonesStory() {
5
- const tones = Object.entries(WORKSHOP_CARD_TONE_OPTIONS)
6
-
7
5
  return (
8
6
  <Flex align="center" height="fill" justify="center" padding={[4, 5, 6]} sizing="border">
9
7
  <Container width={0}>
10
- <Stack space={5}>
11
- {tones.map(([title, tone]) => (
8
+ <Stack space={3}>
9
+ {THEME_COLOR_CARD_TONES.map((tone) => (
12
10
  <Card
13
11
  key={tone}
14
- padding={4}
12
+ padding={3}
15
13
  radius={2}
16
- shadow={4}
14
+ shadow={2}
17
15
  style={{textAlign: 'center'}}
18
16
  tone={tone}
19
17
  >
20
- <Text>{title}</Text>
18
+ <Text size={1}>{tone}</Text>
21
19
  </Card>
22
20
  ))}
23
21
  </Stack>