@sanity/ui 2.0.0-beta.3 → 2.0.0-beta.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ui",
3
- "version": "2.0.0-beta.3",
3
+ "version": "2.0.0-beta.5",
4
4
  "sideEffects": false,
5
5
  "types": "./dist/index.d.ts",
6
6
  "source": "./exports/index.ts",
@@ -16,8 +16,10 @@ const POPOVER_PROPS: MenuButtonProps['popover'] = {
16
16
  matchReferenceWidth: true,
17
17
  }
18
18
 
19
+ const INITIAL_INDEX = 1
20
+
19
21
  export default function SelectedItemStory() {
20
- const [selectedIndex, setSelectedIndex] = useState(0)
22
+ const [selectedIndex, setSelectedIndex] = useState(INITIAL_INDEX)
21
23
 
22
24
  return (
23
25
  <Box padding={[4, 5, 6]}>
@@ -26,12 +28,13 @@ export default function SelectedItemStory() {
26
28
 
27
29
  <MenuButton
28
30
  button={<Button text="Open menu" />}
29
- id="selected-item-example"
31
+ id="menu-button"
30
32
  menu={
31
33
  <Menu>
32
34
  <MenuItem
33
35
  icon={SearchIcon}
34
36
  iconRight={selectedIndex === 0 ? CheckmarkIcon : undefined}
37
+ id="menu-item-1"
35
38
  onClick={() => setSelectedIndex(0)}
36
39
  pressed={selectedIndex === 0}
37
40
  selected={selectedIndex === 0}
@@ -40,6 +43,7 @@ export default function SelectedItemStory() {
40
43
  <MenuItem
41
44
  icon={ClockIcon}
42
45
  iconRight={selectedIndex === 1 ? CheckmarkIcon : undefined}
46
+ id="menu-item-2"
43
47
  onClick={() => setSelectedIndex(1)}
44
48
  pressed={selectedIndex === 1}
45
49
  selected={selectedIndex === 1}
@@ -49,6 +53,7 @@ export default function SelectedItemStory() {
49
53
  <MenuItem
50
54
  icon={ExpandIcon}
51
55
  iconRight={selectedIndex === 2 ? CheckmarkIcon : undefined}
56
+ id="menu-item-3"
52
57
  onClick={() => setSelectedIndex(2)}
53
58
  pressed={selectedIndex === 2}
54
59
  selected={selectedIndex === 2}
@@ -99,6 +99,16 @@ export const MenuButton = forwardRef(function MenuButton(
99
99
  setShouldFocus(null)
100
100
  }, [])
101
101
 
102
+ // Prevent mouse event propagation when the menu is open.
103
+ // This is to ensure that `handleBlur` isn't triggered when clicking the menu button whilst open,
104
+ // which can lead to `setOpen` being triggered multiple times (once by `handleBlur`, and again by `handleButtonClick`).
105
+ const handleMouseDown = useCallback(
106
+ (event: PointerEvent) => {
107
+ if (open) event.preventDefault()
108
+ },
109
+ [open],
110
+ )
111
+
102
112
  const handleButtonKeyDown = useCallback((event: React.KeyboardEvent<HTMLButtonElement>) => {
103
113
  // On `ArrowDown`, `Enter` and `Space`
104
114
  // - Opens menu and moves focus to first menuitem
@@ -231,13 +241,14 @@ export const MenuButton = forwardRef(function MenuButton(
231
241
  id,
232
242
  onClick: handleButtonClick,
233
243
  onKeyDown: handleButtonKeyDown,
244
+ onMouseDown: handleMouseDown,
234
245
  'aria-haspopup': true,
235
246
  'aria-expanded': open,
236
247
  ref: setButtonRef,
237
248
  selected: open,
238
249
  })
239
250
  : null,
240
- [buttonProp, handleButtonClick, handleButtonKeyDown, id, open, setButtonRef],
251
+ [buttonProp, handleButtonClick, handleButtonKeyDown, handleMouseDown, id, open, setButtonRef],
241
252
  )
242
253
 
243
254
  const popoverProps: MenuButtonProps['popover'] = useMemo(
@@ -1,3 +1,5 @@
1
+ import {Transition, Variant} from 'framer-motion'
2
+
1
3
  /**
2
4
  * @internal
3
5
  */
@@ -8,6 +10,22 @@ export const EMPTY_ARRAY: never[] = []
8
10
  */
9
11
  export const EMPTY_RECORD: Record<string, never> = {}
10
12
 
13
+ /**
14
+ * Shared `framer-motion` variants used by `Popover` and `Tooltip` components.
15
+ * @internal
16
+ */
17
+ export const POPOVER_MOTION_PROPS: {
18
+ animate: Variant
19
+ initial: Variant
20
+ exit: Variant
21
+ transition: Transition
22
+ } = {
23
+ initial: {opacity: 0.5, scale: 0.97},
24
+ animate: {opacity: 1, scale: 1},
25
+ exit: {opacity: 0, scale: 0.97},
26
+ transition: {duration: 0.4, type: 'spring'},
27
+ }
28
+
11
29
  /**
12
30
  * @internal
13
31
  * @deprecated No longer used.
@@ -0,0 +1,47 @@
1
+ import {Middleware} from '@floating-ui/react-dom'
2
+
3
+ /**
4
+ * Custom floating-ui middleware which calculates transform-origin X + Y offsets
5
+ * based on the current floating rect's dimensions and shift offset.
6
+ *
7
+ * Scaling popovers with these transform-origin offsets will give the effect of
8
+ * popvers slightly 'growing' from the origin/reference element.
9
+ *
10
+ * This middleware must be applied after both `@sanity/ui/size` and `shift` middlewares.
11
+ */
12
+ export const origin: Middleware = {
13
+ name: '@sanity/ui/origin',
14
+ fn({middlewareData, placement, rects}) {
15
+ const [side] = placement.split('-')
16
+
17
+ const floatingWidth = rects.floating.width
18
+ const floatingHeight = rects.floating.height
19
+
20
+ const shiftX = middlewareData.shift?.x || 0
21
+ const shiftY = middlewareData.shift?.y || 0
22
+
23
+ if (floatingWidth <= 0 || floatingHeight <= 0) {
24
+ return {}
25
+ }
26
+
27
+ const isVerticalPlacement = ['bottom', 'top'].includes(side)
28
+
29
+ const {originX, originY}: {originX: number; originY: number} = isVerticalPlacement
30
+ ? {
31
+ originX: clamp(0.5 - shiftX / floatingWidth, 0, 1),
32
+ originY: side === 'bottom' ? 0 : 1,
33
+ }
34
+ : {
35
+ originX: side === 'left' ? 1 : 0,
36
+ originY: clamp(0.5 - shiftY / floatingHeight, 0, 1),
37
+ }
38
+
39
+ return {
40
+ data: {originX, originY},
41
+ }
42
+ },
43
+ }
44
+
45
+ function clamp(num: number, min: number, max: number) {
46
+ return Math.min(Math.max(num, min), max)
47
+ }
@@ -10,6 +10,7 @@ import {
10
10
  useFloating,
11
11
  } from '@floating-ui/react-dom'
12
12
  import {ThemeColorSchemeKey} from '@sanity/ui/theme'
13
+ import {AnimatePresence} from 'framer-motion'
13
14
  import {
14
15
  MutableRefObject,
15
16
  RefCallback,
@@ -22,9 +23,16 @@ import {
22
23
  useRef,
23
24
  } from 'react'
24
25
  import {useForwardedRef, useArrayProp, useElementSize, useMediaIndex} from '../../hooks'
26
+ import {origin} from '../../middleware/origin'
25
27
  import {useTheme_v2} from '../../theme'
26
28
  import {BoxOverflow, CardTone, Placement, PopoverMargins} from '../../types'
27
- import {LayerProps, LayerProvider, Portal, useBoundaryElement} from '../../utils'
29
+ import {
30
+ ConditionalWrapper,
31
+ LayerProps,
32
+ LayerProvider,
33
+ Portal,
34
+ useBoundaryElement,
35
+ } from '../../utils'
28
36
  import {ResponsiveRadiusProps, ResponsiveShadowProps} from '../types'
29
37
  import {
30
38
  DEFAULT_POPOVER_DISTANCE,
@@ -46,15 +54,41 @@ export interface PopoverProps
46
54
  ResponsiveShadowProps {
47
55
  /** @beta */
48
56
  __unstable_margins?: PopoverMargins
57
+ /**
58
+ * Whether the popover should animate in and out.
59
+ *
60
+ * @beta
61
+ * @defaultValue false
62
+ */
63
+ animate?: boolean
49
64
  arrow?: boolean
50
65
  /** @deprecated Use `floatingBoundary` and/or `referenceBoundary` instead */
51
66
  boundaryElement?: HTMLElement | null
52
67
  children?: React.ReactElement
68
+ /**
69
+ * When `true`, prevent overflow within the current boundary:
70
+ * - by flipping on its side axis
71
+ * - by resizing
72
+ /*
73
+ * Note that:
74
+ * - setting `preventOverflow` to `true` also prevents overflow on its side axis
75
+ * - setting `matchReferenceWidth` to `true` also causes the popover to resize
76
+ *
77
+ * @defaultValue false
78
+ */
53
79
  constrainSize?: boolean
54
80
  content?: React.ReactNode
55
81
  disabled?: boolean
56
82
  fallbackPlacements?: Placement[]
57
83
  floatingBoundary?: HTMLElement | null
84
+ /**
85
+ * When `true`, set the maximum width to match the reference element, and also prevent overflow within
86
+ * the current boundary by resizing.
87
+ *
88
+ * Note that setting `constrainSize` to `true` also causes the popover to resize
89
+ *
90
+ * @defaultValue false
91
+ */
58
92
  matchReferenceWidth?: boolean
59
93
  open?: boolean
60
94
  overflow?: BoxOverflow
@@ -90,6 +124,7 @@ export const Popover = memo(
90
124
 
91
125
  const {
92
126
  __unstable_margins: margins = DEFAULT_POPOVER_MARGINS,
127
+ animate = false,
93
128
  arrow: arrowProp = false,
94
129
  boundaryElement = boundaryElementContext.element,
95
130
  children: childProp,
@@ -234,7 +269,7 @@ export const Popover = memo(
234
269
  )
235
270
  }
236
271
 
237
- // Shift the popover so its sits within the boundary eleement
272
+ // Shift the popover so its sits within the boundary element
238
273
  if (preventOverflow) {
239
274
  ret.push(
240
275
  shift({
@@ -255,6 +290,12 @@ export const Popover = memo(
255
290
  )
256
291
  }
257
292
 
293
+ // Determine the origin to scale from.
294
+ // Must be placed after `@sanity/ui/size` and `shift` middleware.
295
+ if (animate) {
296
+ ret.push(origin)
297
+ }
298
+
258
299
  ret.push(
259
300
  hide({
260
301
  boundary: referenceBoundary || undefined,
@@ -265,6 +306,7 @@ export const Popover = memo(
265
306
 
266
307
  return ret
267
308
  }, [
309
+ animate,
268
310
  arrowProp,
269
311
  constrainSize,
270
312
  fallbackPlacements,
@@ -286,6 +328,9 @@ export const Popover = memo(
286
328
  const arrowX = middlewareData.arrow?.x
287
329
  const arrowY = middlewareData.arrow?.y
288
330
 
331
+ const originX = middlewareData['@sanity/ui/origin']?.originX
332
+ const originY = middlewareData['@sanity/ui/origin']?.originY
333
+
289
334
  const setArrow = useCallback((arrowEl: HTMLDivElement | null) => {
290
335
  arrowRef.current = arrowEl
291
336
  }, [])
@@ -343,6 +388,7 @@ export const Popover = memo(
343
388
  <PopoverCard
344
389
  {...restProps}
345
390
  __unstable_margins={margins}
391
+ animate={animate}
346
392
  arrow={arrowProp}
347
393
  arrowRef={setArrow}
348
394
  arrowX={arrowX}
@@ -355,6 +401,8 @@ export const Popover = memo(
355
401
  ref={setFloating}
356
402
  scheme={scheme}
357
403
  shadow={shadow}
404
+ originX={originX}
405
+ originY={originY}
358
406
  strategy={strategy}
359
407
  tone={tone}
360
408
  width={matchReferenceWidth ? referenceWidthRef.current : width}
@@ -369,17 +417,23 @@ export const Popover = memo(
369
417
  return (
370
418
  <>
371
419
  {/* the popover */}
372
- {open && (
373
- <>
374
- {portal ? (
375
- <Portal __unstable_name={typeof portal === 'string' ? portal : undefined}>
376
- {popover}
377
- </Portal>
378
- ) : (
379
- popover
380
- )}
381
- </>
382
- )}
420
+ <ConditionalWrapper
421
+ condition={animate}
422
+ wrapper={(children) => <AnimatePresence>{children}</AnimatePresence>}
423
+ >
424
+ {open && (
425
+ <ConditionalWrapper
426
+ condition={!!portal}
427
+ wrapper={(children) => (
428
+ <Portal __unstable_name={typeof portal === 'string' ? portal : undefined}>
429
+ {children}
430
+ </Portal>
431
+ )}
432
+ >
433
+ {popover}
434
+ </ConditionalWrapper>
435
+ )}
436
+ </ConditionalWrapper>
383
437
 
384
438
  {/* the referred element */}
385
439
  {child}
@@ -1,10 +1,12 @@
1
1
  import {Strategy} from '@floating-ui/react-dom'
2
2
  import {ThemeColorSchemeKey} from '@sanity/ui/theme'
3
+ import {MotionProps, motion} from 'framer-motion'
3
4
  import React, {CSSProperties, forwardRef, memo, useMemo} from 'react'
4
5
  import styled from 'styled-components'
6
+ import {POPOVER_MOTION_PROPS} from '../../constants'
5
7
  import {BoxOverflow, CardTone, Placement, PopoverMargins, Radius} from '../../types'
6
8
  import {Arrow, useLayer} from '../../utils'
7
- import {Card} from '../card'
9
+ import {Card, CardProps} from '../card'
8
10
  import {Flex} from '../flex'
9
11
  import {
10
12
  DEFAULT_POPOVER_ARROW_HEIGHT,
@@ -13,14 +15,14 @@ import {
13
15
  DEFAULT_POPOVER_MARGINS,
14
16
  } from './constants'
15
17
 
16
- const Root = styled(Card)({
17
- '&:not([hidden])': {
18
- display: 'flex',
19
- },
20
- flexDirection: 'column',
21
- width: 'max-content',
22
- minWidth: 'min-content',
23
- })
18
+ const MotionCard = styled(motion(Card))`
19
+ &:not([hidden]) {
20
+ display: flex;
21
+ }
22
+ flex-direction: column;
23
+ width: max-content;
24
+ min-width: min-content;
25
+ `
24
26
 
25
27
  /**
26
28
  * @internal
@@ -30,13 +32,16 @@ export const PopoverCard = memo(
30
32
  props: {
31
33
  /** @beta*/
32
34
  __unstable_margins?: PopoverMargins
35
+ animate?: boolean
33
36
  arrow: boolean
34
37
  arrowRef: React.Ref<HTMLDivElement>
35
38
  arrowX?: number
36
39
  arrowY?: number
40
+ originX?: number
41
+ originY?: number
37
42
  overflow?: BoxOverflow
38
43
  padding?: number | number[]
39
- placement?: Placement
44
+ placement: Placement
40
45
  radius?: Radius | Radius[]
41
46
  scheme?: ThemeColorSchemeKey
42
47
  shadow?: number | number[]
@@ -50,6 +55,7 @@ export const PopoverCard = memo(
50
55
  ) {
51
56
  const {
52
57
  __unstable_margins: marginsProp,
58
+ animate,
53
59
  arrow,
54
60
  arrowRef,
55
61
  arrowX,
@@ -57,6 +63,8 @@ export const PopoverCard = memo(
57
63
  children,
58
64
  padding,
59
65
  placement,
66
+ originX,
67
+ originY,
60
68
  overflow,
61
69
  radius,
62
70
  scheme,
@@ -84,14 +92,16 @@ export const PopoverCard = memo(
84
92
 
85
93
  const rootStyle: CSSProperties = useMemo(
86
94
  () => ({
95
+ left: x,
96
+ originX,
97
+ originY,
87
98
  position: strategy,
88
99
  top: y,
89
- left: x,
90
100
  width,
91
101
  zIndex,
92
102
  ...style,
93
103
  }),
94
- [strategy, style, width, x, y, zIndex],
104
+ [originX, originY, strategy, style, width, x, y, zIndex],
95
105
  )
96
106
 
97
107
  const arrowStyle: CSSProperties = useMemo(
@@ -105,9 +115,9 @@ export const PopoverCard = memo(
105
115
  )
106
116
 
107
117
  return (
108
- <Root
118
+ <MotionCard
109
119
  data-ui="Popover"
110
- {...restProps}
120
+ {...(restProps as CardProps & MotionProps)}
111
121
  data-placement={placement}
112
122
  radius={radius}
113
123
  ref={ref}
@@ -116,6 +126,7 @@ export const PopoverCard = memo(
116
126
  sizing="border"
117
127
  style={rootStyle}
118
128
  tone={tone}
129
+ {...(animate ? POPOVER_MOTION_PROPS : {})}
119
130
  >
120
131
  <Flex data-ui="Popover__wrapper" direction="column" flex={1} overflow={overflow}>
121
132
  <Flex direction="column" flex={1} padding={padding}>
@@ -132,7 +143,7 @@ export const PopoverCard = memo(
132
143
  radius={DEFAULT_POPOVER_ARROW_RADIUS}
133
144
  />
134
145
  )}
135
- </Root>
146
+ </MotionCard>
136
147
  )
137
148
  }),
138
149
  )
@@ -10,7 +10,7 @@ import {
10
10
  RootBoundary,
11
11
  } from '@floating-ui/react-dom'
12
12
  import {ThemeColorSchemeKey} from '@sanity/ui/theme'
13
- import {AnimatePresence, motion} from 'framer-motion'
13
+ import {AnimatePresence} from 'framer-motion'
14
14
  import {
15
15
  cloneElement,
16
16
  forwardRef,
@@ -19,27 +19,30 @@ import {
19
19
  useMemo,
20
20
  useRef,
21
21
  useState,
22
- CSSProperties,
23
22
  ForwardedRef,
24
23
  useId,
25
24
  } from 'react'
26
25
  import styled from 'styled-components'
27
26
  import {useArrayProp, useForwardedRef} from '../../hooks'
28
27
  import {useDelayedState} from '../../hooks/useDelayedState'
28
+ import {origin} from '../../middleware/origin'
29
29
  import {useTheme_v2} from '../../theme'
30
30
  import {Placement} from '../../types'
31
- import {Arrow, Layer, LayerProps, Portal, useBoundaryElement, usePortal} from '../../utils'
32
- import {Card} from '../card'
31
+ import {
32
+ ConditionalWrapper,
33
+ Layer,
34
+ LayerProps,
35
+ Portal,
36
+ useBoundaryElement,
37
+ usePortal,
38
+ } from '../../utils'
33
39
  import {Delay} from '../types'
34
- import {ConditionalWrapper} from './conditionalWrapper'
35
40
  import {
36
41
  DEFAULT_FALLBACK_PLACEMENTS,
37
- DEFAULT_TOOLTIP_ARROW_HEIGHT,
38
- DEFAULT_TOOLTIP_ARROW_RADIUS,
39
- DEFAULT_TOOLTIP_ARROW_WIDTH,
40
42
  DEFAULT_TOOLTIP_DISTANCE,
41
43
  DEFAULT_TOOLTIP_PADDING,
42
44
  } from './constants'
45
+ import {TooltipCard} from './tooltipCard'
43
46
  import {useTooltipDelayGroup} from './tooltipDelayGroup'
44
47
 
45
48
  /**
@@ -98,6 +101,7 @@ export const Tooltip = forwardRef(function Tooltip(
98
101
  const boundaryElementContext = useBoundaryElement()
99
102
  const {layer} = useTheme_v2()
100
103
  const {
104
+ animate = false,
101
105
  arrow: arrowProp = false,
102
106
  boundaryElement = boundaryElementContext?.element,
103
107
  children: childProp,
@@ -113,7 +117,6 @@ export const Tooltip = forwardRef(function Tooltip(
113
117
  shadow = 2,
114
118
  zOffset = layer.tooltip.zOffset,
115
119
  delay,
116
- animate = false,
117
120
  ...restProps
118
121
  } = props
119
122
  const fallbackPlacements = useArrayProp(fallbackPlacementsProp)
@@ -168,8 +171,14 @@ export const Tooltip = forwardRef(function Tooltip(
168
171
  ret.push(arrow({element: arrowRef, padding: DEFAULT_TOOLTIP_PADDING}))
169
172
  }
170
173
 
174
+ // Determine the origin to scale from.
175
+ // Must be placed after `@sanity/ui/size` and `shift` middleware.
176
+ if (animate) {
177
+ ret.push(origin)
178
+ }
179
+
171
180
  return ret
172
- }, [arrowProp, boundaryElement, fallbackPlacements])
181
+ }, [animate, arrowProp, boundaryElement, fallbackPlacements])
173
182
 
174
183
  const {floatingStyles, placement, middlewareData, refs, update} = useFloating({
175
184
  middleware,
@@ -180,15 +189,8 @@ export const Tooltip = forwardRef(function Tooltip(
180
189
  const arrowX = middlewareData.arrow?.x
181
190
  const arrowY = middlewareData.arrow?.y
182
191
 
183
- const arrowStyle: CSSProperties = useMemo(
184
- () => ({
185
- left: arrowX !== null ? arrowX : undefined,
186
- top: arrowY !== null ? arrowY : undefined,
187
- right: undefined,
188
- bottom: undefined,
189
- }),
190
- [arrowX, arrowY],
191
- )
192
+ const originX = middlewareData['@sanity/ui/origin']?.originX
193
+ const originY = middlewareData['@sanity/ui/origin']?.originY
192
194
 
193
195
  const tooltipId = useId()
194
196
  const [isOpen, setIsOpen] = useDelayedState(false)
@@ -386,7 +388,7 @@ export const Tooltip = forwardRef(function Tooltip(
386
388
 
387
389
  if (disabled) return child
388
390
 
389
- const root = (
391
+ const tooltip = (
390
392
  <Root
391
393
  data-ui="Tooltip"
392
394
  {...restProps}
@@ -395,62 +397,50 @@ export const Tooltip = forwardRef(function Tooltip(
395
397
  zOffset={zOffset}
396
398
  $maxWidth={tooltipWidth}
397
399
  >
398
- <ConditionalWrapper
399
- condition={animate} // Add animation wrapper if it should animate
400
- wrapper={(children) => (
401
- <motion.div
402
- initial={{opacity: 0.5, scale: 0.95}}
403
- animate={{opacity: 1, scale: 1}}
404
- exit={{opacity: 0, scale: 0.95}}
405
- transition={{duration: 0.3, type: 'spring'}}
406
- >
407
- {children}
408
- </motion.div>
409
- )}
400
+ <TooltipCard
401
+ {...restProps}
402
+ animate={animate}
403
+ arrow={arrowProp}
404
+ arrowRef={setArrow}
405
+ arrowX={arrowX}
406
+ arrowY={arrowY}
407
+ originX={originX}
408
+ originY={originY}
409
+ padding={padding}
410
+ placement={placement}
411
+ radius={radius}
412
+ ref={setFloating}
413
+ scheme={scheme}
414
+ shadow={shadow}
410
415
  >
411
- <Card
412
- data-ui="Tooltip__card"
413
- data-placement={placement}
414
- padding={padding}
415
- radius={radius}
416
- scheme={scheme}
417
- shadow={shadow}
418
- >
419
- {content}
420
- {arrowProp && (
421
- <Arrow
422
- ref={setArrow}
423
- style={arrowStyle}
424
- width={DEFAULT_TOOLTIP_ARROW_WIDTH}
425
- height={DEFAULT_TOOLTIP_ARROW_HEIGHT}
426
- radius={DEFAULT_TOOLTIP_ARROW_RADIUS}
427
- />
428
- )}
429
- </Card>
430
- </ConditionalWrapper>
416
+ {content}
417
+ </TooltipCard>
431
418
  </Root>
432
419
  )
433
420
 
434
421
  return (
435
422
  <>
436
- {child}
423
+ {/* the tooltip */}
437
424
  <ConditionalWrapper
438
425
  condition={animate}
439
- wrapper={(children) => <AnimatePresence>{children}</AnimatePresence>} // Add AnimatePresence if it should animate
426
+ wrapper={(children) => <AnimatePresence>{children}</AnimatePresence>}
440
427
  >
441
428
  {showTooltip && (
442
429
  <ConditionalWrapper
443
- condition={!!portalProp} // Add portal if portalProp is set
430
+ condition={!!portalProp}
444
431
  wrapper={(children) => (
445
432
  <Portal __unstable_name={typeof portalProp === 'string' ? portalProp : undefined}>
446
433
  {children}
447
434
  </Portal>
448
435
  )}
449
436
  >
450
- {root}
437
+ {tooltip}
451
438
  </ConditionalWrapper>
452
439
  )}
453
440
  </ConditionalWrapper>
441
+
442
+ {/* the referred element */}
443
+ {child}
454
444
  </>
455
445
  )
456
446
  })