@sanity/ui 5.0.0-alpha.14 → 5.0.0-alpha.16

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 (45) hide show
  1. package/dist/index.d.ts +97 -20
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +342 -83
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/package.json +2 -2
  7. package/src/components/avatar/Avatar.tsx +50 -0
  8. package/src/components/avatar/avatar.css +51 -0
  9. package/src/components/avatar/avatar.props.ts +57 -0
  10. package/src/components/avatar-counter/AvatarCounter.tsx +38 -0
  11. package/src/components/avatar-counter/avatar-counter.css +8 -0
  12. package/src/components/avatar-counter/avatarCounter.props.ts +44 -0
  13. package/src/components/avatar-stack/AvatarStack.tsx +60 -0
  14. package/src/components/avatar-stack/avatar-stack.css +16 -0
  15. package/src/components/avatar-stack/avatarStack.props.ts +20 -0
  16. package/src/components/button/button.css +2 -2
  17. package/src/components/checkbox/Checkbox.tsx +3 -3
  18. package/src/components/checkbox/checkbox.props.ts +2 -2
  19. package/src/components/grid/grid.props.ts +8 -1
  20. package/src/components/hotkeys/Hotkeys.tsx +29 -0
  21. package/src/components/hotkeys/hotkeys.css +3 -0
  22. package/src/components/hotkeys/hotkeys.props.ts +15 -0
  23. package/src/components/icon/Icon.tsx +1 -0
  24. package/src/components/index.css +6 -0
  25. package/src/components/kbd/KBD.tsx +33 -0
  26. package/src/components/kbd/kbd.css +6 -0
  27. package/src/components/kbd/kbd.props.ts +20 -0
  28. package/src/components/radio/Radio.tsx +3 -3
  29. package/src/components/radio/radio.props.ts +2 -2
  30. package/src/components/select/select.props.ts +1 -0
  31. package/src/components/switch/Switch.tsx +3 -3
  32. package/src/components/switch/switch.props.ts +2 -2
  33. package/src/components/text-area/TextArea.tsx +32 -0
  34. package/src/components/text-area/text-area.css +56 -0
  35. package/src/components/text-area/textArea.props.ts +35 -0
  36. package/src/components/text-input/TextInput.tsx +1 -1
  37. package/src/components/text-input/text-input.css +9 -3
  38. package/src/components/text-input/textInput.props.ts +1 -0
  39. package/src/css/classes/dynamic/height.css +2 -0
  40. package/src/css/classes/local/field-sizing.css +3 -0
  41. package/src/css/classes/local/index.css +1 -0
  42. package/src/index.ts +7 -1
  43. package/src/types/Avatar.ts +14 -0
  44. package/src/types/Eyebrow.ts +1 -1
  45. 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.14",
3
+ "version": "5.0.0-alpha.16",
4
4
  "description": "Sanity UI Library",
