@sanity/ui 5.0.0-alpha.1 → 5.0.0-alpha.11

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 (93) hide show
  1. package/README.md +1 -1
  2. package/dist/LazyRefractor-CQ22IeXB.js +19 -0
  3. package/dist/LazyRefractor-CQ22IeXB.js.map +1 -0
  4. package/dist/cli/install.js +2 -1
  5. package/dist/index.d.ts +662 -796
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +2081 -1758
  8. package/dist/index.js.map +1 -1
  9. package/dist/polyfills.d.ts +1 -0
  10. package/dist/polyfills.js +5 -0
  11. package/dist/polyfills.js.map +1 -0
  12. package/dist/styles.css +1 -1
  13. package/package.json +16 -9
  14. package/src/cli/install.ts +2 -1
  15. package/src/components/badge/Badge.tsx +53 -0
  16. package/src/components/badge/badge.css +6 -0
  17. package/src/components/badge/badge.props.ts +31 -0
  18. package/src/components/box/box.css +5 -0
  19. package/src/components/box/box.props.ts +7 -2
  20. package/src/components/button/button.props.ts +27 -9
  21. package/src/components/card/card.props.ts +6 -2
  22. package/src/components/checkbox/Checkbox.tsx +4 -3
  23. package/src/components/checkbox/checkbox.props.ts +11 -3
  24. package/src/components/code/code.props.ts +10 -3
  25. package/src/components/container/Container.tsx +5 -1
  26. package/src/components/container/container.css +4 -0
  27. package/src/components/container/container.props.ts +6 -2
  28. package/src/components/dialog/Dialog.tsx +139 -0
  29. package/src/components/dialog/dialog.css +49 -0
  30. package/src/components/dialog/dialog.props.ts +45 -0
  31. package/src/components/eyebrow/Eyebrow.tsx +1 -1
  32. package/src/components/eyebrow/eyebrow.props.ts +6 -2
  33. package/src/components/flex/flex.css +5 -0
  34. package/src/components/flex/flex.props.ts +6 -2
  35. package/src/components/grid/grid.css +5 -0
  36. package/src/components/grid/grid.props.ts +6 -2
  37. package/src/components/h-stack/HStack.tsx +8 -14
  38. package/src/components/h-stack/hStack.props.ts +5 -13
  39. package/src/components/heading/Heading.tsx +1 -1
  40. package/src/components/heading/heading.props.ts +8 -2
  41. package/src/components/icon/icon.props.ts +6 -2
  42. package/src/components/icon-button/iconButton.props.ts +7 -2
  43. package/src/components/index.css +4 -0
  44. package/src/components/indicator/indicator.props.ts +6 -2
  45. package/src/components/indicator-stack/indicatorStack.props.ts +3 -1
  46. package/src/components/inline/inline.props.ts +6 -2
  47. package/src/components/label/label.props.ts +7 -1
  48. package/src/components/link/link.props.ts +4 -4
  49. package/src/components/list/list.props.ts +27 -9
  50. package/src/components/popover/Popover.tsx +85 -0
  51. package/src/components/popover/popover.css +21 -0
  52. package/src/components/popover/popover.props.ts +38 -0
  53. package/src/components/press-area/pressArea.props.ts +3 -1
  54. package/src/components/radio/Radio.tsx +1 -1
  55. package/src/components/radio/radio.props.ts +7 -2
  56. package/src/components/select/Select.tsx +47 -0
  57. package/src/components/select/select.css +67 -0
  58. package/src/components/select/select.props.ts +25 -0
  59. package/src/components/skip-to-content/skipToContent.props.ts +9 -3
  60. package/src/components/spinner/Spinner.tsx +1 -1
  61. package/src/components/spinner/spinner.props.ts +3 -0
  62. package/src/components/switch/Switch.tsx +1 -1
  63. package/src/components/switch/switch.props.ts +7 -2
  64. package/src/components/text/Text.tsx +1 -1
  65. package/src/components/text/text.props.ts +6 -2
  66. package/src/components/tooltip/Tooltip.tsx +59 -55
  67. package/src/components/tooltip/tooltip.css +19 -40
  68. package/src/components/tooltip/tooltip.props.ts +24 -8
  69. package/src/components/tooltip-group/TooltipGroup.tsx +2 -5
  70. package/src/components/tooltip-group/tooltipGroup.props.ts +3 -1
  71. package/src/components/v-stack/VStack.tsx +8 -14
  72. package/src/components/v-stack/vStack.props.ts +5 -13
  73. package/src/components/visually-hidden/visuallyHidden.props.ts +6 -2
  74. package/src/css/classes/generic/display.css +8 -8
  75. package/src/css/classes/generic/placement.css +31 -0
  76. package/src/css/classes/local/animation.css +4 -0
  77. package/src/css/classes/local/index.css +2 -0
  78. package/src/css/classes/local/top-anchor.css +4 -0
  79. package/src/css/classes/system/shadow.css +6 -6
  80. package/src/css/global/reset.css +32 -19
  81. package/src/css/tokens/semantic.css +4 -0
  82. package/src/css/tokens/surface.css +2 -0
  83. package/src/hooks/useIsClient.ts +10 -0
  84. package/src/index.ts +8 -0
  85. package/src/polyfills.ts +17 -0
  86. package/src/props/formElement.ts +34 -0
  87. package/src/types/FormElement.ts +2 -0
  88. package/src/utils/getProps.ts +1 -1
  89. package/src/utils/mergeTriggerProps.ts +66 -0
  90. package/src/utils/renderPortal.tsx +14 -0
  91. package/src/version.ts +2 -2
  92. package/dist/_chunks-es/LazyRefractor.js +0 -20
  93. package/dist/_chunks-es/LazyRefractor.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ui",
