项目作者: weahead

项目描述 :
A fully customizable react modal with hook primitives
高级语言: TypeScript
项目地址: git://github.com/weahead/react-customizable-modal.git
创建时间: 2019-09-06T22:24:41Z
项目社区:https://github.com/weahead/react-customizable-modal

开源协议:MIT License

下载


react-customizable-modal

A fully customizable and accessable react modal with hook primitives.

Motivation

There are several modal implementations in react, but none of them offered the flexibility to have total control of the components being rendered so that you could use whichever animation library you want.

Features

  • Easy out of the box Modal component.
  • Exposes the underlying hooks to make it possible to compose them as you see fit.

Demos

Default builtin Modal
Animated modal with react-spring
Swipe up from below

Code Examples

Basic Modal

  1. import { Modal } from "@weahead/react-customizable-modal";
  2. function App() {
  3. const [isOpen, setIsOpen] = useState(false);
  4. return (
  5. <div>
  6. <button
  7. onClick={() => {
  8. setIsOpen(true);
  9. }}
  10. >
  11. Open modal
  12. </button>
  13. <Modal
  14. isOpen={isOpen}
  15. onEscape={() => {
  16. setIsOpen(false);
  17. }}
  18. onOverlayClick={() => {
  19. setIsOpen(false);
  20. }}
  21. >
  22. <button
  23. onClick={() => {
  24. setIsOpen(false);
  25. }}
  26. >
  27. Close
  28. </button>
  29. </Modal>
  30. </div>
  31. );
  32. }

Custom Modal with custom overlay

CustomModal.jsx

  1. import React from "react";
  2. import {
  3. useTrapFocus,
  4. useBodyScrollLock,
  5. useCloseOnEsc,
  6. ModalPortal
  7. } from "react-customizable-modal";
  8. function Overlay({ children }) {
  9. return (
  10. <div
  11. style={{
  12. display: "flex",
  13. justifyContent: "center",
  14. alignItems: "center",
  15. position: "fixed",
  16. top: 0,
  17. bottom: 0,
  18. left: 0,
  19. right: 0
  20. }}
  21. >
  22. {children}
  23. </div>
  24. );
  25. }
  26. export default function CustomModal({ isOpen, onClose, children }) {
  27. useBodyScrollLock();
  28. useCloseOnEsc(onClose);
  29. const modalRef = useTrapFocus();
  30. return (
  31. isOpen && (
  32. <ModalPortal id={`customModal`}>
  33. <Overlay>
  34. <div
  35. ref={modalRef}
  36. style={{
  37. width: 500,
  38. height: 400,
  39. backgroundColor: "#fff",
  40. padding: 20,
  41. position: "absolute"
  42. }}
  43. >
  44. <button onClick={onClose}>Close modal</button>
  45. {children}
  46. </div>
  47. </Overlay>
  48. </ModalPortal>
  49. )
  50. );
  51. }

App.jsx

  1. import CustomModal from "CustomModal.jsx";
  2. function App() {
  3. const [isOpen, setIsOpen] = useState(false);
  4. return (
  5. <div>
  6. <button
  7. onClick={() => {
  8. setIsOpen(true);
  9. }}
  10. >
  11. Open modal
  12. </button>
  13. <CustomModal
  14. isOpen={isOpen}
  15. onClose={() => {
  16. setIsOpen(false);
  17. }}
  18. >
  19. This is a custom modal
  20. </CustomModal>
  21. </div>
  22. );
  23. }

Installation

  1. npm i @weahead/react-customizable-modal
  2. OR
  3. yarn add @weahead/react-customizable-modal

API Reference

Components

ModalPortal

a simple wrapper around React.createPortal function, but it also removes the div from the DOM on unmount

Props
Prop Required Description
id yes this will give the DOMnode that is created to hold the modal content an id if there already is a DOMnode with that id it will reuse that for the new modal
children yes This is the content of the portal, usually the entire modal, including the overlay

Modal

A basic Modal component ready for use, if you dont want to implement your own. it uses only the hooks and components from this package

Props
Prop Required Description
id yes this will give the DOMnode that is created to hold the modal content an id if there already is a DOMnode with that id it will reuse that for the new modal
isOpen yes this will be used to create the portal for the modal and mount the modal
role no for a11y purposes we can set the role for our modal, it defaults to dialog
ariaHideApp no sets a aria hide on the div with id of ‘root’
shouldFocusAfterRender no defaults to true and sets the focus on the modal div
onOverlayClick no lets you pass in a function that is triggered when clicking on the overlay, you might want to pass a function that sets the isOpen prop to false IE closing the modal
shouldReturnFocusAfterClose no defaults to true, will return focus to last focus element before the modal was opened
onEscape no lets you pass a function to that is triggered if you press the ESC key while the modal is active, often you want to close the modal
children yes the content of the modal

Hooks

useTrapFocus

import {useTrapFocus} from '@weahead/react-customizable-modal'

used to trap focus inside of the modal component

  1. const modalRef = useTrapFocus(options);
  2. // returns a ref that needs to be given to the element that you want to trap focus within
  3. <div ref={modalRef}>focus will be trapped in this div</div>;
option required description
focusOnRender no defaults to true, will focus the modal container
returnFocus no defaults to true, will return focus to last focused element when the component that uses this hook unmounts

useAriaHide

import {useAriaHide} from '@weahead/react-customizable-modal'

sets the aria-hidden attribute on the element with the id passed in and removes it when the component that uses this hook is unmounted

  1. useAriaHide(id); //often `id` would be 'root'
argument required description
id yes add the aria-hide attribute to the element with the id

useBodyScrollLock

import {useBodyScrollLock} from '@weahead/react-customizable-modal'

this is taken from usehooks all credit goes to them.
it locks the body from scrolling while the component that uses this hook is rendered.

  1. useBodyScrollLock();

useHandleKeyPress

import {useHandleKeyPress} from '@weahead/react-customizable-modal'

this hook is used by the useCloseOnEsc hook and other internal components
it lets you provide a function that is executed on every keypress while the component that uses this hook is mounted

  1. useHandleKeyPress(e => {
  2. console.log(e.keyCode);
  3. });
argument required description
callback yes gets called with every keypress

useCloseOnEsc

import {useCloseOnEsc} from '@weahead/react-customizable-modal'

this hook is used by the useHandleKeyPress hook but only triggers the callback if the escape key is pressed

  1. useCloseOnEsc(() => {
  2. console.log("ESC key was pressed");
  3. });
argument required description
callback yes gets called when escape is pressed

https://github.com/reactjs/react-modal
https://github.com/davidtheclark/tabbable/blob/master/index.js
https://github.com/davidtheclark/focus-trap#focustrap--createfocustrapelement-createoptions