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
+123
View File
@@ -0,0 +1,123 @@
import { useState } from 'react'
import { Dialog } from '#/components/dialog'
import { Icon } from '#/components/icon'
import type { Deck } from './model'
export function DeckSwitcher({
decks,
activeId,
ready,
onSelect,
onCreate,
onDelete,
}: {
decks: Deck[]
activeId: string
ready: boolean
onSelect: (id: string) => void
onCreate: (title: string) => void
onDelete: () => void
}) {
const [creating, setCreating] = useState(false)
const [deleting, setDeleting] = useState(false)
return (
<div className="deck-workspaces">
<div className="deck-actions">
<label htmlFor="deck-selection">デッキプロファイル</label>
<select
id="deck-selection"
value={activeId}
disabled={!ready}
onChange={(event) => {
onSelect(event.target.value)
setCreating(false)
setDeleting(false)
}}
>
{decks.map((deck) => (
<option key={deck.id} value={deck.id}>
{deck.title}
</option>
))}
</select>
<button
className="workspace-create"
aria-label="デッキを作成"
title="デッキを作成"
type="button"
disabled={!ready}
onClick={() => {
setCreating(!creating)
setDeleting(false)
}}
>
<Icon name="plus" />
<span>デッキを作成</span>
</button>
<button
className="workspace-delete"
aria-label="デッキを削除"
title="デッキを削除"
type="button"
disabled={!ready || decks.length <= 1}
onClick={() => {
setDeleting(!deleting)
setCreating(false)
}}
>
<Icon name="trash" />
<span>デッキを削除</span>
</button>
</div>
{creating ? (
<Dialog title="デッキを作成" onClose={() => setCreating(false)}>
<form
className="deck-rename"
onSubmit={(event) => {
event.preventDefault()
const title = String(
new FormData(event.currentTarget).get('title') ?? '',
).trim()
if (!title) return
onCreate(title)
setCreating(false)
}}
>
<label htmlFor="new-deck-title">
新しいデッキ名
<input
id="new-deck-title"
name="title"
maxLength={120}
required
/>
</label>
<button type="submit">作成</button>
<button type="button" onClick={() => setCreating(false)}>
キャンセル
</button>
</form>
</Dialog>
) : null}
{deleting ? (
<Dialog title="デッキを削除" onClose={() => setDeleting(false)}>
<div className="deck-undo">
<p>このデッキとカラムの設定を削除します。</p>
<button
type="button"
onClick={() => {
onDelete()
setDeleting(false)
}}
>
削除する
</button>
<button type="button" onClick={() => setDeleting(false)}>
キャンセル
</button>
</div>
</Dialog>
) : null}
</div>
)
}