@sanity/ui 3.0.0-static.28 → 3.0.0-static.29

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 (37) hide show
  1. package/README.md +5 -8
  2. package/dist/_chunks-cjs/_visual-editing.cjs +66 -66
  3. package/dist/_chunks-cjs/_visual-editing.cjs.map +1 -1
  4. package/dist/_chunks-es/_visual-editing.js +67 -67
  5. package/dist/_chunks-es/_visual-editing.js.map +1 -1
  6. package/dist/{css.cjs → css/index.cjs} +47 -37
  7. package/dist/css/index.cjs.map +1 -0
  8. package/dist/css/index.css +1 -0
  9. package/dist/{css.d.cts → css/index.d.cts} +13 -3
  10. package/dist/{css.d.ts → css/index.d.ts} +13 -3
  11. package/dist/{css.js → css/index.js} +47 -37
  12. package/dist/css/index.js.map +1 -0
  13. package/dist/index.cjs +104 -48
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +28 -9
  16. package/dist/index.d.ts +28 -9
  17. package/dist/index.js +107 -51
  18. package/dist/index.js.map +1 -1
  19. package/package.json +8 -8
  20. package/src/core/components/autocomplete/autocomplete.tsx +3 -3
  21. package/src/core/components/toast/toast.tsx +13 -14
  22. package/src/core/components/toast/toastProvider.tsx +14 -10
  23. package/src/core/index.ts +1 -0
  24. package/src/core/primitives/root/Root.tsx +89 -0
  25. package/src/core/primitives/root/__workshop__/boundary.tsx +5 -0
  26. package/src/core/primitives/root/__workshop__/index.ts +8 -0
  27. package/src/core/primitives/root/index.ts +1 -0
  28. package/src/css/components/toast/toast.css.ts +1 -0
  29. package/src/css/defaultTheme.css.ts +55 -2
  30. package/src/css/index.ts +2 -0
  31. package/src/css/primitives/root/index.ts +2 -0
  32. package/src/css/primitives/root/root.css.ts +15 -0
  33. package/src/css/primitives/root/root.ts +17 -0
  34. package/src/css/primitives/root/types.ts +10 -0
  35. package/dist/css.cjs.map +0 -1
  36. package/dist/css.js.map +0 -1
  37. package/dist/index.css +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ui",
3
- "version": "3.0.0-static.28",
3
+ "version": "3.0.0-static.29",
4
4
  "keywords": [
5
5
  "react",
6
6
  "ui",
@@ -37,11 +37,11 @@
37
37
  },
38
38
  "./css": {
39
39
  "source": "./exports/css.ts",
40
- "import": "./dist/css.js",
41
- "require": "./dist/css.cjs",
42
- "default": "./dist/css.js"
40
+ "import": "./dist/css/index.js",
41
+ "require": "./dist/css/index.cjs",
42
+ "default": "./dist/css/index.js"
43
43
  },
44
- "./index.css": "./dist/index.css",
44
+ "./css/index.css": "./dist/css/index.css",
45
45
  "./package.json": "./package.json",
