@tamagui/popover 2.7.7 → 3.0.0-beta.643.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 +509 -664
- package/dist/cjs/Popover.native.cjs +603 -0
- package/dist/cjs/Popover.native.js +536 -691
- package/dist/cjs/Popover.native.js.map +1 -1
- package/dist/cjs/index.cjs +10 -11
- package/dist/cjs/index.native.cjs +22 -0
- package/dist/cjs/index.native.js +10 -10
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/useFloatingContext.cjs +202 -234
- package/dist/cjs/useFloatingContext.native.cjs +36 -0
- package/dist/cjs/useFloatingContext.native.js +20 -27
- package/dist/cjs/useFloatingContext.native.js.map +1 -1
- package/dist/esm/Popover.mjs +473 -617
- package/dist/esm/Popover.mjs.map +1 -1
- package/dist/esm/Popover.native.js +499 -644
- 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 +186 -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 +473 -617
- package/dist/jsx/Popover.mjs.map +1 -1
- package/dist/jsx/Popover.native.cjs +603 -0
- package/dist/jsx/Popover.native.js +536 -691
- 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 +22 -0
- package/dist/jsx/index.native.js +10 -10
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/useFloatingContext.mjs +186 -208
- package/dist/jsx/useFloatingContext.mjs.map +1 -1
- package/dist/jsx/useFloatingContext.native.cjs +36 -0
- package/dist/jsx/useFloatingContext.native.js +20 -27
- package/dist/jsx/useFloatingContext.native.js.map +1 -1
- package/package.json +22 -22
- package/src/Popover.tsx +115 -147
- package/types/Popover.d.ts +33 -109
- package/types/Popover.d.ts.map +1 -1
- 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
|
@@ -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'
|
|
@@ -168,23 +168,15 @@ type PopoverTriggerContextValue = {
|
|
|
168
168
|
|
|
169
169
|
export const PopoverContext = createStyledContext<PopoverContextValue>(
|
|
170
170
|
// since we always provide this we can avoid setting here
|
|
171
|
-
{}
|
|
171
|
+
{},
|
|
172
172
|
'Popover__'
|
|
173
173
|
)
|
|
174
174
|
|
|
175
175
|
// zIndex flows from root Popover prop to PopoverContent portal
|
|
176
176
|
export const PopoverZIndexContext = React.createContext<number | undefined>(undefined)
|
|
177
177
|
|
|
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
178
|
export const PopoverTriggerContext = createStyledContext<PopoverTriggerContextValue>(
|
|
187
|
-
{}
|
|
179
|
+
{},
|
|
188
180
|
'PopoverTrigger__'
|
|
189
181
|
)
|
|
190
182
|
|
|
@@ -346,7 +338,7 @@ const voidFn = () => {}
|
|
|
346
338
|
export type PopoverAnchorProps = ScopedPopoverProps<YStackProps>
|
|
347
339
|
|
|
348
340
|
export const PopoverAnchor = React.memo(
|
|
349
|
-
|
|
341
|
+
createRefComponent<TamaguiElement, PopoverAnchorProps>(
|
|
350
342
|
function PopoverAnchor(props, forwardedRef) {
|
|
351
343
|
const { scope, ...rest } = props
|
|
352
344
|
const context = usePopoverContext(scope)
|
|
@@ -378,7 +370,7 @@ export type PopoverTriggerProps = ScopedPopoverProps<
|
|
|
378
370
|
>
|
|
379
371
|
|
|
380
372
|
export const PopoverTrigger = React.memo(
|
|
381
|
-
|
|
373
|
+
createRefComponent<TamaguiElement, PopoverTriggerProps>(
|
|
382
374
|
function PopoverTrigger(props, forwardedRef) {
|
|
383
375
|
const { scope, disablePressTrigger, ...rest } = props
|
|
384
376
|
const triggerContext = usePopoverTriggerContext(scope)
|
|
@@ -411,8 +403,10 @@ export const PopoverTrigger = React.memo(
|
|
|
411
403
|
const trigger = (
|
|
412
404
|
<View
|
|
413
405
|
aria-expanded={open}
|
|
414
|
-
//
|
|
415
|
-
//
|
|
406
|
+
// aria-controls is supplied at runtime by floating-ui's useRole
|
|
407
|
+
// (see useFloatingContext), pointing at the role="dialog" content
|
|
408
|
+
// element while open - no manual wiring is needed or wanted here
|
|
409
|
+
// (a manual id would point at the inner frame, not the dialog element)
|
|
416
410
|
data-state={getState(open)}
|
|
417
411
|
{...rest}
|
|
418
412
|
// @ts-ignore
|
|
@@ -484,23 +478,24 @@ export interface PopoverContentTypeProps extends Omit<
|
|
|
484
478
|
* Enable smooth animation when the content position changes (e.g., when flipping sides)
|
|
485
479
|
*/
|
|
486
480
|
animatePosition?: boolean | 'even-when-repositioning'
|
|
487
|
-
/** @deprecated Use `animatePosition` instead */
|
|
488
|
-
enableAnimationForPositionChange?: boolean
|
|
489
481
|
}
|
|
490
482
|
|
|
491
483
|
export type PopoverContentProps = PopoverContentTypeProps
|
|
492
484
|
|
|
493
|
-
export const PopoverContent =
|
|
494
|
-
|
|
485
|
+
export const PopoverContent = createStyledHOC(
|
|
486
|
+
PopperContentFrame,
|
|
487
|
+
function PopoverContent(props: PopoverContentProps, forwardedRef) {
|
|
495
488
|
const {
|
|
496
489
|
trapFocus,
|
|
497
490
|
enableRemoveScroll = false,
|
|
498
491
|
zIndex: zIndexProp,
|
|
499
492
|
scope,
|
|
493
|
+
forceMount,
|
|
500
494
|
...contentImplProps
|
|
501
495
|
} = props
|
|
502
496
|
|
|
503
497
|
const context = usePopoverContext(scope)
|
|
498
|
+
const adaptContext = useAdaptContext(context.adaptScope)
|
|
504
499
|
const zIndexFromContext = React.useContext(PopoverZIndexContext)
|
|
505
500
|
// prop on Content takes precedence for backwards compatibility, then context from root
|
|
506
501
|
const zIndex = zIndexProp ?? zIndexFromContext
|
|
@@ -522,14 +517,13 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
522
517
|
|
|
523
518
|
// when adapted to a Sheet, the content is portaled into the sheet via
|
|
524
519
|
// Adapt.Contents. its mount lifecycle must follow the sheet's slide-out
|
|
525
|
-
// (
|
|
526
|
-
//
|
|
527
|
-
//
|
|
528
|
-
//
|
|
529
|
-
|
|
530
|
-
if (!context.keepChildrenMounted) {
|
|
520
|
+
// (adaptContext.targetFullyHidden, driven by the Adapt handoff), NOT the
|
|
521
|
+
// popup's own exit animation (isFullyHidden); the popup runs in passThrough
|
|
522
|
+
// mode while adapted, so isFullyHidden either fires immediately (content
|
|
523
|
+
// vanishes mid-slide) or never (content leaks), depending on driver.
|
|
524
|
+
if (!forceMount && !context.keepChildrenMounted) {
|
|
531
525
|
if (context.breakpointActive) {
|
|
532
|
-
if (!open &&
|
|
526
|
+
if (!open && adaptContext.targetFullyHidden) {
|
|
533
527
|
return null
|
|
534
528
|
}
|
|
535
529
|
} else if (isFullyHidden && !open) {
|
|
@@ -542,6 +536,7 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
542
536
|
passThrough={context.breakpointActive}
|
|
543
537
|
context={context}
|
|
544
538
|
open={open}
|
|
539
|
+
visuallyActive={open || !isFullyHidden}
|
|
545
540
|
zIndex={zIndex}
|
|
546
541
|
>
|
|
547
542
|
<View
|
|
@@ -552,6 +547,7 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
552
547
|
{...contentImplProps}
|
|
553
548
|
context={context}
|
|
554
549
|
open={open}
|
|
550
|
+
forceMount={forceMount}
|
|
555
551
|
enableRemoveScroll={enableRemoveScroll}
|
|
556
552
|
ref={composedRefs}
|
|
557
553
|
setIsFullyHidden={setIsFullyHidden}
|
|
@@ -564,8 +560,8 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
564
560
|
props.onCloseAutoFocus === false
|
|
565
561
|
? undefined
|
|
566
562
|
: composeEventHandlers(props.onCloseAutoFocus, (event) => {
|
|
567
|
-
if (event.
|
|
568
|
-
event.
|
|
563
|
+
if (event.isCanceled) return
|
|
564
|
+
event.cancel()
|
|
569
565
|
if (!isRightClickOutsideRef.current)
|
|
570
566
|
context.triggerRef.current?.focus()
|
|
571
567
|
})
|
|
@@ -573,7 +569,8 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
573
569
|
onPointerDownOutside={composeEventHandlers(
|
|
574
570
|
props.onPointerDownOutside,
|
|
575
571
|
(event) => {
|
|
576
|
-
const originalEvent = event.
|
|
572
|
+
const originalEvent = event.event
|
|
573
|
+
if (!originalEvent) return
|
|
577
574
|
const ctrlLeftClick =
|
|
578
575
|
originalEvent.button === 0 && originalEvent.ctrlKey === true
|
|
579
576
|
const isRightClick = originalEvent.button === 2 || ctrlLeftClick
|
|
@@ -585,7 +582,9 @@ export const PopoverContent = PopperContentFrame.styleable<PopoverContentProps>(
|
|
|
585
582
|
// We make sure we don't trigger our `onDismiss` in such case.
|
|
586
583
|
onFocusOutside={composeEventHandlers(
|
|
587
584
|
props.onFocusOutside,
|
|
588
|
-
(event) =>
|
|
585
|
+
(event) => {
|
|
586
|
+
if (trapFocus ?? open) event.cancel()
|
|
587
|
+
},
|
|
589
588
|
{ checkDefaultPrevented: false }
|
|
590
589
|
)}
|
|
591
590
|
/>
|
|
@@ -656,6 +655,7 @@ const PortalAdaptSafe = ({
|
|
|
656
655
|
function PopoverPortal({
|
|
657
656
|
context,
|
|
658
657
|
open,
|
|
658
|
+
visuallyActive,
|
|
659
659
|
zIndex,
|
|
660
660
|
passThrough,
|
|
661
661
|
children,
|
|
@@ -663,6 +663,9 @@ function PopoverPortal({
|
|
|
663
663
|
}: Pick<PopoverContentProps, 'zIndex' | 'passThrough' | 'children' | 'onPress'> & {
|
|
664
664
|
context: PopoverContextValue
|
|
665
665
|
open: boolean
|
|
666
|
+
// true while open or still animating out - hides the portal host when false
|
|
667
|
+
// (matters for keepChildrenMounted/forceMount content, see Portal.tsx)
|
|
668
|
+
visuallyActive: boolean
|
|
666
669
|
}) {
|
|
667
670
|
'use no memo'
|
|
668
671
|
|
|
@@ -677,12 +680,18 @@ function PopoverPortal({
|
|
|
677
680
|
}
|
|
678
681
|
|
|
679
682
|
return (
|
|
680
|
-
<Portal
|
|
683
|
+
<Portal
|
|
684
|
+
passThrough={passThrough}
|
|
685
|
+
stackZIndex
|
|
686
|
+
zIndex={zIndex}
|
|
687
|
+
hidden={!visuallyActive}
|
|
688
|
+
>
|
|
681
689
|
{/* forceClassName avoids forced re-mount renders for some reason... see the HeadMenu as you change tints a few times */}
|
|
682
690
|
{/* without this you'll see the site menu re-rendering. It must be something in wrapping children in Theme */}
|
|
683
691
|
{!!open && !context.breakpointActive && !context.hoverable && (
|
|
684
692
|
<YStack
|
|
685
|
-
|
|
693
|
+
position="absolute"
|
|
694
|
+
inset={0}
|
|
686
695
|
onPress={composeEventHandlers(onPress as any, context.onOpenToggle)}
|
|
687
696
|
/>
|
|
688
697
|
)}
|
|
@@ -695,7 +704,7 @@ function PopoverPortal({
|
|
|
695
704
|
|
|
696
705
|
/* -----------------------------------------------------------------------------------------------*/
|
|
697
706
|
|
|
698
|
-
type PopoverContentImplElement =
|
|
707
|
+
type PopoverContentImplElement = GetRef<typeof PopperContent>
|
|
699
708
|
|
|
700
709
|
export type PopoverContentImplProps = PopperContentProps &
|
|
701
710
|
Omit<DismissableProps, 'onDismiss' | 'children' | 'onPointerDownCapture'> & {
|
|
@@ -705,6 +714,13 @@ export type PopoverContentImplProps = PopperContentProps &
|
|
|
705
714
|
*/
|
|
706
715
|
lazyMount?: boolean
|
|
707
716
|
|
|
717
|
+
/**
|
|
718
|
+
* Used to force mounting when more control is needed. Useful when
|
|
719
|
+
* controlling animation with React animation libraries. Matches Dialog:
|
|
720
|
+
* disables part presence gating so the content is always mounted.
|
|
721
|
+
*/
|
|
722
|
+
forceMount?: boolean
|
|
723
|
+
|
|
708
724
|
/**
|
|
709
725
|
* Whether focus should be trapped within the `Popover`
|
|
710
726
|
* @default false
|
|
@@ -718,12 +734,12 @@ export type PopoverContentImplProps = PopperContentProps &
|
|
|
718
734
|
disableFocusScope?: boolean
|
|
719
735
|
|
|
720
736
|
/**
|
|
721
|
-
* Event handler called when auto-focusing on open. Can be
|
|
737
|
+
* Event handler called when auto-focusing on open. Can be canceled.
|
|
722
738
|
*/
|
|
723
739
|
onOpenAutoFocus?: FocusScopeProps['onMountAutoFocus']
|
|
724
740
|
|
|
725
741
|
/**
|
|
726
|
-
* Event handler called when auto-focusing on close. Can be
|
|
742
|
+
* Event handler called when auto-focusing on close. Can be canceled.
|
|
727
743
|
*/
|
|
728
744
|
onCloseAutoFocus?: FocusScopeProps['onUnmountAutoFocus'] | false
|
|
729
745
|
|
|
@@ -747,7 +763,7 @@ type PopoverContentImplInteralProps = PopoverContentImplProps & {
|
|
|
747
763
|
setIsFullyHidden: React.Dispatch<React.SetStateAction<boolean>>
|
|
748
764
|
}
|
|
749
765
|
|
|
750
|
-
const PopoverContentImpl =
|
|
766
|
+
const PopoverContentImpl = createRefComponent<
|
|
751
767
|
PopoverContentImplElement,
|
|
752
768
|
PopoverContentImplInteralProps
|
|
753
769
|
>(function PopoverContentImpl(props, forwardedRef) {
|
|
@@ -768,13 +784,15 @@ const PopoverContentImpl = React.forwardRef<
|
|
|
768
784
|
setIsFullyHidden,
|
|
769
785
|
lazyMount,
|
|
770
786
|
forceUnmount,
|
|
787
|
+
forceMount,
|
|
771
788
|
context,
|
|
772
789
|
open,
|
|
773
790
|
alwaysDisable,
|
|
774
791
|
...contentProps
|
|
775
792
|
} = props
|
|
776
793
|
|
|
777
|
-
const {
|
|
794
|
+
const { disableDismissable } = context
|
|
795
|
+
const keepChildrenMounted = context.keepChildrenMounted || forceMount
|
|
778
796
|
|
|
779
797
|
const handleExitComplete = React.useCallback(() => {
|
|
780
798
|
setIsFullyHidden?.(true)
|
|
@@ -801,7 +819,7 @@ const PopoverContentImpl = React.forwardRef<
|
|
|
801
819
|
onMountAutoFocus={onOpenAutoFocus}
|
|
802
820
|
onUnmountAutoFocus={onCloseAutoFocus === false ? undefined : onCloseAutoFocus}
|
|
803
821
|
>
|
|
804
|
-
|
|
822
|
+
{contents}
|
|
805
823
|
</FocusScope>
|
|
806
824
|
)
|
|
807
825
|
}
|
|
@@ -850,11 +868,6 @@ const PopoverContentImpl = React.forwardRef<
|
|
|
850
868
|
id={context.contentId}
|
|
851
869
|
ref={forwardedRef}
|
|
852
870
|
passThrough={context.breakpointActive}
|
|
853
|
-
{...(!contentProps.unstyled && {
|
|
854
|
-
size: '$true',
|
|
855
|
-
backgroundColor: '$background',
|
|
856
|
-
alignItems: 'center',
|
|
857
|
-
})}
|
|
858
871
|
{...contentProps}
|
|
859
872
|
>
|
|
860
873
|
<PortalAdaptSafe context={context}>{contents}</PortalAdaptSafe>
|
|
@@ -863,25 +876,21 @@ const PopoverContentImpl = React.forwardRef<
|
|
|
863
876
|
)
|
|
864
877
|
})
|
|
865
878
|
|
|
866
|
-
const dspContentsStyle = {
|
|
867
|
-
display: 'contents',
|
|
868
|
-
}
|
|
869
|
-
|
|
870
879
|
/* -------------------------------------------------------------------------------------------------
|
|
871
880
|
* PopoverClose
|
|
872
881
|
* -----------------------------------------------------------------------------------------------*/
|
|
873
882
|
|
|
874
883
|
export type PopoverCloseProps = ScopedPopoverProps<YStackProps>
|
|
875
884
|
|
|
876
|
-
export const PopoverClose =
|
|
885
|
+
export const PopoverClose = createRefComponent<TamaguiElement, PopoverCloseProps>(
|
|
877
886
|
function PopoverClose(props: ScopedPopoverProps<PopoverCloseProps>, forwardedRef) {
|
|
878
|
-
const { scope, ...rest } = props
|
|
887
|
+
const { scope, className, ...rest } = props
|
|
879
888
|
const context = usePopoverContext(scope)
|
|
880
889
|
return (
|
|
881
890
|
<YStack
|
|
882
891
|
{...rest}
|
|
883
892
|
ref={forwardedRef}
|
|
884
|
-
|
|
893
|
+
className={`is_PopoverClose ${className || ''}`.trim()}
|
|
885
894
|
onPress={composeEventHandlers(props.onPress as any, () =>
|
|
886
895
|
context?.onOpenChange?.(false, 'press')
|
|
887
896
|
)}
|
|
@@ -896,9 +905,10 @@ export const PopoverClose = React.forwardRef<TamaguiElement, PopoverCloseProps>(
|
|
|
896
905
|
|
|
897
906
|
export type PopoverArrowProps = PopperArrowProps
|
|
898
907
|
|
|
899
|
-
export const PopoverArrow =
|
|
900
|
-
|
|
901
|
-
|
|
908
|
+
export const PopoverArrow = createStyledHOC(
|
|
909
|
+
PopperArrowFrame,
|
|
910
|
+
function PopoverArrow(props: PopperArrowExtraProps, forwardedRef) {
|
|
911
|
+
const { scope, className, ...rest } = props as PopperArrowProps
|
|
902
912
|
const context = usePopoverContext(scope)
|
|
903
913
|
const isAdapted = useAdaptIsActive(context.adaptScope)
|
|
904
914
|
|
|
@@ -909,7 +919,7 @@ export const PopoverArrow = PopperArrowFrame.styleable<PopperArrowExtraProps>(
|
|
|
909
919
|
return (
|
|
910
920
|
<PopperArrow
|
|
911
921
|
scope={scope}
|
|
912
|
-
|
|
922
|
+
className={`is_PopoverArrow ${className || ''}`.trim()}
|
|
913
923
|
{...rest}
|
|
914
924
|
ref={forwardedRef}
|
|
915
925
|
/>
|
|
@@ -940,7 +950,7 @@ export type PopoverScrollViewProps = ScrollViewProps & {
|
|
|
940
950
|
scope?: string
|
|
941
951
|
}
|
|
942
952
|
|
|
943
|
-
const PopoverScrollView =
|
|
953
|
+
const PopoverScrollView = createRefComponent<ScrollView, PopoverScrollViewProps>(
|
|
944
954
|
({ scope, ...props }, ref) => {
|
|
945
955
|
const context = usePopoverContext(scope)
|
|
946
956
|
|
|
@@ -961,20 +971,49 @@ const PopoverScrollView = React.forwardRef<ScrollView, PopoverScrollViewProps>(
|
|
|
961
971
|
const DEFAULT_SCOPE = ''
|
|
962
972
|
|
|
963
973
|
export const Popover = withStaticProperties(
|
|
964
|
-
|
|
974
|
+
createRefComponent<Popover, PopoverProps>(function Popover(
|
|
965
975
|
{ scope = DEFAULT_SCOPE, ...props },
|
|
966
976
|
ref
|
|
967
977
|
) {
|
|
968
978
|
const id = React.useId()
|
|
969
979
|
const adaptScope = `PopoverAdapt${scope}`
|
|
980
|
+
const { open: openProp, defaultOpen, onOpenChange } = props
|
|
981
|
+
const viaRef = React.useRef<PopoverVia>(undefined)
|
|
982
|
+
|
|
983
|
+
// open state lives here (above AdaptParent) so the Adapt handoff can drive
|
|
984
|
+
// the adapted Sheet's open/close and unmount timing directly, mirroring Dialog
|
|
985
|
+
const [open, setOpen] = useControllableState({
|
|
986
|
+
prop: openProp,
|
|
987
|
+
defaultProp: defaultOpen || false,
|
|
988
|
+
onChange: (val) => {
|
|
989
|
+
onOpenChange?.(val, viaRef.current)
|
|
990
|
+
},
|
|
991
|
+
})
|
|
992
|
+
|
|
993
|
+
const handleOpenChange = useEvent((val: boolean, via?: PopoverVia): void => {
|
|
994
|
+
viaRef.current = via
|
|
995
|
+
setOpen(val)
|
|
996
|
+
})
|
|
997
|
+
|
|
998
|
+
// track open popovers for closeOpenPopovers()
|
|
999
|
+
React.useEffect(() => {
|
|
1000
|
+
if (!open) return
|
|
1001
|
+
openPopovers.add(setOpen)
|
|
1002
|
+
return () => {
|
|
1003
|
+
openPopovers.delete(setOpen)
|
|
1004
|
+
}
|
|
1005
|
+
}, [open, setOpen])
|
|
970
1006
|
|
|
971
1007
|
return (
|
|
972
|
-
<AdaptParent scope={adaptScope}
|
|
1008
|
+
<AdaptParent scope={adaptScope} open={open} onOpenChange={setOpen}>
|
|
973
1009
|
<PopoverInner
|
|
974
1010
|
adaptScope={adaptScope}
|
|
975
1011
|
ref={ref}
|
|
976
1012
|
id={id}
|
|
977
1013
|
scope={scope}
|
|
1014
|
+
open={open}
|
|
1015
|
+
setOpen={setOpen}
|
|
1016
|
+
handleOpenChange={handleOpenChange}
|
|
978
1017
|
{...props}
|
|
979
1018
|
/>
|
|
980
1019
|
</AdaptParent>
|
|
@@ -992,15 +1031,21 @@ export const Popover = withStaticProperties(
|
|
|
992
1031
|
}
|
|
993
1032
|
)
|
|
994
1033
|
|
|
995
|
-
const PopoverInner =
|
|
1034
|
+
const PopoverInner = createRefComponent<
|
|
996
1035
|
Popover,
|
|
997
|
-
PopoverProps & {
|
|
1036
|
+
PopoverProps & {
|
|
1037
|
+
id: string
|
|
1038
|
+
adaptScope: string
|
|
1039
|
+
open: boolean
|
|
1040
|
+
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
|
1041
|
+
handleOpenChange: (open: boolean, via?: PopoverVia) => void
|
|
1042
|
+
}
|
|
998
1043
|
>(function PopoverInner(props, forwardedRef) {
|
|
999
1044
|
const {
|
|
1000
1045
|
children,
|
|
1001
|
-
open:
|
|
1002
|
-
defaultOpen,
|
|
1003
|
-
onOpenChange,
|
|
1046
|
+
open: _openProp,
|
|
1047
|
+
defaultOpen: _defaultOpen,
|
|
1048
|
+
onOpenChange: _onOpenChange,
|
|
1004
1049
|
scope = DEFAULT_SCOPE,
|
|
1005
1050
|
keepChildrenMounted: keepChildrenMountedProp,
|
|
1006
1051
|
hoverable,
|
|
@@ -1009,12 +1054,14 @@ const PopoverInner = React.forwardRef<
|
|
|
1009
1054
|
zIndex,
|
|
1010
1055
|
id,
|
|
1011
1056
|
adaptScope,
|
|
1057
|
+
open,
|
|
1058
|
+
setOpen,
|
|
1059
|
+
handleOpenChange,
|
|
1012
1060
|
...restProps
|
|
1013
1061
|
} = props
|
|
1014
1062
|
|
|
1015
1063
|
const triggerRef = React.useRef<TamaguiElement>(null)
|
|
1016
1064
|
const [hasCustomAnchor, setHasCustomAnchor] = React.useState(false)
|
|
1017
|
-
const viaRef = React.useRef<PopoverVia>(undefined)
|
|
1018
1065
|
|
|
1019
1066
|
const [keepChildrenMounted] = useControllableState({
|
|
1020
1067
|
prop: keepChildrenMountedProp,
|
|
@@ -1022,33 +1069,12 @@ const PopoverInner = React.forwardRef<
|
|
|
1022
1069
|
transition: keepChildrenMountedProp === 'lazy',
|
|
1023
1070
|
})
|
|
1024
1071
|
|
|
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
1072
|
const isAdapted = useAdaptIsActive(adaptScope)
|
|
1048
1073
|
|
|
1049
1074
|
const floatingContext = useFloatingContext({
|
|
1050
1075
|
open,
|
|
1051
|
-
|
|
1076
|
+
// floating passes a string via ('hover'), widen at this boundary
|
|
1077
|
+
setOpen: handleOpenChange as (val: boolean, type?: string) => void,
|
|
1052
1078
|
disable: isAdapted,
|
|
1053
1079
|
hoverable,
|
|
1054
1080
|
disableFocus: disableFocus,
|
|
@@ -1100,9 +1126,7 @@ const PopoverInner = React.forwardRef<
|
|
|
1100
1126
|
disableDismissable={disableDismissable}
|
|
1101
1127
|
hoverable={hoverable}
|
|
1102
1128
|
>
|
|
1103
|
-
|
|
1104
|
-
{children}
|
|
1105
|
-
</PopoverSheetController>
|
|
1129
|
+
{children}
|
|
1106
1130
|
</PopoverContextProvider>
|
|
1107
1131
|
</Popper>
|
|
1108
1132
|
)
|
|
@@ -1135,59 +1159,3 @@ const PopoverInner = React.forwardRef<
|
|
|
1135
1159
|
function getState(open: boolean) {
|
|
1136
1160
|
return open ? 'open' : 'closed'
|
|
1137
1161
|
}
|
|
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
|
-
}
|