diff --git a/apps/expo-nativewind/app/(components)/dialog.tsx b/apps/expo-nativewind/app/(components)/dialog.tsx
index b48ae521..c863fed5 100644
--- a/apps/expo-nativewind/app/(components)/dialog.tsx
+++ b/apps/expo-nativewind/app/(components)/dialog.tsx
@@ -30,10 +30,8 @@ export default function DialogScreen() {
-
-
+
+ OK
diff --git a/apps/expo-nativewind/components/ui/dialog.tsx b/apps/expo-nativewind/components/ui/dialog.tsx
index 75648fb8..da4fd404 100644
--- a/apps/expo-nativewind/components/ui/dialog.tsx
+++ b/apps/expo-nativewind/components/ui/dialog.tsx
@@ -1,7 +1,10 @@
+import { Platform, View } from '@rn-primitives/core';
import * as DialogPrimitive from '@rn-primitives/dialog';
+import { mergeProps } from '@rn-primitives/utils';
import * as React from 'react';
-import { Platform, StyleSheet, View } from 'react-native';
-import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
+import { FadeIn, FadeOut, ZoomIn, ZoomOut } from 'react-native-reanimated';
+import { buttonTextVariants, buttonVariants } from '~/components/ui/button';
+import { TextClassContext } from '~/components/ui/text';
import { X } from '~/lib/icons/X';
import { cn } from '~/lib/utils';
@@ -11,98 +14,83 @@ const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
-const DialogClose = DialogPrimitive.Close;
+const OVERLAY_NATIVE_PROPS = {
+ isAnimated: true,
+ entering: FadeIn,
+ exiting: FadeOut.duration(150),
+};
-const DialogOverlayWeb = ({
- ref,
- className,
- ...props
-}: React.ComponentPropsWithoutRef & {
- ref?: React.RefObject>;
-}) => {
- const { open } = DialogPrimitive.useRootContext();
+function DialogOverlay({ className, native, ...props }: DialogPrimitive.OverlayProps) {
return (
);
-};
+}
-DialogOverlayWeb.displayName = 'DialogOverlayWeb';
+const CONTENT_NATIVE_PROPS = {
+ isAnimated: true,
+ entering: ZoomIn.duration(200).withInitialValues({ transform: [{ scale: 0.85 }] }),
+ exiting: ZoomOut.duration(400),
+};
-const DialogOverlayNative = ({
- ref,
+function DialogContent({
className,
children,
+ native: { portalHost, ...nativeProp } = {},
...props
-}: React.ComponentPropsWithoutRef & {
- ref?: React.RefObject>;
-}) => {
- return (
-
-
- <>{children}>
-
-
- );
-};
-
-DialogOverlayNative.displayName = 'DialogOverlayNative';
-
-const DialogOverlay = Platform.select({
- web: DialogOverlayWeb,
- default: DialogOverlayNative,
-});
-
-const DialogContent = ({ ref, className, children, portalHost, ...props }) => {
+}: Omit & {
+ native?: DialogPrimitive.ContentProps['native'] & { portalHost?: string };
+}) {
const { open } = DialogPrimitive.useRootContext();
return (
-
+
-
- {children}
-
+ {/* DialogPrimitive.Content uses `nativeID` for accessibility, so it prevents the entering animation from working https://docs.swmansion.com/react-native-reanimated/docs/layout-animations/entering-exiting-animations/#remarks */}
+
-
-
+ {children}
+
+
+
+
);
-};
-DialogContent.displayName = DialogPrimitive.Content.displayName;
+}
const DialogHeader = ({ className, ...props }: React.ComponentPropsWithoutRef) => (
);
-DialogHeader.displayName = 'DialogHeader';
const DialogFooter = ({ className, ...props }: React.ComponentPropsWithoutRef) => (
);
-DialogFooter.displayName = 'DialogFooter';
-const DialogTitle = ({
- ref,
- className,
- ...props
-}: React.ComponentPropsWithoutRef & {
- ref?: React.RefObject>;
-}) => (
-
-);
-DialogTitle.displayName = DialogPrimitive.Title.displayName;
+function DialogTitle({ className, ...props }: DialogPrimitive.TitleProps) {
+ return (
+
+ );
+}
-const DialogDescription = ({
- ref,
- className,
- ...props
-}: React.ComponentPropsWithoutRef & {
- ref?: React.RefObject>;
-}) => (
-
+function DialogDescription({ className, ...props }: DialogPrimitive.DescriptionProps) {
+ return (
+
+ );
+}
+
+const DialogClose = ({ className, ...props }: DialogPrimitive.CloseProps) => (
+
+
+
);
-DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
diff --git a/apps/nextjs-nativewind/src/app/page.tsx b/apps/nextjs-nativewind/src/app/page.tsx
index 1fc47cc3..5d550962 100644
--- a/apps/nextjs-nativewind/src/app/page.tsx
+++ b/apps/nextjs-nativewind/src/app/page.tsx
@@ -1,4 +1,5 @@
import { View } from '@rn-primitives/core';
+import { CollapsibleExample } from '~/components/CollapsibleExample';
import { Core } from '~/components/core';
import { ToggleExample } from '~/components/ToggleExample';
import { ToggleGroupExample } from '~/components/ToggleGroupExample';
@@ -22,7 +23,6 @@ import {
import { AspectRatio } from '~/components/ui/aspect-ratio';
import { Avatar, AvatarFallback, AvatarImage } from '~/components/ui/avatar';
import { Button } from '~/components/ui/button';
-import { CollapsibleExample } from '~/components/CollapsibleExample';
import {
Dialog,
DialogClose,
@@ -103,6 +103,7 @@ export default function Home() {
+
@@ -110,7 +111,6 @@ export default function Home() {
{/*
-
@@ -243,10 +243,8 @@ function DialogExample() {
-
-
+
+ OK
diff --git a/apps/nextjs-nativewind/src/components/ui/dialog.tsx b/apps/nextjs-nativewind/src/components/ui/dialog.tsx
index 59b86507..f30608b8 100644
--- a/apps/nextjs-nativewind/src/components/ui/dialog.tsx
+++ b/apps/nextjs-nativewind/src/components/ui/dialog.tsx
@@ -1,9 +1,12 @@
'use client';
+import { Platform, View } from '@rn-primitives/core';
import * as DialogPrimitive from '@rn-primitives/dialog';
+import { mergeProps } from '@rn-primitives/utils';
import * as React from 'react';
-import { Platform, StyleSheet, View } from 'react-native';
-import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
+import { FadeIn, FadeOut, ZoomIn, ZoomOut } from 'react-native-reanimated';
+import { buttonTextVariants, buttonVariants } from '~/components/ui/button';
+import { TextClassContext } from '~/components/ui/text';
import { X } from '~/lib/icons/X';
import { cn } from '~/lib/utils';
@@ -13,98 +16,83 @@ const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
-const DialogClose = DialogPrimitive.Close;
+const OVERLAY_NATIVE_PROPS = {
+ isAnimated: true,
+ entering: FadeIn,
+ exiting: FadeOut.duration(150),
+};
-const DialogOverlayWeb = ({
- ref,
- className,
- ...props
-}: React.ComponentPropsWithoutRef & {
- ref?: React.RefObject>;
-}) => {
- const { open } = DialogPrimitive.useRootContext();
+function DialogOverlay({ className, native, ...props }: DialogPrimitive.OverlayProps) {
return (
);
-};
+}
-DialogOverlayWeb.displayName = 'DialogOverlayWeb';
+const CONTENT_NATIVE_PROPS = {
+ isAnimated: true,
+ entering: ZoomIn.duration(200).withInitialValues({ transform: [{ scale: 0.85 }] }),
+ exiting: ZoomOut.duration(400),
+};
-const DialogOverlayNative = ({
- ref,
+function DialogContent({
className,
children,
+ native: { portalHost, ...nativeProp } = {},
...props
-}: React.ComponentPropsWithoutRef & {
- ref?: React.RefObject>;
-}) => {
- return (
-
-
- <>{children}>
-
-
- );
-};
-
-DialogOverlayNative.displayName = 'DialogOverlayNative';
-
-const DialogOverlay = Platform.select({
- web: DialogOverlayWeb,
- default: DialogOverlayNative,
-});
-
-const DialogContent = ({ ref, className, children, portalHost, ...props }) => {
+}: Omit & {
+ native?: DialogPrimitive.ContentProps['native'] & { portalHost?: string };
+}) {
const { open } = DialogPrimitive.useRootContext();
return (
-
+
-
- {children}
-
+ {/* DialogPrimitive.Content uses `nativeID` for accessibility, so it prevents the entering animation from working https://docs.swmansion.com/react-native-reanimated/docs/layout-animations/entering-exiting-animations/#remarks */}
+
-
-
+ {children}
+
+
+
+
);
-};
-DialogContent.displayName = DialogPrimitive.Content.displayName;
+}
const DialogHeader = ({ className, ...props }: React.ComponentPropsWithoutRef) => (
);
-DialogHeader.displayName = 'DialogHeader';
const DialogFooter = ({ className, ...props }: React.ComponentPropsWithoutRef) => (
);
-DialogFooter.displayName = 'DialogFooter';
-const DialogTitle = ({
- ref,
- className,
- ...props
-}: React.ComponentPropsWithoutRef & {
- ref?: React.RefObject>;
-}) => (
-
-);
-DialogTitle.displayName = DialogPrimitive.Title.displayName;
+function DialogTitle({ className, ...props }: DialogPrimitive.TitleProps) {
+ return (
+
+ );
+}
-const DialogDescription = ({
- ref,
- className,
- ...props
-}: React.ComponentPropsWithoutRef & {
- ref?: React.RefObject>;
-}) => (
-
+function DialogDescription({ className, ...props }: DialogPrimitive.DescriptionProps) {
+ return (
+
+ );
+}
+
+const DialogClose = ({ className, ...props }: DialogPrimitive.CloseProps) => (
+
+
+
);
-DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
diff --git a/apps/nextjs-no-rn/package.json b/apps/nextjs-no-rn/package.json
index 3f375236..2e1aa9b0 100644
--- a/apps/nextjs-no-rn/package.json
+++ b/apps/nextjs-no-rn/package.json
@@ -17,6 +17,7 @@
"@rn-primitives/checkbox": "workspace:*",
"@rn-primitives/collapsible": "workspace:*",
"@rn-primitives/core": "workspace:*",
+ "@rn-primitives/dialog": "workspace:*",
"@rn-primitives/alert-dialog": "workspace:*",
"@rn-primitives/aspect-ratio": "workspace:*",
"@rn-primitives/avatar": "workspace:*",
diff --git a/apps/nextjs-no-rn/src/app/page.tsx b/apps/nextjs-no-rn/src/app/page.tsx
index ce591189..77eb5702 100644
--- a/apps/nextjs-no-rn/src/app/page.tsx
+++ b/apps/nextjs-no-rn/src/app/page.tsx
@@ -23,6 +23,16 @@ import { Avatar, AvatarFallback, AvatarImage } from '~/components/ui/avatar';
import { Button } from '~/components/ui/button';
import { Checkbox } from '~/components/ui/checkbox';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/components/ui/collapsible';
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from '~/components/ui/dialog';
import { Label } from '~/components/ui/label';
import { Progress } from '~/components/ui/progress';
import { Separator } from '~/components/ui/separator';
@@ -47,6 +57,7 @@ export default function Home() {
+
@@ -196,6 +207,32 @@ function CollapsibleExample() {
);
}
+function DialogExample() {
+ return (
+
+ );
+}
+
function LabelExample() {
return (
diff --git a/apps/nextjs-no-rn/src/components/ui/alert-dialog.tsx b/apps/nextjs-no-rn/src/components/ui/alert-dialog.tsx
index e34efc39..b5155ac4 100644
--- a/apps/nextjs-no-rn/src/components/ui/alert-dialog.tsx
+++ b/apps/nextjs-no-rn/src/components/ui/alert-dialog.tsx
@@ -3,9 +3,9 @@
import * as AlertDialogPrimitive from '@rn-primitives/alert-dialog';
import { Platform, View } from '@rn-primitives/core';
import { FadeIn, FadeOut, ZoomIn, ZoomOut } from '@rn-primitives/core/native-only-reanimated';
+import { mergeProps } from '@rn-primitives/utils';
import * as React from 'react';
import { Button } from '~/components/ui/button';
-import { mergeProps } from '@rn-primitives/utils';
import { cn } from '~/lib/utils';
const AlertDialog = AlertDialogPrimitive.Root;
diff --git a/apps/nextjs-no-rn/src/components/ui/dialog.tsx b/apps/nextjs-no-rn/src/components/ui/dialog.tsx
new file mode 100644
index 00000000..9091143c
--- /dev/null
+++ b/apps/nextjs-no-rn/src/components/ui/dialog.tsx
@@ -0,0 +1,141 @@
+'use client';
+
+import { Platform, View } from '@rn-primitives/core';
+import { FadeIn, FadeOut, ZoomIn, ZoomOut } from '@rn-primitives/core/native-only-reanimated';
+import * as DialogPrimitive from '@rn-primitives/dialog';
+import { mergeProps } from '@rn-primitives/utils';
+import { X } from 'lucide-react';
+import * as React from 'react';
+import { Button } from '~/components/ui/button';
+import { cn } from '~/lib/utils';
+
+const Dialog = DialogPrimitive.Root;
+
+const DialogTrigger = DialogPrimitive.Trigger;
+
+const DialogPortal = DialogPrimitive.Portal;
+
+const OVERLAY_NATIVE_PROPS = {
+ isAnimated: true,
+ entering: FadeIn,
+ exiting: FadeOut.duration(150),
+};
+
+function DialogOverlay({ className, native, ...props }: DialogPrimitive.OverlayProps) {
+ return (
+
+ );
+}
+
+const CONTENT_NATIVE_PROPS = {
+ isAnimated: true,
+ entering: ZoomIn.duration(200).withInitialValues({ transform: [{ scale: 0.85 }] }),
+ exiting: ZoomOut.duration(400),
+};
+
+function DialogContent({
+ className,
+ children,
+ native: { portalHost, ...nativeProp } = {},
+ ...props
+}: Omit & {
+ native?: DialogPrimitive.ContentProps['native'] & { portalHost?: string };
+}) {
+ const { open } = DialogPrimitive.useRootContext();
+ return (
+
+
+
+ {/* DialogPrimitive.Content uses `nativeID` for accessibility, so it prevents the entering animation from working https://docs.swmansion.com/react-native-reanimated/docs/layout-animations/entering-exiting-animations/#remarks */}
+
+ {children}
+
+
+
+
+
+
+
+ );
+}
+
+const DialogHeader = ({ className, ...props }: React.ComponentPropsWithoutRef) => (
+
+);
+
+const DialogFooter = ({ className, ...props }: React.ComponentPropsWithoutRef) => (
+
+);
+
+function DialogTitle({ className, ...props }: DialogPrimitive.TitleProps) {
+ return (
+
+ );
+}
+
+function DialogDescription({ className, ...props }: DialogPrimitive.DescriptionProps) {
+ return (
+
+ );
+}
+
+const DialogClose = ({ className, ...props }: DialogPrimitive.CloseProps) => (
+
+);
+
+export {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogOverlay,
+ DialogPortal,
+ DialogTitle,
+ DialogTrigger,
+};
diff --git a/apps/vite-tanstack-router/package.json b/apps/vite-tanstack-router/package.json
index 18aa04f1..541afd0a 100644
--- a/apps/vite-tanstack-router/package.json
+++ b/apps/vite-tanstack-router/package.json
@@ -18,6 +18,7 @@
"@rn-primitives/avatar": "workspace:*",
"@rn-primitives/checkbox": "workspace:*",
"@rn-primitives/collapsible": "workspace:*",
+ "@rn-primitives/dialog": "workspace:*",
"@rn-primitives/label": "workspace:*",
"@rn-primitives/progress": "workspace:*",
"@rn-primitives/separator": "workspace:*",
diff --git a/apps/vite-tanstack-router/src/components/ui/dialog.tsx b/apps/vite-tanstack-router/src/components/ui/dialog.tsx
new file mode 100644
index 00000000..90840eb6
--- /dev/null
+++ b/apps/vite-tanstack-router/src/components/ui/dialog.tsx
@@ -0,0 +1,141 @@
+'use client';
+
+import { Button } from '@/components/ui/button';
+import { cn } from '@/lib/utils';
+import { Platform, View } from '@rn-primitives/core';
+import { FadeIn, FadeOut, ZoomIn, ZoomOut } from '@rn-primitives/core/native-only-reanimated';
+import * as DialogPrimitive from '@rn-primitives/dialog';
+import { mergeProps } from '@rn-primitives/utils';
+import { X } from 'lucide-react';
+import * as React from 'react';
+
+const Dialog = DialogPrimitive.Root;
+
+const DialogTrigger = DialogPrimitive.Trigger;
+
+const DialogPortal = DialogPrimitive.Portal;
+
+const OVERLAY_NATIVE_PROPS = {
+ isAnimated: true,
+ entering: FadeIn,
+ exiting: FadeOut.duration(150),
+};
+
+function DialogOverlay({ className, native, ...props }: DialogPrimitive.OverlayProps) {
+ return (
+
+ );
+}
+
+const CONTENT_NATIVE_PROPS = {
+ isAnimated: true,
+ entering: ZoomIn.duration(200).withInitialValues({ transform: [{ scale: 0.85 }] }),
+ exiting: ZoomOut.duration(400),
+};
+
+function DialogContent({
+ className,
+ children,
+ native: { portalHost, ...nativeProp } = {},
+ ...props
+}: Omit & {
+ native?: DialogPrimitive.ContentProps['native'] & { portalHost?: string };
+}) {
+ const { open } = DialogPrimitive.useRootContext();
+ return (
+
+
+
+ {/* DialogPrimitive.Content uses `nativeID` for accessibility, so it prevents the entering animation from working https://docs.swmansion.com/react-native-reanimated/docs/layout-animations/entering-exiting-animations/#remarks */}
+
+ {children}
+
+
+
+
+
+
+
+ );
+}
+
+const DialogHeader = ({ className, ...props }: React.ComponentPropsWithoutRef) => (
+
+);
+
+const DialogFooter = ({ className, ...props }: React.ComponentPropsWithoutRef) => (
+
+);
+
+function DialogTitle({ className, ...props }: DialogPrimitive.TitleProps) {
+ return (
+
+ );
+}
+
+function DialogDescription({ className, ...props }: DialogPrimitive.DescriptionProps) {
+ return (
+
+ );
+}
+
+const DialogClose = ({ className, ...props }: DialogPrimitive.CloseProps) => (
+
+);
+
+export {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogOverlay,
+ DialogPortal,
+ DialogTitle,
+ DialogTrigger,
+};
diff --git a/apps/vite-tanstack-router/src/routes/index.tsx b/apps/vite-tanstack-router/src/routes/index.tsx
index 37291c50..1a80ab8d 100644
--- a/apps/vite-tanstack-router/src/routes/index.tsx
+++ b/apps/vite-tanstack-router/src/routes/index.tsx
@@ -18,6 +18,16 @@ import {
} from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from '@/components/ui/dialog';
import { Label } from '@/components/ui/label';
import { Progress } from '@/components/ui/progress';
import { Separator } from '@/components/ui/separator';
@@ -51,6 +61,7 @@ export function App() {
+
@@ -197,6 +208,32 @@ function CollapsibleExample() {
);
}
+function DialogExample() {
+ return (
+
+ );
+}
+
function LabelExample() {
return (
diff --git a/packages/dialog/package.json b/packages/dialog/package.json
index 5b8918b1..aeba3a2b 100644
--- a/packages/dialog/package.json
+++ b/packages/dialog/package.json
@@ -1,6 +1,6 @@
{
"name": "@rn-primitives/dialog",
- "version": "1.1.0",
+ "version": "2.0.0-alpha.1",
"description": "Primitive dialog",
"license": "MIT",
"main": "dist/index.js",
@@ -13,11 +13,17 @@
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
- "./dist/dialog": {
- "import": "./dist/dialog.mjs",
- "require": "./dist/dialog.js",
- "types": "./dist/dialog.d.ts",
- "default": "./dist/dialog.js"
+ "./native": {
+ "import": "./dist/native/index.mjs",
+ "require": "./dist/native/index.js",
+ "types": "./dist/native/index.d.ts",
+ "default": "./dist/native/index.js"
+ },
+ "./web": {
+ "import": "./dist/web/index.mjs",
+ "require": "./dist/web/index.js",
+ "types": "./dist/web/index.d.ts",
+ "default": "./dist/web/index.js"
}
},
"files": [
@@ -32,10 +38,12 @@
"pub:release": "pnpm publish --access public"
},
"dependencies": {
- "@radix-ui/react-dialog": "^1.1.1",
+ "@radix-ui/react-dialog": "^1.1.14",
+ "@rn-primitives/core": "workspace:*",
"@rn-primitives/hooks": "workspace:*",
- "@rn-primitives/slot": "workspace:*",
- "@rn-primitives/types": "workspace:*"
+ "@rn-primitives/portal": "workspace:*",
+ "@rn-primitives/types": "workspace:*",
+ "@rn-primitives/utils": "workspace:*"
},
"devDependencies": {
"@rn-primitives/portal": "workspace:*",
diff --git a/packages/dialog/src/base-types.ts b/packages/dialog/src/base-types.ts
new file mode 100644
index 00000000..dbbf2386
--- /dev/null
+++ b/packages/dialog/src/base-types.ts
@@ -0,0 +1,37 @@
+import type {
+ DialogContentProps,
+ DialogOverlayProps,
+ DialogPortalProps,
+ DialogProps,
+} from '@radix-ui/react-dialog';
+import type { Prettify } from '@rn-primitives/types';
+
+type BaseDialogRootProps = Omit, 'children'>;
+
+type BaseDialogTriggerProps = {};
+
+type BaseDialogPortalProps = Pick;
+
+type BaseDialogOverlayProps = Pick;
+
+type BaseDialogCloseProps = {};
+
+type BaseDialogTitleProps = {};
+
+type BaseDialogDescriptionProps = {};
+
+type BaseDialogContentProps = Pick;
+
+type BaseDialogRootContext = Required> | null;
+
+export type {
+ BaseDialogCloseProps,
+ BaseDialogContentProps,
+ BaseDialogDescriptionProps,
+ BaseDialogOverlayProps,
+ BaseDialogPortalProps,
+ BaseDialogRootContext,
+ BaseDialogRootProps,
+ BaseDialogTitleProps,
+ BaseDialogTriggerProps,
+};
diff --git a/packages/dialog/src/dialog.tsx b/packages/dialog/src/dialog.tsx
deleted file mode 100644
index 153214f2..00000000
--- a/packages/dialog/src/dialog.tsx
+++ /dev/null
@@ -1,269 +0,0 @@
-import { useControllableState } from '@rn-primitives/hooks';
-import { Portal as RNPPortal } from '@rn-primitives/portal';
-import { Slot } from '@rn-primitives/slot';
-import * as React from 'react';
-import { BackHandler, GestureResponderEvent, Pressable, Text, View } from 'react-native';
-import type {
- CloseProps,
- CloseRef,
- ContentProps,
- ContentRef,
- DescriptionProps,
- DescriptionRef,
- OverlayProps,
- OverlayRef,
- PortalProps,
- RootContext,
- RootProps,
- RootRef,
- TitleProps,
- TitleRef,
- TriggerProps,
- TriggerRef,
-} from './types';
-
-const DialogContext = React.createContext<(RootContext & { nativeID: string }) | null>(null);
-
-const Root = (
- {
- ref,
- asChild,
- open: openProp,
- defaultOpen,
- onOpenChange: onOpenChangeProp,
- ...viewProps
- }: RootProps & {
- ref: React.RefObject;
- }
-) => {
- const nativeID = React.useId();
- const [open = false, onOpenChange] = useControllableState({
- prop: openProp,
- defaultProp: defaultOpen,
- onChange: onOpenChangeProp,
- });
-
- const Component = asChild ? Slot : View;
- return (
-
-
-
- );
-};
-
-Root.displayName = 'RootNativeDialog';
-
-function useRootContext() {
- const context = React.useContext(DialogContext);
- if (!context) {
- throw new Error('Dialog compound components cannot be rendered outside the Dialog component');
- }
- return context;
-}
-
-const Trigger = (
- {
- ref,
- asChild,
- onPress: onPressProp,
- disabled = false,
- ...props
- }: TriggerProps & {
- ref: React.RefObject;
- }
-) => {
- const { open, onOpenChange } = useRootContext();
-
- function onPress(ev: GestureResponderEvent) {
- if (disabled) return;
- const newValue = !open;
- onOpenChange(newValue);
- onPressProp?.(ev);
- }
-
- const Component = asChild ? Slot : Pressable;
- return (
-
- );
-};
-
-Trigger.displayName = 'TriggerNativeDialog';
-
-/**
- * @warning when using a custom ``, you might have to adjust the Content's sideOffset to account for nav elements like headers.
- */
-function Portal({ forceMount, hostName, children }: PortalProps) {
- const value = useRootContext();
-
- if (!forceMount) {
- if (!value.open) {
- return null;
- }
- }
-
- return (
-
- {children}
-
- );
-}
-
-const Overlay = (
- {
- ref,
- asChild,
- forceMount,
- closeOnPress = true,
- onPress: OnPressProp,
- ...props
- }: OverlayProps & {
- ref: React.RefObject;
- }
-) => {
- const { open, onOpenChange } = useRootContext();
-
- function onPress(ev: GestureResponderEvent) {
- if (closeOnPress) {
- onOpenChange(!open);
- }
- OnPressProp?.(ev);
- }
-
- if (!forceMount) {
- if (!open) {
- return null;
- }
- }
-
- const Component = asChild ? Slot : Pressable;
- return ;
-};
-
-Overlay.displayName = 'OverlayNativeDialog';
-
-const Content = (
- {
- ref,
- asChild,
- forceMount,
- ...props
- }: ContentProps & {
- ref: React.RefObject;
- }
-) => {
- const { open, nativeID, onOpenChange } = useRootContext();
-
- React.useEffect(() => {
- const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
- onOpenChange(false);
- return true;
- });
-
- return () => {
- backHandler.remove();
- };
- }, []);
-
- if (!forceMount) {
- if (!open) {
- return null;
- }
- }
-
- const Component = asChild ? Slot : View;
- return (
-
- );
-};
-
-Content.displayName = 'ContentNativeDialog';
-
-const Close = (
- {
- ref,
- asChild,
- onPress: onPressProp,
- disabled = false,
- ...props
- }: CloseProps & {
- ref: React.RefObject;
- }
-) => {
- const { onOpenChange } = useRootContext();
-
- function onPress(ev: GestureResponderEvent) {
- if (disabled) return;
- onOpenChange(false);
- onPressProp?.(ev);
- }
-
- const Component = asChild ? Slot : Pressable;
- return (
-
- );
-};
-
-Close.displayName = 'CloseNativeDialog';
-
-const Title = (
- {
- ref,
- ...props
- }: TitleProps & {
- ref: React.RefObject;
- }
-) => {
- const { nativeID } = useRootContext();
- return ;
-};
-
-Title.displayName = 'TitleNativeDialog';
-
-const Description = (
- {
- ref,
- ...props
- }: DescriptionProps & {
- ref: React.RefObject;
- }
-) => {
- const { nativeID } = useRootContext();
- return ;
-};
-
-Description.displayName = 'DescriptionNativeDialog';
-
-export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext };
-
-function onStartShouldSetResponder() {
- return true;
-}
diff --git a/packages/dialog/src/dialog.web.tsx b/packages/dialog/src/dialog.web.tsx
deleted file mode 100644
index ed688126..00000000
--- a/packages/dialog/src/dialog.web.tsx
+++ /dev/null
@@ -1,253 +0,0 @@
-import * as Dialog from '@radix-ui/react-dialog';
-import {
- useAugmentedRef,
- useControllableState,
- useIsomorphicLayoutEffect,
-} from '@rn-primitives/hooks';
-import { Slot } from '@rn-primitives/slot';
-import * as React from 'react';
-import { Pressable, Text, View, type GestureResponderEvent } from 'react-native';
-import type {
- CloseProps,
- CloseRef,
- ContentProps,
- ContentRef,
- DescriptionProps,
- DescriptionRef,
- OverlayProps,
- OverlayRef,
- PortalProps,
- RootContext,
- RootProps,
- RootRef,
- TitleProps,
- TitleRef,
- TriggerProps,
- TriggerRef,
-} from './types';
-
-const DialogContext = React.createContext(null);
-
-const Root = (
- {
- ref,
- asChild,
- open: openProp,
- defaultOpen,
- onOpenChange: onOpenChangeProp,
- ...viewProps
- }: RootProps & {
- ref: React.RefObject;
- }
-) => {
- const [open = false, onOpenChange] = useControllableState({
- prop: openProp,
- defaultProp: defaultOpen,
- onChange: onOpenChangeProp,
- });
- const Component = asChild ? Slot : View;
- return (
-
-
-
-
-
- );
-};
-
-Root.displayName = 'RootWebDialog';
-
-function useRootContext() {
- const context = React.useContext(DialogContext);
- if (!context) {
- throw new Error('Dialog compound components cannot be rendered outside the Dialog component');
- }
- return context;
-}
-
-const Trigger = (
- {
- ref,
- asChild,
- onPress: onPressProp,
- role: _role,
- disabled,
- ...props
- }: TriggerProps & {
- ref: React.RefObject;
- }
-) => {
- const augmentedRef = useAugmentedRef({ ref });
- const { onOpenChange, open } = useRootContext();
- function onPress(ev: GestureResponderEvent) {
- if (onPressProp) {
- onPressProp(ev);
- }
- onOpenChange(!open);
- }
-
- useIsomorphicLayoutEffect(() => {
- if (augmentedRef.current) {
- const augRef = augmentedRef.current as unknown as HTMLButtonElement;
- augRef.dataset.state = open ? 'open' : 'closed';
- augRef.type = 'button';
- }
- }, [open]);
-
- const Component = asChild ? Slot : Pressable;
- return (
-
-
-
- );
-};
-
-Trigger.displayName = 'TriggerWebDialog';
-
-function Portal({ forceMount, container, children }: PortalProps) {
- return ;
-}
-
-const Overlay = (
- {
- ref,
- asChild,
- forceMount,
- ...props
- }: OverlayProps & {
- ref: React.RefObject;
- }
-) => {
- const Component = asChild ? Slot : Pressable;
- return (
-
-
-
- );
-};
-
-Overlay.displayName = 'OverlayWebDialog';
-
-const Content = (
- {
- ref,
- asChild,
- forceMount,
- onOpenAutoFocus,
- onCloseAutoFocus,
- onEscapeKeyDown,
- onInteractOutside,
- onPointerDownOutside,
- ...props
- }: ContentProps & {
- ref: React.RefObject;
- }
-) => {
- const Component = asChild ? Slot : View;
- return (
-
-
-
- );
-};
-
-Content.displayName = 'ContentWebDialog';
-
-const Close = (
- {
- ref,
- asChild,
- onPress: onPressProp,
- disabled,
- ...props
- }: CloseProps & {
- ref: React.RefObject;
- }
-) => {
- const augmentedRef = useAugmentedRef({ ref });
- const { onOpenChange, open } = useRootContext();
-
- function onPress(ev: GestureResponderEvent) {
- if (onPressProp) {
- onPressProp(ev);
- }
- onOpenChange(!open);
- }
-
- useIsomorphicLayoutEffect(() => {
- if (augmentedRef.current) {
- const augRef = augmentedRef.current as unknown as HTMLButtonElement;
- augRef.type = 'button';
- }
- }, []);
-
- const Component = asChild ? Slot : Pressable;
- return (
- <>
-
-
-
- >
- );
-};
-
-Close.displayName = 'CloseWebDialog';
-
-const Title = (
- {
- ref,
- asChild,
- ...props
- }: TitleProps & {
- ref: React.RefObject;
- }
-) => {
- const Component = asChild ? Slot : Text;
- return (
-
-
-
- );
-};
-
-Title.displayName = 'TitleWebDialog';
-
-const Description = (
- {
- ref,
- asChild,
- ...props
- }: DescriptionProps & {
- ref: React.RefObject;
- }
-) => {
- const Component = asChild ? Slot : Text;
- return (
-
-
-
- );
-};
-
-Description.displayName = 'DescriptionWebDialog';
-
-export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext };
diff --git a/packages/dialog/src/index.ts b/packages/dialog/src/index.ts
index 67979f9d..9470bad0 100644
--- a/packages/dialog/src/index.ts
+++ b/packages/dialog/src/index.ts
@@ -1,2 +1,22 @@
-export * from './dialog';
-export * from './types';
+export {
+ Close,
+ Content,
+ Description,
+ Overlay,
+ Portal,
+ Root,
+ Title,
+ Trigger,
+ useRootContext,
+} from './universal';
+
+export type {
+ CloseProps,
+ ContentProps,
+ DescriptionProps,
+ OverlayProps,
+ PortalProps,
+ RootProps,
+ TitleProps,
+ TriggerProps,
+} from './universal';
diff --git a/packages/dialog/src/native/dialog-native.native.tsx b/packages/dialog/src/native/dialog-native.native.tsx
new file mode 100644
index 00000000..6ec06a3b
--- /dev/null
+++ b/packages/dialog/src/native/dialog-native.native.tsx
@@ -0,0 +1,196 @@
+import { Pressable, Text, View } from '@rn-primitives/core/dist/native';
+import { useControllableState } from '@rn-primitives/hooks';
+import { Portal as RNPPortal } from '@rn-primitives/portal';
+import * as React from 'react';
+import { BackHandler, type GestureResponderEvent } from 'react-native';
+import { RootContext, useRootContext } from '../utils/contexts';
+import type {
+ CloseProps,
+ ContentProps,
+ DescriptionProps,
+ OverlayProps,
+ PortalProps,
+ RootProps,
+ TitleProps,
+ TriggerProps,
+} from './types';
+
+const RootInternalContext = React.createContext<{ nativeID: string } | null>(null);
+
+function Root({
+ open: openProp,
+ defaultOpen,
+ onOpenChange: onOpenChangeProp,
+ children,
+}: RootProps) {
+ const nativeID = React.useId();
+ const [open = false, onOpenChange] = useControllableState({
+ prop: openProp,
+ defaultProp: defaultOpen,
+ onChange: onOpenChangeProp,
+ });
+ return (
+
+
+ <>{children}>
+
+
+ );
+}
+
+function useRootInternalContext() {
+ const context = React.useContext(RootInternalContext);
+ if (!context) {
+ throw new Error(
+ 'Dialog Internal compound components cannot be rendered outside the Dialog component'
+ );
+ }
+ return context;
+}
+
+function Trigger({ onPress: onPressProp, disabled, ...props }: TriggerProps) {
+ const { open: value, onOpenChange } = useRootContext();
+
+ const onPress = React.useCallback(
+ (ev: GestureResponderEvent) => {
+ onOpenChange(!value);
+ if (typeof onPressProp === 'function') {
+ onPressProp(ev);
+ }
+ },
+ [onOpenChange, onPressProp, value]
+ );
+
+ return (
+
+ );
+}
+
+function Portal({ forceMount, hostName, children }: PortalProps) {
+ const internalValue = useRootInternalContext();
+ const value = useRootContext();
+
+ if (!forceMount) {
+ if (!value.open) {
+ return null;
+ }
+ }
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+function Overlay({
+ forceMount,
+ onAccessibilityEscape: onAccessibilityEscapeProp,
+ ...props
+}: OverlayProps) {
+ const { open: value, onOpenChange } = useRootContext();
+
+ const onAccessibilityEscape = React.useCallback(() => {
+ if (typeof onAccessibilityEscape === 'function') {
+ onAccessibilityEscape();
+ }
+ onOpenChange(false);
+ }, [onAccessibilityEscapeProp, onOpenChange]);
+
+ if (!forceMount) {
+ if (!value) {
+ return null;
+ }
+ }
+
+ return ;
+}
+
+function Content({
+ forceMount,
+ onAccessibilityEscape: onAccessibilityEscapeProp,
+ ...props
+}: ContentProps) {
+ const { open: value, onOpenChange } = useRootContext();
+ const { nativeID } = useRootInternalContext();
+
+ React.useEffect(() => {
+ const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
+ onOpenChange(false);
+ return true;
+ });
+
+ return () => {
+ backHandler.remove();
+ };
+ }, []);
+
+ const onAccessibilityEscape = React.useCallback(() => {
+ if (typeof onAccessibilityEscapeProp === 'function') {
+ onAccessibilityEscapeProp();
+ }
+ onOpenChange(false);
+ }, [onAccessibilityEscapeProp, onOpenChange]);
+
+ if (!forceMount) {
+ if (!value) {
+ return null;
+ }
+ }
+
+ return (
+
+ );
+}
+
+function Close({ onPress: onPressProp, disabled, ...props }: CloseProps) {
+ const { onOpenChange } = useRootContext();
+
+ const onPress = React.useCallback(
+ (ev: GestureResponderEvent) => {
+ onOpenChange(false);
+ if (typeof onPressProp === 'function') {
+ onPressProp(ev);
+ }
+ },
+ [onOpenChange, onPressProp]
+ );
+
+ return (
+
+ );
+}
+
+function Title(props: TitleProps) {
+ const { nativeID } = useRootInternalContext();
+ return ;
+}
+
+function Description({ ...props }: DescriptionProps) {
+ const { nativeID } = useRootInternalContext();
+ return ;
+}
+
+export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext };
diff --git a/packages/dialog/src/native/dialog-native.tsx b/packages/dialog/src/native/dialog-native.tsx
new file mode 100644
index 00000000..3841fd57
--- /dev/null
+++ b/packages/dialog/src/native/dialog-native.tsx
@@ -0,0 +1,76 @@
+import { RootContextReturnType } from '../utils/contexts';
+import type {
+ CloseProps,
+ ContentProps,
+ DescriptionProps,
+ OverlayProps,
+ PortalProps,
+ RootProps,
+ TitleProps,
+ TriggerProps,
+} from './types';
+
+function Root(props: RootProps) {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Root` from @rn-primitives/dialog/native is only supported on native.');
+ }
+ return null;
+}
+
+function Trigger(props: TriggerProps) {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Trigger` from @rn-primitives/dialog/native is only supported on native.');
+ }
+ return null;
+}
+
+function Portal(props: PortalProps) {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Portal` from @rn-primitives/dialog/native is only supported on native.');
+ }
+ return null;
+}
+
+function Overlay(props: OverlayProps) {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Overlay` from @rn-primitives/dialog/native is only supported on native.');
+ }
+ return null;
+}
+
+function Content(props: ContentProps) {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Content` from @rn-primitives/dialog/native is only supported on native.');
+ }
+ return null;
+}
+
+function Close(props: CloseProps) {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Close` from @rn-primitives/dialog/native is only supported on native.');
+ }
+ return null;
+}
+
+function Title(props: TitleProps) {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Title` from @rn-primitives/dialog/native is only supported on native.');
+ }
+ return null;
+}
+
+function Description(props: DescriptionProps) {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Description` from @rn-primitives/dialog/native is only supported on native.');
+ }
+ return null;
+}
+
+const useRootContext = () => {
+ throw new Error(
+ 'Cannot access the native useRootContext on the web. Please import from `@rn-primitives/dialog` or `@rn-primitives/dialog/web`'
+ );
+ return {} as RootContextReturnType;
+};
+
+export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext };
diff --git a/packages/dialog/src/native/index.ts b/packages/dialog/src/native/index.ts
new file mode 100644
index 00000000..58d11be3
--- /dev/null
+++ b/packages/dialog/src/native/index.ts
@@ -0,0 +1,22 @@
+export {
+ Close,
+ Content,
+ Description,
+ Overlay,
+ Portal,
+ Root,
+ Title,
+ Trigger,
+ useRootContext,
+} from './dialog-native';
+
+export type {
+ CloseProps,
+ ContentProps,
+ DescriptionProps,
+ OverlayProps,
+ PortalProps,
+ RootProps,
+ TitleProps,
+ TriggerProps,
+} from './types';
diff --git a/packages/dialog/src/native/types.ts b/packages/dialog/src/native/types.ts
new file mode 100644
index 00000000..53176fb6
--- /dev/null
+++ b/packages/dialog/src/native/types.ts
@@ -0,0 +1,49 @@
+import type { PressableProps, TextProps, ViewProps } from '@rn-primitives/core/dist/native';
+import type {
+ BaseDialogCloseProps,
+ BaseDialogContentProps,
+ BaseDialogDescriptionProps,
+ BaseDialogOverlayProps,
+ BaseDialogPortalProps,
+ BaseDialogRootProps,
+ BaseDialogTitleProps,
+ BaseDialogTriggerProps,
+} from '../base-types';
+
+type ClosePropsNativeOnly = PressableProps;
+type ContentPropsNativeOnly = ViewProps;
+type DescriptionPropsNativeOnly = TextProps;
+type OverlayPropsNativeOnly = ViewProps;
+type PortalPropsNativeOnly = {
+ hostName?: string;
+ children?: React.ReactNode;
+};
+type TitlePropsNativeOnly = TextProps;
+type TriggerPropsNativeOnly = PressableProps;
+
+type RootProps = BaseDialogRootProps & { children?: React.ReactNode };
+type CloseProps = ClosePropsNativeOnly & BaseDialogCloseProps;
+type ContentProps = ContentPropsNativeOnly & BaseDialogContentProps;
+type DescriptionProps = DescriptionPropsNativeOnly & BaseDialogDescriptionProps;
+type OverlayProps = OverlayPropsNativeOnly & BaseDialogOverlayProps;
+type PortalProps = PortalPropsNativeOnly & BaseDialogPortalProps;
+type TitleProps = TitlePropsNativeOnly & BaseDialogTitleProps;
+type TriggerProps = TriggerPropsNativeOnly & BaseDialogTriggerProps;
+
+export type {
+ CloseProps,
+ ClosePropsNativeOnly,
+ ContentProps,
+ ContentPropsNativeOnly,
+ DescriptionProps,
+ DescriptionPropsNativeOnly,
+ OverlayProps,
+ OverlayPropsNativeOnly,
+ PortalProps,
+ PortalPropsNativeOnly,
+ RootProps,
+ TitleProps,
+ TitlePropsNativeOnly,
+ TriggerProps,
+ TriggerPropsNativeOnly,
+};
diff --git a/packages/dialog/src/types.ts b/packages/dialog/src/types.ts
deleted file mode 100644
index 5cbf6bab..00000000
--- a/packages/dialog/src/types.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import type {
- ForceMountable,
- PressableRef,
- SlottablePressableProps,
- SlottableTextProps,
- SlottableViewProps,
- TextRef,
- ViewRef,
-} from '@rn-primitives/types';
-
-type RootContext = {
- open: boolean;
- onOpenChange: (value: boolean) => void;
-};
-
-type RootProps = SlottableViewProps & {
- open?: boolean;
- defaultOpen?: boolean;
- onOpenChange?: (value: boolean) => void;
-};
-
-interface PortalProps extends ForceMountable {
- children: React.ReactNode;
- /**
- * Platform: NATIVE ONLY
- */
- hostName?: string;
- /**
- * Platform: WEB ONLY
- */
- container?: HTMLElement | null | undefined;
-}
-type OverlayProps = ForceMountable &
- SlottablePressableProps & {
- /**
- * Platform: NATIVE ONLY - default: true
- */
- closeOnPress?: boolean;
- };
-type ContentProps = ForceMountable &
- SlottableViewProps & {
- /**
- * Platform: WEB ONLY
- */
- onOpenAutoFocus?: (ev: Event) => void;
- /**
- * Platform: WEB ONLY
- */
- onCloseAutoFocus?: (ev: Event) => void;
- /**
- * Platform: WEB ONLY
- */
- onEscapeKeyDown?: (ev: Event) => void;
- /**
- * Platform: WEB ONLY
- */
- onInteractOutside?: (ev: Event) => void;
- /**
- * Platform: WEB ONLY
- */
- onPointerDownOutside?: (ev: Event) => void;
- };
-
-type TriggerProps = SlottablePressableProps;
-type CloseProps = SlottablePressableProps;
-type TitleProps = SlottableTextProps;
-type DescriptionProps = SlottableTextProps;
-
-type CloseRef = PressableRef;
-type ContentRef = ViewRef;
-type DescriptionRef = TextRef;
-type OverlayRef = PressableRef;
-type RootRef = ViewRef;
-type TitleRef = TextRef;
-type TriggerRef = PressableRef;
-
-export type {
- CloseProps,
- CloseRef,
- ContentProps,
- ContentRef,
- DescriptionProps,
- DescriptionRef,
- OverlayProps,
- OverlayRef,
- PortalProps,
- RootContext,
- RootProps,
- RootRef,
- TitleProps,
- TitleRef,
- TriggerProps,
- TriggerRef,
-};
diff --git a/packages/dialog/src/universal/dialog.tsx b/packages/dialog/src/universal/dialog.tsx
new file mode 100644
index 00000000..b0008ba8
--- /dev/null
+++ b/packages/dialog/src/universal/dialog.tsx
@@ -0,0 +1,58 @@
+import * as React from 'react';
+import {
+ Close as CloseNative,
+ type CloseProps as ClosePropsNative,
+ Content as ContentNative,
+ Description as DescriptionNative,
+ Overlay as OverlayNative,
+ Portal as PortalNative,
+ Root as RootNative,
+ Title as TitleNative,
+ Trigger as TriggerNative,
+ type TriggerProps as TriggerPropsNative,
+ useRootContext,
+} from '../native';
+import type {
+ CloseProps,
+ ContentProps,
+ DescriptionProps,
+ OverlayProps,
+ PortalProps,
+ RootProps,
+ TitleProps,
+ TriggerProps,
+} from './types';
+
+function Root(props: RootProps) {
+ return ;
+}
+
+function Content({ web: _web, native, ...props }: ContentProps) {
+ return ;
+}
+
+function Description({ web: _web, native, ...props }: DescriptionProps) {
+ return ;
+}
+
+function Overlay({ web: _web, native, ...props }: OverlayProps) {
+ return ;
+}
+
+function Portal({ web: _web, native, ...props }: PortalProps) {
+ return ;
+}
+
+function Title({ web: _web, native, ...props }: TitleProps) {
+ return ;
+}
+
+function Trigger({ ref, web: _web, native, ...props }: TriggerProps) {
+ return ;
+}
+
+function Close({ ref, web: _web, native, ...props }: CloseProps) {
+ return ;
+}
+
+export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext };
diff --git a/packages/dialog/src/universal/dialog.web.tsx b/packages/dialog/src/universal/dialog.web.tsx
new file mode 100644
index 00000000..842f74e0
--- /dev/null
+++ b/packages/dialog/src/universal/dialog.web.tsx
@@ -0,0 +1,97 @@
+import { Pressable, Text, View } from '@rn-primitives/core';
+import { mergeProps } from '@rn-primitives/utils';
+import * as React from 'react';
+import {
+ Close as CloseWeb,
+ Content as ContentWeb,
+ Description as DescriptionWeb,
+ Overlay as OverlayWeb,
+ Portal as PortalWeb,
+ Root as RootWeb,
+ Title as TitleWeb,
+ Trigger as TriggerWeb,
+ useRootContext,
+} from '../web';
+import type {
+ CloseProps,
+ ContentProps,
+ DescriptionProps,
+ OverlayProps,
+ PortalProps,
+ RootProps,
+ TitleProps,
+ TriggerProps,
+} from './types';
+
+function Root(props: RootProps) {
+ return ;
+}
+
+function Content({ web, native: _native, style, ...props }: ContentProps) {
+ if (style) {
+ return (
+
+
+
+ );
+ }
+ return ;
+}
+
+function Description({ web, native: _native, style, ...props }: DescriptionProps) {
+ if (style) {
+ return (
+
+
+
+ );
+ }
+
+ return ;
+}
+
+function Overlay({ web, native: _native, style, ...props }: OverlayProps) {
+ if (style) {
+ return (
+
+
+
+ );
+ }
+ return ;
+}
+
+function Portal({ web, native: _native, ...props }: PortalProps) {
+ return ;
+}
+
+function Title({ web, native: _native, style, ...props }: TitleProps) {
+ if (style) {
+ return (
+
+
+
+ );
+ }
+ return ;
+}
+
+const DEFAULT_PRESSABLE_WEB = { as: 'button' } as const;
+
+function Trigger({ native: _native, web, ...props }: TriggerProps) {
+ return (
+
+
+
+ );
+}
+
+function Close({ native: _native, web, ...props }: CloseProps) {
+ return (
+
+
+
+ );
+}
+
+export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext };
diff --git a/packages/dialog/src/universal/index.ts b/packages/dialog/src/universal/index.ts
new file mode 100644
index 00000000..4f0ee069
--- /dev/null
+++ b/packages/dialog/src/universal/index.ts
@@ -0,0 +1,22 @@
+export {
+ Close,
+ Content,
+ Description,
+ Overlay,
+ Portal,
+ Root,
+ Title,
+ Trigger,
+ useRootContext,
+} from './dialog';
+
+export type {
+ CloseProps,
+ ContentProps,
+ DescriptionProps,
+ OverlayProps,
+ PortalProps,
+ RootProps,
+ TitleProps,
+ TriggerProps,
+} from './types';
diff --git a/packages/dialog/src/universal/types.ts b/packages/dialog/src/universal/types.ts
new file mode 100644
index 00000000..1d38df82
--- /dev/null
+++ b/packages/dialog/src/universal/types.ts
@@ -0,0 +1,106 @@
+import type {
+ PressablePropsUniversal,
+ TextPropsUniversal,
+ ViewPropsUniversal,
+} from '@rn-primitives/core';
+import type { Prettify } from '@rn-primitives/types';
+import type {
+ BaseDialogCloseProps,
+ BaseDialogContentProps,
+ BaseDialogDescriptionProps,
+ BaseDialogOverlayProps,
+ BaseDialogPortalProps,
+ BaseDialogRootProps,
+ BaseDialogTitleProps,
+ BaseDialogTriggerProps,
+} from '../base-types';
+import type {
+ ClosePropsNativeOnly,
+ ContentPropsNativeOnly,
+ DescriptionPropsNativeOnly,
+ OverlayPropsNativeOnly,
+ PortalPropsNativeOnly,
+ TitlePropsNativeOnly,
+ TriggerPropsNativeOnly,
+} from '../native/types';
+import type {
+ ClosePropsWebOnly,
+ ContentPropsWebOnly,
+ DescriptionPropsWebOnly,
+ OverlayPropsWebOnly,
+ PortalPropsWebOnly,
+ TitlePropsWebOnly,
+ TriggerPropsWebOnly,
+} from '../web/types';
+
+type ContentProps = Prettify<
+ BaseDialogContentProps &
+ ViewPropsUniversal & {
+ native?: ContentPropsNativeOnly;
+ web?: ContentPropsWebOnly;
+ }
+>;
+
+type RootProps = Prettify<
+ BaseDialogRootProps & {
+ children?: React.ReactNode;
+ }
+>;
+
+type TriggerProps = Prettify<
+ BaseDialogTriggerProps &
+ PressablePropsUniversal & {
+ native?: TriggerPropsNativeOnly;
+ web?: TriggerPropsWebOnly;
+ }
+>;
+
+type CloseProps = Prettify<
+ BaseDialogCloseProps &
+ PressablePropsUniversal & {
+ native?: ClosePropsNativeOnly;
+ web?: ClosePropsWebOnly;
+ }
+>;
+
+type DescriptionProps = Prettify<
+ BaseDialogDescriptionProps &
+ TextPropsUniversal & {
+ native?: DescriptionPropsNativeOnly;
+ web?: DescriptionPropsWebOnly;
+ }
+>;
+
+type OverlayProps = Prettify<
+ BaseDialogOverlayProps &
+ ViewPropsUniversal & {
+ native?: OverlayPropsNativeOnly;
+ web?: OverlayPropsWebOnly;
+ }
+>;
+
+type PortalProps = Prettify<
+ BaseDialogPortalProps & {
+ native?: PortalPropsNativeOnly;
+ web?: PortalPropsWebOnly;
+ }
+>;
+
+type TitleProps = Prettify<
+ BaseDialogTitleProps &
+ TextPropsUniversal & {
+ native?: TitlePropsNativeOnly;
+ web?: TitlePropsWebOnly;
+ }
+>;
+
+export type {
+ CloseProps,
+ ContentProps,
+ DescriptionProps,
+ OverlayProps,
+ PortalProps,
+ RootProps,
+ TitleProps,
+ TriggerProps,
+};
diff --git a/packages/dialog/src/utils/contexts.ts b/packages/dialog/src/utils/contexts.ts
new file mode 100644
index 00000000..d18a8854
--- /dev/null
+++ b/packages/dialog/src/utils/contexts.ts
@@ -0,0 +1,17 @@
+import * as React from 'react';
+import type { BaseDialogRootContext } from '../base-types';
+
+const RootContext = React.createContext(null);
+function useRootContext() {
+ const context = React.useContext(RootContext);
+ if (!context) {
+ throw new Error('Dialog compound components cannot be rendered outside the Dialog component');
+ }
+ return context;
+}
+
+type RootContextReturnType = ReturnType;
+
+export { RootContext, useRootContext };
+
+export type { RootContextReturnType };
diff --git a/packages/dialog/src/web/dialog-web.tsx b/packages/dialog/src/web/dialog-web.tsx
new file mode 100644
index 00000000..ec6765df
--- /dev/null
+++ b/packages/dialog/src/web/dialog-web.tsx
@@ -0,0 +1,76 @@
+import type {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogOverlay,
+ DialogPortal,
+ DialogTitle,
+ DialogTrigger,
+} from '@radix-ui/react-dialog';
+import type { RootContextReturnType } from '../utils/contexts';
+
+const Root = (() => {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Root` from @rn-primitives/dialog/web is only supported on web.');
+ }
+ return null;
+}) as typeof Dialog;
+
+const Trigger = (() => {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Trigger` from @rn-primitives/dialog/web is only supported on web.');
+ }
+ return null;
+}) as unknown as typeof DialogTrigger;
+
+const Content = (() => {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Content` from @rn-primitives/dialog/web is only supported on web.');
+ }
+ return null;
+}) as unknown as typeof DialogContent;
+
+const Close = (() => {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Close` from @rn-primitives/dialog/web is only supported on web.');
+ }
+ return null;
+}) as unknown as typeof DialogClose;
+
+const Description = (() => {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Description` from @rn-primitives/dialog/web is only supported on web.');
+ }
+ return null;
+}) as unknown as typeof DialogDescription;
+
+const Overlay = (() => {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Overlay` from @rn-primitives/dialog/web is only supported on web.');
+ }
+ return null;
+}) as unknown as typeof DialogOverlay;
+
+const Portal = (() => {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Portal` from @rn-primitives/dialog/web is only supported on web.');
+ }
+ return null;
+}) as unknown as typeof DialogPortal;
+
+const Title = (() => {
+ if (process.env.NODE_ENV === 'development') {
+ console.log('`Title` from @rn-primitives/dialog/web is only supported on web.');
+ }
+ return null;
+}) as unknown as typeof DialogTitle;
+
+const useRootContext = () => {
+ throw new Error(
+ 'Cannot access the web useRootContext on a web platform. Please import from `@rn-primitives/dialog` or `@rn-primitives/dialog/web`'
+ );
+ return {} as RootContextReturnType;
+};
+
+export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext };
diff --git a/packages/dialog/src/web/dialog-web.web.tsx b/packages/dialog/src/web/dialog-web.web.tsx
new file mode 100644
index 00000000..12e7a218
--- /dev/null
+++ b/packages/dialog/src/web/dialog-web.web.tsx
@@ -0,0 +1,48 @@
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogOverlay,
+ DialogTitle,
+ DialogTrigger,
+ Portal,
+} from '@radix-ui/react-dialog';
+import { useControllableState } from '@rn-primitives/hooks';
+import { withRNPrimitives } from '@rn-primitives/utils';
+import * as React from 'react';
+import { RootContext, useRootContext } from '../utils/contexts';
+import type { RootProps } from './types';
+
+function Root({
+ children,
+ defaultOpen,
+ onOpenChange: onOpenChangeProp,
+ open: openProp,
+}: RootProps) {
+ const [open = false, onOpenChange] = useControllableState({
+ prop: openProp,
+ defaultProp: defaultOpen,
+ onChange: onOpenChangeProp,
+ });
+
+ return (
+
+
+
+ );
+}
+
+const Close = withRNPrimitives(DialogClose, 'pressable');
+const Content = withRNPrimitives(DialogContent, 'view');
+const Description = withRNPrimitives(DialogDescription, 'text');
+const Overlay = withRNPrimitives(DialogOverlay, 'view');
+const Title = withRNPrimitives(DialogTitle, 'text');
+const Trigger = withRNPrimitives(DialogTrigger, 'pressable');
+
+export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext };
diff --git a/packages/dialog/src/web/index.ts b/packages/dialog/src/web/index.ts
new file mode 100644
index 00000000..0a5fef56
--- /dev/null
+++ b/packages/dialog/src/web/index.ts
@@ -0,0 +1,22 @@
+export {
+ Close,
+ Content,
+ Description,
+ Overlay,
+ Portal,
+ Root,
+ Title,
+ Trigger,
+ useRootContext,
+} from './dialog-web';
+
+export type {
+ CloseProps,
+ ContentProps,
+ DescriptionProps,
+ OverlayProps,
+ PortalProps,
+ RootProps,
+ TitleProps,
+ TriggerProps,
+} from './types';
diff --git a/packages/dialog/src/web/types.ts b/packages/dialog/src/web/types.ts
new file mode 100644
index 00000000..2d249ad6
--- /dev/null
+++ b/packages/dialog/src/web/types.ts
@@ -0,0 +1,59 @@
+import type {
+ DialogCloseProps,
+ DialogContentProps,
+ DialogDescriptionProps,
+ DialogOverlayProps,
+ DialogPortalProps,
+ DialogProps,
+ DialogTitleProps,
+ DialogTriggerProps,
+ Portal,
+} from '@radix-ui/react-dialog';
+
+type ContentPropsWebOnly = React.ComponentProps<'div'>;
+
+type TriggerPropsWebOnly = React.ComponentProps<'button'>;
+
+type ClosePropsWebOnly = React.ComponentProps<'button'>;
+
+type DescriptionPropsWebOnly = React.ComponentProps<'p'>;
+
+type OverlayPropsWebOnly = React.ComponentProps<'div'>;
+
+type PortalPropsWebOnly = Pick, 'container'>;
+
+type TitlePropsWebOnly = React.ComponentProps<'h1'>;
+
+type RootProps = DialogProps;
+
+type CloseProps = DialogCloseProps;
+
+type DescriptionProps = DialogDescriptionProps;
+
+type OverlayProps = DialogOverlayProps;
+
+type PortalProps = DialogPortalProps;
+
+type TitleProps = DialogTitleProps;
+
+type TriggerProps = DialogTriggerProps;
+
+type ContentProps = DialogContentProps;
+
+export type {
+ CloseProps,
+ ClosePropsWebOnly,
+ ContentProps,
+ ContentPropsWebOnly,
+ DescriptionProps,
+ DescriptionPropsWebOnly,
+ OverlayProps,
+ OverlayPropsWebOnly,
+ PortalProps,
+ PortalPropsWebOnly,
+ RootProps,
+ TitleProps,
+ TitlePropsWebOnly,
+ TriggerProps,
+ TriggerPropsWebOnly,
+};
diff --git a/packages/dialog/tsup.config.ts b/packages/dialog/tsup.config.ts
index a7bca0fb..f4e04d29 100644
--- a/packages/dialog/tsup.config.ts
+++ b/packages/dialog/tsup.config.ts
@@ -1,13 +1,32 @@
import { defineConfig, Options } from 'tsup';
export default defineConfig((options: Options) => ({
- entry: ['src/index.ts', 'src/dialog.tsx', 'src/dialog.web.tsx'],
+ entry: [
+ 'src/index.ts',
+ 'src/universal/index.ts',
+ 'src/universal/dialog.tsx',
+ 'src/universal/dialog.web.tsx',
+ 'src/native/index.ts',
+ 'src/native/dialog-native.tsx',
+ 'src/native/dialog-native.native.tsx',
+ 'src/web/index.ts',
+ 'src/web/dialog-web.tsx',
+ 'src/web/dialog-web.web.tsx',
+ ],
banner: {
js: "'use client'",
},
clean: true,
format: ['cjs', 'esm'],
- external: ['react', './dialog'],
+ external: [
+ 'react',
+ './universal',
+ './dialog',
+ '../native',
+ './dialog-native',
+ '../web',
+ './dialog-web',
+ ],
dts: true,
...options,
esbuildOptions(options) {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1f3620b8..f0f52fba 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -713,6 +713,9 @@ importers:
'@rn-primitives/core':
specifier: workspace:*
version: link:../../packages/core
+ '@rn-primitives/dialog':
+ specifier: workspace:*
+ version: link:../../packages/dialog
'@rn-primitives/label':
specifier: workspace:*
version: link:../../packages/label
@@ -804,6 +807,9 @@ importers:
'@rn-primitives/core':
specifier: workspace:*
version: link:../../packages/core
+ '@rn-primitives/dialog':
+ specifier: workspace:*
+ version: link:../../packages/dialog
'@rn-primitives/label':
specifier: workspace:*
version: link:../../packages/label
@@ -1189,24 +1195,27 @@ importers:
packages/dialog:
dependencies:
'@radix-ui/react-dialog':
- specifier: ^1.1.1
- version: 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ specifier: ^1.1.14
+ version: 1.1.14(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@rn-primitives/core':
+ specifier: workspace:*
+ version: link:../core
'@rn-primitives/hooks':
specifier: workspace:*
version: link:../hooks
- '@rn-primitives/slot':
+ '@rn-primitives/portal':
specifier: workspace:*
- version: link:../slot
+ version: link:../portal
'@rn-primitives/types':
specifier: workspace:*
version: link:../types
+ '@rn-primitives/utils':
+ specifier: workspace:*
+ version: link:../utils
react-native-web:
specifier: '*'
version: 0.20.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
devDependencies:
- '@rn-primitives/portal':
- specifier: workspace:*
- version: link:../portal
'@tsconfig/react-native':
specifier: ^1.0.1
version: 1.0.5
@@ -3335,6 +3344,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-dialog@1.1.14':
+ resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: 19.0.0
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-dialog@1.1.7':
resolution: {integrity: sha512-EIdma8C0C/I6kL6sO02avaCRqi3fmWJpxH6mqbVScorW6nNktzKJT/le7VPho3o/7wCsyRg3z0+Q+Obr0Gy/VQ==}
peerDependencies:
@@ -3357,6 +3379,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-dismissable-layer@1.1.10':
+ resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: 19.0.0
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-dismissable-layer@1.1.6':
resolution: {integrity: sha512-7gpgMT2gyKym9Jz2ZhlRXSg2y6cNQIK8d/cqBZ0RBCaps8pFryCWXiUKI+uHGFrhMrbGUP7U6PWgiXzIxoyF3Q==}
peerDependencies:
@@ -3405,6 +3440,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-focus-scope@1.1.7':
+ resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: 19.0.0
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-hover-card@1.1.7':
resolution: {integrity: sha512-HwM03kP8psrv21J1+9T/hhxi0f5rARVbqIZl9+IAq13l4j4fX+oGIuxisukZZmebO7J35w9gpoILvtG8bbph0w==}
peerDependencies:
@@ -3518,6 +3566,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-portal@1.1.9':
+ resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: 19.0.0
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-presence@1.1.3':
resolution: {integrity: sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==}
peerDependencies:
@@ -10290,6 +10351,28 @@ snapshots:
optionalDependencies:
'@types/react': 19.0.10
+ '@radix-ui/react-dialog@1.1.14(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0)
+ aria-hidden: 1.2.4
+ react: 19.0.0
+ react-dom: 19.0.0(react@19.0.0)
+ react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0)
+ optionalDependencies:
+ '@types/react': 19.0.10
+ '@types/react-dom': 19.0.4(@types/react@19.0.10)
+
'@radix-ui/react-dialog@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@radix-ui/primitive': 1.1.2
@@ -10318,6 +10401,19 @@ snapshots:
optionalDependencies:
'@types/react': 19.0.10
+ '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ react: 19.0.0
+ react-dom: 19.0.0(react@19.0.0)
+ optionalDependencies:
+ '@types/react': 19.0.10
+ '@types/react-dom': 19.0.4(@types/react@19.0.10)
+
'@radix-ui/react-dismissable-layer@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@radix-ui/primitive': 1.1.2
@@ -10363,6 +10459,17 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
+ '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ react: 19.0.0
+ react-dom: 19.0.0(react@19.0.0)
+ optionalDependencies:
+ '@types/react': 19.0.10
+ '@types/react-dom': 19.0.4(@types/react@19.0.10)
+
'@radix-ui/react-hover-card@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@radix-ui/primitive': 1.1.2
@@ -10513,6 +10620,16 @@ snapshots:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
+ '@radix-ui/react-portal@1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0)
+ react: 19.0.0
+ react-dom: 19.0.0(react@19.0.0)
+ optionalDependencies:
+ '@types/react': 19.0.10
+ '@types/react-dom': 19.0.4(@types/react@19.0.10)
+
'@radix-ui/react-presence@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0)