@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/dist/index.cjs.mjs +1 -0
- package/dist/index.d.ts +69 -30
- package/dist/index.esm.js +422 -303
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +422 -302
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/components/menu/__workshop__/selectedItem.tsx +7 -2
- package/src/core/components/menu/menuButton.tsx +12 -1
- package/src/core/constants.ts +18 -0
- package/src/core/middleware/origin.ts +47 -0
- package/src/core/primitives/popover/popover.tsx +67 -13
- package/src/core/primitives/popover/popoverCard.tsx +26 -15
- package/src/core/primitives/tooltip/tooltip.tsx +46 -56
- package/src/core/primitives/tooltip/tooltipCard.tsx +104 -0
- package/src/core/{primitives/tooltip → utils/conditionalWrapper}/conditionalWrapper.tsx +3 -0
- package/src/core/utils/conditionalWrapper/index.ts +1 -0
- package/src/core/utils/index.ts +1 -0
package/package.json
CHANGED
|
@@ -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(
|
|
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="
|
|
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(
|
package/src/core/constants.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
|
17
|
-
|
|
18
|
-
display:
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
width:
|
|
22
|
-
|
|
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
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
|
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 {
|
|
32
|
-
|
|
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
|
|
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
|
|
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
|
-
<
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
412
|
-
|
|
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
|
-
{
|
|
423
|
+
{/* the tooltip */}
|
|
437
424
|
<ConditionalWrapper
|
|
438
425
|
condition={animate}
|
|
439
|
-
wrapper={(children) => <AnimatePresence>{children}</AnimatePresence>}
|
|
426
|
+
wrapper={(children) => <AnimatePresence>{children}</AnimatePresence>}
|
|
440
427
|
>
|
|
441
428
|
{showTooltip && (
|
|
442
429
|
<ConditionalWrapper
|
|
443
|
-
condition={!!portalProp}
|
|
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
|
-
{
|
|
437
|
+
{tooltip}
|
|
451
438
|
</ConditionalWrapper>
|
|
452
439
|
)}
|
|
453
440
|
</ConditionalWrapper>
|
|
441
|
+
|
|
442
|
+
{/* the referred element */}
|
|
443
|
+
{child}
|
|
454
444
|
</>
|
|
455
445
|
)
|
|
456
446
|
})
|