3
- "version": "5.0.0-alpha.1",
3
+ "version": "5.0.0-alpha.11",
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",
@@ -45,31 +48,35 @@
45
48
  }
46
49
  },
47
50
  "dependencies": {
48
- "@sanity-labs/design-tokens": "0.0.2-alpha.4",
49
- "@sanity/icons": "^3.7.4",
51
+ "@sanity-labs/design-tokens": "0.0.2-alpha.5",
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": {
55
60
  "@sanity/browserslist-config": "^1.0.5",
56
- "@sanity/pkg-utils": "^10.8.2",
57
61
  "@sanity/tsconfig": "^1.0.0",
62
+ "@sanity/tsdown-config": "^0.28.0",
58
63
  "@types/node": "^25.5.2",
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
- "babel-plugin-react-compiler": "^1.0.0",
64
69
  "concurrently": "^9.2.1",
65
70
  "cssnano": "^8.0.0",
71
+ "oxc-transform-react": "^0.149.0",
66
72
  "postcss": "^8.5.14",
67
73
  "postcss-cli": "^11.0.1",
68
74
  "postcss-import": "^16.1.1",
69
75
  "postcss-prefix-selector": "^2.1.1",
70
76
  "postcss-selector-parser": "^7.1.1",
71
77
  "rimraf": "^6.1.3",
72
- "typescript": "^5.9.3",
78
+ "tsdown": "^0.23.0",
79
+ "typescript": "~6.0.2",
73
80
  "vitest": "^4.1.1"
74
81
  },
75
82
  "peerDependencies": {
@@ -83,11 +90,11 @@
83
90
  "build": "pnpm run sync-version && pnpm run clean && pnpm run build:css && pnpm run build:js && pnpm run build:cli",
84
91
  "build:cli": "tsc -p tsconfig.cli.json",
85
92
  "build:css": "NODE_ENV=production postcss src/index.css -o dist/styles.css",
86
- "build:js": "pkg build --strict --check",
93
+ "build:js": "tsdown",
87
94
  "clean": "rimraf dist",
88
95
  "dev": "concurrently -n css,js \"pnpm run dev:css\" \"pnpm run dev:js\"",
89
96
  "dev:css": "postcss src/index.css -o dist/styles.css --watch",
90
- "dev:js": "pkg watch",
97
+ "dev:js": "tsdown --watch",
91
98
  "sync-version": "node scripts/write-version.js",
92
99
  "test": "vitest run",
93
100
  "ts:check": "tsc --noEmit"
@@ -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 (`<Button iconStart={AddIcon} />`), and under pnpm an import that
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,6 @@
1
+ .sui-Badge {
2
+ --badge-background: var(--background-high);
3
+ --badge-color: var(--foreground-high);
4
+ background-color: var(--badge-background);
5
+ color: var(--badge-color);
6
+ }
@@ -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
+ }
@@ -1,3 +1,8 @@
1
+ .sui-Box {
2
+ min-width: 0;
3
+ min-height: 0;
4
+ }
5
+
1
6
  .sui-Box[class*='sui-tone'] {
2
7
  --box-background: var(--background-low);
3
8
  background-color: var(--box-background);
@@ -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
- /** Element to render */
8
+ /**
9
+ * HTML element or component to render.
10
+ */
9
11
  as?: T
10
- /** CSS **display** property */
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
- /** Element to render */
19
+ /**
20
+ * HTML element to render.
21
+ */
20
22
  as?: InteractiveAs<T>
21
- /** Composite prop for setting padding and gap */
23
+ /**
24
+ * Sets padding and gap together to specify size.
25
+ */
22
26
  density?: ButtonDensity
23
- /** Set CSS **width** property to 100% */
27
+ /**
28
+ * Sets Button to fill the width of its container.
29
+ */
24
30
  fullWidth?: Responsive<boolean>
25
- /** Starting icon */
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
- /** Ending icon */
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
- /** Button level */
39
+ /**
40
+ * Sets the importance of Button's action.
41
+ */
30
42
  level?: ButtonLevel
31
- /** Loading state */
43
+ /**
44
+ * Places Button into a loading state.
45
+ */
32
46
  loading?: boolean
33
- /** Button text */
47
+ /**
48
+ * Sets Button's text label.
49
+ */
34
50
  text?: string
35
- /** Button tone */
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
- /** Element to render */
12
+ /**
13
+ * HTML element or component to render.
14
+ */
13
15
  as?: T
14
- /** Composite prop for setting padding and border radius */
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, RemoveIcon} from '@sanity/icons'
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
 
