@sanity/ui 2.9.0-canary.6 → 2.9.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 (84) hide show
  1. package/dist/_chunks-cjs/getTheme_v2.js +26 -0
  2. package/dist/_chunks-cjs/getTheme_v2.js.map +1 -1
  3. package/dist/_chunks-es/getTheme_v2.mjs +26 -0
  4. package/dist/_chunks-es/getTheme_v2.mjs.map +1 -1
  5. package/dist/_legacy/getTheme_v2.esm.js +26 -0
  6. package/dist/_legacy/getTheme_v2.esm.js.map +1 -1
  7. package/dist/index.esm.js +652 -1048
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +651 -1047
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +652 -1048
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/theme.d.mts +22 -28
  14. package/dist/theme.d.ts +22 -28
  15. package/dist/theme.esm.js +124 -260
  16. package/dist/theme.esm.js.map +1 -1
  17. package/dist/theme.js +124 -260
  18. package/dist/theme.js.map +1 -1
  19. package/dist/theme.mjs +124 -260
  20. package/dist/theme.mjs.map +1 -1
  21. package/package.json +60 -53
  22. package/src/core/__workshop__/constants.ts +27 -31
  23. package/src/core/components/autocomplete/autocomplete.tsx +31 -42
  24. package/src/core/components/breadcrumbs/breadcrumbs.tsx +47 -35
  25. package/src/core/components/dialog/dialog.tsx +1 -1
  26. package/src/core/components/menu/__workshop__/tones.tsx +5 -6
  27. package/src/core/components/menu/useMenuController.ts +21 -18
  28. package/src/core/components/toast/__workshop__/hook.tsx +1 -1
  29. package/src/core/components/toast/toast.tsx +2 -2
  30. package/src/core/components/tree/treeItem.tsx +15 -18
  31. package/src/core/hooks/useElementRect/__workshop__/example.tsx +2 -2
  32. package/src/core/hooks/useMatchMedia.ts +22 -12
  33. package/src/core/primitives/badge/__workshop__/tones.tsx +6 -13
  34. package/src/core/primitives/button/__workshop__/custom.tsx +4 -3
  35. package/src/core/primitives/card/__workshop__/allTones.tsx +6 -8
  36. package/src/core/primitives/card/__workshop__/asButton.tsx +42 -24
  37. package/src/core/primitives/card/__workshop__/asComponent.tsx +1 -1
  38. package/src/core/primitives/card/__workshop__/checkered.tsx +17 -7
  39. package/src/core/primitives/card/__workshop__/interactive.tsx +10 -3
  40. package/src/core/primitives/card/__workshop__/listNavigation.tsx +5 -3
  41. package/src/core/primitives/card/__workshop__/props.tsx +7 -5
  42. package/src/core/primitives/card/__workshop__/selected.tsx +4 -1
  43. package/src/core/primitives/checkbox/__workshop__/tones.tsx +6 -0
  44. package/src/core/primitives/code/__workshop__/props.tsx +1 -1
  45. package/src/core/primitives/container/__workshop__/example.tsx +1 -1
  46. package/src/core/primitives/flex/__workshop__/example.tsx +3 -3
  47. package/src/core/primitives/grid/__workshop__/responsive.tsx +12 -12
  48. package/src/core/primitives/heading/__workshop__/opticalAlignment.tsx +13 -25
  49. package/src/core/primitives/inline/__workshop__/plain.tsx +3 -3
  50. package/src/core/primitives/label/__workshop__/opticalAlignment.tsx +6 -6
  51. package/src/core/primitives/popover/__workshop__/AlignedStory.tsx +1 -1
  52. package/src/core/primitives/popover/__workshop__/MatchReferenceWidthStory.tsx +3 -3
  53. package/src/core/primitives/popover/__workshop__/OpenOnMountStory.tsx +1 -1
  54. package/src/core/primitives/popover/__workshop__/PlainStory.tsx +1 -1
  55. package/src/core/primitives/popover/popover.tsx +140 -74
  56. package/src/core/primitives/popover/popoverCard.tsx +4 -1
  57. package/src/core/primitives/radio/__workshop__/plain.tsx +2 -2
  58. package/src/core/primitives/select/__workshop__/readOnly.tsx +1 -1
  59. package/src/core/primitives/text/__workshop__/example.tsx +6 -6
  60. package/src/core/primitives/text/__workshop__/opticalAlignment.tsx +11 -21
  61. package/src/core/primitives/textArea/__workshop__/plain.tsx +1 -1
  62. package/src/core/primitives/textInput/__workshop__/index.ts +0 -5
  63. package/src/core/primitives/textInput/__workshop__/plain.tsx +1 -2
  64. package/src/core/primitives/textInput/__workshop__/readOnly.tsx +6 -2
  65. package/src/core/primitives/textInput/__workshop__/tones.tsx +38 -0
  66. package/src/core/primitives/tooltip/tooltip.tsx +17 -47
  67. package/src/core/styles/card/_cardColorStyle.ts +8 -0
  68. package/src/core/theme/__workshop__/debug/story.tsx +18 -11
  69. package/src/core/utils/portal/portalProvider.tsx +5 -5
  70. package/src/theme/build/_deprecated/color/factory.ts +1 -1
  71. package/src/theme/build/_deprecated/color/muted/createMuted.ts +2 -2
  72. package/src/theme/build/buildColorTheme.ts +47 -37
  73. package/src/theme/build/renderColorTheme.ts +26 -30
  74. package/src/theme/build/resolveColorTokens.ts +51 -115
  75. package/src/theme/config/tokens/color/types.ts +4 -18
  76. package/src/theme/defaults/colorTokens.ts +8 -5
  77. package/src/theme/getScopedTheme.ts +9 -2
  78. package/src/theme/system/color/_constants.ts +6 -2
  79. package/src/theme/system/color/badge.ts +3 -7
  80. package/src/theme/system/v0/color/_system.ts +8 -4
  81. package/src/theme/system/v0/color/color.ts +3 -2
  82. package/src/theme/versioning/themeColor_v0_v2.ts +26 -0
  83. package/src/theme/versioning/v0_v2.ts +4 -0
  84. package/src/core/primitives/textInput/__workshop__/multipleTones.tsx +0 -20
