@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.
- package/README.md +1 -1
- package/dist/LazyRefractor-CQ22IeXB.js +19 -0
- package/dist/LazyRefractor-CQ22IeXB.js.map +1 -0
- package/dist/cli/install.js +2 -1
- package/dist/index.d.ts +662 -796
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2081 -1758
- 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 +16 -9
- 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.css +5 -0
- 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 +4 -3
- package/src/components/checkbox/checkbox.props.ts +11 -3
- package/src/components/code/code.props.ts +10 -3
- package/src/components/container/Container.tsx +5 -1
- package/src/components/container/container.css +4 -0
- 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.css +5 -0
- package/src/components/flex/flex.props.ts +6 -2
- package/src/components/grid/grid.css +5 -0
- package/src/components/grid/grid.props.ts +6 -2
- package/src/components/h-stack/HStack.tsx +8 -14
- package/src/components/h-stack/hStack.props.ts +5 -13
- 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 +4 -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/select/Select.tsx +47 -0
- package/src/components/select/select.css +67 -0
- package/src/components/select/select.props.ts +25 -0
- 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.tsx +8 -14
- package/src/components/v-stack/vStack.props.ts +5 -13
- 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/classes/system/shadow.css +6 -6
- package/src/css/global/reset.css +32 -19
- package/src/css/tokens/semantic.css +4 -0
- package/src/css/tokens/surface.css +2 -0
- package/src/hooks/useIsClient.ts +10 -0
- package/src/index.ts +8 -0
- package/src/polyfills.ts +17 -0
- package/src/props/formElement.ts +34 -0
- package/src/types/FormElement.ts +2 -0
- package/src/utils/getProps.ts +1 -1
- package/src/utils/mergeTriggerProps.ts +66 -0
- package/src/utils/renderPortal.tsx +14 -0
- package/src/version.ts +2 -2
- package/dist/_chunks-es/LazyRefractor.js +0 -20
- package/dist/_chunks-es/LazyRefractor.js.map +0 -1
|
@@ -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
|
|
|
@@ -1,33 +1,27 @@
|
|
|
1
1
|
import clsx from 'clsx'
|
|
2
2
|
import type {ComponentPropsWithRef, ElementType} from 'react'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {type ComponentProps} from '../../utils/getProps'
|
|
5
5
|
import {suffixClassName} from '../../utils/suffixClassName'
|
|
6
6
|
import {Flex} from '../flex/Flex'
|
|
7
|
-
import {type HStackProps
|
|
7
|
+
import {type HStackProps} from './hStack.props'
|
|
8
8
|
|
|
9
9
|
const hStackClassName = suffixClassName('sui-HStack')
|
|
10
10
|
|
|
11
11
|
/** @public */
|
|
12
|
-
export function HStack<T extends ElementType = 'div'>(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const Component = as || 'div'
|
|
17
|
-
|
|
12
|
+
export function HStack<T extends ElementType = 'div'>({
|
|
13
|
+
className,
|
|
14
|
+
...props
|
|
15
|
+
}: HStackProps<T> & Omit<ComponentPropsWithRef<T>, keyof HStackProps<T>>) {
|
|
18
16
|
return (
|
|
19
17
|
<Flex
|
|
20
|
-
as={Component}
|
|
21
18
|
className={clsx(hStackClassName, className)}
|
|
22
|
-
style={style}
|
|
23
19
|
data-ui="HStack"
|
|
24
20
|
display="inline-flex"
|
|
25
21
|
alignItems="center"
|
|
26
22
|
flexWrap="wrap"
|
|
27
|
-
{...
|
|
28
|
-
|
|
29
|
-
{children}
|
|
30
|
-
</Flex>
|
|
23
|
+
{...(props as ComponentProps)}
|
|
24
|
+
/>
|
|
31
25
|
)
|
|
32
26
|
}
|
|
33
27
|
|
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {type PropDef} from '../../types/PropDef'
|
|
1
|
+
import type {FlexProps} from '../flex/flex.props'
|
|
3
2
|
|
|
4
3
|
/** @public */
|
|
5
|
-
export interface HStackProps<T extends React.ElementType = 'div'> extends Pick<
|
|
6
|
-
|
|
7
|
-
as
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const hStackProps: Record<string, PropDef> = {
|
|
11
|
-
as: {
|
|
12
|
-
type: 'string',
|
|
13
|
-
},
|
|
14
|
-
gap: gapProps['gap'] as PropDef,
|
|
15
|
-
}
|
|
4
|
+
export interface HStackProps<T extends React.ElementType = 'div'> extends Pick<
|
|
5
|
+
FlexProps<T>,
|
|
6
|
+
'as' | 'gap'
|
|
7
|
+
> {}
|
|
@@ -41,7 +41,7 @@ export function Heading<T extends ElementType = 'h2'>({
|
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
43
|
<Component
|
|
44
|
-
className={clsx(headingClassName, 'sui-overflow-hidden', className)}
|
|
44
|
+
className={clsx(headingClassName, props.truncate && 'sui-overflow-hidden', className)}
|
|
45
45
|
style={style}
|
|
46
46
|
data-ui="Heading"
|
|
47
47
|
{...rest}
|
|
@@ -5,9 +5,15 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
5
5
|
|
|
6
6
|
/** @public */
|
|
7
7
|
export interface HeadingProps extends TypographyProps {
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Semantic heading element to render.
|
|
10
|
+
* @remarks Always set this explicitly.
|
|
11
|
+
*/
|
|
9
12
|
as?: HeadingTag
|
|
10
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Sets font size.
|
|
15
|
+
* @remarks Independent of `as`.
|
|
16
|
+
*/
|
|
11
17
|
size?: Responsive<HeadingSize>
|
|
12
18
|
}
|
|
13
19
|
|
|
@@ -10,9 +10,13 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
10
10
|
/** @public */
|
|
11
11
|
export interface IconProps
|
|
12
12
|
extends ComponentProps<'svg'>, Pick<TypographyProps, 'muted'>, MarginProps, ToneProps {
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* The icon component to render.
|
|
15
|
+
*/
|
|
14
16
|
icon: React.ComponentType<SVGProps<SVGSVGElement>>
|
|
15
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Sets icon size.
|
|
19
|
+
*/
|
|
16
20
|
size?: Responsive<IconSize>
|
|
17
21
|
}
|
|
18
22
|
|
|
@@ -9,9 +9,14 @@ export interface IconButtonProps<T extends React.ElementType = 'button'> extends
|
|
|
9
9
|
ButtonProps<T>,
|
|
10
10
|
'as' | 'density' | 'level' | 'loading' | 'tone'
|
|
11
11
|
> {
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Accessible name for the button.
|
|
14
|
+
* @remarks Because IconButtons have no visible text, this is required.
|
|
15
|
+
*/
|
|
13
16
|
'aria-label': string
|
|
14
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Icon to render.
|
|
19
|
+
*/
|
|
15
20
|
'icon': React.ComponentType<SVGProps<SVGSVGElement>>
|
|
16
21
|
}
|
|
17
22
|
|
package/src/components/index.css
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
@import './badge/badge.css';
|
|
1
2
|
@import './box/box.css';
|
|
2
3
|
@import './button/button.css';
|
|
3
4
|
@import './card/card.css';
|
|
4
5
|
@import './checkbox/checkbox.css';
|
|
5
6
|
@import './code/code.css';
|
|
6
7
|
@import './container/container.css';
|
|
8
|
+
@import './dialog/dialog.css';
|
|
7
9
|
@import './eyebrow/eyebrow.css';
|
|
8
10
|
@import './flex/flex.css';
|
|
9
11
|
@import './grid/grid.css';
|
|
@@ -15,8 +17,10 @@
|
|
|
15
17
|
@import './label/label.css';
|
|
16
18
|
@import './link/link.css';
|
|
17
19
|
@import './list/list.css';
|
|
20
|
+
@import './popover/popover.css';
|
|
18
21
|
@import './press-area/press-area.css';
|
|
19
22
|
@import './radio/radio.css';
|
|
23
|
+
@import './select/select.css';
|
|
20
24
|
@import './spinner/spinner.css';
|
|
21
25
|
@import './skip-to-content/skip-to-content.css';
|
|
22
26
|
@import './switch/switch.css';
|
|
@@ -3,9 +3,13 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
3
3
|
|
|
4
4
|
/** @beta */
|
|
5
5
|
export interface IndicatorProps<T extends React.ElementType = 'span'> extends ToneProps {
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* HTML element to render.
|
|
8
|
+
*/
|
|
7
9
|
as?: T
|
|
8
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Accessible label.
|
|
12
|
+
*/
|
|
9
13
|
label?: string
|
|
10
14
|
}
|
|
11
15
|
|
|
@@ -7,9 +7,13 @@ import {SPACE, type SpaceInherit} from '../../types/Space'
|
|
|
7
7
|
/** @deprecated Use HStack component instead */
|
|
8
8
|
/** @public */
|
|
9
9
|
export interface InlineProps<T extends React.ElementType = 'div'> extends PaddingProps {
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* HTML element or component to render.
|
|
12
|
+
*/
|
|
11
13
|
as?: T
|
|
12
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Space between children.
|
|
16
|
+
*/
|
|
13
17
|
gap?: Responsive<SpaceInherit>
|
|
14
18
|
}
|
|
15
19
|
|
|
@@ -3,8 +3,14 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
3
3
|
|
|
4
4
|
/** @beta */
|
|
5
5
|
export interface LabelProps extends React.ComponentProps<'label'>, MarginProps {
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Renders the label in a muted color.
|
|
8
|
+
*/
|
|
7
9
|
disabled?: boolean
|
|
10
|
+
/**
|
|
11
|
+
* Renders the label in the error color.
|
|
12
|
+
* @remarks Use when the linked input has a validation error.
|
|
13
|
+
*/
|
|
8
14
|
error?: boolean
|
|
9
15
|
}
|
|
10
16
|
|
|
@@ -2,12 +2,12 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
2
2
|
|
|
3
3
|
/** @beta */
|
|
4
4
|
export interface LinkProps extends React.ComponentProps<'a'> {
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Sets the link to `target="_blank"` and `rel="noopener noreferrer"`.
|
|
7
|
+
*/
|
|
6
8
|
openInNewTab?: boolean
|
|
7
9
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @defaultValue true
|
|
10
|
+
* Underlines the link.
|
|
11
11
|
*/
|
|
12
12
|
underlined?: boolean
|
|
13
13
|
}
|
|
@@ -11,7 +11,9 @@ import type {Responsive} from '../../types/Responsive'
|
|
|
11
11
|
|
|
12
12
|
/** @beta */
|
|
13
13
|
export interface ListProps<T extends ListTag = 'ul'> extends Pick<GapProps, 'gap'> {
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* HTML element to render.
|
|
16
|
+
*/
|
|
15
17
|
as?: T
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -24,11 +26,17 @@ export const listProps: Record<string, PropDef> = {
|
|
|
24
26
|
|
|
25
27
|
/** @beta */
|
|
26
28
|
export interface ListItemProps extends React.ComponentProps<'li'> {
|
|
27
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Sets horizontal padding, gap, and minimum row height together.
|
|
31
|
+
*/
|
|
28
32
|
density?: Responsive<Density>
|
|
29
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Slot at the left edge of the row.
|
|
35
|
+
*/
|
|
30
36
|
start?: React.ReactNode
|
|
31
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Slot at the right edge of the row.
|
|
39
|
+
*/
|
|
32
40
|
end?: React.ReactNode
|
|
33
41
|
}
|
|
34
42
|
|
|
@@ -73,9 +81,13 @@ export const listItemProps: Record<string, PropDef> = {
|
|
|
73
81
|
|
|
74
82
|
/** @beta */
|
|
75
83
|
export interface ListButtonItemProps<T extends React.ElementType = 'button'> extends ListItemProps {
|
|
76
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Element or component to render for the press target.
|
|
86
|
+
*/
|
|
77
87
|
as?: InteractiveAs<T>
|
|
78
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Marks the row as the current selection.
|
|
90
|
+
*/
|
|
79
91
|
selected?: boolean
|
|
80
92
|
}
|
|
81
93
|
|
|
@@ -91,11 +103,17 @@ export const listButtonItemProps: Record<string, PropDef> = {
|
|
|
91
103
|
|
|
92
104
|
/** @beta */
|
|
93
105
|
export interface ListItemTextProps<T extends React.ElementType = 'div'> {
|
|
94
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* HTML element to render.
|
|
108
|
+
*/
|
|
95
109
|
as?: T
|
|
96
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Main label.
|
|
112
|
+
*/
|
|
97
113
|
title?: React.ReactNode
|
|
98
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* Text below the title.
|
|
116
|
+
*/
|
|
99
117
|
subtitle?: React.ReactNode
|
|
100
118
|
}
|
|
101
119
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
import {Activity, cloneElement, useId, useState, type ToggleEvent} from 'react'
|
|
3
|
+
|
|
4
|
+
import {useIsClient} from '../../hooks/useIsClient'
|
|
5
|
+
import {getProps} from '../../utils/getProps'
|
|
6
|
+
import {mergeTriggerProps} from '../../utils/mergeTriggerProps'
|
|
7
|
+
import {renderPortal} from '../../utils/renderPortal'
|
|
8
|
+
import {suffixClassName} from '../../utils/suffixClassName'
|
|
9
|
+
import {type PopoverProps, popoverProps} from './popover.props'
|
|
10
|
+
|
|
11
|
+
const popoverClassName = suffixClassName('sui-PopoverContent')
|
|
12
|
+
|
|
13
|
+
function PopoverRoot({
|
|
14
|
+
placement = 'bottom',
|
|
15
|
+
...props
|
|
16
|
+
}: PopoverProps & {
|
|
17
|
+
triggerProps?: Record<string, unknown>
|
|
18
|
+
}) {
|
|
19
|
+
const {
|
|
20
|
+
children,
|
|
21
|
+
className,
|
|
22
|
+
style,
|
|
23
|
+
id: idProp,
|
|
24
|
+
anchorName,
|
|
25
|
+
content,
|
|
26
|
+
portal,
|
|
27
|
+
triggerProps: forwardedTriggerProps,
|
|
28
|
+
...rest
|
|
29
|
+
} = getProps({placement, ...props}, popoverProps)
|
|
30
|
+
const reactId = useId()
|
|
31
|
+
const id = idProp || reactId
|
|
32
|
+
const [open, setOpen] = useState(false)
|
|
33
|
+
const isClient = useIsClient()
|
|
34
|
+
|
|
35
|
+
const handleToggle = (e: ToggleEvent) => {
|
|
36
|
+
setOpen(e.newState === 'open')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const triggerProps = {
|
|
40
|
+
popoverTarget: id,
|
|
41
|
+
style: {anchorName: `--anchor-${anchorName || id}`},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const trigger = children.type.forwardsTriggerProps
|
|
45
|
+
? cloneElement(children, {triggerProps})
|
|
46
|
+
: cloneElement(children, mergeTriggerProps(children.props, forwardedTriggerProps, triggerProps))
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
{trigger}
|
|
51
|
+
|
|
52
|
+
{renderPortal(
|
|
53
|
+
<Activity mode={open ? 'visible' : 'hidden'}>
|
|
54
|
+
<div
|
|
55
|
+
className={clsx(
|
|
56
|
+
popoverClassName,
|
|
57
|
+
'sui-px2 sui-py1 sui-radius2 sui-position-fixed sui-shadow2',
|
|
58
|
+
className,
|
|
59
|
+
)}
|
|
60
|
+
style={{
|
|
61
|
+
...style,
|
|
62
|
+
positionAnchor: `--anchor-${anchorName || id}`,
|
|
63
|
+
}}
|
|
64
|
+
data-ui="Popover"
|
|
65
|
+
popover="auto"
|
|
66
|
+
id={id}
|
|
67
|
+
onToggle={handleToggle}
|
|
68
|
+
{...rest}
|
|
69
|
+
>
|
|
70
|
+
{content}
|
|
71
|
+
</div>
|
|
72
|
+
</Activity>,
|
|
73
|
+
isClient,
|
|
74
|
+
portal,
|
|
75
|
+
)}
|
|
76
|
+
</>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** @beta */
|
|
81
|
+
export const Popover = Object.assign(PopoverRoot, {
|
|
82
|
+
forwardsTriggerProps: true,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
export type {PopoverProps}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.sui-PopoverContent {
|
|
2
|
+
visibility: hidden;
|
|
3
|
+
opacity: 0;
|
|
4
|
+
scale: 0.9;
|
|
5
|
+
transition: none;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.sui-PopoverContent:popover-open {
|
|
9
|
+
visibility: visible;
|
|
10
|
+
opacity: 1;
|
|
11
|
+
scale: 1;
|
|
12
|
+
transition: var(--fade-in-transition);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@starting-style {
|
|
16
|
+
.sui-PopoverContent:popover-open {
|
|
17
|
+
visibility: hidden;
|
|
18
|
+
opacity: 0;
|
|
19
|
+
scale: 0.9;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {type PlacementProps, placementProps} from '../../props/placement'
|
|
2
|
+
import {type PropDef} from '../../types/PropDef'
|
|
3
|
+
|
|
4
|
+
/** @beta */
|
|
5
|
+
export interface PopoverProps
|
|
6
|
+
extends Omit<React.ComponentProps<'div'>, 'children' | 'content'>, PlacementProps {
|
|
7
|
+
/**
|
|
8
|
+
* Shared anchor identifier.
|
|
9
|
+
* @remarks Set the same value on a Tooltip and a Popover to point both at one trigger.
|
|
10
|
+
*/
|
|
11
|
+
anchorName?: React.ReactNode
|
|
12
|
+
/**
|
|
13
|
+
* The trigger element.
|
|
14
|
+
* @remarks Popover clones it and attaches the `popovertarget` attribute that opens the floating layer.
|
|
15
|
+
*/
|
|
16
|
+
children: React.ReactElement<Record<string, unknown>>
|
|
17
|
+
/**
|
|
18
|
+
* The floating content.
|
|
19
|
+
*/
|
|
20
|
+
content?: React.ReactNode
|
|
21
|
+
/**
|
|
22
|
+
* Renders the content into `document.body` through a React portal instead of inline.
|
|
23
|
+
*/
|
|
24
|
+
portal?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const popoverProps: Record<string, PropDef> = {
|
|
28
|
+
anchorName: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
},
|
|
31
|
+
content: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
},
|
|
34
|
+
portal: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
},
|
|
37
|
+
...placementProps,
|
|
38
|
+
}
|
|
@@ -3,9 +3,14 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
3
3
|
|
|
4
4
|
/** @beta */
|
|
5
5
|
export interface RadioProps extends React.ComponentProps<'input'>, MarginProps {
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Applies error styling to the radio mark.
|
|
8
|
+
* @remarks Use when form validation fails. Pair with a visible error message. The prop is visual only.
|
|
9
|
+
*/
|
|
7
10
|
error?: boolean
|
|
8
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Visible label next to the radio.
|
|
13
|
+
*/
|
|
9
14
|
label: React.ReactNode
|
|
10
15
|
}
|
|
11
16
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import SelectIcon from '@sanity/icons/Select'
|
|
2
|
+
import clsx from 'clsx'
|
|
3
|
+
import {useId} from 'react'
|
|
4
|
+
|
|
5
|
+
import {getProps} from '../../utils/getProps'
|
|
6
|
+
import {suffixClassName} from '../../utils/suffixClassName'
|
|
7
|
+
import {Icon} from '../icon/Icon'
|
|
8
|
+
import {type SelectProps, selectProps} from './select.props'
|
|
9
|
+
|
|
10
|
+
const selectClassName = suffixClassName('sui-Select')
|
|
11
|
+
|
|
12
|
+
/** @public */
|
|
13
|
+
export function Select({density = 'regular', hasError = false, ...props}: SelectProps) {
|
|
14
|
+
const {children, className, style, ...rest} = getProps({density, ...props}, selectProps)
|
|
15
|
+
|
|
16
|
+
const anchorId = useId()
|
|
17
|
+
const anchorName = `--anchor-${anchorId}`
|
|
18
|
+
|
|
19
|
+
const selectClasses = clsx(
|
|
20
|
+
selectClassName,
|
|
21
|
+
hasError && 'sui-error',
|
|
22
|
+
'sui-radius2',
|
|
23
|
+
'sui-width-full',
|
|
24
|
+
className,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const iconClasses = `sui-position-absolute sui-display-flex sui-align-items-center sui-px1`
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<select
|
|
32
|
+
data-ui="Select"
|
|
33
|
+
className={selectClasses}
|
|
34
|
+
style={{...style, anchorName}}
|
|
35
|
+
{...rest}
|
|
36
|
+
aria-invalid={hasError || undefined}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
</select>
|
|
40
|
+
<div className={iconClasses} data-ui="Select-icon" style={{positionAnchor: anchorName}}>
|
|
41
|
+
<Icon icon={SelectIcon} size={1} />
|
|
42
|
+
</div>
|
|
43
|
+
</>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type {SelectProps}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
.sui-Select {
|
|
2
|
+
--background-color: var(--backdrop);
|
|
3
|
+
--border-color: var(--separator-high);
|
|
4
|
+
--color: var(--foreground-high);
|
|
5
|
+
background-color: var(--background-color);
|
|
6
|
+
border: 1px solid var(--border-color);
|
|
7
|
+
font: var(--form-element-text);
|
|
8
|
+
color: var(--color);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
[data-ui="Select-icon"] {
|
|
12
|
+
--background-color: var(--backdrop);
|
|
13
|
+
background-color: var(--background-color);
|
|
14
|
+
pointer-events: none;
|
|
15
|
+
/* reduce block size by 1px at each end and inset from right to not interrupt Select’s border */
|
|
16
|
+
top: calc(anchor(top) + 1px);
|
|
17
|
+
bottom: calc(anchor(bottom) + 1px);
|
|
18
|
+
right: calc(anchor(right) + 2px);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Error
|
|
23
|
+
*/
|
|
24
|
+
.sui-Select.sui-error {
|
|
25
|
+
--border-color: var(--error-separator);
|
|
26
|
+
--color: var(--error-foreground);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.sui-Select.sui-error + [data-ui="Select-icon"] svg {
|
|
30
|
+
color: var(--error-foreground);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Hover - uses relative border/background colours to darken/lighten in both normal & error state
|
|
35
|
+
*/
|
|
36
|
+
.sui-Select:hover {
|
|
37
|
+
border-color: light-dark(
|
|
38
|
+
oklch(from var(--border-color) calc(l - var(--hover-lightness-delta)) c h),
|
|
39
|
+
oklch(from var(--border-color) calc(l + var(--hover-lightness-delta)) c h)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Disabled
|
|
45
|
+
*/
|
|
46
|
+
.sui-Select:disabled {
|
|
47
|
+
--background-color: var(--inert-background);
|
|
48
|
+
--border-color: var(--separator-low);
|
|
49
|
+
--color: var(--inert-foreground);
|
|
50
|
+
pointer-events: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.sui-Select:disabled + [data-ui="Select-icon"] {
|
|
54
|
+
--background-color: var(--inert-background);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.sui-Select:disabled + [data-ui="Select-icon"] svg {
|
|
58
|
+
color: var(--inert-foreground);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Focus
|
|
63
|
+
*/
|
|
64
|
+
.sui-Select:focus-visible {
|
|
65
|
+
outline: var(--focus-outline);
|
|
66
|
+
outline-offset: var(--focus-offset);
|
|
67
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {formElementDensityProps, type FormElementDensityProps} from '../../props/formElement'
|
|
2
|
+
import {type PropDef} from '../../types/PropDef'
|
|
3
|
+
|
|
4
|
+
/** @public */
|
|
5
|
+
export type SelectProps = React.ComponentProps<'select'> &
|
|
6
|
+
FormElementDensityProps & {
|
|
7
|
+
/**
|
|
8
|
+
* Disables the select element, blocking interactions and hiding it from screen readers.
|
|
9
|
+
*/
|
|
10
|
+
disabled?: boolean
|
|
11
|
+
/**
|
|
12
|
+
* Used to trigger styling for invalid states.
|
|
13
|
+
*/
|
|
14
|
+
hasError?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const selectProps: Record<string, PropDef> = {
|
|
18
|
+
disabled: {
|
|
19
|
+
type: 'boolean',
|
|
20
|
+
},
|
|
21
|
+
hasError: {
|
|
22
|
+
type: 'boolean',
|
|
23
|
+
},
|
|
24
|
+
...formElementDensityProps,
|
|
25
|
+
}
|
|
@@ -3,12 +3,18 @@ import {type PropDef} from '../../types/PropDef'
|
|
|
3
3
|
/** @beta */
|
|
4
4
|
export interface SkipToContentProps extends React.ComponentProps<'a'> {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Same-page fragment to jump to.
|
|
7
|
+
* @remarks Note: The hash character # is required for proper linking.
|
|
7
8
|
*/
|
|
8
9
|
href: string
|
|
9
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Visible and announced text.
|
|
12
|
+
*/
|
|
10
13
|
label: string
|
|
11
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Not accepted.
|
|
16
|
+
* @remarks Pass text via `label`.
|
|
17
|
+
*/
|
|
12
18
|
children?: never
|
|
13
19
|
}
|
|
14
20
|
|