@tamagui/popover 2.7.7 → 3.0.0-beta.1091.1
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/cjs/Popover.cjs +522 -665
- package/dist/cjs/Popover.native.cjs +615 -0
- package/dist/cjs/Popover.native.js +549 -692
- package/dist/cjs/Popover.native.js.map +1 -1
- package/dist/cjs/index.cjs +9 -11
- package/dist/cjs/index.native.cjs +21 -0
- package/dist/cjs/index.native.js +9 -10
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/useFloatingContext.cjs +213 -234
- package/dist/cjs/useFloatingContext.native.cjs +35 -0
- package/dist/cjs/useFloatingContext.native.js +19 -27
- package/dist/cjs/useFloatingContext.native.js.map +1 -1
- package/dist/esm/Popover.mjs +487 -618
- package/dist/esm/Popover.mjs.map +1 -1
- package/dist/esm/Popover.native.js +513 -645
- package/dist/esm/Popover.native.js.map +1 -1
- package/dist/esm/index.js +3 -3
- package/dist/esm/index.mjs +3 -3
- package/dist/esm/index.native.js +3 -3
- package/dist/esm/useFloatingContext.mjs +198 -208
- package/dist/esm/useFloatingContext.mjs.map +1 -1
- package/dist/esm/useFloatingContext.native.js +4 -2
- package/dist/esm/useFloatingContext.native.js.map +1 -1
- package/dist/jsx/Popover.mjs +487 -618
- package/dist/jsx/Popover.mjs.map +1 -1
- package/dist/jsx/Popover.native.cjs +615 -0
- package/dist/jsx/Popover.native.js +549 -692
- package/dist/jsx/Popover.native.js.map +1 -1
- package/dist/jsx/index.js +3 -3
- package/dist/jsx/index.mjs +3 -3
- package/dist/jsx/index.native.cjs +21 -0
- package/dist/jsx/index.native.js +9 -10
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/useFloatingContext.mjs +198 -208
- package/dist/jsx/useFloatingContext.mjs.map +1 -1
- package/dist/jsx/useFloatingContext.native.cjs +35 -0
- package/dist/jsx/useFloatingContext.native.js +19 -27
- package/dist/jsx/useFloatingContext.native.js.map +1 -1
- package/package.json +24 -23
- package/src/Popover.tsx +157 -154
- package/src/useFloatingContext.tsx +15 -7
- package/types/Popover.d.ts +40 -112
- package/types/Popover.d.ts.map +1 -1
- package/types/useFloatingContext.d.ts +5 -2
- package/types/useFloatingContext.d.ts.map +1 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/index.mjs.map +0 -1
- package/dist/esm/index.native.js.map +0 -1
- package/dist/jsx/index.js.map +0 -1
- package/dist/jsx/index.mjs.map +0 -1
package/src/Popover.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '@tamagui/polyfill-dev'
|
|
2
2
|
|
|
3
|
-
import type { UseHoverProps } from '@tamagui/floating'
|
|
3
|
+
import type { OpenChangeReason, UseHoverProps } from '@tamagui/floating'
|
|
4
4
|
import {
|
|
5
5
|
Adapt,
|
|
6
6
|
AdaptParent,
|
|
@@ -13,12 +13,13 @@ import { Animate } from '@tamagui/animate'
|
|
|
13
13
|
import { ResetPresence } from '@tamagui/animate-presence'
|
|
14
14
|
import { useComposedRefs } from '@tamagui/compose-refs'
|
|
15
15
|
import { isWeb, useIsomorphicLayoutEffect } from '@tamagui/constants'
|
|
16
|
-
import type { SizeTokens, TamaguiElement, ViewProps } from '@tamagui/core'
|
|
16
|
+
import type { SizeTokens, GetRef, TamaguiElement, ViewProps } from '@tamagui/core'
|
|
17
17
|
import {
|
|
18
|
+
createStyledHOC,
|
|
19
|
+
createRefComponent,
|
|
18
20
|
createStyledContext,
|
|
19
21
|
useCreateShallowSetState,
|
|
20
22
|
useEvent,
|
|
21
|
-
useGet,
|
|
22
23
|
View,
|
|
23
24
|
} from '@tamagui/core'
|
|
24
25
|
import {
|
|
@@ -47,7 +48,6 @@ import {
|
|
|
47
48
|
import { needsPortalRepropagation, Portal } from '@tamagui/portal'
|
|
48
49
|
import { RemoveScroll } from '@tamagui/remove-scroll'
|
|
49
50
|
import { ScrollView, type ScrollViewProps } from '@tamagui/scroll-view'
|
|
50
|
-
import { SheetController } from '@tamagui/sheet/controller'
|
|
51
51
|
import type { YStackProps } from '@tamagui/stacks'
|
|
52
52
|
import { YStack } from '@tamagui/stacks'
|
|
53
53
|
import { useControllableState } from '@tamagui/use-controllable-state'
|
|
@@ -105,7 +105,8 @@ export type PopoverProps = ScopedPopoverProps<PopperProps> & {
|
|
|
105
105
|
hoverable?: boolean | UseHoverProps
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
*
|
|
108
|
+
* disable opening when the trigger receives keyboard focus.
|
|
109
|
+
* defaults to true unless hoverable is enabled.
|
|
109
110
|
*/
|
|
110
111
|
disableFocus?: boolean
|
|
111
112
|
|
|
@@ -151,6 +152,8 @@ type PopoverContextValue = {
|
|
|
151
152
|
anchorTo?: Rect
|
|
152
153
|
// scoped branches Set for DismissableBranch/Dismissable to share
|
|
153
154
|
branches: Set<HTMLElement>
|
|
155
|
+
// carry dismissal intent through focus restoration.
|
|
156
|
+
closeReasonRef?: React.RefObject<OpenChangeReason | undefined>
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
type PopoverTriggerStateSetter = React.Dispatch<React.SetStateAction<boolean>>
|
|
@@ -168,23 +171,15 @@ type PopoverTriggerContextValue = {
|
|
|
168
171
|
|
|
169
172
|
export const PopoverContext = createStyledContext<PopoverContextValue>(
|
|
170
173
|
// since we always provide this we can avoid setting here
|
|
171
|
-
{}
|
|
174
|
+
{},
|
|
172
175
|
'Popover__'
|
|
173
176
|
)
|
|
174
177
|
|
|
175
178
|
// zIndex flows from root Popover prop to PopoverContent portal
|
|
176
179
|
export const PopoverZIndexContext = React.createContext<number | undefined>(undefined)
|
|
177
180
|
|
|
178
|
-
// when adapted to a Sheet, tracks whether the sheet has finished sliding out.
|
|
179
|
-
// PopoverSheetController flips this true via SheetController.onAnimationComplete
|
|
180
|
-
// so PopoverContent can hold its adapted children mounted until the slide-out
|
|
181
|
-
// is done, instead of unmounting them on the popup's (passThrough) exit.
|
|
182
|
-
// defaults true (= safe to unmount) for any PopoverContent rendered outside a
|
|
183
|
-
// PopoverSheetController.
|
|
184
|
-
const PopoverAdaptHiddenContext = React.createContext(true)
|
|
185
|
-
|
|
186
181
|
export const PopoverTriggerContext = createStyledContext<PopoverTriggerContextValue>(
|
|
187
|
-
{}
|
|
182
|
+
{},
|
|
188
183
|
'PopoverTrigger__'
|
|
189
184
|
)
|
|
190
185
|
|
|
@@ -268,6 +263,7 @@ export type PopoverContextProviderProps = {
|
|
|
268
263
|
keepChildrenMounted?: boolean | 'lazy'
|
|
269
264
|
disableDismissable?: boolean
|
|
270
265
|
hoverable?: boolean | object
|
|
266
|
+
closeReasonRef?: React.RefObject<OpenChangeReason | undefined>
|
|
271
267
|
}
|
|
272
268
|
|
|
273
269
|
/**
|
|
@@ -293,6 +289,7 @@ export const PopoverContextProvider = React.memo(
|
|
|
293
289
|
keepChildrenMounted,
|
|
294
290
|
disableDismissable,
|
|
295
291
|
hoverable,
|
|
292
|
+
closeReasonRef,
|
|
296
293
|
}: PopoverContextProviderProps) => {
|
|
297
294
|
const [branches] = React.useState(() => new Set<HTMLElement>())
|
|
298
295
|
const { setActiveTrigger, registerTrigger, unregisterTrigger } =
|
|
@@ -318,6 +315,7 @@ export const PopoverContextProvider = React.memo(
|
|
|
318
315
|
keepChildrenMounted={keepChildrenMounted}
|
|
319
316
|
disableDismissable={disableDismissable}
|
|
320
317
|
hoverable={hoverable}
|
|
318
|
+
closeReasonRef={closeReasonRef}
|
|
321
319
|
>
|
|
322
320
|
<PopoverTriggerContext.Provider
|
|
323
321
|
scope={scope}
|
|
@@ -346,7 +344,7 @@ const voidFn = () => {}
|
|
|
346
344
|
export type PopoverAnchorProps = ScopedPopoverProps<YStackProps>
|
|
347
345
|
|
|
348
346
|
export const PopoverAnchor = React.memo(
|
|
349
|
-
|
|
347
|
+
createRefComponent<TamaguiElement, PopoverAnchorProps>(
|
|
350
348
|
function PopoverAnchor(props, forwardedRef) {
|
|
351
349
|
const { scope, ...rest } = props
|
|
352
350
|
const context = usePopoverContext(scope)
|
|
@@ -378,7 +376,7 @@ export type PopoverTriggerProps = ScopedPopoverProps<
|
|
|
378
376
|
>
|
|
379
377
|
|
|
380
378
|
export const PopoverTrigger = React.memo(
|
|
381
|
-
|
|
379
|
+
createRefComponent<TamaguiElement, PopoverTriggerProps>(
|
|
382
380
|
function PopoverTrigger(props, forwardedRef) {
|
|
383
381
|
const { scope, disablePressTrigger, ...rest } = props
|
|
384
382
|
const triggerContext = usePopoverTriggerContext(scope)
|
|
@@ -411,8 +409,10 @@ export const PopoverTrigger = React.memo(
|
|
|
411
409
|
const trigger = (
|
|
412
410
|
<View
|
|
413
411
|
aria-expanded={open}
|
|
414
|
-
//
|
|
415
|
-
//
|
|
412
|
+
// aria-controls is supplied at runtime by floating-ui's useRole
|
|
413
|
+
// (see useFloatingContext), pointing at the role="dialog" content
|
|
414
|
+
// element while open - no manual wiring is needed or wanted here
|
|
415
|
+
// (a manual id would point at the inner frame, not the dialog element)
|
|
416
416
|
data-state={getState(open)}
|
|
417
417
|
{...rest}
|
|
418
418
|
// @ts-ignore
|
|
@@ -484,23 +484,24 @@ export interface PopoverContentTypeProps extends Omit<
|
|
|
484
484
|
* Enable smooth animation when the content position changes (e.g., when flipping sides)
|
|
485
485
|
*/
|
|
486
486
|
animatePosition?: boolean | 'even-when-repositioning'
|
|
487
|
-
/** @deprecated Use `animatePosition` instead */
|
|
488
|
-
enableAnimationForPositionChange?: boolean
|
|
489
487
|
}
|
|
490
488
|
|
|
491
489
|
export type PopoverContentProps = PopoverContentTypeProps
|
|
492
490
|
|
|
493
|
-
export const PopoverContent =
|
|
494
|
-
|
|
491
|
+
export const PopoverContent = createStyledHOC(
|
|
492
|
+
PopperContentFrame,
|
|
493
|
+
function PopoverContent(props: PopoverContentProps, forwardedRef) {
|
|
495
494
|
const {
|
|
496
495
|
trapFocus,
|
|
497
496
|
enableRemoveScroll = false,
|
|
498
497
|
zIndex: zIndexProp,
|
|
499
498
|
scope,
|
|
499
|
+
forceMount,
|
|
500
500
|
...contentImplProps
|
|
501
501
|
} = props
|
|
502
502
|
|
|
503
503
|
const context = usePopoverContext(scope)
|
|
504
|
+
const adaptContext = useAdaptContext(context.adaptScope)
|
|
504
505
|
const zIndexFromContext = React.useContext(PopoverZIndexContext)
|
|
505
506
|
// prop on Content takes precedence for backwards compatibility, then context from root
|
|
506
507
|
const zIndex = zIndexProp ?? zIndexFromContext
|
|
@@ -522,14 +523,13 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
522
523
|
|
|
523
524
|
// when adapted to a Sheet, the content is portaled into the sheet via
|
|
524
525
|
// Adapt.Contents. its mount lifecycle must follow the sheet's slide-out
|
|
525
|
-
// (
|
|
526
|
-
//
|
|
527
|
-
//
|
|
528
|
-
//
|
|
529
|
-
|
|
530
|
-
if (!context.keepChildrenMounted) {
|
|
526
|
+
// (adaptContext.targetFullyHidden, driven by the Adapt handoff), NOT the
|
|
527
|
+
// popup's own exit animation (isFullyHidden); the popup runs in passThrough
|
|
528
|
+
// mode while adapted, so isFullyHidden either fires immediately (content
|
|
529
|
+
// vanishes mid-slide) or never (content leaks), depending on driver.
|
|
530
|
+
if (!forceMount && !context.keepChildrenMounted) {
|
|
531
531
|
if (context.breakpointActive) {
|
|
532
|
-
if (!open &&
|
|
532
|
+
if (!open && adaptContext.targetFullyHidden) {
|
|
533
533
|
return null
|
|
534
534
|
}
|
|
535
535
|
} else if (isFullyHidden && !open) {
|
|
@@ -542,6 +542,7 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
542
542
|
passThrough={context.breakpointActive}
|
|
543
543
|
context={context}
|
|
544
544
|
open={open}
|
|
545
|
+
visuallyActive={open || !isFullyHidden}
|
|
545
546
|
zIndex={zIndex}
|
|
546
547
|
>
|
|
547
548
|
<View
|
|
@@ -552,6 +553,7 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
552
553
|
{...contentImplProps}
|
|
553
554
|
context={context}
|
|
554
555
|
open={open}
|
|
556
|
+
forceMount={forceMount}
|
|
555
557
|
enableRemoveScroll={enableRemoveScroll}
|
|
556
558
|
ref={composedRefs}
|
|
557
559
|
setIsFullyHidden={setIsFullyHidden}
|
|
@@ -564,8 +566,8 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
564
566
|
props.onCloseAutoFocus === false
|
|
565
567
|
? undefined
|
|
566
568
|
: composeEventHandlers(props.onCloseAutoFocus, (event) => {
|
|
567
|
-
if (event.
|
|
568
|
-
event.
|
|
569
|
+
if (event.isCanceled) return
|
|
570
|
+
event.cancel()
|
|
569
571
|
if (!isRightClickOutsideRef.current)
|
|
570
572
|
context.triggerRef.current?.focus()
|
|
571
573
|
})
|
|
@@ -573,7 +575,8 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
573
575
|
onPointerDownOutside={composeEventHandlers(
|
|
574
576
|
props.onPointerDownOutside,
|
|
575
577
|
(event) => {
|
|
576
|
-
const originalEvent = event.
|
|
578
|
+
const originalEvent = event.event
|
|
579
|
+
if (!originalEvent) return
|
|
577
580
|
const ctrlLeftClick =
|
|
578
581
|
originalEvent.button === 0 && originalEvent.ctrlKey === true
|
|
579
582
|
const isRightClick = originalEvent.button === 2 || ctrlLeftClick
|
|
@@ -585,13 +588,25 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
585
588
|
// We make sure we don't trigger our `onDismiss` in such case.
|
|
586
589
|
onFocusOutside={composeEventHandlers(
|
|
587
590
|
props.onFocusOutside,
|
|
588
|
-
(event) =>
|
|
591
|
+
(event) => {
|
|
592
|
+
if (trapFocus ?? open) event.cancel()
|
|
593
|
+
},
|
|
589
594
|
{ checkDefaultPrevented: false }
|
|
590
595
|
)}
|
|
591
596
|
/>
|
|
592
597
|
</View>
|
|
593
598
|
</PopoverPortal>
|
|
594
599
|
)
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
staticConfig: {
|
|
603
|
+
inlineProps: new Set([
|
|
604
|
+
...(PopperContentFrame.staticConfig.inlineProps || []),
|
|
605
|
+
'zIndex',
|
|
606
|
+
'opacity',
|
|
607
|
+
'y',
|
|
608
|
+
]),
|
|
609
|
+
},
|
|
595
610
|
}
|
|
596
611
|
)
|
|
597
612
|
|
|
@@ -656,6 +671,7 @@ const PortalAdaptSafe = ({
|
|
|
656
671
|
function PopoverPortal({
|
|
657
672
|
context,
|
|
658
673
|
open,
|
|
674
|
+
visuallyActive,
|
|
659
675
|
zIndex,
|
|
660
676
|
passThrough,
|
|
661
677
|
children,
|
|
@@ -663,6 +679,9 @@ function PopoverPortal({
|
|
|
663
679
|
}: Pick<PopoverContentProps, 'zIndex' | 'passThrough' | 'children' | 'onPress'> & {
|
|
664
680
|
context: PopoverContextValue
|
|
665
681
|
open: boolean
|
|
682
|
+
// true while open or still animating out - hides the portal host when false
|
|
683
|
+
// (matters for keepChildrenMounted/forceMount content, see Portal.tsx)
|
|
684
|
+
visuallyActive: boolean
|
|
666
685
|
}) {
|
|
667
686
|
'use no memo'
|
|
668
687
|
|
|
@@ -677,12 +696,18 @@ function PopoverPortal({
|
|
|
677
696
|
}
|
|
678
697
|
|
|
679
698
|
return (
|
|
680
|
-
<Portal
|
|
699
|
+
<Portal
|
|
700
|
+
passThrough={passThrough}
|
|
701
|
+
stackZIndex
|
|
702
|
+
zIndex={zIndex}
|
|
703
|
+
hidden={!visuallyActive}
|
|
704
|
+
>
|
|
681
705
|
{/* forceClassName avoids forced re-mount renders for some reason... see the HeadMenu as you change tints a few times */}
|
|
682
706
|
{/* without this you'll see the site menu re-rendering. It must be something in wrapping children in Theme */}
|
|
683
707
|
{!!open && !context.breakpointActive && !context.hoverable && (
|
|
684
708
|
<YStack
|
|
685
|
-
|
|
709
|
+
position="absolute"
|
|
710
|
+
inset={0}
|
|
686
711
|
onPress={composeEventHandlers(onPress as any, context.onOpenToggle)}
|
|
687
712
|
/>
|
|
688
713
|
)}
|
|
@@ -695,7 +720,7 @@ function PopoverPortal({
|
|
|
695
720
|
|
|
696
721
|
/* -----------------------------------------------------------------------------------------------*/
|
|
697
722
|
|
|
698
|
-
type PopoverContentImplElement =
|
|
723
|
+
type PopoverContentImplElement = GetRef<typeof PopperContent>
|
|
699
724
|
|
|
700
725
|
export type PopoverContentImplProps = PopperContentProps &
|
|
701
726
|
Omit<DismissableProps, 'onDismiss' | 'children' | 'onPointerDownCapture'> & {
|
|
@@ -705,6 +730,13 @@ export type PopoverContentImplProps = PopperContentProps &
|
|
|
705
730
|
*/
|
|
706
731
|
lazyMount?: boolean
|
|
707
732
|
|
|
733
|
+
/**
|
|
734
|
+
* Used to force mounting when more control is needed. Useful when
|
|
735
|
+
* controlling animation with React animation libraries. Matches Dialog:
|
|
736
|
+
* disables part presence gating so the content is always mounted.
|
|
737
|
+
*/
|
|
738
|
+
forceMount?: boolean
|
|
739
|
+
|
|
708
740
|
/**
|
|
709
741
|
* Whether focus should be trapped within the `Popover`
|
|
710
742
|
* @default false
|
|
@@ -718,12 +750,12 @@ export type PopoverContentImplProps = PopperContentProps &
|
|
|
718
750
|
disableFocusScope?: boolean
|
|
719
751
|
|
|
720
752
|
/**
|
|
721
|
-
* Event handler called when auto-focusing on open. Can be
|
|
753
|
+
* Event handler called when auto-focusing on open. Can be canceled.
|
|
722
754
|
*/
|
|
723
755
|
onOpenAutoFocus?: FocusScopeProps['onMountAutoFocus']
|
|
724
756
|
|
|
725
757
|
/**
|
|
726
|
-
* Event handler called when auto-focusing on close. Can be
|
|
758
|
+
* Event handler called when auto-focusing on close. Can be canceled.
|
|
727
759
|
*/
|
|
728
760
|
onCloseAutoFocus?: FocusScopeProps['onUnmountAutoFocus'] | false
|
|
729
761
|
|
|
@@ -747,7 +779,7 @@ type PopoverContentImplInteralProps = PopoverContentImplProps & {
|
|
|
747
779
|
setIsFullyHidden: React.Dispatch<React.SetStateAction<boolean>>
|
|
748
780
|
}
|
|
749
781
|
|
|
750
|
-
const PopoverContentImpl =
|
|
782
|
+
const PopoverContentImpl = createRefComponent<
|
|
751
783
|
PopoverContentImplElement,
|
|
752
784
|
PopoverContentImplInteralProps
|
|
753
785
|
>(function PopoverContentImpl(props, forwardedRef) {
|
|
@@ -768,13 +800,15 @@ const PopoverContentImpl = React.forwardRef<
|
|
|
768
800
|
setIsFullyHidden,
|
|
769
801
|
lazyMount,
|
|
770
802
|
forceUnmount,
|
|
803
|
+
forceMount,
|
|
771
804
|
context,
|
|
772
805
|
open,
|
|
773
806
|
alwaysDisable,
|
|
774
807
|
...contentProps
|
|
775
808
|
} = props
|
|
776
809
|
|
|
777
|
-
const {
|
|
810
|
+
const { disableDismissable } = context
|
|
811
|
+
const keepChildrenMounted = context.keepChildrenMounted || forceMount
|
|
778
812
|
|
|
779
813
|
const handleExitComplete = React.useCallback(() => {
|
|
780
814
|
setIsFullyHidden?.(true)
|
|
@@ -784,9 +818,16 @@ const PopoverContentImpl = React.forwardRef<
|
|
|
784
818
|
<ResetPresence disable={context.breakpointActive}>{children}</ResetPresence>
|
|
785
819
|
)
|
|
786
820
|
|
|
787
|
-
const handleDismiss = React.useCallback(
|
|
788
|
-
|
|
789
|
-
|
|
821
|
+
const handleDismiss = React.useCallback<NonNullable<DismissableProps['onDismiss']>>(
|
|
822
|
+
(details) => {
|
|
823
|
+
if (context.closeReasonRef) {
|
|
824
|
+
context.closeReasonRef.current =
|
|
825
|
+
details.reason === 'escape-key' ? 'escape-key' : 'dismiss'
|
|
826
|
+
}
|
|
827
|
+
context.onOpenChange(false, 'press')
|
|
828
|
+
},
|
|
829
|
+
[context]
|
|
830
|
+
)
|
|
790
831
|
|
|
791
832
|
// i want to avoid reparenting but react-remove-scroll makes it hard
|
|
792
833
|
// TODO its removed now so we can probable do it now
|
|
@@ -801,7 +842,7 @@ const PopoverContentImpl = React.forwardRef<
|
|
|
801
842
|
onMountAutoFocus={onOpenAutoFocus}
|
|
802
843
|
onUnmountAutoFocus={onCloseAutoFocus === false ? undefined : onCloseAutoFocus}
|
|
803
844
|
>
|
|
804
|
-
|
|
845
|
+
{contents}
|
|
805
846
|
</FocusScope>
|
|
806
847
|
)
|
|
807
848
|
}
|
|
@@ -850,11 +891,6 @@ const PopoverContentImpl = React.forwardRef<
|
|
|
850
891
|
id={context.contentId}
|
|
851
892
|
ref={forwardedRef}
|
|
852
893
|
passThrough={context.breakpointActive}
|
|
853
|
-
{...(!contentProps.unstyled && {
|
|
854
|
-
size: '$true',
|
|
855
|
-
backgroundColor: '$background',
|
|
856
|
-
alignItems: 'center',
|
|
857
|
-
})}
|
|
858
894
|
{...contentProps}
|
|
859
895
|
>
|
|
860
896
|
<PortalAdaptSafe context={context}>{contents}</PortalAdaptSafe>
|
|
@@ -863,25 +899,21 @@ const PopoverContentImpl = React.forwardRef<
|
|
|
863
899
|
)
|
|
864
900
|
})
|
|
865
901
|
|
|
866
|
-
const dspContentsStyle = {
|
|
867
|
-
display: 'contents',
|
|
868
|
-
}
|
|
869
|
-
|
|
870
902
|
/* -------------------------------------------------------------------------------------------------
|
|
871
903
|
* PopoverClose
|
|
872
904
|
* -----------------------------------------------------------------------------------------------*/
|
|
873
905
|
|
|
874
906
|
export type PopoverCloseProps = ScopedPopoverProps<YStackProps>
|
|
875
907
|
|
|
876
|
-
export const PopoverClose =
|
|
908
|
+
export const PopoverClose = createRefComponent<TamaguiElement, PopoverCloseProps>(
|
|
877
909
|
function PopoverClose(props: ScopedPopoverProps<PopoverCloseProps>, forwardedRef) {
|
|
878
|
-
const { scope, ...rest } = props
|
|
910
|
+
const { scope, className, ...rest } = props
|
|
879
911
|
const context = usePopoverContext(scope)
|
|
880
912
|
return (
|
|
881
913
|
<YStack
|
|
882
914
|
{...rest}
|
|
883
915
|
ref={forwardedRef}
|
|
884
|
-
|
|
916
|
+
className={`is_PopoverClose ${className || ''}`.trim()}
|
|
885
917
|
onPress={composeEventHandlers(props.onPress as any, () =>
|
|
886
918
|
context?.onOpenChange?.(false, 'press')
|
|
887
919
|
)}
|
|
@@ -896,9 +928,10 @@ export const PopoverClose = React.forwardRef<TamaguiElement, PopoverCloseProps>(
|
|
|
896
928
|
|
|
897
929
|
export type PopoverArrowProps = PopperArrowProps
|
|
898
930
|
|
|
899
|
-
export const PopoverArrow =
|
|
900
|
-
|
|
901
|
-
|
|
931
|
+
export const PopoverArrow = createStyledHOC(
|
|
932
|
+
PopperArrowFrame,
|
|
933
|
+
function PopoverArrow(props: PopperArrowExtraProps, forwardedRef) {
|
|
934
|
+
const { scope, className, ...rest } = props as PopperArrowProps
|
|
902
935
|
const context = usePopoverContext(scope)
|
|
903
936
|
const isAdapted = useAdaptIsActive(context.adaptScope)
|
|
904
937
|
|
|
@@ -909,7 +942,7 @@ export const PopoverArrow = PopperArrowFrame.styleable<PopperArrowExtraProps>(
|
|
|
909
942
|
return (
|
|
910
943
|
<PopperArrow
|
|
911
944
|
scope={scope}
|
|
912
|
-
|
|
945
|
+
className={`is_PopoverArrow ${className || ''}`.trim()}
|
|
913
946
|
{...rest}
|
|
914
947
|
ref={forwardedRef}
|
|
915
948
|
/>
|
|
@@ -940,7 +973,7 @@ export type PopoverScrollViewProps = ScrollViewProps & {
|
|
|
940
973
|
scope?: string
|
|
941
974
|
}
|
|
942
975
|
|
|
943
|
-
const PopoverScrollView =
|
|
976
|
+
const PopoverScrollView = createRefComponent<ScrollView, PopoverScrollViewProps>(
|
|
944
977
|
({ scope, ...props }, ref) => {
|
|
945
978
|
const context = usePopoverContext(scope)
|
|
946
979
|
|
|
@@ -961,20 +994,56 @@ const PopoverScrollView = React.forwardRef<ScrollView, PopoverScrollViewProps>(
|
|
|
961
994
|
const DEFAULT_SCOPE = ''
|
|
962
995
|
|
|
963
996
|
export const Popover = withStaticProperties(
|
|
964
|
-
|
|
997
|
+
createRefComponent<Popover, PopoverProps>(function Popover(
|
|
965
998
|
{ scope = DEFAULT_SCOPE, ...props },
|
|
966
999
|
ref
|
|
967
1000
|
) {
|
|
968
1001
|
const id = React.useId()
|
|
969
1002
|
const adaptScope = `PopoverAdapt${scope}`
|
|
1003
|
+
const { open: openProp, defaultOpen, onOpenChange } = props
|
|
1004
|
+
const viaRef = React.useRef<PopoverVia>(undefined)
|
|
1005
|
+
const closeReasonRef = React.useRef<OpenChangeReason | undefined>(undefined)
|
|
1006
|
+
|
|
1007
|
+
// open state lives here (above AdaptParent) so the Adapt handoff can drive
|
|
1008
|
+
// the adapted Sheet's open/close and unmount timing directly, mirroring Dialog
|
|
1009
|
+
const [open, setOpen] = useControllableState({
|
|
1010
|
+
prop: openProp,
|
|
1011
|
+
defaultProp: defaultOpen || false,
|
|
1012
|
+
onChange: (val) => {
|
|
1013
|
+
onOpenChange?.(val, viaRef.current)
|
|
1014
|
+
},
|
|
1015
|
+
})
|
|
1016
|
+
|
|
1017
|
+
const handleOpenChange = useEvent((val: boolean, via?: PopoverVia): void => {
|
|
1018
|
+
viaRef.current = via
|
|
1019
|
+
setOpen(val)
|
|
1020
|
+
})
|
|
1021
|
+
|
|
1022
|
+
// track open popovers for closeOpenPopovers()
|
|
1023
|
+
React.useEffect(() => {
|
|
1024
|
+
if (!open) return
|
|
1025
|
+
openPopovers.add(setOpen)
|
|
1026
|
+
return () => {
|
|
1027
|
+
openPopovers.delete(setOpen)
|
|
1028
|
+
}
|
|
1029
|
+
}, [open, setOpen])
|
|
970
1030
|
|
|
971
1031
|
return (
|
|
972
|
-
<AdaptParent
|
|
1032
|
+
<AdaptParent
|
|
1033
|
+
scope={adaptScope}
|
|
1034
|
+
adaptChildren={props.children}
|
|
1035
|
+
open={open}
|
|
1036
|
+
onOpenChange={setOpen}
|
|
1037
|
+
>
|
|
973
1038
|
<PopoverInner
|
|
974
1039
|
adaptScope={adaptScope}
|
|
975
1040
|
ref={ref}
|
|
976
1041
|
id={id}
|
|
977
1042
|
scope={scope}
|
|
1043
|
+
open={open}
|
|
1044
|
+
setOpen={setOpen}
|
|
1045
|
+
handleOpenChange={handleOpenChange}
|
|
1046
|
+
closeReasonRef={closeReasonRef}
|
|
978
1047
|
{...props}
|
|
979
1048
|
/>
|
|
980
1049
|
</AdaptParent>
|
|
@@ -992,29 +1061,39 @@ export const Popover = withStaticProperties(
|
|
|
992
1061
|
}
|
|
993
1062
|
)
|
|
994
1063
|
|
|
995
|
-
const PopoverInner =
|
|
1064
|
+
const PopoverInner = createRefComponent<
|
|
996
1065
|
Popover,
|
|
997
|
-
PopoverProps & {
|
|
1066
|
+
PopoverProps & {
|
|
1067
|
+
id: string
|
|
1068
|
+
adaptScope: string
|
|
1069
|
+
open: boolean
|
|
1070
|
+
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
|
1071
|
+
handleOpenChange: (open: boolean, via?: PopoverVia) => void
|
|
1072
|
+
closeReasonRef: React.RefObject<OpenChangeReason | undefined>
|
|
1073
|
+
}
|
|
998
1074
|
>(function PopoverInner(props, forwardedRef) {
|
|
999
1075
|
const {
|
|
1000
1076
|
children,
|
|
1001
|
-
open:
|
|
1002
|
-
defaultOpen,
|
|
1003
|
-
onOpenChange,
|
|
1077
|
+
open: _openProp,
|
|
1078
|
+
defaultOpen: _defaultOpen,
|
|
1079
|
+
onOpenChange: _onOpenChange,
|
|
1004
1080
|
scope = DEFAULT_SCOPE,
|
|
1005
1081
|
keepChildrenMounted: keepChildrenMountedProp,
|
|
1006
1082
|
hoverable,
|
|
1007
|
-
disableFocus,
|
|
1083
|
+
disableFocus = !hoverable,
|
|
1008
1084
|
disableDismissable,
|
|
1009
1085
|
zIndex,
|
|
1010
1086
|
id,
|
|
1011
1087
|
adaptScope,
|
|
1088
|
+
open,
|
|
1089
|
+
setOpen,
|
|
1090
|
+
handleOpenChange,
|
|
1091
|
+
closeReasonRef,
|
|
1012
1092
|
...restProps
|
|
1013
1093
|
} = props
|
|
1014
1094
|
|
|
1015
1095
|
const triggerRef = React.useRef<TamaguiElement>(null)
|
|
1016
1096
|
const [hasCustomAnchor, setHasCustomAnchor] = React.useState(false)
|
|
1017
|
-
const viaRef = React.useRef<PopoverVia>(undefined)
|
|
1018
1097
|
|
|
1019
1098
|
const [keepChildrenMounted] = useControllableState({
|
|
1020
1099
|
prop: keepChildrenMountedProp,
|
|
@@ -1022,36 +1101,16 @@ const PopoverInner = React.forwardRef<
|
|
|
1022
1101
|
transition: keepChildrenMountedProp === 'lazy',
|
|
1023
1102
|
})
|
|
1024
1103
|
|
|
1025
|
-
const [open, setOpen] = useControllableState({
|
|
1026
|
-
prop: openProp,
|
|
1027
|
-
defaultProp: defaultOpen || false,
|
|
1028
|
-
onChange: (val) => {
|
|
1029
|
-
onOpenChange?.(val, viaRef.current)
|
|
1030
|
-
},
|
|
1031
|
-
})
|
|
1032
|
-
|
|
1033
|
-
// track open popovers for closeOpenPopovers()
|
|
1034
|
-
React.useEffect(() => {
|
|
1035
|
-
if (!open) return
|
|
1036
|
-
openPopovers.add(setOpen)
|
|
1037
|
-
return () => {
|
|
1038
|
-
openPopovers.delete(setOpen)
|
|
1039
|
-
}
|
|
1040
|
-
}, [open, setOpen])
|
|
1041
|
-
|
|
1042
|
-
const handleOpenChange = useEvent((val, via) => {
|
|
1043
|
-
viaRef.current = via
|
|
1044
|
-
setOpen(val)
|
|
1045
|
-
})
|
|
1046
|
-
|
|
1047
1104
|
const isAdapted = useAdaptIsActive(adaptScope)
|
|
1048
1105
|
|
|
1049
1106
|
const floatingContext = useFloatingContext({
|
|
1050
1107
|
open,
|
|
1051
|
-
|
|
1108
|
+
// floating passes a string via ('hover'), widen at this boundary
|
|
1109
|
+
setOpen: handleOpenChange as (val: boolean, type?: string) => void,
|
|
1110
|
+
closeReasonRef,
|
|
1052
1111
|
disable: isAdapted,
|
|
1053
1112
|
hoverable,
|
|
1054
|
-
disableFocus
|
|
1113
|
+
disableFocus,
|
|
1055
1114
|
})
|
|
1056
1115
|
|
|
1057
1116
|
const [anchorTo, setAnchorToRaw] = React.useState<Rect>()
|
|
@@ -1074,6 +1133,7 @@ const PopoverInner = React.forwardRef<
|
|
|
1074
1133
|
if (open && isAdapted) {
|
|
1075
1134
|
return
|
|
1076
1135
|
}
|
|
1136
|
+
closeReasonRef.current = open ? 'reference-press' : undefined
|
|
1077
1137
|
setOpen(!open)
|
|
1078
1138
|
})
|
|
1079
1139
|
|
|
@@ -1099,10 +1159,9 @@ const PopoverInner = React.forwardRef<
|
|
|
1099
1159
|
keepChildrenMounted={keepChildrenMounted}
|
|
1100
1160
|
disableDismissable={disableDismissable}
|
|
1101
1161
|
hoverable={hoverable}
|
|
1162
|
+
closeReasonRef={closeReasonRef}
|
|
1102
1163
|
>
|
|
1103
|
-
|
|
1104
|
-
{children}
|
|
1105
|
-
</PopoverSheetController>
|
|
1164
|
+
{children}
|
|
1106
1165
|
</PopoverContextProvider>
|
|
1107
1166
|
</Popper>
|
|
1108
1167
|
)
|
|
@@ -1135,59 +1194,3 @@ const PopoverInner = React.forwardRef<
|
|
|
1135
1194
|
function getState(open: boolean) {
|
|
1136
1195
|
return open ? 'open' : 'closed'
|
|
1137
1196
|
}
|
|
1138
|
-
|
|
1139
|
-
const PopoverSheetController = ({
|
|
1140
|
-
open,
|
|
1141
|
-
scope,
|
|
1142
|
-
...props
|
|
1143
|
-
}: {
|
|
1144
|
-
open: boolean
|
|
1145
|
-
scope?: string
|
|
1146
|
-
children: React.ReactNode
|
|
1147
|
-
onOpenChange: React.Dispatch<React.SetStateAction<boolean>>
|
|
1148
|
-
}) => {
|
|
1149
|
-
const context = usePopoverContext(scope)
|
|
1150
|
-
const showSheet = useShowPopoverSheet(context, open)
|
|
1151
|
-
const breakpointActive = context?.breakpointActive
|
|
1152
|
-
const getShowSheet = useGet(showSheet)
|
|
1153
|
-
|
|
1154
|
-
// tracks whether the adapted Sheet has finished its slide-out animation.
|
|
1155
|
-
// starts true (= safe to unmount) when closed; flips false the moment the
|
|
1156
|
-
// popover opens; flips back to true when the sheet signals onAnimationComplete
|
|
1157
|
-
// with open=false (slide-out finished). mirrors DialogSheetController.
|
|
1158
|
-
const [isAdaptFullyHidden, setIsAdaptFullyHidden] = React.useState(!open)
|
|
1159
|
-
if (open && isAdaptFullyHidden) {
|
|
1160
|
-
setIsAdaptFullyHidden(false)
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
const handleSheetAnimationComplete = React.useCallback(
|
|
1164
|
-
({ open: isOpen }: { open: boolean }) => {
|
|
1165
|
-
if (!isOpen) {
|
|
1166
|
-
setIsAdaptFullyHidden(true)
|
|
1167
|
-
}
|
|
1168
|
-
},
|
|
1169
|
-
[]
|
|
1170
|
-
)
|
|
1171
|
-
|
|
1172
|
-
return (
|
|
1173
|
-
<SheetController
|
|
1174
|
-
onOpenChange={(val: boolean) => {
|
|
1175
|
-
if (getShowSheet()) {
|
|
1176
|
-
props.onOpenChange?.(val)
|
|
1177
|
-
}
|
|
1178
|
-
}}
|
|
1179
|
-
onAnimationComplete={handleSheetAnimationComplete}
|
|
1180
|
-
open={open}
|
|
1181
|
-
hidden={!breakpointActive}
|
|
1182
|
-
>
|
|
1183
|
-
<PopoverAdaptHiddenContext.Provider value={isAdaptFullyHidden}>
|
|
1184
|
-
{props.children}
|
|
1185
|
-
</PopoverAdaptHiddenContext.Provider>
|
|
1186
|
-
</SheetController>
|
|
1187
|
-
)
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
const useShowPopoverSheet = (context: PopoverContextValue, open: boolean) => {
|
|
1191
|
-
const isAdapted = useAdaptIsActive(context.adaptScope)
|
|
1192
|
-
return open === false ? false : isAdapted
|
|
1193
|
-
}
|