ListUsersブロック
ListUsers は、MUI の Table プリミティブ上に構築されたユーザーディレクトリテーブルです。ステータスフィルタリングタブ、解除可能な検索チップフィルター、ステータス変更ドロップダウンアクション、ページネーション、および複合子要素またはブロックオーバーライドによる直接的なレイアウトカスタマイズをサポートします。
インストール
- npm
- yarn
- pnpm
- bun
npm install @nodeblocks/frontend-list-users-block
yarn add @nodeblocks/frontend-list-users-block
pnpm add @nodeblocks/frontend-list-users-block
bun add @nodeblocks/frontend-list-users-block
必要なもの
| 項目 | 用途 |
|---|---|
listUsersTitle | テーブルセクションの見出しテキストまたは React ノード |
data | ListUsersRowData(id、name、createdAt、status)に一致するユーザーレコードの配列 |
labels | ヘッダー、検索プレースホルダー、行アクション、空状態をマッピングするテキストラベルオブジェクト |
ListUsers はリストの状態を保持しません。現在のページ、アクティブな検索値、検索チップ、タブフィルターなどのパラメーターをアプリケーションの状態で管理し、コールバックイベントで処理してください。
コード例
- クイックスタート
- ラベルと URL
- 複合コンポーネント
- ブロックのオーバーライド
function Example() { const [page, setPage] = React.useState(1); const users = [ { id: '1', name: '田中太郎', createdAt: '2026-01-15T10:30:00Z', status: 'active' }, { id: '2', name: '佐藤花子', createdAt: '2026-01-14T09:15:00Z', status: 'inactive' } ]; const labels = { emptyStateMessage: 'ユーザーが見つかりません', searchFieldPlaceholder: 'ユーザーを検索...', headerRow: { name: '名前', createdAt: '作成日', status: 'ステータス', }, cellData: { statusActive: 'アクティブ', statusInactive: '非アクティブ', }, }; const [lastAction, setLastAction] = React.useState('ナビゲーションのフィードバックがここに表示されます。'); return ( <div style={{ minHeight: 400 }}> {/** ナビゲーション用のインラインフィードバック */} <ListUsers listUsersTitle="ユーザーディレクトリ" labels={labels} data={users} onNavigate={(to) => setLastAction(`ナビゲート: ${to}`)} rowHref={(row) => `#users/${row.id}`} pagination={{ currentPage: page, totalPages: 1, onPageChange: setPage, }} /> <div style={{marginTop: 12, color: '#475569', fontSize: 13}}>{lastAction}</div> </div> ); }
アクティブな検索送信、フィルターチップ、ナビゲーション行 URL、有効化/無効化コールバックを接続します。
function Example() { const [searchValue, setSearchValue] = React.useState(''); const [chips, setChips] = React.useState([ { key: 'admin', label: '管理者' } ]); const [lastAction, setLastAction] = React.useState('検索またはナビゲートすると、インラインのフィードバックがここに表示されます。'); const [users, setUsers] = React.useState([ { id: '1', name: 'Michael Brown', createdAt: '2026-05-20T08:00:00Z', status: 'active', }, { id: '2', name: 'Emma Watson', createdAt: '2026-05-18T14:45:00Z', status: 'inactive', } ]); const labels = { emptyStateMessage: '条件に一致するユーザーがいません', searchFieldPlaceholder: 'ディレクトリを検索...', rowActions: { activate: 'アカウントを有効化', deactivate: 'アカウントを停止', }, headerRow: { name: '従業員', createdAt: '入社日', status: 'ライフサイクルステータス', }, cellData: { statusActive: '有効', statusInactive: '無効', }, }; const handleChipDelete = (chipToDelete) => { setChips(prev => prev.filter(c => c.key !== chipToDelete.key)); }; const handleSearchSubmit = () => { if (searchValue.trim() && !chips.some(c => c.label === searchValue.trim())) { setChips(prev => [...prev, { key: `search-${Date.now()}`, label: searchValue.trim() }]); } setSearchValue(''); }; const handleActivate = (id) => { setUsers(prev => prev.map(u => u.id === id ? { ...u, status: 'active' } : u)); setLastAction(`ユーザーを有効化しました ID: ${id}`); }; const handleDeactivate = (id) => { setUsers(prev => prev.map(u => u.id === id ? { ...u, status: 'inactive' } : u)); setLastAction(`ユーザーを停止しました ID: ${id}`); }; return ( <div style={{ minHeight: 400 }}> <ListUsers listUsersTitle="企業ディレクトリ" labels={labels} data={users} // 検索とフィルター searchValue={searchValue} onSearchFieldChange={setSearchValue} onSearchSubmit={handleSearchSubmit} searchChipsTitle="フィルター" searchChips={chips} onSearchChipDelete={handleChipDelete} // アクションハンドラー onItemActivate={handleActivate} onItemDeactivate={handleDeactivate} resolveRowAction={(row) => row.status === 'inactive' ? ['activate'] : ['deactivate']} shouldShowDropdownMenu={() => true} // 行ナビゲーション Href onNavigate={(to) => setLastAction(`ルーティング: ${to}`)} rowHref={(row) => `#profile/${row.id}`} /> <div style={{marginTop: 12, color: '#475569', fontSize: 13}}>{lastAction}</div> </div> ); }
onItemActivateとonItemDeactivateコールバックを提供します。応答として、アカウントステータスを切り替える API リクエストをトリガーし、ローカルのdataを同期してください。resolveRowActionを'activate' \| 'deactivate'リスト、またはundefinedを返すように設定し、アクションメニュー内に表示されるオプションの切り替えを制御します。
細粒度の複合子ブロックを使用してレイアウトを構成します。親コンテキストではなく、特定の要素に直接パラメーターを渡します。
function Example() { const [currentTab, setCurrentTab] = React.useState('all'); const [searchValue, setSearchValue] = React.useState(''); const [lastAction, setLastAction] = React.useState('インラインのフィードバックが下に更新されます。'); const users = [ { id: '1', name: '田中太郎', createdAt: '2026-01-15T10:30:00Z', status: 'active' } ]; const labels = { emptyStateMessage: '一致する結果がありません', searchFieldPlaceholder: 'レコードを検索...', headerRow: { name: '名前', createdAt: '作成日', status: 'ステータス', }, cellData: { statusActive: 'アクティブ', statusInactive: '非アクティブ', }, }; return ( <div style={{ minHeight: 400 }}> <ListUsers listUsersTitle="ワークスペースメンバー" labels={labels} data={users} tabs={[ { key: 'all', label: 'すべてのユーザー' }, { key: 'active', label: '有効' } ]} currentTab={currentTab} onTabChange={setCurrentTab} onNavigate={(to) => setLastAction(`ナビゲート: ${to}`)} > <ListUsers.Header sx={{ padding: '12px 24px', backgroundColor: '#fafafa', borderRadius: '6px 6px 0 0', borderBottom: '1px solid #e8e8e8', }} > <ListUsers.Title listUsersTitle="ワークスペースメンバー" /> <ListUsers.Action labels={labels} searchValue={searchValue} onSearchFieldChange={setSearchValue} onSearchSubmit={() => setLastAction(`検索: ${searchValue}`)} /> </ListUsers.Header> <ListUsers.Tabs /> <ListUsers.Content> <ListUsers.Table labels={labels} data={users} /> </ListUsers.Content> <ListUsers.Pagination /> </ListUsers> <div style={{marginTop: 12, color: '#475569', fontSize: 13}}>{lastAction}</div> </div> ); }
標準的な関数の子要素オーバーライドブロックマッピングを使用して、ブロックの順序を変更したり、通知ヘッダーを挿入したりします。
function Example() { const [currentTab, setCurrentTab] = React.useState('all'); const users = [ { id: '1', name: '田中太郎', createdAt: '2026-01-15T10:30:00Z', status: 'active' } ]; const labels = { emptyStateMessage: '空', searchFieldPlaceholder: '検索...', headerRow: { name: '名前', createdAt: '作成日', status: 'ステータス' }, cellData: { statusActive: 'アクティブ', statusInactive: '非アクティブ' }, }; return ( <div style={{ minHeight: 450 }}> <ListUsers listUsersTitle="オーバーライドされたリストレイアウト" labels={labels} data={users} tabs={[{ key: 'all', label: 'すべてのユーザー' }]} currentTab={currentTab} onTabChange={setCurrentTab} onNavigate={() => {}} > {({ defaultBlocks, defaultBlockOrder }) => ({ blocks: { ...defaultBlocks, systemAlert: ( <div style={{ background: '#f6ffed', border: '1px solid #b7eb8f', borderRadius: '6px', padding: '10px 16px', fontSize: '13px', color: '#389e0d', }} > データベース接続は安全です。ディレクトリは最新です。 </div> ), }, blockOrder: ['systemAlert', 'header', 'tabs', 'content', 'pagination'], })} </ListUsers> </div> ); }
ブロックのオーバーライドを使うタイミング
オーバーライドを使用して、ユーザーグリッドリストの上にバナー、お知らせ、またはカスタムテーブルを前置します。標準的なブロック順序は ['header', 'searchChips', 'content', 'pagination'](defaultBlockOrder)です。
主要プロパティ
コアプロパティ
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
data | ListUsersRowData[] | はい | - | ユーザーレコードオブジェクトの配列 |
isLoading | boolean | いいえ | undefined | true のとき、デフォルトのコンテンツ領域の代わりに円形ローダーをレンダリングする |
onNavigate | (to: string) => void | いいえ | undefined | 行クリックなどのクリックアクションでトリガーされるナビゲーションコールバック |
onItemActivate | (rowId: string) => void | いいえ | undefined | 「有効化」アクションドロップダウンオプションを選択したときにトリガーされるコールバック |
onItemDeactivate | (rowId: string) => void | いいえ | undefined | 「無効化」アクションドロップダウンオプションを選択したときにトリガーされるコールバック |
resolveRowAction | (row: ListUsersRowData) => ('activate' | 'deactivate')[] | undefined | いいえ | undefined | 行ごとにアクティブなアカウントオプションを評価する直接チェック |
shouldShowDropdownMenu | (row: ListUsersRowData) => boolean | いいえ | undefined | アクションドロップダウンボタンを完全に表示するかどうかを決定するチェック |
statusMatch | { inUse: string; notInUse: string } | いいえ | { inUse: 'active', notInUse: 'inactive' } | 生の row.status 値を生成されるステータス列のアクティブ/非アクティブテキストにマッピングする |
pagination | PaginationProps | いいえ | undefined | ステートフルなページネーションコントロール: { currentPage, totalPages, onPageChange, className? } |
コンテンツプロパティ
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
listUsersTitle | ReactNode | はい | - | Title サブブロック内にレンダリングされる見出しテキストまたはコンポーネント |
labels | { emptyStateMessage: string; searchFieldPlaceholder: string; rowActions?: { activate: string; deactivate: string }; headerRow: { createdAt: string; name: string; status: string }; cellData: { statusActive: string; statusInactive: string } } | はい | - | レイアウトの翻訳と文言マッピング |
rowHref | (row: ListUsersRowData) => string | いいえ | undefined | 行 href URL を生成する直接コールバック |
tabs | { key: string; label: string; isDisabled?: boolean; subtitle?: string }[] | いいえ | undefined | タブフィルターカテゴリ項目 |
currentTab | string | いいえ | undefined | 現在選択されているタブ識別子 |
onTabChange | (tab: string) => void | いいえ | undefined | カテゴリタブクリックでトリガーされるコールバック |
searchValue | string | いいえ | undefined | 検索テキスト入力の値 |
onSearchFieldChange | (value: string) => void | いいえ | undefined | 検索テキスト入力への入力時にトリガーされるコールバック |
onSearchSubmit | () => void | いいえ | undefined | 検索ボタンのクリックまたは Enter 送信時にトリガーされる |
searchChipsTitle | ReactNode | いいえ | undefined | アクティブなチップフィルターバッジの横に表示されるラベル |
searchChips | BaseTableSearchChip[] | いいえ | undefined | 解除可能な検索チップバー内にマッピングされるフィルター |
onSearchChipDelete | (chip: BaseTableSearchChip, index: number, event: SyntheticEvent) => void | いいえ | undefined | アクティブなチップの解除時にトリガーされるコールバック |
labels 構造
次のレイアウト構造に一致する labels オブジェクト内に正確な文言キーを提供します:
{
emptyStateMessage: string;
searchFieldPlaceholder: string;
rowActions?: {
activate: string;
deactivate: string;
};
headerRow: {
createdAt: string;
name: string;
status: string;
};
cellData: {
statusActive: string;
statusInactive: string;
};
}
レイアウトと構成プロパティ
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
children | BlocksOverride | ReactNode | いいえ | undefined | 複合子要素またはオーバーライド関数の子 |
className | string | いいえ | undefined | 外側コンテナに適用されるスタイリングクラス |
sx | SxProps | いいえ | undefined | 外側 Stack に渡される MUI SX スタイリングオーバーライド |
ListUsers は StackProps(children を除く)をすべて継承します。デフォルトのブロック順序は ['header', 'searchChips', 'content', 'pagination'](defaultBlockOrder)です。
デフォルト UI ブロック
| ブロック | ベース | 備考 |
|---|---|---|
ListUsers (ルート) | BaseTable | テーマコンテキストを確立する直接テーブルラッパー |
ListUsers.Header | BaseTable.Header | Title と検索 Actions を保持する Flex コンテナ |
ListUsers.Title | BaseTable.Header.Title | メイン見出しテキストをレンダリングする |
ListUsers.Action | BaseTable.Header.Actions | ユーザー検索入力コンポーネントをラップする |
ListUsers.SearchChips | BaseTable.SearchChips | アクティブな検索フィルターを保持するコンテナ |
ListUsers.Tabs | BaseTable.Tabs | 複合/オーバーライドレイアウトで利用可能。ルートの tabs からタブ定義を読み取る |
ListUsers.Content | BaseTable.Content | ローダーまたは表形式データグリッドを保持するコンテナブロック |
ListUsers.Loader | BaseTable.Content.Loader | ロード中に表示されるプログレススピナー |
ListUsers.Table | BaseTable.Content.Grid | ユーザー名、作成日、ステータスセル、アクションを表示する |
ListUsers.Pagination | BaseTable.Pagination | 下部にマッピングされる標準的なページネーションコントロール |
TypeScript
import { ListUsers } from '@nodeblocks/frontend-list-users-block';
import type { ListUsersRowData } from '@nodeblocks/frontend-list-users-block';
const users: ListUsersRowData[] = [
{
id: 'usr_001',
name: '佐藤健二',
createdAt: '2026-05-27T01:00:00Z',
status: 'active',
}
];
const labels = {
emptyStateMessage: '従業員が見つかりません',
searchFieldPlaceholder: 'データベースを検索',
rowActions: { activate: '有効化', deactivate: '停止' },
headerRow: {
name: 'メンバー',
createdAt: '参加日',
status: 'ステータス',
},
cellData: { statusActive: 'アクティブ', statusInactive: '停止中' },
};
<ListUsers
listUsersTitle="企業レジストリ"
labels={labels}
data={users}
/>;