@sanity/ui 5.0.0-alpha.1 → 5.0.0-alpha.10
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/README.md +1 -1
- package/dist/cli/install.js +2 -1
- package/dist/index.d.ts +362 -76
- package/dist/index.js +420 -158
- package/dist/index.js.map +1 -1
- package/dist/polyfills.d.ts +1 -0
- package/dist/polyfills.js +5 -0
- package/dist/polyfills.js.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +9 -3
- package/src/cli/install.ts +2 -1
- package/src/components/badge/Badge.tsx +53 -0
- package/src/components/badge/badge.css +6 -0
- package/src/components/badge/badge.props.ts +31 -0
- package/src/components/box/Box.tsx +6 -1
- package/src/components/box/box.props.ts +7 -2
- package/src/components/button/button.props.ts +27 -9
- package/src/components/card/card.props.ts +6 -2
- package/src/components/checkbox/Checkbox.tsx +3 -2
- package/src/components/checkbox/checkbox.props.ts +11 -3
- package/src/components/code/code.props.ts +10 -3
- package/src/components/container/container.props.ts +6 -2
- package/src/components/dialog/Dialog.tsx +139 -0
- package/src/components/dialog/dialog.css +49 -0
- package/src/components/dialog/dialog.props.ts +45 -0
- package/src/components/eyebrow/Eyebrow.tsx +1 -1
- package/src/components/eyebrow/eyebrow.props.ts +6 -2
- package/src/components/flex/Flex.tsx +6 -1
- package/src/components/flex/flex.props.ts +6 -2
- package/src/components/grid/Grid.tsx +6 -1
- package/src/components/grid/grid.props.ts +6 -2
- package/src/components/h-stack/hStack.props.ts +3 -1
- package/src/components/heading/Heading.tsx +1 -1
- package/src/components/heading/heading.props.ts +8 -2
- package/src/components/icon/icon.props.ts +6 -2
- package/src/components/icon-button/iconButton.props.ts +7 -2
- package/src/components/index.css +3 -0
- package/src/components/indicator/indicator.props.ts +6 -2
- package/src/components/indicator-stack/indicatorStack.props.ts +3 -1
- package/src/components/inline/inline.props.ts +6 -2
- package/src/components/label/label.props.ts +7 -1
- package/src/components/link/link.props.ts +4 -4
- package/src/components/list/list.props.ts +27 -9
- package/src/components/popover/Popover.tsx +85 -0
- package/src/components/popover/popover.css +21 -0
- package/src/components/popover/popover.props.ts +38 -0
- package/src/components/press-area/pressArea.props.ts +3 -1
- package/src/components/radio/Radio.tsx +1 -1
- package/src/components/radio/radio.props.ts +7 -2
- package/src/components/skip-to-content/skipToContent.props.ts +9 -3
- package/src/components/spinner/Spinner.tsx +1 -1
- package/src/components/spinner/spinner.props.ts +3 -0
- package/src/components/switch/Switch.tsx +1 -1
- package/src/components/switch/switch.props.ts +7 -2
- package/src/components/text/Text.tsx +1 -1
- package/src/components/text/text.props.ts +6 -2
- package/src/components/tooltip/Tooltip.tsx +59 -55
- package/src/components/tooltip/tooltip.css +19 -40
- package/src/components/tooltip/tooltip.props.ts +24 -8
- package/src/components/tooltip-group/TooltipGroup.tsx +2 -5
- package/src/components/tooltip-group/tooltipGroup.props.ts +3 -1
- package/src/components/v-stack/vStack.props.ts +3 -1
- package/src/components/visually-hidden/visuallyHidden.props.ts +6 -2
- package/src/css/classes/generic/display.css +8 -8
- package/src/css/classes/generic/placement.css +31 -0
- package/src/css/classes/local/animation.css +4 -0
- package/src/css/classes/local/index.css +2 -0
- package/src/css/classes/local/top-anchor.css +4 -0
- package/src/css/global/reset.css +20 -18
- package/src/css/tokens/semantic.css +4 -0
- package/src/hooks/useIsClient.ts +10 -0
- package/src/index.ts +5 -0
- package/src/polyfills.ts +15 -0
- package/src/utils/mergeTriggerProps.ts +66 -0
- package/src/utils/renderPortal.tsx +14 -0
- package/src/version.ts +2 -2
|
@@ -41,7 +41,7 @@ export function Heading<T extends ElementType = 'h2'>({
|
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
43
|
<Component
|
|
44
|
-
className={clsx(headingClassName, 'sui-overflow-hidden', className)}
|
|
44
|
+
className={clsx(headingClassName, props.truncate && 'sui-overflow-hidden', className)}
|
|
45
45
|
style={style}
|
|
46
46
|
data-ui="Heading"
|
|
47
47
|
{...rest}
|
|
@@ -5,9 +5,15 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
5
5
|
|
|
6
6
|
/** @public */
|
|
7
7
|
export interface HeadingProps extends TypographyProps {
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Semantic heading element to render.
|
|
10
|
+
* @remarks Always set this explicitly.
|
|
11
|
+
*/
|
|
9
12
|
as?: HeadingTag
|
|
10
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Sets font size.
|
|
15
|
+
* @remarks Independent of `as`.
|
|
16
|
+
*/
|
|
11
17
|
size?: Responsive<HeadingSize>
|
|
12
18
|
}
|
|
13
19
|
|
|
@@ -10,9 +10,13 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
10
10
|
/** @public */
|
|
11
11
|
export interface IconProps
|
|
12
12
|
extends ComponentProps<'svg'>, Pick<TypographyProps, 'muted'>, MarginProps, ToneProps {
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* The icon component to render.
|
|
15
|
+
*/
|
|
14
16
|
icon: React.ComponentType<SVGProps<SVGSVGElement>>
|
|
15
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Sets icon size.
|
|
19
|
+
*/
|
|
16
20
|
size?: Responsive<IconSize>
|
|
17
21
|
}
|
|
18
22
|
|
|
@@ -9,9 +9,14 @@ export interface IconButtonProps<T extends React.ElementType = 'button'> extends
|
|
|
9
9
|
ButtonProps<T>,
|
|
10
10
|
'as' | 'density' | 'level' | 'loading' | 'tone'
|
|
11
11
|
> {
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Accessible name for the button.
|
|
14
|
+
* @remarks Because IconButtons have no visible text, this is required.
|
|
15
|
+
*/
|
|
13
16
|
'aria-label': string
|
|
14
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Icon to render.
|
|
19
|
+
*/
|
|
15
20
|
'icon': React.ComponentType<SVGProps<SVGSVGElement>>
|
|
16
21
|
}
|
|
17
22
|
|
package/src/components/index.css
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
@import './badge/badge.css';
|
|
1
2
|
@import './box/box.css';
|
|
2
3
|
@import './button/button.css';
|
|
3
4
|
@import './card/card.css';
|
|
4
5
|
@import './checkbox/checkbox.css';
|
|
5
6
|
@import './code/code.css';
|
|
6
7
|
@import './container/container.css';
|
|
8
|
+
@import './dialog/dialog.css';
|
|
7
9
|
@import './eyebrow/eyebrow.css';
|
|
8
10
|
@import './flex/flex.css';
|
|
9
11
|
@import './grid/grid.css';
|
|
@@ -15,6 +17,7 @@
|
|
|
15
17
|
@import './label/label.css';
|
|
16
18
|
@import './link/link.css';
|
|
17
19
|
@import './list/list.css';
|
|
20
|
+
@import './popover/popover.css';
|
|
18
21
|
@import './press-area/press-area.css';
|
|
19
22
|
@import './radio/radio.css';
|
|
20
23
|
@import './spinner/spinner.css';
|
|
@@ -3,9 +3,13 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
3
3
|
|
|
4
4
|
/** @beta */
|
|
5
5
|
export interface IndicatorProps<T extends React.ElementType = 'span'> extends ToneProps {
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* HTML element to render.
|
|
8
|
+
*/
|
|
7
9
|
as?: T
|
|
8
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Accessible label.
|
|
12
|
+
*/
|
|
9
13
|
label?: string
|
|
10
14
|
}
|
|
11
15
|
|
|
@@ -7,9 +7,13 @@ import {SPACE, type SpaceInherit} from '../../types/Space'
|
|
|
7
7
|
/** @deprecated Use HStack component instead */
|
|
8
8
|
/** @public */
|
|
9
9
|
export interface InlineProps<T extends React.ElementType = 'div'> extends PaddingProps {
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* HTML element or component to render.
|
|
12
|
+
*/
|
|
11
13
|
as?: T
|
|
12
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Space between children.
|
|
16
|
+
*/
|
|
13
17
|
gap?: Responsive<SpaceInherit>
|
|
14
18
|
}
|
|
15
19
|
|
|
@@ -3,8 +3,14 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
3
3
|
|
|
4
4
|
/** @beta */
|
|
5
5
|
export interface LabelProps extends React.ComponentProps<'label'>, MarginProps {
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Renders the label in a muted color.
|
|
8
|
+
*/
|
|
7
9
|
disabled?: boolean
|
|
10
|
+
/**
|
|
11
|
+
* Renders the label in the error color.
|
|
12
|
+
* @remarks Use when the linked input has a validation error.
|
|
13
|
+
*/
|
|
8
14
|
error?: boolean
|
|
9
15
|
}
|
|
10
16
|
|
|
@@ -2,12 +2,12 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
2
2
|
|
|
3
3
|
/** @beta */
|
|
4
4
|
export interface LinkProps extends React.ComponentProps<'a'> {
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Sets the link to `target="_blank"` and `rel="noopener noreferrer"`.
|
|
7
|
+
*/
|
|
6
8
|
openInNewTab?: boolean
|
|
7
9
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @defaultValue true
|
|
10
|
+
* Underlines the link.
|
|
11
11
|
*/
|
|
12
12
|
underlined?: boolean
|
|
13
13
|
}
|
|
@@ -11,7 +11,9 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
11
11
|
|
|
12
12
|
/** @beta */
|
|
13
13
|
export interface ListProps<T extends ListTag = 'ul'> extends Pick<GapProps, 'gap'> {
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* HTML element to render.
|
|
16
|
+
*/
|
|
15
17
|
as?: T
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -24,11 +26,17 @@ export const listProps: Record<string, PropDef> = {
|
|
|
24
26
|
|
|
25
27
|
/** @beta */
|
|
26
28
|
export interface ListItemProps extends React.ComponentProps<'li'> {
|
|
27
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Sets horizontal padding, gap, and minimum row height together.
|
|
31
|
+
*/
|
|
28
32
|
density?: Responsive<Density>
|
|
29
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Slot at the left edge of the row.
|
|
35
|
+
*/
|
|
30
36
|
start?: React.ReactNode
|
|
31
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Slot at the right edge of the row.
|
|
39
|
+
*/
|
|
32
40
|
end?: React.ReactNode
|
|
33
41
|
}
|
|
34
42
|
|
|
@@ -73,9 +81,13 @@ export const listItemProps: Record<string, PropDef> = {
|
|
|
73
81
|
|
|
74
82
|
/** @beta */
|
|
75
83
|
export interface ListButtonItemProps<T extends React.ElementType = 'button'> extends ListItemProps {
|
|
76
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Element or component to render for the press target.
|
|
86
|
+
*/
|
|
77
87
|
as?: InteractiveAs<T>
|
|
78
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Marks the row as the current selection.
|
|
90
|
+
*/
|
|
79
91
|
selected?: boolean
|
|
80
92
|
}
|
|
81
93
|
|
|
@@ -91,11 +103,17 @@ export const listButtonItemProps: Record<string, PropDef> = {
|
|
|
91
103
|
|
|
92
104
|
/** @beta */
|
|
93
105
|
export interface ListItemTextProps<T extends React.ElementType = 'div'> {
|
|
94
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* HTML element to render.
|
|
108
|
+
*/
|
|
95
109
|
as?: T
|
|
96
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Main label.
|
|
112
|
+
*/
|
|
97
113
|
title?: React.ReactNode
|
|
98
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* Text below the title.
|
|
116
|
+
*/
|
|
99
117
|
subtitle?: React.ReactNode
|
|
100
118
|
}
|
|
101
119
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
import {Activity, cloneElement, useId, useState, type ToggleEvent} from 'react'
|
|
3
|
+
|
|
4
|
+
import {useIsClient} from '../../hooks/useIsClient'
|
|
5
|
+
import {getProps} from '../../utils/getProps'
|
|
6
|
+
import {mergeTriggerProps} from '../../utils/mergeTriggerProps'
|
|
7
|
+
import {renderPortal} from '../../utils/renderPortal'
|
|
8
|
+
import {suffixClassName} from '../../utils/suffixClassName'
|
|
9
|
+
import {type PopoverProps, popoverProps} from './popover.props'
|
|
10
|
+
|
|
11
|
+
const popoverClassName = suffixClassName('sui-PopoverContent')
|
|
12
|
+
|
|
13
|
+
function PopoverRoot({
|
|
14
|
+
placement = 'bottom',
|
|
15
|
+
...props
|
|
16
|
+
}: PopoverProps & {
|
|
17
|
+
triggerProps?: Record<string, unknown>
|
|
18
|
+
}) {
|
|
19
|
+
const {
|
|
20
|
+
children,
|
|
21
|
+
className,
|
|
22
|
+
style,
|
|
23
|
+
id: idProp,
|
|
24
|
+
anchorName,
|
|
25
|
+
content,
|
|
26
|
+
portal,
|
|
27
|
+
triggerProps: forwardedTriggerProps,
|
|
28
|
+
...rest
|
|
29
|
+
} = getProps({placement, ...props}, popoverProps)
|
|
30
|
+
const reactId = useId()
|
|
31
|
+
const id = idProp || reactId
|
|
32
|
+
const [open, setOpen] = useState(false)
|
|
33
|
+
const isClient = useIsClient()
|
|
34
|
+
|
|
35
|
+
const handleToggle = (e: ToggleEvent) => {
|
|
36
|
+
setOpen(e.newState === 'open')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const triggerProps = {
|
|
40
|
+
popoverTarget: id,
|
|
41
|
+
style: {anchorName: `--anchor-${anchorName || id}`},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const trigger = children.type.forwardsTriggerProps
|
|
45
|
+
? cloneElement(children, {triggerProps})
|
|
46
|
+
: cloneElement(children, mergeTriggerProps(children.props, forwardedTriggerProps, triggerProps))
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
{trigger}
|
|
51
|
+
|
|
52
|
+
{renderPortal(
|
|
53
|
+
<Activity mode={open ? 'visible' : 'hidden'}>
|
|
54
|
+
<div
|
|
55
|
+
className={clsx(
|
|
56
|
+
popoverClassName,
|
|
57
|
+
'sui-px2 sui-py1 sui-radius2 sui-position-fixed sui-shadow2',
|
|
58
|
+
className,
|
|
59
|
+
)}
|
|
60
|
+
style={{
|
|
61
|
+
...style,
|
|
62
|
+
positionAnchor: `--anchor-${anchorName || id}`,
|
|
63
|
+
}}
|
|
64
|
+
data-ui="Popover"
|
|
65
|
+
popover="auto"
|
|
66
|
+
id={id}
|
|
67
|
+
onToggle={handleToggle}
|
|
68
|
+
{...rest}
|
|
69
|
+
>
|
|
70
|
+
{content}
|
|
71
|
+
</div>
|
|
72
|
+
</Activity>,
|
|
73
|
+
isClient,
|
|
74
|
+
portal,
|
|
75
|
+
)}
|
|
76
|
+
</>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** @beta */
|
|
81
|
+
export const Popover = Object.assign(PopoverRoot, {
|
|
82
|
+
forwardsTriggerProps: true,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
export type {PopoverProps}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.sui-PopoverContent {
|
|
2
|
+
visibility: hidden;
|
|
3
|
+
opacity: 0;
|
|
4
|
+
scale: 0.9;
|
|
5
|
+
transition: none;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.sui-PopoverContent:popover-open {
|
|
9
|
+
visibility: visible;
|
|
10
|
+
opacity: 1;
|
|
11
|
+
scale: 1;
|
|
12
|
+
transition: var(--fade-in-transition);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@starting-style {
|
|
16
|
+
.sui-PopoverContent:popover-open {
|
|
17
|
+
visibility: hidden;
|
|
18
|
+
opacity: 0;
|
|
19
|
+
scale: 0.9;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {type PlacementProps, placementProps} from '../../props/placement'
|
|
2
|
+
import {type PropDef} from '../../types/PropDef'
|
|
3
|
+
|
|
4
|
+
/** @beta */
|
|
5
|
+
export interface PopoverProps
|
|
6
|
+
extends Omit<React.ComponentProps<'div'>, 'children' | 'content'>, PlacementProps {
|
|
7
|
+
/**
|
|
8
|
+
* Shared anchor identifier.
|
|
9
|
+
* @remarks Set the same value on a Tooltip and a Popover to point both at one trigger.
|
|
10
|
+
*/
|
|
11
|
+
anchorName?: React.ReactNode
|
|
12
|
+
/**
|
|
13
|
+
* The trigger element.
|
|
14
|
+
* @remarks Popover clones it and attaches the `popovertarget` attribute that opens the floating layer.
|
|
15
|
+
*/
|
|
16
|
+
children: React.ReactElement<Record<string, unknown>>
|
|
17
|
+
/**
|
|
18
|
+
* The floating content.
|
|
19
|
+
*/
|
|
20
|
+
content?: React.ReactNode
|
|
21
|
+
/**
|
|
22
|
+
* Renders the content into `document.body` through a React portal instead of inline.
|
|
23
|
+
*/
|
|
24
|
+
portal?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const popoverProps: Record<string, PropDef> = {
|
|
28
|
+
anchorName: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
},
|
|
31
|
+
content: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
},
|
|
34
|
+
portal: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
},
|
|
37
|
+
...placementProps,
|
|
38
|
+
}
|
|
@@ -3,9 +3,14 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
3
3
|
|
|
4
4
|
/** @beta */
|
|
5
5
|
export interface RadioProps extends React.ComponentProps<'input'>, MarginProps {
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Applies error styling to the radio mark.
|
|
8
|
+
* @remarks Use when form validation fails. Pair with a visible error message. The prop is visual only.
|
|
9
|
+
*/
|
|
7
10
|
error?: boolean
|
|
8
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Visible label next to the radio.
|
|
13
|
+
*/
|
|
9
14
|
label: React.ReactNode
|
|
10
15
|
}
|
|
11
16
|
|
|
@@ -3,12 +3,18 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
3
3
|
/** @beta */
|
|
4
4
|
export interface SkipToContentProps extends React.ComponentProps<'a'> {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Same-page fragment to jump to.
|
|
7
|
+
* @remarks Note: The hash character # is required for proper linking.
|
|
7
8
|
*/
|
|
8
9
|
href: string
|
|
9
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Visible and announced text.
|
|
12
|
+
*/
|
|
10
13
|
label: string
|
|
11
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Not accepted.
|
|
16
|
+
* @remarks Pass text via `label`.
|
|
17
|
+
*/
|
|
12
18
|
children?: never
|
|
13
19
|
}
|
|
14
20
|
|
|
@@ -3,9 +3,14 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
3
3
|
|
|
4
4
|
/** @beta */
|
|
5
5
|
export interface SwitchProps extends React.ComponentProps<'input'>, MarginProps {
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Applies error styling to the switch track.
|
|
8
|
+
* @remarks Use when form validation fails. Pair with a visible error message. The prop is visual only.
|
|
9
|
+
*/
|
|
7
10
|
error?: boolean
|
|
8
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Visible label next to the switch.
|
|
13
|
+
*/
|
|
9
14
|
label: React.ReactNode
|
|
10
15
|
}
|
|
11
16
|
|
|
@@ -40,7 +40,7 @@ export function Text<T extends ElementType = 'span'>({
|
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
42
|
<Component
|
|
43
|
-
className={clsx(textClassName, 'sui-overflow-hidden', className)}
|
|
43
|
+
className={clsx(textClassName, props.truncate && 'sui-overflow-hidden', className)}
|
|
44
44
|
style={style}
|
|
45
45
|
data-ui="Text"
|
|
46
46
|
{...rest}
|
|
@@ -5,9 +5,13 @@ import {TEXT_SIZE, type TextSize} from '../../types/Text'
|
|
|
5
5
|
|
|
6
6
|
/** @public */
|
|
7
7
|
export interface TextProps<T extends React.ElementType = 'span'> extends TypographyProps {
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* HTML element to render.
|
|
10
|
+
*/
|
|
9
11
|
as?: T
|
|
10
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Sets font size.
|
|
14
|
+
*/
|
|
11
15
|
size?: Responsive<TextSize>
|
|
12
16
|
}
|
|
13
17
|
|
|
@@ -1,93 +1,97 @@
|
|
|
1
1
|
import clsx from 'clsx'
|
|
2
|
-
import {cloneElement,
|
|
2
|
+
import {cloneElement, useId, useState, type ToggleEvent} from 'react'
|
|
3
3
|
|
|
4
|
+
import {useIsClient} from '../../hooks/useIsClient'
|
|
4
5
|
import {getProps} from '../../utils/getProps'
|
|
6
|
+
import {mergeTriggerProps} from '../../utils/mergeTriggerProps'
|
|
7
|
+
import {renderPortal} from '../../utils/renderPortal'
|
|
5
8
|
import {suffixClassName} from '../../utils/suffixClassName'
|
|
6
|
-
import {Box} from '../box/Box'
|
|
7
9
|
import {type TooltipProps, tooltipProps} from './tooltip.props'
|
|
8
10
|
|
|
9
11
|
const tooltipClassName = suffixClassName('sui-Tooltip')
|
|
10
|
-
const tooltipDismissedClassName = suffixClassName('sui-Tooltip-Dismissed')
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
function TooltipRoot({
|
|
14
|
+
placement = 'bottom',
|
|
15
|
+
...props
|
|
16
|
+
}: TooltipProps & {
|
|
17
|
+
triggerProps?: Record<string, unknown>
|
|
18
|
+
}) {
|
|
14
19
|
const {
|
|
15
20
|
children,
|
|
16
21
|
className,
|
|
17
22
|
style,
|
|
18
|
-
disabled,
|
|
19
23
|
id: idProp,
|
|
20
|
-
|
|
24
|
+
anchorName,
|
|
25
|
+
content,
|
|
26
|
+
portal,
|
|
27
|
+
triggerProps: forwardedTriggerProps,
|
|
21
28
|
...rest
|
|
22
29
|
} = getProps({placement, ...props}, tooltipProps)
|
|
23
30
|
const reactId = useId()
|
|
24
31
|
const id = idProp || reactId
|
|
25
32
|
const [dismissed, setDismissed] = useState(false)
|
|
33
|
+
const isClient = useIsClient()
|
|
26
34
|
|
|
27
|
-
|
|
28
|
-
if (dismissed) {
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const handleKeyDown = (e: KeyboardEvent) => {
|
|
33
|
-
if (e.key === 'Escape') {
|
|
34
|
-
setDismissed(true)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
window.addEventListener('keydown', handleKeyDown)
|
|
39
|
-
return () => window.removeEventListener('keydown', handleKeyDown)
|
|
40
|
-
}, [dismissed])
|
|
41
|
-
|
|
42
|
-
const trigger = cloneElement(children, {
|
|
35
|
+
const triggerProps = {
|
|
43
36
|
'aria-describedby': id,
|
|
44
|
-
'
|
|
37
|
+
'interestfor': id,
|
|
38
|
+
'style': {anchorName: `--anchor-${anchorName || id}`},
|
|
39
|
+
'onMouseLeave': () => {
|
|
45
40
|
setDismissed(false)
|
|
46
|
-
children.props.onMouseEnter?.(e)
|
|
47
41
|
},
|
|
48
|
-
'
|
|
42
|
+
'onBlur': () => {
|
|
49
43
|
setDismissed(false)
|
|
50
|
-
children.props.onFocus?.(e)
|
|
51
44
|
},
|
|
52
|
-
'onClick': (
|
|
45
|
+
'onClick': () => {
|
|
53
46
|
setDismissed(true)
|
|
54
|
-
|
|
47
|
+
document.getElementById(id)?.hidePopover()
|
|
55
48
|
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const trigger = children.type.forwardsTriggerProps
|
|
52
|
+
? cloneElement(children, {triggerProps})
|
|
53
|
+
: cloneElement(children, mergeTriggerProps(children.props, forwardedTriggerProps, triggerProps))
|
|
61
54
|
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
const handleBeforeToggle = (e: ToggleEvent) => {
|
|
56
|
+
if (e.newState === 'open' && dismissed) {
|
|
57
|
+
e.preventDefault()
|
|
58
|
+
}
|
|
64
59
|
}
|
|
65
60
|
|
|
66
61
|
return (
|
|
67
62
|
<>
|
|
68
63
|
{trigger}
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
65
|
+
{renderPortal(
|
|
66
|
+
<div
|
|
67
|
+
className={clsx(
|
|
68
|
+
tooltipClassName,
|
|
69
|
+
'sui-px2 sui-py1 sui-radius2 sui-position-fixed sui-shadow2',
|
|
70
|
+
className,
|
|
71
|
+
)}
|
|
72
|
+
style={{
|
|
73
|
+
...style,
|
|
74
|
+
positionAnchor: `--anchor-${anchorName || id}`,
|
|
75
|
+
}}
|
|
76
|
+
data-ui="Tooltip"
|
|
77
|
+
role="tooltip"
|
|
78
|
+
popover="hint"
|
|
79
|
+
id={id}
|
|
80
|
+
onBeforeToggle={handleBeforeToggle}
|
|
81
|
+
{...rest}
|
|
82
|
+
>
|
|
83
|
+
{content}
|
|
84
|
+
</div>,
|
|
85
|
+
isClient,
|
|
86
|
+
portal,
|
|
87
|
+
)}
|
|
89
88
|
</>
|
|
90
89
|
)
|
|
91
90
|
}
|
|
92
91
|
|
|
92
|
+
/** @beta */
|
|
93
|
+
export const Tooltip = Object.assign(TooltipRoot, {
|
|
94
|
+
forwardsTriggerProps: true,
|
|
95
|
+
}) as typeof TooltipRoot
|
|
96
|
+
|
|
93
97
|
export type {TooltipProps}
|