205 lines
5.5 KiB
TypeScript
205 lines
5.5 KiB
TypeScript
import type { Meta, StoryObj } from "@storybook/react";
|
|
import { useState } from "react";
|
|
import DragFill from "./drag-fill";
|
|
import type { DragFillProps } from "./drag-fill.types";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const meta: Meta<typeof DragFill> = {
|
|
title: "Components/Quiz/DragFill",
|
|
component: DragFill,
|
|
tags: ["autodocs"],
|
|
argTypes: {
|
|
options: {
|
|
control: { type: "object" },
|
|
description: "可拖拽的选项列表",
|
|
},
|
|
blanks: {
|
|
control: { type: "object" },
|
|
description: "空白区域列表",
|
|
},
|
|
value: {
|
|
control: { type: "object" },
|
|
description: "当前填充结果(受控模式)",
|
|
},
|
|
defaultValue: {
|
|
control: { type: "object" },
|
|
description: "默认填充结果",
|
|
},
|
|
disabled: {
|
|
control: { type: "boolean" },
|
|
description: "是否禁用整个组件",
|
|
},
|
|
allowDuplicate: {
|
|
control: { type: "boolean" },
|
|
description: "是否允许重复选择同一选项",
|
|
},
|
|
showSubmitButton: {
|
|
control: { type: "boolean" },
|
|
description: "是否显示提交按钮",
|
|
},
|
|
submitText: {
|
|
control: { type: "text" },
|
|
description: "提交按钮文本",
|
|
},
|
|
},
|
|
};
|
|
|
|
export default meta;
|
|
type Story = StoryObj<typeof DragFill>;
|
|
|
|
// 基础示例
|
|
export const Default: Story = {
|
|
args: {
|
|
options: ["选项A", "选项B", "选项C", "选项D"],
|
|
blanks: ["空白1", "空白2", "空白3"],
|
|
onSubmit: (results) => {
|
|
console.log("填充结果:", results);
|
|
},
|
|
},
|
|
};
|
|
|
|
// 使用选项和空白对象
|
|
export const WithObjects: Story = {
|
|
args: {
|
|
options: [
|
|
{ value: "opt1", label: "选项一" },
|
|
{ value: "opt2", label: "选项二" },
|
|
{ value: "opt3", label: "选项三(禁用)", disabled: true },
|
|
{ value: "opt4", label: "选项四" },
|
|
],
|
|
blanks: [
|
|
{ id: "blank1", label: "第一个空白", required: true },
|
|
{ id: "blank2", label: "第二个空白" },
|
|
{ id: "blank3", label: "第三个空白(必填)", required: true },
|
|
],
|
|
onSubmit: (results) => {
|
|
console.log("填充结果:", results);
|
|
},
|
|
},
|
|
};
|
|
|
|
// 受控模式
|
|
export const Controlled: Story = {
|
|
render: () => {
|
|
const [value, setValue] = useState<any[]>([]);
|
|
return (
|
|
<div className="space-y-4">
|
|
<DragFill
|
|
options={["选项A", "选项B", "选项C"]}
|
|
blanks={["空白1", "空白2"]}
|
|
value={value}
|
|
onChange={setValue}
|
|
onSubmit={(results) => {
|
|
alert(`提交的结果: ${JSON.stringify(results)}`);
|
|
}}
|
|
/>
|
|
<div className="text-sm text-gray-600">
|
|
当前填充: {JSON.stringify(value)}
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
};
|
|
|
|
// 允许重复选择
|
|
export const AllowDuplicate: Story = {
|
|
args: {
|
|
options: ["选项A", "选项B", "选项C"],
|
|
blanks: ["空白1", "空白2", "空白3"],
|
|
allowDuplicate: true,
|
|
onSubmit: (results) => {
|
|
console.log("填充结果:", results);
|
|
},
|
|
},
|
|
};
|
|
|
|
// 禁用状态
|
|
export const Disabled: Story = {
|
|
args: {
|
|
options: ["选项A", "选项B", "选项C"],
|
|
blanks: ["空白1", "空白2"],
|
|
disabled: true,
|
|
defaultValue: [{ blankId: "blank1", optionValue: "选项A" }],
|
|
},
|
|
};
|
|
|
|
// 不显示提交按钮
|
|
export const WithoutSubmitButton: Story = {
|
|
args: {
|
|
options: ["选项A", "选项B", "选项C"],
|
|
blanks: ["空白1", "空白2"],
|
|
showSubmitButton: false,
|
|
onChange: (results) => {
|
|
console.log("填充变化:", results);
|
|
},
|
|
},
|
|
};
|
|
|
|
// 自定义样式
|
|
export const CustomStyled: Story = {
|
|
args: {
|
|
options: ["选项A", "选项B", "选项C", "选项D"],
|
|
blanks: ["空白1", "空白2"],
|
|
className: "max-w-2xl mx-auto p-6 bg-gray-50 rounded-xl",
|
|
optionClassName: "text-lg font-semibold",
|
|
blankClassName: "min-h-[100px]",
|
|
submitText: "确认答案",
|
|
onSubmit: (results) => {
|
|
console.log("填充结果:", results);
|
|
},
|
|
},
|
|
};
|
|
|
|
// 自定义渲染
|
|
export const CustomRender: Story = {
|
|
render: () => {
|
|
return (
|
|
<DragFill
|
|
options={["选项A", "选项B", "选项C"]}
|
|
blanks={["空白1", "空白2"]}
|
|
renderOption={(option, isUsed) => (
|
|
<div
|
|
className={cn(
|
|
"p-4 rounded-lg border-2 transition-all cursor-move",
|
|
isUsed
|
|
? "border-gray-200 bg-gray-100 opacity-50"
|
|
: "border-blue-300 bg-blue-50 hover:border-blue-500"
|
|
)}
|
|
draggable={!isUsed}
|
|
>
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-2xl">📦</span>
|
|
<span className="font-medium">{option.label}</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
renderBlank={(blank, filledValue) => (
|
|
<div
|
|
className={cn(
|
|
"min-h-[100px] p-6 rounded-lg border-2 border-dashed flex items-center justify-center",
|
|
filledValue
|
|
? "border-green-400 bg-green-50"
|
|
: "border-purple-300 bg-purple-50"
|
|
)}
|
|
>
|
|
{filledValue ? (
|
|
<div className="text-green-700 font-bold text-lg">
|
|
✓ {filledValue}
|
|
</div>
|
|
) : (
|
|
<div className="text-purple-500 text-center">
|
|
<div className="text-3xl mb-2">⬇️</div>
|
|
<div>{blank.label || "拖拽到这里"}</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
onSubmit={(results) => {
|
|
console.log("填充结果:", results);
|
|
}}
|
|
/>
|
|
);
|
|
},
|
|
};
|
|
|