@sanity/ui 5.0.0-alpha.5 → 5.0.0-alpha.7

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.
@@ -1,19 +1,27 @@
1
1
  import {type PlacementProps, placementProps} from '../../props/placement'
2
2
  import {type PropDef} from '../../types/PropDef'
3
3
 
4
- /** @public */
5
- export interface TooltipProps extends React.ComponentProps<'div'>, PlacementProps {
6
- /** Tooltip text */
7
- text?: React.ReactNode
8
- /** Disabled state */
9
- disabled?: boolean
4
+ /** @beta */
5
+ export interface TooltipProps
6
+ extends Omit<React.ComponentProps<'div'>, 'children' | 'content'>, PlacementProps {
7
+ /** Anchor name for positioning */
8
+ anchorName?: React.ReactNode
9
+ /** Focusable trigger element */
10
+ children: React.ReactElement<Record<string, unknown>>
11
+ /** Tooltip content */
12
+ content?: React.ReactNode
13
+ /** Render tooltip in portal */
14
+ portal?: boolean
10
15
  }
11
16
 
12
17
  export const tooltipProps: Record<string, PropDef> = {
13
- text: {
18
+ anchorName: {
14
19
  type: 'string',
15
20
  },
16
- disabled: {
21
+ content: {
22
+ type: 'string',
23
+ },
24
+ portal: {
17
25
  type: 'boolean',
18
26
  },
19
27
  ...placementProps,
@@ -23,10 +23,7 @@ export function TooltipGroup<T extends ElementType = 'div'>(
23
23
  return
24
24
  }
25
25
 
26
- if (
27
- e.propertyName === 'visibility' &&
28
- window.getComputedStyle(tooltip).visibility !== 'hidden'
29
- ) {
26
+ if (e.propertyName === 'opacity' && window.getComputedStyle(tooltip).opacity !== '0') {
30
27
  setIsActive(true)
31
28
  }
32
29
  }
@@ -49,7 +46,7 @@ export function TooltipGroup<T extends ElementType = 'div'>(
49
46
  style={{
50
47
  ...style,
51
48
  ...(isActive && {
52
- '--tooltip-delay-group': 0,
49
+ '--tooltip-delay-group': '0ms',
53
50
  }),
54
51
  }}
55
52
  data-ui="TooltipGroup"
@@ -1,3 +1,8 @@
1
+ [class*='placement-'] {
2
+ inset: auto;
3
+ margin: 0;
4
+ }
5
+
1
6
  @position-try --top-center {
2
7
  top: auto;
3
8
  right: auto;
@@ -169,3 +174,29 @@
169
174
  bottom: anchor(bottom);
170
175
  position-try-fallbacks: --left-end;
171
176
  }
177
+
178
+ [class*="placement-bottom"],
179
+ [class*="placement-top"] {
180
+ margin-block: var(--space-1);
181
+ }
182
+
183
+ [class*="placement-left"],
184
+ [class*="placement-right"] {
185
+ margin-inline: var(--space-1);
186
+ }
187
+
188
+ [class*="placement-bottom"] {
189
+ transform-origin: center top;
190
+ }
191
+
192
+ [class*="placement-top"] {
193
+ transform-origin: center bottom;
194
+ }
195
+
196
+ [class*="placement-left"] {
197
+ transform-origin: right center;
198
+ }
199
+
200
+ [class*="placement-right"] {
201
+ transform-origin: left center;
202
+ }
@@ -18,4 +18,8 @@
18
18
  --hover-transition: border-color 100ms linear, background-color 100ms linear;
19
19
  --inert-background: light-dark(var(--gray-50), var(--gray-950));
20
20
  --inert-foreground: light-dark(var(--gray-400), var(--gray-600));
21
+ --fade-in-transition:
22
+ opacity 200ms cubic-bezier(0.34, 1.2, 0.64, 1),
23
+ scale 200ms cubic-bezier(0.34, 1.2, 0.64, 1), visibility 0s linear,
24
+ display 200ms linear allow-discrete;
21
25
  }
@@ -0,0 +1,10 @@
1
+ import {useSyncExternalStore} from 'react'
2
+
3
+ /** @public */
4
+ export function useIsClient() {
5
+ return useSyncExternalStore(
6
+ () => () => {},
7
+ () => true,
8
+ () => false,
9
+ )
10
+ }
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export * from './components/card/Card'
4
4
  export * from './components/checkbox/Checkbox'
5
5
  export * from './components/code/Code'
6
6
  export * from './components/container/Container'
7
+ export * from './components/dialog/Dialog'
7
8
  export * from './components/divider/Divider'
8
9
  export * from './components/eyebrow/Eyebrow'
9
10
  export * from './components/flex/Flex'
@@ -18,7 +19,7 @@ export * from './components/inline/Inline'
18
19
  export * from './components/label/Label'
19
20
  export * from './components/link/Link'
20
21
  export * from './components/list/List'
21
- export * from './components/modal/Modal'
22
+ export * from './components/popover/Popover'
22
23
  export * from './components/press-area/PressArea'
23
24
  export * from './components/radio/Radio'
24
25
  export * from './components/skip-to-content/SkipToContent'
@@ -30,6 +31,8 @@ export * from './components/tooltip-group/TooltipGroup'
30
31
  export * from './components/v-stack/VStack'
31
32
  export * from './components/visually-hidden/VisuallyHidden'
32
33
 
34
+ export * from './hooks/useIsClient'
35
+
33
36
  export type {FlexParentProps} from './props/flexParent'
34
37
  export type {GapProps} from './props/gap'
35
38
  export type {GridParentProps} from './props/gridParent'
@@ -0,0 +1,15 @@
1
+ import {
2
+ apply as applyClosedByPolyfill,
3
+ isSupported as isClosedBySupported,
4
+ } from 'dialog-closedby-polyfill'
5
+
6
+ if (
7
+ typeof HTMLButtonElement !== 'undefined' &&
8
+ !('interestForElement' in HTMLButtonElement.prototype)
9
+ ) {
10
+ void import('interestfor')
11
+ }
12
+
13
+ if (typeof HTMLDialogElement !== 'undefined' && !isClosedBySupported()) {
14
+ applyClosedByPolyfill()
15
+ }
@@ -0,0 +1,66 @@
1
+ function isReactEventHandler(key: string, value: unknown): value is (e: never) => void {
2
+ return /^on[A-Z]/.test(key) && typeof value === 'function'
3
+ }
4
+
5
+ function chainEventHandlers<E>(
6
+ ...handlers: (((e: E) => void) | undefined)[]
7
+ ): ((e: E) => void) | undefined {
8
+ const defined = handlers.filter(Boolean) as ((e: E) => void)[]
9
+
10
+ if (defined.length === 0) {
11
+ return undefined
12
+ }
13
+
14
+ if (defined.length === 1) {
15
+ return defined[0]
16
+ }
17
+
18
+ return (e: E) => {
19
+ for (const handler of defined) {
20
+ handler(e)
21
+ }
22
+ }
23
+ }
24
+
25
+ export function mergeTriggerProps(
26
+ childProps: Record<string, unknown>,
27
+ forwardedProps?: Record<string, unknown>,
28
+ ownProps?: Record<string, unknown>,
29
+ ) {
30
+ const result: Record<string, unknown> = {}
31
+
32
+ for (const source of [forwardedProps, ownProps]) {
33
+ if (!source) {
34
+ continue
35
+ }
36
+
37
+ for (const [key, value] of Object.entries(source)) {
38
+ if (key === 'style') {
39
+ continue
40
+ }
41
+
42
+ if (isReactEventHandler(key, value)) {
43
+ result[key] = chainEventHandlers(result[key] as (e: never) => void, value)
44
+ continue
45
+ }
46
+
47
+ if (value !== undefined) {
48
+ result[key] = value
49
+ }
50
+ }
51
+ }
52
+
53
+ result['style'] = {
54
+ ...(childProps['style'] as object),
55
+ ...(forwardedProps?.['style'] as object),
56
+ ...(ownProps?.['style'] as object),
57
+ }
58
+
59
+ for (const [key, value] of Object.entries(childProps)) {
60
+ if (isReactEventHandler(key, value)) {
61
+ result[key] = chainEventHandlers(result[key] as (e: never) => void, value)
62
+ }
63
+ }
64
+
65
+ return result
66
+ }
@@ -0,0 +1,14 @@
1
+ import {type ReactNode} from 'react'
2
+ import {createPortal} from 'react-dom'
3
+
4
+ export function renderPortal(node: ReactNode, mounted: boolean, portal?: boolean) {
5
+ if (!portal) {
6
+ return node
7
+ }
8
+
9
+ if (!mounted) {
10
+ return null
11
+ }
12
+
13
+ return createPortal(node, document.body)
14
+ }
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // This file is generated by scripts/write-version.js. Do not edit.
2
- export const VERSION = '500alpha5'
3
- export const HASH = '024922'
2
+ export const VERSION = '500alpha7'
3
+ export const HASH = '47d232'
@@ -1,49 +0,0 @@
1
- .sui-Modal {
2
- min-width: 320px;
3
- max-width: 80dvw;
4
- max-height: 80dvh;
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-Modal:not([open]) {
13
- opacity: 0;
14
- scale: 0.9;
15
- }
16
-
17
- .sui-Modal[open] {
18
- display: flex;
19
- opacity: 1;
20
- scale: 1;
21
- }
22
-
23
- .sui-Modal::backdrop {
24
- --modal-backdrop: hsl(from var(--separator-high) h s l / var(--overlay-opacity));
25
- background-color: var(--modal-backdrop);
26
- transition:
27
- display 150ms allow-discrete,
28
- overlay 150ms allow-discrete,
29
- opacity 150ms var(--timing-linear);
30
- }
31
-
32
- .sui-Modal:not([open])::backdrop {
33
- opacity: 0;
34
- }
35
-
36
- .sui-Modal[open]::backdrop {
37
- opacity: 1;
38
- }
39
-
40
- @starting-style {
41
- .sui-Modal[open] {
42
- opacity: 0;
43
- scale: 0.9;
44
- }
45
-
46
- .sui-Modal[open]::backdrop {
47
- opacity: 0;
48
- }
49
- }
@@ -1,20 +0,0 @@
1
- import {type PropDef} from '../../types/PropDef'
2
-
3
- /** @beta */
4
- export interface ModalProps extends Omit<React.ComponentProps<'dialog'>, 'open'> {
5
- /** Text to be displayed as the modal's heading; optional but recommended for optimal accessibility and presentation */
6
- header?: string
7
- /** Used to set the value of open to false. Fires whenever the modal is closed, either programmatically or by the user (via Esc key, clicking background, clicking the modal's close button, or any other means.) */
8
- onClose: React.ReactEventHandler<HTMLDialogElement>
9
- /** Whether the modal is open; defaults to false. */
10
- open?: boolean
11
- }
12
-
13
- export const modalProps: Record<string, PropDef> = {
14
- header: {
15
- type: 'string',
16
- },
17
- open: {
18
- type: 'boolean',
19
- },
20
- }