2022-05-29 13:48:51 -07:00
|
|
|
import { h, ComponentChildren } from 'preact';
|
2022-05-31 17:41:24 -07:00
|
|
|
import { createPortal } from 'preact/compat';
|
2022-05-10 06:52:06 -07:00
|
|
|
import { useCallback } from 'preact/hooks';
|
|
|
|
import { useHotKey } from './hooks/useHotKey';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Temporary JS styling while I figure out how I really want
|
|
|
|
* to do it.
|
|
|
|
*/
|
|
|
|
const modalOverlayStyle = {
|
|
|
|
position: 'fixed',
|
|
|
|
left: '0',
|
|
|
|
right: '0',
|
|
|
|
top: '0',
|
|
|
|
bottom: '0',
|
|
|
|
display: 'flex',
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
|
|
|
background: 'rgba(0,0,0,0.6)',
|
|
|
|
zIndex: 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
const modalStyle = {
|
|
|
|
backgroundColor: '#c4c1a0',
|
|
|
|
padding: '10px',
|
|
|
|
maxHeight: '100vh',
|
|
|
|
borderRadius: '4px',
|
|
|
|
overflowY: 'auto',
|
|
|
|
boxSizing: 'border-box',
|
|
|
|
};
|
|
|
|
|
|
|
|
const modalHeaderStyle = {
|
|
|
|
display: 'flex',
|
|
|
|
fontSize: '14px',
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
alignItems: 'center',
|
|
|
|
background: '#44372C',
|
|
|
|
color: '#fff',
|
|
|
|
padding: '5px 11px',
|
|
|
|
border: '1px outset #66594E',
|
|
|
|
borderRadius: '3px',
|
|
|
|
};
|
|
|
|
|
|
|
|
const modalTitleStyle = {
|
|
|
|
marginTop: 0,
|
|
|
|
marginBottom: 0,
|
|
|
|
fontWeight: 600,
|
|
|
|
fontSize: '1.25rem',
|
|
|
|
lineHeight: 1.25,
|
|
|
|
color: '#fff',
|
|
|
|
boxSizing: 'border-box',
|
|
|
|
};
|
|
|
|
|
|
|
|
const modalContentStyle = {
|
|
|
|
marginTop: '10px',
|
|
|
|
marginBottom: '10px',
|
|
|
|
lineHeight: 1.5,
|
|
|
|
color: '#000'
|
|
|
|
};
|
|
|
|
|
|
|
|
const modalFooterStyle = {
|
|
|
|
textAlign: 'right'
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ModalOverlay creates a semi-transparent overlay in which the
|
|
|
|
* modal is centered.
|
|
|
|
*
|
|
|
|
* @returns ModalOverlay component
|
|
|
|
*/
|
2022-05-29 13:48:51 -07:00
|
|
|
export const ModalOverlay = ({ children }: { children: ComponentChildren }) => {
|
2022-05-10 06:52:06 -07:00
|
|
|
return (
|
2022-05-31 17:41:24 -07:00
|
|
|
<div style={modalOverlayStyle} className="modal-overlay">
|
2022-05-10 06:52:06 -07:00
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ModalContent provides a styled container for modal content
|
|
|
|
*
|
|
|
|
* @returns ModalContent component
|
|
|
|
*/
|
2022-05-29 13:48:51 -07:00
|
|
|
export const ModalContent = ({ children }: { children: ComponentChildren }) => {
|
2022-05-10 06:52:06 -07:00
|
|
|
return (
|
|
|
|
<div style={modalContentStyle}>
|
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ModalFooter provides a right-aligned container for modal buttons.
|
|
|
|
*
|
|
|
|
* @returns ModalFooter component
|
|
|
|
*/
|
2022-05-29 13:48:51 -07:00
|
|
|
export const ModalFooter = ({ children }: { children: ComponentChildren }) => {
|
2022-05-10 06:52:06 -07:00
|
|
|
return (
|
2022-05-31 17:41:24 -07:00
|
|
|
<footer style={modalFooterStyle}>
|
2022-05-10 06:52:06 -07:00
|
|
|
{children}
|
2022-05-31 17:41:24 -07:00
|
|
|
</footer>
|
2022-05-10 06:52:06 -07:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ModalCloseButton component properties
|
|
|
|
*/
|
|
|
|
interface ModalCloseButtonProp {
|
|
|
|
onClose: (closeBox?: boolean) => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Renders a close button and registers a global Escape key
|
|
|
|
* hook to trigger it.
|
|
|
|
*
|
|
|
|
* @param onClose Close callback
|
|
|
|
* @returns ModalClose component
|
|
|
|
*/
|
|
|
|
export const ModalCloseButton = ({ onClose }: ModalCloseButtonProp) => {
|
|
|
|
const doClose = useCallback(() => onClose(true), [onClose]);
|
|
|
|
useHotKey('Escape', doClose);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<button onClick={doClose} title="Close">
|
|
|
|
{'\u2715'}
|
|
|
|
</button>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-05-11 17:20:49 -07:00
|
|
|
type OnCloseCallback = (closeBox?: boolean) => void;
|
|
|
|
|
2022-05-10 06:52:06 -07:00
|
|
|
/**
|
|
|
|
* ModalHeader component properties
|
|
|
|
*/
|
2022-05-11 17:20:49 -07:00
|
|
|
|
2022-05-10 06:52:06 -07:00
|
|
|
export interface ModalHeaderProps {
|
2022-05-11 17:20:49 -07:00
|
|
|
onClose?: OnCloseCallback;
|
2022-05-10 06:52:06 -07:00
|
|
|
title: string;
|
2022-05-31 17:41:24 -07:00
|
|
|
icon?: string;
|
2022-05-10 06:52:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Header used internally for Modal component
|
|
|
|
*
|
|
|
|
* @param onClose Close callback
|
|
|
|
* @param title Modal title
|
|
|
|
* @returns ModalHeader component
|
|
|
|
*/
|
2022-05-31 17:41:24 -07:00
|
|
|
export const ModalHeader = ({ onClose, title, icon }: ModalHeaderProps) => {
|
2022-05-10 06:52:06 -07:00
|
|
|
return (
|
2022-05-31 17:41:24 -07:00
|
|
|
<header style={modalHeaderStyle}>
|
|
|
|
<span style={modalTitleStyle}>
|
|
|
|
{icon && <i className={`fas fa-${icon}`} role="img" />}
|
|
|
|
{' '}
|
|
|
|
{title}
|
|
|
|
</span>
|
2022-05-10 06:52:06 -07:00
|
|
|
{onClose && <ModalCloseButton onClose={onClose} />}
|
2022-05-31 17:41:24 -07:00
|
|
|
</header>
|
2022-05-10 06:52:06 -07:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modal component properties
|
|
|
|
*/
|
|
|
|
export interface ModalProps {
|
2022-05-10 08:04:20 -07:00
|
|
|
onClose?: (closeBox?: boolean) => void;
|
|
|
|
isOpen: boolean;
|
|
|
|
title: string;
|
2022-05-29 13:48:51 -07:00
|
|
|
children: ComponentChildren;
|
2022-05-31 17:41:24 -07:00
|
|
|
icon?: string;
|
2022-05-10 06:52:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Very simple modal component, provides a transparent overlay, title bar
|
|
|
|
* with optional close box if onClose is provided. ModalContent and
|
|
|
|
* ModalFooter components are provided for convenience but not required.
|
|
|
|
*
|
|
|
|
* @param isOpen true to show modal
|
|
|
|
* @param title Modal title
|
2022-05-11 17:20:49 -07:00
|
|
|
* @param onClose Close callback
|
2022-05-10 06:52:06 -07:00
|
|
|
* @returns Modal component
|
|
|
|
*/
|
2022-05-29 13:48:51 -07:00
|
|
|
export const Modal = ({
|
2022-05-10 06:52:06 -07:00
|
|
|
isOpen,
|
|
|
|
children,
|
2022-05-11 17:20:49 -07:00
|
|
|
title,
|
|
|
|
...props
|
2022-05-29 13:48:51 -07:00
|
|
|
}: ModalProps) => {
|
2022-05-10 06:52:06 -07:00
|
|
|
return (
|
2022-05-31 17:41:24 -07:00
|
|
|
isOpen ? createPortal((
|
2022-05-10 06:52:06 -07:00
|
|
|
<ModalOverlay>
|
2022-05-31 17:41:24 -07:00
|
|
|
<div style={modalStyle} role="dialog">
|
2022-05-11 17:20:49 -07:00
|
|
|
{title && <ModalHeader title={title} {...props} />}
|
2022-05-10 06:52:06 -07:00
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
</ModalOverlay>
|
2022-05-31 17:41:24 -07:00
|
|
|
), document.body) : null
|
2022-05-10 06:52:06 -07:00
|
|
|
);
|
|
|
|
};
|