原语 / Radix UI
Questionnaire · Radix UI
多步问卷,支持单选、多选、自由填写与跳过;题目依次滑动切换。
安装
Radix UI 的原语保持上游 API 不变,安装只需要三步:加包、加组件、把 import 换掉。
bun add @animate-ui/react motion
bunx shadcn@latest add questionnaire
bunx @animate-ui/cli@latest animate questionnaire用法
从 Animate UI 引入原语,其余结构、属性与 data 属性都保持 Radix UI 的写法。也可以按子路径单独引入:
import { Questionnaire } from '@animate-ui/react/primitives/questionnaire';import { Questionnaire } from '@animate-ui/react/primitives/questionnaire';import {
Questionnaire,
QuestionnaireActions,
QuestionnaireChoice,
QuestionnaireChoices,
QuestionnaireDescription,
QuestionnaireError,
QuestionnaireInput,
QuestionnaireItem,
QuestionnaireNext,
QuestionnairePrevious,
QuestionnaireProgress,
QuestionnaireSkip,
QuestionnaireSubmit,
QuestionnaireTitle,
} from '@/components/ui/questionnaire';
const items = [
{
name: 'direction',
required: true,
prompt: 'What should we prototype next?',
description: 'Choose a direction or write your own.',
choices: [
{
value: 'delegation',
label: 'Delegation',
description: 'Show how work moves to a specialist.',
},
{
value: 'questions',
label: 'Question prompts',
description: 'Show choices while the interface waits.',
},
{ value: 'both', label: 'Both together' },
],
input: { label: 'Another answer', placeholder: 'Type another answer…' },
},
{
name: 'detail',
required: false,
prompt: 'How much detail should it include?',
description: 'Skip this if you are not sure yet.',
choices: [
{ value: 'focused', label: 'Focused' },
{ value: 'complete', label: 'Complete flow' },
],
},
] as const;
export function PrototypeQuestionnaire() {
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const answers = new FormData(event.currentTarget);
// answers.get("direction"), answers.getAll(...) for multiple items.
}
return (
<Questionnaire items={items} onSubmit={handleSubmit}>
<QuestionnaireProgress />
{items.map((question) => (
<QuestionnaireItem
key={question.name}
name={question.name}
required={question.required}
>
<QuestionnaireTitle>{question.prompt}</QuestionnaireTitle>
<QuestionnaireDescription>
{question.description}
</QuestionnaireDescription>
<QuestionnaireChoices>
{question.choices.map((choice) => (
<QuestionnaireChoice key={choice.value} value={choice.value}>
<span className="font-medium">{choice.label}</span>
{'description' in choice ? (
<span className="text-muted-foreground">
{choice.description}
</span>
) : null}
</QuestionnaireChoice>
))}
{'input' in question ? (
<QuestionnaireInput
aria-label={question.input.label}
placeholder={question.input.placeholder}
/>
) : null}
</QuestionnaireChoices>
<QuestionnaireError />
</QuestionnaireItem>
))}
<QuestionnaireActions>
<QuestionnairePrevious />
<QuestionnaireSkip />
<QuestionnaireNext />
<QuestionnaireSubmit />
</QuestionnaireActions>
</Questionnaire>
);
}动效
- 前进时当前题向起始方向滑出 24px、淡入 5px 模糊(150ms),下一题从另一侧 24px 进入(300ms)。
- 高度在旧题离场后以 450ms 布局曲线滑向新高度;跨题跳转直接抵达,中间题不闪现。
所有时长都会乘以 AnimateUIProvider 的 tempo,并遵守系统的减少动效设置。
API
动效相关的 transition 与 exitTransition 是 Animate UI 唯一新增的属性;其余 props 与 Radix UI 完全一致。
这一页没有额外的动画属性,API 与 Radix UI 相同。