UNPKG

glass-app-manager

Version:

Informatica's Glass Framework CLI for bootstrapping

189 lines (156 loc) 4.82 kB
// @flow import * as React from "react"; import { Rnd } from "react-rnd/lib/index.es5.js"; import classNames from "classnames"; import DialogHeader from "./components/DialogHeader"; import DialogContent from "./components/DialogContent"; import DialogFooter from "./components/DialogFooter"; import withPortal from "../withPortal/withPortal"; import { type DialogContextProps, DialogContext } from "./DialogContext"; let rndDefault: ?{| x: number, y: number, |} = null; export type DialogProps = { /** * Set true if the dialog is closable. Setting false will prevent the close * icon from rendering in the dialog header. */ closable?: boolean, /** * Set false to disable dialog dragging. */ draggable?: boolean, /** * Set false to disable dialog resizing. */ resizable?: boolean, /** * Provide valid URL to the dialog to render the help icon in the dialog footer. */ help?: string, /** * Callback to execute when the dialog is closed. */ onClose: () => void, /** * Provide custom instructional text. This will be _above_ the panel or * custom rendered `Dialog.Content`. */ instructions?: React.Node, /** * Set this prop to control the visibility of the dialog. */ closed?: boolean, /** * Set false to not show the dimmer. */ dimmer?: boolean, /** * Children of the dialog. */ children: React.Node, /** * Any props to forward to `react-rnd`. Applicable only when * draggable or resizable is set true. */ rnd?: {}, }; const ESC_KEY = 27; const RegularDialog = function(props: DialogProps, id: number) { const { className, style, children, ...rest }: { className: string, style: {}, rest: DialogContextProps } = props; rest.id = id; if (props.draggable || props.resizable) { const width = Math.round(window.innerWidth / 2); const height = Math.round(window.innerHeight / 2); if (rndDefault) { if (rndDefault.x !== width) { rndDefault.x = width; } if (rndDefault.y !== height) { rndDefault.y = height; } } } return ( <div className={classNames( "dialog", props.resizable ? "dialog--resize" : "", props.draggable ? "dialog--draggable" : "", className )} style={style} role="dialog" aria-labelledby={`dialog__title__${id}`} aria-describedby={`dialog__content__${id}`} aria-modal="true" aria-hidden={props.closed}> <DialogContext.Provider value={rest}>{children}</DialogContext.Provider> </div> ); }; function Dialog(props: DialogProps) { const handleKeyUp = React.useCallback( (e: SyntheticKeyboardEvent<HTMLElement>) => { if (e.keyCode === ESC_KEY) { props.onClose(); } }, [props.onClose] ); React.useEffect(() => { if (!rndDefault) { rndDefault = { x: Math.round(window.innerWidth / 2), y: Math.round(window.innerHeight / 2), }; } if (!props.closed) { window.addEventListener("keyup", handleKeyUp); } return () => { window.removeEventListener("keyup", handleKeyUp); }; }, [props.closed, rndDefault]); if (props.closed) { return null; } if (props.closable && !props.onClose) { throw new Error("A closable dialog must contain an onClose callback"); } const id = Math.round(Math.random() * 1000); if (props.draggable || props.resizable) { return ( <div className="dimmer"> <Rnd default={rndDefault} disableDragging={!props.draggable} {...props.rnd}> {RegularDialog(props, id)} </Rnd> </div> ); } return ( <> {props.dimmer ? <div className="dimmer" /> : null} {RegularDialog(props, id)} </> ); } Dialog.Header = DialogHeader; Dialog.Content = DialogContent; Dialog.Footer = DialogFooter; Dialog.Features = { withPortal: args => withPortal({ ...args, customSelector: "dialog--portal", }), }; Dialog.defaultProps = { resizable: true, draggable: true, bounds: "window", closable: true, dimmer: true, }; export default Dialog;