@sanity/ui 0.38.3-next.18 → 0.38.3-next.36

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 (38) hide show
  1. package/dist/sanity-ui.esm.js +9835 -0
  2. package/dist/sanity-ui.esm.js.map +1 -0
  3. package/dist/sanity-ui.js +9975 -1
  4. package/dist/sanity-ui.js.map +1 -1
  5. package/dist/types/src/index.d.ts +386 -384
  6. package/package.json +12 -19
  7. package/src/components/autocomplete/__workshop__/async.tsx +5 -3
  8. package/src/components/autocomplete/autocomplete.styles.tsx +0 -5
  9. package/src/components/autocomplete/autocomplete.tsx +51 -51
  10. package/src/components/autocomplete/constants.ts +3 -3
  11. package/src/components/menu/menu.test.tsx +2 -0
  12. package/src/components/toast/toast.test.tsx +2 -0
  13. package/src/constants.ts +17 -0
  14. package/src/primitives/badge/badge.test.tsx +2 -0
  15. package/src/primitives/button/button.test.tsx +2 -0
  16. package/src/primitives/popover/__workshop__/PlainStory.tsx +1 -0
  17. package/src/primitives/popover/__workshop__/RightAlignedStory.tsx +1 -0
  18. package/src/primitives/popover/__workshop__/TestStory.tsx +1 -0
  19. package/src/primitives/popover/popover.tsx +239 -197
  20. package/src/primitives/popover/{arrow.tsx → popoverArrow.tsx} +5 -5
  21. package/src/primitives/popover/popoverCard.tsx +160 -0
  22. package/src/primitives/tooltip/__workshop__/props.tsx +14 -9
  23. package/src/primitives/tooltip/tooltip.tsx +162 -92
  24. package/src/primitives/tooltip/tooltipArrow.tsx +25 -10
  25. package/src/styles/margin/marginsStyle.test.ts +2 -0
  26. package/src/styles/padding/paddingStyle.test.ts +2 -0
  27. package/src/theme/lib/color-fns/color-fns.test.ts +2 -0
  28. package/src/theme/lib/theme/color/color.test.ts +3 -1
  29. package/src/theme/theme.test.tsx +2 -0
  30. package/src/utils/boundaryElement/boundaryElement.test.tsx +2 -0
  31. package/src/utils/layer/layer.test.tsx +2 -0
  32. package/src/utils/portal/portal.test.tsx +2 -0
  33. package/dist/sanity-ui.mjs +0 -2
  34. package/dist/sanity-ui.mjs.map +0 -1
  35. package/src/primitives/popover/modifiers/getPopoverModifiers.ts +0 -137
  36. package/src/primitives/popover/modifiers/index.ts +0 -1
  37. package/src/primitives/popover/modifiers/types.ts +0 -18
  38. package/src/primitives/popover/modifiers/usePopoverModifiers.ts +0 -58
@@ -1,22 +1,27 @@
1
- import {Box, Button, Card, Flex, Text, Tooltip} from '@sanity/ui'
2
- import {useSelect, useText} from '@sanity/ui-workshop'
3
- import {WORKSHOP_PLACEMENT_OPTIONS} from '../../../__workshop__/constants'
1
+ import {Button, Card, Flex, Text, Tooltip} from '@sanity/ui'
2
+ import {useBoolean, useSelect, useText} from '@sanity/ui-workshop'
3
+ import {
4
+ WORKSHOP_PLACEMENT_OPTIONS,
5
+ WORKSHOP_SHADOW_OPTIONS,
6
+ WORKSHOP_SPACE_OPTIONS,
7
+ } from '../../../__workshop__/constants'
4
8
 
