@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.
Files changed (48) hide show
  1. package/dist/cjs/Popover.cjs +509 -664
  2. package/dist/cjs/Popover.native.cjs +603 -0
  3. package/dist/cjs/Popover.native.js +536 -691
  4. package/dist/cjs/Popover.native.js.map +1 -1
  5. package/dist/cjs/index.cjs +10 -11
  6. package/dist/cjs/index.native.cjs +22 -0
  7. package/dist/cjs/index.native.js +10 -10
  8. package/dist/cjs/index.native.js.map +1 -1
  9. package/dist/cjs/useFloatingContext.cjs +202 -234
  10. package/dist/cjs/useFloatingContext.native.cjs +36 -0
  11. package/dist/cjs/useFloatingContext.native.js +20 -27
  12. package/dist/cjs/useFloatingContext.native.js.map +1 -1
  13. package/dist/esm/Popover.mjs +473 -617
  14. package/dist/esm/Popover.mjs.map +1 -1
  15. package/dist/esm/Popover.native.js +499 -644
  16. package/dist/esm/Popover.native.js.map +1 -1
  17. package/dist/esm/index.js +3 -3
  18. package/dist/esm/index.mjs +3 -3
  19. package/dist/esm/index.native.js +3 -3
  20. package/dist/esm/useFloatingContext.mjs +186 -208
  21. package/dist/esm/useFloatingContext.mjs.map +1 -1
  22. package/dist/esm/useFloatingContext.native.js +4 -2
  23. package/dist/esm/useFloatingContext.native.js.map +1 -1
  24. package/dist/jsx/Popover.mjs +473 -617
  25. package/dist/jsx/Popover.mjs.map +1 -1
  26. package/dist/jsx/Popover.native.cjs +603 -0
  27. package/dist/jsx/Popover.native.js +536 -691
  28. package/dist/jsx/Popover.native.js.map +1 -1
  29. package/dist/jsx/index.js +3 -3
  30. package/dist/jsx/index.mjs +3 -3
  31. package/dist/jsx/index.native.cjs +22 -0
  32. package/dist/jsx/index.native.js +10 -10
  33. package/dist/jsx/index.native.js.map +1 -1
  34. package/dist/jsx/useFloatingContext.mjs +186 -208
  35. package/dist/jsx/useFloatingContext.mjs.map +1 -1
  36. package/dist/jsx/useFloatingContext.native.cjs +36 -0
  37. package/dist/jsx/useFloatingContext.native.js +20 -27
  38. package/dist/jsx/useFloatingContext.native.js.map +1 -1
  39. package/package.json +22 -22
  40. package/src/Popover.tsx +115 -147
  41. package/types/Popover.d.ts +33 -109
  42. package/types/Popover.d.ts.map +1 -1
  43. package/types/useFloatingContext.d.ts.map +1 -1
  44. package/dist/esm/index.js.map +0 -1
  45. package/dist/esm/index.mjs.map +0 -1
  46. package/dist/esm/index.native.js.map +0 -1
  47. package/dist/jsx/index.js.map +0 -1
  48. 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
- {} as PopoverContextValue,
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
- {} as PopoverTriggerContextValue,
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
- React.forwardRef<TamaguiElement, PopoverAnchorProps>(
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
- React.forwardRef<TamaguiElement, PopoverTriggerProps>(
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
- // TODO not matching
415
- // aria-controls={context.contentId}
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 = PopperContentFrame.styleable<PopoverContentProps>(
494
- function PopoverContent(props, forwardedRef) {
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
- // (PopoverAdaptHiddenContext, flipped by SheetController.onAnimationComplete),
526
- // NOT the popup's own exit animation (isFullyHidden); the popup runs in
527
- // passThrough mode while adapted, so isFullyHidden either fires immediately
528
- // (content vanishes mid-slide) or never (content leaks), depending on driver.
529
- const isAdaptFullyHidden = React.useContext(PopoverAdaptHiddenContext)
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 && isAdaptFullyHidden) {
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.defaultPrevented) return
568
- event.preventDefault()
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.detail.originalEvent
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) => event.preventDefault(),
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 passThrough={passThrough} stackZIndex zIndex={zIndex}>
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
- fullscreen
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 = React.ElementRef<typeof PopperContent>
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 prevented.
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 prevented.
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 = React.forwardRef<
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 { keepChildrenMounted, disableDismissable } = context
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
- <div style={dspContentsStyle}>{contents}</div>
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 = React.forwardRef<TamaguiElement, PopoverCloseProps>(
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
- componentName="PopoverClose"
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 = PopperArrowFrame.styleable<PopperArrowExtraProps>(
900
- function PopoverArrow(props, forwardedRef) {
901
- const { scope, ...rest } = props
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
- componentName="PopoverArrow"
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 = React.forwardRef<ScrollView, PopoverScrollViewProps>(
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
- React.forwardRef<Popover, PopoverProps>(function Popover(
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} portal>
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 = React.forwardRef<
1034
+ const PopoverInner = createRefComponent<
996
1035
  Popover,
997
- PopoverProps & { id: string; adaptScope: string }
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: openProp,
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
- setOpen: handleOpenChange,
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
- <PopoverSheetController onOpenChange={setOpen} open={open} scope={scope}>
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
- }