@sanity/ui 2.14.4-canary.2 → 2.14.4

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.
@@ -10,60 +10,47 @@ export const EMPTY_ARRAY: never[] = []
10
10
  */
11
11
  export const EMPTY_RECORD: Record<string, never> = {}
12
12
 
13
- const POPOVER_MOTION_DURATION = 0.2
13
+ /**
14
+ * @internal
15
+ */
16
+ export const POPOVER_MOTION_CONTENT_OPACITY_PROPERTY = '--motion-content-opacity' as string
14
17
 
15
18
  /**
16
19
  * Shared `framer-motion` variants used by `Popover` and `Tooltip` components.
17
20
  * @internal
18
21
  */
19
22
  export const POPOVER_MOTION_PROPS: {
20
- outerVariants: {
21
- initial: Variant
22
- hidden: Variant
23
- visible: Variant
24
- scaleIn: Variant
25
- scaleOut: Variant
26
- }
27
- innerVariants: {
28
- hidden: Variant
29
- visible: Variant
30
- }
23
+ animate: Variant
24
+ initial: Variant
25
+ exit: Variant
31
26
  transition: Transition
32
27
  } = {
33
- outerVariants: {
34
- initial: {
35
- scale: 0.97,
36
- willChange: 'transform',
37
- },
38
- hidden: {
39
- opacity: 0,
40
- },
41
- visible: {
42
- opacity: 1,
43
- transition: {
44
- when: 'beforeChildren',
45
- duration: POPOVER_MOTION_DURATION / 2,
46
- },
47
- },
48
- scaleIn: {
49
- scale: 1,
50
- },
51
- scaleOut: {
52
- scale: 0.97,
53
- },
28
+ /**
29
+ * These variants makes use of special timing, by using a negative opacity as a starting position,
30
+ * as well as double opacity as the end position.
31
+ * The purpose of this is to make the tooltip/popover container appear before the content, and when exiting
32
+ * we want the content to disappear faster than the container.
33
+ */
34
+ initial: {
35
+ opacity: 0.5,
36
+ // the nagative opacity here, as well as the double opacity further down, are to make the content appear after the backgdrop, and when exiting the content should disappear first.
37
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: -1,
38
+ scale: 0.97,
39
+ willChange: 'transform',
40
+ },
41
+ animate: {
42
+ opacity: 2,
43
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: 1,
44
+ scale: 1,
54
45
  },
55
- innerVariants: {
56
- hidden: {
57
- opacity: 0,
58
- },
59
- visible: {
60
- opacity: 1,
61
- },
46
+ exit: {
47
+ opacity: 0,
48
+ [POPOVER_MOTION_CONTENT_OPACITY_PROPERTY as string]: -1,
49
+ scale: 0.97,
62
50
  },
63
51
  transition: {
52
+ duration: 0.4,
64
53
  type: 'spring',
65
- visualDuration: POPOVER_MOTION_DURATION,
66
- bounce: 0.25,
67
54
  },
68
55
  }
69
56
 
@@ -10,7 +10,6 @@ import {ResponsiveGridProps} from '../types'
10
10
  */
11
11
  export interface GridProps extends Omit<BoxProps, 'display'>, ResponsiveGridProps {}
12
12
 
13
- // @TODO this might be how ToastLayer has to be setup
14
13
  const StyledGrid = styled(Box)<ResponsiveGridStyleProps>(responsiveGridStyle)
15
14
 
16
15
  /**
@@ -3,7 +3,7 @@ import {ThemeColorSchemeKey} from '@sanity/ui/theme'
3
3
  import {type MotionProps, motion} from 'framer-motion'
4
4
  import React, {CSSProperties, forwardRef, memo, useMemo} from 'react'
5
5
  import {styled} from 'styled-components'
6
- import {POPOVER_MOTION_PROPS} from '../../constants'
6
+ import {POPOVER_MOTION_CONTENT_OPACITY_PROPERTY, POPOVER_MOTION_PROPS} from '../../constants'
7
7
  import {BoxOverflow, CardTone, Placement, PopoverMargins, Radius} from '../../types'
8
8
  import {Arrow, useLayer} from '../../utils'
9
9
  import {Card, CardProps} from '../card'
@@ -22,11 +22,10 @@ const MotionCard = styled(motion.create(Card))`
22
22
  flex-direction: column;
23
23
  width: max-content;
24
24
  min-width: min-content;
25
- will-change: transform;
26
- `
27
-
28
- const MotionFlex = styled(motion.create(Flex))`
29
- will-change: opacity;
25
+ & > * {
26
+ opacity: var(${POPOVER_MOTION_CONTENT_OPACITY_PROPERTY}, 1);
27
+ will-change: opacity;
28
+ }
30
29
  `
31
30
 
32
31
  /**
@@ -132,24 +131,13 @@ export const PopoverCard = memo(
132
131
  sizing="border"
133
132
  style={rootStyle}
134
133
  tone={tone}
135
- variants={POPOVER_MOTION_PROPS.outerVariants}
136
- transition={POPOVER_MOTION_PROPS.transition}
137
- initial={animate ? ['hidden', 'initial'] : undefined}
138
- animate={animate ? ['visible', 'scaleIn'] : undefined}
139
- exit={animate ? ['hidden', 'scaleOut'] : undefined}
134
+ {...(animate ? POPOVER_MOTION_PROPS : {})}
140
135
  >
141
- <MotionFlex
142
- data-ui="Popover__wrapper"
143
- direction="column"
144
- flex={1}
145
- overflow={overflow}
146
- variants={POPOVER_MOTION_PROPS.innerVariants}
147
- transition={POPOVER_MOTION_PROPS.transition}
148
- >
136
+ <Flex data-ui="Popover__wrapper" direction="column" flex={1} overflow={overflow}>
149
137
  <Flex direction="column" flex={1} padding={padding}>
150
138
  {children}
151
139
  </Flex>
152
- </MotionFlex>
140
+ </Flex>
153
141
 
154
142
  {arrow && (
155
143
  <Arrow
@@ -219,6 +219,49 @@ export const Tooltip = forwardRef(function Tooltip(
219
219
  [isInsideGroup, delayGroupContext, openDelay, tooltipId, closeDelay, setIsOpen],
220
220
  )
221
221
 
222
+ const handleBlur = useCallback(
223
+ (e: FocusEvent) => {
224
+ handleIsOpenChange(false)
225
+ childProp?.props?.onBlur?.(e)
226
+ },
227
+ [childProp?.props, handleIsOpenChange],
228
+ )
229
+ const handleClick = useCallback(
230
+ (e: MouseEvent) => {
231
+ handleIsOpenChange(false, true)
232
+ childProp?.props.onClick?.(e)
233
+ },
234
+ [childProp?.props, handleIsOpenChange],
235
+ )
236
+ const handleContextMenu = useCallback(
237
+ (e: MouseEvent) => {
238
+ handleIsOpenChange(false, true)
239
+ childProp?.props.onContextMenu?.(e)
240
+ },
241
+ [childProp?.props, handleIsOpenChange],
242
+ )
243
+ const handleFocus = useCallback(
244
+ (e: FocusEvent) => {
245
+ handleIsOpenChange(true)
246
+ childProp?.props?.onFocus?.(e)
247
+ },
248
+ [childProp?.props, handleIsOpenChange],
249
+ )
250
+ const handleMouseEnter = useCallback(
251
+ (e: MouseEvent) => {
252
+ handleIsOpenChange(true)
253
+ childProp?.props?.onMouseEnter?.(e)
254
+ },
255
+ [childProp?.props, handleIsOpenChange],
256
+ )
257
+ const handleMouseLeave = useCallback(
258
+ (e: MouseEvent) => {
259
+ handleIsOpenChange(false)
260
+ childProp?.props?.onMouseLeave?.(e)
261
+ },
262
+ [childProp?.props, handleIsOpenChange],
263
+ )
264
+
222
265
  // Handle closing the tooltip when the mouse leaves the referenceElement
223
266
  useCloseOnMouseLeave({handleIsOpenChange, referenceElement, showTooltip})
224
267
 
@@ -249,28 +292,6 @@ export const Tooltip = forwardRef(function Tooltip(
249
292
  }
250
293
  }, [handleIsOpenChange, showTooltip])
251
294
 
252
- /* eslint-disable padding-line-between-statements */
253
- // Handle delays and grouping
254
- const handleChildEvent = useEffectEvent((open: boolean, immediate?: boolean) =>
255
- handleIsOpenChange(open, immediate),
256
- )
257
- useEffect(() => {
258
- if (!referenceElement) return
259
-
260
- const controller = new AbortController()
261
- const {signal} = controller
262
-
263
- referenceElement.addEventListener('blur', () => handleChildEvent(false), {signal})
264
- referenceElement.addEventListener('focus', () => handleChildEvent(true), {signal})
265
- referenceElement.addEventListener('mouseenter', () => handleChildEvent(true), {signal})
266
- referenceElement.addEventListener('mouseleave', () => handleChildEvent(false), {signal})
267
- referenceElement.addEventListener('click', () => handleChildEvent(false, true), {signal})
268
- referenceElement.addEventListener('contextmenu', () => handleChildEvent(false, true), {signal})
269
-
270
- return () => controller.abort()
271
- }, [referenceElement])
272
- /* eslint-enable padding-line-between-statements */
273
-
274
295
  // // Set the max width of the tooltip based on boundaries and portals
275
296
  useLayoutEffect(() => {
276
297
  // Get the maximum tooltip width (sans tooltip padding)
@@ -303,8 +324,24 @@ export const Tooltip = forwardRef(function Tooltip(
303
324
  const child = useMemo(() => {
304
325
  if (!childProp) return null
305
326
 
306
- return cloneElement(childProp, {ref: setReferenceElement})
307
- }, [childProp])
327
+ return cloneElement(childProp, {
328
+ onBlur: handleBlur,
329
+ onFocus: handleFocus,
330
+ onMouseEnter: handleMouseEnter,
331
+ onMouseLeave: handleMouseLeave,
332
+ onClick: handleClick,
333
+ onContextMenu: handleContextMenu,
334
+ ref: setReferenceElement,
335
+ })
336
+ }, [
337
+ childProp,
338
+ handleBlur,
339
+ handleClick,
340
+ handleContextMenu,
341
+ handleFocus,
342
+ handleMouseEnter,
343
+ handleMouseLeave,
344
+ ])
308
345
 
309
346
  // If there's a child then we need to set the reference element to the cloned child ref
310
347
  // and if child changes we make sure to update or remove the reference element.
@@ -2,7 +2,7 @@ import {ThemeColorSchemeKey} from '@sanity/ui/theme'
2
2
  import {type MotionProps, motion} from 'framer-motion'
3
3
  import React, {CSSProperties, forwardRef, memo, useMemo} from 'react'
4
4
  import {styled} from 'styled-components'
5
- import {POPOVER_MOTION_PROPS} from '../../constants'
5
+ import {POPOVER_MOTION_CONTENT_OPACITY_PROPERTY, POPOVER_MOTION_PROPS} from '../../constants'
6
6
  import {Placement, Radius} from '../../types'
7
7
  import {Arrow} from '../../utils'
8
8
  import {Card, CardProps} from '../card'
@@ -13,7 +13,10 @@ import {
13
13
  } from './constants'
14
14
 
15
15
  const MotionCard = styled(motion.create(Card))`
16
- will-change: transform;
16
+ & > * {
17
+ opacity: var(${POPOVER_MOTION_CONTENT_OPACITY_PROPERTY}, 1);
18
+ will-change: opacity;
19
+ }
17
20
  `
18
21
 
19
22
  /**
@@ -86,11 +89,7 @@ export const TooltipCard = memo(
86
89
  scheme={scheme}
87
90
  shadow={shadow}
88
91
  style={rootStyle}
89
- variants={POPOVER_MOTION_PROPS.outerVariants}
90
- transition={POPOVER_MOTION_PROPS.transition}
91
- initial={animate ? ['hidden', 'initial'] : undefined}
92
- animate={animate ? ['visible', 'scaleIn'] : undefined}
93
- exit={animate ? ['hidden', 'scaleOut'] : undefined}
92
+ {...(animate ? POPOVER_MOTION_PROPS : {})}
94
93
  >
95
94
  {children}
96
95
 
@@ -66,8 +66,8 @@ const LayerChildren = forwardRef(function LayerChildren(
66
66
 
67
67
  return (
68
68
  <StyledLayer
69
- data-ui="Layer"
70
69
  {...restProps}
70
+ data-ui="Layer"
71
71
  onFocus={handleFocus}
72
72
  ref={ref}
73
73
  style={{...style, zIndex}}
@@ -1,50 +0,0 @@
1
- import {styled} from 'styled-components'
2
- import {Grid} from '../../primitives/grid'
3
- import {useLayer} from '../../utils'
4
-
5
- /**
6
- * @public
7
- */
8
- export interface ToastLayerProps {
9
- children: React.ReactNode
10
- padding?: number | number[]
11
- paddingX?: number | number[]
12
- paddingY?: number | number[]
13
- gap?: number | number[]
14
- }
15
-
16
- /**
17
- * @internal
18
- */
19
- export function ToastLayer(props: ToastLayerProps): React.JSX.Element {
20
- const {children, padding = 4, paddingX, paddingY, gap = 3} = props
21
- const {zIndex} = useLayer()
22
-
23
- return (
24
- <StyledLayer
25
- forwardedAs="ul"
26
- data-ui="ToastProvider"
27
- padding={padding}
28
- paddingX={paddingX}
29
- paddingY={paddingY}
30
- gap={gap}
31
- columns={1}
32
- style={{zIndex}}
33
- >
34
- {children}
35
- </StyledLayer>
36
- )
37
- }
38
-
39
- ToastLayer.displayName = 'ToastLayer'
40
-
41
- const StyledLayer = styled(Grid)`
42
- box-sizing: border-box;
43
- position: fixed;
44
- right: 0;
45
- bottom: 0;
46
- list-style: none;
47
- pointer-events: none;
48
- max-width: 420px;
49
- width: 100%;
50
- `