チャット会話ブロック
Chat Conversationコンポーネントは、ReactとTypeScriptで構築された完全にカスタマイズ可能でアクセシブルなチャットインターフェースです。メッセージ表示、入力処理、リアルタイムメッセージングアプリケーションのための柔軟なカスタマイズオプションを備えた完全な会話ビューを提供します。
🚀 インストール
npm install @nodeblocks/frontend-chat-conversation-block@0.2.1
📖 使用法
import {ChatConversation} from '@nodeblocks/frontend-chat-conversation-block';
- 基本的な使用法
- 高度な使用法
ライブエディター
function SimpleChatConversation() { const [messages, setMessages] = useState([ { title: '田中太郎', content: 'こんにちは!今日の調子はいかがですか?', id: 'message-001', createdAt: '2025-02-01T10:00:00Z', isOwnMessage: false, avatar: { sx: { bgcolor: 'primary.main' }, }, }, { title: 'あなた', content: 'こんにちは!とても調子いいです、ありがとうございます!', id: 'message-002', createdAt: '2025-02-01T10:01:00Z', isOwnMessage: true, }, ]); return ( <ChatConversation chatView={{ heading: { avatar: { sx: { bgcolor: 'secondary.main' }, }, buttonHref: '/home', }, isLoading: false, }} onInputChange={(text) => { console.log('入力が変更されました:', text); }} onMessageSubmit={(text) => { console.log('メッセージが送信されました:', text); setMessages(prev => [...prev, { title: 'あなた', content: text, id: 'message-' + Date.now(), createdAt: new Date().toISOString(), isOwnMessage: true, }]); }} commentInput={{ isDisabled: false, }} commentSubmitButton={{ isDisabled: false, }} messages={messages} labels={{ chatViewHeadingButtonText: 'ホーム', chatViewHeadingText: '会話', }} onNavigate={(url) => { console.log('ナビゲート:', url); }} placeholders={{ commentInput: 'メッセージを入力...', }} > <ChatConversation.Heading /> <ChatConversation.Body /> </ChatConversation> ); }
結果
Loading...
ライブエディター
function AdvancedChatConversation() { const [messages, setMessages] = useState([ { title: 'サポートチーム', content: 'サポートチャットへようこそ!今日はどのようなお手伝いができますか?', id: 'message-001', createdAt: '2025-02-01T10:00:00Z', isOwnMessage: false, avatar: { sx: { bgcolor: 'info.main' }, src: '/support-avatar.jpg', }, }, { title: 'あなた', content: 'アカウント設定についてサポートが必要です。', id: 'message-002', createdAt: '2025-02-01T10:01:00Z', isOwnMessage: true, }, ]); return ( <ChatConversation chatView={{ isLoading: false, }} onInputChange={(text) => { console.log('入力が変更されました:', text); }} onMessageSubmit={(text) => { console.log('メッセージが送信されました:', text); setMessages(prev => [...prev, { title: 'あなた', content: text, id: 'message-' + Date.now(), createdAt: new Date().toISOString(), isOwnMessage: true, }]); }} commentInput={{ isDisabled: false, errorText: '', }} commentSubmitButton={{ isDisabled: false, }} messages={messages} labels={{ chatViewHeadingButtonText: 'ダッシュボード', chatViewHeadingText: 'サポートチャット', }} onNavigate={(url) => { console.log('ナビゲート:', url); }} placeholders={{ commentInput: '質問を入力...', }} className="custom-chat-container" sx={{maxHeight: '600px'}} > {({defaultBlocks}) => ({ blocks: { ...defaultBlocks, toolbar: ( <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 16px', backgroundColor: '#e7f3ff', borderBottom: '1px solid #b3d9ff', fontSize: '14px', color: '#0066cc', }} > <span>🟢 オンライン - サポートエージェント</span> <span>応答時間: 約2分</span> </div> ), statusBar: ( <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', padding: '8px', backgroundColor: '#f8f9fa', borderTop: '1px solid #dee2e6', fontSize: '12px', color: '#6c757d', }} > <span> 💬 {messages.length} メッセージ • 最終更新: {new Date().toLocaleTimeString()} </span> </div> ), }, blockOrder: ['heading', 'toolbar', 'body', 'statusBar'], })} </ChatConversation> ); }
結果
Loading...
🔧 プロパティリファレンス
メインコンポーネントプロパティ
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
onMessageSubmit | (text: string) => void | undefined | メッセージ送信時のコールバック |
onInputChange | (text: string) => void | undefined | コメント入力変更時のコールバック |
commentInput | CommentInputConfig | undefined | 下部のコメント入力の設定 |
commentSubmitButton | {isDisabled?: boolean;} | undefined | コメント送信ボタンの設定 |
chatView | ChatViewConfig | 必須 | チャットパネルの設定 |
messages | Message[] | 必須 | チャットビューに表示するメッセージの配列 |
labels | LabelsConfig | 必須 | ラベルテキストの設定 |
onNavigate | (url: string) => void | 必須 | ナビゲーションコールバック(hrefに必要) |
placeholders | {commentInput: string} | 必須 | プレースホルダーテキストの設定 |
className | string | undefined | スタイリング用の追加CSSクラス名 |
children | BlocksOverride | undefined | デフォルトレンダリングをオーバーライドするカスタムブロックコンポーネント |
sx | SxProps<Theme> | undefined | カスタムスタイリング用のMUIシステムプロパティ |
注意: このコンポーネントはすべてのMUI Stackコンポーネントプロパティ(direction、spacing、dividerなど)を継承します。
サブコンポーネント
ChatConversationコンポーネントは複数のサブコンポーネントを提供します。すべてのサブコンポーネントは、メインコンポーネントのコンテキストからデフォルト値を受け取り、プロパティを通じてこれらの値をオーバーライドできます。
ChatConversation.Heading
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
labels | LabelsConfig | コンテキスト値 | ラベル設定(コンテキストをオーバーライド) |
chatView | ChatViewConfig | コンテキスト値 | チャットビュー設定(コンテキストをオーバーライド) |
onNavigate | (url: string) => void | コンテキスト値 | ナビゲーションコールバック(コンテキストをオーバーライド) |
className | string | undefined | スタイリング用の追加CSSクラス名 |
children | ReactNode | デフォルトヘッダーコンテンツ | デフォルトヘッダーをオーバーライドするカスタムコンテンツ |
direction | StackDirection | "row" | ヘッダーレイアウトのフレックス方向 |
spacing | number | string | 2 | ヘッダー要素間のスペーシング |
sx | SxProps<Theme> | undefined | カスタムスタイリング用のMUIシステムプロパティ |
注意: このコンポーネントはすべてのMUI Stackコンポーネントプロパティを継承します。
ChatConversation.Body
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
onMessageSubmit | (text: string) => void | コンテキスト値 | メッセージ送信コールバック(コンテキストをオーバーライド) |
onInputChange | (text: string) => void | コンテキスト値 | 入力変更コールバック(コンテキストをオーバーライド) |
commentInput | CommentInputConfig | コンテキスト値 | コメント入力設定(コンテキストをオーバーライド) |
commentSubmitButton | {isDisabled?: boolean;} | コンテキスト値 | 送信ボタン設定(コンテキストをオーバーライド) |
chatView | ChatViewConfig | コンテキスト値 | チャットビュー設定(コンテキストをオーバーライド) |
messages | Message[] | コンテキスト値 | メッセージ配列(コンテキストをオーバーライド) |
onNavigate | (url: string) => void | コンテキスト値 | ナビゲーションコールバック(コンテキストをオーバーライド) |
placeholders | {commentInput: string} | コンテキスト値 | プレースホルダー設定(コンテキストをオーバーライド) |
className | string | undefined | スタイリング用の追加CSSクラス名 |
sx | SxProps<Theme> | undefined | カスタムスタイリング用のMUIシステムプロパティ |
注意: このコンポーネントはスタイリング用にMUI Boxコンポーネントプロパティを受け入れます。
🎨 設定例
このコンポーネントはアバターのカスタマイズにMUI Avatarプロパティを使用します。アバターの設定方法は以下の通りです:
メッセージアバター
const message = {
id: 'msg-001',
content: 'こんにちは!',
createdAt: new Date().toISOString(),
avatar: {
// MUI Avatarプロパティ
src: '/path/to/image.jpg', // 画像ソース
srcSet: '/path/to/image@2x.jpg 2x', // レスポンシブ画像
sx: {bgcolor: 'primary.main'}, // カスタムスタイリング
children: 'JD', // フォールバックコンテンツ(イニシャル)
// 拡張プロパティ
href: '/users/john-doe', // ナビゲーションリンク(onNavigateが必要)
isLoading: false, // ローディングプレースホルダーを表示
// リンク動作用
component: 'a', // アンカータグとしてレンダリング
},
};
ヘッダーアバター
<ChatConversation
chatView={{
heading: {
avatar: {
src: '/team-avatar.jpg',
sx: { width: 32, height: 32 },
isLoading: false,
// クリック可能なアバター用
component: 'a',
href: '/team-profile',
},
buttonHref: '/home',
},
}}
// ...その他のプロパティ
/>
🔧 TypeScriptサポート
包括的な型定義による完全なTypeScriptサポート:
import {ChatConversation} from '@nodeblocks/frontend-chat-conversation-block';
import {AvatarProps} from '@mui/material';
import {useState} from 'react';
interface LabelsConfig {
chatViewHeadingButtonText?: string;
chatViewHeadingText?: string;
}
interface CommentInputConfig {
/** 入力を無効化(送信中など) */
isDisabled?: boolean;
/** コメント入力フィールド下に表示するエラーメッセージ */
errorText?: string;
}
interface ChatViewConfig {
heading?: {
/** オプションのisLoadingステートを持つMUI Avatarプロパティ */
avatar?:
| (Partial<AvatarProps> & { isLoading?: boolean })
| (Partial<AvatarProps<'a'>> & { isLoading?: boolean; component: 'a' });
buttonHref: string;
};
/**
* 全体ビューまたは以前のメッセージのローディング状態
* (messagesが空かどうかによる)
*/
isLoading?: boolean;
/**
* ユーザーが上にスクロールしてチャットビューの上部に達したときのコールバック。
* サーバーから追加のメッセージを取得するために使用できます。
*/
onScrollTop?: (earliestMessageId: string) => void;
}
// メッセージインターフェース
interface Message {
avatar?: {
sx?: Record<string, unknown>;
src?: string;
href?: string;
isLoading?: boolean;
};
content: string;
createdAt: string;
id: string;
isOwnMessage?: boolean;
title?: string;
}
// 完全な型付き例
function TypedChatConversation() {
const [messages, setMessages] = useState<Message[]>([
{
title: 'サポートエージェント',
content: 'こんにちは!今日はどのようなお手伝いができますか?',
id: 'msg-001',
createdAt: '2025-02-01T09:00:00Z',
isOwnMessage: false,
avatar: {
sx: { bgcolor: 'info.main' },
src: '/path/to/agent-avatar.jpg',
},
},
{
title: 'あなた',
content: 'アカウント設定についてサポートが必要です。',
id: 'msg-002',
createdAt: '2025-02-01T09:01:00Z',
isOwnMessage: true,
},
]);
const [inputValue, setInputValue] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const [inputError, setInputError] = useState<string | undefined>();
const handleInputChange = (text: string) => {
setInputValue(text);
if (text.length > 500) {
setInputError('メッセージが長すぎます(最大500文字)');
} else {
setInputError(undefined);
}
};
const handleMessageSubmit = async (text: string) => {
if (!text.trim()) return;
setIsSubmitting(true);
const userMessage: Message = {
title: 'あなた',
content: text,
id: `msg-${Date.now()}`,
createdAt: new Date().toISOString(),
isOwnMessage: true,
};
setMessages(prev => [...prev, userMessage]);
setInputValue('');
try {
await new Promise(resolve => setTimeout(resolve, 2000));
const responseMessage: Message = {
title: 'サポートエージェント',
content: 'メッセージありがとうございます。それについてお手伝いさせていただきます。',
id: `msg-${Date.now()}-response`,
createdAt: new Date().toISOString(),
isOwnMessage: false,
avatar: {
sx: {bgcolor: 'info.main'},
},
};
setMessages(prev => [...prev, responseMessage]);
} catch (error) {
console.error('メッセージ送信に失敗しました:', error);
} finally {
setIsSubmitting(false);
}
};
const handleScrollTop = (earliestMessageId: string) => {
console.log('以前のメッセージを読み込み:', earliestMessageId);
};
return (
<ChatConversation
chatView={{
heading: {
avatar: {
sx: {bgcolor: 'secondary.main', width: 32, height: 32},
src: '/path/to/avatar.jpg',
},
buttonHref: '/support/dashboard',
},
isLoading: isSubmitting,
onScrollTop: handleScrollTop,
}}
onInputChange={handleInputChange}
onMessageSubmit={handleMessageSubmit}
commentInput={{
isDisabled: isSubmitting,
errorText: inputError,
}}
commentSubmitButton={{
isDisabled: isSubmitting || Boolean(inputError) || !inputValue.trim(),
}}
messages={messages}
labels={{
chatViewHeadingButtonText: 'ダッシュボードに戻る',
chatViewHeadingText: 'カスタマーサポート',
}}
onNavigate={(url) => {
window.location.href = url;
}}
placeholders={{
commentInput: 'ここにメッセージを入力...',
}}
className="custom-chat-container"
sx={{maxHeight: '600px'}}
>
<ChatConversation.Heading className="custom-chat-header" />
<ChatConversation.Body className="custom-chat-body" />
</ChatConversation>
);
}
📝 注意事項
- メッセージはLuxonライブラリを使用して日本語の日付形式(
yyyy年MM月dd日)でグループ化されます - 新しいメッセージが追加されると、チャットビューは自動的に下部にスクロールします
onScrollTopコールバックはユーザーが上部までスクロールしたときにトリガーされ、ページネーションに便利です- Enter(Shiftなし)を押すとメッセージが送信されます。Shift+Enterで改行が作成されます
- コンポーネントは一貫したスタイリングのためにMUIのテーマシステムを使用します
React、TypeScript、MUI、Luxonを使用して❤️で構築されました。