@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
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.10",
|
|
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",
|
|
@@ -46,9 +49,11 @@
|
|
|
46
49
|
},
|
|
47
50
|
"dependencies": {
|
|
48
51
|
"@sanity-labs/design-tokens": "0.0.2-alpha.4",
|
|
49
|
-
"@sanity/icons": "^
|
|
52
|
+
"@sanity/icons": "^5.2.1",
|
|
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",
|
package/src/cli/install.ts
CHANGED
|
@@ -36,7 +36,8 @@ function exactFromRange(range: string | undefined): string | null {
|
|
|
36
36
|
*
|
|
37
37
|
* Icons is already a dependency, so the package manager installs it either way.
|
|
38
38
|
* It is listed explicitly because apps import icons directly to pass to
|
|
39
|
-
* components (
|
|
39
|
+
* components (`import {AddIcon} from '@sanity/icons/Add'`, then
|
|
40
|
+
* `<Button iconStart={AddIcon} />`), and under pnpm an import that
|
|
40
41
|
* isn't in the app's own package.json doesn't resolve. Dependencies the app
|
|
41
42
|
* never imports itself (react-refractor, clsx) are left to the package manager.
|
|
42
43
|
* React/react-dom are peers that belong to the host app, so they are left out
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
import {isValidElement, type ComponentPropsWithRef, type ElementType} from 'react'
|
|
3
|
+
|
|
4
|
+
import {getProps} from '../../utils/getProps'
|
|
5
|
+
import {suffixClassName} from '../../utils/suffixClassName'
|
|
6
|
+
import {Icon} from '../icon/Icon'
|
|
7
|
+
import {Text} from '../text/Text'
|
|
8
|
+
import {type BadgeProps, badgeProps} from './badge.props'
|
|
9
|
+
|
|
10
|
+
const badgeClassName = suffixClassName('sui-Badge')
|
|
11
|
+
|
|
12
|
+
/** @public */
|
|
13
|
+
export function Badge<T extends ElementType = 'span'>({
|
|
14
|
+
tone = 'neutral',
|
|
15
|
+
...props
|
|
16
|
+
}: BadgeProps<T> & Omit<ComponentPropsWithRef<T>, keyof BadgeProps<T>>) {
|
|
17
|
+
const {
|
|
18
|
+
as,
|
|
19
|
+
children,
|
|
20
|
+
className,
|
|
21
|
+
style,
|
|
22
|
+
iconStart: IconStart,
|
|
23
|
+
text,
|
|
24
|
+
...rest
|
|
25
|
+
} = getProps({tone, ...props}, badgeProps)
|
|
26
|
+
const Component = as || 'span'
|
|
27
|
+
|
|
28
|
+
const paddingClasses = IconStart ? 'sui-p1' : 'sui-px2 sui-py1'
|
|
29
|
+
|
|
30
|
+
const badgeClasses = clsx(
|
|
31
|
+
badgeClassName,
|
|
32
|
+
`${paddingClasses} sui-radius-full sui-display-inline-flex sui-align-items-center sui-gap1`,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Component className={clsx(badgeClasses, className)} style={style} data-ui="Badge" {...rest}>
|
|
37
|
+
{IconStart &&
|
|
38
|
+
(isValidElement(IconStart) ? (
|
|
39
|
+
IconStart
|
|
40
|
+
) : (
|
|
41
|
+
<Icon icon={IconStart} marginX={-1} marginY={-2} size={0} />
|
|
42
|
+
))}
|
|
43
|
+
{text && (
|
|
44
|
+
<Text size={1} trim>
|
|
45
|
+
{text}
|
|
46
|
+
</Text>
|
|
47
|
+
)}
|
|
48
|
+
{children}
|
|
49
|
+
</Component>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type {BadgeProps}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {type ToneProps, toneProps} from '../../props/tone'
|
|
2
|
+
import {type PropDef} from '../../types/PropDef'
|
|
3
|
+
|
|
4
|
+
/** @public */
|
|
5
|
+
export interface BadgeProps<T extends React.ElementType = 'span'> extends ToneProps {
|
|
6
|
+
/**
|
|
7
|
+
* HTML element to render.
|
|
8
|
+
*/
|
|
9
|
+
as?: T
|
|
10
|
+
/**
|
|
11
|
+
* Shows an icon in the start position (left side in left-to-right languages).
|
|
12
|
+
*/
|
|
13
|
+
iconStart?: React.ElementType | React.ReactNode
|
|
14
|
+
/**
|
|
15
|
+
* Sets Badge's text label. Use instead of children unless you have a special use case.
|
|
16
|
+
*/
|
|
17
|
+
text?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const badgeProps: Record<string, PropDef> = {
|
|
21
|
+
as: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
},
|
|
24
|
+
iconStart: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
},
|
|
27
|
+
text: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
},
|
|
30
|
+
...toneProps,
|
|
31
|
+
}
|
|
@@ -10,9 +10,14 @@ const boxClassName = suffixClassName('sui-Box')
|
|
|
10
10
|
/** @public */
|
|
11
11
|
export function Box<T extends ElementType = 'div'>({
|
|
12
12
|
display = 'block',
|
|
13
|
+
minHeight = '0',
|
|
14
|
+
minWidth = '0',
|
|
13
15
|
...props
|
|
14
16
|
}: BoxProps<T> & Omit<ComponentPropsWithRef<T>, keyof BoxProps<T>>) {
|
|
15
|
-
const {as, children, className, style, ...rest} = getProps(
|
|
17
|
+
const {as, children, className, style, ...rest} = getProps(
|
|
18
|
+
{display, minHeight, minWidth, ...props},
|
|
19
|
+
boxProps,
|
|
20
|
+
)
|
|
16
21
|
const Component = as || 'div'
|
|
17
22
|
|
|
18
23
|
return (
|
|
@@ -5,9 +5,14 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
5
5
|
|
|
6
6
|
/** @public */
|
|
7
7
|
export interface BoxProps<T extends React.ElementType = 'div'> extends LayoutProps {
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* HTML element or component to render.
|
|
10
|
+
*/
|
|
9
11
|
as?: T
|
|
10
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* CSS `display` property.
|
|
14
|
+
* @remarks Does not include `'flex'` or `'grid'`. Use the Flex or Grid components for those.
|
|
15
|
+
*/
|
|
11
16
|
display?: Responsive<DisplayBlock>
|
|
12
17
|
}
|
|
13
18
|
|
|
@@ -16,23 +16,41 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
16
16
|
|
|
17
17
|
/** @public */
|
|
18
18
|
export interface ButtonProps<T extends React.ElementType = 'button'> {
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* HTML element to render.
|
|
21
|
+
*/
|
|
20
22
|
as?: InteractiveAs<T>
|
|
21
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Sets padding and gap together to specify size.
|
|
25
|
+
*/
|
|
22
26
|
density?: ButtonDensity
|
|
23
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Sets Button to fill the width of its container.
|
|
29
|
+
*/
|
|
24
30
|
fullWidth?: Responsive<boolean>
|
|
25
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Shows an icon in the start position (left side in left-to-right languages).
|
|
33
|
+
*/
|
|
26
34
|
iconStart?: React.ElementType | React.ReactNode
|
|
27
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Shows an icon in the end position (right side in left-to-right languages).
|
|
37
|
+
*/
|
|
28
38
|
iconEnd?: React.ElementType | React.ReactNode
|
|
29
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Sets the importance of Button's action.
|
|
41
|
+
*/
|
|
30
42
|
level?: ButtonLevel
|
|
31
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Places Button into a loading state.
|
|
45
|
+
*/
|
|
32
46
|
loading?: boolean
|
|
33
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Sets Button's text label.
|
|
49
|
+
*/
|
|
34
50
|
text?: string
|
|
35
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Sets Button's semantic meaning and related color.
|
|
53
|
+
*/
|
|
36
54
|
tone?: ButtonTone
|
|
37
55
|
}
|
|
38
56
|
|
|
@@ -9,9 +9,13 @@ import {type Responsive} from '../../types/Responsive'
|
|
|
9
9
|
|
|
10
10
|
/** @public */
|
|
11
11
|
export interface CardProps<T extends React.ElementType = 'div'> extends MarginProps, ToneProps {
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* HTML element or component to render.
|
|
14
|
+
*/
|
|
13
15
|
as?: T
|
|
14
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Sets padding, gap, and border-radius together to specify size. This prop is used in place of separate `padding`, `gap`, and `border-radius` props.
|
|
18
|
+
*/
|
|
15
19
|
density?: Responsive<Density>
|
|
16
20
|
}
|
|
17
21
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {CheckmarkIcon
|
|
1
|
+
import {CheckmarkIcon} from '@sanity/icons/Checkmark'
|
|
2
|
+
import {RemoveIcon} from '@sanity/icons/Remove'
|
|
2
3
|
import clsx from 'clsx'
|
|
3
4
|
import {useEffect, useRef} from 'react'
|
|
4
5
|
|
|
@@ -29,7 +30,7 @@ export function Checkbox(props: CheckboxProps) {
|
|
|
29
30
|
|
|
30
31
|
return (
|
|
31
32
|
<Label
|
|
32
|
-
className={clsx(checkboxClassName, className)}
|
|
33
|
+
className={clsx(checkboxClassName, 'sui-position-relative', className)}
|
|
33
34
|
style={style}
|
|
34
35
|
data-ui="Checkbox"
|
|
35
36
|
disabled={disabled}
|
|
@@ -3,11 +3,19 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
3
3
|
|
|
4
4
|
/** @beta */
|
|
5
5
|
export interface CheckboxProps extends React.ComponentProps<'input'>, MarginProps {
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Applies error styling to the checkbox 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
|
+
* Renders the "mixed state" mark (a horizontal line).
|
|
13
|
+
* @remarks Use when the checkbox controls a group with some-but-not-all children checked.
|
|
14
|
+
*/
|
|
9
15
|
indeterminate?: boolean
|
|
10
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Visible label next to the checkbox.
|
|
18
|
+
*/
|
|
11
19
|
label: React.ReactNode
|
|
12
20
|
}
|
|
13
21
|
|
|
@@ -9,11 +9,18 @@ export interface CodeProps<T extends CodeTag = 'pre'> extends Omit<
|
|
|
9
9
|
TypographyProps,
|
|
10
10
|
'align' | 'truncate' | 'tone'
|
|
11
11
|
> {
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* HTML element or component to render.
|
|
14
|
+
* @remarks The defined element wraps an inner `<code>` tag.
|
|
15
|
+
*/
|
|
13
16
|
as?: T
|
|
14
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Refractor language for syntax highlighting.
|
|
19
|
+
*/
|
|
15
20
|
language?: string
|
|
16
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Sets font size.
|
|
23
|
+
*/
|
|
17
24
|
size?: Responsive<CodeSize>
|
|
18
25
|
}
|
|
19
26
|
|
|
@@ -5,9 +5,13 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
5
5
|
|
|
6
6
|
/** @public */
|
|
7
7
|
export interface ContainerProps<T extends React.ElementType = 'div'> extends LayoutProps {
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* HTML element or component to render.
|
|
10
|
+
*/
|
|
9
11
|
as?: T
|
|
10
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Maximum width of the container.
|
|
14
|
+
*/
|
|
11
15
|
size?: Responsive<ContainerSize>
|
|
12
16
|
}
|
|
13
17
|
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {CloseIcon} from '@sanity/icons/Close'
|
|
2
|
+
import clsx from 'clsx'
|
|
3
|
+
import {type ComponentProps, useEffect, useId, useRef} from 'react'
|
|
4
|
+
|
|
5
|
+
import {getProps} from '../../utils/getProps'
|
|
6
|
+
import {suffixClassName} from '../../utils/suffixClassName'
|
|
7
|
+
import {Box} from '../box/Box'
|
|
8
|
+
import {Flex} from '../flex/Flex'
|
|
9
|
+
import {Heading} from '../heading/Heading'
|
|
10
|
+
import {IconButton} from '../icon-button/IconButton'
|
|
11
|
+
import {type DialogProps, dialogProps} from './dialog.props'
|
|
12
|
+
|
|
13
|
+
const dialogClassName = suffixClassName('sui-Dialog')
|
|
14
|
+
const dialogContentClassName = suffixClassName('sui-DialogContent')
|
|
15
|
+
const dialogFooterClassName = suffixClassName('sui-DialogFooter')
|
|
16
|
+
|
|
17
|
+
function DialogRoot({open = false, size = 0, ...props}: DialogProps) {
|
|
18
|
+
const {children, className, style, header, onClose, ...rest} = getProps(
|
|
19
|
+
{size, ...props},
|
|
20
|
+
dialogProps,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const dialogRef = useRef<HTMLDialogElement>(null)
|
|
24
|
+
const headerId = useId()
|
|
25
|
+
|
|
26
|
+
const dialogClasses = clsx(
|
|
27
|
+
dialogClassName,
|
|
28
|
+
`${props.topAnchor ? 'sui-mx-auto' : 'sui-m-auto'} sui-radius5 sui-shadow3 sui-p4 sui-flex-direction-column sui-gap4 sui-overflow-y-auto`,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const dialogElement = dialogRef.current
|
|
33
|
+
if (!dialogElement) return
|
|
34
|
+
|
|
35
|
+
// `open` = `open` prop
|
|
36
|
+
// dialogElement.open = the `open` attribute on the HTML dialog element
|
|
37
|
+
// The prop and the attribute are manipulated independently, thus they
|
|
38
|
+
// can get out of sync if not handled within this effect.
|
|
39
|
+
if (open) {
|
|
40
|
+
if (!dialogElement.open) {
|
|
41
|
+
dialogElement.showModal()
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
if (dialogElement.open) {
|
|
45
|
+
dialogElement.close()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Ensure the dialog is closed (and thus its `open` attribute updated)
|
|
50
|
+
// when the component is unmounted.
|
|
51
|
+
return () => {
|
|
52
|
+
dialogElement.close()
|
|
53
|
+
}
|
|
54
|
+
}, [open])
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<dialog
|
|
58
|
+
{...rest}
|
|
59
|
+
ref={dialogRef}
|
|
60
|
+
closedby="any"
|
|
61
|
+
aria-labelledby={header ? headerId : undefined}
|
|
62
|
+
onClose={onClose}
|
|
63
|
+
className={clsx(dialogClasses, className)}
|
|
64
|
+
style={style}
|
|
65
|
+
data-ui="Dialog"
|
|
66
|
+
>
|
|
67
|
+
<Flex flexShrink={0} justifyContent="space-between" alignItems="center">
|
|
68
|
+
{header ? (
|
|
69
|
+
<Heading trim size={1} id={headerId}>
|
|
70
|
+
{header}
|
|
71
|
+
</Heading>
|
|
72
|
+
) : (
|
|
73
|
+
<div />
|
|
74
|
+
)}
|
|
75
|
+
<IconButton
|
|
76
|
+
aria-label="Close"
|
|
77
|
+
level="tertiary"
|
|
78
|
+
icon={CloseIcon}
|
|
79
|
+
onClick={() => dialogRef.current?.close()}
|
|
80
|
+
/>
|
|
81
|
+
</Flex>
|
|
82
|
+
{children}
|
|
83
|
+
</dialog>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function DialogContent(props: ComponentProps<'div'>) {
|
|
88
|
+
const {children, className, style, ...rest} = getProps(props, {})
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<Box
|
|
92
|
+
{...rest}
|
|
93
|
+
className={clsx(dialogContentClassName, className)}
|
|
94
|
+
style={style}
|
|
95
|
+
data-ui="DialogContent"
|
|
96
|
+
flexGrow={1}
|
|
97
|
+
overflowY="auto"
|
|
98
|
+
>
|
|
99
|
+
{children}
|
|
100
|
+
</Box>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function DialogFooter(props: ComponentProps<'div'>) {
|
|
105
|
+
const {children, className, style, ...rest} = getProps(props, {})
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Box
|
|
109
|
+
{...rest}
|
|
110
|
+
className={clsx(dialogFooterClassName, className)}
|
|
111
|
+
style={style}
|
|
112
|
+
data-ui="DialogFooter"
|
|
113
|
+
flexShrink={0}
|
|
114
|
+
>
|
|
115
|
+
{children}
|
|
116
|
+
</Box>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
DialogRoot.displayName = 'Dialog'
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @beta
|
|
124
|
+
*
|
|
125
|
+
* Renders a modal dialog element — should not be used for nonmodal dialogs.
|
|
126
|
+
* Modal dialogs make their containing documents inert, and render on the topmost
|
|
127
|
+
* layer within that containing document; they must be dimissed before the containing
|
|
128
|
+
* document and its contents become unblocked. They also trap focus within the bounds
|
|
129
|
+
* of the dialog element and its descendants.
|
|
130
|
+
*
|
|
131
|
+
* For more on dialogs and modality, refer to the HTML specification for the dialog element:
|
|
132
|
+
* https://html.spec.whatwg.org/dev/interactive-elements.html#the-dialog-element
|
|
133
|
+
*/
|
|
134
|
+
export const Dialog = DialogRoot
|
|
135
|
+
|
|
136
|
+
DialogRoot.Content = DialogContent
|
|
137
|
+
DialogRoot.Footer = DialogFooter
|
|
138
|
+
|
|
139
|
+
export type {DialogProps}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
.sui-Dialog {
|
|
2
|
+
/* Computed width is moderated by Dialog's size prop, which controls max-width */
|
|
3
|
+
width: calc(100dvw - 2rem);
|
|
4
|
+
max-height: calc(100dvh - 2rem);
|
|
5
|
+
transition:
|
|
6
|
+
display 150ms allow-discrete,
|
|
7
|
+
overlay 150ms allow-discrete,
|
|
8
|
+
opacity 150ms var(--timing-linear),
|
|
9
|
+
scale 150ms var(--timing-curve);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.sui-Dialog:not([open]) {
|
|
13
|
+
opacity: 0;
|
|
14
|
+
scale: 0.9;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.sui-Dialog[open] {
|
|
18
|
+
display: flex;
|
|
19
|
+
opacity: 1;
|
|
20
|
+
scale: 1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.sui-Dialog::backdrop {
|
|
24
|
+
--dialog-backdrop: hsl(from var(--separator-high) h s l / var(--overlay-opacity));
|
|
25
|
+
background-color: var(--dialog-backdrop);
|
|
26
|
+
transition:
|
|
27
|
+
display 150ms allow-discrete,
|
|
28
|
+
overlay 150ms allow-discrete,
|
|
29
|
+
opacity 150ms var(--timing-linear);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.sui-Dialog:not([open])::backdrop {
|
|
33
|
+
opacity: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.sui-Dialog[open]::backdrop {
|
|
37
|
+
opacity: 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@starting-style {
|
|
41
|
+
.sui-Dialog[open] {
|
|
42
|
+
opacity: 0;
|
|
43
|
+
scale: 0.9;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.sui-Dialog[open]::backdrop {
|
|
47
|
+
opacity: 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {CONTAINER_SIZE, type ContainerSize} from '../../types/Container'
|
|
2
|
+
import {type PropDef} from '../../types/PropDef'
|
|
3
|
+
import type {Responsive} from '../../types/Responsive'
|
|
4
|
+
|
|
5
|
+
/** @beta */
|
|
6
|
+
export interface DialogProps extends Omit<React.ComponentProps<'dialog'>, 'open'> {
|
|
7
|
+
/**
|
|
8
|
+
* Text for the dialog heading.
|
|
9
|
+
* @remarks Recommended for accessibility. The component sets `aria-labelledby` to the heading when you give this prop.
|
|
10
|
+
*/
|
|
11
|
+
header?: string
|
|
12
|
+
/**
|
|
13
|
+
* A function to call alongside the native `close` event.
|
|
14
|
+
* @remarks Use it to set `open` back to `false`. It runs for every close cause: the Escape key, a backdrop click, the close button, or a programmatic close.
|
|
15
|
+
*/
|
|
16
|
+
onClose: React.ReactEventHandler<HTMLDialogElement>
|
|
17
|
+
/**
|
|
18
|
+
* Toggles the dialog's open state.
|
|
19
|
+
* @remarks `true` calls `showModal()` on the element. `false` calls `close()`.
|
|
20
|
+
*/
|
|
21
|
+
open?: boolean
|
|
22
|
+
/** Sets the max width of the dialog */
|
|
23
|
+
size?: Responsive<ContainerSize>
|
|
24
|
+
/** CSS length value used to set the Dialog’s distance from the top of the viewport; Dialog is centered if not provided */
|
|
25
|
+
topAnchor?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const dialogProps: Record<string, PropDef> = {
|
|
29
|
+
header: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
},
|
|
32
|
+
open: {
|
|
33
|
+
type: 'boolean',
|
|
34
|
+
},
|
|
35
|
+
size: {
|
|
36
|
+
type: 'union',
|
|
37
|
+
className: 'container',
|
|
38
|
+
values: CONTAINER_SIZE,
|
|
39
|
+
},
|
|
40
|
+
topAnchor: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
className: 'top-anchor',
|
|
43
|
+
variable: '--top-anchor',
|
|
44
|
+
},
|
|
45
|
+
}
|
|
@@ -41,7 +41,7 @@ export function Eyebrow<T extends ElementType = 'span'>({
|
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
43
|
<Component
|
|
44
|
-
className={clsx(eyebrowClassName, 'sui-overflow-hidden', className)}
|
|
44
|
+
className={clsx(eyebrowClassName, props.truncate && 'sui-overflow-hidden', className)}
|
|
45
45
|
style={style}
|
|
46
46
|
data-ui="Eyebrow"
|
|
47
47
|
{...rest}
|
|
@@ -5,9 +5,13 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
5
5
|
|
|
6
6
|
/** @public */
|
|
7
7
|
export interface EyebrowProps<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. Uses the same scale as Label.
|
|
14
|
+
*/
|
|
11
15
|
size?: Responsive<EyebrowSize>
|
|
12
16
|
}
|
|
13
17
|
|
|
@@ -10,9 +10,14 @@ const flexClassName = suffixClassName('sui-Flex')
|
|
|
10
10
|
/** @public */
|
|
11
11
|
export function Flex<T extends ElementType = 'div'>({
|
|
12
12
|
display = 'flex',
|
|
13
|
+
minHeight = '0',
|
|
14
|
+
minWidth = '0',
|
|
13
15
|
...props
|
|
14
16
|
}: FlexProps<T> & Omit<ComponentPropsWithRef<T>, keyof FlexProps<T>>) {
|
|
15
|
-
const {as, children, className, style, ...rest} = getProps(
|
|
17
|
+
const {as, children, className, style, ...rest} = getProps(
|
|
18
|
+
{display, minHeight, minWidth, ...props},
|
|
19
|
+
flexProps,
|
|
20
|
+
)
|
|
16
21
|
const Component = as || 'div'
|
|
17
22
|
|
|
18
23
|
return (
|
|
@@ -8,9 +8,13 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
8
8
|
/** @public */
|
|
9
9
|
export interface FlexProps<T extends React.ElementType = 'div'>
|
|
10
10
|
extends FlexParentProps, GapProps, LayoutProps {
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* HTML element or component to render.
|
|
13
|
+
*/
|
|
12
14
|
as?: T
|
|
13
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* CSS `display` property.
|
|
17
|
+
*/
|
|
14
18
|
display?: Responsive<DisplayFlex>
|
|
15
19
|
}
|
|
16
20
|
|
|
@@ -10,9 +10,14 @@ const gridClassName = suffixClassName('sui-Grid')
|
|
|
10
10
|
/** @public */
|
|
11
11
|
export function Grid<T extends ElementType = 'div'>({
|
|
12
12
|
display = 'grid',
|
|
13
|
+
minHeight = '0',
|
|
14
|
+
minWidth = '0',
|
|
13
15
|
...props
|
|
14
16
|
}: GridProps<T> & Omit<ComponentPropsWithRef<T>, keyof GridProps<T>>) {
|
|
15
|
-
const {as, children, className, style, ...rest} = getProps(
|
|
17
|
+
const {as, children, className, style, ...rest} = getProps(
|
|
18
|
+
{display, minHeight, minWidth, ...props},
|
|
19
|
+
gridProps,
|
|
20
|
+
)
|
|
16
21
|
const Component = as || 'div'
|
|
17
22
|
|
|
18
23
|
return (
|
|
@@ -8,9 +8,13 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
8
8
|
/** @public */
|
|
9
9
|
export interface GridProps<T extends React.ElementType = 'div'>
|
|
10
10
|
extends GridParentProps, GapProps, LayoutProps {
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* HTML element or component to render.
|
|
13
|
+
*/
|
|
12
14
|
as?: T
|
|
13
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* CSS `display` property.
|
|
17
|
+
*/
|
|
14
18
|
display?: Responsive<DisplayGrid>
|
|
15
19
|
}
|
|
16
20
|
|