Skip to content

Commit e5161fd

Browse files
authored
feat(Modal): using dialog element (#359)
* using dialog element * fix ModalLegacy story meta
1 parent b5ab4dc commit e5161fd

9 files changed

Lines changed: 340 additions & 219 deletions

File tree

.storybook/StoryLayout.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ const StoryLayout = ({ children, title, description, source }: Props) => {
8282
className="preview border-base-300 bg-base-200 rounded-b-box rounded-tr-box
8383
flex min-h-[6rem] min-w-[18rem] flex-wrap items-center justify-center gap-2
8484
overflow-x-hidden overflow-y-hidden border bg-cover bg-top p-4"
85-
style={{ backgroundSize: '5px 5px' }}
8685
>
8786
{children}
8887
</div>

package-lock.json

Lines changed: 51 additions & 104 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"generate": "plop"
3636
},
3737
"peerDependencies": {
38-
"daisyui": "^2.51.5",
38+
"daisyui": "^3.0.22",
3939
"react": ">=16",
4040
"react-dom": ">=16",
4141
"tailwindcss": ">=3.2.7"

src/Modal/Modal.stories.tsx

Lines changed: 62 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react'
1+
import React, { useRef, useCallback } from 'react'
22
import { StoryFn as Story, Meta } from '@storybook/react'
33

44
import Modal, { ModalProps } from '.'
@@ -10,117 +10,106 @@ export default {
1010
} as Meta
1111

1212
export const Default: Story<ModalProps> = (args) => {
13-
const [visible, setVisible] = useState<boolean>(false)
14-
15-
const toggleVisible = () => {
16-
setVisible(!visible)
17-
}
18-
13+
const ref = useRef<HTMLDialogElement>(null)
14+
const handleShow = useCallback(() => {
15+
ref.current?.showModal()
16+
}, [ref])
1917
return (
2018
<div className="font-sans">
21-
<Button onClick={toggleVisible}>Open Modal</Button>
22-
<Modal {...args} open={visible}>
23-
<Modal.Header className="font-bold">
24-
Congratulations random Interner user!
25-
</Modal.Header>
26-
19+
<Button onClick={handleShow}>Open Modal</Button>
20+
<Modal {...args} ref={ref}>
21+
<Modal.Header className="font-bold">Hello!</Modal.Header>
2722
<Modal.Body>
28-
You've been selected for a chance to get one year of subscription to
29-
use Wikipedia for free!
23+
Press ESC key or click the button below to close
3024
</Modal.Body>
31-
3225
<Modal.Actions>
33-
<Button onClick={toggleVisible}>Yay!</Button>
26+
<Button>Close</Button>
3427
</Modal.Actions>
3528
</Modal>
3629
</div>
3730
)
3831
}
3932

40-
export const CloseButton: Story<ModalProps> = (args) => {
41-
const [visible, setVisible] = useState<boolean>(false)
42-
43-
const toggleVisible = () => {
44-
setVisible(!visible)
45-
}
33+
export const ClickedOutside: Story<ModalProps> = (args) => {
34+
const ref = useRef<HTMLDialogElement>(null)
35+
const handleShow = useCallback(() => {
36+
ref.current?.showModal()
37+
}, [ref])
38+
return (
39+
<div className="font-sans">
40+
<Button onClick={handleShow}>Open Modal</Button>
41+
<Modal {...args} ref={ref}>
42+
<Modal.Header className="font-bold">Hello!</Modal.Header>
43+
<Modal.Body>Press ESC key or click outside to close</Modal.Body>
44+
</Modal>
45+
</div>
46+
)
47+
}
48+
ClickedOutside.args = {
49+
backdrop: true,
50+
}
4651

52+
export const CloseButton: Story<ModalProps> = (args) => {
53+
const ref = useRef<HTMLDialogElement>(null)
54+
const handleShow = useCallback(() => {
55+
ref.current?.showModal()
56+
}, [ref])
4757
return (
4858
<div className="font-sans">
49-
<Button onClick={toggleVisible}>Open Modal</Button>
50-
<Modal {...args} open={visible}>
59+
<Button onClick={handleShow}>Open Modal</Button>
60+
<Modal {...args} ref={ref}>
5161
<Button
5262
size="sm"
63+
color="ghost"
5364
shape="circle"
5465
className="absolute right-2 top-2"
55-
onClick={toggleVisible}
5666
>
57-
67+
x
5868
</Button>
59-
<Modal.Header className="font-bold">
60-
Congratulations random Interner user!
61-
</Modal.Header>
62-
63-
<Modal.Body>
64-
You've been selected for a chance to get one year of subscription to
65-
use Wikipedia for free!
66-
</Modal.Body>
69+
<Modal.Header className="font-bold">Hello!</Modal.Header>
70+
<Modal.Body>Press ESC key or click on X button to close</Modal.Body>
6771
</Modal>
6872
</div>
6973
)
7074
}
7175

72-
export const ClickedOutside: Story<ModalProps> = (args) => {
73-
const [visible, setVisible] = useState<boolean>(false)
74-
75-
const toggleVisible = () => {
76-
setVisible(!visible)
77-
}
78-
76+
export const CustomWidth: Story<ModalProps> = (args) => {
77+
const ref = useRef<HTMLDialogElement>(null)
78+
const handleShow = useCallback(() => {
79+
ref.current?.showModal()
80+
}, [ref])
7981
return (
8082
<div className="font-sans">
81-
<Button onClick={toggleVisible}>Open Modal</Button>
82-
<Modal {...args} open={visible} onClickBackdrop={toggleVisible}>
83-
<Modal.Header className="font-bold">
84-
Congratulations random Interner user!
85-
</Modal.Header>
86-
83+
<Button onClick={handleShow}>Open Modal</Button>
84+
<Modal {...args} ref={ref}>
85+
<Modal.Header className="font-bold">Hello!</Modal.Header>
8786
<Modal.Body>
88-
You've been selected for a chance to get one year of subscription to
89-
use Wikipedia for free!
87+
Press ESC key or click the button below to close
9088
</Modal.Body>
89+
<Modal.Actions>
90+
<Button>Close</Button>
91+
</Modal.Actions>
9192
</Modal>
9293
</div>
9394
)
9495
}
9596

96-
export const CustomWidth: Story<ModalProps> = (args) => {
97-
const [visible, setVisible] = useState<boolean>(false)
98-
99-
const toggleVisible = () => {
100-
setVisible(!visible)
101-
}
97+
CustomWidth.args = {
98+
className: 'w-11/12 max-w-5xl',
99+
}
102100

101+
export const UseDialogHook: Story<ModalProps> = (args) => {
102+
const { Dialog, handleShow } = Modal.useDialog()
103103
return (
104104
<div className="font-sans">
105-
<Button onClick={toggleVisible}>Open Modal</Button>
106-
<Modal {...args} open={visible}>
107-
<Modal.Header className="font-bold">
108-
Congratulations random Interner user!
109-
</Modal.Header>
110-
111-
<Modal.Body>
112-
You've been selected for a chance to get one year of subscription to
113-
use Wikipedia for free!
114-
</Modal.Body>
115-
105+
<Button onClick={handleShow}>Open Modal</Button>
106+
<Dialog {...args}>
107+
<Modal.Header className="font-bold">Hello!</Modal.Header>
108+
<Modal.Body>This modal works with useDialog hook!</Modal.Body>
116109
<Modal.Actions>
117-
<Button onClick={toggleVisible}>Yay!</Button>
110+
<Button>Close</Button>
118111
</Modal.Actions>
119-
</Modal>
112+
</Dialog>
120113
</div>
121114
)
122115
}
123-
124-
CustomWidth.args = {
125-
className: 'w-11/12 max-w-5xl',
126-
}

src/Modal/Modal.tsx

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { forwardRef } from 'react'
1+
import React, { forwardRef, useCallback, useRef } from 'react'
22
import clsx from 'clsx'
33
import { twMerge } from 'tailwind-merge'
44

@@ -7,25 +7,18 @@ import { IComponentBaseProps } from '../types'
77
import ModalActions from './ModalActions'
88
import ModalBody from './ModalBody'
99
import ModalHeader from './ModalHeader'
10+
import ModalLegacy, { ModalProps as ModalLegacyProps } from './ModalLegacy'
1011

11-
export type ModalProps = React.HTMLAttributes<HTMLDivElement> &
12+
export type ModalProps = React.HTMLAttributes<HTMLDialogElement> &
1213
IComponentBaseProps & {
1314
open?: boolean
1415
responsive?: boolean
15-
onClickBackdrop?: () => void
16+
backdrop?: boolean
1617
}
1718

18-
const Modal = forwardRef<HTMLDivElement, ModalProps>(
19+
const Modal = forwardRef<HTMLDialogElement, ModalProps>(
1920
(
20-
{
21-
children,
22-
open,
23-
responsive,
24-
onClickBackdrop,
25-
dataTheme,
26-
className,
27-
...props
28-
},
21+
{ children, open, responsive, backdrop, dataTheme, className, ...props },
2922
ref
3023
): JSX.Element => {
3124
const containerClasses = twMerge(
@@ -39,39 +32,57 @@ const Modal = forwardRef<HTMLDivElement, ModalProps>(
3932
const bodyClasses = twMerge('modal-box', className)
4033

4134
return (
42-
<div
35+
<dialog
36+
{...props}
4337
aria-label="Modal"
4438
aria-hidden={!open}
39+
open={open}
4540
aria-modal={open}
4641
data-theme={dataTheme}
4742
className={containerClasses}
48-
onClick={(e) => {
49-
e.stopPropagation()
50-
if (e.target === e.currentTarget) {
51-
e.stopPropagation()
52-
if (onClickBackdrop) {
53-
onClickBackdrop()
54-
}
55-
}
56-
}}
43+
ref={ref}
5744
>
58-
<div
59-
{...props}
60-
data-theme={dataTheme}
61-
className={bodyClasses}
62-
ref={ref}
63-
>
45+
<form method="dialog" data-theme={dataTheme} className={bodyClasses}>
6446
{children}
65-
</div>
66-
</div>
47+
</form>
48+
{backdrop && (
49+
<form method="dialog" className="modal-backdrop">
50+
<button>close</button>
51+
</form>
52+
)}
53+
</dialog>
6754
)
6855
}
6956
)
7057

7158
Modal.displayName = 'Modal'
7259

60+
export type DialogProps = Omit<ModalProps, 'ref'>
61+
const useDialog = () => {
62+
const dialogRef = useRef<HTMLDialogElement>(null)
63+
64+
const handleShow = useCallback(() => {
65+
dialogRef.current?.showModal()
66+
}, [dialogRef])
67+
68+
const handleHide = useCallback(() => {
69+
dialogRef.current?.close()
70+
}, [dialogRef])
71+
72+
const Dialog = ({ children, ...props }: DialogProps) => {
73+
return (
74+
<Modal {...props} ref={dialogRef}>
75+
{children}
76+
</Modal>
77+
)
78+
}
79+
Dialog.displayName = 'Dialog'
80+
return { dialogRef, Dialog, handleShow, handleHide }
81+
}
7382
export default Object.assign(Modal, {
7483
Header: ModalHeader,
7584
Body: ModalBody,
7685
Actions: ModalActions,
86+
Legacy: ModalLegacy,
87+
useDialog,
7788
})

0 commit comments

Comments
 (0)