messages component

This commit is contained in:
Red Adaya 2024-10-06 20:25:48 +08:00
parent 2486804d62
commit 2fc9c54785
3 changed files with 201 additions and 0 deletions

View File

@ -0,0 +1,51 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
.chat-messages {
height: 100%;
width: 100%;
padding: 10px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.chat-message {
display: flex;
align-items: flex-start;
align-items: center;
margin-bottom: 8px;
font-size: 1em;
line-height: 1.4;
}
.chat-user-icon {
height: 1em; /* Make user icon height match the text height */
width: 1em; /* Keep the icon proportional */
border-radius: 50%;
margin-right: 8px;
}
.chat-username {
font-weight: bold;
margin-right: 4px;
line-height: 1.4; /* Ensure alignment with the first line of the message */
}
.chat-text {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
}
.chat-text img {
height: 1em; /* Make inline images (rendered via markdown) match the text height */
width: auto; /* Keep the aspect ratio of images */
margin: 0 4px;
display: inline;
}
.chat-emoji {
margin: 0 2px;
font-size: 1em; /* Match emoji size with the text height */
}

View File

@ -0,0 +1,90 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import type { Meta, StoryObj } from "@storybook/react";
import { ChatMessages } from "./chatmessages";
import "./chatmessages.less";
export interface ChatMessage {
id: string;
username: string;
message: string;
color?: string;
userIcon?: string;
messageIcon?: string;
}
const meta = {
title: "Elements/ChatMessages",
component: ChatMessages,
args: {
messages: [
{
id: "1",
username: "User1",
message: "Hello everyone! 👋",
color: "#ff4500",
userIcon: "https://via.placeholder.com/50",
},
{
id: "2",
username: "User2",
message: "Check this out: ![cool icon](https://via.placeholder.com/20)",
color: "#1e90ff",
},
{
id: "3",
username: "User3",
message: "This is a simple text message without icons.",
color: "#32cd32",
userIcon: "https://via.placeholder.com/50",
},
{
id: "4",
username: "User4",
message: "🎉 👏 Great job!",
color: "#ff6347",
},
{
id: "5",
username: "User5",
message: "Look at this cool icon: Isn't it awesome? ![cool icon](https://via.placeholder.com/20)",
color: "#8a2be2",
userIcon: "https://via.placeholder.com/50",
},
],
},
argTypes: {
messages: {
description: "Array of chat messages to be displayed",
},
},
} satisfies Meta<typeof ChatMessages>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Messages: Story = {
render: (args) => (
<div>
<ChatMessages {...args} />
</div>
),
};
export const ScrollableMessages: Story = {
render: (args) => (
<div style={{ height: "100%", overflow: "hidden" }}>
<ChatMessages {...args} />
</div>
),
args: {
messages: Array.from({ length: 50 }, (_, i) => ({
id: `${i + 1}`,
username: `User${i + 1}`,
message: `This is message number ${i + 1}.`,
color: i % 2 === 0 ? "#ff6347" : "#1e90ff",
userIcon: "https://via.placeholder.com/50",
})),
},
};

View File

@ -0,0 +1,60 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { Markdown } from "@/app/element/markdown";
import clsx from "clsx";
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
import { memo, useEffect, useRef } from "react";
import "./chatmessages.less";
export interface ChatMessage {
id: string;
username: string;
message: string;
color?: string;
userIcon?: string;
}
interface ChatMessagesProps {
messages: ChatMessage[];
className?: string;
}
const ChatMessages = memo(({ messages, className }: ChatMessagesProps) => {
const messagesEndRef = useRef<HTMLDivElement>(null);
const overlayScrollRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
return (
<OverlayScrollbarsComponent
ref={overlayScrollRef}
className={clsx("chat-messages", className)}
options={{ scrollbars: { autoHide: "leave" } }}
>
{messages.map(({ id, username, message, color, userIcon }) => (
<div key={id} className="chat-message">
{userIcon && <img src={userIcon} alt="user icon" className="chat-user-icon" />}
<span className="chat-username" style={{ color: color || "var(--main-text-color)" }}>
{username}:
</span>
<span className="chat-text">
<Markdown scrollable={false} text={message}></Markdown>
</span>
</div>
))}
<div ref={messagesEndRef} />
</OverlayScrollbarsComponent>
);
});
ChatMessages.displayName = "ChatMessages";
export { ChatMessages };