apple2js/js/components/Modal.tsx

189 lines
4.1 KiB
TypeScript

import { h, FunctionalComponent } from 'preact';
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
*/
export const ModalOverlay: FunctionalComponent = ({ children }) => {
return (
<div style={modalOverlayStyle}>
{children}
</div>
);
};
/**
* ModalContent provides a styled container for modal content
*
* @returns ModalContent component
*/
export const ModalContent: FunctionalComponent = ({ children }) => {
return (
<div style={modalContentStyle}>
{children}
</div>
);
};
/**
* ModalFooter provides a right-aligned container for modal buttons.
*
* @returns ModalFooter component
*/
export const ModalFooter: FunctionalComponent = ({ children }) => {
return (
<div style={modalFooterStyle}>
{children}
</div>
);
};
/**
* 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>
);
};
/**
* ModalHeader component properties
*/
export interface ModalHeaderProps {
onClose?: (closeBox?: boolean) => void;
title: string;
}
/**
* Header used internally for Modal component
*
* @param onClose Close callback
* @param title Modal title
* @returns ModalHeader component
*/
export const ModalHeader = ({ onClose, title }: ModalHeaderProps) => {
return (
<div style={modalHeaderStyle}>
<span style={modalTitleStyle}>{title}</span>
{onClose && <ModalCloseButton onClose={onClose} />}
</div>
);
};
/**
* Modal component properties
*/
export interface ModalProps {
onClose?: (closeBox?: boolean) => void
isOpen: boolean
title: string
}
/**
* 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
* @onClose Close callback
* @returns Modal component
*/
export const Modal: FunctionalComponent<ModalProps> = ({
isOpen,
title,
children,
onClose
}) => {
return (
isOpen ? (
<ModalOverlay>
<div style={modalStyle}>
{title && <ModalHeader onClose={onClose} title={title} />}
{children}
</div>
</ModalOverlay>
) : null
);
};