feat: unify research in profile-bound decks with TweetDeck-style UI

This commit is contained in:
2026-09-24 15:59:54 +09:00
parent da8f52e605
commit d2cbf4dbd3
90 changed files with 2617 additions and 6934 deletions
+51
View File
@@ -0,0 +1,51 @@
import { useEffect, useId, useRef } from 'react'
import { Icon } from './icon'
export function Dialog({
title,
onClose,
children,
}: {
title: string
onClose: () => void
children: React.ReactNode
}) {
const dialog = useRef<HTMLDialogElement>(null)
const headingId = useId()
useEffect(() => {
const element = dialog.current
const opener =
document.activeElement instanceof HTMLElement
? document.activeElement
: null
element?.showModal()
return () => {
element?.close()
if (opener?.isConnected) opener.focus({ preventScroll: true })
}
}, [])
return (
<dialog
ref={dialog}
className="deck-dialog"
aria-labelledby={headingId}
onCancel={(event) => {
event.preventDefault()
onClose()
}}
>
<div className="dialog-heading">
<h2 id={headingId}>{title}</h2>
<button
type="button"
className="icon-button"
aria-label="閉じる"
onClick={onClose}
>
<Icon name="close" />
</button>
</div>
{children}
</dialog>
)
}