Open History to see the last commit
1import type { ReactNode, RefObject } from "react";
2import { createContext, useContext, useRef, useState } from "react";
3import { View, type ViewInstance } from "react-native";
4import type { SharedValue } from "react-native-reanimated";
5
6type ComposerPopoverHostValue = {
7 readonly hostRef: RefObject<ViewInstance | null>;
8 readonly setContent: (content: ReactNode) => void;
9 readonly layoutVersion: number;
10 readonly popoverHeight: SharedValue<number>;
11};
12
13const ComposerPopoverHostContext = createContext<ComposerPopoverHostValue | null>(null);
14
15export function useComposerPopoverHost() {
16 return useContext(ComposerPopoverHostContext);
17}
18
19/** Mount inside KeyboardStickyView for Android gestures. Hide explicitly because portal content outlives its subtree. */
20export function ComposerPopoverHost(props: {
21 readonly hidden: boolean;
22 readonly inFlow?: boolean;
23 readonly popoverHeight: SharedValue<number>;
24 readonly children: ReactNode;
25}) {
26 const hostRef = useRef<ViewInstance>(null);
27 const [content, setContent] = useState<ReactNode>(null);
28 const [layoutVersion, setLayoutVersion] = useState(0);
29 const value = { hostRef, setContent, layoutVersion, popoverHeight: props.popoverHeight };
30
31 return (
32 <ComposerPopoverHostContext.Provider value={value}>
33 <View
34 ref={hostRef}
35 collapsable={false}
36 pointerEvents="box-none"
37 className={props.inFlow ? undefined : "absolute inset-0"}
38 onLayout={props.hidden ? undefined : () => setLayoutVersion((version) => version + 1)}
39 >
40 {props.children}
41 {props.hidden ? null : content}
42 </View>
43 </ComposerPopoverHostContext.Provider>
44 );
45}