組織リストブロック
ListOrganizationsコンポーネントは、ReactとTypeScriptで構築された完全にカスタマイズ可能でアクセシブルな組織テーブルインターフェースです。モダンなデザインパターン、監査ステータス管理、検索機能、タブ、行アクション、ページネーションサポート、ローディング状態、および高度な組織管理アプリケーション向けの柔軟なカスタマイズオプションを備えた完全な表形式組織リスト体験を提供します。
🚀 インストール
npm install @nodeblocks/frontend-list-organization-block
📖 使用方法
import {ListOrganizations} from '@nodeblocks/frontend-list-organization-block';
- 基本的な使用方法
- 高度な使用方法
ライブエディター
function BasicListOrganizations() { const [currentTab, setCurrentTab] = useState('all'); const [isLoading, setIsLoading] = useState(false); const [searchValue, setSearchValue] = useState(''); const organizationData = [ { id: '1', createdAt: '2024-01-15T10:30:00Z', name: 'アクメ株式会社', joinDate: '2024-01-15T10:30:00Z', auditStatus: 'approved' }, { id: '2', createdAt: '2024-01-14T09:15:00Z', name: 'テック・ソリューションズ', joinDate: '2024-01-20T14:45:00Z', auditStatus: 'waiting_for_review' }, { id: '3', createdAt: '2024-01-13T16:20:00Z', name: 'グローバル・インダストリーズ', joinDate: '2024-01-18T11:30:00Z', auditStatus: 'rejected' } ]; const tabs = [ { label: 'すべての組織' }, { label: '承認済み' }, { label: '審査中' }, { label: '拒否済み' } ]; const labels = { emptyStateMessage: '組織が見つかりません', searchFieldPlaceholder: '組織を検索...', actions: { headerAction: '組織を追加', rowAction: '審査' }, headerRow: { createdAt: '作成日時', name: '組織名', joinDate: '参加日', auditStatus: 'ステータス' }, cellData: { statusApproved: '承認済み', statusRejected: '拒否済み', statusWaitingForReview: '審査中' } }; const handleSearchChange = (value) => { setSearchValue(value); console.log('検索変更:', value); }; const handleActionClick = () => { console.log('ヘッダーアクションがクリックされました'); }; const handleNavigate = (path) => { console.log('ナビゲート先:', path); }; const handleRowActionClick = (rowData) => { console.log('行アクションがクリックされました:', rowData); }; const getRowHref = (row) => `/organizations/${row.id}`; return ( <ListOrganizations listOrganizationsTitle="組織管理" labels={labels} isLoading={isLoading} onSearchFieldChange={handleSearchChange} onActionClick={handleActionClick} onNavigate={handleNavigate} onRowActionClick={handleRowActionClick} data={organizationData} rowHref={getRowHref} tabs={tabs} currentTab={currentTab} onTabChange={setCurrentTab}> <ListOrganizations.Header style={{display: 'flex', justifyContent: 'space-between'}}> <ListOrganizations.Title /> <ListOrganizations.Action /> </ListOrganizations.Header> <ListOrganizations.Content> {isLoading ? ( <ListOrganizations.Loader /> ) : ( <> <ListOrganizations.Tabs /> <ListOrganizations.Table /> </> )} </ListOrganizations.Content> </ListOrganizations> ); }
結果
Loading...
ライブエディター
function AdvancedListOrganizations() { const [currentTab, setCurrentTab] = useState('すべて'); const [isLoading, setIsLoading] = useState(false); const [searchValue, setSearchValue] = useState(''); const organizationData = [ { id: '1', createdAt: '2024-01-15T10:30:00Z', name: '株式会社テクノロジー', joinDate: '2024-01-15T10:30:00Z', auditStatus: 'approved' }, { id: '2', createdAt: '2024-01-14T09:15:00Z', name: 'グローバル・ソリューションズ', joinDate: '2024-01-20T14:45:00Z', auditStatus: 'waiting_for_review' }, { id: '3', createdAt: '2024-01-13T16:20:00Z', name: 'イノベーション株式会社', joinDate: '2024-01-18T11:30:00Z', auditStatus: 'rejected' }, { id: '4', createdAt: '2024-01-12T14:10:00Z', name: 'デジタル・エンタープライズ', joinDate: '2024-01-16T09:20:00Z', auditStatus: 'approved' }, { id: '5', createdAt: '2024-01-11T11:45:00Z', name: 'スマート・システムズ', joinDate: '2024-01-19T16:30:00Z', auditStatus: 'waiting_for_review' } ]; const tabs = [ { label: 'すべて', key: 'all' }, { label: '承認済み', key: 'approved' }, { label: '審査中', key: 'pending' }, { label: '拒否済み', key: 'rejected' } ]; const labels = { emptyStateMessage: '組織が見つかりません', searchFieldPlaceholder: '組織名で検索...', actions: { headerAction: '組織を追加', rowAction: '審査' }, headerRow: { createdAt: '作成日', name: '組織名', joinDate: '参加日', auditStatus: 'ステータス' }, cellData: { statusApproved: <span style={{color: '#27ae60', fontWeight: 'bold'}}>✅ 承認済み</span>, statusRejected: <span style={{color: '#e74c3c', fontWeight: 'bold'}}>❌ 拒否済み</span>, statusWaitingForReview: <span style={{color: '#f39c12', fontWeight: 'bold'}}>⏳ 審査中</span> } }; const handleSearchChange = (value) => { setSearchValue(value); console.log('検索変更:', value); }; const handleActionClick = () => { console.log('新しい組織を追加'); }; const handleNavigate = (path) => { console.log('ナビゲート:', path); }; const handleRowActionClick = (rowData) => { console.log('組織審査:', rowData); }; const getRowHref = (row) => `/organizations/${row.id}`; return ( <ListOrganizations listOrganizationsTitle="🏢 組織管理システム" labels={labels} isLoading={isLoading} onSearchFieldChange={handleSearchChange} onActionClick={handleActionClick} onNavigate={handleNavigate} onRowActionClick={handleRowActionClick} data={organizationData} rowHref={getRowHref} tabs={tabs} currentTab={currentTab} onTabChange={setCurrentTab} style={{ background: 'linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)', borderRadius: '24px', padding: '32px', margin: '20px', boxShadow: '0 20px 60px rgba(0,0,0,0.12)', border: '1px solid rgba(255,255,255,0.3)' }}> {({ defaultBlocks, defaultBlockOrder }) => ({ blocks: { // プロフェッショナルスタイリングで強化されたヘッダー header: { ...defaultBlocks.header, props: { ...defaultBlocks.header.props, style: { background: 'rgba(255, 255, 255, 0.8)', borderRadius: '20px', padding: '24px', marginBottom: '24px', backdropFilter: 'blur(10px)', border: '1px solid rgba(255,255,255,0.4)', boxShadow: '0 8px 32px rgba(0,0,0,0.08)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' } } }, // グラデーションスタイリングで強化されたタイトル title: { ...defaultBlocks.title, props: { ...defaultBlocks.title.props, style: { background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', fontSize: window.innerWidth <= 800 ? '28px' : '36px', fontWeight: 'bold', margin: '0' } } }, // 検索とボタンで強化されたアクションセクション action: { ...defaultBlocks.action, props: { ...defaultBlocks.action.props, children: ( <div style={{ display: 'flex', alignItems: 'center', gap: '16px', flexWrap: 'wrap' }}> {/* 強化された検索入力 */} <input type="text" placeholder={labels.searchFieldPlaceholder} onChange={(e) => handleSearchChange(e.target.value)} style={{ padding: '12px 16px', borderRadius: '12px', border: '1px solid rgba(102, 126, 234, 0.2)', background: 'rgba(255, 255, 255, 0.9)', fontSize: '14px', minWidth: '200px', transition: 'all 0.3s ease', boxShadow: '0 2px 8px rgba(0,0,0,0.05)' }} onFocus={(e) => { e.target.style.borderColor = '#667eea'; e.target.style.boxShadow = '0 4px 16px rgba(102, 126, 234, 0.2)'; }} onBlur={(e) => { e.target.style.borderColor = 'rgba(102, 126, 234, 0.2)'; e.target.style.boxShadow = '0 2px 8px rgba(0,0,0,0.05)'; }} /> {/* 強化されたアクションボタン */} <button onClick={handleActionClick} style={{ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', border: 'none', borderRadius: '12px', padding: '12px 24px', color: 'white', fontSize: '14px', fontWeight: 'bold', cursor: 'pointer', transition: 'all 0.3s ease', boxShadow: '0 4px 16px rgba(102, 126, 234, 0.3)', display: 'flex', alignItems: 'center', gap: '8px' }} onMouseOver={(e) => { e.target.style.transform = 'translateY(-2px)'; e.target.style.boxShadow = '0 8px 24px rgba(102, 126, 234, 0.4)'; }} onMouseOut={(e) => { e.target.style.transform = 'translateY(0)'; e.target.style.boxShadow = '0 4px 16px rgba(102, 126, 234, 0.3)'; }}> <span style={{ fontSize: '16px' }}>➕</span> {labels.actions.headerAction} </button> </div> ) } }, // 強化されたコンテンツコンテナ content: { ...defaultBlocks.content, props: { ...defaultBlocks.content.props, style: { background: 'rgba(255, 255, 255, 0.9)', borderRadius: '20px', padding: '24px', backdropFilter: 'blur(10px)', border: '1px solid rgba(255,255,255,0.4)', boxShadow: '0 8px 32px rgba(0,0,0,0.08)' } } }, // モダンスタイリングで強化されたタブ tabs: { ...defaultBlocks.tabs, props: { ...defaultBlocks.tabs.props, style: { marginBottom: '24px' } } }, // プロフェッショナルスタイリングで強化されたテーブル table: { ...defaultBlocks.table, props: { ...defaultBlocks.table.props, style: { borderRadius: '16px', overflow: 'hidden', boxShadow: '0 4px 20px rgba(0,0,0,0.08)' } } } }, blockOrder: defaultBlockOrder })} </ListOrganizations> ); }
結果
Loading...
🔧 プロパティリファレンス
メインコンポーネントのプロパティ
プロパティ | 型 | デフォルト | 説明 |
---|---|---|---|
listOrganizationsTitle | ReactNode | 必須 | 組織テーブルセクションのタイトル |
labels | OrganizationLabels | 必須 | テーブルヘッダー、アクション、メッセージのラベルオブジェクト |
isLoading | boolean | undefined | テーブルが現在読み込み中かどうか |
onSearchFieldChange | (value: string) => void | 必須 | 検索フィールドの値が変更されたときのコールバック関数 |
onActionClick | () => void | 必須 | ヘッダーアクションボタンのコールバック関数 |
onNavigate | (to: string) => void | 必須 | ナビゲーション用のコールバック関数 |
onRowActionClick | (rowData: ListOrganizationsRowData) => void | 必須 | 行アクションがクリックされたときのコールバック関数 |
pagination | {currentPage: number; onPageChange: (page: number) => void; totalPages: number} | undefined | ページネーション設定オブジェクト |
data | ListOrganizationsRowData[] | 必須 | 組織データオブジェクトの配列 |
rowHref | (row: ListOrganizationsRowData) => string | 必須 | 行リンクURLを生成する関数 |
tabs | Tab[] | 必須 | タブ設定オブジェクトの配列 |
currentTab | string | undefined | 現在アクティブなタブ識別子 |
onTabChange | (tab: string) => void | undefined | タブが変更されたときのコールバック関数 |
className | string | undefined | コンテナスタイリング用の追加CSSクラス名 |
children | BlocksOverride | undefined | デフォルトレンダリングをオーバーライドするカスタムブロックコンポーネント |
注意: このコンポーネントはchildren
を除くすべての標準HTML div
要素プロパティを継承します。
サブコンポーネント
ListOrganizationsコンポーネントは複数のサブコンポーネントを提供します。すべてのサブコンポーネントは、メインコンポーネントのコンテキストからデフォルト値を受け取り、プロパティを通じてこれらの値をオーバーライドできます。
ListOrganizations.Title
プロパティ | 型 | デフォルト | 説明 |
---|---|---|---|
children | ReactNode | コンテキストから | デフォルトタイトルレンダリングをオーバーライドするカスタムコンテンツ |
className | string | undefined | スタイリング用の追加CSSクラス名 |
size | enum | "3XL" | タイトルのタイポグラフィサイズ |
color | enum | "low-emphasis" | タイトルのカラーテーマ |
weight | enum | "bold" | タイトルの太さ |
ListOrganizations.Action
プロパティ | 型 | デフォルト | 説明 |
---|---|---|---|
children | ReactNode | コンテキストから | デフォルトアクションレンダリングをオーバーライドするカスタムコンテンツ |
className | string | undefined | スタイリング用の追加CSSクラス名 |
direction | enum | "row" | アクションコンポーネントのフレックス方向 |
alignItems | enum | Spacingデフォルトから | コンテナ内のアイテムの整列 |
gapSize | enum | Spacingデフォルトから | コンテナ内のアイテム間のギャップ |
ListOrganizations.Header
プロパティ | 型 | デフォルト | 説明 |
---|---|---|---|
children | ReactNode | デフォルトヘッダーレイアウト | デフォルトヘッダーレンダリングをオーバーライドするカスタムコンテンツ |
className | string | undefined | スタイリング用の追加CSSクラス名 |
注意: このコンポーネントはHTML div
要素のすべてのプロパティを継承します。
ListOrganizations.Loader
プロパティ | 型 | デフォルト | 説明 |
---|---|---|---|
children | ReactNode | 円形プログレスインジケーター | カスタムローディングインジケーターコンテンツ |
className | string | undefined | スタイリング用の追加CSSクラス名 |
direction | enum | "row" | アクションコンポーネントのフレックス方向 |
alignItems | enum | Spacingデフォルトから | コンテナ内のアイテムの整列 |
gapSize | enum | Spacingデフォルトから | コンテナ内のアイテム間のギャップ |
ListOrganizations.Content
プロパティ | 型 | デフォルト | 説明 |
---|---|---|---|
children | ReactNode | デフォルトコンテンツレイアウト | デフォルトコンテンツレンダリングをオーバーライドするカスタムコンテンツ |
className | string | undefined | スタイリング用の追加CSSクラス名 |
isLoading | boolean | コンテキストから | コンテキストからのローディング状態 |
注意: このコンポーネントはHTML div
要素のすべてのプロパティを継承します。
ListProductsTable.Tabs
プロパティ | 型 | デフォルト | 説明 |
---|---|---|---|
tabs | Tab[] | コンテキストから | タブ設定オブジェクトの配列 |
currentTab | string | コンテキストから | 現在アクティブなタブ識別子 |
onTabChange | (tab: string) => void | コンテキストから | タブ変更コールバック関数 |
tabWidth | string | "stretch" | タブの幅の動作 |
className | string | undefined | スタイリング用の追加CSSクラス名 |
ListOrganizations.Table
プロパティ | 型 | デフォルト | 説明 |
---|---|---|---|
className | string | undefined | スタイリング用の追加CSSクラス名 |
labels | OrganizationLabels | コンテキストから | テーブルヘッダーとアクションのラベル |
isLoading | boolean | コンテキストから | ローディング状態 |
data | ListOrganizationsRowData[] | コンテキストから | テーブルデータの配列 |
rowHref | (row: ListOrganizationsRowData) => string | コンテキストから | 行リンク関数 |
onNavigate | (to: string) => void | コンテキストから | ナビゲーションコールバック関数 |
onRowActionClick | (rowData: ListOrganizationsRowData) => void | コンテキストから | 行アクションコールバック関数 |
pagination | PaginationProps | コンテキストから | ページネーション設定 |
emptyState | {icon: enum, message: string} | コンテキストから | 空状態の設定 |
🔧 TypeScript サポート
包括的な型定義による完全なTypeScriptサポート:
import {ListOrganizations} from '@nodeblocks/frontend-list-organization-block';
interface Tab {
isDisabled?: boolean;
key?: string;
label: string;
subtitle?: string;
}
interface ListOrganizationsRowData {
id: string;
createdAt: string;
name: string;
joinDate: string;
auditStatus: string;
}
interface OrganizationLabels {
emptyStateMessage: string;
searchFieldPlaceholder: string;
actions: {
headerAction: ReactNode;
rowAction: ReactNode;
};
headerRow: {
createdAt: string;
name: string;
joinDate: string;
auditStatus: string;
};
cellData: {
statusApproved: ReactNode;
statusRejected: ReactNode;
statusWaitingForReview: ReactNode;
};
}
interface CustomOrganizationTableProps {
organizations: ListOrganizationsRowData[];
onSearch: (query: string) => void;
onAddOrganization: () => void;
onReviewOrganization: (org: ListOrganizationsRowData) => void;
tableTabs: Array<Tab>;
currentActiveTab: string;
onTabSwitch: (tab: string) => void;
isTableLoading: boolean;
}
const OrganizationTableComponent = ({
organizations,
onSearch,
onAddOrganization,
onReviewOrganization,
tableTabs,
currentActiveTab,
onTabSwitch,
isTableLoading,
}: CustomOrganizationTableProps) => {
const tableLabels: OrganizationLabels = {
emptyStateMessage: '利用可能な組織がありません',
searchFieldPlaceholder: '組織名で検索...',
actions: {
headerAction: '新規組織',
rowAction: '申請を審査',
},
headerRow: {
createdAt: '作成日',
name: '組織名',
joinDate: '参加日',
auditStatus: '監査ステータス',
},
cellData: {
statusApproved: <span style={{color: 'green'}}>承認済み</span>,
statusRejected: <span style={{color: 'red'}}>拒否済み</span>,
statusWaitingForReview: <span style={{color: 'orange'}}>審査中</span>,
},
};
return (
<ListOrganizations
listOrganizationsTitle="組織管理ダッシュボード"
labels={tableLabels}
isLoading={isTableLoading}
onSearchFieldChange={onSearch}
onActionClick={onAddOrganization}
onNavigate={path => console.log('ナビゲート:', path)}
onRowActionClick={onReviewOrganization}
data={organizations}
rowHref={row => `/organizations/${row.id}`}
tabs={tableTabs}
currentTab={currentActiveTab}
onTabChange={onTabSwitch}>
<ListOrganizations.Header />
<ListOrganizations.Content />
</ListOrganizations>
);
};
React、TypeScript、モダンなウェブ標準を使用して❤️で構築されました。