46
46
  "./theme": {
47
47
  "source": "./exports/theme.ts",
@@ -210,15 +210,15 @@
210
210
  "figma:build": "pnpm run --filter 'figma-plugin-sanity-ui' build",
211
211
  "format": "prettier --write --cache --ignore-unknown .",
212
212
  "lint": "eslint .",
213
- "pkg:build": "mkdirp dist && touch dist/index.css && NODE_ENV=production pkg build --strict",
213
+ "pkg:build": "mkdirp dist/css && touch dist/css/index.css && NODE_ENV=production pkg build --strict",
214
214
  "pkg:check": "pkg --strict",
215
- "pkg:watch": "mkdirp dist && touch dist/index.css && pkg watch --strict",
215
+ "pkg:watch": "mkdirp dist/css && touch dist/css/index.css && NODE_ENV=production pkg watch --strict",
216
216
  "release": "semantic-release",
217
217
  "storybook:build": "pnpm run --filter 'sanity-ui-storybook' build",
218
218
  "storybook:dev": "pnpm run --filter 'sanity-ui-storybook' dev",
219
219
  "test": "vitest run",
220
220
  "test:browser": "start-server-and-test 'run-s workshop:build workshop:preview' http://localhost:1337 'run-s cypress:run'",
221
- "typecheck": "tsc",
221
+ "typecheck": "tsc --build",
222
222
  "watch": "pkg watch --strict",
223
223
  "workshop:build": "workshop build",
224
224
  "workshop:clean": "rimraf .workshop",
@@ -22,9 +22,9 @@ import {EMPTY_ARRAY, EMPTY_RECORD} from '../../constants'
22
22
  import {_raf} from '../../helpers/animation'
23
23
  import {_hasFocus, focusFirstDescendant} from '../../helpers/focus'
24
24
  import {useResponsiveProp} from '../../hooks/useResponsiveProp'
25
+ import {Selectable} from '../../primitives/_selectable'
25
26
  import {Box, type BoxOwnProps} from '../../primitives/box'
26
27
  import {Button} from '../../primitives/button'
27
- import {Card} from '../../primitives/card'
28
28
  import {Popover, type PopoverProps} from '../../primitives/popover'
29
29
  import {AnimatedSpinnerIcon} from '../../primitives/spinner/animatedSpinnerIcon'
30
30
  import {Stack} from '../../primitives/stack'
@@ -157,11 +157,11 @@ export function Autocomplete<
157
157
 
158
158
  const defaultRenderOption = useCallback(
159
159
  ({value}: BaseAutocompleteOption) => (
160
- <Card as="button" padding={paddingProp} radius={2} tone="inherit">
160
+ <Selectable as="button" padding={paddingProp} radius={2}>
161
161
  <Text size={fontSize} textOverflow="ellipsis">
162
162
  {value}
163
163
  </Text>
164
- </Card>
164
+ </Selectable>
165
165
  ),
166
166
  [fontSize, paddingProp],
167
167
  )
@@ -1,7 +1,7 @@
1
1
  import {CloseIcon} from '@sanity/icons'
2
2
  import {type RadiusStyleProps, toast} from '@sanity/ui/css'
3
3
  import {motion, type Variant, type Variants} from 'framer-motion'
4
- import type {ReactNode} from 'react'
4
+ import {type ReactNode} from 'react'
5
5
 
6
6
  import {usePrefersReducedMotion} from '../../hooks/usePrefersReducedMotion'
7
7
  import {Box} from '../../primitives/box'
@@ -12,28 +12,29 @@ import {Stack} from '../../primitives/stack'
12
12
  import {Text} from '../../primitives/text'
13
13
  import type {ComponentType, Props} from '../../types/props'
14
14
 
15
- /** @public */
15
+ /** @internal */
16
16
  export const DEFAULT_TOAST_ELEMENT = 'li'
17
17
 
18
- /** @public */
18
+ /** @internal */
19
19
  export type ToastOwnProps = RadiusStyleProps & {
20
20
  closable?: boolean
21
21
  description?: ReactNode
22
22
  onClose?: () => void
23
23
  title?: ReactNode
24
24
  status?: 'error' | 'warning' | 'success' | 'info'
25
- /** @deprecated */
26
25
  duration?: number
27
- /** @deprecated */
28
- updatedAt?: number
29
26
  }
30
27
 
31
- /** @public */
28
+ /** @internal */
32
29
  export type ToastElementType = 'li' | ComponentType
33
30
 
34
- /** @public */
31
+ /** @internal */
35
32
  export type ToastProps<E extends ToastElementType = ToastElementType> = Props<ToastOwnProps, E>
36
33
 
34
+ // Support pattern used by Sanity Studio, that works around the lack of `duration: Infinity` support in older @sanity/ui versions
35
+ // https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value
36
+ const LONG_ENOUGH_BUT_NOT_TOO_LONG = 1000 * 60 * 60 * 24 * 24
37
+
37
38
  const ROLES = {
38
39
  error: 'alert',
39
40
  warning: 'alert',
@@ -58,9 +59,9 @@ const BUTTON_TONE = {
58
59
  /**
59
60
  * The `Toast` component gives feedback to users when an action has taken place.
60
61
  *
61
- * Toasts can be closed with a close button, or auto-dismiss after a certain timeout.
62
+ * Toasts can be closed with a close button, or auto-dismiss when the duration expires.
62
63
  *
63
- * @public
64
+ * @internal
64
65
  */
65
66
  export function Toast<E extends ToastElementType = typeof DEFAULT_TOAST_ELEMENT>(
66
67
  props: ToastProps<E>,
@@ -69,14 +70,11 @@ export function Toast<E extends ToastElementType = typeof DEFAULT_TOAST_ELEMENT>
69
70
  as = DEFAULT_TOAST_ELEMENT,
70
71
  closable,
71
72
  description,
72
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
73
73
  duration,
74
74
  onClose,
75
75
  radius = 3,
76
76
  title,
77
77
  status,
78
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
79
- updatedAt,
80
78
  ...rest
81
79
  } = props as ToastProps<typeof DEFAULT_TOAST_ELEMENT>
82
80
 
@@ -89,7 +87,7 @@ export function Toast<E extends ToastElementType = typeof DEFAULT_TOAST_ELEMENT>
89
87
  const visualDuration: number = prefersReducedMotion ? 0 : 0.26
90
88
  const transition = visualDuration ? {type: 'spring', visualDuration, bounce: 0.25} : {duration: 0}
91
89
 
92
- // const hasDuration = duration && isFinite(duration) && duration < LONG_ENOUGH_BUT_NOT_TOO_LONG
90
+ const hasDuration = duration && isFinite(duration) && duration < LONG_ENOUGH_BUT_NOT_TOO_LONG
93
91
  const initial: ContainerVariants[] = ['hidden', 'initial']
94
92
  const animate: ContainerVariants[] = ['visible', 'slideIn']
95
93
  const exit: ContainerVariants[] = ['hidden', 'slideOut']
@@ -102,6 +100,7 @@ export function Toast<E extends ToastElementType = typeof DEFAULT_TOAST_ELEMENT>
102
100
  role={role}
103
101
  {...rest}
104
102
  custom={visualDuration}
103
+ data-has-duration={hasDuration ? '' : undefined}
105
104
  radius={radius}
106
105
  shadow={2}
107
106
  tone={cardTone}
@@ -13,7 +13,6 @@ import type {ToastContextValue, ToastParams} from './types'
13
13
  type ToastState = {
14
14
  dismiss: () => void
15
15
  id: string
16
- updatedAt: number
17
16
  params: ToastParams
18
17
  }[]
19
18
 
@@ -31,8 +30,8 @@ export function ToastProvider(props: ToastProviderProps): React.JSX.Element {
31
30
 
32
31
  const value: ToastContextValue = useMemo(() => {
33
32
  const push = (params: ToastParams) => {
34
- const id = params.id || generateToastId()
35
- const duration = params.duration || 5000
33
+ const id = params.id ?? generateToastId()
34
+ const duration = params.duration ?? 5000
36
35
 
37
36
  startTransition(() => {
38
37
  setState((prevState): ToastState => {
@@ -51,10 +50,17 @@ export function ToastProvider(props: ToastProviderProps): React.JSX.Element {
51
50
  * This function will be passed to the Toast component
52
51
  * and called either on close button click or after duration.
53
52
  */
54
- const dismiss = () =>
55
- startTransition(() =>
56
- setState((currentState) => currentState.filter((toast) => toast.id !== id)),
57
- )
53
+ const dismiss = () => {
54
+ startTransition(() => {
55
+ if (timeoutId) {
56
+ clearTimeout(timeoutId)
57
+ }
58
+
59
+ setState((currentState) => currentState.filter((toast) => toast.id !== id))
60
+ })
61
+ }
62
+
63
+ const timeoutId = setTimeout(dismiss, duration)
58
64
 
59
65
  /**
60
66
  * Create updated state by:
@@ -67,7 +73,6 @@ export function ToastProvider(props: ToastProviderProps): React.JSX.Element {
67
73
  {
68
74
  dismiss,
69
75
  id,
70
- updatedAt: Date.now(),
71
76
  params: {...params, duration},
72
77
  },
73
78
  ]
@@ -87,7 +92,7 @@ export function ToastProvider(props: ToastProviderProps): React.JSX.Element {
87
92
  <LayerProvider zOffset={zOffset}>
88
93
  <ToastLayer padding={padding} paddingX={paddingX} paddingY={paddingY} gap={gap}>
89
94
  <AnimatePresence initial={false} mode="popLayout">
90
- {state.map(({dismiss, id, params, updatedAt}) => (
95
+ {state.map(({dismiss, id, params}) => (
91
96
  <Toast
92
97
  key={id}
93
98
  closable={params.closable}
@@ -96,7 +101,6 @@ export function ToastProvider(props: ToastProviderProps): React.JSX.Element {
96
101
  status={params.status}
97
102
  title={params.title}
98
103
  duration={params.duration}
99
- updatedAt={updatedAt}
100
104
  />
101
105
  ))}
102
106
  </AnimatePresence>
package/src/core/index.ts CHANGED
@@ -58,6 +58,7 @@ export * from './primitives/label'
58
58
  export * from './primitives/layer'
59
59
  export * from './primitives/popover'
60
60
  export * from './primitives/radio'
61
+ export * from './primitives/root'
61
62
  export * from './primitives/select'
62
63
  export * from './primitives/skeleton'
63
64
  export * from './primitives/spinner'
@@ -0,0 +1,89 @@
1
+ import {box, root, type RootStyleProps} from '@sanity/ui/css'
2
+ import {useCallback, useImperativeHandle, useRef, useState} from 'react'
3
+
4
+ import {ToastProvider} from '../../components/toast'
5
+ import type {Props} from '../../types/props'
6
+ import {BoundaryElementProvider} from '../../utils/boundaryElement'
7
+ import {PortalProvider} from '../../utils/portal'
8
+ import {Box, type BoxOwnProps} from '../box'
9
+ import {CardProvider} from '../card'
10
+ import {LayerProvider} from '../layer'
11
+
12
+ /** @public */
13
+ export const DEFAULT_ROOT_ELEMENT = 'html'
14
+
15
+ /** @public */
16
+ export interface RootOwnProps extends BoxOwnProps, RootStyleProps {}
17
+
18
+ /** @public */
19
+ export type RootElementType = 'html' | 'body' | 'div'
20
+
21
+ /** @public */
22
+ export type RootProps<E extends RootElementType = RootElementType> = Props<RootOwnProps, E>
23
+
24
+ /**
25
+ * The `Root` component.
26
+ *
27
+ * @public
28
+ */
29
+ export function Root<E extends RootElementType = typeof DEFAULT_ROOT_ELEMENT>(props: RootProps<E>) {
30
+ const {
31
+ as: as = DEFAULT_ROOT_ELEMENT,
32
+ children,
33
+ className,
34
+ height = 'fill',
35
+ ref: forwardedRef,
36
+ scheme = 'light',
37
+ tone = 'transparent',
38
+ ...rest
39
+ } = props as RootProps<typeof DEFAULT_ROOT_ELEMENT>
40
+
41
+ const ref = useRef<HTMLHtmlElement | null>(null)
42
+
43
+ useImperativeHandle<HTMLHtmlElement | null, HTMLHtmlElement | null>(
44
+ forwardedRef,
45
+ () => ref.current,
46
+ )
47
+
48
+ const [portalElement, setPortalElement] = useState<HTMLElement | null>(null)
49
+ const [boundaryElement, setBoundaryElement] = useState<HTMLElement | null>(null)
50
+
51
+ const handleRef = useCallback((el: HTMLHtmlElement | null) => {
52
+ setBoundaryElement(el)
53
+ ref.current = el
54
+ }, [])
55
+
56
+ let node = (
57
+ <LayerProvider>
58
+ <CardProvider scheme={scheme} tone={tone}>
59
+ <ToastProvider>
60
+ <BoundaryElementProvider element={boundaryElement}>
61
+ <PortalProvider element={portalElement}>{children}</PortalProvider>
62
+ </BoundaryElementProvider>
63
+ <div data-portal="" ref={setPortalElement} />
64
+ </ToastProvider>
65
+ </CardProvider>
66
+ </LayerProvider>
67
+ )
68
+
69
+ if (as === 'html') {
70
+ node = <body className={box({height, margin: 0})}>{node}</body>
71
+ }
72
+
73
+ return (
74
+ <Box
75
+ as={as}
76
+ data-ui="Root"
77
+ {...rest}
78
+ className={root({
79
+ className,
80
+ height,
81
+ scheme,
82
+ tone,
83
+ })}
84
+ ref={handleRef}
85
+ >
86
+ {node}
87
+ </Box>
88
+ )
89
+ }
@@ -0,0 +1,5 @@
1
+ import {Root} from '@sanity/ui'
2
+
3
+ export default function BoundaryStory() {
4
+ return <Root>Hello!</Root>
5
+ }
@@ -0,0 +1,8 @@
1
+ import {defineScope} from '@sanity/ui-workshop'
2
+ import {lazy} from 'react'
3
+
4
+ export default defineScope({
5
+ name: 'primitives/radio',
6
+ title: 'Radio',
7
+ stories: [{name: 'boundary', title: 'Boundary', component: lazy(() => import('./boundary'))}],
8
+ })
@@ -0,0 +1 @@
1
+ export * from './Root'
@@ -6,6 +6,7 @@ export const layer = _style(layers.components, {
6
6
  right: 0,
7
7
  bottom: 0,
8
8
  pointerEvents: 'none',
9
+ minWidth: `${320 / 16}rem`, // 320px
9
10
  maxWidth: `${420 / 16}rem`, // 420px
10
11
  width: '100%',
11
12
  })
@@ -25,7 +25,7 @@ import {
25
25
  type ThemeColorVariantKey,
26
26
  TINTS,
27
27
  } from '@sanity/ui/theme'
28
- import {createTheme} from '@vanilla-extract/css'
28
+ import {createTheme, globalFontFace} from '@vanilla-extract/css'
29
29
 
30
30
  import {_fromEntries} from './_fromEntries'
31
31
  import {layers} from './layers.css'
@@ -33,6 +33,59 @@ import type {CSSTheme, CSSThemeColorCard} from './types'
33
33
  import {px, toBoxShadow} from './util'
34
34
  import {paletteVars, themeVars} from './vars.css'
35
35
 
36
+ const fontDisplay: FontDisplay = 'auto'
37
+
38
+ globalFontFace('Inter', [
39
+ {
40
+ src: `url('https://studio-static.sanity.io/Inter-Regular.woff2') format('woff2')`,
41
+ fontDisplay,
42
+ fontStyle: 'normal',
43
+ fontWeight: '400',
44
+ },
45
+ {
46
+ src: `url('https://studio-static.sanity.io/Inter-Italic.woff2') format('woff2')`,
47
+ fontDisplay,
48
+ fontStyle: 'italic',
49
+ fontWeight: '400',
50
+ },
51
+ {
52
+ src: `url('https://studio-static.sanity.io/Inter-Medium.woff2') format('woff2')`,
53
+ fontDisplay,
54
+ fontStyle: 'normal',
55
+ fontWeight: '500',
56
+ },
57
+ {
58
+ src: `url('https://studio-static.sanity.io/Inter-MediumItalic.woff2') format('woff2')`,
59
+ fontDisplay,
60
+ fontStyle: 'italic',
61
+ fontWeight: '500',
62
+ },
63
+ {
64
+ src: `url('https://studio-static.sanity.io/Inter-SemiBold.woff2') format('woff2')`,
65
+ fontDisplay,
66
+ fontStyle: 'normal',
67
+ fontWeight: '600',
68
+ },
69
+ {
70
+ src: `url('https://studio-static.sanity.io/Inter-SemiBoldItalic.woff2') format('woff2')`,
71
+ fontDisplay,
72
+ fontStyle: 'italic',
73
+ fontWeight: '600',
74
+ },
75
+ {
76
+ src: `url('https://studio-static.sanity.io/Inter-Bold.woff2') format('woff2')`,
77
+ fontDisplay,
78
+ fontStyle: 'normal',
79
+ fontWeight: '700',
80
+ },
81
+ {
82
+ src: `url('https://studio-static.sanity.io/Inter-BoldItalic.woff2') format('woff2')`,
83
+ fontDisplay,
84
+ fontStyle: 'italic',
85
+ fontWeight: '700',
86
+ },
87
+ ])
88
+
36
89
  const theme = buildTheme_v3()
37
90
 
38
91
  const themeValue: CSSTheme = {
@@ -253,7 +306,7 @@ const themeValue: CSSTheme = {
253
306
  } as const
254
307
 
255
308
  /** @public */
256
- export const defaultTheme = createTheme(themeVars, themeValue)
309
+ export const themeClassName = createTheme(themeVars, themeValue)
257
310
 
258
311
  function renderColorScheme(options: {scheme: ThemeColorSchemeKey}) {
259
312
  const {scheme} = options
package/src/css/index.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  export * from './layers.css'
4
4
  export * from './vars.css'
5
5
 
6
+ // theme
6
7
  export * from './defaultTheme.css'
7
8
 
8
9
  // props
@@ -61,6 +62,7 @@ export * from './primitives/kbd'
61
62
  export * from './primitives/label'
62
63
  export * from './primitives/popover'
63
64
  export * from './primitives/radio'
65
+ export * from './primitives/root'
64
66
  export * from './primitives/select'
65
67
  export * from './primitives/spinner'
66
68
  export * from './primitives/srOnly'
@@ -0,0 +1,2 @@
1
+ export * from './root'
2
+ export * from './types'
@@ -0,0 +1,15 @@
1
+ import {_style} from '../../_style.css'
2
+ import {layers} from '../../layers.css'
3
+
4
+ export const _root = _style(layers.primitives, {
5
+ minWidth: `${320 / 16}rem`, // 320px
6
+
7
+ selectors: {
8
+ 'html.&': {
9
+ MozTextSizeAdjust: 'none',
10
+ WebkitTextSizeAdjust: 'none',
11
+ textSizeAdjust: 'none',
12
+ WebkitFontSmoothing: 'antialiased',
13
+ },
14
+ },
15
+ })
@@ -0,0 +1,17 @@
1
+ import {_composeClassNames} from '../../_composeClassNames'
2
+ import {themeClassName} from '../../defaultTheme.css'
3
+ import {height} from '../../props/height'
4
+ import {card} from '../card'
5
+ import {_root} from './root.css'
6
+ import type {RootStyleProps} from './types'
7
+
8
+ /** @public */
9
+ export function root(props: RootStyleProps) {
10
+ return _composeClassNames(
11
+ props.className,
12
+ themeClassName,
13
+ _root,
14
+ card({scheme: props.scheme ?? 'light', tone: props.tone ?? 'transparent'}),
15
+ height({height: props.height ?? 'fill'}),
16
+ )
17
+ }
@@ -0,0 +1,10 @@
1
+ import type {ThemeColorCardToneKey, ThemeColorSchemeKey} from '@sanity/ui/theme'
2
+
3
+ import type {HeightStyleProps} from '../../props/height'
4
+
5
+ /** @public */
6
+ export interface RootStyleProps extends HeightStyleProps {
7
+ className?: string
8
+ scheme?: ThemeColorSchemeKey
9
+ tone?: ThemeColorCardToneKey
10
+ }