-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from Atnic/feature/modal
feat: modal component
- Loading branch information
Showing
36 changed files
with
1,954 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# @jala-banyu/modal | ||
|
||
A Quick description of the component | ||
|
||
> This is an internal utility, not intended for public usage. | ||
## Installation | ||
|
||
```sh | ||
yarn add @jala-banyu/modal | ||
# or | ||
npm i @jala-banyu/modal | ||
``` | ||
|
||
## Contribution | ||
|
||
Yes please! See the | ||
[contributing guidelines](https://github.com/Atnic/banyu/blob/master/CONTRIBUTING.md) | ||
for details. | ||
|
||
## Licence | ||
|
||
This project is licensed under the terms of the | ||
[MIT license](https://github.com/Atnic/banyu/blob/master/LICENSE). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import * as React from "react"; | ||
import {render} from "@testing-library/react"; | ||
|
||
import {Modal} from "../src"; | ||
|
||
describe("Modal", () => { | ||
it("should render correctly", () => { | ||
// eslint-disable-next-line react/no-children-prop | ||
const wrapper = render( | ||
<Modal> | ||
<span>test</span> | ||
</Modal>, | ||
); | ||
|
||
expect(() => wrapper.unmount()).not.toThrow(); | ||
}); | ||
|
||
it("ref should be forwarded", () => { | ||
const ref = React.createRef<HTMLDivElement>(); | ||
|
||
render( | ||
<Modal ref={ref}> | ||
<span>test</span> | ||
</Modal>, | ||
); | ||
// expect(ref.current).not.toBeNull(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
{ | ||
"name": "@jala-banyu/modal", | ||
"version": "0.0.0", | ||
"description": "Displays a dialog with a custom content that requires attention or provides additional information.", | ||
"keywords": [ | ||
"modal" | ||
], | ||
"author": "Muhammad Amien <[email protected]>", | ||
"homepage": "#", | ||
"license": "MIT", | ||
"main": "src/index.ts", | ||
"sideEffects": false, | ||
"files": [ | ||
"dist" | ||
], | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/Atnic/banyu.git", | ||
"directory": "packages/components/modal" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/Atnic/banyu/issues" | ||
}, | ||
"scripts": { | ||
"build": "tsup src --dts", | ||
"build:fast": "tsup src", | ||
"dev": "yarn build:fast -- --watch", | ||
"clean": "rimraf dist .turbo", | ||
"typecheck": "tsc --noEmit", | ||
"prepack": "clean-package", | ||
"postpack": "clean-package restore" | ||
}, | ||
"peerDependencies": { | ||
"react": ">=18", | ||
"react-dom": ">=18", | ||
"@jala-banyu/theme": ">=1.2.0", | ||
"@jala-banyu/system": ">=1.0.0" | ||
}, | ||
"dependencies": { | ||
"@jala-banyu/use-disclosure": "workspace:*", | ||
"@jala-banyu/use-aria-button": "workspace:*", | ||
"@jala-banyu/framer-transitions": "workspace:*", | ||
"@jala-banyu/shared-utils": "workspace:*", | ||
"@jala-banyu/react-utils": "workspace:*", | ||
"@jala-banyu/shared-icons": "workspace:*", | ||
"@jala-banyu/use-aria-modal-overlay": "workspace:*", | ||
"@react-aria/dialog": "^3.5.7", | ||
"@react-aria/interactions": "^3.19.1", | ||
"@react-aria/overlays": "^3.18.1", | ||
"@react-aria/utils": "^3.21.1", | ||
"@react-stately/overlays": "^3.6.3", | ||
"@react-aria/focus": "^3.14.3", | ||
"@react-types/overlays": "^3.8.3", | ||
"react-remove-scroll": "^2.5.6" | ||
}, | ||
"devDependencies": { | ||
"@jala-banyu/theme": "workspace:*", | ||
"@jala-banyu/system": "workspace:*", | ||
"@jala-banyu/input": "workspace:*", | ||
"@jala-banyu/checkbox": "workspace:*", | ||
"@jala-banyu/button": "workspace:*", | ||
"@jala-banyu/link": "workspace:*", | ||
"react-lorem-component": "0.13.0", | ||
"framer-motion": "^10.16.4", | ||
"clean-package": "2.2.0", | ||
"react": "^18.0.0", | ||
"react-dom": "^18.0.0" | ||
}, | ||
"clean-package": "../../../clean-package.config.json" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import Modal from "./modal"; | ||
import ModalContent from "./modal-content"; | ||
import ModalHeader from "./modal-header"; | ||
import ModalBody from "./modal-body"; | ||
import ModalFooter from "./modal-footer"; | ||
|
||
// export types | ||
export type {ModalProps} from "./modal"; | ||
export type {ModalContentProps} from "./modal-content"; | ||
export type {ModalHeaderProps} from "./modal-header"; | ||
export type {ModalBodyProps} from "./modal-body"; | ||
export type {ModalFooterProps} from "./modal-footer"; | ||
export type {UseDisclosureProps} from "@jala-banyu/use-disclosure"; | ||
|
||
// export hooks | ||
export {useModal} from "./use-modal"; | ||
export {useDisclosure} from "@jala-banyu/use-disclosure"; | ||
|
||
// export context | ||
export {ModalProvider, useModalContext} from "./modal-context"; | ||
|
||
// export components | ||
export {Modal, ModalContent, ModalHeader, ModalBody, ModalFooter}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import {useEffect} from "react"; | ||
import {forwardRef, HTMLBanyuProps} from "@jala-banyu/system"; | ||
import {useDOMRef} from "@jala-banyu/react-utils"; | ||
import {clsx} from "@jala-banyu/shared-utils"; | ||
|
||
import {useModalContext} from "./modal-context"; | ||
|
||
export interface ModalBodyProps extends HTMLBanyuProps<"div"> {} | ||
|
||
const ModalBody = forwardRef<"div", ModalBodyProps>((props, ref) => { | ||
const {as, children, className, ...otherProps} = props; | ||
|
||
const {slots, classNames, bodyId, setBodyMounted} = useModalContext(); | ||
|
||
const domRef = useDOMRef(ref); | ||
|
||
const Component = as || "div"; | ||
|
||
/** | ||
* Notify us if this component was rendered or used, | ||
* so we can append `aria-labelledby` automatically | ||
*/ | ||
useEffect(() => { | ||
setBodyMounted(true); | ||
|
||
return () => setBodyMounted(false); | ||
}, [setBodyMounted]); | ||
|
||
return ( | ||
<Component | ||
ref={domRef} | ||
className={slots.body({class: clsx(classNames?.body, className)})} | ||
id={bodyId} | ||
{...otherProps} | ||
> | ||
{children} | ||
</Component> | ||
); | ||
}); | ||
|
||
ModalBody.displayName = "Banyu.ModalBody"; | ||
|
||
export default ModalBody; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import type {AriaDialogProps} from "@react-aria/dialog"; | ||
import type {HTMLMotionProps} from "framer-motion"; | ||
|
||
import {cloneElement, isValidElement, ReactNode, useMemo} from "react"; | ||
import {forwardRef} from "@jala-banyu/system"; | ||
import {DismissButton} from "@react-aria/overlays"; | ||
import {TRANSITION_VARIANTS} from "@jala-banyu/framer-transitions"; | ||
import {CloseIcon} from "@jala-banyu/shared-icons"; | ||
import {RemoveScroll} from "react-remove-scroll"; | ||
import {motion} from "framer-motion"; | ||
import {useDialog} from "@react-aria/dialog"; | ||
import {mergeProps} from "@react-aria/utils"; | ||
import {HTMLBanyuProps} from "@jala-banyu/system"; | ||
|
||
import {useModalContext} from "./modal-context"; | ||
import {scaleInOut} from "./modal-transition"; | ||
|
||
type KeysToOmit = "children" | "role"; | ||
|
||
export interface ModalContentProps extends AriaDialogProps, HTMLBanyuProps<"div", KeysToOmit> { | ||
children: ReactNode | ((onClose: () => void) => ReactNode); | ||
} | ||
|
||
const ModalContent = forwardRef<"div", ModalContentProps, KeysToOmit>((props, _) => { | ||
const {as, children, role = "dialog", ...otherProps} = props; | ||
|
||
const { | ||
Component: DialogComponent, | ||
domRef, | ||
slots, | ||
isOpen, | ||
classNames, | ||
motionProps, | ||
backdrop, | ||
closeButton, | ||
hideCloseButton, | ||
disableAnimation, | ||
shouldBlockScroll, | ||
getDialogProps, | ||
getBackdropProps, | ||
getCloseButtonProps, | ||
onClose, | ||
} = useModalContext(); | ||
|
||
const Component = as || DialogComponent || "div"; | ||
|
||
const {dialogProps} = useDialog( | ||
{ | ||
role, | ||
}, | ||
domRef, | ||
); | ||
|
||
const closeButtonContent = isValidElement(closeButton) ? ( | ||
cloneElement(closeButton, getCloseButtonProps()) | ||
) : ( | ||
<button {...getCloseButtonProps()}> | ||
<CloseIcon /> | ||
</button> | ||
); | ||
|
||
const content = ( | ||
<Component {...getDialogProps(mergeProps(dialogProps, otherProps))}> | ||
<DismissButton onDismiss={onClose} /> | ||
{!hideCloseButton && closeButtonContent} | ||
{typeof children === "function" ? children(onClose) : children} | ||
<DismissButton onDismiss={onClose} /> | ||
</Component> | ||
); | ||
|
||
const backdropContent = useMemo(() => { | ||
if (backdrop === "transparent") { | ||
return null; | ||
} | ||
|
||
if (disableAnimation) { | ||
return <div {...getBackdropProps()} />; | ||
} | ||
|
||
return ( | ||
<motion.div | ||
animate="enter" | ||
exit="exit" | ||
initial="exit" | ||
variants={TRANSITION_VARIANTS.fade} | ||
{...(getBackdropProps() as HTMLMotionProps<"div">)} | ||
/> | ||
); | ||
}, [backdrop, disableAnimation, getBackdropProps]); | ||
|
||
return ( | ||
<div tabIndex={-1}> | ||
{backdropContent} | ||
<RemoveScroll forwardProps enabled={shouldBlockScroll && isOpen} removeScrollBar={false}> | ||
{disableAnimation ? ( | ||
<div className={slots.wrapper({class: classNames?.wrapper})}>{content}</div> | ||
) : ( | ||
<motion.div | ||
animate="enter" | ||
className={slots.wrapper({class: classNames?.wrapper})} | ||
exit="exit" | ||
initial="exit" | ||
variants={scaleInOut} | ||
{...motionProps} | ||
> | ||
{content} | ||
</motion.div> | ||
)} | ||
</RemoveScroll> | ||
</div> | ||
); | ||
}); | ||
|
||
ModalContent.displayName = "Banyu.ModalContent"; | ||
|
||
export default ModalContent; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import {createContext} from "@jala-banyu/react-utils"; | ||
|
||
import {UseModalReturn} from "./use-modal"; | ||
|
||
export const [ModalProvider, useModalContext] = createContext<UseModalReturn>({ | ||
name: "ModalContext", | ||
errorMessage: | ||
"useModalContext: `context` is undefined. Seems you forgot to wrap all popover components within `<Modal />`", | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import {forwardRef, HTMLBanyuProps} from "@jala-banyu/system"; | ||
import {useDOMRef} from "@jala-banyu/react-utils"; | ||
import {clsx} from "@jala-banyu/shared-utils"; | ||
|
||
import {useModalContext} from "./modal-context"; | ||
|
||
export interface ModalFooterProps extends HTMLBanyuProps<"footer"> {} | ||
|
||
const ModalFooter = forwardRef<"footer", ModalFooterProps>((props, ref) => { | ||
const {as, children, className, ...otherProps} = props; | ||
|
||
const {slots, classNames} = useModalContext(); | ||
|
||
const domRef = useDOMRef(ref); | ||
|
||
const Component = as || "footer"; | ||
|
||
return ( | ||
<Component | ||
ref={domRef} | ||
className={slots.footer({class: clsx(classNames?.footer, className)})} | ||
{...otherProps} | ||
> | ||
{children} | ||
</Component> | ||
); | ||
}); | ||
|
||
ModalFooter.displayName = "Banyu.ModalFooter"; | ||
|
||
export default ModalFooter; |
Oops, something went wrong.