5
9
  export default function PropsStory() {
6
10
  const content = useText('Content', 'Tooltip content')
11
+ const padding = useSelect('Padding', WORKSHOP_SPACE_OPTIONS, 2, 'Props')
7
12
  const placement = useSelect('Placement', WORKSHOP_PLACEMENT_OPTIONS, 'top')
13
+ const portal = useBoolean('Portal', true)
14
+ const shadow = useSelect('Shadow', WORKSHOP_SHADOW_OPTIONS, 2)
8
15
 
9
16
  return (
10
17
  <Card height="fill">
11
18
  <Flex align="center" height="fill" justify="center" padding={4} sizing="border">
12
19
  <Tooltip
13
- content={
14
- <Box padding={2}>
15
- <Text size={1}>{content}</Text>
16
- </Box>
17
- }
20
+ content={<Text size={1}>{content}</Text>}
21
+ padding={padding}
18
22
  placement={placement}
19
- portal
23
+ portal={portal}
24
+ shadow={shadow}
20
25
  >
21
26
  <Button mode="bleed" text="Hover me" />
22
27
  </Tooltip>
@@ -1,6 +1,26 @@
1
- import {cloneElement, forwardRef, useCallback, useEffect, useRef, useState} from 'react'
2
- import {usePopper} from 'react-popper'
1
+ import {
2
+ arrow,
3
+ autoUpdate,
4
+ flip,
5
+ offset,
6
+ shift,
7
+ useFloating,
8
+ Middleware,
9
+ RootBoundary,
10
+ } from '@floating-ui/react-dom'
11
+ import {
12
+ cloneElement,
13
+ forwardRef,
14
+ useCallback,
15
+ useEffect,
16
+ useMemo,
17
+ useRef,
18
+ useState,
19
+ CSSProperties,
20
+ ForwardedRef,
21
+ } from 'react'
3
22
  import styled from 'styled-components'
23
+ import {FLOATING_STATIC_SIDES} from '../../constants'
4
24
  import {useArrayProp, useForwardedRef} from '../../hooks'
5
25
  import {ThemeColorSchemeKey, useTheme} from '../../theme'
6
26
  import {Placement} from '../../types'
@@ -12,15 +32,18 @@ import {TooltipArrow} from './tooltipArrow'
12
32
  * @public
13
33
  */
14
34
  export interface TooltipProps extends Omit<LayerProps, 'as'> {
35
+ /** @deprecated Use `fallbackPlacements` instead. */
15
36
  allowedAutoPlacements?: Placement[]
16
37
  boundaryElement?: HTMLElement | null
17
38
  children?: React.ReactElement
18
39
  content?: React.ReactNode
19
40
  disabled?: boolean
20
41
  fallbackPlacements?: Placement[]
42
+ padding?: number | number[]
21
43
  placement?: Placement
22
44
  portal?: boolean | string
23
45
  scheme?: ThemeColorSchemeKey
46
+ shadow?: number | number[]
24
47
  }
25
48
 
26
49
  const Root = styled(Layer)`
@@ -37,55 +60,89 @@ export const Tooltip = forwardRef(function Tooltip(
37
60
  const boundaryElementContext = useBoundaryElement()
38
61
  const theme = useTheme()
39
62
  const {
40
- allowedAutoPlacements,
41
63
  boundaryElement = boundaryElementContext?.element,
42
- children,
64
+ children: childProp,
43
65
  content,
44
66
  disabled,
45
67
  fallbackPlacements: fallbackPlacementsProp,
46
- placement = 'bottom',
68
+ padding,
69
+ placement: placementProp = 'bottom',
47
70
  portal,
48
71
  scheme,
72
+ shadow = 2,
49
73
  zOffset = theme.sanity.layer?.tooltip.zOffset,
50
74
  ...restProps
51
75
  } = props
52
76
  const fallbackPlacements = useArrayProp(fallbackPlacementsProp)
53
77
  const forwardedRef = useForwardedRef(ref)
54
78
  const [referenceElement, setReferenceElement] = useState<HTMLElement | null>(null)
55
- const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
56
- const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
57
- const popper = usePopper(referenceElement, popperElement, {
58
- placement,
59
- modifiers: [
60
- {
61
- name: 'arrow',
62
- options: {
63
- element: arrowElement,
64
- padding: 4,
65
- },
66
- },
67
- {
68
- name: 'preventOverflow',
69
- options: {
70
- altAxis: true,
71
- boundary: boundaryElement || undefined,
72
- padding: 4,
73
- },
74
- },
75
- {
76
- name: 'offset',
77
- options: {offset: [0, 3]},
78
- },
79
- {
80
- name: 'flip',
81
- options: {
82
- allowedAutoPlacements,
83
- fallbackPlacements,
84
- },
85
- },
86
- ],
79
+ const arrowRef = useRef<HTMLDivElement | null>(null)
80
+ const rootBoundary: RootBoundary = 'viewport'
81
+
82
+ const middleware = useMemo(() => {
83
+ const ret: Middleware[] = []
84
+
85
+ // Flip the floating element when leaving the boundary box
86
+ ret.push(
87
+ flip({
88
+ boundary: boundaryElement || undefined,
89
+ fallbackPlacements,
90
+ padding: 4,
91
+ rootBoundary,
92
+ })
93
+ )
94
+
95
+ // Define distance between reference and floating element
96
+ ret.push(offset({mainAxis: 3}))
97
+
98
+ // Shift the tooltip so its sits with the boundary eleement
99
+ ret.push(
100
+ shift({
101
+ boundary: boundaryElement || undefined,
102
+ rootBoundary,
103
+ padding: 4,
104
+ })
105
+ )
106
+
107
+ // Place arrow
108
+ ret.push(arrow({element: arrowRef, padding: 2}))
109
+
110
+ return ret
111
+ }, [boundaryElement, fallbackPlacements])
112
+
113
+ const {x, y, placement, reference, floating, middlewareData, update, strategy} = useFloating({
114
+ middleware,
115
+ placement: placementProp,
116
+ whileElementsMounted: autoUpdate,
87
117
  })
88
- const {forceUpdate} = popper
118
+
119
+ const rootStyle: CSSProperties = useMemo(
120
+ () => ({
121
+ position: strategy,
122
+ top: y ?? 0,
123
+ left: x ?? 0,
124
+ }),
125
+ [strategy, x, y]
126
+ )
127
+
128
+ const staticSide = placement && FLOATING_STATIC_SIDES[placement.split('-')[0]]
129
+
130
+ const arrowX = middlewareData.arrow?.x
131
+ const arrowY = middlewareData.arrow?.y
132
+
133
+ const arrowStyle: CSSProperties = useMemo(() => {
134
+ const style: CSSProperties = {
135
+ left: arrowX !== null ? arrowX : undefined,
136
+ top: arrowY !== null ? arrowY : undefined,
137
+ right: undefined,
138
+ bottom: undefined,
139
+ }
140
+
141
+ if (staticSide) style[staticSide] = -15
142
+
143
+ return style
144
+ }, [arrowX, arrowY, staticSide])
145
+
89
146
  const [isOpen, setIsOpen] = useState(false)
90
147
  const handleBlur = useCallback(() => setIsOpen(false), [])
91
148
  const handleFocus = useCallback(() => setIsOpen(true), [])
@@ -118,25 +175,6 @@ export const Tooltip = forwardRef(function Tooltip(
118
175
  }
119
176
  }, [isOpen, referenceElement])
120
177
 
121
- const updateTimeoutRef = useRef<NodeJS.Timeout | null>(null)
122
-
123
- useEffect(() => {
124
- if (updateTimeoutRef.current) {
125
- clearTimeout(updateTimeoutRef.current)
126
- updateTimeoutRef.current = null
127
- }
128
-
129
- updateTimeoutRef.current = setTimeout(() => {
130
- if (forceUpdate) {
131
- try {
132
- forceUpdate()
133
- } catch (_) {
134
- // ignore caught error
135
- }
136
- }
137
- }, 0)
138
- }, [forceUpdate, content])
139
-
140
178
  // Close when `disabled` changes to `true`
141
179
  useEffect(() => {
142
180
  if (disabled) setIsOpen(false)
@@ -147,54 +185,86 @@ export const Tooltip = forwardRef(function Tooltip(
147
185
  if (!content) setIsOpen(false)
148
186
  }, [content])
149
187
 
150
- const setRef = (el: HTMLDivElement | null) => {
151
- setPopperElement(el)
152
- forwardedRef.current = el
153
- }
154
-
155
- if (!children) return <></>
156
-
157
- if (disabled) return children
158
-
159
- const referenceProps = {
160
- onBlur: handleBlur,
161
- onFocus: handleFocus,
162
- onMouseEnter: handleMouseEnter,
163
- onMouseLeave: handleMouseLeave,
164
- ref: setReferenceElement,
165
- }
166
-
167
- const referenceNode = cloneElement(children, referenceProps)
168
-
169
- const popperNode = (
170
- <Root
171
- data-ui="Tooltip"
172
- {...restProps}
173
- {...popper.attributes.popper}
174
- ref={setRef}
175
- style={popper.styles.popper}
176
- zOffset={zOffset}
177
- >
178
- <Card radius={2} scheme={scheme} shadow={3}>
188
+ // Update reference
189
+ useEffect(() => reference(referenceElement), [reference, referenceElement])
190
+
191
+ const setArrow = useCallback(
192
+ (arrowEl: HTMLDivElement | null) => {
193
+ arrowRef.current = arrowEl
194
+ update()
195
+ },
196
+ [update]
197
+ )
198
+
199
+ const setFloating = useCallback(
200
+ (node: HTMLDivElement | null) => {
201
+ forwardedRef.current = node
202
+ floating(node)
203
+ },
204
+ [floating, forwardedRef]
205
+ )
206
+
207
+ const childRef: ForwardedRef<HTMLElement | null> = (childProp as any)?.ref
208
+
209
+ const setReference = useCallback(
210
+ (node: HTMLElement | null) => {
211
+ if (typeof childRef === 'function') {
212
+ childRef(node)
213
+ } else if (childRef) {
214
+ childRef.current = node
215
+ }
216
+
217
+ // childRef.current = node
218
+ setReferenceElement(node)
219
+ },
220
+ [childRef]
221
+ )
222
+
223
+ const child = useMemo(() => {
224
+ if (!childProp) return null
225
+
226
+ return cloneElement(childProp, {
227
+ onBlur: handleBlur,
228
+ onFocus: handleFocus,
229
+ onMouseEnter: handleMouseEnter,
230
+ onMouseLeave: handleMouseLeave,
231
+ ref: setReference,
232
+ })
233
+ }, [childProp, handleBlur, handleFocus, handleMouseEnter, handleMouseLeave, setReference])
234
+
235
+ if (!child) return <></>
236
+
237
+ if (disabled) return child
238
+
239
+ const root = (
240
+ <Root data-ui="Tooltip" {...restProps} ref={setFloating} style={rootStyle} zOffset={zOffset}>
241
+ <Card
242
+ data-ui="Tooltip__card"
243
+ data-placement={placement}
244
+ padding={padding}
245
+ radius={2}
246
+ scheme={scheme}
247
+ shadow={shadow}
248
+ >
179
249
  {content}
180
- <TooltipArrow ref={setArrowElement} style={popper.styles.arrow} />
250
+ <TooltipArrow ref={setArrow} style={arrowStyle} />
181
251
  </Card>
182
252
  </Root>
183
253
  )
184
254
 
185
255
  return (
186
256
  <>
187
- {referenceNode}
257
+ {child}
188
258
 
189
259
  {isOpen && (
190
260
  <>
191
- {portal && (
261
+ {portal ? (
192
262
  <Portal __unstable_name={typeof portal === 'string' ? portal : undefined}>
193
- {popperNode}
263
+ {root}
194
264
  </Portal>
265
+ ) : (
266
+ root
195
267
  )}
196
-
197
- {!portal && popperNode}
198
268
  </>
199
269
  )}
200
270
  </>
@@ -3,6 +3,21 @@ import styled from 'styled-components'
3
3
 
4
4
  const Root = styled.div`
5
5
  position: absolute;
6
+ pointer-events: none;
7
+ width: 15px;
8
+ height: 15px;
9
+ fill: none;
10
+
11
+ :empty + & {
12
+ display: none;
13
+ }
14
+
15
+ & > svg {
16
+ display: block;
17
+ transform-origin: 7.5px 7.5px;
18
+ }
19
+
20
+ /* position: absolute;
6
21
  width: 15px;
7
22
  height: 15px;
8
23
  fill: none;
@@ -16,30 +31,30 @@ const Root = styled.div`
16
31
  display: block;
17
32
  }
18
33
  transform-origin: 7.5px 7.5px;
19
- }
34
+ } */
20
35
 
21
- [data-popper-placement^='top'] > div > & {
22
- bottom: -15px;
36
+ [data-placement^='top'] > & {
37
+ bottom: -27px;
23
38
  }
24
39
 
25
- [data-popper-placement^='right'] > div > & {
26
- left: -15px;
40
+ [data-placement^='right'] > & {
41
+ left: -27px;
27
42
 
28
43
  & > svg {
29
44
  transform: rotate(90deg);
30
45
  }
31
46
  }
32
47
 
33
- [data-popper-placement^='left'] > div > & {
34
- right: -15px;
48
+ [data-placement^='left'] > & {
49
+ right: -27px;
35
50
 
36
51
  & > svg {
37
52
  transform: rotate(-90deg);
38
53
  }
39
54
  }
40
55
 
41
- [data-popper-placement^='bottom'] > div > & {
42
- top: -15px;
56
+ [data-placement^='bottom'] > & {
57
+ top: -27px;
43
58
 
44
59
  & > svg {
45
60
  transform: rotate(180deg);
@@ -64,7 +79,7 @@ export const TooltipArrow = forwardRef(function TooltipArrow(
64
79
  const {...restProps} = props
65
80
 
66
81
  return (
67
- <Root {...restProps} ref={ref}>
82
+ <Root data-ui="Tooltip__arrow" {...restProps} ref={ref}>
68
83
  <svg width="15" height="15" viewBox="0 0 15 15">
69
84
  <Border d="M11.5266 1C11.032 1.32802 10.5837 1.73105 10.1995 2.20057L9.04792 3.6081C8.24771 4.58614 6.7523 4.58614 5.95209 3.6081L4.80047 2.20057C4.41632 1.73105 3.96796 1.32802 3.47341 1H0.156727C1.65639 1 3.07687 1.67313 4.02651 2.83381L5.17813 4.24134C6.37844 5.70839 8.62156 5.70839 9.82187 4.24134L10.9735 2.83381C11.9231 1.67313 13.3436 1 14.8433 1H11.5266Z" />
70
85
  <Shape d="M0.156725 0C1.95632 0 3.66089 0.80776 4.80047 2.20057L5.95209 3.6081C6.75229 4.58614 8.24771 4.58614 9.04791 3.6081L10.1995 2.20057C11.3391 0.80776 13.0437 0 14.8433 0H15H0H0.156725Z" />
@@ -1,3 +1,5 @@
1
+ /** @jest-environment jsdom */
2
+
1
3
  import {studioTheme, Theme} from '../../theme'
2
4
  import {responsiveMarginStyle} from './marginStyle'
3
5
 
@@ -1,3 +1,5 @@
1
+ /** @jest-environment jsdom */
2
+
1
3
  import {studioTheme, Theme} from '../../theme'
2
4
  import {responsivePaddingStyle} from './paddingStyle'
3
5
 
@@ -1,3 +1,5 @@
1
+ /** @jest-environment jsdom */
2
+
1
3
  import {hexToRgb, multiply, parseColor, rgba, screen} from '.'
2
4
 
3
5
  const hues = {
@@ -1,4 +1,6 @@
1
- import {createColorTheme} from '.'
1
+ /** @jest-environment jsdom */
2
+
3
+ import {createColorTheme} from './factory'
2
4
 
3
5
  describe('color', () => {
4
6
  describe('createColorTheme', () => {
@@ -1,3 +1,5 @@
1
+ /** @jest-environment jsdom */
2
+
1
3
  import {render} from '../../test'
2
4
  import {studioTheme} from './studioTheme'
3
5
  import {ThemeContext} from './themeContext'
@@ -1,3 +1,5 @@
1
+ /** @jest-environment jsdom */
2
+
1
3
  import {render} from '../../../test'
2
4
  import {BoundaryElementContext} from './boundaryElementContext'
3
5
  import {BoundaryElementContextValue} from './types'
@@ -1,3 +1,5 @@
1
+ /** @jest-environment jsdom */
2
+
1
3
  import {render} from '../../../test'
2
4
  import {LayerContext} from './layerContext'
3
5
  import {LayerContextValue} from './types'
@@ -1,3 +1,5 @@
1
+ /** @jest-environment jsdom */
2
+
1
3
  import {render} from '../../../test'
2
4
  import {PortalContext} from './portalContext'
3
5
  import {PortalContextValue} from './types'