diff --git a/.changeset/perfect-plums-pretend.md b/.changeset/perfect-plums-pretend.md new file mode 100644 index 0000000..b690cc5 --- /dev/null +++ b/.changeset/perfect-plums-pretend.md @@ -0,0 +1,27 @@ +--- +"@wethegit/react-modal": major +--- + +## Breaking changes + +- **Removed** `appendToBody` prop. + - The `appendToBody` prop has been removed. This prop was previously used to determine whether the modal should be appended to the body element. + +## New Features + +- **Added** `renderTo` prop. + - Introduced the `renderTo` prop, which accepts an HTMLElement where the modal will be appended. This provides greater flexibilty, allowing users to specify any element to render the modal, including the body. This change enhances the customization options for the modal rendering. + +## Migration + +- **Before** + + ```javascript + + ``` + +- **After** + + ```javascript + + ``` diff --git a/README.md b/README.md index 0e9ef2f..a4001c7 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Custom transition, focus management and hash-based state management. Use your favorite animation library, [@wethegit/react-hooks](https://wethegit.github.io/react-hooks/use-animate-presence) provides a simple one for these cases. ```jsx +import { useRef } from 'react' import { useAnimatePresence } from '@wethegit/react-hooks' import { Modal, @@ -114,6 +115,7 @@ import { function MyModal() { const triggerButton = useRef(null) + const modalRootRef = useRef(null) const { isOpen, toggle } = useModal({ // `triggerRef` allows the focus to shift to whatever triggered the modal @@ -132,9 +134,12 @@ function MyModal() { +
- {render && ( - @@ -158,7 +163,7 @@ function MyModal() { | prop | type | default value | description | | -------------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| appendToBody | Boolean | true | Optional. Whether to append the Modal markup to the HTML `` element, or to leave it where it exists within your component structure. Setting this to `false` can be useful for "local" dialog boxes. | +| renderTo | HTMLElement | null | If a valid HTMLElement is provided, the modal will be appended to that element. Otherwise will be rendered in place. | | className | String | null | | | children | ReactNode | null | | diff --git a/src/lib/components/modal/modal.tsx b/src/lib/components/modal/modal.tsx index 906460e..1d978c5 100644 --- a/src/lib/components/modal/modal.tsx +++ b/src/lib/components/modal/modal.tsx @@ -1,34 +1,27 @@ -import ReactDOM from "react-dom" -import { usePreventScroll } from "@wethegit/react-hooks" +"use client" +import ReactDOM from "react-dom" import { ModalInner } from "../modal-inner" import type { ModalInnerProps } from "../modal-inner" import { classnames } from "../../../utils/classnames" import styles from "./modal.module.scss" - export interface ModalProps extends ModalInnerProps { /** - * If true, the modal will be appended to the body instead of being rendered in place. - * @defaultValue true - */ - appendToBody?: boolean + * The modal will be appended to the passed element instead of being rendered in place + * @defaultValue defaults inPlace + **/ + renderTo: HTMLElement } -export function Modal({ appendToBody, className, ...props }: ModalProps) { - usePreventScroll(appendToBody) +export function Modal({ renderTo, className, ...props }: ModalProps) { + const classes = classnames([styles.ModalFixed, className]) - const classes = classnames([ - appendToBody ? styles.ModalFixed : styles.ModalAbsolute, - className, - ]) + const modalContent = - if (appendToBody) { - return ReactDOM.createPortal( - , - document.body - ) - } else { - return + if (renderTo) { + return ReactDOM.createPortal(modalContent, renderTo) } + + return modalContent } diff --git a/src/main.module.css b/src/main.module.css index f6d1eef..4651be0 100644 --- a/src/main.module.css +++ b/src/main.module.css @@ -31,3 +31,7 @@ --ease: cubic-bezier(0.77, 0, 0.1, 1.39); --scale: 1; } + +.CustomModalAbsolute { + position: absolute; +} diff --git a/src/main.tsx b/src/main.tsx index f628efc..c71a5b2 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from "react" +import React, { useRef, useEffect } from "react" import { createRoot } from "react-dom/client" import { Modal, ModalContent, useModal, ModalBackdrop } from "./lib" @@ -8,6 +8,7 @@ import styles from "./main.module.css" function CustomModal() { const triggerButton = useRef(null) + const modalRootRef = useRef(null) const { isOpen, toggle } = useModal({ triggerRef: triggerButton, }) @@ -16,11 +17,45 @@ function CustomModal() { <> {/* `triggerRef` allows the focus to shift to whatever triggered the modal, on close. */} +
- {isOpen && ( - + {isOpen && modalRootRef.current && ( + + + + +

Voluptate Lorem ut minim excepteur sit fugiat anim magna aliquip.

+
+
+ )} + + ) +} + +function CustomModalOnBody() { + const triggerButton = useRef(null) + const bodyRef = useRef(null) + const { isOpen, toggle } = useModal({ + triggerRef: triggerButton, + }) + + useEffect(() => { + bodyRef.current = document.body + }, []) + + return ( + <> + {/* `triggerRef` allows the focus to shift to whatever triggered the modal, on close. */} + + + {isOpen && bodyRef.current && ( + +
- {isOpen && ( - + {isOpen && modalRootRef.current && ( +