Yugen UI

MessageScroller

MessageScroller là vùng cuộn hội thoại biết bám đáy khi có tin mới, báo “N tin nhắn mới” khi bạn đang đọc tin cũ và giữ nguyên vị trí khi tải thêm lịch sử.

Đi kèm MessageBubble (tin đến / đi, avatar, giờ, trạng thái gửi, thử lại, cảm xúc, tự gộp nhóm tin liên tiếp cùng người gửi, nội dung stream), MessageReaction, MessageDaySeparator và TypingIndicator. Phím End nhảy tới tin mới nhất; tin đến được đọc qua live region polite — tin đang stream chỉ đọc khi đã nhận đủ.
Hôm nay
Sakura Bot
Chào bạn 👋 Mình là Sakura Bot.
Hỏi mình bất cứ điều gì về Yugen UI nhé!

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 {  MessageScroller, MessageBubble, MessageReaction, MessageDaySeparator, TypingIndicator, ChatComposer,} 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.

Bạn
Mình gửi file thiết kế rồi nhé.
Đã xem
Bạn xem giúp phần màu Sakura.
Đã nhận
Cần trước 5 giờ chiều.
Đã gửi
Cảm ơn bạn!

Trạng thái gửi & thử lại

sending → sent → delivered → read; lỗi hiện thông báo + nút Thử lại.

Thứ Hai, 19/10
Nguyễn Văn An
Sáng nay bản build mới đã lên staging.
Mọi người kiểm tra giúp phần Trace nhé!
Có gì cứ nhắn mình.
Bạn
Đã xem, chạy mượt lắm 🎉

Nhóm tin & cảm xúc

Tin liên tiếp cùng người gửi tự gộp: chỉ tin đầu có avatar, tên, giờ.

Bình
Tin nhắn số 100
Tin nhắn số 101
Bạn
Tin nhắn số 102
Bình
Tin nhắn số 103
Tin nhắn số 104
Bạn
Tin nhắn số 105
Bình
Tin nhắn số 106
Tin nhắn số 107
Bạn
Tin nhắn số 108
Bình
Tin nhắn số 109
Tin nhắn số 110
Bạn
Tin nhắn số 111

Tải tin cũ

Cuộn lên đầu để gọi onLoadOlder; vị trí đọc được giữ nguyên khi tin cũ chèn vào.

Cường
Cập nhật #1
Dũng
Cập nhật #2
Cường
Cập nhật #3
Dũng
Cập nhật #4
Cường
Cập nhật #5
Dũng
Cập nhật #6
Cường
Cập nhật #7
Dũng
Cập nhật #8
Cường
Cập nhật #9
Dũng
Cập nhật #10
Cường
Cập nhật #11
Dũng
Cập nhật #12
Cường
Cập nhật #13
Dũng
Cập nhật #14

Tin mới khi đang đọc tin cũ

Cuộn lên rồi bấm “Nhận tin mới”: danh sách không tự nhảy, nút nổi đếm số tin mới.

Cách dùng

Nên
  • Đặt key ổn định (id tin nhắn) cho mỗi phần tử để phát hiện tin mới / tin cũ chính xác.
  • Luôn kéo xuống đáy khi chính người dùng gửi tin (component tự làm với side="outgoing").
  • Cho tin lỗi một đường thử lại rõ ràng ngay tại tin đó.
Không nên
  • Không tự cuộn xuống khi người dùng đang đọc tin cũ — để nút “tin nhắn mới” làm việc đó.
  • Không đặt aria-live trên cả danh sách khi stream chữ — trình đọc màn hình sẽ đọc từng mẩu.
  • Không render HTML/markdown chưa làm sạch trong bong bóng — MessageBubble hiển thị văn bản thuần.

Props

Prop · KiểuMặc địnhMô tả
MessageScroller.onLoadOlder() => void | Promise<void>Mặc định: —Gọi khi cuộn chạm đầu; Promise chặn gọi lặp.
MessageScroller.hasOlder / loadingOlderbooleanMặc định: —Còn tin cũ không; hiện spinner ở đầu danh sách.
MessageScroller.labelstringMặc định: "Conversation"Tên truy cập của vùng log.
MessageScroller.bottomThresholdnumberMặc định: 48Khoảng (px) tới đáy vẫn coi là “đang ở cuối”.
MessageScroller.announceIncomingbooleanMặc định: trueĐọc tin đến qua live region polite.
MessageBubble.side"incoming" | "outgoing"Mặc định: "incoming"Tin của người khác / của mình.
MessageBubble.author / avatar / avatarSrcstring / ReactNode / stringMặc định: —Người gửi; avatar mặc định là Avatar theo tên (tin đến).
MessageBubble.time / dateTimestringMặc định: —Giờ hiển thị và giá trị ISO cho <time>.
MessageBubble.status / onRetry / errorMessage"sending" | "sent" | "delivered" | "read" | "error"Mặc định: —Trạng thái gửi; error hiện nút Thử lại.
MessageBubble.groupPosition"single" | "first" | "middle" | "last"Mặc định: —Ghi đè vị trí trong nhóm (mặc định tự tính).
MessageBubble.reactionsReactNodeMặc định: —Slot cảm xúc (MessageReaction).
MessageBubble.streamingbooleanMặc định: —Đang stream: con trỏ nhấp nháy, aria-busy, chưa đọc qua live region.
MessageBubble.appearance"bubble" | "plain"Mặc định: "bubble"plain: không nền, rộng hết — cho câu trả lời dài của AI.
MessageReaction{ emoji, count?, selected?, label? }Mặc định: —Nút cảm xúc (aria-pressed).
TypingIndicator{ author?, avatar?, label? }Mặc định: —Ba chấm (tĩnh khi giảm chuyển động), role=status.

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.messageScroller

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

Nhãn vùng hội thoại (khi không truyền label).

Mặc định (tiếng Anh)"Conversation"messagesVi"Cuộc trò chuyện"
loadingOlderstring

Spinner khi đang tải tin cũ.

Mặc định (tiếng Anh)"Loading earlier messages…"messagesVi"Đang tải tin nhắn cũ…"
jumpToLateststring

Tên nút cuộn xuống đáy khi không có tin mới.

Mặc định (tiếng Anh)"Jump to latest message"messagesVi"Tới tin nhắn mới nhất"
newMessages(count: number) => string

Chữ trên nút cuộn xuống đáy khi có tin mới.

Mặc định (tiếng Anh)(count) => (count === 1 ? "1 new message" : `${count} new messages`)messagesVi(count) => `${count} tin nhắn mới`

messages.messageBubble

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

Nhãn trạng thái gửi — cùng tên với MessageStatus.

Mặc định (tiếng Anh)"Sending"messagesVi"Đang gửi"
sentstringMặc định (tiếng Anh)"Sent"messagesVi"Đã gửi"
deliveredstringMặc định (tiếng Anh)"Delivered"messagesVi"Đã nhận"
readstringMặc định (tiếng Anh)"Seen"messagesVi"Đã xem"
errorstring

Lỗi gửi (khi không truyền errorMessage).

Mặc định (tiếng Anh)"Couldn't send."messagesVi"Không gửi được."
retrystringMặc định (tiếng Anh)"Retry"messagesVi"Thử lại"

messages.typingIndicator

Khoá · KiểuMặc định (tiếng Anh)messagesVi
label(author?: string) => string

Câu đọc cho trình đọc màn hình (khi không truyền label).

Mặc định (tiếng Anh)(author) => `${author ?? "Someone"} is typing…`messagesVi(author) => `${author ?? "Đối phương"} đang soạn tin…`