@cdx-ui/primitives 0.0.1-alpha.21 → 0.0.1-alpha.23
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.
- package/lib/commonjs/dialog/context.js +11 -0
- package/lib/commonjs/dialog/context.js.map +1 -0
- package/lib/commonjs/dialog/createDialogBody.js +21 -0
- package/lib/commonjs/dialog/createDialogBody.js.map +1 -0
- package/lib/commonjs/dialog/createDialogClose.js +37 -0
- package/lib/commonjs/dialog/createDialogClose.js.map +1 -0
- package/lib/commonjs/dialog/createDialogContent.js +141 -0
- package/lib/commonjs/dialog/createDialogContent.js.map +1 -0
- package/lib/commonjs/dialog/createDialogDescription.js +30 -0
- package/lib/commonjs/dialog/createDialogDescription.js.map +1 -0
- package/lib/commonjs/dialog/createDialogFooter.js +21 -0
- package/lib/commonjs/dialog/createDialogFooter.js.map +1 -0
- package/lib/commonjs/dialog/createDialogHeader.js +21 -0
- package/lib/commonjs/dialog/createDialogHeader.js.map +1 -0
- package/lib/commonjs/dialog/createDialogRoot.js +54 -0
- package/lib/commonjs/dialog/createDialogRoot.js.map +1 -0
- package/lib/commonjs/dialog/createDialogTitle.js +21 -0
- package/lib/commonjs/dialog/createDialogTitle.js.map +1 -0
- package/lib/commonjs/dialog/createDialogTrigger.js +37 -0
- package/lib/commonjs/dialog/createDialogTrigger.js.map +1 -0
- package/lib/commonjs/dialog/index.js +60 -0
- package/lib/commonjs/dialog/index.js.map +1 -0
- package/lib/commonjs/dialog/types.js +6 -0
- package/lib/commonjs/dialog/types.js.map +1 -0
- package/lib/commonjs/index.js +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/overlay/OverlayContainer.js +3 -8
- package/lib/commonjs/overlay/OverlayContainer.js.map +1 -1
- package/lib/commonjs/overlay/index.js +7 -0
- package/lib/commonjs/overlay/index.js.map +1 -1
- package/lib/commonjs/overlay/useEscapeKey.js +14 -0
- package/lib/commonjs/overlay/useEscapeKey.js.map +1 -0
- package/lib/commonjs/overlay/useEscapeKey.web.js +30 -0
- package/lib/commonjs/overlay/useEscapeKey.web.js.map +1 -0
- package/lib/commonjs/utils/createPortal.js +23 -0
- package/lib/commonjs/utils/createPortal.js.map +1 -0
- package/lib/module/dialog/context.js +5 -0
- package/lib/module/dialog/context.js.map +1 -0
- package/lib/module/dialog/createDialogBody.js +15 -0
- package/lib/module/dialog/createDialogBody.js.map +1 -0
- package/lib/module/dialog/createDialogClose.js +31 -0
- package/lib/module/dialog/createDialogClose.js.map +1 -0
- package/lib/module/dialog/createDialogContent.js +136 -0
- package/lib/module/dialog/createDialogContent.js.map +1 -0
- package/lib/module/dialog/createDialogDescription.js +24 -0
- package/lib/module/dialog/createDialogDescription.js.map +1 -0
- package/lib/module/dialog/createDialogFooter.js +15 -0
- package/lib/module/dialog/createDialogFooter.js.map +1 -0
- package/lib/module/dialog/createDialogHeader.js +15 -0
- package/lib/module/dialog/createDialogHeader.js.map +1 -0
- package/lib/module/dialog/createDialogRoot.js +49 -0
- package/lib/module/dialog/createDialogRoot.js.map +1 -0
- package/lib/module/dialog/createDialogTitle.js +15 -0
- package/lib/module/dialog/createDialogTitle.js.map +1 -0
- package/lib/module/dialog/createDialogTrigger.js +31 -0
- package/lib/module/dialog/createDialogTrigger.js.map +1 -0
- package/lib/module/dialog/index.js +45 -0
- package/lib/module/dialog/index.js.map +1 -0
- package/lib/module/dialog/types.js +4 -0
- package/lib/module/dialog/types.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/overlay/OverlayContainer.js +1 -6
- package/lib/module/overlay/OverlayContainer.js.map +1 -1
- package/lib/module/overlay/index.js +1 -0
- package/lib/module/overlay/index.js.map +1 -1
- package/lib/module/overlay/useEscapeKey.js +10 -0
- package/lib/module/overlay/useEscapeKey.js.map +1 -0
- package/lib/module/overlay/useEscapeKey.web.js +27 -0
- package/lib/module/overlay/useEscapeKey.web.js.map +1 -0
- package/lib/module/utils/createPortal.js +19 -0
- package/lib/module/utils/createPortal.js.map +1 -0
- package/lib/typescript/dialog/context.d.ts +6 -0
- package/lib/typescript/dialog/context.d.ts.map +1 -0
- package/lib/typescript/dialog/createDialogBody.d.ts +4 -0
- package/lib/typescript/dialog/createDialogBody.d.ts.map +1 -0
- package/lib/typescript/dialog/createDialogClose.d.ts +4 -0
- package/lib/typescript/dialog/createDialogClose.d.ts.map +1 -0
- package/lib/typescript/dialog/createDialogContent.d.ts +4 -0
- package/lib/typescript/dialog/createDialogContent.d.ts.map +1 -0
- package/lib/typescript/dialog/createDialogDescription.d.ts +4 -0
- package/lib/typescript/dialog/createDialogDescription.d.ts.map +1 -0
- package/lib/typescript/dialog/createDialogFooter.d.ts +4 -0
- package/lib/typescript/dialog/createDialogFooter.d.ts.map +1 -0
- package/lib/typescript/dialog/createDialogHeader.d.ts +4 -0
- package/lib/typescript/dialog/createDialogHeader.d.ts.map +1 -0
- package/lib/typescript/dialog/createDialogRoot.d.ts +4 -0
- package/lib/typescript/dialog/createDialogRoot.d.ts.map +1 -0
- package/lib/typescript/dialog/createDialogTitle.d.ts +4 -0
- package/lib/typescript/dialog/createDialogTitle.d.ts.map +1 -0
- package/lib/typescript/dialog/createDialogTrigger.d.ts +4 -0
- package/lib/typescript/dialog/createDialogTrigger.d.ts.map +1 -0
- package/lib/typescript/dialog/index.d.ts +18 -0
- package/lib/typescript/dialog/index.d.ts.map +1 -0
- package/lib/typescript/dialog/types.d.ts +52 -0
- package/lib/typescript/dialog/types.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/overlay/OverlayContainer.d.ts.map +1 -1
- package/lib/typescript/overlay/index.d.ts +1 -0
- package/lib/typescript/overlay/index.d.ts.map +1 -1
- package/lib/typescript/overlay/useEscapeKey.d.ts +6 -0
- package/lib/typescript/overlay/useEscapeKey.d.ts.map +1 -0
- package/lib/typescript/overlay/useEscapeKey.web.d.ts +6 -0
- package/lib/typescript/overlay/useEscapeKey.web.d.ts.map +1 -0
- package/lib/typescript/utils/createPortal.d.ts +11 -0
- package/lib/typescript/utils/createPortal.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/dialog/context.tsx +4 -0
- package/src/dialog/createDialogBody.tsx +11 -0
- package/src/dialog/createDialogClose.tsx +27 -0
- package/src/dialog/createDialogContent.tsx +122 -0
- package/src/dialog/createDialogDescription.tsx +20 -0
- package/src/dialog/createDialogFooter.tsx +11 -0
- package/src/dialog/createDialogHeader.tsx +11 -0
- package/src/dialog/createDialogRoot.tsx +66 -0
- package/src/dialog/createDialogTitle.tsx +11 -0
- package/src/dialog/createDialogTrigger.tsx +27 -0
- package/src/dialog/index.tsx +93 -0
- package/src/dialog/types.ts +88 -0
- package/src/index.ts +1 -0
- package/src/overlay/OverlayContainer.tsx +2 -10
- package/src/overlay/index.ts +1 -0
- package/src/overlay/useEscapeKey.ts +7 -0
- package/src/overlay/useEscapeKey.web.ts +27 -0
- package/src/utils/createPortal.ts +20 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef, useCallback, useId, useMemo } from 'react';
|
|
3
|
+
import { useControllableState } from '@cdx-ui/utils';
|
|
4
|
+
import { dataAttributes } from '../utils/dataAttributes';
|
|
5
|
+
import { DialogProvider } from './context';
|
|
6
|
+
import type { IDialogRootProps } from './types';
|
|
7
|
+
|
|
8
|
+
export const createDialogRoot = <T,>(BaseRoot: React.ComponentType<T>) =>
|
|
9
|
+
forwardRef(
|
|
10
|
+
(
|
|
11
|
+
{
|
|
12
|
+
open: openProp,
|
|
13
|
+
onOpenChange,
|
|
14
|
+
defaultOpen = false,
|
|
15
|
+
closeOnBackdropPress = true,
|
|
16
|
+
closeOnEscKey = true,
|
|
17
|
+
role = 'dialog',
|
|
18
|
+
children,
|
|
19
|
+
...props
|
|
20
|
+
}: IDialogRootProps,
|
|
21
|
+
ref?: React.Ref<T>,
|
|
22
|
+
) => {
|
|
23
|
+
const [open, setOpenState] = useControllableState<boolean>({
|
|
24
|
+
prop: openProp,
|
|
25
|
+
defaultProp: defaultOpen,
|
|
26
|
+
onChange: onOpenChange,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const setOpen = useCallback(
|
|
30
|
+
(newOpen: boolean) => {
|
|
31
|
+
setOpenState(newOpen);
|
|
32
|
+
},
|
|
33
|
+
[setOpenState],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const id = useId();
|
|
37
|
+
const nativeID = `dialog-${id}`;
|
|
38
|
+
|
|
39
|
+
const contextValue = useMemo(
|
|
40
|
+
() => ({
|
|
41
|
+
open: open ?? false,
|
|
42
|
+
onOpenChange: setOpen,
|
|
43
|
+
closeOnBackdropPress,
|
|
44
|
+
closeOnEscKey,
|
|
45
|
+
nativeID,
|
|
46
|
+
role,
|
|
47
|
+
}),
|
|
48
|
+
[open, setOpen, closeOnBackdropPress, closeOnEscKey, nativeID, role],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<DialogProvider value={contextValue}>
|
|
53
|
+
<BaseRoot
|
|
54
|
+
ref={ref}
|
|
55
|
+
{...(props as T)}
|
|
56
|
+
{...dataAttributes({
|
|
57
|
+
slot: 'dialog',
|
|
58
|
+
state: open ? 'open' : 'closed',
|
|
59
|
+
})}
|
|
60
|
+
>
|
|
61
|
+
{children}
|
|
62
|
+
</BaseRoot>
|
|
63
|
+
</DialogProvider>
|
|
64
|
+
);
|
|
65
|
+
},
|
|
66
|
+
);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import type { IDialogTitleProps } from './types';
|
|
3
|
+
|
|
4
|
+
export const createDialogTitle = <T,>(BaseTitle: React.ComponentType<T>) =>
|
|
5
|
+
forwardRef<unknown, IDialogTitleProps>(({ children, ...props }, ref) => {
|
|
6
|
+
return (
|
|
7
|
+
<BaseTitle ref={ref} {...(props as T)}>
|
|
8
|
+
{children}
|
|
9
|
+
</BaseTitle>
|
|
10
|
+
);
|
|
11
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import type { IDialogTriggerProps } from './types';
|
|
3
|
+
import { useDialog } from './context';
|
|
4
|
+
|
|
5
|
+
export const createDialogTrigger = <T,>(BaseTrigger: React.ComponentType<T>) =>
|
|
6
|
+
forwardRef(
|
|
7
|
+
({ asChild = false, children, onPress, ...props }: IDialogTriggerProps, ref?: React.Ref<T>) => {
|
|
8
|
+
const { onOpenChange } = useDialog();
|
|
9
|
+
|
|
10
|
+
const handlePress = (e?: any) => {
|
|
11
|
+
onPress?.(e);
|
|
12
|
+
onOpenChange(true);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
if (asChild && React.isValidElement(children)) {
|
|
16
|
+
return React.cloneElement(children as React.ReactElement<any>, {
|
|
17
|
+
onPress: handlePress,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<BaseTrigger ref={ref} {...(props as T)} onPress={handlePress}>
|
|
23
|
+
{children}
|
|
24
|
+
</BaseTrigger>
|
|
25
|
+
);
|
|
26
|
+
},
|
|
27
|
+
);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { createDialogRoot } from './createDialogRoot';
|
|
3
|
+
import { createDialogTrigger } from './createDialogTrigger';
|
|
4
|
+
import { createDialogContent } from './createDialogContent';
|
|
5
|
+
import { createDialogHeader } from './createDialogHeader';
|
|
6
|
+
import { createDialogTitle } from './createDialogTitle';
|
|
7
|
+
import { createDialogDescription } from './createDialogDescription';
|
|
8
|
+
import { createDialogBody } from './createDialogBody';
|
|
9
|
+
import { createDialogFooter } from './createDialogFooter';
|
|
10
|
+
import { createDialogClose } from './createDialogClose';
|
|
11
|
+
import { DialogProvider, useDialog } from './context';
|
|
12
|
+
import type { IDialogComponentType } from './types';
|
|
13
|
+
|
|
14
|
+
export { DialogProvider, useDialog };
|
|
15
|
+
export type {
|
|
16
|
+
IDialogContextType,
|
|
17
|
+
IDialogRootProps,
|
|
18
|
+
IDialogTriggerProps,
|
|
19
|
+
IDialogContentProps,
|
|
20
|
+
IDialogOverlayProps,
|
|
21
|
+
IDialogHeaderProps,
|
|
22
|
+
IDialogTitleProps,
|
|
23
|
+
IDialogDescriptionProps,
|
|
24
|
+
IDialogBodyProps,
|
|
25
|
+
IDialogFooterProps,
|
|
26
|
+
IDialogCloseProps,
|
|
27
|
+
} from './types';
|
|
28
|
+
|
|
29
|
+
export function createDialog<
|
|
30
|
+
RootProps,
|
|
31
|
+
TriggerProps,
|
|
32
|
+
ContentProps,
|
|
33
|
+
OverlayProps,
|
|
34
|
+
HeaderProps,
|
|
35
|
+
TitleProps,
|
|
36
|
+
DescriptionProps,
|
|
37
|
+
BodyProps,
|
|
38
|
+
FooterProps,
|
|
39
|
+
CloseProps,
|
|
40
|
+
>(BaseComponents: {
|
|
41
|
+
Root: React.ComponentType<RootProps>;
|
|
42
|
+
Trigger: React.ComponentType<TriggerProps>;
|
|
43
|
+
Content: React.ComponentType<ContentProps>;
|
|
44
|
+
Overlay: React.ComponentType<OverlayProps>;
|
|
45
|
+
Header: React.ComponentType<HeaderProps>;
|
|
46
|
+
Title: React.ComponentType<TitleProps>;
|
|
47
|
+
Description: React.ComponentType<DescriptionProps>;
|
|
48
|
+
Body: React.ComponentType<BodyProps>;
|
|
49
|
+
Footer: React.ComponentType<FooterProps>;
|
|
50
|
+
Close: React.ComponentType<CloseProps>;
|
|
51
|
+
}) {
|
|
52
|
+
const Root = createDialogRoot(BaseComponents.Root);
|
|
53
|
+
const Trigger = createDialogTrigger(BaseComponents.Trigger);
|
|
54
|
+
const Content = createDialogContent(BaseComponents.Content, BaseComponents.Overlay);
|
|
55
|
+
const Header = createDialogHeader(BaseComponents.Header);
|
|
56
|
+
const Title = createDialogTitle(BaseComponents.Title);
|
|
57
|
+
const Description = createDialogDescription(BaseComponents.Description);
|
|
58
|
+
const Body = createDialogBody(BaseComponents.Body);
|
|
59
|
+
const Footer = createDialogFooter(BaseComponents.Footer);
|
|
60
|
+
const Close = createDialogClose(BaseComponents.Close);
|
|
61
|
+
|
|
62
|
+
Root.displayName = 'DialogPrimitive.Root';
|
|
63
|
+
Trigger.displayName = 'DialogPrimitive.Trigger';
|
|
64
|
+
Content.displayName = 'DialogPrimitive.Content';
|
|
65
|
+
Header.displayName = 'DialogPrimitive.Header';
|
|
66
|
+
Title.displayName = 'DialogPrimitive.Title';
|
|
67
|
+
Description.displayName = 'DialogPrimitive.Description';
|
|
68
|
+
Body.displayName = 'DialogPrimitive.Body';
|
|
69
|
+
Footer.displayName = 'DialogPrimitive.Footer';
|
|
70
|
+
Close.displayName = 'DialogPrimitive.Close';
|
|
71
|
+
|
|
72
|
+
return Object.assign(Root, {
|
|
73
|
+
Root,
|
|
74
|
+
Trigger,
|
|
75
|
+
Content,
|
|
76
|
+
Header,
|
|
77
|
+
Title,
|
|
78
|
+
Description,
|
|
79
|
+
Body,
|
|
80
|
+
Footer,
|
|
81
|
+
Close,
|
|
82
|
+
}) as IDialogComponentType<
|
|
83
|
+
RootProps,
|
|
84
|
+
TriggerProps,
|
|
85
|
+
ContentProps,
|
|
86
|
+
HeaderProps,
|
|
87
|
+
TitleProps,
|
|
88
|
+
DescriptionProps,
|
|
89
|
+
BodyProps,
|
|
90
|
+
FooterProps,
|
|
91
|
+
CloseProps
|
|
92
|
+
>;
|
|
93
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { PropsWithoutRef, RefAttributes } from 'react';
|
|
2
|
+
import type { PressableProps, TextProps, ViewProps } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface IDialogContextType {
|
|
5
|
+
readonly open: boolean;
|
|
6
|
+
readonly onOpenChange: (open: boolean) => void;
|
|
7
|
+
readonly closeOnBackdropPress: boolean;
|
|
8
|
+
readonly closeOnEscKey: boolean;
|
|
9
|
+
readonly nativeID: string;
|
|
10
|
+
readonly role: 'dialog' | 'alertdialog';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface IDialogRootProps {
|
|
14
|
+
readonly open?: boolean;
|
|
15
|
+
readonly onOpenChange?: (open: boolean) => void;
|
|
16
|
+
readonly defaultOpen?: boolean;
|
|
17
|
+
readonly closeOnBackdropPress?: boolean;
|
|
18
|
+
readonly closeOnEscKey?: boolean;
|
|
19
|
+
readonly role?: 'dialog' | 'alertdialog';
|
|
20
|
+
readonly children: React.ReactNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface IDialogTriggerProps extends PressableProps {
|
|
24
|
+
readonly asChild?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface IDialogContentProps extends ViewProps {
|
|
28
|
+
readonly forceMount?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IDialogOverlayProps extends PressableProps {}
|
|
32
|
+
|
|
33
|
+
export interface IDialogHeaderProps extends ViewProps {}
|
|
34
|
+
|
|
35
|
+
export interface IDialogTitleProps extends TextProps {}
|
|
36
|
+
|
|
37
|
+
export interface IDialogDescriptionProps extends TextProps {}
|
|
38
|
+
|
|
39
|
+
export interface IDialogBodyProps extends ViewProps {}
|
|
40
|
+
|
|
41
|
+
export interface IDialogFooterProps extends ViewProps {}
|
|
42
|
+
|
|
43
|
+
export interface IDialogCloseProps extends PressableProps {
|
|
44
|
+
readonly asChild?: boolean;
|
|
45
|
+
readonly accessibilityLabel?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type IDialogComponentType<
|
|
49
|
+
RootProps,
|
|
50
|
+
TriggerProps,
|
|
51
|
+
ContentProps,
|
|
52
|
+
HeaderProps,
|
|
53
|
+
TitleProps,
|
|
54
|
+
DescriptionProps,
|
|
55
|
+
BodyProps,
|
|
56
|
+
FooterProps,
|
|
57
|
+
CloseProps,
|
|
58
|
+
ContentRef = unknown,
|
|
59
|
+
TriggerRef = unknown,
|
|
60
|
+
CloseRef = unknown,
|
|
61
|
+
> = React.ForwardRefExoticComponent<
|
|
62
|
+
PropsWithoutRef<RootProps & IDialogRootProps> & RefAttributes<unknown>
|
|
63
|
+
> & {
|
|
64
|
+
Trigger: React.ForwardRefExoticComponent<
|
|
65
|
+
PropsWithoutRef<TriggerProps & IDialogTriggerProps> & RefAttributes<TriggerRef>
|
|
66
|
+
>;
|
|
67
|
+
Content: React.ForwardRefExoticComponent<
|
|
68
|
+
PropsWithoutRef<ContentProps & IDialogContentProps> & RefAttributes<ContentRef>
|
|
69
|
+
>;
|
|
70
|
+
Header: React.ForwardRefExoticComponent<
|
|
71
|
+
PropsWithoutRef<HeaderProps & IDialogHeaderProps> & RefAttributes<unknown>
|
|
72
|
+
>;
|
|
73
|
+
Title: React.ForwardRefExoticComponent<
|
|
74
|
+
PropsWithoutRef<TitleProps & IDialogTitleProps> & RefAttributes<unknown>
|
|
75
|
+
>;
|
|
76
|
+
Description: React.ForwardRefExoticComponent<
|
|
77
|
+
PropsWithoutRef<DescriptionProps & IDialogDescriptionProps> & RefAttributes<unknown>
|
|
78
|
+
>;
|
|
79
|
+
Body: React.ForwardRefExoticComponent<
|
|
80
|
+
PropsWithoutRef<BodyProps & IDialogBodyProps> & RefAttributes<unknown>
|
|
81
|
+
>;
|
|
82
|
+
Footer: React.ForwardRefExoticComponent<
|
|
83
|
+
PropsWithoutRef<FooterProps & IDialogFooterProps> & RefAttributes<unknown>
|
|
84
|
+
>;
|
|
85
|
+
Close: React.ForwardRefExoticComponent<
|
|
86
|
+
PropsWithoutRef<CloseProps & IDialogCloseProps> & RefAttributes<CloseRef>
|
|
87
|
+
>;
|
|
88
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -3,21 +3,13 @@ import {
|
|
|
3
3
|
Modal,
|
|
4
4
|
Platform,
|
|
5
5
|
Pressable,
|
|
6
|
-
type StyleProp,
|
|
7
6
|
StyleSheet,
|
|
8
7
|
View,
|
|
8
|
+
type StyleProp,
|
|
9
9
|
type ViewStyle,
|
|
10
10
|
} from 'react-native';
|
|
11
11
|
import Animated, { type EntryOrExitLayoutType } from 'react-native-reanimated';
|
|
12
|
-
|
|
13
|
-
let createPortalFn:
|
|
14
|
-
| ((children: React.ReactNode, container: Element) => React.ReactPortal)
|
|
15
|
-
| undefined;
|
|
16
|
-
if (Platform.OS === 'web') {
|
|
17
|
-
try {
|
|
18
|
-
createPortalFn = require('react-dom').createPortal;
|
|
19
|
-
} catch {}
|
|
20
|
-
}
|
|
12
|
+
import { createPortalFn } from '../utils/createPortal';
|
|
21
13
|
|
|
22
14
|
export interface OverlayContainerProps {
|
|
23
15
|
onDismiss: () => void;
|
package/src/overlay/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { OverlayContainer } from './OverlayContainer';
|
|
|
2
2
|
export type { OverlayContainerProps } from './OverlayContainer';
|
|
3
3
|
export { useAnchorPosition, type AnchorLayout } from './useAnchorPosition';
|
|
4
4
|
export { useDismissOverlay } from './useDismissOverlay';
|
|
5
|
+
export { useEscapeKey } from './useEscapeKey';
|
|
5
6
|
export {
|
|
6
7
|
useOverlayPosition,
|
|
7
8
|
type OverlayPosition,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* No-op on native — Escape key dismissal is not applicable.
|
|
5
|
+
* Native uses the system back gesture / hardware button via Modal.
|
|
6
|
+
*/
|
|
7
|
+
export function useEscapeKey(_open: boolean, _onEscape: () => void) {}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Closes an overlay when the user presses the Escape key.
|
|
5
|
+
* Web-only — native platforms use Modal's `onRequestClose` for back/dismiss.
|
|
6
|
+
*/
|
|
7
|
+
export function useEscapeKey(open: boolean, onEscape: () => void) {
|
|
8
|
+
const onEscapeRef = useRef(onEscape);
|
|
9
|
+
onEscapeRef.current = onEscape;
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!open) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
17
|
+
if (e.key === 'Escape') {
|
|
18
|
+
onEscapeRef.current();
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
23
|
+
return () => {
|
|
24
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
25
|
+
};
|
|
26
|
+
}, [open]);
|
|
27
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
|
|
4
|
+
type CreatePortalFn = (children: React.ReactNode, container: Element) => React.ReactPortal;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A reference to `ReactDOM.createPortal` on web, `undefined` on native.
|
|
8
|
+
*
|
|
9
|
+
* Loaded lazily via `require` so that native bundles never reference
|
|
10
|
+
* `react-dom`, which would cause a build error on native platforms.
|
|
11
|
+
*/
|
|
12
|
+
export const createPortalFn: CreatePortalFn | undefined = (() => {
|
|
13
|
+
if (Platform.OS !== 'web') return undefined;
|
|
14
|
+
try {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
|
+
return (require('react-dom') as { createPortal: CreatePortalFn }).createPortal;
|
|
17
|
+
} catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
})();
|