diff --git a/package.json b/package.json index 4d319413..d3562b7f 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "tailwindcss": "yarn:@tailwindcss/postcss7-compat@^2.1.2" }, "dependencies": { + "@headlessui/react": "^1.4.0", "clsx": "^1.1.1", "goober": "^2.0.39", "prop-types": "^15.7.2" diff --git a/src/base-components/headlessui-dialog-base.js b/src/base-components/headlessui-dialog-base.js new file mode 100644 index 00000000..86842001 --- /dev/null +++ b/src/base-components/headlessui-dialog-base.js @@ -0,0 +1,86 @@ +import React, { useRef } from 'react' +import PropTypes from 'prop-types' +import clsx from 'clsx' + +import { Dialog } from '@headlessui/react' + +import { useComponentIsActive } from '../hooks' + + +const baseClasses = Object.freeze({ + modal: 'fixed z-20 max-h-full max-w-full top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2', + overlay: 'absolute z-10 inset-0 w-full h-full bg-black bg-opacity-50 transition-opacity duration-200', +}) + +const ChildWithProps = ({ children, ...props }) => (React.Children.map(children, (child) => { + if (React.isValidElement(child)) { + return React.cloneElement(child, { ...props }) + } + return child +})) +ChildWithProps.propTypes = { children: PropTypes.node.isRequired } + +const HeadlessUIDialog = ({ children, classes, modal, button, open, onClick, onClose }) => { + const ref = useRef(null) + const { ref: activeRef, componentIsActive, setComponentIsActive } = useComponentIsActive() + const controlledOpen = open || open === false || open === '' + let _open = componentIsActive + + if (controlledOpen) { + _open = open + } + + const handleButtonClick = () => { + if (!controlledOpen) { + setComponentIsActive((state) => !state) + } + onClick() + } + + const handleClose = () => { + if (!controlledOpen) { + return + } + onClose() + } + + const unControlledProps = controlledOpen ? {} : { onClick: handleButtonClick } + + return ( + <> +
+ {button && + {onClick && {button}} + {!onClick && button} + } + + +
+ {children} +
+
+
+ {_open && modal &&
} + + ) +} + +HeadlessUIDialog.propTypes = { + children: PropTypes.node.isRequired, + classes: PropTypes.object, + modal: PropTypes.bool, + button: PropTypes.node, + open: PropTypes.bool, + onClick: PropTypes.func, + onClose: PropTypes.func, +} +HeadlessUIDialog.defaultProps = { + classes: { root: '', button: '', modal: '', dialog: '', overlay: '' }, + modal: false, + button: null, + open: undefined, + onClick: () => {}, + onClose: () => {}, +} + +export default HeadlessUIDialog diff --git a/stories/dialog.stories.js b/stories/dialog.stories.js index f82f0d7e..97f3860e 100644 --- a/stories/dialog.stories.js +++ b/stories/dialog.stories.js @@ -1,6 +1,7 @@ import React from 'react' import { DialogBase } from '../src/base-components' +import HeadlessUIDialog from '../src/base-components/headlessui-dialog-base' export default { @@ -25,39 +26,76 @@ export default { export const Base = () => { const button = () return ( - -

- I am just a simple dialog. - More improvements needed to make my usage more flexible. -

-
+ <> + +

+ I am just a simple dialog. + More improvements needed to make my usage more flexible. +

+
+ +
+

Headless Dialog example:

+ +

+ I am just a simple dialog. + More improvements needed to make my usage more flexible. +

+
+
+ ) } export const BaseModal = () => { const button = () return ( - -

- I am just a simple modal. - More improvements needed to make my usage more flexible. -

-
+ <> + +

+ I am just a simple modal. + More improvements needed to make my usage more flexible. +

+
+ +
+

Headless Dialog example:

+ +

+ I am just a simple dialog. + More improvements needed to make my usage more flexible. +

+
+
+ ) } export const CustomDialog = () => { - const button = () + const button = () const customClasses = { - button: 'w-32 p-4 rounded-md cursor-pointer bg-blue-50 hover:bg-blue-100', dialog: 'w-96 p-4 shadow-md mt-1 bg-blue-200', } return ( - -

- I am just a simple dialog. - More improvements needed to make my usage more flexible. -

-
+ <> + +

+ I am just a simple dialog. + More improvements needed to make my usage more flexible. +

+
+ +
+

Headless Dialog example:

+ +

+ I am just a simple dialog. + More improvements needed to make my usage more flexible. +

+
+
+ ) } diff --git a/tailwind.config.js b/tailwind.config.js index b61c36c3..021691db 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1033,12 +1033,12 @@ module.exports = { plugins: [ function({ addUtilities, theme }) { - let newUtilities = {}; - const boxShadowPrefix = '0 0 0 1px'; - const colors = theme('colors'); + let newUtilities = {} + const boxShadowPrefix = '0 0 0 1px' + const colors = theme('colors') Object.keys(colors).forEach(color => { - const colorData = colors[color]; + const colorData = colors[color] if (typeof colorData === 'string') { newUtilities[`.shadow-${color}`] = { boxShadow: `${boxShadowPrefix} ${colorData}`, @@ -1049,12 +1049,12 @@ module.exports = { newUtilities[`.shadow-${color}-${colorVariation}`] = { boxShadow: `${boxShadowPrefix} ${colorData[colorVariation]}`, } - }); + }) } - }); + }) addUtilities(newUtilities, { - variants: ['focus', 'hover', 'active'] - }); - } + variants: ['focus', 'hover', 'active'], + }) + }, ], } diff --git a/yarn.lock b/yarn.lock index d5ce61df..eb97effb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1249,6 +1249,11 @@ dependencies: purgecss "^3.1.3" +"@headlessui/react@^1.4.0": + version "1.4.0" + resolved "https://registry.npmjs.org/@headlessui/react/-/react-1.4.0.tgz#c6d424d8ab10ac925e4423d7f3cbab02c30d736a" + integrity sha512-C+FmBVF6YGvqcEI5fa2dfVbEaXr2RGR6Kw1E5HXIISIZEfsrH/yuCgsjWw5nlRF9vbCxmQ/EKs64GAdKeb8gCw== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"