From 8f440d61df8d6c72787f6c7ebdb4451397f8ef82 Mon Sep 17 00:00:00 2001 From: Amauri Dias Date: Fri, 17 Oct 2025 08:59:39 -0300 Subject: [PATCH] feat: add closeOnBackPress prop to Dialog --- apps/docs/src/content/docs/dialog.mdx | 43 ++++++++++++++------------- packages/dialog/src/dialog.tsx | 21 ++++++++++--- packages/dialog/src/types.ts | 10 +++++++ 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/apps/docs/src/content/docs/dialog.mdx b/apps/docs/src/content/docs/dialog.mdx index 6c818991..6cd16b2d 100644 --- a/apps/docs/src/content/docs/dialog.mdx +++ b/apps/docs/src/content/docs/dialog.mdx @@ -21,7 +21,7 @@ A modal dialog that interrupts the user with important content and expects a res - Install the component via your command line. +Install the component via your command line. ```bash npx expo install @rn-primitives/dialog @@ -86,21 +86,21 @@ A modal dialog that interrupts the user with important content and expects a res @@ -118,7 +118,7 @@ function Example() { - Dialog Title + Dialog Title Dialog description. @@ -137,12 +137,13 @@ function Example() { Extends [`View`](https://reactnative.dev/docs/view#props) props -| Prop | Type | Note | -| :----------: | :----------------------: | :----------: | -| asChild | boolean | _(optional)_ | -| open | boolean | _(optional)_ | -| onOpenChange | (value: boolean) => void | _(optional)_ | -| defaultOpen | boolean | _(optional)_ | +| Prop | Type | Note | +| :--------------: | :----------------------: | :----------------------: | +| asChild | boolean | _(optional)_ | +| open | boolean | _(optional)_ | +| onOpenChange | (value: boolean) => void | _(optional)_ | +| defaultOpen | boolean | _(optional)_ | +| closeOnBackPress | boolean | Native Only _(optional)_ | ### Trigger diff --git a/packages/dialog/src/dialog.tsx b/packages/dialog/src/dialog.tsx index 03657774..32ab8fbc 100644 --- a/packages/dialog/src/dialog.tsx +++ b/packages/dialog/src/dialog.tsx @@ -25,7 +25,17 @@ import type { const DialogContext = React.createContext<(RootContext & { nativeID: string }) | null>(null); const Root = React.forwardRef( - ({ asChild, open: openProp, defaultOpen, onOpenChange: onOpenChangeProp, ...viewProps }, ref) => { + ( + { + asChild, + open: openProp, + defaultOpen, + onOpenChange: onOpenChangeProp, + closeOnBackPress = true, + ...viewProps + }, + ref + ) => { const nativeID = React.useId(); const [open = false, onOpenChange] = useControllableState({ prop: openProp, @@ -40,6 +50,7 @@ const Root = React.forwardRef( open, onOpenChange, nativeID, + closeOnBackPress, }} > @@ -130,18 +141,20 @@ Overlay.displayName = 'OverlayNativeDialog'; const Content = React.forwardRef( ({ asChild, forceMount, ...props }, ref) => { - const { open, nativeID, onOpenChange } = useRootContext(); + const { open, nativeID, onOpenChange, closeOnBackPress } = useRootContext(); React.useEffect(() => { const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - onOpenChange(false); + if (closeOnBackPress) { + onOpenChange(false); + } return true; }); return () => { backHandler.remove(); }; - }, []); + }, [closeOnBackPress]); if (!forceMount) { if (!open) { diff --git a/packages/dialog/src/types.ts b/packages/dialog/src/types.ts index 5cbf6bab..dfbe319e 100644 --- a/packages/dialog/src/types.ts +++ b/packages/dialog/src/types.ts @@ -11,12 +11,22 @@ import type { type RootContext = { open: boolean; onOpenChange: (value: boolean) => void; + + /** + * Platform: NATIVE ONLY + */ + closeOnBackPress?: boolean; }; type RootProps = SlottableViewProps & { open?: boolean; defaultOpen?: boolean; onOpenChange?: (value: boolean) => void; + + /** + * Platform: NATIVE ONLY + */ + closeOnBackPress?: boolean; }; interface PortalProps extends ForceMountable {