@sanity/ui 2.0.0-alpha.16 → 2.0.0-alpha.18
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.d.ts +83 -1
- package/dist/index.esm.js +35 -22
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +35 -22
- package/dist/index.js.map +1 -1
- package/package.json +5 -7
- package/src/components/autocomplete/autocomplete.tsx +6 -0
- package/src/components/dialog/dialog.tsx +2 -0
- package/src/components/hotkeys/hotkeys.tsx +2 -0
- package/src/components/menu/menu.tsx +2 -0
- package/src/components/menu/menuButton.tsx +2 -0
- package/src/components/toast/toast.tsx +4 -0
- package/src/primitives/_selectable/style.ts +2 -0
- package/src/primitives/avatar/avatar.tsx +16 -9
- package/src/primitives/avatar/avatarCounter.tsx +3 -3
- package/src/primitives/avatar/styles.ts +1 -1
- package/src/primitives/badge/badge.tsx +2 -0
- package/src/primitives/box/box.tsx +3 -0
- package/src/primitives/button/__workshop__/sanityUploadButton.tsx +2 -1
- package/src/primitives/button/styles.ts +1 -0
- package/src/primitives/card/card.tsx +3 -0
- package/src/primitives/card/styles.ts +2 -0
- package/src/primitives/checkbox/checkbox.tsx +2 -0
- package/src/primitives/code/code.tsx +1 -0
- package/src/primitives/container/container.tsx +2 -0
- package/src/primitives/flex/flex.tsx +2 -0
- package/src/primitives/grid/grid.tsx +2 -0
- package/src/primitives/heading/heading.tsx +2 -0
- package/src/primitives/inline/inline.tsx +3 -0
- package/src/primitives/kbd/kbd.tsx +2 -0
- package/src/primitives/label/label.tsx +2 -0
- package/src/primitives/popover/popover.tsx +6 -1
- package/src/primitives/radio/radio.tsx +2 -0
- package/src/primitives/select/select.tsx +2 -0
- package/src/primitives/select/styles.ts +2 -0
- package/src/primitives/spinner/spinner.tsx +2 -0
- package/src/primitives/stack/stack.tsx +2 -0
- package/src/primitives/switch/switch.tsx +4 -0
- package/src/primitives/text/text.tsx +3 -0
- package/src/primitives/textArea/textArea.tsx +3 -0
- package/src/primitives/textInput/textInput.tsx +2 -0
- package/src/primitives/tooltip/tooltip.test.tsx +67 -0
- package/src/primitives/tooltip/tooltip.tsx +28 -5
- package/src/primitives/types.ts +4 -0
- package/src/theme/lib/theme/avatar.ts +2 -0
- package/src/theme/studioTheme/theme.ts +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/ui",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.18",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"source": "./src/index.ts",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"test": "jest",
|
|
45
45
|
"test:browser": "start-server-and-test 'run-s workshop:build workshop:start' http://localhost:1337 'run-s cypress:run'",
|
|
46
46
|
"watch": "pkg watch --strict",
|
|
47
|
-
"workshop:build": "
|
|
48
|
-
"workshop:dev": "
|
|
47
|
+
"workshop:build": "workshop build",
|
|
48
|
+
"workshop:dev": "workshop dev",
|
|
49
49
|
"workshop:start": "http-server -a localhost -c-0 -p 1337 -s -P http://localhost:1337/index.html? dist",
|
|
50
50
|
"storybook": "storybook dev -p 6006",
|
|
51
51
|
"build-storybook": "storybook build"
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@juggle/resize-observer": "^3.4.0",
|
|
69
69
|
"@sanity/pkg-utils": "^2.4.8",
|
|
70
70
|
"@sanity/semantic-release-preset": "^4.1.4",
|
|
71
|
-
"@sanity/ui-workshop": "^
|
|
71
|
+
"@sanity/ui-workshop": "^2.0.0",
|
|
72
72
|
"@storybook/addon-a11y": "^7.4.5",
|
|
73
73
|
"@storybook/addon-docs": "^7.4.5",
|
|
74
74
|
"@storybook/addon-essentials": "^7.4.5",
|
|
@@ -100,8 +100,6 @@
|
|
|
100
100
|
"cypress": "^13.1.0",
|
|
101
101
|
"cypress-real-events": "^1.10.1",
|
|
102
102
|
"cz-conventional-changelog": "^3.3.0",
|
|
103
|
-
"esbuild": "^0.19.2",
|
|
104
|
-
"esbuild-register": "^3.4.2",
|
|
105
103
|
"eslint": "^8.48.0",
|
|
106
104
|
"eslint-config-prettier": "^9.0.0",
|
|
107
105
|
"eslint-plugin-import": "^2.28.1",
|
|
@@ -126,7 +124,7 @@
|
|
|
126
124
|
"rimraf": "^5.0.1",
|
|
127
125
|
"semantic-release": "^21.1.1",
|
|
128
126
|
"start-server-and-test": "^2.0.0",
|
|
129
|
-
"storybook": "^7.
|
|
127
|
+
"storybook": "^7.5.2",
|
|
130
128
|
"styled-components": "^6.0.7",
|
|
131
129
|
"tsconfig-paths": "^4.2.0",
|
|
132
130
|
"typescript": "^5.2.2"
|
|
@@ -60,6 +60,7 @@ export interface AutocompleteProps<Option extends BaseAutocompleteOption = BaseA
|
|
|
60
60
|
onSelect?: (value: string) => void
|
|
61
61
|
/** @beta */
|
|
62
62
|
openButton?: boolean | AutocompleteOpenButtonProps
|
|
63
|
+
/** The options to render. */
|
|
63
64
|
options?: Option[]
|
|
64
65
|
padding?: number | number[]
|
|
65
66
|
popover?: Omit<PopoverProps, 'content' | 'onMouseEnter' | 'onMouseLeave' | 'open'> &
|
|
@@ -68,6 +69,7 @@ export interface AutocompleteProps<Option extends BaseAutocompleteOption = BaseA
|
|
|
68
69
|
radius?: Radius | Radius[]
|
|
69
70
|
/** @beta */
|
|
70
71
|
relatedElements?: HTMLElement[]
|
|
72
|
+
/** The callback function for rendering each option. */
|
|
71
73
|
renderOption?: (option: Option) => ReactElement
|
|
72
74
|
/** @beta */
|
|
73
75
|
renderPopover?: (
|
|
@@ -82,6 +84,7 @@ export interface AutocompleteProps<Option extends BaseAutocompleteOption = BaseA
|
|
|
82
84
|
) => ReactNode
|
|
83
85
|
renderValue?: (value: string, option?: Option) => string
|
|
84
86
|
suffix?: ReactNode
|
|
87
|
+
/** The current value. */
|
|
85
88
|
value?: string
|
|
86
89
|
}
|
|
87
90
|
|
|
@@ -684,6 +687,9 @@ const InnerAutocomplete = forwardRef(function InnerAutocomplete<
|
|
|
684
687
|
})
|
|
685
688
|
|
|
686
689
|
/**
|
|
690
|
+
* The Autocomplete component is typically used for search components.
|
|
691
|
+
* It consists of a text input for writing a query, and properties for rendering suggestions.
|
|
692
|
+
*
|
|
687
693
|
* @public
|
|
688
694
|
*/
|
|
689
695
|
export const Autocomplete = InnerAutocomplete as <Option extends BaseAutocompleteOption>(
|
|
@@ -37,6 +37,10 @@ const TextBox = styled(Flex)`
|
|
|
37
37
|
`
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
+
* The `Toast` component gives feedback to users when an action has taken place.
|
|
41
|
+
*
|
|
42
|
+
* Toasts can be closed with a close button, or auto-dismiss after a certain timeout.
|
|
43
|
+
*
|
|
40
44
|
* @public
|
|
41
45
|
*/
|
|
42
46
|
export function Toast(
|
|
@@ -15,6 +15,8 @@ export interface AvatarProps {
|
|
|
15
15
|
arrowPosition?: AvatarPosition
|
|
16
16
|
as?: React.ElementType | keyof JSX.IntrinsicElements
|
|
17
17
|
color?: ThemeColorSpotKey
|
|
18
|
+
/** @beta */
|
|
19
|
+
hideInnerStroke?: boolean
|
|
18
20
|
initials?: string
|
|
19
21
|
onImageLoadError?: (event: Error) => void
|
|
20
22
|
size?: AvatarSize | AvatarSize[]
|
|
@@ -41,6 +43,8 @@ const Stroke = styled.ellipse(avatarStyle.stroke)
|
|
|
41
43
|
const Initials = styled.div(avatarStyle.initials)
|
|
42
44
|
|
|
43
45
|
/**
|
|
46
|
+
* Avatars are used to represent people and other agents (e.g. bots).
|
|
47
|
+
*
|
|
44
48
|
* @public
|
|
45
49
|
*/
|
|
46
50
|
export const Avatar = forwardRef(function Avatar(
|
|
@@ -56,6 +60,7 @@ export const Avatar = forwardRef(function Avatar(
|
|
|
56
60
|
onImageLoadError,
|
|
57
61
|
arrowPosition: arrowPositionProp,
|
|
58
62
|
animateArrowFrom,
|
|
63
|
+
hideInnerStroke,
|
|
59
64
|
status = 'online',
|
|
60
65
|
size: sizeProp = 0,
|
|
61
66
|
...restProps
|
|
@@ -140,13 +145,15 @@ export const Avatar = forwardRef(function Avatar(
|
|
|
140
145
|
</defs>
|
|
141
146
|
|
|
142
147
|
<circle cx={_radius} cy={_radius} r={_radius} fill={`url(#${imageId})`} />
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
{!hideInnerStroke && (
|
|
149
|
+
<BgStroke
|
|
150
|
+
cx={_radius}
|
|
151
|
+
cy={_radius}
|
|
152
|
+
rx={_radius}
|
|
153
|
+
ry={_radius}
|
|
154
|
+
vectorEffect="non-scaling-stroke"
|
|
155
|
+
/>
|
|
156
|
+
)}
|
|
150
157
|
<Stroke
|
|
151
158
|
cx={_radius}
|
|
152
159
|
cy={_radius}
|
|
@@ -161,8 +168,8 @@ export const Avatar = forwardRef(function Avatar(
|
|
|
161
168
|
{(imageFailed || !src) && initials && (
|
|
162
169
|
<>
|
|
163
170
|
<Initials>
|
|
164
|
-
<Text as="span" size={initialsSize}>
|
|
165
|
-
|
|
171
|
+
<Text as="span" size={initialsSize} weight="medium">
|
|
172
|
+
{initials}
|
|
166
173
|
</Text>
|
|
167
174
|
</Initials>
|
|
168
175
|
</>
|
|
@@ -36,7 +36,7 @@ function _avatarCounterBaseStyle(props: ThemeProps) {
|
|
|
36
36
|
background: var(--card-bg-color);
|
|
37
37
|
box-shadow:
|
|
38
38
|
0 0 0 1px var(--card-bg-color),
|
|
39
|
-
inset 0 0 0
|
|
39
|
+
inset 0 0 0 1px var(--card-hairline-hard-color);
|
|
40
40
|
padding: 0 ${rem(theme.sanity.space[2])};
|
|
41
41
|
|
|
42
42
|
&:not([hidden]) {
|
|
@@ -73,8 +73,8 @@ export const AvatarCounter = forwardRef(function AvatarCounter(
|
|
|
73
73
|
|
|
74
74
|
return (
|
|
75
75
|
<Root $size={size} data-ui="AvatarCounter" ref={ref}>
|
|
76
|
-
<Text as="span" size={counterSize}>
|
|
77
|
-
|
|
76
|
+
<Text as="span" size={counterSize} weight="medium">
|
|
77
|
+
{count}
|
|
78
78
|
</Text>
|
|
79
79
|
</Root>
|
|
80
80
|
)
|
|
@@ -4,7 +4,7 @@ import styled from 'styled-components'
|
|
|
4
4
|
|
|
5
5
|
const SanityUploadButton = styled(Button).attrs({forwardedAs: 'label'})`
|
|
6
6
|
& input {
|
|
7
|
-
|
|
7
|
+
appearance: none;
|
|
8
8
|
overflow: hidden;
|
|
9
9
|
overflow: clip;
|
|
10
10
|
top: 0;
|
|
@@ -14,6 +14,7 @@ const SanityUploadButton = styled(Button).attrs({forwardedAs: 'label'})`
|
|
|
14
14
|
opacity: 0;
|
|
15
15
|
position: absolute;
|
|
16
16
|
max-width: 0;
|
|
17
|
+
width: -webkit-fill-available;
|
|
17
18
|
width: stretch;
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -48,6 +48,9 @@ const Root = styled(Box)<
|
|
|
48
48
|
>(responsiveBorderStyle, responsiveRadiusStyle, responsiveShadowStyle, cardStyle)
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
+
* The `Card` component acts much like a `Box`, but with a background and foreground color.
|
|
52
|
+
* Components within a `Card` inherit its colors.
|
|
53
|
+
*
|
|
51
54
|
* @public
|
|
52
55
|
*/
|
|
53
56
|
export const Card = forwardRef(function Card(
|
|
@@ -11,6 +11,7 @@ import {codeBaseStyle} from './styles'
|
|
|
11
11
|
*/
|
|
12
12
|
export interface CodeProps {
|
|
13
13
|
as?: React.ElementType | keyof JSX.IntrinsicElements
|
|
14
|
+
/** Define the language to use for syntax highlighting. */
|
|
14
15
|
language?: string
|
|
15
16
|
size?: number | number[]
|
|
16
17
|
weight?: string
|
|
@@ -13,6 +13,8 @@ export interface GridProps extends Omit<BoxProps, 'display'>, ResponsiveGridProp
|
|
|
13
13
|
const Root = styled(Box)<ResponsiveGridStyleProps>(responsiveGridStyle)
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
+
* The `Grid` component is for building 2-dimensional layers (based on CSS grid).
|
|
17
|
+
*
|
|
16
18
|
* @public
|
|
17
19
|
*/
|
|
18
20
|
export const Grid = forwardRef(function Grid(
|
|
@@ -10,12 +10,15 @@ import {ResponsiveInlineSpaceStyleProps} from './types'
|
|
|
10
10
|
* @public
|
|
11
11
|
*/
|
|
12
12
|
export interface InlineProps extends Omit<BoxProps, 'display'> {
|
|
13
|
+
/** The spacing between children. */
|
|
13
14
|
space?: number | number[]
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
const Root = styled(Box)<ResponsiveInlineSpaceStyleProps>(inlineBaseStyle, inlineSpaceStyle)
|
|
17
18
|
|
|
18
19
|
/**
|
|
20
|
+
* The `Inline` component is a layout utility for aligning and spacing items horizontally.
|
|
21
|
+
*
|
|
19
22
|
* @public
|
|
20
23
|
*/
|
|
21
24
|
export const Inline = forwardRef(function Inline(
|
|
@@ -59,6 +59,7 @@ export interface PopoverProps
|
|
|
59
59
|
overflow?: BoxOverflow
|
|
60
60
|
padding?: number | number[]
|
|
61
61
|
placement?: Placement
|
|
62
|
+
/** Whether or not to render the popover in a portal element. */
|
|
62
63
|
portal?: boolean | string
|
|
63
64
|
preventOverflow?: boolean
|
|
64
65
|
referenceBoundary?: HTMLElement | null
|
|
@@ -72,7 +73,11 @@ export interface PopoverProps
|
|
|
72
73
|
width?: PopoverWidth | PopoverWidth[]
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* The `Popover` component is used to display some content on top of another.
|
|
78
|
+
*
|
|
79
|
+
* @public
|
|
80
|
+
*/
|
|
76
81
|
export const Popover = memo(
|
|
77
82
|
forwardRef(function Popover(
|
|
78
83
|
props: PopoverProps &
|
|
@@ -15,6 +15,8 @@ export interface StackProps extends BoxProps {
|
|
|
15
15
|
const Root = styled(Box)<ResponsiveStackSpaceStyleProps>(stackBaseStyle, responsiveStackSpaceStyle)
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
+
* The `Stack` component is used to place elements on top of each other.
|
|
19
|
+
*
|
|
18
20
|
* @public
|
|
19
21
|
*/
|
|
20
22
|
export const Stack = forwardRef(function Stack(
|
|
@@ -23,6 +23,10 @@ const Track = styled.span(switchTrackStyles)
|
|
|
23
23
|
const Thumb = styled.span<{$checked?: boolean; $indeterminate?: boolean}>(switchThumbStyles)
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
+
* The `Switch` component allows the user to toggle a setting on and off.
|
|
27
|
+
*
|
|
28
|
+
* Extends all properties of an `<input type="checkbox" />` element, except type.
|
|
29
|
+
*
|
|
26
30
|
* @public
|
|
27
31
|
*/
|
|
28
32
|
export const Switch = forwardRef(function Switch(
|
|
@@ -17,6 +17,7 @@ export interface TextProps {
|
|
|
17
17
|
accent?: boolean
|
|
18
18
|
align?: TextAlign | TextAlign[]
|
|
19
19
|
as?: React.ElementType | keyof JSX.IntrinsicElements
|
|
20
|
+
/** When `true` the text color will be muted. */
|
|
20
21
|
muted?: boolean
|
|
21
22
|
size?: number | number[]
|
|
22
23
|
/**
|
|
@@ -43,6 +44,8 @@ const SpanWithTextOverflow = styled.span`
|
|
|
43
44
|
`
|
|
44
45
|
|
|
45
46
|
/**
|
|
47
|
+
* The `Text` component is an agile, themed typographic element.
|
|
48
|
+
*
|
|
46
49
|
* @public
|
|
47
50
|
*/
|
|
48
51
|
export const Text = forwardRef(function Text(
|
|
@@ -275,4 +275,71 @@ describe('Tooltip', () => {
|
|
|
275
275
|
jest.clearAllMocks()
|
|
276
276
|
})
|
|
277
277
|
})
|
|
278
|
+
describe('Closing the <Tooltip /> with the Escape key', () => {
|
|
279
|
+
it('Standalone tooltip closes immediately with Escape key', () => {
|
|
280
|
+
const delay = 150
|
|
281
|
+
|
|
282
|
+
jest.useFakeTimers()
|
|
283
|
+
|
|
284
|
+
render(
|
|
285
|
+
<Tooltip
|
|
286
|
+
content={<Text size={1}>{'Tooltip content'}</Text>}
|
|
287
|
+
placement={'top'}
|
|
288
|
+
delay={delay}
|
|
289
|
+
>
|
|
290
|
+
<Button mode="bleed" text="Hover me" />
|
|
291
|
+
</Tooltip>,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
const button = screen.getByText('Hover me')
|
|
295
|
+
|
|
296
|
+
// Validate tooltip content is not rendered
|
|
297
|
+
expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
|
|
298
|
+
fireEvent.focus(button)
|
|
299
|
+
act(() => jest.advanceTimersByTime(delay))
|
|
300
|
+
|
|
301
|
+
// Validate tooltip content is rendered
|
|
302
|
+
screen.getByText('Tooltip content')
|
|
303
|
+
|
|
304
|
+
act(() => {
|
|
305
|
+
fireEvent.keyDown(button, {key: 'Escape', code: 'Escape'})
|
|
306
|
+
})
|
|
307
|
+
// Validate tooltip content is not rendered anymore
|
|
308
|
+
expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
|
|
309
|
+
})
|
|
310
|
+
it('With <TooltipDelayGroupProvider /> closes immediately with Escape key', () => {
|
|
311
|
+
const delay = 150
|
|
312
|
+
|
|
313
|
+
jest.useFakeTimers()
|
|
314
|
+
|
|
315
|
+
render(
|
|
316
|
+
<TooltipDelayGroupProvider delay={{close: delay}}>
|
|
317
|
+
<Tooltip
|
|
318
|
+
content={<Text size={1}>{'Tooltip content'}</Text>}
|
|
319
|
+
placement={'top'}
|
|
320
|
+
delay={{close: delay}}
|
|
321
|
+
>
|
|
322
|
+
<Button mode="bleed" text="Hover me" />
|
|
323
|
+
</Tooltip>
|
|
324
|
+
</TooltipDelayGroupProvider>,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
const button = screen.getByText('Hover me')
|
|
328
|
+
|
|
329
|
+
// Validate tooltip content is not rendered
|
|
330
|
+
expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
|
|
331
|
+
fireEvent.focus(button)
|
|
332
|
+
|
|
333
|
+
act(() => jest.advanceTimersByTime(delay))
|
|
334
|
+
|
|
335
|
+
// Validate tooltip content is rendered
|
|
336
|
+
screen.getByText('Tooltip content')
|
|
337
|
+
|
|
338
|
+
act(() => {
|
|
339
|
+
fireEvent.keyDown(button, {key: 'Escape', code: 'Escape'})
|
|
340
|
+
})
|
|
341
|
+
// Validate tooltip content is not rendered anymore
|
|
342
|
+
expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument()
|
|
343
|
+
})
|
|
344
|
+
})
|
|
278
345
|
})
|
|
@@ -48,6 +48,7 @@ export interface TooltipProps extends Omit<LayerProps, 'as'> {
|
|
|
48
48
|
fallbackPlacements?: Placement[]
|
|
49
49
|
padding?: number | number[]
|
|
50
50
|
placement?: Placement
|
|
51
|
+
/** Whether or not to render the tooltip in a portal element. */
|
|
51
52
|
portal?: boolean | string
|
|
52
53
|
scheme?: ThemeColorSchemeKey
|
|
53
54
|
shadow?: number | number[]
|
|
@@ -66,6 +67,8 @@ const Root = styled(Layer)`
|
|
|
66
67
|
`
|
|
67
68
|
|
|
68
69
|
/**
|
|
70
|
+
* Tooltips display information when hovering, focusing or tapping.
|
|
71
|
+
*
|
|
69
72
|
* @public
|
|
70
73
|
*/
|
|
71
74
|
export const Tooltip = forwardRef(function Tooltip(
|
|
@@ -177,23 +180,27 @@ export const Tooltip = forwardRef(function Tooltip(
|
|
|
177
180
|
const closeDelay = isInsideGroup ? delayGroupContext.closeDelay : closeDelayProp
|
|
178
181
|
|
|
179
182
|
const handleIsOpenChange = useCallback(
|
|
180
|
-
(open: boolean) => {
|
|
183
|
+
(open: boolean, immediate?: boolean) => {
|
|
181
184
|
if (isInsideGroup) {
|
|
182
185
|
// When it's inside a group, the open or close status will be handled by the group.
|
|
183
186
|
if (open) {
|
|
184
|
-
|
|
185
|
-
|
|
187
|
+
const groupedOpenDelay = immediate ? 0 : openDelay
|
|
188
|
+
|
|
189
|
+
delayGroupContext.setIsGroupActive(open, groupedOpenDelay)
|
|
190
|
+
delayGroupContext.setOpenTooltipId(tooltipId, groupedOpenDelay)
|
|
186
191
|
} else {
|
|
187
192
|
const minimumGroupDeactivateDelay = 200 // We should provide some delay to allow the user to reach the next tooltip.
|
|
188
193
|
const groupDeactivateDelay =
|
|
189
194
|
closeDelay > minimumGroupDeactivateDelay ? closeDelay : minimumGroupDeactivateDelay
|
|
190
195
|
|
|
191
196
|
delayGroupContext.setIsGroupActive(open, groupDeactivateDelay)
|
|
192
|
-
delayGroupContext.setOpenTooltipId(null, closeDelay)
|
|
197
|
+
delayGroupContext.setOpenTooltipId(null, immediate ? 0 : closeDelay)
|
|
193
198
|
}
|
|
194
199
|
} else {
|
|
200
|
+
const standaloneDelay = immediate ? 0 : open ? openDelay : closeDelay
|
|
201
|
+
|
|
195
202
|
// When it's not inside a group, the open or close status will be handled by the tooltip itself.
|
|
196
|
-
setIsOpen(open,
|
|
203
|
+
setIsOpen(open, standaloneDelay)
|
|
197
204
|
}
|
|
198
205
|
},
|
|
199
206
|
[isInsideGroup, delayGroupContext, openDelay, tooltipId, closeDelay, setIsOpen],
|
|
@@ -243,6 +250,22 @@ export const Tooltip = forwardRef(function Tooltip(
|
|
|
243
250
|
// Update reference
|
|
244
251
|
useEffect(() => refs.setReference(referenceElement), [referenceElement, refs])
|
|
245
252
|
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
// If the user clicks on escape key, close the tooltip.
|
|
255
|
+
if (!showTooltip) return
|
|
256
|
+
|
|
257
|
+
function handleWindowKeyDown(event: KeyboardEvent) {
|
|
258
|
+
if (event.key === 'Escape') {
|
|
259
|
+
handleIsOpenChange(false, true)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
window.addEventListener('keydown', handleWindowKeyDown)
|
|
264
|
+
|
|
265
|
+
return () => {
|
|
266
|
+
window.removeEventListener('keydown', handleWindowKeyDown)
|
|
267
|
+
}
|
|
268
|
+
}, [handleIsOpenChange, showTooltip])
|
|
246
269
|
const setArrow = useCallback(
|
|
247
270
|
(arrowEl: HTMLDivElement | null) => {
|
|
248
271
|
arrowRef.current = arrowEl
|