Fix isolated bottom sheet modal lifecycle
This commit is contained in:
@@ -196,7 +196,7 @@ export function AdaptiveModalSheet({
|
|||||||
() => ({ backgroundColor: theme.colors.surface2 }),
|
() => ({ backgroundColor: theme.colors.surface2 }),
|
||||||
[theme.colors.surface2],
|
[theme.colors.surface2],
|
||||||
);
|
);
|
||||||
const { sheetRef, handleSheetChange } = useIsolatedBottomSheetVisibility({
|
const { sheetRef, handleSheetChange, handleSheetDismiss } = useIsolatedBottomSheetVisibility({
|
||||||
visible,
|
visible,
|
||||||
isEnabled: isMobile,
|
isEnabled: isMobile,
|
||||||
onClose,
|
onClose,
|
||||||
@@ -228,6 +228,7 @@ export function AdaptiveModalSheet({
|
|||||||
index={0}
|
index={0}
|
||||||
enableDynamicSizing={false}
|
enableDynamicSizing={false}
|
||||||
onChange={handleSheetChange}
|
onChange={handleSheetChange}
|
||||||
|
onDismiss={handleSheetDismiss}
|
||||||
backdropComponent={renderBackdrop}
|
backdropComponent={renderBackdrop}
|
||||||
enablePanDownToClose
|
enablePanDownToClose
|
||||||
backgroundComponent={SheetBackground}
|
backgroundComponent={SheetBackground}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { createContext, useContext, useCallback, useMemo, useRef, ReactNode } from "react";
|
import React, { createContext, useContext, useCallback, useMemo } from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
import { View, Text, Pressable } from "react-native";
|
import { View, Text, Pressable } from "react-native";
|
||||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||||
import Animated from "react-native-reanimated";
|
import Animated from "react-native-reanimated";
|
||||||
@@ -11,7 +12,7 @@ import { X } from "lucide-react-native";
|
|||||||
import type { ToolCallDetail } from "@server/server/agent/agent-sdk-types";
|
import type { ToolCallDetail } from "@server/server/agent/agent-sdk-types";
|
||||||
import {
|
import {
|
||||||
IsolatedBottomSheetModal,
|
IsolatedBottomSheetModal,
|
||||||
type IsolatedBottomSheetModalRef,
|
useIsolatedBottomSheetVisibility,
|
||||||
} from "@/components/ui/isolated-bottom-sheet-modal";
|
} from "@/components/ui/isolated-bottom-sheet-modal";
|
||||||
import { resolveToolCallIcon } from "@/utils/tool-call-icon";
|
import { resolveToolCallIcon } from "@/utils/tool-call-icon";
|
||||||
import { ToolCallDetailsContent } from "./tool-call-details";
|
import { ToolCallDetailsContent } from "./tool-call-details";
|
||||||
@@ -63,32 +64,33 @@ interface ToolCallSheetProviderProps {
|
|||||||
|
|
||||||
export function ToolCallSheetProvider({ children }: ToolCallSheetProviderProps) {
|
export function ToolCallSheetProvider({ children }: ToolCallSheetProviderProps) {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const bottomSheetRef = useRef<IsolatedBottomSheetModalRef>(null);
|
|
||||||
const hasPresentedRef = useRef(false);
|
|
||||||
const [sheetData, setSheetData] = React.useState<ToolCallSheetData | null>(null);
|
const [sheetData, setSheetData] = React.useState<ToolCallSheetData | null>(null);
|
||||||
|
const [isSheetOpen, setIsSheetOpen] = React.useState(false);
|
||||||
|
|
||||||
const snapPoints = useMemo(() => ["60%", "95%"], []);
|
const snapPoints = useMemo(() => ["60%", "95%"], []);
|
||||||
|
|
||||||
const openToolCall = useCallback((data: ToolCallSheetData) => {
|
const openToolCall = useCallback((data: ToolCallSheetData) => {
|
||||||
setSheetData(data);
|
setSheetData(data);
|
||||||
if (hasPresentedRef.current) {
|
setIsSheetOpen(true);
|
||||||
requestAnimationFrame(() => bottomSheetRef.current?.snapToIndex(0));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPresentedRef.current = true;
|
|
||||||
bottomSheetRef.current?.present();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const closeToolCall = useCallback(() => {
|
const closeToolCall = useCallback(() => {
|
||||||
bottomSheetRef.current?.close();
|
setIsSheetOpen(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSheetChange = useCallback((index: number) => {
|
const {
|
||||||
if (index === -1) {
|
sheetRef: bottomSheetRef,
|
||||||
setSheetData(null);
|
handleSheetChange,
|
||||||
}
|
handleSheetDismiss,
|
||||||
}, []);
|
} = useIsolatedBottomSheetVisibility({
|
||||||
|
visible: isSheetOpen,
|
||||||
|
onClose: closeToolCall,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleToolCallSheetDismiss = useCallback(() => {
|
||||||
|
handleSheetDismiss();
|
||||||
|
setSheetData(null);
|
||||||
|
}, [handleSheetDismiss]);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
const renderBackdrop = useCallback(
|
||||||
(props: React.ComponentProps<typeof BottomSheetBackdrop>) => (
|
(props: React.ComponentProps<typeof BottomSheetBackdrop>) => (
|
||||||
@@ -116,6 +118,7 @@ export function ToolCallSheetProvider({ children }: ToolCallSheetProviderProps)
|
|||||||
index={0}
|
index={0}
|
||||||
enableDynamicSizing={false}
|
enableDynamicSizing={false}
|
||||||
onChange={handleSheetChange}
|
onChange={handleSheetChange}
|
||||||
|
onDismiss={handleToolCallSheetDismiss}
|
||||||
backdropComponent={renderBackdrop}
|
backdropComponent={renderBackdrop}
|
||||||
enablePanDownToClose
|
enablePanDownToClose
|
||||||
backgroundComponent={CustomSheetBackground}
|
backgroundComponent={CustomSheetBackground}
|
||||||
|
|||||||
@@ -910,10 +910,13 @@ function resolveInitialActiveIndex(
|
|||||||
return selectedIndex >= 0 ? selectedIndex : fallbackIndex;
|
return selectedIndex >= 0 ? selectedIndex : fallbackIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BottomSheetVisibility = ReturnType<typeof useIsolatedBottomSheetVisibility>;
|
||||||
|
|
||||||
interface MobileBodyProps {
|
interface MobileBodyProps {
|
||||||
bottomSheetRef: ReturnType<typeof useIsolatedBottomSheetVisibility>["sheetRef"];
|
bottomSheetRef: BottomSheetVisibility["sheetRef"];
|
||||||
snapPoints: string[];
|
snapPoints: string[];
|
||||||
handleSheetChange: ReturnType<typeof useIsolatedBottomSheetVisibility>["handleSheetChange"];
|
handleSheetChange: BottomSheetVisibility["handleSheetChange"];
|
||||||
|
handleSheetDismiss: BottomSheetVisibility["handleSheetDismiss"];
|
||||||
handleIndicatorStyle: { backgroundColor: string };
|
handleIndicatorStyle: { backgroundColor: string };
|
||||||
titleColor: string;
|
titleColor: string;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -971,6 +974,7 @@ function MobileComboboxBody(props: MobileBodyProps): ReactElement {
|
|||||||
index={0}
|
index={0}
|
||||||
enableDynamicSizing={false}
|
enableDynamicSizing={false}
|
||||||
onChange={props.handleSheetChange}
|
onChange={props.handleSheetChange}
|
||||||
|
onDismiss={props.handleSheetDismiss}
|
||||||
backdropComponent={renderBackdrop}
|
backdropComponent={renderBackdrop}
|
||||||
enablePanDownToClose
|
enablePanDownToClose
|
||||||
backgroundComponent={ComboboxSheetBackground}
|
backgroundComponent={ComboboxSheetBackground}
|
||||||
@@ -1298,7 +1302,11 @@ export function Combobox({
|
|||||||
referenceWidth,
|
referenceWidth,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { sheetRef: bottomSheetRef, handleSheetChange } = useIsolatedBottomSheetVisibility({
|
const {
|
||||||
|
sheetRef: bottomSheetRef,
|
||||||
|
handleSheetChange,
|
||||||
|
handleSheetDismiss,
|
||||||
|
} = useIsolatedBottomSheetVisibility({
|
||||||
visible: isOpen,
|
visible: isOpen,
|
||||||
isEnabled: isMobile,
|
isEnabled: isMobile,
|
||||||
onClose: handleClose,
|
onClose: handleClose,
|
||||||
@@ -1454,6 +1462,7 @@ export function Combobox({
|
|||||||
bottomSheetRef={bottomSheetRef}
|
bottomSheetRef={bottomSheetRef}
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
handleSheetChange={handleSheetChange}
|
handleSheetChange={handleSheetChange}
|
||||||
|
handleSheetDismiss={handleSheetDismiss}
|
||||||
handleIndicatorStyle={handleIndicatorStyle}
|
handleIndicatorStyle={handleIndicatorStyle}
|
||||||
titleColor={titleColor}
|
titleColor={titleColor}
|
||||||
title={title}
|
title={title}
|
||||||
|
|||||||
@@ -400,7 +400,11 @@ export function ContextMenuContent({
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
}, [setOpen]);
|
}, [setOpen]);
|
||||||
|
|
||||||
const { sheetRef: bottomSheetRef, handleSheetChange } = useIsolatedBottomSheetVisibility({
|
const {
|
||||||
|
sheetRef: bottomSheetRef,
|
||||||
|
handleSheetChange,
|
||||||
|
handleSheetDismiss,
|
||||||
|
} = useIsolatedBottomSheetVisibility({
|
||||||
visible: open,
|
visible: open,
|
||||||
isEnabled: useMobileSheet,
|
isEnabled: useMobileSheet,
|
||||||
onClose: handleClose,
|
onClose: handleClose,
|
||||||
@@ -534,6 +538,7 @@ export function ContextMenuContent({
|
|||||||
snapPoints={sheetSnapPoints}
|
snapPoints={sheetSnapPoints}
|
||||||
enableDynamicSizing={false}
|
enableDynamicSizing={false}
|
||||||
onChange={handleSheetChange}
|
onChange={handleSheetChange}
|
||||||
|
onDismiss={handleSheetDismiss}
|
||||||
backdropComponent={renderSheetBackdrop}
|
backdropComponent={renderSheetBackdrop}
|
||||||
enablePanDownToClose
|
enablePanDownToClose
|
||||||
backgroundStyle={sheetBackgroundStyle}
|
backgroundStyle={sheetBackgroundStyle}
|
||||||
|
|||||||
@@ -14,7 +14,12 @@ const SNAP_POINTS_90: (string | number)[] = ["90%"];
|
|||||||
|
|
||||||
function noop(): void {}
|
function noop(): void {}
|
||||||
|
|
||||||
const { modalMethods, modalProps } = vi.hoisted(() => ({
|
interface CapturedModalProps {
|
||||||
|
onChange?: (index: number) => void;
|
||||||
|
onDismiss?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { modalMethods, modalProps, shouldExposeModalRef } = vi.hoisted(() => ({
|
||||||
modalMethods: {
|
modalMethods: {
|
||||||
present: vi.fn(),
|
present: vi.fn(),
|
||||||
close: vi.fn(),
|
close: vi.fn(),
|
||||||
@@ -22,6 +27,7 @@ const { modalMethods, modalProps } = vi.hoisted(() => ({
|
|||||||
dismiss: vi.fn(),
|
dismiss: vi.fn(),
|
||||||
},
|
},
|
||||||
modalProps: vi.fn(),
|
modalProps: vi.fn(),
|
||||||
|
shouldExposeModalRef: { current: true },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("@gorhom/bottom-sheet", async () => {
|
vi.mock("@gorhom/bottom-sheet", async () => {
|
||||||
@@ -29,7 +35,7 @@ vi.mock("@gorhom/bottom-sheet", async () => {
|
|||||||
const MockBottomSheetModal = React.forwardRef(
|
const MockBottomSheetModal = React.forwardRef(
|
||||||
(props: Record<string, unknown>, ref: React.ForwardedRef<unknown>) => {
|
(props: Record<string, unknown>, ref: React.ForwardedRef<unknown>) => {
|
||||||
modalProps(props);
|
modalProps(props);
|
||||||
React.useImperativeHandle(ref, () => modalMethods);
|
React.useImperativeHandle(ref, () => (shouldExposeModalRef.current ? modalMethods : null));
|
||||||
return React.createElement(
|
return React.createElement(
|
||||||
"div",
|
"div",
|
||||||
{ "data-testid": "bottom-sheet" },
|
{ "data-testid": "bottom-sheet" },
|
||||||
@@ -54,7 +60,7 @@ vi.mock("@gorhom/portal", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function Harness({ visible, onClose }: { visible: boolean; onClose: () => void }) {
|
function Harness({ visible, onClose }: { visible: boolean; onClose: () => void }) {
|
||||||
const { sheetRef, handleSheetChange } = useIsolatedBottomSheetVisibility({
|
const { sheetRef, handleSheetChange, handleSheetDismiss } = useIsolatedBottomSheetVisibility({
|
||||||
visible,
|
visible,
|
||||||
onClose,
|
onClose,
|
||||||
});
|
});
|
||||||
@@ -65,26 +71,34 @@ function Harness({ visible, onClose }: { visible: boolean; onClose: () => void }
|
|||||||
index={0}
|
index={0}
|
||||||
snapPoints={SNAP_POINTS_50}
|
snapPoints={SNAP_POINTS_50}
|
||||||
onChange={handleSheetChange}
|
onChange={handleSheetChange}
|
||||||
|
onDismiss={handleSheetDismiss}
|
||||||
>
|
>
|
||||||
<div>Sheet content</div>
|
<div>Sheet content</div>
|
||||||
</IsolatedBottomSheetModal>
|
</IsolatedBottomSheetModal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function latestModalProps(): CapturedModalProps {
|
||||||
|
const props = modalProps.mock.lastCall?.[0];
|
||||||
|
expect(props).toBeDefined();
|
||||||
|
return props as CapturedModalProps;
|
||||||
|
}
|
||||||
|
|
||||||
describe("IsolatedBottomSheetModal", () => {
|
describe("IsolatedBottomSheetModal", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
cleanup();
|
cleanup();
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
shouldExposeModalRef.current = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("forces sheet isolation and keeps modal content mounted while hidden", () => {
|
it("forces sheet isolation and uses the modal dismissal lifecycle", () => {
|
||||||
const onClose = vi.fn();
|
const onClose = vi.fn();
|
||||||
const { getByTestId, rerender } = render(<Harness visible={false} onClose={onClose} />);
|
const { getByTestId, rerender } = render(<Harness visible={false} onClose={onClose} />);
|
||||||
|
|
||||||
expect(getByTestId("app-portal").getAttribute("data-host")).toBe("root");
|
expect(getByTestId("app-portal").getAttribute("data-host")).toBe("root");
|
||||||
expect(modalProps).toHaveBeenLastCalledWith(
|
expect(modalProps).toHaveBeenLastCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
enableDismissOnClose: false,
|
enableDismissOnClose: true,
|
||||||
stackBehavior: "replace",
|
stackBehavior: "replace",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -94,28 +108,61 @@ describe("IsolatedBottomSheetModal", () => {
|
|||||||
expect(modalMethods.present).toHaveBeenCalledTimes(1);
|
expect(modalMethods.present).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
rerender(<Harness visible={false} onClose={onClose} />);
|
rerender(<Harness visible={false} onClose={onClose} />);
|
||||||
expect(modalMethods.close).toHaveBeenCalledTimes(1);
|
expect(modalMethods.dismiss).toHaveBeenCalledTimes(1);
|
||||||
expect(modalMethods.dismiss).not.toHaveBeenCalled();
|
expect(modalMethods.close).not.toHaveBeenCalled();
|
||||||
|
|
||||||
rerender(<Harness visible onClose={onClose} />);
|
rerender(<Harness visible onClose={onClose} />);
|
||||||
expect(modalMethods.present).toHaveBeenCalledTimes(1);
|
expect(modalMethods.present).toHaveBeenCalledTimes(2);
|
||||||
expect(modalMethods.snapToIndex).toHaveBeenCalledWith(0);
|
expect(modalMethods.snapToIndex).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("only reports a user close when the sheet was visible", () => {
|
it("only reports a user close when the sheet was visible", () => {
|
||||||
const onClose = vi.fn();
|
const onClose = vi.fn();
|
||||||
const { rerender } = render(<Harness visible onClose={onClose} />);
|
const { rerender } = render(<Harness visible onClose={onClose} />);
|
||||||
|
|
||||||
const latestProps = modalProps.mock.lastCall?.[0] as { onChange: (index: number) => void };
|
latestModalProps().onChange?.(-1);
|
||||||
latestProps.onChange(-1);
|
|
||||||
expect(onClose).toHaveBeenCalledTimes(1);
|
expect(onClose).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
rerender(<Harness visible={false} onClose={onClose} />);
|
rerender(<Harness visible={false} onClose={onClose} />);
|
||||||
const closedProps = modalProps.mock.lastCall?.[0] as { onChange: (index: number) => void };
|
latestModalProps().onChange?.(-1);
|
||||||
closedProps.onChange(-1);
|
|
||||||
expect(onClose).toHaveBeenCalledTimes(1);
|
expect(onClose).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reports a dismiss while visible as a close request", () => {
|
||||||
|
const onClose = vi.fn();
|
||||||
|
render(<Harness visible onClose={onClose} />);
|
||||||
|
|
||||||
|
latestModalProps().onDismiss?.();
|
||||||
|
|
||||||
|
expect(onClose).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("deduplicates close notifications from change and dismiss callbacks", () => {
|
||||||
|
const onClose = vi.fn();
|
||||||
|
render(<Harness visible onClose={onClose} />);
|
||||||
|
|
||||||
|
const props = latestModalProps();
|
||||||
|
props.onChange?.(-1);
|
||||||
|
props.onDismiss?.();
|
||||||
|
|
||||||
|
expect(onClose).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("presents when the sheet ref becomes available after opening", () => {
|
||||||
|
shouldExposeModalRef.current = false;
|
||||||
|
const onClose = vi.fn();
|
||||||
|
const { rerender } = render(<Harness visible onClose={onClose} />);
|
||||||
|
|
||||||
|
expect(modalMethods.present).not.toHaveBeenCalled();
|
||||||
|
expect(modalMethods.snapToIndex).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
shouldExposeModalRef.current = true;
|
||||||
|
rerender(<Harness visible onClose={onClose} />);
|
||||||
|
|
||||||
|
expect(modalMethods.present).toHaveBeenCalledTimes(1);
|
||||||
|
expect(modalMethods.snapToIndex).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("allows nested sheets inside a parent sheet without creating a sibling provider", () => {
|
it("allows nested sheets inside a parent sheet without creating a sibling provider", () => {
|
||||||
const { getAllByTestId } = render(
|
const { getAllByTestId } = render(
|
||||||
<IsolatedBottomSheetModal index={0} snapPoints={SNAP_POINTS_90}>
|
<IsolatedBottomSheetModal index={0} snapPoints={SNAP_POINTS_90}>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const IsolatedBottomSheetModal = forwardRef<
|
|||||||
<GorhomBottomSheetModal
|
<GorhomBottomSheetModal
|
||||||
{...bottomSheetProps}
|
{...bottomSheetProps}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
enableDismissOnClose={false}
|
enableDismissOnClose
|
||||||
stackBehavior={isNestedSheet ? "push" : "replace"}
|
stackBehavior={isNestedSheet ? "push" : "replace"}
|
||||||
>
|
>
|
||||||
{scopedChildren}
|
{scopedChildren}
|
||||||
@@ -68,39 +68,92 @@ export function useIsolatedBottomSheetVisibility({
|
|||||||
isEnabled?: boolean;
|
isEnabled?: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) {
|
}) {
|
||||||
const sheetRef = useRef<IsolatedBottomSheetModalRef>(null);
|
const sheetRef = useRef<IsolatedBottomSheetModalRef | null>(null);
|
||||||
const hasPresentedRef = useRef(false);
|
const visibleRef = useRef(visible);
|
||||||
|
const isEnabledRef = useRef(isEnabled);
|
||||||
|
const isPresentedRef = useRef(false);
|
||||||
|
const hasNotifiedCloseRef = useRef(false);
|
||||||
|
|
||||||
|
visibleRef.current = visible;
|
||||||
|
isEnabledRef.current = isEnabled;
|
||||||
|
|
||||||
|
const presentSheet = useCallback((sheet: IsolatedBottomSheetModalRef) => {
|
||||||
|
if (isPresentedRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPresentedRef.current = true;
|
||||||
|
hasNotifiedCloseRef.current = false;
|
||||||
|
sheet.present();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const dismissSheet = useCallback((sheet: IsolatedBottomSheetModalRef) => {
|
||||||
|
if (!isPresentedRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPresentedRef.current = false;
|
||||||
|
sheet.dismiss();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const notifyClose = useCallback(() => {
|
||||||
|
if (hasNotifiedCloseRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNotifiedCloseRef.current = true;
|
||||||
|
onClose();
|
||||||
|
}, [onClose]);
|
||||||
|
|
||||||
|
const handleSheetDismiss = useCallback(() => {
|
||||||
|
isPresentedRef.current = false;
|
||||||
|
if (visibleRef.current) {
|
||||||
|
notifyClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hasNotifiedCloseRef.current = false;
|
||||||
|
}, [notifyClose]);
|
||||||
|
|
||||||
|
const handleSheetChange = useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
if (index === -1 && visibleRef.current) {
|
||||||
|
notifyClose();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[notifyClose],
|
||||||
|
);
|
||||||
|
|
||||||
|
const setSheetRef = useCallback(
|
||||||
|
(instance: IsolatedBottomSheetModalRef | null) => {
|
||||||
|
sheetRef.current = instance;
|
||||||
|
if (instance && visibleRef.current && isEnabledRef.current !== false) {
|
||||||
|
presentSheet(instance);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[presentSheet],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isEnabled === false) return;
|
if (isEnabled === false) return;
|
||||||
|
|
||||||
|
const sheet = sheetRef.current;
|
||||||
if (visible) {
|
if (visible) {
|
||||||
if (hasPresentedRef.current) {
|
if (!sheet) {
|
||||||
sheetRef.current?.snapToIndex(0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPresentedRef.current = true;
|
presentSheet(sheet);
|
||||||
sheetRef.current?.present();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPresentedRef.current) {
|
if (sheet) {
|
||||||
sheetRef.current?.close();
|
dismissSheet(sheet);
|
||||||
}
|
}
|
||||||
}, [isEnabled, visible]);
|
}, [dismissSheet, isEnabled, presentSheet, visible]);
|
||||||
|
|
||||||
const handleSheetChange = useCallback(
|
|
||||||
(index: number) => {
|
|
||||||
if (index === -1 && visible) {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[onClose, visible],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sheetRef,
|
sheetRef: setSheetRef,
|
||||||
handleSheetChange,
|
handleSheetChange,
|
||||||
|
handleSheetDismiss,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user