feat(ui): Checkbox/Radio 原子件 + 3 处原生控件采用(P2-5)
- checkboxClass/radioClass(accent-cinnabar + 统一焦点环)+ 同名测试 - Checkbox/Radio 原子件:无 label 只渲染受控原生 input 便于就地替换 - 采用:ChainStarter 链类型单选、GeneratorRunner 入库勾选、CharacterCardItem 角色勾选
This commit is contained in:
@@ -5,6 +5,7 @@ import { Play } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/Button";
|
||||
import { Field } from "@/components/ui/Field";
|
||||
import { Radio } from "@/components/ui/Radio";
|
||||
import { SectionHeader } from "@/components/ui/SectionHeader";
|
||||
import { TextInput } from "@/components/ui/TextInput";
|
||||
import { CHAIN_KINDS, type ChainKind } from "@/lib/chain/chain";
|
||||
@@ -58,8 +59,7 @@ export function ChainStarter({ onStart, disabled }: ChainStarterProps) {
|
||||
key={kind.key}
|
||||
className="flex items-start gap-2 text-sm text-ink"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
<Radio
|
||||
name="chain-kind"
|
||||
value={kind.key}
|
||||
checked={chainKey === kind.key}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Checkbox } from "@/components/ui/Checkbox";
|
||||
import { TextInput } from "@/components/ui/TextInput";
|
||||
import type { CharacterCardView } from "@/lib/api/types";
|
||||
|
||||
@@ -25,12 +26,10 @@ export function CharacterCardItem({
|
||||
}`}
|
||||
>
|
||||
<header className="mb-2 flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
<Checkbox
|
||||
checked={selected}
|
||||
onChange={onToggle}
|
||||
aria-label={`选择角色 ${card.name}`}
|
||||
className="size-4 accent-cinnabar"
|
||||
/>
|
||||
{onEdit ? (
|
||||
<TextInput
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useToast } from "@/components/Toast";
|
||||
import { ConflictAdjudication } from "@/components/generation/ConflictAdjudication";
|
||||
import { Button } from "@/components/ui/Button";
|
||||
import { Badge } from "@/components/ui/Badge";
|
||||
import { Checkbox } from "@/components/ui/Checkbox";
|
||||
import { Field } from "@/components/ui/Field";
|
||||
import { StatusNote } from "@/components/ui/StatusNote";
|
||||
import { TextArea } from "@/components/ui/TextArea";
|
||||
@@ -380,8 +381,7 @@ function PreviewRow({
|
||||
return (
|
||||
<li className="flex gap-2 rounded border border-line bg-bg p-3 text-sm">
|
||||
{selectable ? (
|
||||
<input
|
||||
type="checkbox"
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
onChange={() => onToggle(index)}
|
||||
className="mt-1"
|
||||
|
||||
20
apps/web/components/ui/Checkbox.tsx
Normal file
20
apps/web/components/ui/Checkbox.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { InputHTMLAttributes, ReactNode } from "react";
|
||||
|
||||
import { checkboxClass } from "@/lib/ui/variants";
|
||||
|
||||
interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
|
||||
label?: ReactNode;
|
||||
}
|
||||
|
||||
// 勾选框:无 label 时只渲染受控原生 input(供已有 <label>/布局包裹的调用点直接替换);
|
||||
// 有 label 时自带 <label> 包裹,保留原生键盘/读屏语义。
|
||||
export function Checkbox({ label, className, ...props }: CheckboxProps) {
|
||||
const input = <input type="checkbox" className={checkboxClass(className)} {...props} />;
|
||||
if (!label) return input;
|
||||
return (
|
||||
<label className="inline-flex items-center gap-2 text-sm text-ink">
|
||||
{input}
|
||||
<span>{label}</span>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
20
apps/web/components/ui/Radio.tsx
Normal file
20
apps/web/components/ui/Radio.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { InputHTMLAttributes, ReactNode } from "react";
|
||||
|
||||
import { radioClass } from "@/lib/ui/variants";
|
||||
|
||||
interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
|
||||
label?: ReactNode;
|
||||
}
|
||||
|
||||
// 单选钮:无 label 时只渲染受控原生 input(供已有 <label>/布局包裹的调用点直接替换);
|
||||
// 有 label 时自带 <label> 包裹,保留原生键盘/读屏语义。
|
||||
export function Radio({ label, className, ...props }: RadioProps) {
|
||||
const input = <input type="radio" className={radioClass(className)} {...props} />;
|
||||
if (!label) return input;
|
||||
return (
|
||||
<label className="inline-flex items-center gap-2 text-sm text-ink">
|
||||
{input}
|
||||
<span>{label}</span>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user