@@ -11,40 +11,40 @@ export default function ResponsiveStory() {
11
11
  rows={[1, 2, 3, 4, 5, 6, 7]}
12
12
  style={{textAlign: 'center'}}
13
13
  >
14
- <Card padding={[1, 2, 3]} tone="transparent">
14
+ <Card padding={[1, 2, 3]} tone="neutral">
15
15
  <Code>1</Code>
16
16
  </Card>
17
- <Card padding={[1, 2, 3]} tone="transparent">
17
+ <Card padding={[1, 2, 3]} tone="neutral">
18
18
  <Code>2</Code>
19
19
  </Card>
20
- <Card padding={[1, 2, 3]} tone="transparent">
20
+ <Card padding={[1, 2, 3]} tone="neutral">
21
21
  <Code>3</Code>
22
22
  </Card>
23
- <Card padding={[1, 2, 3]} tone="transparent">
23
+ <Card padding={[1, 2, 3]} tone="neutral">
24
24
  <Code>4</Code>
25
25
  </Card>
26
- <Card padding={[1, 2, 3]} tone="transparent">
26
+ <Card padding={[1, 2, 3]} tone="neutral">
27
27
  <Code>5</Code>
28
28
  </Card>
29
- <Card padding={[1, 2, 3]} tone="transparent">
29
+ <Card padding={[1, 2, 3]} tone="neutral">
30
30
  <Code>6</Code>
31
31
  </Card>
32
- <Card padding={[1, 2, 3]} tone="transparent">
32
+ <Card padding={[1, 2, 3]} tone="neutral">
33
33
  <Code>7</Code>
34
34
  </Card>
35
- <Card padding={[1, 2, 3]} tone="transparent">
35
+ <Card padding={[1, 2, 3]} tone="neutral">
36
36
  <Code>8</Code>
37
37
  </Card>
38
- <Card padding={[1, 2, 3]} tone="transparent">
38
+ <Card padding={[1, 2, 3]} tone="neutral">
39
39
  <Code>9</Code>
40
40
  </Card>
41
- <Card padding={[1, 2, 3]} tone="transparent">
41
+ <Card padding={[1, 2, 3]} tone="neutral">
42
42
  <Code>10</Code>
43
43
  </Card>
44
- <Card padding={[1, 2, 3]} tone="transparent">
44
+ <Card padding={[1, 2, 3]} tone="neutral">
45
45
  <Code>11</Code>
46
46
  </Card>
47
- <Card padding={[1, 2, 3]} tone="transparent">
47
+ <Card padding={[1, 2, 3]} tone="neutral">
48
48
  <Code>12</Code>
49
49
  </Card>
50
50
  </Grid>
@@ -6,55 +6,43 @@ export default function OpticalAlignment() {
6
6
  <Box padding={[4, 5, 6]}>
7
7
  <Stack space={1}>
8
8
  <Flex>
9
- <Card padding={0} scheme="dark">
10
- <Heading accent size={5}>
11
- Hamburgefonstiv M
12
- </Heading>
9
+ <Card padding={0} tone="neutral">
10
+ <Heading size={5}>Hamburgefonstiv M</Heading>
13
11
  </Card>
14
12
  </Flex>
15
13
 
16
14
  <Flex>
17
- <Card padding={0} scheme="dark">
18
- <Heading accent size={4}>
19
- Hamburgefonstiv M
20
- </Heading>
15
+ <Card padding={0} tone="neutral">
16
+ <Heading size={4}>Hamburgefonstiv M</Heading>
21
17
  </Card>
22
18
  </Flex>
23
19
 
24
20
  <Flex>
25
- <Card padding={0} scheme="dark">
26
- <Heading accent size={3}>
27
- Hamburgefonstiv M
28
- </Heading>
21
+ <Card padding={0} tone="neutral">
22
+ <Heading size={3}>Hamburgefonstiv M</Heading>
29
23
  </Card>
30
24
  </Flex>
31
25
 
32
26
  <Flex>
33
- <Card padding={0} scheme="dark">
34
- <Heading accent size={2}>
35
- Hamburgefonstiv M
36
- </Heading>
27
+ <Card padding={0} tone="neutral">
28
+ <Heading size={2}>Hamburgefonstiv M</Heading>
37
29
  </Card>
38
30
  </Flex>
39
31
 
40
32
  <Flex>
41
- <Card padding={0} scheme="dark">
42
- <Heading accent size={1}>
43
- Hamburgefonstiv M
44
- </Heading>
33
+ <Card padding={0} tone="neutral">
34
+ <Heading size={1}>Hamburgefonstiv M</Heading>
45
35
  </Card>
46
36
  </Flex>
47
37
 
48
38
  <Flex>
49
- <Card padding={0} scheme="dark">
50
- <Heading accent size={0}>
51
- Hamburgefonstiv M
52
- </Heading>
39
+ <Card padding={0} tone="neutral">
40
+ <Heading size={0}>Hamburgefonstiv M</Heading>
53
41
  </Card>
54
42
  </Flex>
55
43
 
56
44
  <Flex>
57
- <Card padding={2} scheme="dark">
45
+ <Card padding={2} tone="neutral">
58
46
  <Heading accent>
59
47
  <AddCircleIcon />
60
48
  </Heading>
@@ -10,15 +10,15 @@ export default function PlainStory() {
10
10
  space={useSelect('Space', WORKSHOP_SPACE_OPTIONS, 0, 'Props')}
11
11
  >
12
12
  <Card padding={1} shadow={1}>
13
- <Text>Inline item</Text>
13
+ <Text size={1}>Inline item</Text>
14
14
  </Card>
15
15
 
16
16
  <Card padding={2} shadow={1}>
17
- <Text>Inline item</Text>
17
+ <Text size={1}>Inline item</Text>
18
18
  </Card>
19
19
 
20
20
  <Card padding={3} shadow={1}>
21
- <Text>Inline item</Text>
21
+ <Text size={1}>Inline item</Text>
22
22
  </Card>
23
23
  </Inline>
24
24
  </Flex>
@@ -6,37 +6,37 @@ export default function OpticalAlignment() {
6
6
  <Box padding={[4, 5, 6]}>
7
7
  <Stack space={1}>
8
8
  <Flex>
9
- <Card padding={0} scheme="dark">
9
+ <Card padding={0} tone="suggest">
10
10
  <Label size={4}>Hamburgefonstiv M</Label>
11
11
  </Card>
12
12
  </Flex>
13
13
 
14
14
  <Flex>
15
- <Card padding={0} scheme="dark">
15
+ <Card padding={0} tone="suggest">
16
16
  <Label size={3}>Hamburgefonstiv M</Label>
17
17
  </Card>
18
18
  </Flex>
19
19
 
20
20
  <Flex>
21
- <Card padding={0} scheme="dark">
21
+ <Card padding={0} tone="suggest">
22
22
  <Label size={2}>Hamburgefonstiv M</Label>
23
23
  </Card>
24
24
  </Flex>
25
25
 
26
26
  <Flex>
27
- <Card padding={0} scheme="dark">
27
+ <Card padding={0} tone="suggest">
28
28
  <Label size={1}>Hamburgefonstiv M</Label>
29
29
  </Card>
30
30
  </Flex>
31
31
 
32
32
  <Flex>
33
- <Card padding={0} scheme="dark">
33
+ <Card padding={0} tone="suggest">
34
34
  <Label size={0}>Hamburgefonstiv M</Label>
35
35
  </Card>
36
36
  </Flex>
37
37
 
38
38
  <Flex>
39
- <Card padding={2} scheme="dark">
39
+ <Card padding={2} tone="suggest">
40
40
  <Label>
41
41
  <AddCircleIcon />
42
42
  </Label>
@@ -24,7 +24,7 @@ export default function AlignedStory() {
24
24
  const popoverElementRef = useRef<HTMLDivElement | null>(null)
25
25
 
26
26
  const content = (
27
- <Text>
27
+ <Text size={1}>
28
28
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque at nisl at sem tempor
29
29
  hendrerit scelerisque ut libero. Maecenas iaculis efficitur lorem, ac faucibus mi imperdiet
30
30
  quis. Cras a consectetur erat. Fusce imperdiet, dolor et pellentesque iaculis, ex quam luctus
@@ -11,8 +11,8 @@ export default function MatchReferenceWidthStory() {
11
11
  <Popover
12
12
  arrow={arrow}
13
13
  content={
14
- <Box padding={2}>
15
- <Text>Content</Text>
14
+ <Box padding={3}>
15
+ <Text size={1}>Content</Text>
16
16
  </Box>
17
17
  }
18
18
  matchReferenceWidth
@@ -22,7 +22,7 @@ export default function MatchReferenceWidthStory() {
22
22
  >
23
23
  <Container width={0}>
24
24
  <Stack>
25
- <Button fontSize={3} text="Button" />
25
+ <Button fontSize={1} mode="ghost" selected text="Button" />
26
26
  </Stack>
27
27
  </Container>
28
28
  </Popover>
@@ -3,7 +3,7 @@ import {Button, Card, Popover, Text} from '@sanity/ui'
3
3
  export default function OpenOnMountStory() {
4
4
  return (
5
5
  <Card height="fill" padding={3} sizing="border">
6
- <Popover content={<Text>popover content</Text>} open padding={3}>
6
+ <Popover content={<Text size={1}>popover content</Text>} open padding={3}>
7
7
  <Button text="This button is the popover reference" />
8
8
  </Popover>
9
9
  </Card>
@@ -48,7 +48,7 @@ export default function PlainStory() {
48
48
  arrow={arrow}
49
49
  boundaryElement={boundaryElementFlag ? boundaryElement : undefined}
50
50
  constrainSize={constrainSize}
51
- content={<Text>popover content</Text>}
51
+ content={<Text size={1}>popover content</Text>}
52
52
  fallbackPlacements={['top', 'bottom']}
53
53
  matchReferenceWidth={matchReferenceWidth}
54
54
  open={open}
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import {
3
+ Middleware,
3
4
  RootBoundary,
4
5
  arrow,
5
6
  autoUpdate,
@@ -20,10 +21,8 @@ import {
20
21
  useCallback,
21
22
  useEffect,
22
23
  useImperativeHandle,
23
- useLayoutEffect,
24
24
  useMemo,
25
25
  useRef,
26
- useState,
27
26
  } from 'react'
28
27
  import {useArrayProp, useElementSize, useMediaIndex, usePrefersReducedMotion} from '../../hooks'
29
28
  import {origin} from '../../middleware/origin'
@@ -127,14 +126,15 @@ export const Popover = memo(
127
126
  __unstable_margins: margins = DEFAULT_POPOVER_MARGINS,
128
127
  animate: _animate = false,
129
128
  arrow: arrowProp = false,
130
- boundaryElement: _boundaryElement,
129
+ boundaryElement = boundaryElementContext.element,
131
130
  children: childProp,
132
131
  constrainSize = false,
133
132
  content,
134
133
  disabled,
135
- fallbackPlacements: _fallbackPlacements,
134
+ fallbackPlacements = props.fallbackPlacements ??
135
+ DEFAULT_FALLBACK_PLACEMENTS[props.placement ?? 'bottom'],
136
136
  matchReferenceWidth,
137
- floatingBoundary: _floatingBoundary,
137
+ floatingBoundary = props.boundaryElement ?? boundaryElementContext.element,
138
138
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
139
139
  onActivate,
140
140
  open,
@@ -144,23 +144,16 @@ export const Popover = memo(
144
144
  portal,
145
145
  preventOverflow = true,
146
146
  radius: radiusProp = 3,
147
- referenceBoundary: _referenceBoundary,
147
+ referenceBoundary = props.boundaryElement ?? boundaryElementContext.element,
148
148
  referenceElement,
149
149
  scheme,
150
150
  shadow: shadowProp = 3,
151
151
  tone = 'inherit',
152
152
  width: widthProp = 'auto',
153
- zOffset: _zOffsetProp,
153
+ zOffset: zOffsetProp = layer.popover.zOffset,
154
154
  updateRef,
155
155
  ...restProps
156
156
  } = props
157
- const boundaryElement = _boundaryElement ?? boundaryElementContext.element
158
- const fallbackPlacements =
159
- _fallbackPlacements ?? DEFAULT_FALLBACK_PLACEMENTS[props.placement ?? 'bottom']
160
- const floatingBoundary = _floatingBoundary ?? boundaryElement ?? boundaryElementContext.element
161
- const referenceBoundary =
162
- _referenceBoundary ?? boundaryElement ?? boundaryElementContext.element
163
- const zOffsetProp = _zOffsetProp ?? layer.popover.zOffset
164
157
  const prefersReducedMotion = usePrefersReducedMotion()
165
158
  const animate = prefersReducedMotion ? false : _animate
166
159
  const boundarySize = useElementSize(boundaryElement)?.border
@@ -170,7 +163,7 @@ export const Popover = memo(
170
163
  const widthArrayProp = useArrayProp(widthProp)
171
164
  const zOffset = useArrayProp(zOffsetProp)
172
165
  const ref = useRef<HTMLDivElement | null>(null)
173
- const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
166
+ const arrowRef = useRef<HTMLDivElement | null>(null)
174
167
  const rootBoundary: RootBoundary = 'viewport'
175
168
 
176
169
  useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(
@@ -181,70 +174,94 @@ export const Popover = memo(
181
174
  const mediaIndex = useMediaIndex()
182
175
  const boundaryWidth = constrainSize || preventOverflow ? boundarySize?.width : undefined
183
176
 
177
+ // Update width when
178
+ // - media index changes
179
+ // - `width` property changes
180
+ const width = calcCurrentWidth({
181
+ container,
182
+ mediaIndex,
183
+ width: widthArrayProp,
184
+ })
185
+ const widthRef = useRef(width)
186
+
187
+ useEffect(() => {
188
+ widthRef.current = width
189
+ }, [width])
190
+
191
+ // Update max width when
192
+ // - boundary width changes
193
+ // - `width` property changes
194
+ const maxWidth = calcMaxWidth({boundaryWidth, currentWidth: width})
195
+ const maxWidthRef = useRef(maxWidth)
196
+
197
+ useEffect(() => {
198
+ maxWidthRef.current = maxWidth
199
+ }, [maxWidth])
200
+
201
+ // Keep track of reference element width (see `size` middleware below)
202
+ const referenceWidthRef = useRef<number>()
203
+
184
204
  // Force apply width & max width to floating element
185
- useLayoutEffect(() => {
205
+ useEffect(() => {
186
206
  const floatingElement = ref.current
187
207
 
188
- // If constrainSize or matchReferenceWidth is true, then the styles are set by the `size` middleware
189
- if (!open || !floatingElement || constrainSize || matchReferenceWidth) return
208
+ if (!open || !floatingElement) return
190
209
 
191
- const currentWidth = calcCurrentWidth({
192
- container,
193
- mediaIndex,
194
- width: widthArrayProp,
195
- })
196
- const maxWidth = calcMaxWidth({boundaryWidth, currentWidth})
210
+ const referenceWidth = referenceWidthRef.current
197
211
 
198
- if (currentWidth !== undefined) {
199
- floatingElement.style.width = `${currentWidth}px`
212
+ if (matchReferenceWidth) {
213
+ if (referenceWidth !== undefined) {
214
+ floatingElement.style.width = `${referenceWidth}px`
215
+ }
216
+ } else if (width !== undefined) {
217
+ floatingElement.style.width = `${width}px`
200
218
  }
201
219
 
202
220
  if (typeof maxWidth === 'number') {
203
221
  floatingElement.style.maxWidth = `${maxWidth}px`
204
222
  }
205
- }, [
206
- boundaryWidth,
207
- constrainSize,
208
- container,
209
- matchReferenceWidth,
210
- mediaIndex,
211
- open,
212
- widthArrayProp,
213
- ])
223
+ }, [width, matchReferenceWidth, maxWidth, open])
214
224
 
215
- const {x, y, middlewareData, placement, refs, strategy, update} = useFloating({
216
- middleware: [
217
- // Flip the floating element when leaving the boundary box
218
- (constrainSize || preventOverflow) &&
225
+ const middleware = useMemo(() => {
226
+ const ret: Middleware[] = []
227
+
228
+ // Flip the floating element when leaving the boundary box
229
+ if (constrainSize || preventOverflow) {
230
+ ret.push(
219
231
  flip({
220
232
  boundary: floatingBoundary || undefined,
221
233
  fallbackPlacements,
222
234
  padding: DEFAULT_POPOVER_PADDING,
223
235
  rootBoundary,
224
236
  }),
225
- // Define distance between reference and floating element
226
- offset({mainAxis: DEFAULT_POPOVER_DISTANCE}),
227
- // Track sizes
228
- (constrainSize || matchReferenceWidth) &&
237
+ )
238
+ }
239
+
240
+ // Define distance between reference and floating element
241
+ ret.push(offset({mainAxis: DEFAULT_POPOVER_DISTANCE}))
242
+
243
+ // Track sizes
244
+ if (constrainSize || matchReferenceWidth) {
245
+ ret.push(
229
246
  size({
230
247
  apply({availableWidth, availableHeight, elements, referenceWidth}) {
231
- const currentWidth = calcCurrentWidth({
232
- container,
233
- mediaIndex,
234
- width: widthArrayProp,
235
- })
236
- const maxWidth = calcMaxWidth({boundaryWidth, currentWidth})
248
+ // not fresh, so use refs
249
+
250
+ referenceWidthRef.current = referenceWidth
251
+
252
+ const _currentWidth = widthRef.current
253
+ const _maxWidth = maxWidthRef.current
237
254
 
238
255
  if (matchReferenceWidth) {
239
256
  elements.floating.style.width = `${referenceWidth}px`
240
- } else if (currentWidth !== undefined) {
241
- elements.floating.style.width = `${currentWidth}px`
257
+ } else if (_currentWidth !== undefined) {
258
+ elements.floating.style.width = `${_currentWidth}px`
242
259
  }
243
260
 
244
261
  if (constrainSize) {
245
262
  elements.floating.style.maxWidth = `${Math.min(
246
263
  availableWidth,
247
- maxWidth ?? Infinity,
264
+ _maxWidth ?? Infinity,
248
265
  )}px`
249
266
 
250
267
  elements.floating.style.maxHeight = `${availableHeight}px`
@@ -256,28 +273,59 @@ export const Popover = memo(
256
273
  matchReferenceWidth,
257
274
  padding: DEFAULT_POPOVER_PADDING,
258
275
  }),
259
- // Shift the popover so its sits within the boundary element
260
- preventOverflow &&
276
+ )
277
+ }
278
+
279
+ // Shift the popover so its sits within the boundary element
280
+ if (preventOverflow) {
281
+ ret.push(
261
282
  shift({
262
283
  boundary: floatingBoundary || undefined,
263
284
  rootBoundary,
264
285
  padding: DEFAULT_POPOVER_PADDING,
265
286
  }),
266
- // Place arrow
267
- arrowProp &&
287
+ )
288
+ }
289
+
290
+ // Place arrow
291
+ if (arrowProp) {
292
+ ret.push(
268
293
  arrow({
269
- element: arrowElement,
294
+ element: arrowRef,
270
295
  padding: DEFAULT_POPOVER_PADDING,
271
296
  }),
272
- // Determine the origin to scale from.
273
- // Must be placed after `@sanity/ui/size` and `shift` middleware.
274
- animate && origin,
297
+ )
298
+ }
299
+
300
+ // Determine the origin to scale from.
301
+ // Must be placed after `@sanity/ui/size` and `shift` middleware.
302
+ if (animate) {
303
+ ret.push(origin)
304
+ }
305
+
306
+ ret.push(
275
307
  hide({
276
308
  boundary: referenceBoundary || undefined,
277
309
  padding: DEFAULT_POPOVER_PADDING,
278
310
  strategy: 'referenceHidden',
279
311
  }),
280
- ],
312
+ )
313
+
314
+ return ret
315
+ }, [
316
+ animate,
317
+ arrowProp,
318
+ constrainSize,
319
+ fallbackPlacements,
320
+ floatingBoundary,
321
+ margins,
322
+ matchReferenceWidth,
323
+ preventOverflow,
324
+ referenceBoundary,
325
+ ])
326
+
327
+ const {x, y, middlewareData, placement, refs, strategy, update} = useFloating({
328
+ middleware,
281
329
  placement: placementProp,
282
330
  whileElementsMounted: autoUpdate,
283
331
  })
@@ -290,6 +338,10 @@ export const Popover = memo(
290
338
  const originX = middlewareData['@sanity/ui/origin']?.originX
291
339
  const originY = middlewareData['@sanity/ui/origin']?.originY
292
340
 
341
+ const setArrow = useCallback((arrowEl: HTMLDivElement | null) => {
342
+ arrowRef.current = arrowEl
343
+ }, [])
344
+
293
345
  const setFloating = useCallback(
294
346
  (node: HTMLDivElement | null) => {
295
347
  ref.current = node
@@ -298,29 +350,43 @@ export const Popover = memo(
298
350
  [refs],
299
351
  )
300
352
 
301
- const [childElement, setChildElement] = useState<HTMLElement | null>(null)
302
353
  const setReference = useCallback(
303
354
  (node: HTMLElement | null) => {
304
355
  refs.setReference(node)
305
- setChildElement(node)
356
+
357
+ const childRef = getElementRef(childProp as any)
358
+
359
+ if (typeof childRef === 'function') {
360
+ childRef(node)
361
+ } else if (childRef) {
362
+ childRef.current = node
363
+ }
306
364
  },
307
- [refs],
365
+ [childProp, refs],
308
366
  )
309
367
 
310
- useImperativeHandle(getElementRef(childProp as any), () => childElement, [childElement])
311
-
312
368
  const child = useMemo(() => {
313
- if (!childProp || referenceElement) return null
369
+ // If a reference element is defined, we don't need to clone the child
370
+ if (referenceElement) return childProp
371
+
372
+ if (!childProp) return null
314
373
 
315
374
  return cloneElement(childProp, {ref: setReference})
316
375
  }, [childProp, referenceElement, setReference])
317
376
 
318
- useImperativeHandle(updateRef, () => update, [update])
377
+ useEffect(() => {
378
+ if (updateRef) {
379
+ if (typeof updateRef === 'function') {
380
+ updateRef(update)
381
+ } else if (updateRef) {
382
+ updateRef.current = update
383
+ }
384
+ }
385
+ }, [update, updateRef])
319
386
 
320
387
  useEffect(() => {
321
- if (child) return
322
- refs.setReference(referenceElement || null)
323
- }, [referenceElement, refs, child])
388
+ if (referenceElement) refs.setReference(referenceElement)
389
+ }, [referenceElement, refs])
324
390
 
325
391
  if (disabled) {
326
392
  return childProp || <></>
@@ -333,7 +399,7 @@ export const Popover = memo(
333
399
  __unstable_margins={margins}
334
400
  animate={animate}
335
401
  arrow={arrowProp}
336
- arrowRef={setArrowElement}
402
+ arrowRef={setArrow}
337
403
  arrowX={arrowX}
338
404
  arrowY={arrowY}
339
405
  hidden={referenceHidden}
@@ -348,6 +414,7 @@ export const Popover = memo(
348
414
  originY={originY}
349
415
  strategy={strategy}
350
416
  tone={tone}
417
+ width={matchReferenceWidth ? referenceWidthRef.current : width}
351
418
  x={x}
352
419
  y={y}
353
420
  >
@@ -382,8 +449,7 @@ Popover.displayName = 'Memo(ForwardRef(Popover))'
382
449
  // https://github.com/facebook/react/pull/28348
383
450
  //
384
451
  // Access the ref using the method that doesn't yield a warning.
385
- function getElementRef(element?: React.ReactElement) {
386
- if (!element) return null
452
+ function getElementRef(element: React.ReactElement) {
387
453
  // React <=18 in DEV
388
454
  let getter = Object.getOwnPropertyDescriptor(element.props, 'ref')?.get
389
455
  let mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning
@@ -51,6 +51,7 @@ export const PopoverCard = memo(
51
51
  shadow?: number | number[]
52
52
  strategy: Strategy
53
53
  tone: CardTone
54
+ width: number | undefined
54
55
  x: number | null
55
56
  y: number | null
56
57
  } & Omit<React.HTMLProps<HTMLDivElement>, 'as' | 'height' | 'width'>,
@@ -75,6 +76,7 @@ export const PopoverCard = memo(
75
76
  strategy,
76
77
  style,
77
78
  tone,
79
+ width,
78
80
  x: xProp,
79
81
  y: yProp,
80
82
  ...restProps
@@ -99,11 +101,12 @@ export const PopoverCard = memo(
99
101
  originY,
100
102
  position: strategy,
101
103
  top: y,
104
+ width,
102
105
  zIndex,
103
106
  willChange: animate ? 'transform' : undefined,
104
107
  ...style,
105
108
  }),
106
- [animate, originX, originY, strategy, style, x, y, zIndex],
109
+ [animate, originX, originY, strategy, style, width, x, y, zIndex],
107
110
  )
108
111
 
109
112
  const arrowStyle: CSSProperties = useMemo(
@@ -2,8 +2,8 @@ import {Flex, Radio} from '@sanity/ui'
2
2
  import {useAction, useBoolean} from '@sanity/ui-workshop'
3
3
 
4
4
  export default function PlainStory() {
5
- const checked = useBoolean('Checked?', false, 'Props')
6
- const disabled = useBoolean('Disabled?', false, 'Props')
5
+ const checked = useBoolean('Checked', false, 'Props')
6
+ const disabled = useBoolean('Disabled', false, 'Props')
7
7
  const id = 'radioStory'
8
8
  const name = 'radioStory'
9
9
  const onBlur = useAction('onBlur')
@@ -9,7 +9,7 @@ export default function ReadOnlyStory() {
9
9
  Select
10
10
  </Label>
11
11
 
12
- <Select id="select-example" radius={3} readOnly>
12
+ <Select id="select-example" readOnly>
13
13
  <option value="a">Option A</option>
14
14
  <option value="b">Option B</option>
15
15
  <option value="c">Option C</option>