Yugen UI

Questionnaire

Questionnaire dựng khảo sát hoặc form nhiều bước từ một mảng cấu hình — mỗi màn một câu như Typeform, hoặc tất cả trên một trang.

Hỗ trợ single, multiple, rating (dùng Rating), scale (NPS 0–10), text, longText, yesNo; required và validate tuỳ chỉnh; thanh tiến độ, Quay lại / Tiếp, Enter để tiếp tục, phím số (0–9) để chọn, chuyển cảnh trượt (tắt khi giảm chuyển động), bước xem lại và màn hoàn tất. Hỗ trợ controlled value / onChange.
Yugen Labs · thử nghiệmComponent này nằm ngoài SemVer: API có thể đổi ở một bản minor, và thay đổi sẽ được ghi trong Có gì mới. Chính sách phiên bản

Khảo sát nhanh

6 câu · khoảng 1 phút

Câu 1/617%

Import

Cài từ npm, chép mã nguồn vào dự án bằng @yugen-ui/cli hoặc shadcn CLI, hoặc import thẳng khi làm việc trong repo này.

TSX
// npm install yugen-uiimport { Questionnaire, type Question } from "yugen-ui";

Biến thể

Mỗi ô là một mẫu độc lập: hover để Copy, hoặc bấm Code để xem mã nguồn.

Chế độ list + validate

mode="list": mọi câu trên một trang, mỗi câu bọc trong Field; email kiểm tra bằng validate.

Đã trả lời 0 /2 câu

Câu 1/250%

Controlled & màn hoàn tất

value / onChange để đồng bộ với state bên ngoài; completion là render-prop nhận answers và restart.

Cách dùng

Nên
  • Giữ mỗi câu một ý; mô tả ngắn chỉ khi cần giải thích.
  • Chỉ đánh dấu required khi thật sự cần — câu tuỳ chọn giúp người dùng đi nhanh.
  • Bật bước xem lại cho khảo sát dài hoặc khi câu trả lời khó sửa sau khi gửi.
Không nên
  • Không dùng stepper cho form 2–3 ô đơn giản — dùng mode="list" hoặc Field thường.
  • Không quá 9 lựa chọn trong một câu (phím số chỉ tới 9, và khó đọc).
  • Không dùng validate để chặn câu tuỳ chọn còn trống — validate chỉ chạy khi đã có câu trả lời.

Props

Prop · KiểuMặc địnhMô tả
questionsQuestion[]Mặc định: —Cấu hình câu hỏi: { id, type, title, description?, required?, validate?, …theo loại }.
mode"stepper" | "list"Mặc định: "stepper"Mỗi màn một câu, hoặc tất cả trên một trang.
value / defaultValueQuestionnaireAnswersMặc định: —Record<id, string | string[] | number | boolean>.
onChange(answers, questionId) => voidMặc định: —Gọi mỗi khi một câu trả lời đổi.
onSubmit(answers) => void | Promise<void>Mặc định: —Gọi khi mọi câu hợp lệ; Promise → nút Gửi hiện “Đang gửi…”, lỗi → thông báo thử lại.
reviewbooleanMặc định: trueThêm bước xem lại trước khi gửi.
completionReactNode | (answers, restart) => ReactNodeMặc định: —Thay màn hoàn tất mặc định.
title / description / submitLabelReactNode / ReactNode / stringMặc định: — / — / "Submit"Phần đầu và nhãn nút gửi.
appearance"outline" | "subtle"Mặc định: "outline"Có khung (Card) hay không.
SingleQuestion / MultipleQuestion{ options, min?, max? }Mặc định: —Thẻ lựa chọn; multiple giới hạn bằng min / max.
RatingQuestion / ScaleQuestion{ max? } / { min?, max?, minLabel?, maxLabel? }Mặc định: —Sao (Rating) hoặc thang điểm phân đoạn (mặc định 0–10).
TextQuestion / LongTextQuestion{ placeholder?, inputType?, maxLength?, rows? }Mặc định: —Input một dòng / Textarea có bộ đếm.
YesNoQuestion{ yesLabel?, noLabel? }Mặc định: —Có / Không — phím Y, N (hoặc C, K).

Messages

Chữ có sẵn của component: nhãn, aria-label, thông báo cho trình đọc màn hình.

Mặc định là tiếng Anh. Đổi qua prop messages của YugenProvider, theo namespace bên dưới; messagesVi chứa bản tiếng Việt. Xem Ngôn ngữ giao diện.

messages.questionnaire

Khoá · KiểuMặc định (tiếng Anh)messagesVi
requiredTextstring

Lỗi khi câu bắt buộc chưa trả lời (text / longText · rating / scale · còn lại).

Mặc định (tiếng Anh)"Enter an answer."messagesVi"Vui lòng nhập câu trả lời."
requiredScorestringMặc định (tiếng Anh)"Select a rating."messagesVi"Vui lòng chọn một mức điểm."
requiredChoicestringMặc định (tiếng Anh)"Select an answer."messagesVi"Vui lòng chọn một câu trả lời."
minSelected(min: number) => string

