チャット会話リストブロック
Chat Conversation Listコンポーネントは、ReactとTypeScriptで構築された完全にカスタマイズ可能でアクセシブルな会話リストインターフェースです。検索機能、無限スクロール、チャットアプリケーションインターフェースのための柔軟なカスタマイズオプションを備えた完全な会話概要を提供します。
🚀 インストール
npm install @nodeblocks/frontend-chat-conversation-list-block@0.1.3
📖 使用法
import {ChatConversationList} from '@nodeblocks/frontend-chat-conversation-list-block';
- 基本的な使用法
- 高度な使用法
function BasicChatConversationList() {
const [conversations, setConversations] = useState([
{
id: 'conv-001',
title: 'カスタマーサポート',
content: 'お問い合わせありがとうございます。今日はどのようなお手伝いができますか?',
dateTime: '2025-02-01T10:00:00Z',
titleLines: 1,
unreadCount: 3,
href: '/chat/conv-001',
avatar: {
avatarColor: '1',
avatarSize: 'medium',
},
},
{
id: 'conv-002',
title: 'プロジェクトチーム討議',
content: '新機能の実装準備ができました。レビューをお願いします。',
dateTime: '2025-02-01T09:30:00Z',
titleLines: 2,
unreadCount: 1,
href: '/chat/conv-002',
avatar: {
avatarColor: '2',
avatarSize: 'medium',
},
},
{
id: 'conv-003',
title: 'マーケティングキャンペーン',
content: 'キャンペーン戦略について話し合うミーティングをスケジュールしましょう。',
dateTime: '2025-01-31T16:45:00Z',
titleLines: 1,
href: '/chat/conv-003',
avatar: {
avatarColor: '3',
avatarSize: 'medium',
},
},
]);
return (
<ChatConversationList
conversations={conversations}
labels={{
heading: '会話',
emptyState: 'まだ会話がありません',
}}
placeholders={{
search: '会話を検索...',
}}
search={{
defaultValue: '',
onChange: (value) => {
console.log('検索が変更されました:', value);
},
}}
onNavigate={(url) => {
console.log('ナビゲート:', url);
}}
onScrollBottom={() => {
console.log('さらに会話を読み込み');
}}
isLoading={false}>
<ChatConversationList.Heading />
<ChatConversationList.SearchBar />
<ChatConversationList.ScrollPanel />
</ChatConversationList>
);
}
function AdvancedChatConversationList() {
const [conversations, setConversations] = useState([
{
id: 'support-001',
title: 'カスタマーサポートチーム',
content: 'あなたの問題は解決されました。他にお手伝いできることはありますか?',
dateTime: '2025-02-01T14:30:00Z',
titleLines: 1,
unreadCount: 5,
href: '/chat/support-001',
isSelected: false,
avatar: {
avatarColor: '1',
avatarSize: 'medium',
avatarUrl: '/avatars/support-team.jpg',
},
},
{
id: 'team-002',
title: '開発チーム討議',
content: 'コードレビューが完了しました。新機能ブランチをマージする準備ができています。',
dateTime: '2025-02-01T13:15:00Z',
titleLines: 2,
unreadCount: 2,
href: '/chat/team-002',
isSelected: true,
avatar: {
avatarColor: '2',
avatarSize: 'medium',
},
},
{
id: 'client-003',
title: 'クライアントプロジェクト更新',
content: 'プロジェクトのタイムラインについて話し合うため、明日午後2時にミーティングを予定しています。',
dateTime: '2025-02-01T11:45:00Z',
titleLines: 1,
href: '/chat/client-003',
avatar: {
avatarColor: '3',
avatarSize: 'medium',
avatarUrl: '/avatars/client.jpg',
},
},
]);
return (
<ChatConversationList
conversations={conversations}
labels={{
heading: 'チーム会話',
emptyState: '検索に一致する会話がありません',
}}
placeholders={{
search: '名前またはメッセージで検索...',
}}
search={{
defaultValue: '',
onChange: (value) => {
console.log('検索が変更されました:', value);
},
}}
onNavigate={(url) => {
console.log('ナビゲート:', url);
}}
onScrollBottom={() => {
console.log('さらに会話を読み込み');
}}
isLoading={false}
className="custom-conversation-list"
style={{ maxHeight: '500px', border: '1px solid #e0e0e0' }}>
{({ defaultBlocks, defaultBlockOrder }) => ({
blocks: {
...defaultBlocks,
// 🎯 強化されたスタイリング付きカスタムヘッダー
heading: {
...defaultBlocks.heading,
props: {
...defaultBlocks.heading.props,
className: 'custom-heading',
style: {
backgroundColor: '#f0f8ff',
borderBottom: '2px solid #007bff',
padding: '20px 16px',
fontWeight: 'bold',
},
},
},
// 🔍 カスタムスタイリング付き強化された検索バー
serachBar: {
...defaultBlocks.serachBar,
props: {
...defaultBlocks.serachBar.props,
className: 'custom-search-bar',
debounceTime: 300,
style: {
backgroundColor: '#e7f3ff',
borderBottom: '1px solid #b3d9ff',
},
},
},
// 📊 会話上部のカスタム統計バー
statsBar: (
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '12px 16px',
backgroundColor: '#f8f9fa',
borderBottom: '1px solid #dee2e6',
fontSize: '14px',
color: '#6c757d',
}}>
<span>📈 {conversations.length} 会話</span>
<span>🔔 {conversations.reduce((acc, conv) => acc + (conv.unreadCount || 0), 0)} 未読</span>
</div>
),
// 💬 強化されたスタイリング付きカスタムスクロールパネル
scrollPanel: {
...defaultBlocks.scrollPanel,
props: {
...defaultBlocks.scrollPanel.props,
className: 'custom-scroll-panel',
style: {
backgroundColor: '#fafafa',
borderRadius: '8px',
maxHeight: '400px',
},
},
},
// 🎨 アクション付きカスタムフッター
footer: (
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: '16px',
backgroundColor: '#f8f9fa',
borderTop: '1px solid #dee2e6',
}}>
<button style={{
padding: '8px 16px',
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}>
➕ 新しい会話を始める
</button>
</div>
),
},
blockOrder: ['heading', 'serachBar', 'statsBar', 'scrollPanel', 'footer'],
})}
</ChatConversationList>
);
}
🔧 プロパティリファレンス
メインコンポーネントプロパティ
メインのChatConversationListコンポーネントはSpacingコンポーネントのすべてのプロパティを継承し、以下を追加します:
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
labels | {heading?: string; emptyState?: string;} | undefined | テキストラベルの設定 |
placeholders | {search?: string;} | undefined | プレースホルダーテキストの設定 |
isLoading | boolean | undefined | ローディング状態を表示 |
onNavigate | (url: string) => void | 必須 | ナビゲーションコールバック(hrefに必要) |
onScrollBottom | () => void | undefined | リストの下部に到達したときのコールバック |
search | {defaultValue?: string; onChange?: (value: string) => void;} | undefined | 検索入力の設定 |
conversations | Conversation[] | 必須 | 表示する会話オブジェクトの配列 |
className | string | undefined | スタイリング用の追加CSSクラス名 |
children | BlocksOverride | undefined | デフォルトレンダリングをオーバーライドするカスタムブロックコンポーネント |
会話オブジェクト
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
id | string | 必須 | 会話の一意識別子 |
title | string | 必須 | 会話のタイトル |
content | string | undefined | 会話本文(一行後に切り取られます) |
dateTime | string | undefined | ISO文字列としての日時(コンポーネントによってフォーマットされます) |
titleLines | 1 | 2 | 必須 | タイトルが切り取られる前の行数 |
unreadCount | number | undefined | 未読数(99を超えると99で切り取られます) |
href | string | undefined | 会話をリンク可能にし、ホバー状態を追加 |
isSelected | boolean | undefined | trueの場合、会話をハイライト |
avatar | AvatarProps | undefined | アバターの設定 |
サブコンポーネント
ChatConversationListコンポーネントは複数のサブコンポーネントを提供します。すべてのサブコンポーネントは、メインコンポーネントのコンテキストからデフォルト値を受け取り、プロパティを通じてこれらの値をオーバーライドできます。
ChatConversationList.Heading
会話リストのヘッダーをレンダリングします。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
labels | {heading?: string;} | コンテキスト値 | ラベル設定(コンテキストをオーバーライド) |
className | string | undefined | スタイリング用の追加CSSクラス名 |
children | ReactNode | デフォルトヘッダーコンテンツ | デフォルトヘッダーをオーバーライドするカスタムコンテンツ |
注意: このコンポーネントはすべてのHTML div要素プロパティを継承します。
ChatConversationList.SearchBar
デバウンス検索機能付きの検索入力をレンダリングします。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
search | {defaultValue?: string; onChange?: (value: string) => void;} | コンテキスト値 | 検索設定オブジェクト(コンテキスト値をオーバーライド) |
placeholders | {search?: string;} | コンテキスト値 | プレースホルダーテキスト設定(コンテキスト値をオーバーライド) |
conversations | Conversation[] | コンテキスト値 | 検索バーの表示を決定する会話配列(コンテキストをオーバーライド) |
debounceTime | number | 500 | 検索onChangeコールバックをトリガーする前の遅延(ミリ秒) |
className | string | undefined | 検索コンテナのスタイリング用の追加CSSクラス名 |
defaultValue | string | undefined | 検索入力フィールドの初期値 |
placeholder | string | undefined | 検索入力が空のときに表示されるプレースホルダーテキスト |
disabled | boolean | undefined | 検索入力が無効かどうか |
id | string | undefined | 検索入力のHTML id属性 |
name | string | undefined | 検索入力のHTML name属性 |
autoComplete | string | undefined | 検索入力のHTML autocomplete属性 |
autoFocus | boolean | undefined | マウント時に検索入力を自動的にフォーカスするかどうか |
ChatConversationList.ScrollPanel
無限スクロールサポート付きのスクロール可能な会話リストをレンダリングします。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
conversations | Conversation[] | コンテキスト値 | 会話配列(コンテキストをオーバーライド) |
labels | {heading?: string; emptyState?: string;} | コンテキスト値 | ラベル設定(コンテキストをオーバーライド) |
isLoading | boolean | コンテキスト値 | ローディング状態(コンテキストをオーバーライド) |
onScrollBottom | () => void | コンテキスト値 | スクロールコールバック(コンテキストをオーバーライド) |
className | string | undefined | スタイリング用の追加CSSクラス名 |
children | ReactNode | デフォルト会話アイテム | デフォルト会話レンダリングをオーバーライドするカスタムコンテンツ |
注意: このコンポーネントはすべてのHTML div要素プロパティを継承します。
🔧 TypeScriptサポート
包括的な型定義による完全なTypeScriptサポート:
import {ChatConversationList} from '@nodeblocks/frontend-chat-conversation-list-block';
import {AvatarProps} from '@basaldev/blocks-frontend-framework';
import {useState} from 'react';
interface LabelsConfig {
heading?: string;
emptyState?: string;
}
interface SearchConfig {
defaultValue?: string;
onChange?: (value: string) => void;
}
interface PlaceholdersConfig {
search?: string;
}
interface Conversation {
id: string;
title: string;
content?: string;
dateTime?: string;
titleLines: 1 | 2;
unreadCount?: number;
href?: string;
isSelected?: boolean;
avatar?: AvatarProps;
}
// 検索と無限スクロール付きの高度な会話リスト
function AdvancedChatConversationList() {
const [conversations, setConversations] = useState<Conversation[]>([
{
id: 'support-001',
title: 'カスタマーサポートチーム',
content: 'あなたの問題は解決されました。他にお手伝いできることはありますか?',
dateTime: '2025-02-01T14:30:00Z',
titleLines: 1,
unreadCount: 2,
href: '/chat/support-001',
isSelected: false,
avatar: {
avatarColor: '1',
avatarSize: 'medium',
avatarUrl: '/avatars/support-team.jpg',
},
},
{
id: 'team-002',
title: '開発チーム討議',
content: 'コードレビューが完了しました。新機能ブランチをマージする準備ができています。',
dateTime: '2025-02-01T13:15:00Z',
titleLines: 2,
unreadCount: 5,
href: '/chat/team-002',
isSelected: true,
avatar: {
avatarColor: '2',
avatarSize: 'medium',
},
},
{
id: 'client-003',
title: 'クライアントプロジェクト更新',
content: 'プロジェクトのタイムラインについて話し合うため、明日午後2時にミーティングを予定しています。',
dateTime: '2025-02-01T11:45:00Z',
titleLines: 1,
href: '/chat/client-003',
avatar: {
avatarColor: '3',
avatarSize: 'medium',
avatarUrl: '/avatars/client.jpg',
},
},
]);
const [isLoading, setIsLoading] = useState(false);
const [searchValue, setSearchValue] = useState('');
const [filteredConversations, setFilteredConversations] = useState(conversations);
const handleSearch = (value: string) => {
setSearchValue(value);
if (!value.trim()) {
setFilteredConversations(conversations);
return;
}
const filtered = conversations.filter(
(conv) =>
conv.title.toLowerCase().includes(value.toLowerCase()) ||
conv.content?.toLowerCase().includes(value.toLowerCase())
);
setFilteredConversations(filtered);
};
const handleScrollBottom = () => {
console.log('さらに会話を読み込み中...');
setIsLoading(true);
// API呼び出しをシミュレート
setTimeout(() => {
const newConversations: Conversation[] = [
{
id: `conv-${Date.now()}`,
title: '新しい会話',
content: 'これは新しく読み込まれた会話です。',
dateTime: new Date().toISOString(),
titleLines: 1,
href: `/chat/conv-${Date.now()}`,
avatar: {
avatarColor: '4',
avatarSize: 'medium',
},
},
];
setConversations(prev => [...prev, ...newConversations]);
if (!searchValue.trim()) {
setFilteredConversations(prev => [...prev, ...newConversations]);
}
setIsLoading(false);
}, 1500);
};
const handleNavigate = (url: string) => {
console.log('ナビゲート:', url);
// 選択された会話を更新
setConversations(prev =>
prev.map(conv => ({
...conv,
isSelected: conv.href === url,
}))
);
};
return (
<ChatConversationList
conversations={filteredConversations}
labels={{
heading: 'チャット会話',
emptyState: searchValue ? '検索に一致する会話がありません' : 'まだ会話がありません',
}}
placeholders={{
search: '名前またはメッセージで検索...',
}}
search={{
defaultValue: searchValue,
onChange: handleSearch,
}}
onNavigate={handleNavigate}
onScrollBottom={handleScrollBottom}
isLoading={isLoading}
className="custom-conversation-list"
style={{ maxHeight: '500px', border: '1px solid #e0e0e0' }}>
<ChatConversationList.Heading className="custom-heading" />
<ChatConversationList.SearchBar
className="custom-search-bar"
debounceTime={300}
/>
<ChatConversationList.ScrollPanel className="custom-scroll-panel" />
</ChatConversationList>
);
}
React、TypeScript、モダンWebスタンダードを使用して❤️で構築されました。