@sanity/ui 5.0.0-alpha.5 → 5.0.0-alpha.6
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 +54 -9
- package/dist/index.js +197 -79
- 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 +8 -2
- package/src/components/checkbox/Checkbox.tsx +1 -1
- package/src/components/index.css +1 -0
- package/src/components/modal/Modal.tsx +5 -10
- package/src/components/modal/modal.css +3 -3
- package/src/components/modal/modal.props.ts +9 -0
- package/src/components/popover/Popover.tsx +85 -0
- package/src/components/popover/popover.css +21 -0
- package/src/components/popover/popover.props.ts +28 -0
- package/src/components/radio/Radio.tsx +1 -1
- package/src/components/switch/Switch.tsx +1 -1
- package/src/components/tooltip/Tooltip.tsx +59 -55
- package/src/components/tooltip/tooltip.css +19 -40
- package/src/components/tooltip/tooltip.props.ts +16 -8
- package/src/components/tooltip-group/TooltipGroup.tsx +2 -5
- package/src/css/classes/generic/placement.css +31 -0
- package/src/css/tokens/semantic.css +4 -0
- package/src/hooks/useIsClient.ts +10 -0
- package/src/index.ts +3 -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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/ui",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.6",
|
|
4
4
|
"description": "Sanity UI Library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"components",
|
|
@@ -30,11 +30,14 @@
|
|
|
30
30
|
],
|
|
31
31
|
"type": "module",
|
|
32
32
|
"sideEffects": [
|
|
33
|
-
"*.css"
|
|
33
|
+
"*.css",
|
|
34
|
+
"./src/polyfills.ts",
|
|
35
|
+
"./dist/polyfills.js"
|
|
34
36
|
],
|
|
35
37
|
"types": "./dist/index.d.ts",
|
|
36
38
|
"exports": {
|
|
37
39
|
".": "./dist/index.js",
|
|
40
|
+
"./polyfills": "./dist/polyfills.js",
|
|
38
41
|
"./package.json": "./package.json",
|
|
39
42
|
"./styles.css": {
|
|
40
43
|
"types": "./src/styles.css.d.ts",
|
|
@@ -49,6 +52,8 @@
|
|
|
49
52
|
"@sanity/icons": "^3.7.4",
|
|
50
53
|
"clsx": "^2.1.1",
|
|
51
54
|
"comment-json": "^5.0.0",
|
|
55
|
+
"dialog-closedby-polyfill": "^1.3.0",
|
|
56
|
+
"interestfor": "^1.0.8",
|
|
52
57
|
"react-refractor": "^4.0.0"
|
|
53
58
|
},
|
|
54
59
|
"devDependencies": {
|
|
@@ -59,6 +64,7 @@
|
|
|
59
64
|
"@types/postcss-import": "^14.0.3",
|
|
60
65
|
"@types/postcss-prefix-selector": "^1.16.3",
|
|
61
66
|
"@types/react": "^19.2.17",
|
|
67
|
+
"@types/react-dom": "^19.2.3",
|
|
62
68
|
"autoprefixer": "^10.4.27",
|
|
63
69
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
64
70
|
"concurrently": "^9.2.1",
|
|
@@ -29,7 +29,7 @@ export function Checkbox(props: CheckboxProps) {
|
|
|
29
29
|
|
|
30
30
|
return (
|
|
31
31
|
<Label
|
|
32
|
-
className={clsx(checkboxClassName, className)}
|
|
32
|
+
className={clsx(checkboxClassName, 'sui-position-relative', className)}
|
|
33
33
|
style={style}
|
|
34
34
|
data-ui="Checkbox"
|
|
35
35
|
disabled={disabled}
|
package/src/components/index.css
CHANGED
|
@@ -14,16 +14,11 @@ const modalClassName = suffixClassName('sui-Modal')
|
|
|
14
14
|
const modalContentClassName = suffixClassName('sui-ModalContent')
|
|
15
15
|
const modalFooterClassName = suffixClassName('sui-ModalFooter')
|
|
16
16
|
|
|
17
|
-
function ModalRoot(props: ModalProps) {
|
|
18
|
-
const {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
header,
|
|
23
|
-
open = false,
|
|
24
|
-
onClose,
|
|
25
|
-
...rest
|
|
26
|
-
} = getProps(props, modalProps)
|
|
17
|
+
function ModalRoot({open = false, size = 0, ...props}: ModalProps) {
|
|
18
|
+
const {children, className, style, header, onClose, ...rest} = getProps(
|
|
19
|
+
{size, ...props},
|
|
20
|
+
modalProps,
|
|
21
|
+
)
|
|
27
22
|
|
|
28
23
|
const modalRef = useRef<HTMLDialogElement>(null)
|
|
29
24
|
const headerId = useId()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.sui-Modal {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
max-height:
|
|
2
|
+
/* Computed width is moderated by Modal's size prop, which controls max-width */
|
|
3
|
+
width: calc(100dvw - 2rem);
|
|
4
|
+
max-height: calc(100dvh - 2rem);
|
|
5
5
|
transition:
|
|
6
6
|
display 150ms allow-discrete,
|
|
7
7
|
overlay 150ms allow-discrete,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import {CONTAINER_SIZE, type ContainerSize} from '../../types/Container'
|
|
1
2
|
import {type PropDef} from '../../types/PropDef'
|
|
3
|
+
import type {Responsive} from '../../types/Responsive'
|
|
2
4
|
|
|
3
5
|
/** @beta */
|
|
4
6
|
export interface ModalProps extends Omit<React.ComponentProps<'dialog'>, 'open'> {
|
|
@@ -8,6 +10,8 @@ export interface ModalProps extends Omit<React.ComponentProps<'dialog'>, 'open'>
|
|
|
8
10
|
onClose: React.ReactEventHandler<HTMLDialogElement>
|
|
9
11
|
/** Whether the modal is open; defaults to false. */
|
|
10
12
|
open?: boolean
|
|
13
|
+
/** Max width of the modal */
|
|
14
|
+
size?: Responsive<ContainerSize>
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
export const modalProps: Record<string, PropDef> = {
|
|
@@ -17,4 +21,9 @@ export const modalProps: Record<string, PropDef> = {
|
|
|
17
21
|
open: {
|
|
18
22
|
type: 'boolean',
|
|
19
23
|
},
|
|
24
|
+
size: {
|
|
25
|
+
type: 'union',
|
|
26
|
+
className: 'container',
|
|
27
|
+
values: CONTAINER_SIZE,
|
|
28
|
+
},
|
|
20
29
|
}
|
|
@@ -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,28 @@
|
|
|
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
|
+
/** Anchor name for positioning */
|
|
8
|
+
anchorName?: React.ReactNode
|
|
9
|
+
/** Focusable trigger element */
|
|
10
|
+
children: React.ReactElement<Record<string, unknown>>
|
|
11
|
+
/** Popover content */
|
|
12
|
+
content?: React.ReactNode
|
|
13
|
+
/** Render tooltip in portal */
|
|
14
|
+
portal?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const popoverProps: Record<string, PropDef> = {
|
|
18
|
+
anchorName: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
},
|
|
21
|
+
content: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
},
|
|
24
|
+
portal: {
|
|
25
|
+
type: 'boolean',
|
|
26
|
+
},
|
|
27
|
+
...placementProps,
|
|
28
|
+
}
|
|
@@ -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}
|
|
@@ -1,52 +1,31 @@
|
|
|
1
|
+
[interestfor] {
|
|
2
|
+
--tooltip-delay: var(--tooltip-delay-group, 500ms);
|
|
3
|
+
/* The interestfor polyfill looks for intereset delay variables */
|
|
4
|
+
--interest-delay-start: var(--tooltip-delay);
|
|
5
|
+
--interest-delay-end: 0ms;
|
|
6
|
+
interest-delay-start: var(--tooltip-delay);
|
|
7
|
+
interest-delay-end: 0ms;
|
|
8
|
+
}
|
|
9
|
+
|
|
1
10
|
.sui-Tooltip {
|
|
2
|
-
--tooltip-delay-open: var(--tooltip-delay-group, 500ms);
|
|
3
|
-
--tooltip-delay-close: var(--tooltip-delay-group, 100ms);
|
|
4
11
|
background: var(--backdrop);
|
|
12
|
+
visibility: hidden;
|
|
5
13
|
opacity: 0;
|
|
6
14
|
scale: 0.9;
|
|
7
|
-
|
|
8
|
-
transition:
|
|
9
|
-
opacity 200ms cubic-bezier(0.34, 1.2, 0.64, 1) var(--tooltip-delay-close),
|
|
10
|
-
scale 200ms cubic-bezier(0.34, 1.2, 0.64, 1) var(--tooltip-delay-close),
|
|
11
|
-
visibility 0s linear calc(var(--tooltip-delay-close) + 200ms);
|
|
15
|
+
transition: none;
|
|
12
16
|
}
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
.sui-Tooltip:popover-open {
|
|
15
19
|
visibility: visible;
|
|
16
20
|
opacity: 1;
|
|
17
21
|
scale: 1;
|
|
18
|
-
transition:
|
|
19
|
-
opacity 200ms cubic-bezier(0.34, 1.2, 0.64, 1) var(--tooltip-delay-open),
|
|
20
|
-
scale 200ms cubic-bezier(0.34, 1.2, 0.64, 1) var(--tooltip-delay-open),
|
|
21
|
-
visibility 0s linear var(--tooltip-delay-open);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
[aria-describedby]:is(:hover, :focus-visible) + .sui-Tooltip-Dismissed[role="tooltip"] {
|
|
25
|
-
transition: none;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.sui-Tooltip[class*='placement-bottom'],
|
|
29
|
-
.sui-Tooltip[class*='placement-top'] {
|
|
30
|
-
margin-block: var(--space-1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.sui-Tooltip[class*='placement-left'],
|
|
34
|
-
.sui-Tooltip[class*='placement-right'] {
|
|
35
|
-
margin-inline: var(--space-1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.sui-Tooltip[class*='bottom'] {
|
|
39
|
-
transform-origin: center top;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.sui-Tooltip[class*='top'] {
|
|
43
|
-
transform-origin: center bottom;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.sui-Tooltip[class*='left'] {
|
|
47
|
-
transform-origin: right center;
|
|
22
|
+
transition: var(--fade-in-transition);
|
|
48
23
|
}
|
|
49
24
|
|
|
50
|
-
|
|
51
|
-
|
|
25
|
+
@starting-style {
|
|
26
|
+
.sui-Tooltip:popover-open {
|
|
27
|
+
visibility: hidden;
|
|
28
|
+
opacity: 0;
|
|
29
|
+
scale: 0.9;
|
|
30
|
+
}
|
|
52
31
|
}
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import {type PlacementProps, placementProps} from '../../props/placement'
|
|
2
2
|
import {type PropDef} from '../../types/PropDef'
|
|
3
3
|
|
|
4
|
-
/** @
|
|
5
|
-
export interface TooltipProps
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
/** @beta */
|
|
5
|
+
export interface TooltipProps
|
|
6
|
+
extends Omit<React.ComponentProps<'div'>, 'children' | 'content'>, PlacementProps {
|
|
7
|
+
/** Anchor name for positioning */
|
|
8
|
+
anchorName?: React.ReactNode
|
|
9
|
+
/** Focusable trigger element */
|
|
10
|
+
children: React.ReactElement<Record<string, unknown>>
|
|
11
|
+
/** Tooltip content */
|
|
12
|
+
content?: React.ReactNode
|
|
13
|
+
/** Render tooltip in portal */
|
|
14
|
+
portal?: boolean
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
export const tooltipProps: Record<string, PropDef> = {
|
|
13
|
-
|
|
18
|
+
anchorName: {
|
|
14
19
|
type: 'string',
|
|
15
20
|
},
|
|
16
|
-
|
|
21
|
+
content: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
},
|
|
24
|
+
portal: {
|
|
17
25
|
type: 'boolean',
|
|
18
26
|
},
|
|
19
27
|
...placementProps,
|
|
@@ -23,10 +23,7 @@ export function TooltipGroup<T extends ElementType = 'div'>(
|
|
|
23
23
|
return
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
if (
|
|
27
|
-
e.propertyName === 'visibility' &&
|
|
28
|
-
window.getComputedStyle(tooltip).visibility !== 'hidden'
|
|
29
|
-
) {
|
|
26
|
+
if (e.propertyName === 'opacity' && window.getComputedStyle(tooltip).opacity !== '0') {
|
|
30
27
|
setIsActive(true)
|
|
31
28
|
}
|
|
32
29
|
}
|
|
@@ -49,7 +46,7 @@ export function TooltipGroup<T extends ElementType = 'div'>(
|
|
|
49
46
|
style={{
|
|
50
47
|
...style,
|
|
51
48
|
...(isActive && {
|
|
52
|
-
'--tooltip-delay-group':
|
|
49
|
+
'--tooltip-delay-group': '0ms',
|
|
53
50
|
}),
|
|
54
51
|
}}
|
|
55
52
|
data-ui="TooltipGroup"
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
[class*='placement-'] {
|
|
2
|
+
inset: auto;
|
|
3
|
+
margin: 0;
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
@position-try --top-center {
|
|
2
7
|
top: auto;
|
|
3
8
|
right: auto;
|
|
@@ -169,3 +174,29 @@
|
|
|
169
174
|
bottom: anchor(bottom);
|
|
170
175
|
position-try-fallbacks: --left-end;
|
|
171
176
|
}
|
|
177
|
+
|
|
178
|
+
[class*="placement-bottom"],
|
|
179
|
+
[class*="placement-top"] {
|
|
180
|
+
margin-block: var(--space-1);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
[class*="placement-left"],
|
|
184
|
+
[class*="placement-right"] {
|
|
185
|
+
margin-inline: var(--space-1);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
[class*="placement-bottom"] {
|
|
189
|
+
transform-origin: center top;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
[class*="placement-top"] {
|
|
193
|
+
transform-origin: center bottom;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
[class*="placement-left"] {
|
|
197
|
+
transform-origin: right center;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
[class*="placement-right"] {
|
|
201
|
+
transform-origin: left center;
|
|
202
|
+
}
|
|
@@ -18,4 +18,8 @@
|
|
|
18
18
|
--hover-transition: border-color 100ms linear, background-color 100ms linear;
|
|
19
19
|
--inert-background: light-dark(var(--gray-50), var(--gray-950));
|
|
20
20
|
--inert-foreground: light-dark(var(--gray-400), var(--gray-600));
|
|
21
|
+
--fade-in-transition:
|
|
22
|
+
opacity 200ms cubic-bezier(0.34, 1.2, 0.64, 1),
|
|
23
|
+
scale 200ms cubic-bezier(0.34, 1.2, 0.64, 1), visibility 0s linear,
|
|
24
|
+
display 200ms linear allow-discrete;
|
|
21
25
|
}
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ export * from './components/label/Label'
|
|
|
19
19
|
export * from './components/link/Link'
|
|
20
20
|
export * from './components/list/List'
|
|
21
21
|
export * from './components/modal/Modal'
|
|
22
|
+
export * from './components/popover/Popover'
|
|
22
23
|
export * from './components/press-area/PressArea'
|
|
23
24
|
export * from './components/radio/Radio'
|
|
24
25
|
export * from './components/skip-to-content/SkipToContent'
|
|
@@ -30,6 +31,8 @@ export * from './components/tooltip-group/TooltipGroup'
|
|
|
30
31
|
export * from './components/v-stack/VStack'
|
|
31
32
|
export * from './components/visually-hidden/VisuallyHidden'
|
|
32
33
|
|
|
34
|
+
export * from './hooks/useIsClient'
|
|
35
|
+
|
|
33
36
|
export type {FlexParentProps} from './props/flexParent'
|
|
34
37
|
export type {GapProps} from './props/gap'
|
|
35
38
|
export type {GridParentProps} from './props/gridParent'
|
package/src/polyfills.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
apply as applyClosedByPolyfill,
|
|
3
|
+
isSupported as isClosedBySupported,
|
|
4
|
+
} from 'dialog-closedby-polyfill'
|
|
5
|
+
|
|
6
|
+
if (
|
|
7
|
+
typeof HTMLButtonElement !== 'undefined' &&
|
|
8
|
+
!('interestForElement' in HTMLButtonElement.prototype)
|
|
9
|
+
) {
|
|
10
|
+
void import('interestfor')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (typeof HTMLDialogElement !== 'undefined' && !isClosedBySupported()) {
|
|
14
|
+
applyClosedByPolyfill()
|
|
15
|
+
}
|