Lỗi số mục đã chọn của câu multiple.

Mặc định (tiếng Anh)(min) => (min === 1 ? "Select at least 1 item." : `Select at least ${min} items.`)messagesVi(min) => `Chọn ít nhất ${min} mục.`
maxSelected(max: number) => stringMặc định (tiếng Anh)(max) => (max === 1 ? "Select up to 1 item." : `Select up to ${max} items.`)messagesVi(max) => `Chọn tối đa ${max} mục.`
selectedCount(count: number, max: number) => string

Đếm số mục đã chọn của câu multiple có max.

Mặc định (tiếng Anh)(count, max) => `${count}/${max} selected`messagesVi(count, max) => `Đã chọn ${count}/${max}`
unansweredstring

Trang xem lại: câu chưa trả lời, điểm rating.

Mặc định (tiếng Anh)"Not answered"messagesVi"Chưa trả lời"
ratingAnswer(value: number, max: number) => stringMặc định (tiếng Anh)(value, max) => `${value}/${max} stars`messagesVi(value, max) => `${value}/${max} sao`
yesstring

Nhãn mặc định của câu yesNo.

Mặc định (tiếng Anh)"Yes"messagesVi"Có"
nostringMặc định (tiếng Anh)"No"messagesVi"Không"
requiredstring

Chữ ẩn (sr-only) sau tiêu đề câu bắt buộc.

Mặc định (tiếng Anh)"(required)"messagesVi"(bắt buộc)"
keyHintChoice(count: number) => string

Gợi ý phím tắt (chế độ stepper).

Mặc định (tiếng Anh)(count) => `Press 1–${count} to select`messagesVi(count) => `Nhấn 1–${count} để chọn`
keyHintYesNostringMặc định (tiếng Anh)"Press Y or N to select"messagesVi"Nhấn Y hoặc N để chọn"
keyHintRating(max: number) => stringMặc định (tiếng Anh)(max) => `Press 1–${max} to rate`messagesVi(max) => `Nhấn 1–${max} để chấm điểm`
keyHintScalestringMặc định (tiếng Anh)"Press a number key to select"messagesVi"Nhấn phím số để chọn"
questionOf(current: number, total: number) => string

Dòng tiến độ và thanh tiến độ (chế độ stepper); current đếm từ 1.

Mặc định (tiếng Anh)(current, total) => `Question ${current} of ${total}`messagesVi(current, total) => `Câu ${current}/${total}`
progressLabelstringMặc định (tiếng Anh)"Survey progress"messagesVi"Tiến độ khảo sát"
progressValue(current: number, total: number) => stringMặc định (tiếng Anh)(current, total) => `Question ${current} of ${total}`messagesVi(current, total) => `Câu ${current} trên ${total}`
reviewTitlestring

Trang xem lại.

Mặc định (tiếng Anh)"Review your answers"messagesVi"Xem lại câu trả lời"
reviewDescriptionstringMặc định (tiếng Anh)"Check your answers before you submit. Select “Edit” to make changes."messagesVi"Kiểm tra lần cuối trước khi gửi. Chọn “Sửa” để thay đổi."
editQuestion(index: number, title: string) => string

aria-label nút sửa một câu; index đếm từ 1.

Mặc định (tiếng Anh)(index, title) => `Edit question ${index}: ${title}`messagesVi(index, title) => `Sửa câu ${index}: ${title}`
doneTitlestring

Màn hình hoàn tất mặc định.

Mặc định (tiếng Anh)"Thank you!"messagesVi"Cảm ơn bạn!"
doneDescriptionstringMặc định (tiếng Anh)"Your answers have been recorded."messagesVi"Câu trả lời của bạn đã được ghi nhận."
restartstringMặc định (tiếng Anh)"Start over"messagesVi"Làm lại"
submitstring

Nút và trạng thái.

Mặc định (tiếng Anh)"Submit"messagesVi"Gửi"
submittingstringMặc định (tiếng Anh)"Submitting…"messagesVi"Đang gửi…"
submitErrorstringMặc định (tiếng Anh)"Couldn't submit your answers. Try again."messagesVi"Không gửi được câu trả lời. Vui lòng thử lại."
reviewstringMặc định (tiếng Anh)"Review"messagesVi"Xem lại"
backstringMặc định (tiếng Anh)"Back"messagesVi"Quay lại"
backToEditstringMặc định (tiếng Anh)"Back to editing"messagesVi"Quay lại chỉnh sửa"
nextstringMặc định (tiếng Anh)"Next"messagesVi"Tiếp"
orPressstring

Chữ trước phím Enter cạnh nút Tiếp.

Mặc định (tiếng Anh)"or press"messagesVi"hoặc nhấn"