@sanity/ui 2.1.15-canary.5 → 2.2.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 (39) hide show
  1. package/dist/index.d.mts +2 -7
  2. package/dist/index.d.ts +2 -7
  3. package/dist/index.esm.js +2964 -2988
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/index.js +2964 -2987
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +2964 -2988
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/theme.esm.js +478 -1283
  10. package/dist/theme.esm.js.map +1 -1
  11. package/dist/theme.js +478 -1283
  12. package/dist/theme.js.map +1 -1
  13. package/dist/theme.mjs +478 -1283
  14. package/dist/theme.mjs.map +1 -1
  15. package/package.json +40 -38
  16. package/src/core/components/autocomplete/autocomplete.tsx +12 -9
  17. package/src/core/components/dialog/dialog.tsx +32 -20
  18. package/src/core/components/menu/menu.tsx +6 -8
  19. package/src/core/components/menu/menuItem.tsx +10 -10
  20. package/src/core/components/tab/tab.tsx +12 -9
  21. package/src/core/components/tree/tree.tsx +8 -21
  22. package/src/core/hooks/useArrayProp.ts +10 -1
  23. package/src/core/hooks/useForwardedRef.ts +11 -9
  24. package/src/core/hooks/useIsomorphicEffect.ts +0 -1
  25. package/src/core/primitives/card/__workshop__/props.tsx +2 -0
  26. package/src/core/primitives/card/card.tsx +3 -0
  27. package/src/core/primitives/card/styles.ts +2 -2
  28. package/src/core/primitives/card/types.ts +1 -0
  29. package/src/core/primitives/checkbox/checkbox.tsx +5 -10
  30. package/src/core/primitives/popover/popover.tsx +13 -13
  31. package/src/core/primitives/radio/radio.tsx +3 -8
  32. package/src/core/primitives/select/select.tsx +3 -8
  33. package/src/core/primitives/switch/switch.tsx +4 -8
  34. package/src/core/primitives/textArea/textArea.tsx +3 -8
  35. package/src/core/primitives/textInput/textInput.tsx +4 -16
  36. package/src/core/primitives/tooltip/tooltip.tsx +5 -8
  37. package/src/core/utils/elementQuery/elementQuery.tsx +11 -10
  38. package/src/core/utils/layer/layer.tsx +13 -8
  39. package/src/core/utils/virtualList/virtualList.tsx +8 -10
@@ -19,11 +19,16 @@ import {
19
19
  memo,
20
20
  useCallback,
21
21
  useEffect,
22
- useImperativeHandle,
23
22
  useMemo,
24
23
  useRef,
25
24
  } from 'react'
26
- import {useArrayProp, useElementSize, useMediaIndex, usePrefersReducedMotion} from '../../hooks'
25
+ import {
26
+ useForwardedRef,
27
+ useArrayProp,
28
+ useElementSize,
29
+ useMediaIndex,
30
+ usePrefersReducedMotion,
31
+ } from '../../hooks'
27
32
  import {origin} from '../../middleware/origin'
28
33
  import {useTheme_v2} from '../../theme'
29
34
  import {BoxOverflow, CardTone, Placement, PopoverMargins} from '../../types'
