@sanity/ui 4.0.0-static.34 → 4.0.0-static.36
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/dist/index.d.ts +6 -6
- package/dist/index.js +75 -50
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/core/components/dialog/Dialog.tsx +3 -2
- package/src/core/components/dialog/DialogCard.tsx +4 -4
- package/src/core/index.ts +4 -4
- package/src/core/primitives/card/useCard.ts +8 -3
- package/src/core/primitives/popover/Popover.tsx +20 -15
- package/src/core/primitives/tooltip/Tooltip.tsx +30 -20
- package/src/core/utils/boundaryElement/BoundaryElementContext.ts +2 -5
- package/src/core/utils/boundaryElement/BoundaryElementProvider.tsx +2 -4
- package/src/core/utils/boundaryElement/types.ts +1 -4
- package/src/core/utils/boundaryElement/useBoundaryElement.ts +6 -18
- package/src/core/utils/portal/Portal.tsx +6 -3
- package/src/core/utils/portal/usePortal.ts +9 -2
- package/src/core/utils/boundaryElement/BoundaryElement.test.tsx +0 -102
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/ui",
|
|
3
|
-
"version": "4.0.0-static.
|
|
3
|
+
"version": "4.0.0-static.36",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"react",
|
|
6
6
|
"ui",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"@sanity/icons": "^3.7.4",
|
|
120
120
|
"@vanilla-extract/dynamic": "^2.1.5",
|
|
121
121
|
"csstype": "^3.1.3",
|
|
122
|
-
"framer-motion": "^12.23.
|
|
122
|
+
"framer-motion": "^12.23.21",
|
|
123
123
|
"react-refractor": "^4.0.0",
|
|
124
124
|
"use-effect-event": "^2.0.3"
|
|
125
125
|
},
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
"react-dom": "^19",
|
|
197
197
|
"react-is": "^19"
|
|
198
198
|
},
|
|
199
|
-
"packageManager": "pnpm@10.17.
|
|
199
|
+
"packageManager": "pnpm@10.17.1",
|
|
200
200
|
"engines": {
|
|
201
201
|
"node": ">=20.19 <22 || >=22.12"
|
|
202
202
|
},
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type FocusEvent,
|
|
10
10
|
type ForwardedRef,
|
|
11
11
|
type ReactNode,
|
|
12
|
+
use,
|
|
12
13
|
useCallback,
|
|
13
14
|
useMemo,
|
|
14
15
|
useRef,
|
|
@@ -22,7 +23,7 @@ import {usePrefersReducedMotion} from '../../hooks/usePrefersReducedMotion'
|
|
|
22
23
|
import {Container} from '../../primitives/container/Container'
|
|
23
24
|
import {Layer, type LayerOwnProps, type LayerProps} from '../../primitives/layer/Layer'
|
|
24
25
|
import type {ComponentType, Props} from '../../types'
|
|
25
|
-
import {
|
|
26
|
+
import {BoundaryElementContext} from '../../utils/boundaryElement/BoundaryElementContext'
|
|
26
27
|
import {Portal} from '../../utils/portal/Portal'
|
|
27
28
|
import {usePortal} from '../../utils/portal/usePortal'
|
|
28
29
|
import {DialogCard} from './DialogCard'
|
|
@@ -115,7 +116,7 @@ export function Dialog<E extends DialogElementType = typeof DEFAULT_DIALOG_ELEME
|
|
|
115
116
|
const animate = prefersReducedMotion ? false : _animate
|
|
116
117
|
const portal = usePortal()
|
|
117
118
|
const portalElement = portalProp ? portal.elements?.[portalProp] || null : portal.element
|
|
118
|
-
const boundaryElement =
|
|
119
|
+
const boundaryElement = use(BoundaryElementContext)
|
|
119
120
|
const cardRadius = useMemo(() => _getResponsiveProp(cardRadiusProp), [cardRadiusProp])
|
|
120
121
|
const position = useMemo(() => _getResponsiveProp(positionProp), [positionProp])
|
|
121
122
|
const zOffset = useMemo(() => _getResponsiveProp(zOffsetProp), [zOffsetProp])
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
type ForwardedRef,
|
|
13
13
|
type ReactNode,
|
|
14
|
+
use,
|
|
14
15
|
useCallback,
|
|
15
16
|
useEffect,
|
|
16
17
|
useImperativeHandle,
|
|
@@ -28,7 +29,7 @@ import {Flex} from '../../primitives/flex/Flex'
|
|
|
28
29
|
import {useLayer} from '../../primitives/layer/useLayer'
|
|
29
30
|
import {Text} from '../../primitives/text/Text'
|
|
30
31
|
import type {Props} from '../../types'
|
|
31
|
-
import {
|
|
32
|
+
import {BoundaryElementContext} from '../../utils/boundaryElement/BoundaryElementContext'
|
|
32
33
|
import {usePortal} from '../../utils/portal/usePortal'
|
|
33
34
|
import {isTargetWithinScope} from './isTargetWithinScope'
|
|
34
35
|
|
|
@@ -87,11 +88,10 @@ export function DialogCard<E extends DialogCardElementType = typeof DEFAULT_DIAL
|
|
|
87
88
|
|
|
88
89
|
const portal = usePortal()
|
|
89
90
|
const portalElement = portalProp ? portal.elements?.[portalProp] || null : portal.element
|
|
90
|
-
const boundaryElement =
|
|
91
|
+
const boundaryElement = use(BoundaryElementContext)
|
|
91
92
|
const ref = useRef<HTMLDivElement | null>(null)
|
|
92
93
|
const contentRef = useRef<HTMLDivElement | null>(null)
|
|
93
|
-
const
|
|
94
|
-
const {isTopLayer} = layer
|
|
94
|
+
const {isTopLayer} = useLayer()
|
|
95
95
|
const labelId = `${id}_label`
|
|
96
96
|
const showCloseButton = Boolean(onClose) && hideCloseButton === false
|
|
97
97
|
const showHeader = Boolean(header) || showCloseButton
|
package/src/core/index.ts
CHANGED
|
@@ -60,7 +60,7 @@ export * from './primitives/button/types'
|
|
|
60
60
|
export * from './primitives/card/Card'
|
|
61
61
|
export * from './primitives/card/CardProvider'
|
|
62
62
|
export * from './primitives/card/types'
|
|
63
|
-
export
|
|
63
|
+
export {useCard} from './primitives/card/useCard'
|
|
64
64
|
export * from './primitives/checkbox/Checkbox'
|
|
65
65
|
export * from './primitives/code/Code'
|
|
66
66
|
export * from './primitives/container/Container'
|
|
@@ -96,18 +96,18 @@ export * from './primitives/tooltip/Tooltip'
|
|
|
96
96
|
export * from './primitives/tooltip/tooltipDelayGroup/TooltipDelayGroupContext'
|
|
97
97
|
export * from './primitives/tooltip/tooltipDelayGroup/TooltipDelayGroupProvider'
|
|
98
98
|
export * from './primitives/tooltip/tooltipDelayGroup/types'
|
|
99
|
-
export
|
|
99
|
+
export {useTooltipDelayGroup} from './primitives/tooltip/tooltipDelayGroup/useTooltipDelayGroup'
|
|
100
100
|
|
|
101
101
|
// utils
|
|
102
102
|
export * from './utils/boundaryElement/BoundaryElementProvider'
|
|
103
103
|
export * from './utils/boundaryElement/types'
|
|
104
|
-
export
|
|
104
|
+
export {useBoundaryElement} from './utils/boundaryElement/useBoundaryElement'
|
|
105
105
|
export * from './utils/elementQuery/ElementQuery'
|
|
106
106
|
export * from './utils/ErrorBoundary'
|
|
107
107
|
export * from './utils/portal/Portal'
|
|
108
108
|
export * from './utils/portal/PortalProvider'
|
|
109
109
|
export * from './utils/portal/types'
|
|
110
|
-
export
|
|
110
|
+
export {usePortal} from './utils/portal/usePortal'
|
|
111
111
|
|
|
112
112
|
// root
|
|
113
113
|
export * from './Root'
|
|
@@ -7,9 +7,14 @@ import type {CardContextValue} from './types'
|
|
|
7
7
|
export function useCard(): CardContextValue {
|
|
8
8
|
const card = use(CardContext)
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
throw new Error('useCard(): missing context value')
|
|
12
|
-
}
|
|
10
|
+
assertCardContext(card)
|
|
13
11
|
|
|
14
12
|
return card
|
|
15
13
|
}
|
|
14
|
+
|
|
15
|
+
/** @internal */
|
|
16
|
+
export function assertCardContext(card: CardContextValue | null): asserts card is CardContextValue {
|
|
17
|
+
if (!card) {
|
|
18
|
+
throw new Error('useCard(): missing context value', {cause: card})
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
type ReactElement,
|
|
19
19
|
type ReactNode,
|
|
20
20
|
type Ref,
|
|
21
|
+
use,
|
|
21
22
|
useCallback,
|
|
22
23
|
useImperativeHandle,
|
|
23
24
|
useMemo,
|
|
@@ -29,11 +30,12 @@ import {_getResponsiveProp} from '../../helpers/props'
|
|
|
29
30
|
import {usePrefersReducedMotion} from '../../hooks/usePrefersReducedMotion'
|
|
30
31
|
import {origin} from '../../middleware/origin'
|
|
31
32
|
import type {ComponentType, Placement, Props} from '../../types'
|
|
32
|
-
import {
|
|
33
|
+
import {BoundaryElementContext} from '../../utils/boundaryElement/BoundaryElementContext'
|
|
33
34
|
import {getElementRef} from '../../utils/getElementRef'
|
|
34
35
|
import {Portal} from '../../utils/portal/Portal'
|
|
36
|
+
import {CardContext} from '../card/CardContext'
|
|
35
37
|
import {CardProvider} from '../card/CardProvider'
|
|
36
|
-
import {
|
|
38
|
+
import {assertCardContext} from '../card/useCard'
|
|
37
39
|
import {type LayerOwnProps} from '../layer/Layer'
|
|
38
40
|
import {useLayer} from '../layer/useLayer'
|
|
39
41
|
import {
|
|
@@ -153,8 +155,6 @@ function ViewportOverlay() {
|
|
|
153
155
|
export function Popover<E extends PopoverElementType = typeof DEFAULT_POPOVER_ELEMENT>(
|
|
154
156
|
props: PopoverProps<E>,
|
|
155
157
|
): React.JSX.Element {
|
|
156
|
-
const boundaryElementContext = useBoundaryElement()
|
|
157
|
-
|
|
158
158
|
const {
|
|
159
159
|
__unstable_margins: margins = DEFAULT_POPOVER_MARGINS,
|
|
160
160
|
animate: _animate = false,
|
|
@@ -191,10 +191,8 @@ export function Popover<E extends PopoverElementType = typeof DEFAULT_POPOVER_EL
|
|
|
191
191
|
} = props as PopoverProps<typeof DEFAULT_POPOVER_ELEMENT>
|
|
192
192
|
const fallbackPlacements =
|
|
193
193
|
_fallbackPlacements ?? DEFAULT_FALLBACK_PLACEMENTS[props.placement ?? 'bottom']
|
|
194
|
-
const floatingBoundary = _floatingBoundary ??
|
|
195
|
-
const referenceBoundary = _referenceBoundary ??
|
|
196
|
-
|
|
197
|
-
const card = useCard()
|
|
194
|
+
const floatingBoundary = _floatingBoundary ?? use(BoundaryElementContext)
|
|
195
|
+
const referenceBoundary = _referenceBoundary ?? use(BoundaryElementContext)
|
|
198
196
|
|
|
199
197
|
const prefersReducedMotion = usePrefersReducedMotion()
|
|
200
198
|
const animate = prefersReducedMotion ? false : _animate
|
|
@@ -309,17 +307,24 @@ export function Popover<E extends PopoverElementType = typeof DEFAULT_POPOVER_EL
|
|
|
309
307
|
</>
|
|
310
308
|
)
|
|
311
309
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
310
|
+
let children = open ? node : null
|
|
311
|
+
|
|
312
|
+
if (open && portal) {
|
|
313
|
+
let resolvedTone = tone as Exclude<typeof tone, 'inherit'>
|
|
314
|
+
if (tone === 'inherit') {
|
|
315
|
+
const card = use(CardContext)
|
|
316
|
+
assertCardContext(card)
|
|
317
|
+
resolvedTone = card.tone
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
children = (
|
|
315
321
|
<Portal __unstable_name={typeof portal === 'string' ? portal : undefined}>
|
|
316
|
-
<CardProvider tone={
|
|
322
|
+
<CardProvider tone={resolvedTone} scheme={scheme ?? 'light'}>
|
|
317
323
|
{node}
|
|
318
324
|
</CardProvider>
|
|
319
325
|
</Portal>
|
|
320
|
-
)
|
|
321
|
-
|
|
322
|
-
))
|
|
326
|
+
)
|
|
327
|
+
}
|
|
323
328
|
|
|
324
329
|
return (
|
|
325
330
|
<>
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type ReactElement,
|
|
20
20
|
type ReactNode,
|
|
21
21
|
type RefAttributes,
|
|
22
|
+
use,
|
|
22
23
|
useCallback,
|
|
23
24
|
useEffect,
|
|
24
25
|
useId,
|
|
@@ -35,20 +36,21 @@ import {useDelayedState} from '../../hooks/useDelayedState'
|
|
|
35
36
|
import {usePrefersReducedMotion} from '../../hooks/usePrefersReducedMotion'
|
|
36
37
|
import {origin} from '../../middleware/origin'
|
|
37
38
|
import type {ComponentType, Delay, Placement, Props} from '../../types'
|
|
38
|
-
import {
|
|
39
|
+
import {BoundaryElementContext} from '../../utils/boundaryElement/BoundaryElementContext'
|
|
39
40
|
import {getElementRef} from '../../utils/getElementRef'
|
|
40
41
|
import {Portal} from '../../utils/portal/Portal'
|
|
41
|
-
import {
|
|
42
|
+
import {PortalContext} from '../../utils/portal/PortalContext'
|
|
43
|
+
import {assertPortalContext} from '../../utils/portal/usePortal'
|
|
44
|
+
import {CardContext} from '../card/CardContext'
|
|
42
45
|
import {CardProvider} from '../card/CardProvider'
|
|
43
|
-
import {
|
|
46
|
+
import {assertCardContext} from '../card/useCard'
|
|
44
47
|
import type {LayerOwnProps} from '../layer/Layer'
|
|
45
48
|
import {
|
|
46
49
|
DEFAULT_FALLBACK_PLACEMENTS,
|
|
47
50
|
DEFAULT_TOOLTIP_DISTANCE,
|
|
48
51
|
DEFAULT_TOOLTIP_PADDING,
|
|
49
52
|
} from './constants'
|
|
50
|
-
import {
|
|
51
|
-
// import {useTooltipDelayGroup} from './TooltipDelayGroup'
|
|
53
|
+
import {TooltipDelayGroupContext} from './tooltipDelayGroup/TooltipDelayGroupContext'
|
|
52
54
|
import {TooltipLayer} from './TooltipLayer'
|
|
53
55
|
|
|
54
56
|
/** @public */
|
|
@@ -106,8 +108,6 @@ export type TooltipProps<E extends TooltipElementType = TooltipElementType> = Pr
|
|
|
106
108
|
export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_ELEMENT>(
|
|
107
109
|
props: TooltipProps<E>,
|
|
108
110
|
): React.JSX.Element {
|
|
109
|
-
const boundaryElementContext = useBoundaryElement()
|
|
110
|
-
|
|
111
111
|
const {
|
|
112
112
|
animate: _animate = false,
|
|
113
113
|
arrow: arrowProp = false,
|
|
@@ -130,11 +130,10 @@ export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_EL
|
|
|
130
130
|
tone = 'inherit',
|
|
131
131
|
...rest
|
|
132
132
|
} = props as TooltipProps<typeof DEFAULT_TOOLTIP_ELEMENT>
|
|
133
|
-
const boundaryElement = _boundaryElement ??
|
|
133
|
+
const boundaryElement = _boundaryElement ?? use(BoundaryElementContext)
|
|
134
134
|
const fallbackPlacements =
|
|
135
135
|
_fallbackPlacements ?? DEFAULT_FALLBACK_PLACEMENTS[props.placement ?? 'bottom']
|
|
136
136
|
|
|
137
|
-
const card = useCard()
|
|
138
137
|
const prefersReducedMotion = usePrefersReducedMotion()
|
|
139
138
|
const animate = prefersReducedMotion ? false : _animate
|
|
140
139
|
const ref = useRef<HTMLDivElement | null>(null)
|
|
@@ -145,9 +144,13 @@ export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_EL
|
|
|
145
144
|
|
|
146
145
|
useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
let portalElement: HTMLElement | null = null
|
|
148
|
+
if (portalProp) {
|
|
149
|
+
const portal = use(PortalContext)
|
|
150
|
+
assertPortalContext(portal)
|
|
151
|
+
portalElement =
|
|
152
|
+
typeof portalProp === 'string' ? portal.elements?.[portalProp] || null : portal.element
|
|
153
|
+
}
|
|
151
154
|
|
|
152
155
|
const middleware = useMiddleware({
|
|
153
156
|
animate,
|
|
@@ -174,7 +177,7 @@ export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_EL
|
|
|
174
177
|
|
|
175
178
|
const tooltipId = useId()
|
|
176
179
|
const [isOpen, setIsOpen] = useDelayedState(false)
|
|
177
|
-
const delayGroupContext =
|
|
180
|
+
const delayGroupContext = use(TooltipDelayGroupContext)
|
|
178
181
|
const {setIsGroupActive, setOpenTooltipId} = delayGroupContext || {}
|
|
179
182
|
const showTooltip = isOpen || delayGroupContext?.openTooltipId === tooltipId
|
|
180
183
|
|
|
@@ -385,17 +388,24 @@ export function Tooltip<E extends TooltipElementType = typeof DEFAULT_TOOLTIP_EL
|
|
|
385
388
|
</TooltipLayer>
|
|
386
389
|
)
|
|
387
390
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
+
let children = showTooltip ? node : null
|
|
392
|
+
|
|
393
|
+
if (showTooltip && portalProp) {
|
|
394
|
+
let resolvedTone = tone as Exclude<typeof tone, 'inherit'>
|
|
395
|
+
if (tone === 'inherit') {
|
|
396
|
+
const card = use(CardContext)
|
|
397
|
+
assertCardContext(card)
|
|
398
|
+
resolvedTone = card.tone
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
children = (
|
|
391
402
|
<Portal __unstable_name={typeof portalProp === 'string' ? portalProp : undefined}>
|
|
392
|
-
<CardProvider tone={
|
|
403
|
+
<CardProvider tone={resolvedTone} scheme={scheme ?? 'light'}>
|
|
393
404
|
{node}
|
|
394
405
|
</CardProvider>
|
|
395
406
|
</Portal>
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
))
|
|
407
|
+
)
|
|
408
|
+
}
|
|
399
409
|
|
|
400
410
|
return (
|
|
401
411
|
<>
|
|
@@ -3,8 +3,5 @@ import type {Context} from 'react'
|
|
|
3
3
|
import {createGlobalScopedContext} from '../../lib/createGlobalScopedContext'
|
|
4
4
|
import type {BoundaryElementContextValue} from './types'
|
|
5
5
|
|
|
6
|
-
export const BoundaryElementContext: Context<BoundaryElementContextValue
|
|
7
|
-
createGlobalScopedContext<BoundaryElementContextValue
|
|
8
|
-
'@sanity/ui/v4/boundaryElement',
|
|
9
|
-
null,
|
|
10
|
-
)
|
|
6
|
+
export const BoundaryElementContext: Context<BoundaryElementContextValue> =
|
|
7
|
+
createGlobalScopedContext<BoundaryElementContextValue>('@sanity/ui/v4/boundaryElement', null)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {type ReactNode
|
|
1
|
+
import {type ReactNode} from 'react'
|
|
2
2
|
|
|
3
3
|
import {BoundaryElementContext} from './BoundaryElementContext'
|
|
4
|
-
import type {BoundaryElementContextValue} from './types'
|
|
5
4
|
|
|
6
5
|
/** @public */
|
|
7
6
|
export interface BoundaryElementProviderProps {
|
|
@@ -12,7 +11,6 @@ export interface BoundaryElementProviderProps {
|
|
|
12
11
|
/** @public */
|
|
13
12
|
export function BoundaryElementProvider(props: BoundaryElementProviderProps): React.JSX.Element {
|
|
14
13
|
const {children, element} = props
|
|
15
|
-
const value: BoundaryElementContextValue = useMemo(() => ({version: 0.0, element}), [element])
|
|
16
14
|
|
|
17
|
-
return <BoundaryElementContext value={
|
|
15
|
+
return <BoundaryElementContext value={element}>{children}</BoundaryElementContext>
|
|
18
16
|
}
|
|
@@ -1,24 +1,12 @@
|
|
|
1
|
-
import {use} from 'react'
|
|
1
|
+
import {use, useMemo} from 'react'
|
|
2
2
|
|
|
3
|
-
import {isRecord} from '../../lib/isRecord'
|
|
4
3
|
import {BoundaryElementContext} from './BoundaryElementContext'
|
|
5
4
|
import type {BoundaryElementContextValue} from './types'
|
|
6
5
|
|
|
7
|
-
const DEFAULT_VALUE: BoundaryElementContextValue = {
|
|
8
|
-
version: 0.0,
|
|
9
|
-
element: null,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
6
|
/** @public */
|
|
13
|
-
export function useBoundaryElement(): BoundaryElementContextValue {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// - If the value is an object, but doesn’t have `version: 0.0`, it’s not compatible with the current version
|
|
19
|
-
if (value && (!isRecord(value) || value.version !== 0.0)) {
|
|
20
|
-
throw new Error('useBoundaryElement(): the context value is not compatible')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return value || DEFAULT_VALUE
|
|
7
|
+
export function useBoundaryElement(): {version: 0.0; element: BoundaryElementContextValue} {
|
|
8
|
+
// eslint-disable-next-line no-warning-comments
|
|
9
|
+
// @TODO this wrapping is for BC and should be removed
|
|
10
|
+
const element = use(BoundaryElementContext)
|
|
11
|
+
return useMemo(() => ({version: 0.0, element}), [element])
|
|
24
12
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import {type ReactNode, type ReactPortal, use} from 'react'
|
|
2
2
|
import {createPortal} from 'react-dom'
|
|
3
3
|
|
|
4
|
+
import {CardContext} from '../../primitives/card/CardContext'
|
|
4
5
|
import {CardProvider} from '../../primitives/card/CardProvider'
|
|
5
|
-
import {
|
|
6
|
+
import {assertCardContext} from '../../primitives/card/useCard'
|
|
6
7
|
import {usePortal} from './usePortal'
|
|
7
8
|
|
|
8
9
|
/** @public */
|
|
@@ -17,7 +18,6 @@ export interface PortalProps {
|
|
|
17
18
|
/** @public */
|
|
18
19
|
export function Portal(props: PortalProps): ReactPortal | null {
|
|
19
20
|
const {children, __unstable_name: name} = props
|
|
20
|
-
const card = useCard()
|
|
21
21
|
const portal = usePortal()
|
|
22
22
|
const portalElement =
|
|
23
23
|
(name ? portal.elements && portal.elements[name] : portal.element) ||
|
|
@@ -27,6 +27,9 @@ export function Portal(props: PortalProps): ReactPortal | null {
|
|
|
27
27
|
return null
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
const card = use(CardContext)
|
|
31
|
+
assertCardContext(card)
|
|
32
|
+
|
|
30
33
|
return createPortal(
|
|
31
34
|
<CardProvider tone="transparent" scheme={card.scheme}>
|
|
32
35
|
{children}
|
|
@@ -8,6 +8,15 @@ import type {PortalContextValue} from './types'
|
|
|
8
8
|
export function usePortal(): PortalContextValue {
|
|
9
9
|
const value = use(PortalContext)
|
|
10
10
|
|
|
11
|
+
assertPortalContext(value)
|
|
12
|
+
|
|
13
|
+
return value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @internal */
|
|
17
|
+
export function assertPortalContext(
|
|
18
|
+
value: PortalContextValue | null,
|
|
19
|
+
): asserts value is PortalContextValue {
|
|
11
20
|
if (!value) {
|
|
12
21
|
throw new Error('usePortal(): missing context value')
|
|
13
22
|
}
|
|
@@ -18,6 +27,4 @@ export function usePortal(): PortalContextValue {
|
|
|
18
27
|
if (!isRecord(value) || value.version !== 0.0) {
|
|
19
28
|
throw new Error('usePortal(): the context value is not compatible')
|
|
20
29
|
}
|
|
21
|
-
|
|
22
|
-
return value
|
|
23
30
|
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import {describe, expect, it, vi} from 'vitest'
|
|
2
|
-
|
|
3
|
-
import {render} from '$test/utils'
|
|
4
|
-
|
|
5
|
-
import {BoundaryElementContext} from './BoundaryElementContext'
|
|
6
|
-
import type {BoundaryElementContextValue} from './types'
|
|
7
|
-
import {useBoundaryElement} from './useBoundaryElement'
|
|
8
|
-
|
|
9
|
-
describe('utils/boundaryElement', () => {
|
|
10
|
-
describe('useBoundaryElement', () => {
|
|
11
|
-
it('should get context value', async () => {
|
|
12
|
-
const log = vi.fn()
|
|
13
|
-
|
|
14
|
-
function Debug() {
|
|
15
|
-
const rootBoundaryElement = useBoundaryElement()
|
|
16
|
-
|
|
17
|
-
log(rootBoundaryElement)
|
|
18
|
-
|
|
19
|
-
return <>debug</>
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function Root() {
|
|
23
|
-
const value: BoundaryElementContextValue = {
|
|
24
|
-
version: 0.0,
|
|
25
|
-
element: null,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<BoundaryElementContext value={value}>
|
|
30
|
-
<Debug />
|
|
31
|
-
</BoundaryElementContext>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
render(<Root />)
|
|
36
|
-
|
|
37
|
-
expect(log.mock.calls[0][0].version).toBe(0.0)
|
|
38
|
-
expect(log.mock.calls[0][0].element).toBe(null)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should provide default value when no context value is provided', async () => {
|
|
42
|
-
const log = vi.fn()
|
|
43
|
-
|
|
44
|
-
function Debug() {
|
|
45
|
-
const rootBoundaryElement = useBoundaryElement()
|
|
46
|
-
|
|
47
|
-
log(rootBoundaryElement)
|
|
48
|
-
|
|
49
|
-
return null
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function Root() {
|
|
53
|
-
return (
|
|
54
|
-
<BoundaryElementContext
|
|
55
|
-
// @ts-expect-error - we want to test the error case
|
|
56
|
-
value={undefined}
|
|
57
|
-
>
|
|
58
|
-
<Debug />
|
|
59
|
-
</BoundaryElementContext>
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
render(<Root />)
|
|
64
|
-
|
|
65
|
-
expect(log.mock.calls[0][0].version).toBe(0.0)
|
|
66
|
-
expect(log.mock.calls[0][0].element).toBe(null)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('should fail when context value is not compatible', async () => {
|
|
70
|
-
const log = vi.fn()
|
|
71
|
-
|
|
72
|
-
function Debug() {
|
|
73
|
-
try {
|
|
74
|
-
useBoundaryElement()
|
|
75
|
-
} catch (err) {
|
|
76
|
-
log(err)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return null
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function Root() {
|
|
83
|
-
// NOTE: we’re testing this because the context value may be a function in the future
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<BoundaryElementContext
|
|
87
|
-
// @ts-expect-error - we want to test the error case
|
|
88
|
-
value={() => ({version: 1})}
|
|
89
|
-
>
|
|
90
|
-
<Debug />
|
|
91
|
-
</BoundaryElementContext>
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
render(<Root />)
|
|
96
|
-
|
|
97
|
-
expect(log.mock.calls[0][0].message).toEqual(
|
|
98
|
-
'useBoundaryElement(): the context value is not compatible',
|
|
99
|
-
)
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
})
|