glass-app-manager
Version:
Informatica's Glass Framework CLI for bootstrapping
189 lines (156 loc) • 4.82 kB
JavaScript
// @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;