ListInvitesブロック
ListInvites は、ページネーション、行アクション、ブロック構成を備えた招待管理テーブルです。
インストール
- npm
- yarn
- pnpm
- bun
npm install @nodeblocks/frontend-list-invites-block
yarn add @nodeblocks/frontend-list-invites-block
pnpm add @nodeblocks/frontend-list-invites-block
bun add @nodeblocks/frontend-list-invites-block
必要なもの
| 項目 | 用途 |
|---|---|
labels | ヘッダー、ステータス、空状態、アクションのコピー |
data | 行(id、name、email、status) |
listInvitesTitle | ヘッダーのページタイトル |
onClickAction | ユーザーを招待ボタンのハンドラー |
isLoading (任意) | ローディング状態を表示する |
pagination (任意) | クライアント側またはサーバー駆動のページ変更 |
rowHref + onNavigate (任意) | アプリのルーティングで行をクリック可能にする |
statusMatch (任意) | 生の row.status 値をステータスラベルにマッピングする |
shouldShowDropdownMenu (任意) | 一致する行の行アクションメニューを非表示にする |
resolveRowAction + ハンドラー (任意) | 行ごとの有効化/無効化メニューアクション |
ListInvites はテーブルやページネーションの状態を保持しません。data、pagination.currentPage、pagination.onPageChange をアプリ側で管理してください(クライアントでページごとに行をスライスするか、API からページを取得します)。行をクリック可能にするには rowHref と onNavigate をセットで渡してください。
各行は ListInvitesRowData オブジェクトです: id、name、email、status。生成されるステータス列は row.status を statusMatch(デフォルト active / inactive / pending)と比較して、表示するステータスラベルを決定します。行アクションには labels.rowActions、resolveRowAction、および対応するハンドラー(onItemActivate / onItemDeactivate)が必要です。shouldShowDropdownMenu で行ごとに非表示にできます。statusMatch は行アクションの選択には使用されません。
コード例
- クイックスタート
- ラベルと URL
- 複合コンポーネント
- ブロックのオーバーライド
function Example() { const allInviteData = [ {id: '0', name: 'User 0', email: 'user0@example.com', status: 'pending'}, {id: '1', name: 'User 1', email: 'user1@example.com', status: 'active'}, {id: '2', name: 'User 2', email: 'user2@example.com', status: 'inactive'}, {id: '3', name: 'User 3', email: 'user3@example.com', status: 'pending'}, {id: '4', name: 'User 4', email: 'user4@example.com', status: 'active'}, {id: '5', name: 'User 5', email: 'user5@example.com', status: 'inactive'}, ]; const labels = { emptyStateMessage: '招待が見つかりません', actions: {inviteUser: 'ユーザーを招待'}, headerRow: {name: '名前', email: 'メールアドレス', status: 'ステータス'}, cellData: {statusActive: 'アクティブ', statusInactive: '非アクティブ', statusPending: '保留中'}, rowActions: {activate: '招待を有効化', deactivate: '招待を無効化'}, }; const [currentPage, setCurrentPage] = React.useState(1); const [lastAction, setLastAction] = React.useState('招待アクションを選択すると、ここにフィードバックが表示されます。'); const itemsPerPage = 5; const totalPages = Math.ceil(allInviteData.length / itemsPerPage); const startIndex = (currentPage - 1) * itemsPerPage; const paginatedData = allInviteData.slice(startIndex, startIndex + itemsPerPage); return ( <> <ListInvites listInvitesTitle="招待管理" labels={labels} data={paginatedData} rowHref={row => `/invites/${row.id}`} onNavigate={path => setLastAction(`ナビゲート: ${path}`)} onClickAction={() => setLastAction('ユーザーを招待がクリックされました')} onItemActivate={id => setLastAction(`有効化: ${id}`)} onItemDeactivate={id => setLastAction(`無効化: ${id}`)} resolveRowAction={row => { if (row.id === '0') return undefined; return row.status === 'inactive' ? ['activate'] : ['deactivate']; }} pagination={{ currentPage, totalPages, onPageChange: setCurrentPage, }} /> <div style={{marginTop: 12, color: '#475569', fontSize: 13}}>{lastAction}</div> </> ); }
ラベル、タイトル、行ナビゲーション URL の動作をカスタマイズしつつ、デフォルトのレイアウトを維持します。
function Example() { const labels = { emptyStateMessage: 'チームの招待はまだありません', actions: {inviteUser: '招待を送信'}, headerRow: {name: 'メンバー', email: 'メールアドレス', status: 'ステータス'}, cellData: {statusActive: 'アクティブ', statusInactive: '非アクティブ', statusPending: '保留中'}, }; return ( <> <div style={{marginBottom: 12, color: '#475569', fontSize: 13}}>アクションボタンと行メニューを使用して、下のインラインステータスを更新します。</div> <ListInvites listInvitesTitle="チーム招待" labels={labels} data={[{id: '1', name: 'Alex', email: 'alex@example.com', status: 'active'}]} rowHref={row => `/team/invites/${row.id}`} onNavigate={() => {}} onClickAction={() => {}} /> </> ); }
isLoading={true} を設定するとローダーが表示されます。空状態を表示するには data={[]} と isLoading={false}(または isLoading を省略)を渡し、labels.emptyStateMessage を表示します。
ヘッダー、コンテンツ(ローダーまたはテーブル)、ページネーションのサブブロックを明示的に構成します。
function Example() { const allInviteData = [ {id: '0', name: 'User 0', email: 'user0@example.com', status: 'pending'}, {id: '1', name: 'User 1', email: 'user1@example.com', status: 'active'}, {id: '2', name: 'User 2', email: 'user2@example.com', status: 'inactive'}, {id: '3', name: 'User 3', email: 'user3@example.com', status: 'pending'}, {id: '4', name: 'User 4', email: 'user4@example.com', status: 'active'}, ]; const labels = { emptyStateMessage: '招待が見つかりません', actions: {inviteUser: 'ユーザーを招待'}, headerRow: {name: '名前', email: 'メールアドレス', status: 'ステータス'}, cellData: {statusActive: 'アクティブ', statusInactive: '非アクティブ', statusPending: '保留中'}, rowActions: {activate: '招待を有効化', deactivate: '招待を無効化'}, }; const [currentPage, setCurrentPage] = React.useState(1); const isLoading = false; const [lastAction, setLastAction] = React.useState('インラインアクションのフィードバックがここに表示されます。'); const itemsPerPage = 5; const totalPages = Math.ceil(allInviteData.length / itemsPerPage); const startIndex = (currentPage - 1) * itemsPerPage; const paginatedData = allInviteData.slice(startIndex, startIndex + itemsPerPage); return ( <ListInvites listInvitesTitle="招待管理" labels={labels} onClickAction={() => setLastAction('ユーザーを招待がクリックされました')} data={paginatedData} > <ListInvites.Header> <ListInvites.Title listInvitesTitle="招待管理" /> <ListInvites.Action labels={labels} onClickAction={() => setLastAction('ユーザーを招待がクリックされました')} /> </ListInvites.Header> <ListInvites.Content> {isLoading ? ( <ListInvites.Loader /> ) : ( <ListInvites.Table labels={labels} data={paginatedData} rowHref={row => `/invites/${row.id}`} onNavigate={path => setLastAction(`ナビゲート: ${path}`)} onItemActivate={id => setLastAction(`有効化: ${id}`)} onItemDeactivate={id => setLastAction(`無効化: ${id}`)} resolveRowAction={row => { if (row.id === '0') return undefined; return row.status === 'inactive' ? ['activate'] : ['deactivate']; }} /> )} </ListInvites.Content> <ListInvites.Pagination pagination={{ currentPage, totalPages, onPageChange: setCurrentPage, }} data={paginatedData} /> <div style={{marginTop: 12, color: '#475569', fontSize: 13}}>{lastAction}</div> </ListInvites> ); }
カスタムグリッドと MUI Menu の行アクションには、ListInvites.Table に columns、rowActions、rowMenu を渡します(Storybook の WithCompoundComponents を参照)。
関数の子要素を使用して、カスタムブロックを前置し、順序を制御します。
function Example() { const allInviteData = [ {id: '0', name: 'User 0', email: 'user0@example.com', status: 'pending'}, {id: '1', name: 'User 1', email: 'user1@example.com', status: 'active'}, {id: '2', name: 'User 2', email: 'user2@example.com', status: 'inactive'}, {id: '3', name: 'User 3', email: 'user3@example.com', status: 'pending'}, {id: '4', name: 'User 4', email: 'user4@example.com', status: 'active'}, ]; const labels = { emptyStateMessage: '招待が見つかりません', actions: {inviteUser: 'ユーザーを招待'}, headerRow: {name: '名前', email: 'メールアドレス', status: 'ステータス'}, cellData: {statusActive: 'アクティブ', statusInactive: '非アクティブ', statusPending: '保留中'}, }; const [currentPage, setCurrentPage] = React.useState(1); const [lastAction, setLastAction] = React.useState('招待アクションを選択すると、ここにフィードバックが表示されます。'); const itemsPerPage = 5; const totalPages = Math.ceil(allInviteData.length / itemsPerPage); const startIndex = (currentPage - 1) * itemsPerPage; const paginatedData = allInviteData.slice(startIndex, startIndex + itemsPerPage); return ( <> <ListInvites listInvitesTitle="招待管理" labels={labels} data={paginatedData} onClickAction={() => setLastAction('ユーザーを招待がクリックされました')} pagination={{ currentPage, totalPages, onPageChange: setCurrentPage, }} > {({defaultBlocks}) => ({ blocks: { ...defaultBlocks, notificationBanner: ( <div style={{ marginBottom: 16, padding: 12, background: '#eef4ff', border: '1px solid #cddcff', borderRadius: 8, fontSize: 14, }} > 対応が必要な保留中の招待があります。 </div> ), stats: ( <div style={{ marginBottom: 16, padding: 12, background: '#f5f5f5', borderRadius: 8, fontSize: 14, }} > 招待の合計: {allInviteData.length} </div> ), table: <ListInvites.Table />, pagination: <ListInvites.Pagination />, }, blockOrder: ['notificationBanner', 'stats', 'header', 'table', 'pagination'], })} </ListInvites> <div style={{marginTop: 12, color: '#475569', fontSize: 13}}>{lastAction}</div> </> ); }
ブロックのオーバーライドを使うタイミング
テーブルの上にバナーや統計情報が必要な場合、または header、content、table、pagination ブロックを明示的な順序で配置したい場合にオーバーライドを使用します。オーバーライドがない場合、レンダリング順序は header、content、pagination(defaultBlockOrder)です。デフォルトのテーブルは引き続き labels と statusMatch から列を、data から行を導出します。defaultBlocks にはスプレッド用の title、action、loader、table、pagination も含まれるため、レイアウトをカスタマイズする際に content ラッパーを明示的なテーブル/ページネーション構成に置き換えられます(Storybook と同じパターン)。
主要プロパティ
コアプロパティ
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
labels | { emptyStateMessage: string; actions: { inviteUser: string }; headerRow: { name: string; email: string; status: string }; cellData: { statusActive: string; statusInactive: string; statusPending: string }; rowActions?: { activate: string; deactivate: string } } | はい | - | ヘッダー、ステータス、空状態、アクションの UI コピー |
data | ListInvitesRowData[] | はい | - | テーブル行(id、name、email、status) |
listInvitesTitle | ReactNode | はい | - | ヘッダータイトル |
onClickAction | () => void | はい | - | ユーザーを招待ボタンのハンドラー |
isLoading | boolean | いいえ | undefined | true のときローディング状態を表示する |
pagination | { currentPage: number; totalPages: number; onPageChange: (page: number) => void; className?: string } | いいえ | undefined | ページコントロール。ページは 1 から数える |
rowHref | (row: ListInvitesRowData) => string | いいえ | undefined | 行リンク先を構築する。onNavigate とセットで行をクリック可能にする |
onNavigate | (to: string) => void | いいえ | undefined | クリック可能な行がアクティブ化されたときに呼ばれる |
shouldShowDropdownMenu | (row: ListInvitesRowData) => boolean | いいえ | undefined | false を返すと、その行の行アクションメニュー項目を非表示にする |
resolveRowAction | (row: ListInvitesRowData) => ('activate' | 'deactivate')[] | undefined | いいえ | undefined | 表示する行アクションを選択する |
statusMatch | { active: string; inactive: string; pending: string } | いいえ | { active: 'active', inactive: 'inactive', pending: 'pending' } | 生の row.status 値を生成されるステータスラベルにマッピングする |
onItemActivate | (id: string) => void | いいえ | undefined | activate 行アクションを処理する |
onItemDeactivate | (id: string) => void | いいえ | undefined | deactivate 行アクションを処理する |
行アクションには labels.rowActions と resolveRowAction が必要です。ハンドラーが欠けている場合、そのアクションはメニューから省略されます。
コンテンツプロパティ
labels キー(rowActions を除くルートの labels オブジェクト上のすべて必須):
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
labels.emptyStateMessage | string | はい | - | 空のテーブルのメッセージ |
labels.actions.inviteUser | string | はい | - | ヘッダーボタンのラベル |
labels.headerRow.name | string | はい | - | 名列のヘッダー |
labels.headerRow.email | string | はい | - | メール列のヘッダー |
labels.headerRow.status | string | はい | - | ステータス列のヘッダー |
labels.cellData.statusActive | string | はい | - | アクティブステータスのラベル |
labels.cellData.statusInactive | string | はい | - | 非アクティブステータスのラベル |
labels.cellData.statusPending | string | はい | - | 保留中ステータスのラベル |
labels.rowActions.activate | string | いいえ | undefined | 有効化アクションの行メニューラベル |
labels.rowActions.deactivate | string | いいえ | undefined | 無効化アクションの行メニューラベル |
サブコンポーネント(複合レイアウト):
| コンポーネント | プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|---|
Title | children | ReactNode | いいえ | ルートの listInvitesTitle | カスタムタイトルマークアップ |
Title | listInvitesTitle | ReactNode | いいえ | ルートの listInvitesTitle | children が提供されない場合のタイトルテキスト |
Action | children | ReactNode | いいえ | ルートの labels.actions.inviteUser と onClickAction からの招待ボタン | カスタムアクションマークアップ |
Action | labels | { actions: { inviteUser: string } } | いいえ | ルートの labels | ボタンラベルのソース |
Action | onClickAction | () => void | いいえ | ルートのハンドラー | 招待ボタンのクリックハンドラー |
Header | children | ReactNode | いいえ | undefined | ヘッダーラッパーのコンテンツ |
Loader | children | ReactNode | いいえ | CircularProgress | ローディングインジケーター |
Content | children | ReactNode | いいえ | undefined | ローダーおよび/またはテーブルをラップする |
Content | isLoading | boolean | いいえ | undefined | コンテンツラッパーにローディング状態を転送する |
Table | labels | { emptyStateMessage: string; actions: { inviteUser: string }; headerRow: { name: string; email: string; status: string }; cellData: { statusActive: string; statusInactive: string; statusPending: string }; rowActions?: { activate: string; deactivate: string } } | いいえ | ルートの labels | デフォルト列と行アクションを生成する |
Table | data | ListInvitesRowData[] | いいえ | ルートの data | レンダリングする行 |
Table | rowHref | (row: ListInvitesRowData) => string | いいえ | ルートの rowHref | 行リンク先 |
Table | onNavigate | (to: string) => void | いいえ | ルートの onNavigate | 行ナビゲーションハンドラー |
Table | onItemActivate | (id: string) => void | いいえ | ルートの onItemActivate | activate 行アクションを処理する |
Table | onItemDeactivate | (id: string) => void | いいえ | ルートの onItemDeactivate | deactivate 行アクションを処理する |
Table | shouldShowDropdownMenu | (row: ListInvitesRowData) => boolean | いいえ | ルートの shouldShowDropdownMenu | 一致する行の行メニューを非表示にする |
Table | resolveRowAction | (row: ListInvitesRowData) => ('activate' | 'deactivate')[] | undefined | いいえ | ルートの resolveRowAction | 行メニューアクションを選択する |
Table | statusMatch | { active: string; inactive: string; pending: string } | いいえ | ルートの statusMatch | カスタムステータスマッピング |
Pagination | pagination | { currentPage: number; totalPages: number; onPageChange: (page: number) => void; className?: string } | いいえ | ルートの pagination | ページコントロール |
Pagination | data | ListInvitesRowData[] | いいえ | ルートの data | ページネーション用の行数 |
Pagination | isLoading | boolean | いいえ | ルートの isLoading | ローディング中はページネーションを非表示にする |
Title、Action、Header、Loader、Content、Table、Pagination は ListInvites.Title などです。完全にカスタムなグリッド動作が必要な場合、ListInvites.Table はカスタムの columns、rowActions、rowMenu、actionColumn、onRowClick も受け取ります。
レイアウトと構成プロパティ
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
children | BlocksOverride | ReactNode | いいえ | undefined | 複合 JSX の子、または blocks と blockOrder を返す関数オーバーライド |
className | string | いいえ | undefined | ルートコンテナ(nbb-list-invites-container)のクラス |
sx | SxProps | いいえ | undefined | ルート用の MUI システムスタイル |
ListInvites は StackProps(children を除く)を継承します。オーバーライドがない場合のレンダリング順序は header、content、pagination です。defaultBlockOrder は header、content、pagination(defaultBlocks キー: title、action、header、loader、content、table、pagination)。
デフォルト UI ブロック
| ブロック | ベース | 備考 |
|---|---|---|
ListInvites (ルート) | Stack | テーブルシェル。header/content/pagination をレンダリング。空状態は PersonOutlined を使用 |
ListInvites.Title | Typography | listInvitesTitle からのタイトルテキスト |
ListInvites.Action | Button | PeopleOutlineOutlined アイコン付きのユーザーを招待アクション |
ListInvites.Header | Stack | タイトルとアクション用のヘッダーラッパー |
ListInvites.Loader | CircularProgress | ローディング状態 |
ListInvites.Content | Box | ローダー、空状態、またはグリッド用のコンテンツラッパー |
ListInvites.Table | Stack + TableContainer + Table | データグリッド。カスタム列、行アクション、行メニュー、行クリック動作をサポート |
ListInvites.Pagination | Stack + Pagination | ページコントロール |
TypeScript
import { ListInvites, type ListInvitesRowData } from '@nodeblocks/frontend-list-invites-block';
const labels = {
emptyStateMessage: '招待が見つかりません',
actions: {inviteUser: 'ユーザーを招待'},
headerRow: {name: '名前', email: 'メールアドレス', status: 'ステータス'},
cellData: {statusActive: 'アクティブ', statusInactive: '非アクティブ', statusPending: '保留中'},
rowActions: {activate: '招待を有効化', deactivate: '招待を無効化'},
};
const rows: ListInvitesRowData[] = [{id: '1', name: 'Alex', email: 'alex@example.com', status: 'pending'}];
<ListInvites
listInvitesTitle="招待管理"
labels={labels}
data={rows}
onClickAction={() => {}}
rowHref={row => `/invites/${row.id}`}
onNavigate={(path) => window.location.assign(path)}
/>;