Page MenuHomePhabricator

D5353.id17527.diff
No OneTemporary

D5353.id17527.diff

diff --git a/lib/components/modal-provider.react.js b/lib/components/modal-provider.react.js
new file mode 100644
--- /dev/null
+++ b/lib/components/modal-provider.react.js
@@ -0,0 +1,68 @@
+// @flow
+
+import invariant from 'invariant';
+import * as React from 'react';
+
+import { getUUID } from '../utils/uuid';
+
+type Props = {
+ +children: React.Node,
+};
+type ModalContextType = {
+ +modals: $ReadOnlyArray<[React.Node, string]>,
+ +pushModal: React.Node => void,
+ +popModal: () => void,
+ +clearModals: () => void,
+};
+
+const ModalContext: React.Context<?ModalContextType> = React.createContext<?ModalContextType>(
+ {
+ modals: [],
+ pushModal: () => {},
+ popModal: () => {},
+ clearModals: () => {},
+ },
+);
+
+function ModalProvider(props: Props): React.Node {
+ const { children } = props;
+
+ const [modals, setModals] = React.useState<
+ $ReadOnlyArray<[React.Node, string]>,
+ >([]);
+
+ const popModal = React.useCallback(
+ () => setModals(oldModals => oldModals.slice(0, -1)),
+ [],
+ );
+
+ const pushModal = React.useCallback(newModal => {
+ const key = getUUID();
+ setModals(oldModals => [...oldModals, [newModal, key]]);
+ }, []);
+
+ const clearModals = React.useCallback(() => setModals([]), []);
+
+ const value = React.useMemo(
+ () => ({
+ modals,
+ pushModal,
+ popModal,
+ clearModals,
+ }),
+ [modals, pushModal, popModal, clearModals],
+ );
+
+ return (
+ <ModalContext.Provider value={value}>{children}</ModalContext.Provider>
+ );
+}
+
+function useModalContext(): ModalContextType {
+ const context = React.useContext(ModalContext);
+ invariant(context, 'ModalContext not found');
+
+ return context;
+}
+
+export { ModalProvider, useModalContext };
diff --git a/lib/components/modal.css b/lib/components/modal.css
new file mode 100644
--- /dev/null
+++ b/lib/components/modal.css
@@ -0,0 +1,13 @@
+div.modalOverlay {
+ position: fixed;
+ left: 0;
+ top: 0;
+ height: 100%;
+ z-index: 4;
+ width: 100%;
+ background-color: #000000e6;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
diff --git a/lib/components/modal.react.js b/lib/components/modal.react.js
new file mode 100644
--- /dev/null
+++ b/lib/components/modal.react.js
@@ -0,0 +1,54 @@
+// @flow
+
+import * as React from 'react';
+
+import css from './modal.css';
+
+type ModalProps = {
+ +onClose: () => void,
+ +children?: React.Node,
+};
+
+function Modal(props: ModalProps): React.Node {
+ const { children, onClose } = props;
+
+ const overlayRef = React.useRef();
+
+ React.useLayoutEffect(() => {
+ if (overlayRef.current) {
+ overlayRef.current.focus();
+ }
+ }, []);
+
+ const onBackgroundClick = React.useCallback(
+ event => {
+ if (event.target === overlayRef.current) {
+ onClose();
+ }
+ },
+ [onClose],
+ );
+
+ const onKeyDown = React.useCallback(
+ event => {
+ if (event.keyCode === 27) {
+ onClose();
+ }
+ },
+ [onClose],
+ );
+
+ return (
+ <div
+ className={css.modalOverlay}
+ ref={overlayRef}
+ onClick={onBackgroundClick}
+ tabIndex={0}
+ onKeyDown={onKeyDown}
+ >
+ {children}
+ </div>
+ );
+}
+
+export default Modal;

File Metadata

Mime Type
text/plain
Expires
Fri, Sep 20, 6:07 AM (16 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2136025
Default Alt Text
D5353.id17527.diff (3 KB)

Event Timeline