Skip to content

Commit 7d36bc8

Browse files
Merge pull request #47 from sparcs-kaist/feat/PostWrite
TextEditorLinkDialog
2 parents 21c81a8 + 10711a4 commit 7d36bc8

File tree

3 files changed

+184
-0
lines changed

3 files changed

+184
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
'use client'
2+
3+
import { useState, useRef, useCallback } from 'react'
4+
5+
interface Props {
6+
onSubmit: (url: string, title: string, isBookmark: boolean) => void
7+
}
8+
9+
export default function TextEditorLinkDialog({ onSubmit }: Props) {
10+
const [shown, setShown] = useState(false)
11+
const [url, setUrl] = useState('')
12+
const [title, setTitle] = useState('')
13+
const dialogRef = useRef<HTMLDivElement>(null)
14+
15+
const open = useCallback((defaultTitle = '') => {
16+
setUrl('')
17+
setTitle(defaultTitle)
18+
setShown(true)
19+
}, [])
20+
21+
const close = useCallback(() => {
22+
setShown(false)
23+
}, [])
24+
25+
const handleSubmit = (isBookmark: boolean) => {
26+
if (!/^https?:\/\//.test(url)) {
27+
alert('URL은 http:// 또는 https:// 로 시작해야 합니다.')
28+
return
29+
}
30+
onSubmit(url, title.trim() || url, isBookmark)
31+
close()
32+
}
33+
34+
return (
35+
<>
36+
<div
37+
ref={dialogRef}
38+
className={`fixed inset-0 z-50 flex items-center justify-center transition-opacity duration-300 ${
39+
shown ? 'opacity-100' : 'opacity-0 pointer-events-none'
40+
}`}
41+
>
42+
<div
43+
className="absolute inset-0 bg-black bg-opacity-40"
44+
onClick={close}
45+
/>
46+
<section className="bg-white rounded-lg shadow-lg max-w-md w-full z-10">
47+
<header className="bg-primary-500 text-white px-4 py-3 rounded-t-lg flex justify-between items-center">
48+
<h2 className="text-lg font-semibold">링크 추가하기</h2>
49+
<button onClick={close} className="text-white">
50+
<span className="material-icons">close</span>
51+
</button>
52+
</header>
53+
<div className="px-6 py-4">
54+
<input
55+
type="text"
56+
placeholder="링크 URL"
57+
value={url}
58+
onChange={e => setUrl(e.target.value)}
59+
className="w-full mb-3 px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-primary-400"
60+
/>
61+
<input
62+
type="text"
63+
placeholder="링크 텍스트"
64+
value={title}
65+
onChange={e => setTitle(e.target.value)}
66+
className="w-full mb-4 px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-primary-400"
67+
/>
68+
<div className="flex justify-end space-x-2">
69+
<button
70+
onClick={() => handleSubmit(false)}
71+
className="px-4 py-2 rounded bg-gray-200 hover:bg-gray-300 text-gray-800"
72+
>
73+
링크 추가
74+
</button>
75+
<button
76+
onClick={() => handleSubmit(true)}
77+
className="px-4 py-2 rounded bg-primary-500 hover:bg-primary-600 text-white"
78+
>
79+
북마크 추가
80+
</button>
81+
</div>
82+
</div>
83+
</section>
84+
</div>
85+
</>
86+
)
87+
}
88+
89+
// Dialog open trigger 사용 예:
90+
// const dialogRef = useRef<InstanceType<typeof TextEditorLinkDialog>>(null)
91+
// dialogRef.current?.open('기본 제목')
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
'use client'
2+
3+
import { useState, useRef, useCallback } from 'react'
4+
5+
interface Props {
6+
onSubmit: (url: string, title: string, isBookmark: boolean) => void
7+
}
8+
9+
export default function TextEditorLinkDialog({ onSubmit }: Props) {
10+
const [shown, setShown] = useState(false)
11+
const [url, setUrl] = useState('')
12+
const [title, setTitle] = useState('')
13+
const dialogRef = useRef<HTMLDivElement>(null)
14+
15+
const open = useCallback((defaultTitle = '') => {
16+
setUrl('')
17+
setTitle(defaultTitle)
18+
setShown(true)
19+
}, [])
20+
21+
const close = useCallback(() => {
22+
setShown(false)
23+
}, [])
24+
25+
const handleSubmit = (isBookmark: boolean) => {
26+
if (!/^https?:\/\//.test(url)) {
27+
alert('URL은 http:// 또는 https:// 로 시작해야 합니다.')
28+
return
29+
}
30+
onSubmit(url, title.trim() || url, isBookmark)
31+
close()
32+
}
33+
34+
return (
35+
<>
36+
<div
37+
ref={dialogRef}
38+
className={`fixed inset-0 z-50 flex items-center justify-center transition-opacity duration-300 ${
39+
shown ? 'opacity-100' : 'opacity-0 pointer-events-none'
40+
}`}
41+
>
42+
<div
43+
className="absolute inset-0 bg-black bg-opacity-40"
44+
onClick={close}
45+
/>
46+
<section className="bg-white rounded-lg shadow-lg max-w-md w-full z-10">
47+
<header className="bg-primary-500 text-white px-4 py-3 rounded-t-lg flex justify-between items-center">
48+
<h2 className="text-lg font-semibold">링크 추가하기</h2>
49+
<button onClick={close} className="text-white">
50+
<span className="material-icons">close</span>
51+
</button>
52+
</header>
53+
<div className="px-6 py-4">
54+
<input
55+
type="text"
56+
placeholder="링크 URL"
57+
value={url}
58+
onChange={e => setUrl(e.target.value)}
59+
className="w-full mb-3 px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-primary-400"
60+
/>
61+
<input
62+
type="text"
63+
placeholder="링크 텍스트"
64+
value={title}
65+
onChange={e => setTitle(e.target.value)}
66+
className="w-full mb-4 px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-primary-400"
67+
/>
68+
<div className="flex justify-end space-x-2">
69+
<button
70+
onClick={() => handleSubmit(false)}
71+
className="px-4 py-2 rounded bg-gray-200 hover:bg-gray-300 text-gray-800"
72+
>
73+
링크 추가
74+
</button>
75+
<button
76+
onClick={() => handleSubmit(true)}
77+
className="px-4 py-2 rounded bg-primary-500 hover:bg-primary-600 text-white"
78+
>
79+
북마크 추가
80+
</button>
81+
</div>
82+
</div>
83+
</section>
84+
</div>
85+
</>
86+
)
87+
}
88+
89+
// Dialog open trigger 사용 예:
90+
// const dialogRef = useRef<InstanceType<typeof TextEditorLinkDialog>>(null)
91+
// dialogRef.current?.open('기본 제목')

src/app/write/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
2+
13
export default function Write() {
24
return (
35
<div>

0 commit comments

Comments
 (0)