@@ -122,7 +127,7 @@ export const Popover = memo(
122
127
  forwardRef(function Popover(
123
128
  props: PopoverProps &
124
129
  Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'children' | 'content' | 'width'>,
125
- forwardedRef: React.ForwardedRef<HTMLDivElement>,
130
+ ref: React.ForwardedRef<HTMLDivElement>,
126
131
  ): React.ReactElement {
127
132
  const {container, layer} = useTheme_v2()
128
133
  const boundaryElementContext = useBoundaryElement()
@@ -167,15 +172,10 @@ export const Popover = memo(
167
172
  const shadow = useArrayProp(shadowProp)
168
173
  const widthArrayProp = useArrayProp(widthProp)
169
174
  const zOffset = useArrayProp(zOffsetProp)
170
- const ref = useRef<HTMLDivElement | null>(null)
175
+ const forwardedRef = useForwardedRef(ref)
171
176
  const arrowRef = useRef<HTMLDivElement | null>(null)
172
177
  const rootBoundary: RootBoundary = 'viewport'
173
178
 
174
- useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(
175
- forwardedRef,
176
- () => ref.current,
177
- )
178
-
179
179
  const mediaIndex = useMediaIndex()
180
180
  const boundaryWidth = constrainSize || preventOverflow ? boundarySize?.width : undefined
181
181
 
@@ -208,7 +208,7 @@ export const Popover = memo(
208
208
 
209
209
  // Force apply width & max width to floating element
210
210
  useEffect(() => {
211
- const floatingElement = ref.current
211
+ const floatingElement = forwardedRef.current
212
212
 
213
213
  if (!open || !floatingElement) return
214
214
 
@@ -225,7 +225,7 @@ export const Popover = memo(
225
225
  if (typeof maxWidth === 'number') {
226
226
  floatingElement.style.maxWidth = `${maxWidth}px`
227
227
  }
228
- }, [width, matchReferenceWidth, maxWidth, open])
228
+ }, [width, forwardedRef, matchReferenceWidth, maxWidth, open])
229
229
 
230
230
  const middleware = useMemo(() => {
231
231
  const ret: Middleware[] = []
@@ -349,10 +349,10 @@ export const Popover = memo(
349
349
 
350
350
  const setFloating = useCallback(
351
351
  (node: HTMLDivElement | null) => {
352
- ref.current = node
352
+ forwardedRef.current = node
353
353
  refs.setFloating(node)
354
354
  },
355
- [refs],
355
+ [forwardedRef, refs],
356
356
  )
357
357
 
358
358
  const setReference = useCallback(
@@ -1,6 +1,6 @@
1
- import {forwardRef, useImperativeHandle, useRef} from 'react'
1
+ import {forwardRef} from 'react'
2
2
  import {styled} from 'styled-components'
3
- import {useCustomValidity} from '../../hooks'
3
+ import {useForwardedRef, useCustomValidity} from '../../hooks'
4
4
  import {radioBaseStyle, inputElementStyle} from './styles'
5
5
 
6
6
  /**
@@ -23,12 +23,7 @@ export const Radio = forwardRef(function Radio(
23
23
  forwardedRef: React.ForwardedRef<HTMLInputElement>,
24
24
  ) {
25
25
  const {className, disabled, style, customValidity, readOnly, ...restProps} = props
26
- const ref = useRef<HTMLInputElement | null>(null)
27
-
28
- useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(
29
- forwardedRef,
30
- () => ref.current,
31
- )
26
+ const ref = useForwardedRef(forwardedRef)
32
27
 
33
28
  useCustomValidity(ref, customValidity)
34
29
 
@@ -1,7 +1,7 @@
1
1
  import {ChevronDownIcon} from '@sanity/icons'
2
- import {forwardRef, useImperativeHandle, useRef} from 'react'
2
+ import {forwardRef} from 'react'
3
3
  import {styled} from 'styled-components'
4
- import {useCustomValidity, useArrayProp} from '../../hooks'
4
+ import {useForwardedRef, useCustomValidity, useArrayProp} from '../../hooks'
5
5
  import {Radius} from '../../types'
6
6
  import {Box} from '../box'
7
7
  import {Text} from '../text'
@@ -50,12 +50,7 @@ export const Select = forwardRef(function Select(
50
50
  ...restProps
51
51
  } = props
52
52
 
53
- const ref = useRef<HTMLSelectElement | null>(null)
54
-
55
- useImperativeHandle<HTMLSelectElement | null, HTMLSelectElement | null>(
56
- forwardedRef,
57
- () => ref.current,
58
- )
53
+ const ref = useForwardedRef(forwardedRef)
59
54
 
60
55
  useCustomValidity(ref, customValidity)
61
56
 
@@ -1,5 +1,6 @@
1
- import {forwardRef, useEffect, useImperativeHandle, useRef} from 'react'
1
+ import {forwardRef, useEffect} from 'react'
2
2
  import {styled} from 'styled-components'
3
+ import {useForwardedRef} from '../../hooks'
3
4
  import {
4
5
  switchBaseStyles,
5
6
  switchRepresentationStyles,
@@ -33,19 +34,14 @@ export const Switch = forwardRef(function Switch(
33
34
  forwardedRef: React.ForwardedRef<HTMLInputElement>,
34
35
  ) {
35
36
  const {checked, className, disabled, indeterminate, readOnly, style, ...restProps} = props
36
- const ref = useRef<HTMLInputElement | null>(null)
37
-
38
- useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(
39
- forwardedRef,
40
- () => ref.current,
41
- )
37
+ const ref = useForwardedRef(forwardedRef)
42
38
 
43
39
  useEffect(() => {
44
40
  if (ref.current) {
45
41
  // Set the indeterminate state
46
42
  ref.current.indeterminate = indeterminate || false
47
43
  }
48
- }, [indeterminate])
44
+ }, [indeterminate, ref])
49
45
 
50
46
  return (
51
47
  <Root className={className} data-ui="Switch" style={style}>
@@ -1,7 +1,7 @@
1
1
  import {ThemeFontWeightKey} from '@sanity/ui/theme'
2
- import {forwardRef, useImperativeHandle, useRef} from 'react'
2
+ import {forwardRef} from 'react'
3
3
  import {styled} from 'styled-components'
4
- import {useCustomValidity, useArrayProp} from '../../hooks'
4
+ import {useForwardedRef, useCustomValidity, useArrayProp} from '../../hooks'
5
5
  import {
6
6
  responsiveInputPaddingStyle,
7
7
  responsiveRadiusStyle,
@@ -69,15 +69,10 @@ export const TextArea = forwardRef(function TextArea(
69
69
  ...restProps
70
70
  } = props
71
71
 
72
- const ref = useRef<HTMLTextAreaElement | null>(null)
72
+ const ref = useForwardedRef(forwardedRef)
73
73
 
74
74
  const rootTheme = useRootTheme()
75
75
 
76
- useImperativeHandle<HTMLTextAreaElement | null, HTMLTextAreaElement | null>(
77
- forwardedRef,
78
- () => ref.current,
79
- )
80
-
81
76
  useCustomValidity(ref, customValidity)
82
77
 
83
78
  return (
@@ -1,18 +1,10 @@
1
1
  import {CloseIcon} from '@sanity/icons'
2
2
  import {ThemeFontWeightKey} from '@sanity/ui/theme'
3
- import {
4
- createElement,
5
- forwardRef,
6
- isValidElement,
7
- useCallback,
8
- useImperativeHandle,
9
- useMemo,
10
- useRef,
11
- } from 'react'
3
+ import {createElement, forwardRef, isValidElement, useCallback, useMemo} from 'react'
12
4
  import {isValidElementType} from 'react-is'
13
5
  import {styled} from 'styled-components'
14
6
  import {EMPTY_RECORD} from '../../constants'
15
- import {useArrayProp, useCustomValidity} from '../../hooks'
7
+ import {useArrayProp, useForwardedRef, useCustomValidity} from '../../hooks'
16
8
  import {
17
9
  responsiveRadiusStyle,
18
10
  ResponsiveRadiusStyleProps,
@@ -181,7 +173,8 @@ export const TextInput = forwardRef(function TextInput(
181
173
  weight,
182
174
  ...restProps
183
175
  } = props
184
- const ref = useRef<HTMLInputElement | null>(null)
176
+
177
+ const ref = useForwardedRef(forwardedRef)
185
178
 
186
179
  const rootTheme = useRootTheme()
187
180
 
@@ -197,11 +190,6 @@ export const TextInput = forwardRef(function TextInput(
197
190
  const $hasSuffix = Boolean(suffix)
198
191
  const $hasPrefix = Boolean(prefix)
199
192
 
200
- useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(
201
- forwardedRef,
202
- () => ref.current,
203
- )
204
-
205
193
  useCustomValidity(ref, customValidity)
206
194
 
207
195
  // Prevent the clear button from taking the focus away from the input
@@ -22,10 +22,9 @@ import {
22
22
  type ForwardedRef,
23
23
  useId,
24
24
  useSyncExternalStore,
25
- useImperativeHandle,
26
25
  } from 'react'
27
26
  import {styled} from 'styled-components'
28
- import {useArrayProp, usePrefersReducedMotion} from '../../hooks'
27
+ import {useArrayProp, useForwardedRef, usePrefersReducedMotion} from '../../hooks'
29
28
  import {useDelayedState} from '../../hooks/useDelayedState'
30
29
  import {origin} from '../../middleware/origin'
31
30
  import {useTheme_v2} from '../../theme'
@@ -98,7 +97,7 @@ const Root = styled(Layer)<{$maxWidth: number}>`
98
97
  */
99
98
  export const Tooltip = forwardRef(function Tooltip(
100
99
  props: TooltipProps & Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'children' | 'content'>,
101
- forwardedRef: React.ForwardedRef<HTMLDivElement>,
100
+ ref: React.ForwardedRef<HTMLDivElement>,
102
101
  ) {
103
102
  const boundaryElementContext = useBoundaryElement()
104
103
  const {layer} = useTheme_v2()
@@ -124,13 +123,11 @@ export const Tooltip = forwardRef(function Tooltip(
124
123
  const prefersReducedMotion = usePrefersReducedMotion()
125
124
  const animate = prefersReducedMotion ? false : _animate
126
125
  const fallbackPlacements = useArrayProp(fallbackPlacementsProp)
127
- const ref = useRef<HTMLDivElement | null>(null)
126
+ const forwardedRef = useForwardedRef(ref)
128
127
  const [referenceElement, setReferenceElement] = useState<HTMLElement | null>(null)
129
128
  const arrowRef = useRef<HTMLDivElement | null>(null)
130
129
  const rootBoundary: RootBoundary = 'viewport'
131
130
 
132
- useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
133
-
134
131
  const portal = usePortal()
135
132
  const portalElement =
136
133
  typeof portalProp === 'string' ? portal.elements?.[portalProp] || null : portal.element
@@ -351,10 +348,10 @@ export const Tooltip = forwardRef(function Tooltip(
351
348
 
352
349
  const setFloating = useCallback(
353
350
  (node: HTMLDivElement | null) => {
354
- ref.current = node
351
+ forwardedRef.current = node
355
352
  refs.setFloating(node)
356
353
  },
357
- [refs],
354
+ [forwardedRef, refs],
358
355
  )
359
356
 
360
357
  const childRef: ForwardedRef<HTMLElement | null> = (childProp as any)?.ref
@@ -1,5 +1,5 @@
1
- import {forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState} from 'react'
2
- import {useElementSize} from '../../hooks'
1
+ import {forwardRef, useCallback, useMemo, useState} from 'react'
2
+ import {useElementSize, useForwardedRef} from '../../hooks'
3
3
  import {useTheme_v2} from '../../theme'
4
4
  import {findMaxBreakpoints, findMinBreakpoints} from './helpers'
5
5
 
@@ -18,12 +18,12 @@ export interface MediaQueryProps {
18
18
  */
19
19
  export const ElementQuery = forwardRef(function ElementQuery(
20
20
  props: MediaQueryProps & Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'media'>,
21
- forwardedRef: React.ForwardedRef<HTMLDivElement>,
21
+ ref: React.ForwardedRef<HTMLDivElement>,
22
22
  ) {
23
23
  const theme = useTheme_v2()
24
24
  const {children, media = theme.media, ...restProps} = props
25
25
 
26
- const ref = useRef<HTMLDivElement | null>(null)
26
+ const forwardedRef = useForwardedRef(ref)
27
27
  const [element, setElement] = useState<HTMLDivElement | null>(null)
28
28
  const elementSize = useElementSize(element)
29
29
  const width = useMemo(() => elementSize?.border.width ?? window.innerWidth, [elementSize])
@@ -31,12 +31,13 @@ export const ElementQuery = forwardRef(function ElementQuery(
31
31
  const max = useMemo(() => findMaxBreakpoints(media, width), [media, width])
32
32
  const min = useMemo(() => findMinBreakpoints(media, width), [media, width])
33
33
 
34
- useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
35
-
36
- const setRef = useCallback((el: HTMLDivElement | null) => {
37
- ref.current = el
38
- setElement(el)
39
- }, [])
34
+ const setRef = useCallback(
35
+ (el: HTMLDivElement | null) => {
36
+ forwardedRef.current = el
37
+ setElement(el)
38
+ },
39
+ [forwardedRef],
40
+ )
40
41
 
41
42
  return (
42
43
  <div
@@ -1,7 +1,8 @@
1
- import {FocusEvent, forwardRef, useCallback, useEffect, useImperativeHandle, useRef} from 'react'
1
+ import {FocusEvent, forwardRef, useCallback, useEffect, useRef} from 'react'
2
2
  import {styled} from 'styled-components'
3
3
  import {EMPTY_RECORD} from '../../constants'
4
4
  import {containsOrEqualsElement, isHTMLElement} from '../../helpers'
5
+ import {useForwardedRef} from '../../hooks'
5
6
  import {LayerProvider} from './layerProvider'
6
7
  import {useLayer} from './useLayer'
7
8
 
@@ -24,16 +25,14 @@ const Root = styled.div({position: 'relative'})
24
25
 
25
26
  const LayerChildren = forwardRef(function LayerChildren(
26
27
  props: LayerChildrenProps & Omit<React.HTMLProps<HTMLDivElement>, 'as'>,
27
- forwardedRef: React.Ref<HTMLDivElement>,
28
+ ref: React.Ref<HTMLDivElement>,
28
29
  ) {
29
30
  const {children, onActivate, onFocus, style = EMPTY_RECORD, ...restProps} = props
30
31
  const {zIndex, isTopLayer} = useLayer()
31
32
  const lastFocusedRef = useRef<HTMLElement | null>(null)
32
- const ref = useRef<HTMLDivElement | null>(null)
33
+ const forwardedRef = useForwardedRef(ref)
33
34
  const isTopLayerRef = useRef<boolean>(isTopLayer)
34
35
 
35
- useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
36
-
37
36
  // When the layer very first mounts, it will be the top layer, but we don't want to fire
38
37
  // the callback in that case. We use a ref to track the previous value of isTopLayer to
39
38
  // determine if the layer has become the top layer since the last render.
@@ -52,7 +51,7 @@ const LayerChildren = forwardRef(function LayerChildren(
52
51
  // Call the user-provided onFocus handler if any
53
52
  onFocus?.(event)
54
53
 
55
- const rootElement = ref.current
54
+ const rootElement = forwardedRef.current
56
55
  const target = document.activeElement
57
56
 
58
57
  if (!isTopLayer || !rootElement || !target) return
@@ -61,11 +60,17 @@ const LayerChildren = forwardRef(function LayerChildren(
61
60
  lastFocusedRef.current = target
62
61
  }
63
62
  },
64
- [isTopLayer, onFocus],
63
+ [forwardedRef, isTopLayer, onFocus],
65
64
  )
66
65
 
67
66
  return (
68
- <Root {...restProps} data-ui="Layer" onFocus={handleFocus} ref={ref} style={{...style, zIndex}}>
67
+ <Root
68
+ {...restProps}
69
+ data-ui="Layer"
70
+ onFocus={handleFocus}
71
+ ref={forwardedRef}
72
+ style={{...style, zIndex}}
73
+ >
69
74
  {children}
70
75
  </Root>
71
76
  )
@@ -1,7 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import {forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'
2
+ import {forwardRef, useEffect, useMemo, useRef, useState} from 'react'
3
3
  import {styled} from 'styled-components'
4
4
  import {_isScrollable} from '../../helpers'
5
+ import {useForwardedRef} from '../../hooks'
5
6
  import {_ResizeObserver} from '../../observers'
6
7
  import {StackProps} from '../../primitives'
7
8
  import {useTheme_v2} from '../../theme'
@@ -47,19 +48,16 @@ export const VirtualList = forwardRef(function VirtualList(
47
48
  props: VirtualListProps &
48
49
  StackProps &
49
50
  Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'children' | 'onChange' | 'ref'>,
50
- forwardedRef: React.ForwardedRef<HTMLDivElement>,
51
+ ref: React.ForwardedRef<HTMLDivElement>,
51
52
  ): React.ReactElement {
52
53
  const {as = 'div', gap = 0, getItemKey, items = [], onChange, renderItem, ...restProps} = props
53
54
  const {space} = useTheme_v2()
54
- const ref = useRef<HTMLDivElement | null>(null)
55
+ const forwardedRef = useForwardedRef(ref)
55
56
  const wrapperRef = useRef<HTMLDivElement | null>(null)
56
57
  const [scrollTop, setScrollTop] = useState(0)
57
58
  const [scrollHeight, setScrollHeight] = useState(0)
58
59
  const [itemHeight, setItemHeight] = useState(-1)
59
60
 
60
- // Sync ref to parent
61
- useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
62
-
63
61
  useEffect(() => {
64
62
  if (!wrapperRef.current) return
65
63
 
@@ -71,9 +69,9 @@ export const VirtualList = forwardRef(function VirtualList(
71
69
  }, [renderItem])
72
70
 
73
71
  useEffect(() => {
74
- if (!ref.current) return
72
+ if (!forwardedRef.current) return
75
73
 
76
- let _scrollEl = ref.current.parentNode
74
+ let _scrollEl = forwardedRef.current.parentNode
77
75
 
78
76
  while (_scrollEl && !_isScrollable(_scrollEl)) {
79
77
  _scrollEl = _scrollEl.parentNode
@@ -125,7 +123,7 @@ export const VirtualList = forwardRef(function VirtualList(
125
123
  window.removeEventListener('scroll', handleScroll)
126
124
  window.removeEventListener('resize', handleResize)
127
125
  }
128
- }, [])
126
+ }, [forwardedRef])
129
127
 
130
128
  const len = items.length
131
129
  const height = itemHeight ? len * (itemHeight + space[gap]) - space[gap] : 0
@@ -160,7 +158,7 @@ export const VirtualList = forwardRef(function VirtualList(
160
158
  const wrapperStyle = useMemo(() => ({height}), [height])
161
159
 
162
160
  return (
163
- <Root as={as} data-ui="VirtualList" {...restProps} ref={ref}>
161
+ <Root as={as} data-ui="VirtualList" {...restProps} ref={forwardedRef}>
164
162
  <div ref={wrapperRef} style={wrapperStyle}>
165
163
  {children}
166
164
  </div>