@sanity/ui 2.2.0 → 2.3.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.
@@ -1,7 +1,7 @@
1
1
  import {ThemeFontWeightKey} from '@sanity/ui/theme'
2
- import {forwardRef} from 'react'
2
+ import {forwardRef, useImperativeHandle, useRef} from 'react'
3
3
  import {styled} from 'styled-components'
4
- import {useForwardedRef, useCustomValidity, useArrayProp} from '../../hooks'
4
+ import {useCustomValidity, useArrayProp} from '../../hooks'
5
5
  import {
6
6
  responsiveInputPaddingStyle,
7
7
  responsiveRadiusStyle,
@@ -69,10 +69,15 @@ export const TextArea = forwardRef(function TextArea(
69
69
  ...restProps
70
70
  } = props
71
71
 
72
- const ref = useForwardedRef(forwardedRef)
72
+ const ref = useRef<HTMLTextAreaElement | null>(null)
73
73
 
74
74
  const rootTheme = useRootTheme()
75
75
 
76
+ useImperativeHandle<HTMLTextAreaElement | null, HTMLTextAreaElement | null>(
77
+ forwardedRef,
78
+ () => ref.current,
79
+ )
80
+
76
81
  useCustomValidity(ref, customValidity)
77
82
 
78
83
  return (
@@ -1,10 +1,18 @@
1
1
  import {CloseIcon} from '@sanity/icons'
2
2
  import {ThemeFontWeightKey} from '@sanity/ui/theme'
3
- import {createElement, forwardRef, isValidElement, useCallback, useMemo} from 'react'
3
+ import {
4
+ createElement,
5
+ forwardRef,
6
+ isValidElement,
7
+ useCallback,
8
+ useImperativeHandle,
9
+ useMemo,
10
+ useRef,
11
+ } from 'react'
4
12
  import {isValidElementType} from 'react-is'
5
13
  import {styled} from 'styled-components'
6
14
  import {EMPTY_RECORD} from '../../constants'
7
- import {useArrayProp, useForwardedRef, useCustomValidity} from '../../hooks'
15
+ import {useArrayProp, useCustomValidity} from '../../hooks'
8
16
  import {
9
17
  responsiveRadiusStyle,
10
18
  ResponsiveRadiusStyleProps,
@@ -173,8 +181,7 @@ export const TextInput = forwardRef(function TextInput(
173
181
  weight,
174
182
  ...restProps
175
183
  } = props
176
-
177
- const ref = useForwardedRef(forwardedRef)
184
+ const ref = useRef<HTMLInputElement | null>(null)
178
185
 
179
186
  const rootTheme = useRootTheme()
180
187
 
@@ -190,6 +197,11 @@ export const TextInput = forwardRef(function TextInput(
190
197
  const $hasSuffix = Boolean(suffix)
191
198
  const $hasPrefix = Boolean(prefix)
192
199
 
200
+ useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(
201
+ forwardedRef,
202
+ () => ref.current,
203
+ )
204
+
193
205
  useCustomValidity(ref, customValidity)
194
206
 
195
207
  // Prevent the clear button from taking the focus away from the input
@@ -22,9 +22,10 @@ import {
22
22
  type ForwardedRef,
23
23
  useId,
24
24
  useSyncExternalStore,
25
+ useImperativeHandle,
25
26
  } from 'react'
26
27
  import {styled} from 'styled-components'
27
- import {useArrayProp, useForwardedRef, usePrefersReducedMotion} from '../../hooks'
28
+ import {useArrayProp, usePrefersReducedMotion} from '../../hooks'
28
29
  import {useDelayedState} from '../../hooks/useDelayedState'
29
30
  import {origin} from '../../middleware/origin'
30
31
  import {useTheme_v2} from '../../theme'
@@ -97,7 +98,7 @@ const Root = styled(Layer)<{$maxWidth: number}>`
97
98
  */
98
99
  export const Tooltip = forwardRef(function Tooltip(
99
100
  props: TooltipProps & Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'children' | 'content'>,
100
- ref: React.ForwardedRef<HTMLDivElement>,
101
+ forwardedRef: React.ForwardedRef<HTMLDivElement>,
101
102
  ) {
102
103
  const boundaryElementContext = useBoundaryElement()
103
104
  const {layer} = useTheme_v2()
@@ -123,11 +124,13 @@ export const Tooltip = forwardRef(function Tooltip(
123
124
  const prefersReducedMotion = usePrefersReducedMotion()
124
125
  const animate = prefersReducedMotion ? false : _animate
125
126
  const fallbackPlacements = useArrayProp(fallbackPlacementsProp)
126
- const forwardedRef = useForwardedRef(ref)
127
+ const ref = useRef<HTMLDivElement | null>(null)
127
128
  const [referenceElement, setReferenceElement] = useState<HTMLElement | null>(null)
128
129
  const arrowRef = useRef<HTMLDivElement | null>(null)
129
130
  const rootBoundary: RootBoundary = 'viewport'
130
131
 
132
+ useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
133
+
131
134
  const portal = usePortal()
132
135
  const portalElement =
133
136
  typeof portalProp === 'string' ? portal.elements?.[portalProp] || null : portal.element
@@ -348,10 +351,10 @@ export const Tooltip = forwardRef(function Tooltip(
348
351
 
349
352
  const setFloating = useCallback(
350
353
  (node: HTMLDivElement | null) => {
351
- forwardedRef.current = node
354
+ ref.current = node
352
355
  refs.setFloating(node)
353
356
  },
354
- [forwardedRef, refs],
357
+ [refs],
355
358
  )
356
359
 
357
360
  const childRef: ForwardedRef<HTMLElement | null> = (childProp as any)?.ref
@@ -1,5 +1,5 @@
1
- import {forwardRef, useCallback, useMemo, useState} from 'react'
2
- import {useElementSize, useForwardedRef} from '../../hooks'
1
+ import {forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState} from 'react'
2
+ import {useElementSize} 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
- ref: React.ForwardedRef<HTMLDivElement>,
21
+ forwardedRef: React.ForwardedRef<HTMLDivElement>,
22
22
  ) {
23
23
  const theme = useTheme_v2()
24
24
  const {children, media = theme.media, ...restProps} = props
25
25
 
26
- const forwardedRef = useForwardedRef(ref)
26
+ const ref = useRef<HTMLDivElement | null>(null)
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,13 +31,12 @@ 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
- const setRef = useCallback(
35
- (el: HTMLDivElement | null) => {
36
- forwardedRef.current = el
37
- setElement(el)
38
- },
39
- [forwardedRef],
40
- )
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
+ }, [])
41
40
 
42
41
  return (
43
42
  <div
@@ -1,8 +1,7 @@
1
- import {FocusEvent, forwardRef, useCallback, useEffect, useRef} from 'react'
1
+ import {FocusEvent, forwardRef, useCallback, useEffect, useImperativeHandle, 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'
6
5
  import {LayerProvider} from './layerProvider'
7
6
  import {useLayer} from './useLayer'
8
7
 
@@ -25,14 +24,16 @@ const Root = styled.div({position: 'relative'})
25
24
 
26
25
  const LayerChildren = forwardRef(function LayerChildren(
27
26
  props: LayerChildrenProps & Omit<React.HTMLProps<HTMLDivElement>, 'as'>,
28
- ref: React.Ref<HTMLDivElement>,
27
+ forwardedRef: React.Ref<HTMLDivElement>,
29
28
  ) {
30
29
  const {children, onActivate, onFocus, style = EMPTY_RECORD, ...restProps} = props
31
30
  const {zIndex, isTopLayer} = useLayer()
32
31
  const lastFocusedRef = useRef<HTMLElement | null>(null)
33
- const forwardedRef = useForwardedRef(ref)
32
+ const ref = useRef<HTMLDivElement | null>(null)
34
33
  const isTopLayerRef = useRef<boolean>(isTopLayer)
35
34
 
35
+ useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
36
+
36
37
  // When the layer very first mounts, it will be the top layer, but we don't want to fire
37
38
  // the callback in that case. We use a ref to track the previous value of isTopLayer to
38
39
  // determine if the layer has become the top layer since the last render.
@@ -51,7 +52,7 @@ const LayerChildren = forwardRef(function LayerChildren(
51
52
  // Call the user-provided onFocus handler if any
52
53
  onFocus?.(event)
53
54
 
54
- const rootElement = forwardedRef.current
55
+ const rootElement = ref.current
55
56
  const target = document.activeElement
56
57
 
57
58
  if (!isTopLayer || !rootElement || !target) return
@@ -60,17 +61,11 @@ const LayerChildren = forwardRef(function LayerChildren(
60
61
  lastFocusedRef.current = target
61
62
  }
62
63
  },
63
- [forwardedRef, isTopLayer, onFocus],
64
+ [isTopLayer, onFocus],
64
65
  )
65
66
 
66
67
  return (
67
- <Root
68
- {...restProps}
69
- data-ui="Layer"
70
- onFocus={handleFocus}
71
- ref={forwardedRef}
72
- style={{...style, zIndex}}
73
- >
68
+ <Root {...restProps} data-ui="Layer" onFocus={handleFocus} ref={ref} style={{...style, zIndex}}>
74
69
  {children}
75
70
  </Root>
76
71
  )
@@ -1,8 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import {forwardRef, useEffect, useMemo, useRef, useState} from 'react'
2
+ import {forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'
3
3
  import {styled} from 'styled-components'
4
4
  import {_isScrollable} from '../../helpers'
5
- import {useForwardedRef} from '../../hooks'
6
5
  import {_ResizeObserver} from '../../observers'
7
6
  import {StackProps} from '../../primitives'
8
7
  import {useTheme_v2} from '../../theme'
@@ -48,16 +47,19 @@ export const VirtualList = forwardRef(function VirtualList(
48
47
  props: VirtualListProps &
49
48
  StackProps &
50
49
  Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'children' | 'onChange' | 'ref'>,
51
- ref: React.ForwardedRef<HTMLDivElement>,
50
+ forwardedRef: React.ForwardedRef<HTMLDivElement>,
52
51
  ): React.ReactElement {
53
52
  const {as = 'div', gap = 0, getItemKey, items = [], onChange, renderItem, ...restProps} = props
54
53
  const {space} = useTheme_v2()
55
- const forwardedRef = useForwardedRef(ref)
54
+ const ref = useRef<HTMLDivElement | null>(null)
56
55
  const wrapperRef = useRef<HTMLDivElement | null>(null)
57
56
  const [scrollTop, setScrollTop] = useState(0)
58
57
  const [scrollHeight, setScrollHeight] = useState(0)
59
58
  const [itemHeight, setItemHeight] = useState(-1)
60
59
 
60
+ // Sync ref to parent
61
+ useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
62
+
61
63
  useEffect(() => {
62
64
  if (!wrapperRef.current) return
63
65
 
@@ -69,9 +71,9 @@ export const VirtualList = forwardRef(function VirtualList(
69
71
  }, [renderItem])
70
72
 
71
73
  useEffect(() => {
72
- if (!forwardedRef.current) return
74
+ if (!ref.current) return
73
75
 
74
- let _scrollEl = forwardedRef.current.parentNode
76
+ let _scrollEl = ref.current.parentNode
75
77
 
76
78
  while (_scrollEl && !_isScrollable(_scrollEl)) {
77
79
  _scrollEl = _scrollEl.parentNode
@@ -123,7 +125,7 @@ export const VirtualList = forwardRef(function VirtualList(
123
125
  window.removeEventListener('scroll', handleScroll)
124
126
  window.removeEventListener('resize', handleResize)
125
127
  }
126
- }, [forwardedRef])
128
+ }, [])
127
129
 
128
130
  const len = items.length
129
131
  const height = itemHeight ? len * (itemHeight + space[gap]) - space[gap] : 0
@@ -158,7 +160,7 @@ export const VirtualList = forwardRef(function VirtualList(
158
160
  const wrapperStyle = useMemo(() => ({height}), [height])
159
161
 
160
162
  return (
161
- <Root as={as} data-ui="VirtualList" {...restProps} ref={forwardedRef}>
163
+ <Root as={as} data-ui="VirtualList" {...restProps} ref={ref}>
162
164
  <div ref={wrapperRef} style={wrapperStyle}>
163
165
  {children}
164
166
  </div>