-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathModal.tsx
More file actions
130 lines (113 loc) · 3.71 KB
/
Copy pathModal.tsx
File metadata and controls
130 lines (113 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import React, { ElementRef, ReactNode, useMemo, useRef } from "react";
import { useDialog } from "react-aria";
import { classNames, variationName } from "../utilities/css";
import { ModalBody } from "./ModalBody";
import { ModalFooter } from "./ModalFooter";
import { ModalHeader } from "./ModalHeader";
import { ModalTrigger } from "./ModalTrigger";
import { ModalContext } from "./context";
import { useIntersectionDetection } from "./useIntersectionDetection";
import { ModalContainer } from "./ModalContainer";
import { useModalTrigger } from "./context";
import { ThirdPartyOverlayBoundary } from "./ThirdPartyOverlayBoundary";
import styles from "./Modal.module.scss";
type ModalSize = "sm" | "md" | "lg" | "xl";
export type ModalProps = {
/**
* Content of the modal.
*/
children: ReactNode;
/**
* Size of the modal.
*
* @default lg
*/
size?: ModalSize;
};
/**
* A `Modal` is a page overlay that displays information and blocks interaction
* with the page until an action is taken or the `Modal` is dismissed.
*
* @remarks
* Use a `<Modal />` when you want to capture information from the user without
* having them leave the parent page, or when you want to show additional
* information to the user without losing context of the parent page.
*
* @example
* <Modal.Trigger>
* <Button>Open modal</Button>
* <Modal>
* <Modal.Header>H4 Title</Modal.Header>
* <Modal.Body>Modal content</Modal.Body>
* <Modal.Footer>
* <HorizontalStack align="end">
* <Button>Continue</Button>
* </HorizontalStack>
* </Modal.Footer>
* </Modal>
* </Modal.Trigger>
*/
export function Modal(props: ModalProps) {
const { children, size = "lg" } = props;
const bodyRef = useRef<ElementRef<"div">>(null);
const headerInterceptorRef = useRef<ElementRef<"div">>(null);
const footerInterceptorRef = useRef<ElementRef<"div">>(null);
const isHeaderStuck = useIntersectionDetection(headerInterceptorRef, bodyRef);
const isFooterStuck = useIntersectionDetection(footerInterceptorRef, bodyRef);
const dialogRef = React.useRef(null);
const { dialogProps, titleProps } = useDialog({ role: "dialog" }, dialogRef);
const context = useMemo(() => {
return {
bodyRef,
headerInterceptorRef,
footerInterceptorRef,
isHeaderStuck,
isFooterStuck,
dialogProps,
titleProps,
};
}, [dialogProps, isFooterStuck, isHeaderStuck, titleProps]);
const className = classNames(
styles.Modal,
styles[variationName("size", size)],
);
return (
<ModalContext.Provider value={context}>
<div {...dialogProps} className={className} ref={dialogRef}>
{children}
</div>
</ModalContext.Provider>
);
}
/**
* Represents the trigger for a `<Modal />`.
*
* @remarks
* A `<Modal />` must be attached to a focusable trigger element such as a
* `Button` through the `<Modal.Trigger />` component. This ensures
* the trigger and modal are accessible.
*
* `<Modal.Trigger />` must contain exactly two direct children. The first
* child must be a focusable trigger such as `Button`. The second child must
* be either a `Modal` or a render function that returns a `Modal`. If using
* a render function, a `close` argument will be passed to allow for
* programmatically closing the `Modal`.
*/
Modal.Trigger = ModalTrigger;
/**
* Represents the header of a `<Modal />`.
*/
Modal.Header = ModalHeader;
/**
* Represents the body of a `<Modal />`.
*/
Modal.Body = ModalBody;
/**
* Represents the footer of a `<Modal />`.
*/
Modal.Footer = ModalFooter;
/**
* Represents a boundary for third-party overlays within a `<Modal />`.
*/
Modal.ThirdPartyOverlayBoundary = ThirdPartyOverlayBoundary;
export { ModalContainer, useModalTrigger };