@@ -25,11 +26,11 @@ export function Checkbox(props: CheckboxProps) {
25
26
  if (inputRef.current) {
26
27
  inputRef.current.indeterminate = indeterminate
27
28
  }
28
- }, [indeterminate])
29
+ }, [indeterminate, inputRef])
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
- /** Error state */
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
- /** Indeterminate state */
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
- /** Input label */
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
- /** Element to render */
12
+ /**
13
+ * HTML element or component to render.
14
+ * @remarks The defined element wraps an inner `<code>` tag.
15
+ */
13
16
  as?: T
14
- /** Refractor language for syntax highlighting */
17
+ /**
18
+ * Refractor language for syntax highlighting.
19
+ */
15
20
  language?: string
16
- /** CSS **font-size** property */
21
+ /**
22
+ * Sets font size.
23
+ */
17
24
  size?: Responsive<CodeSize>
18
25
  }
19
26
 
@@ -10,9 +10,13 @@ const containerClassName = suffixClassName('sui-Container')
10
10
  /** @public */
11
11
  export function Container<T extends ElementType = 'div'>({
12
12
  marginX = 'auto',
13
+ size = 2,
13
14
  ...props
14
15
  }: ContainerProps<T> & Omit<ComponentPropsWithRef<T>, keyof ContainerProps<T>>) {
15
- const {as, children, className, style, ...rest} = getProps({marginX, ...props}, containerProps)
16
+ const {as, children, className, style, ...rest} = getProps(
17
+ {marginX, size, ...props},
18
+ containerProps,
19
+ )
16
20
  const Component = as || 'div'
17
21
 
18
22
  return (
@@ -1,3 +1,7 @@
1
+ .sui-Container {
2
+ width: 100%;
3
+ }
4
+
1
5
  .sui-Container[class*='sui-tone'] {
2
6
  --container-background: var(--background-low);
3
7
  background-color: var(--container-background);
@@ -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
- /** Element to render */
8
+ /**
9
+ * HTML element or component to render.
10
+ */
9
11
  as?: T
10
- /** CSS **max-width** property */
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
- /** Element to render */
8
+ /**
9
+ * HTML element to render.
10
+ */
9
11
  as?: T
10
- /** CSS **font-size** property */
12
+ /**
13
+ * Sets font size. Uses the same scale as Label.
14
+ */
11
15
  size?: Responsive<EyebrowSize>
12
16
  }
13
17
 
@@ -1,3 +1,8 @@
1
+ .sui-Flex {
2
+ min-width: 0;
3
+ min-height: 0;
4
+ }
5
+
1
6
  .sui-Flex[class*='sui-tone'] {
2
7
  --flex-background: var(--background-low);
3
8
  background-color: var(--flex-background);
@@ -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
- /** Element to render */
11
+ /**
12
+ * HTML element or component to render.
13
+ */
12
14
  as?: T
13
- /** CSS **display** property */
15
+ /**
16
+ * CSS `display` property.
17
+ */
14
18
  display?: Responsive<DisplayFlex>
15
19
  }
16
20