5
5
  "keywords": [
6
6
  "components",
@@ -48,7 +48,7 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@sanity-labs/design-tokens": "0.0.2-alpha.6",
51
+ "@sanity-labs/design-tokens": "0.0.2-alpha.7",
52
52
  "@sanity/icons": "^5.2.1",
53
53
  "clsx": "^2.1.1",
54
54
  "comment-json": "^5.0.0",
@@ -0,0 +1,50 @@
1
+ import clsx from 'clsx'
2
+ import {useState} from 'react'
3
+
4
+ import {getProps} from '../../utils/getProps'
5
+ import {suffixClassName} from '../../utils/suffixClassName'
6
+ import {Text} from '../text/Text'
7
+ import {type AvatarProps, avatarProps} from './avatar.props'
8
+
9
+ const avatarClassName = suffixClassName('sui-Avatar')
10
+
11
+ /** @public */
12
+ export function Avatar({color = 'gray', size = 1, ...props}: AvatarProps) {
13
+ const {
14
+ className,
15
+ style,
16
+ 'aria-label': ariaLabel,
17
+ name,
18
+ src,
19
+ ...rest
20
+ } = getProps({color, size, ...props}, avatarProps)
21
+ const [error, setError] = useState(false)
22
+
23
+ return (
24
+ <figure
25
+ className={clsx(
26
+ avatarClassName,
27
+ 'sui-display-inline-flex sui-align-items-center sui-justify-content-center sui-radius-full sui-position-relative',
28
+ className,
29
+ )}
30
+ style={style}
31
+ data-ui="Avatar"
32
+ {...rest}
33
+ >
34
+ {src && !error ? (
35
+ <img
36
+ className="sui-radius-full sui-width-full sui-height-full"
37
+ src={src}
38
+ alt={ariaLabel || name}
39
+ onError={() => setError(true)}
40
+ />
41
+ ) : (
42
+ <Text role="img" weight="semibold" aria-label={ariaLabel || name}>
43
+ {name.slice(0, 1)}
44
+ </Text>
45
+ )}
46
+ </figure>
47
+ )
48
+ }
49
+
50
+ export type {AvatarProps}
@@ -0,0 +1,51 @@
1
+ .sui-Avatar {
2
+ border: 1px solid transparent;
3
+ aspect-ratio: 1;
4
+ }
5
+
6
+ .sui-Avatar img {
7
+ border: 1px solid light-dark(var(--white), var(--black));
8
+ object-fit: cover;
9
+ }
10
+
11
+ .sui-Avatar .sui-Text {
12
+ color: light-dark(var(--white), var(--black));
13
+ font-size: inherit;
14
+ text-transform: uppercase;
15
+ }
16
+
17
+ .sui-Avatar.sui-avatar-gray {
18
+ background-color: light-dark(var(--gray-600), var(--gray-400));
19
+ }
20
+
21
+ .sui-Avatar.sui-avatar-blue {
22
+ background-color: light-dark(var(--blue-600), var(--blue-400));
23
+ }
24
+
25
+ .sui-Avatar.sui-avatar-purple {
26
+ background-color: light-dark(var(--purple-600), var(--purple-400));
27
+ }
28
+
29
+ .sui-Avatar.sui-avatar-magenta {
30
+ background-color: light-dark(var(--magenta-600), var(--magenta-400));
31
+ }
32
+
33
+ .sui-Avatar.sui-avatar-red {
34
+ background-color: light-dark(var(--red-600), var(--red-400));
35
+ }
36
+
37
+ .sui-Avatar.sui-avatar-orange {
38
+ background-color: light-dark(var(--orange-600), var(--orange-400));
39
+ }
40
+
41
+ .sui-Avatar.sui-avatar-yellow {
42
+ background-color: light-dark(var(--yellow-600), var(--yellow-400));
43
+ }
44
+
45
+ .sui-Avatar.sui-avatar-green {
46
+ background-color: light-dark(var(--green-600), var(--green-400));
47
+ }
48
+
49
+ .sui-Avatar.sui-avatar-cyan {
50
+ background-color: light-dark(var(--cyan-600), var(--cyan-400));
51
+ }
@@ -0,0 +1,57 @@
1
+ import {heightProps} from '../../props/height'
2
+ import {AVATAR_COLOR, AVATAR_SIZE, type AvatarColor, type AvatarSize} from '../../types/Avatar'
3
+ import {type PropDef} from '../../types/PropDef'
4
+ import type {Responsive} from '../../types/Responsive'
5
+ import {TEXT_SIZE} from '../../types/Text'
6
+
7
+ /** @public */
8
+ export interface AvatarProps extends React.ComponentProps<'figure'> {
9
+ /** Avatar color */
10
+ color?: AvatarColor
11
+ /** Avatar name, only first letter will be displayed */
12
+ name: string
13
+ /** Composite prop for setting height and font size */
14
+ size?: Responsive<AvatarSize>
15
+ /** Avatar image src */
16
+ src?: string
17
+ }
18
+
19
+ export const avatarProps: Record<string, PropDef> = {
20
+ color: {
21
+ type: 'union',
22
+ className: 'avatar',
23
+ values: AVATAR_COLOR,
24
+ },
25
+ name: {
26
+ type: 'string',
27
+ },
28
+ size: {
29
+ type: 'composite',
30
+ values: AVATAR_SIZE,
31
+ composition: {
32
+ size: {
33
+ propDef: {
34
+ type: 'union',
35
+ className: 'text-body',
36
+ values: TEXT_SIZE,
37
+ },
38
+ mapping: {
39
+ 0: 0,
40
+ 1: 1,
41
+ 2: 2,
42
+ },
43
+ },
44
+ height: {
45
+ propDef: heightProps['height'] as PropDef,
46
+ mapping: {
47
+ 0: '19px',
48
+ 1: '25px',
49
+ 2: '33px',
50
+ },
51
+ },
52
+ },
53
+ },
54
+ src: {
55
+ type: 'string',
56
+ },
57
+ }
@@ -0,0 +1,38 @@
1
+ import clsx from 'clsx'
2
+
3
+ import {getProps} from '../../utils/getProps'
4
+ import {suffixClassName} from '../../utils/suffixClassName'
5
+ import {Text} from '../text/Text'
6
+ import {type AvatarCounterProps, avatarCounterProps} from './avatarCounter.props'
7
+
8
+ const avatarCounterClassName = suffixClassName('sui-AvatarCounter')
9
+
10
+ /** @public */
11
+ export function AvatarCounter({size = 1, ...props}: AvatarCounterProps) {
12
+ const {
13
+ className,
14
+ style,
15
+ 'aria-label': ariaLabel,
16
+ count,
17
+ ...rest
18
+ } = getProps({size, ...props}, avatarCounterProps)
19
+
20
+ return (
21
+ <figure
22
+ className={clsx(
23
+ avatarCounterClassName,
24
+ 'sui-display-inline-flex sui-align-items-center sui-justify-content-center sui-radius-full sui-border',
25
+ className,
26
+ )}
27
+ style={style}
28
+ data-ui="AvatarCounter"
29
+ {...rest}
30
+ >
31
+ <Text role="img" weight="semibold" aria-label={ariaLabel || `+ ${count} more`}>
32
+ {count}
33
+ </Text>
34
+ </figure>
35
+ )
36
+ }
37
+
38
+ export type {AvatarCounterProps}
@@ -0,0 +1,8 @@
1
+ .sui-AvatarCounter {
2
+ aspect-ratio: 1;
3
+ padding-inline: 0.5em;
4
+ }
5
+
6
+ .sui-AvatarCounter .sui-Text {
7
+ font-size: inherit;
8
+ }
@@ -0,0 +1,44 @@
1
+ import {heightProps} from '../../props/height'
2
+ import {AVATAR_SIZE, type AvatarSize} from '../../types/Avatar'
3
+ import {type PropDef} from '../../types/PropDef'
4
+ import {TEXT_SIZE} from '../../types/Text'
5
+
6
+ /** @public */
7
+ export interface AvatarCounterProps extends React.ComponentProps<'figure'> {
8
+ /** Count */
9
+ count: number
10
+ /** Composite prop for setting height and font size */
11
+ size?: AvatarSize
12
+ }
13
+
14
+ export const avatarCounterProps: Record<string, PropDef> = {
15
+ count: {
16
+ type: 'number',
17
+ },
18
+ size: {
19
+ type: 'composite',
20
+ values: AVATAR_SIZE,
21
+ composition: {
22
+ size: {
23
+ propDef: {
24
+ type: 'union',
25
+ className: 'text-body',
26
+ values: TEXT_SIZE,
27
+ },
28
+ mapping: {
29
+ 0: 0,
30
+ 1: 1,
31
+ 2: 2,
32
+ },
33
+ },
34
+ height: {
35
+ propDef: heightProps['height'] as PropDef,
36
+ mapping: {
37
+ 0: '19px',
38
+ 1: '25px',
39
+ 2: '33px',
40
+ },
41
+ },
42
+ },
43
+ },
44
+ }
@@ -0,0 +1,60 @@
1
+ import clsx from 'clsx'
2
+ import {Children, cloneElement, isValidElement, type ReactElement} from 'react'
3
+
4
+ import {getProps} from '../../utils/getProps'
5
+ import {suffixClassName} from '../../utils/suffixClassName'
6
+ import {AvatarCounter} from '../avatar-counter/AvatarCounter'
7
+ import type {AvatarProps} from '../avatar/avatar.props'
8
+ import {type AvatarStackProps, avatarStackProps} from './avatarStack.props'
9
+
10
+ const avatarStackClassName = suffixClassName('sui-AvatarStack')
11
+
12
+ /** @public */
13
+ export function AvatarStack({size = 1, ...props}: AvatarStackProps) {
14
+ const {
15
+ children,
16
+ className,
17
+ style,
18
+ maxLength: maxLengthProp,
19
+ size: sizeProp,
20
+ ...rest
21
+ } = getProps({size, ...props}, avatarStackProps)
22
+ const childArray = Children.toArray(children).filter(
23
+ isValidElement,
24
+ ) as ReactElement<AvatarProps>[]
25
+ const length = childArray.length
26
+ const maxLength = maxLengthProp ?? length
27
+ const visibleAvatars =
28
+ length > maxLength ? childArray.slice(length - maxLength, length) : childArray
29
+
30
+ return (
31
+ <ul
32
+ className={clsx(avatarStackClassName, 'sui-display-flex', className)}
33
+ style={style}
34
+ data-ui="AvatarStack"
35
+ {...rest}
36
+ >
37
+ {(!length || length > maxLength) && (
38
+ <li className="sui-display-flex">
39
+ <AvatarCounter
40
+ count={!length ? 0 : length - maxLength}
41
+ size={sizeProp}
42
+ aria-label={
43
+ !length
44
+ ? 'No users'
45
+ : `+ ${length - maxLength} more user${length - maxLength > 1 ? 's' : ''}`
46
+ }
47
+ />
48
+ </li>
49
+ )}
50
+
51
+ {visibleAvatars.map((child) => (
52
+ <li key={child.key} className="sui-display-flex">
53
+ {cloneElement(child, {size: sizeProp})}
54
+ </li>
55
+ ))}
56
+ </ul>
57
+ )
58
+ }
59
+
60
+ export type {AvatarStackProps}
@@ -0,0 +1,16 @@
1
+ .sui-AvatarStack {
2
+ list-style: none;
3
+ }
4
+
5
+ .sui-AvatarStack li:has([class*='sui-Avatar']) + li:has([class*='sui-Avatar']) {
6
+ margin-left: calc(-1 * var(--space-1));
7
+ }
8
+
9
+ .sui-AvatarStack li:has(+ li [class*='sui-Avatar']) {
10
+ mask-image: radial-gradient(
11
+ 50% 50% at calc(150% - var(--space-1) - 1.25px) 50%,
12
+ transparent 97.5%,
13
+ #000 100%
14
+ );
15
+ }
16
+
@@ -0,0 +1,20 @@
1
+ import {AVATAR_SIZE, type AvatarSize} from '../../types/Avatar'
2
+ import {type PropDef} from '../../types/PropDef'
3
+
4
+ /** @public */
5
+ export interface AvatarStackProps extends React.ComponentProps<'ul'> {
6
+ /** Max number of avatars to display */
7
+ maxLength?: number
8
+ /** Avatar and AvatarCounter size */
9
+ size?: AvatarSize
10
+ }
11
+
12
+ export const avatarStackProps: Record<string, PropDef> = {
13
+ maxLength: {
14
+ type: 'number',
15
+ },
16
+ size: {
17
+ type: 'union',
18
+ values: AVATAR_SIZE,
19
+ },
20
+ }
@@ -10,7 +10,7 @@
10
10
  .sui-Button:hover {
11
11
  background: color-mix(
12
12
  in srgb,
13
- var(--tone-500) 25%,
13
+ var(--tone-500) 12%,
14
14
  var(--background-color)
15
15
  );
16
16
  }
@@ -18,7 +18,7 @@
18
18
  .sui-Button.sui-button-level-primary:hover {
19
19
  background: color-mix(
20
20
  in srgb,
21
- var(--tone-950) 25%,
21
+ var(--tone-950) 12%,
22
22
  var(--background-color)
23
23
  );
24
24
  }
@@ -17,7 +17,7 @@ const checkboxMarkClassName = suffixClassName('sui-CheckboxMark')
17
17
  /** @beta */
18
18
  export function Checkbox(props: CheckboxProps) {
19
19
  const inputRef = useRef<HTMLInputElement>(null)
20
- const {className, style, disabled, error, indeterminate, label, ...rest} = getProps(
20
+ const {className, style, disabled, hasError, indeterminate, label, ...rest} = getProps(
21
21
  props,
22
22
  checkboxProps,
23
23
  )
@@ -34,7 +34,7 @@ export function Checkbox(props: CheckboxProps) {
34
34
  style={style}
35
35
  data-ui="Checkbox"
36
36
  disabled={disabled}
37
- error={error}
37
+ error={hasError}
38
38
  >
39
39
  <VisuallyHidden
40
40
  as="input"
@@ -47,7 +47,7 @@ export function Checkbox(props: CheckboxProps) {
47
47
 
48
48
  <Box
49
49
  as="span"
50
- className={clsx(checkboxMarkClassName, error && 'sui-error')}
50
+ className={clsx(checkboxMarkClassName, hasError && 'sui-error')}
51
51
  position="relative"
52
52
  radius={2}
53
53
  width="17px"
@@ -7,7 +7,7 @@ export interface CheckboxProps extends React.ComponentProps<'input'>, MarginProp
7
7
  * Applies error styling to the checkbox mark.
8
8
  * @remarks Use when form validation fails. Pair with a visible error message. The prop is visual only.
9
9
  */
10
- error?: boolean
10
+ hasError?: boolean
11
11
  /**
12
12
  * Renders the "mixed state" mark (a horizontal line).
13
13
  * @remarks Use when the checkbox controls a group with some-but-not-all children checked.
@@ -20,7 +20,7 @@ export interface CheckboxProps extends React.ComponentProps<'input'>, MarginProp
20
20
  }
21
21
 
22
22
  export const checkboxProps: Record<string, PropDef> = {
23
- error: {
23
+ hasError: {
24
24
  type: 'boolean',
25
25
  },
26
26
  indeterminate: {
@@ -1,3 +1,4 @@
1
+ import {flexParentProps, type FlexParentProps} from '../../props/flexParent'
1
2
  import {type GapProps, gapProps} from '../../props/gap'
2
3
  import {type GridParentProps, gridParentProps} from '../../props/gridParent'
3
4
  import {type LayoutProps, layoutProps} from '../../props/layout'
@@ -7,7 +8,11 @@ import type {Responsive} from '../../types/Responsive'
7
8
 
8
9
  /** @public */
9
10
  export interface GridProps<T extends React.ElementType = 'div'>
10
- extends GridParentProps, GapProps, LayoutProps {
11
+ extends
12
+ Pick<FlexParentProps, 'alignItems' | 'justifyContent'>,
13
+ GridParentProps,
14
+ GapProps,
15
+ LayoutProps {
11
16
  /**
12
17
  * HTML element or component to render.
13
18
  */
@@ -27,6 +32,8 @@ export const gridProps: Record<string, PropDef> = {
27
32
  className: 'display',
28
33
  values: DISPLAY_GRID,
29
34
  },
35
+ alignItems: flexParentProps['alignItems'] as PropDef,
36
+ justifyContent: flexParentProps['justifyContent'] as PropDef,
30
37
  ...gridParentProps,
31
38
  ...gapProps,
32
39
  ...layoutProps,
@@ -0,0 +1,29 @@
1
+ import clsx from 'clsx'
2
+
3
+ import {getProps} from '../../utils/getProps'
4
+ import {suffixClassName} from '../../utils/suffixClassName'
5
+ import {KBD} from '../kbd/KBD'
6
+ import {type HotkeysProps, hotkeysProps} from './hotkeys.props'
7
+
8
+ const hotkeysClassName = suffixClassName('sui-Hotkeys')
9
+
10
+ /** @alpha */
11
+ export function Hotkeys({keys, ...props}: HotkeysProps) {
12
+ const {className, style, ...rest} = getProps(props, hotkeysProps)
13
+
14
+ if (!keys || keys.length === 0) {
15
+ return null
16
+ }
17
+
18
+ const hotkeysClasses = clsx(hotkeysClassName, 'sui-display-inline-flex')
19
+
20
+ return (
21
+ <kbd className={clsx(hotkeysClasses, className)} style={style} data-ui="Hotkeys" {...rest}>
22
+ {keys.map((key) => (
23
+ <KBD key={key} text={key} />
24
+ ))}
25
+ </kbd>
26
+ )
27
+ }
28
+
29
+ export type {HotkeysProps}
@@ -0,0 +1,3 @@
1
+ .sui-Hotkeys {
2
+ gap: calc(var(--space-1) / 2);
3
+ }
@@ -0,0 +1,15 @@
1
+ import {type PropDef} from '../../types/PropDef'
2
+
3
+ /** @public */
4
+ export interface HotkeysProps extends React.ComponentProps<'kbd'> {
5
+ /**
6
+ * The keys in the shortcut, in order. Each one renders as a KBD.
7
+ */
8
+ keys?: string[]
9
+ }
10
+
11
+ export const hotkeysProps: Record<string, PropDef> = {
12
+ keys: {
13
+ type: 'string',
14
+ },
15
+ }
@@ -17,6 +17,7 @@ export function Icon({icon, size = 2, ...props}: IconProps) {
17
17
  const svgProps = {
18
18
  'className': clsx(
19
19
  iconClassName,
20
+ 'sui-display-block',
20
21
  className,
21
22
  iconElement ? iconElement.props.className : undefined,
22
23
  ),
@@ -1,3 +1,6 @@
1
+ @import './avatar/avatar.css';
2
+ @import './avatar-counter/avatar-counter.css';
3
+ @import './avatar-stack/avatar-stack.css';
1
4
  @import './badge/badge.css';
2
5
  @import './box/box.css';
3
6
  @import './button/button.css';
@@ -10,10 +13,12 @@
10
13
  @import './flex/flex.css';
11
14
  @import './grid/grid.css';
12
15
  @import './heading/heading.css';
16
+ @import './hotkeys/hotkeys.css';
13
17
  @import './icon/icon.css';
14
18
  @import './indicator/indicator.css';
15
19
  @import './indicator-stack/indicator-stack.css';
16
20
  @import './inline/inline.css';
21
+ @import './kbd/kbd.css';
17
22
  @import './label/label.css';
18
23
  @import './link/link.css';
19
24
  @import './list/list.css';
@@ -25,6 +30,7 @@
25
30
  @import './skip-to-content/skip-to-content.css';
26
31
  @import './switch/switch.css';
27
32
  @import './text/text.css';
33
+ @import './text-area/text-area.css';
28
34
  @import './text-input/text-input.css';
29
35
  @import './tooltip/tooltip.css';
30
36
  @import './visually-hidden/visually-hidden.css';
@@ -0,0 +1,33 @@
1
+ import clsx from 'clsx'
2
+
3
+ import {getProps} from '../../utils/getProps'
4
+ import {suffixClassName} from '../../utils/suffixClassName'
5
+ import {Text} from '../text/Text'
6
+ import {type KBDProps, kbdProps} from './kbd.props'
7
+
8
+ const kbdClassName = suffixClassName('sui-KBD')
9
+
10
+ /** @alpha */
11
+ export function KBD(props: KBDProps) {
12
+ const {className, style, text, ...rest} = getProps(props, kbdProps)
13
+
14
+ const kbdClasses = clsx(kbdClassName, 'sui-p1 sui-radius2 sui-display-inline-block')
15
+
16
+ return (
17
+ <Text
18
+ as="kbd"
19
+ className={clsx(kbdClasses, className)}
20
+ style={style}
21
+ data-ui="KBD"
22
+ size={0}
23
+ weight="semibold"
24
+ muted
25
+ trim
26
+ {...rest}
27
+ >
28
+ {text}
29
+ </Text>
30
+ )
31
+ }
32
+
33
+ export type {KBDProps}
@@ -0,0 +1,6 @@
1
+ .sui-KBD {
2
+ --kbd-background: var(--backdrop);
3
+ --kbd-border-color: var(--separator-low);
4
+ background-color: var(--kbd-background);
5
+ box-shadow: inset 0 0 0 1px var(--kbd-border-color);
6
+ }
@@ -0,0 +1,20 @@
1
+ import {type PropDef} from '../../types/PropDef'
2
+
3
+ /** @public */
4
+ export interface KBDProps extends React.ComponentProps<'kbd'> {
5
+ /**
6
+ * Sets KBD's key label.
7
+ */
8
+ text?: string
9
+ /**
10
+ * Not accepted.
11
+ * @remarks Pass the key label via `text`.
12
+ */
13
+ children?: never
14
+ }
15
+
16
+ export const kbdProps: Record<string, PropDef> = {
17
+ text: {
18
+ type: 'string',
19
+ },
20
+ }
@@ -13,7 +13,7 @@ const radioMarkClassName = suffixClassName('sui-RadioMark')
13
13
 
14
14
  /** @beta */
15
15
  export function Radio(props: RadioProps) {
16
- const {className, style, label, disabled, error, ...rest} = getProps(props, radioProps)
16
+ const {className, style, label, disabled, hasError, ...rest} = getProps(props, radioProps)
17
17
 
18
18
  return (
19
19
  <Label
@@ -21,7 +21,7 @@ export function Radio(props: RadioProps) {
21
21
  style={style}
22
22
  data-ui="Radio"
23
23
  disabled={disabled}
24
- error={error}
24
+ error={hasError}
25
25
  >
26
26
  <VisuallyHidden
27
27
  as="input"
@@ -33,7 +33,7 @@ export function Radio(props: RadioProps) {
33
33
 
34
34
  <Box
35
35
  as="span"
36
- className={clsx(radioMarkClassName, error && 'sui-error')}
36
+ className={clsx(radioMarkClassName, hasError && 'sui-error')}
37
37
  position="relative"
38
38
  radius="full"
39
39
  width="17px"
@@ -7,7 +7,7 @@ export interface RadioProps extends React.ComponentProps<'input'>, MarginProps {
7
7
  * Applies error styling to the radio mark.
8
8
  * @remarks Use when form validation fails. Pair with a visible error message. The prop is visual only.
9
9
  */
10
- error?: boolean
10
+ hasError?: boolean
11
11
  /**
12
12
  * Visible label next to the radio.
13
13
  */
@@ -15,7 +15,7 @@ export interface RadioProps extends React.ComponentProps<'input'>, MarginProps {
15
15
  }
16
16
 
17
17
  export const radioProps: Record<string, PropDef> = {
18
- error: {
18
+ hasError: {
19
19
  type: 'boolean',
20
20
  },
21
21
  label: {
@@ -5,6 +5,7 @@ import {type PropDef} from '../../types/PropDef'
5
5
  export interface SelectProps extends React.ComponentProps<'select'>, FormElementDensityProps {
6
6
  /**
7
7
  * Used to trigger styling for invalid states.
8
+ * @remarks Use when form validation fails. Pair with a visible error message.
8
9
  */
9
10
  hasError?: boolean
10
11
  }