Package Exports
- @reactleaf/react-modal
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@reactleaf/react-modal) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@reactleaf/react-modal
React modal with context and hooks
Concept
This library uses HoC to provide context and container, and provide hooks to open and close modal. Main Concept of this library is providing type-safe method to open specific modals on anywhere of your code.
More details are on below
Installation and Usage
npm install @reactleaf/react-modal
# or
yarn add @reactleaf/react-modalModal Register
At first, you should make a your own modal register. we are using dynamic import, to reduce initial loading size. Any modals registered will be loaded on when modal is called to open, not on initialize sequence.
const register = {
Alert: () => import("./Alert"),
Confirm: () => import("./Confirm"),
};
export default register;Modal Context
Now provide this register to your app. This HoC will provide modalContext to your app, and also modal container, that modals will be rendered. So Simple!
import { withModal } from "@reactleaf/react-modal";
import register from "./modals/register";
function App() {
...
}
export default withModal(register, App);useModal Hook
To open modal, you should use useModal hook. for type-safe of your code, do not use useModal directly.
Don't
If you are non-typescript user, it's okay to use this way.
import { useModal } from "@reactleaf/react-modal";
const { openModal } = useModal();
function openAlert() {
// openModal cannot check types.
openModal({ type: "Alert", props: { title: "Hello" } });
}Recommended Way
With this way, you can check type and props are properly provided.
// useModal.ts
import { createModalHook } from "@reactleaf/react-modal";
import register from "./register";
export const useModal = createModalHook<typeof register>();openModal will check modal type
import { useModal } from './modals/useModal'
const { openModal } = useModal()
function openAlert() {
openModal({ type: 'Confrim', props: { title: 'Hello', message: 'Wow' } })
^^^^
type 'Confrim' is not assignable to type 'Alert' | 'Confirm'
}openModal will check props type for the matching type
import { useModal } from './modals/useModal'
const { openModal } = useModal()
function openAlert() {
openModal({ type: 'Alert', props: { title: 'Hello' } })
^^^^^
property 'message' is missing
}Preload modals
We use dynamic import to load modals when modal opens. It makes code-splitting easier, and initial bundle size smaller.
But sometimes, you may need synchronous imports, for instantly open modals.
It might for mounting animation, or modal has large dependencies to load on open.
Then, you can preload modals before user click the button that opens modal.
This calls import() from your register, to ensure openModal() runs synchronously.
import { createModalPreloader } from "@reactleaf/react-modal";
const preloadModal = createModalPreloader(register);
// when component mounted, load relative modals.
useEffect(() => {
preloadModal("Alert", "Confirm");
}, []);Props
withModal(register, App)
register- your modal registerApp- your Appreturns- Higher ordered App
createModalHook()
const useModal = createModalHook<typeof yourModalRegister>();useModal()
retuns- { openModal, closeAll }
openModal(payload)
open selected typed modal with given props
function openModal(payload: {
type: keyof Register;
props: Props;
overlayOptions?: OverlayOptions;
});Props- Matching Props as type. if type === "Alert", props should beReact.ComponentProps<Alert>OverlayOptions
export interface OverlayOptions {
dim?: boolean; // default: true
closeDelay?: number; // default: 0, as ms. this will make modal close(unmount) delayed. Useful if you want to add closing animation.
closeOnOverlayClick?: boolean; // default: true
preventScroll?: boolean; // default: true, when modal is opened, body scroll is blocked.
}closeAll()
close all opened modals
How to add opening / closing animation?
For animation, modal opening is delayed for a frame. You can add Overlay styles like this.
.modal-overlay {
opacity: 0;
transition: opacity 0.3s;
}
.modal-overlay.visible {
opacity: 1;
}and also in your custom modal, has visible props. See below to know more about visible props.
Be sure that closeDelay option is properly set, if you want to animate on closing.
Detailed implementation is at Slideup Example
.slideup {
transition: transform 500ms;
transform: translateY(100%);
}
.slideup.visible {
transform: translateY(0);
}How to close opened modal?
Modal can only closed by modal itself. see more on below
But there are 2 exceptions.
closeAll()- If you close All Modals, then every opened modals are closed.closeOnOverlayClick: true- if user click outside of modal (may be darken with dim color), top modal is closed.
BasicModalProps
When modal is opened by openModal, 2 more props are injected to your modal.
close(): voidvisible: boolean
So, When implementing modal, you can consider close props like this.
import { BasicModalProps } from "@reactleaf/react-modal";
interface Props extends BasicModalProps {
title: string;
message: string;
}
const Alert = ({
title,
message,
visible,
close, // injected by react-modal
}: Props) => {
return (
<div className={cx("alert", "modal", { visible })}>
<p className="modal-title">{title}</p>
<div className="modal-body">
<p className="message">{message}</p>
</div>
<div className="modal-buttons">
<button onClick={close}>Close</button>
</div>
</div>
);
};Working Examples
See more on Examples