商品テーブルリストブロック
ListProductsTableコンポーネントは、ReactとTypeScriptで構築された完全にカスタマイズ可能でアクセシブルな商品テーブルインターフェースです。モダンなデザインパターン、ソート可能なカラム、アクションドロップダウン、タブ、公開ステータス管理、ページネーションサポート、ローディング状態、高度な商品管理アプリケーション用の柔軟なカスタマイズオプションを備えた完全な表形式商品リスティング体験を提供します。
🚀 インストール
npm install @nodeblocks/frontend-list-products-table-block@0.2.0
📖 使用法
import {ListProductsTable} from '@nodeblocks/frontend-list-products-table-block';
- 基本的な使用法
- 高度な使用法
function BasicListProductsTable() { const [currentTab, setCurrentTab] = useState('published'); const [isLoading] = useState(false); const productData = [ { id: '1', title: 'プレミアムオフィスチェア', category: '家具', createdAt: '2024-01-15T10:30:00Z', updatedAt: '2024-01-16T14:20:00Z', numberOfApplicants: '5', publication: { since: '2024-01-15T00:00:00Z', until: '2024-03-15T23:59:59Z', status: 'ACTIVE', }, }, { id: '2', title: 'ワイヤレスヘッドホン', category: '電子機器', createdAt: '2024-01-14T09:15:00Z', updatedAt: '2024-01-14T09:15:00Z', numberOfApplicants: '12', publication: { since: '2024-01-20T00:00:00Z', until: '2024-04-20T23:59:59Z', status: 'INACTIVE', }, }, ]; const tabs = [{label: '公開商品'}, {label: '下書き商品'}, {label: 'アーカイブ商品'}]; const labels = { emptyStateMessage: '商品が見つかりません', actions: { createProduct: '商品を作成' }, headerRow: { title: '商品タイトル', publicationPeriod: '公開期間', createdAt: '作成日', updatedAt: '更新日', numberOfApplicants: '申込者数' }, rowActions: { public: '公開する', private: '非公開にする', edit: '商品を編集', archive: '商品をアーカイブ', unarchive: '商品をアーカイブ解除' }, unsetData: { since: '未設定', until: '未設定', date: '公開期間なし', }, }; const handleProductPublic = (productId, title) => { console.log('商品を公開しました:', productId, title); }; const handleProductPrivate = (productId, title) => { console.log('商品を非公開にしました:', productId, title); }; const handleProductArchive = (productId, title) => { console.log('商品をアーカイブしました:', productId, title); }; const handleProductUnarchive = (productId) => { console.log('商品をアーカイブ解除しました:', productId); }; const handleNavigate = (path) => { console.log('ナビゲート先:', path); }; const getRowHref = (row) => `/products/${row.id}`; const getUpdateRowHref = (row) => `/products/${row.id}/edit`; return ( <ListProductsTable listProductsTableTitle="商品管理" labels={labels} isLoading={isLoading} onNavigate={handleNavigate} onProductPublic={handleProductPublic} onProductPrivate={handleProductPrivate} onProductArchive={handleProductArchive} onProductUnarchive={handleProductUnarchive} data={productData} rowHref={getRowHref} updateRowHref={getUpdateRowHref} tabs={tabs} currentTab={currentTab} onTabChange={setCurrentTab} createHref="#products/new" > <ListProductsTable.Header style={{display: 'flex', justifyContent: 'space-between'}}> <ListProductsTable.Title /> <ListProductsTable.Action /> </ListProductsTable.Header> <ListProductsTable.Content> {isLoading ? ( <ListProductsTable.Loader /> ) : ( <> <ListProductsTable.Tabs /> <ListProductsTable.Table /> </> )} </ListProductsTable.Content> </ListProductsTable> ); }
function AdvancedProductsTable() { const [currentTab, setCurrentTab] = useState('published'); const [isLoading] = useState(false); const productData = [ { id: '3', title: 'プレミアムノイズキャンセリングヘッドホン', category: '電子機器', createdAt: '2024-01-15T10:30:00Z', updatedAt: '2024-01-16T14:20:00Z', numberOfApplicants: '24', publication: { since: '2024-01-15T00:00:00Z', until: '2024-03-15T23:59:59Z', status: 'ACTIVE', }, }, { id: '4', title: 'MacBook Pro 16" M3 Max', category: 'コンピューティング', createdAt: '2024-01-14T09:15:00Z', updatedAt: '2024-01-14T09:15:00Z', numberOfApplicants: '45', publication: { since: '2024-01-20T00:00:00Z', until: '2024-04-20T23:59:59Z', status: 'INACTIVE', }, }, { id: '6', title: 'PlayStation 5 Pro コンソール', category: 'ゲーミング', createdAt: '2024-01-13T08:45:00Z', updatedAt: '2024-01-15T16:30:00Z', numberOfApplicants: '67', publication: { since: '2024-01-13T00:00:00Z', until: '2024-05-13T23:59:59Z', status: 'ACTIVE', }, }, ]; const tabs = [ {label: '公開中商品', key: 'published'}, {label: '下書き商品', key: 'draft'}, {label: 'アーカイブ商品', key: 'archived'}, ]; const labels = { emptyStateMessage: 'このカテゴリに商品が見つかりません', actions: { createProduct: '新しい商品を追加' }, headerRow: { title: '商品情報', publicationPeriod: '販売期間', createdAt: '作成日', updatedAt: '最終更新', numberOfApplicants: '関心数' }, rowActions: { public: '公開', private: '非公開にする', edit: '詳細を編集', archive: 'アーカイブ', unarchive: '復元' }, unsetData: { since: '開始日未定', until: '終了日未定', date: '公開期間が設定されていません', }, }; return ( <ListProductsTable listProductsTableTitle="商品ダッシュボード" labels={labels} isLoading={isLoading} onNavigate={path => console.log('ナビゲート先:', path)} onProductPublic={(id, title) => console.log('公開中:', title)} onProductPrivate={(id, title) => console.log('非公開にする:', title)} onProductArchive={(id, title) => console.log('アーカイブ中:', title)} onProductUnarchive={id => console.log('商品を復元中:', id)} data={productData} rowHref={row => `/products/${row.id}`} updateRowHref={row => `/products/${row.id}/edit`} tabs={tabs} currentTab={currentTab} onTabChange={setCurrentTab} createHref="#products/new" sx={{background: '#fafafa', borderRadius: '16px'}} > {({defaultBlocks, defaultBlockOrder}) => ({ blocks: { ...defaultBlocks, // Header with soft blue gradient header: { ...defaultBlocks.header, props: { ...defaultBlocks.header.props, sx: { background: 'linear-gradient(135deg, #dbeafe 0%, #e0f2fe 100%)', borderRadius: '14px', padding: '24px', border: '1px solid #93c5fd', marginBottom: '16px', }, }, }, // Title with blue accent title: { ...defaultBlocks.title, props: { ...defaultBlocks.title.props, sx: {color: '#1e40af', fontWeight: 700}, }, }, // Content with white background content: { ...defaultBlocks.content, props: { ...defaultBlocks.content.props, sx: { background: 'white', borderRadius: '14px', padding: '20px', border: '1px solid #e5e7eb', }, }, }, }, blockOrder: defaultBlockOrder, })} </ListProductsTable> ); }
🔧 プロパティリファレンス
メインコンポーネントのプロパティ
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
listProductsTableTitle | ReactNode | 必須 | 商品テーブルセクションのタイトル |
labels | TableLabels | 必須 | テーブルヘッダー、アクション、メッセージ用のラベルオブジェクト |
isLoading | boolean | undefined | テーブルが現在ローディング中かどうか |
onNavigate | (to: string) => void | 必須 | ナビゲーション用のコールバック関数 |
onProductPublic | (id: string, title: string) => void | 必須 | 商品が公開されたときのコールバック関数 |
onProductPrivate | (id: string, title: string) => void | 必須 | 商品が非公開にされたときのコールバック関数 |
onProductArchive | (id: string, title: string) => void | 必須 | 商品がアーカイブされたときのコールバック関数 |
onProductUnarchive | (id: string) => void | 必須 | 商品がアーカイブ解除されたときのコールバック関数 |
data | ListProductsTableRowData[] | 必須 | 商品データオブジェクトの配列 |
rowHref | (row: ListProductsTableRowData) => string | 必須 | 行リンクURLを生成する関数 |
updateRowHref | (row: ListProductsTableRowData) => string | 必須 | 編集ページURLを生成する関数 |
tabs | TabData[] | 必須 | タブ設定オブジェクトの配列 |
currentTab | string | undefined | 現在アクティブなタブラベル |
onTabChange | (tab: string) => void | undefined | タブが変更されたときのコールバック関数 |
createHref | string | 必須 | 新しい商品作成用のURL |
pagination | PaginationProps | undefined | ページネーション設定オブジェクト |
spacing | number | 3 | 子要素間のスペーシング |
className | string | undefined | コンテナスタイリング用の追加CSSクラス名 |
children | BlocksOverride | undefined | デフォルトレンダリングをオーバーライドするカスタムブロックコンポーネント |
注意: メインコンポーネントはMUI StackProps を拡張します。デフォルトスタイリングには p: 3 パディングが含まれます。
サブコンポーネント
ListProductsTableコンポーネントは、独立して使用できる複数のサブコンポーネントを提供します:
ListProductsTable.Header
タイトルとアクションボタンのコンテナ。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
children | ReactNode | デフォルトレイアウト | デフォルトヘッダーレンダリングをオーバーライドするカスタムコンテンツ |
className | string | undefined | スタイリング用の追加CSSクラス名 |
注意: このコンポーネントはMUI StackProps を拡張します。デフォルトスタイリングには direction="row" と justifyContent: 'space-between' が含まれます。
ListProductsTable.Title
タイトルタイポグラフィコンポーネント。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
listProductsTableTitle | ReactNode | コンテキストから | タイトルとして表示するコンテンツ |
variant | TypographyVariant | "h4" | MUI Typography バリアント |
component | ElementType | "h1" | レンダリングするHTML要素 |
children | ReactNode | undefined | タイトルをオーバーライドするカスタムコンテンツ |
className | string | undefined | 追加CSSクラス名 |
注意: このコンポーネントはMUI TypographyProps を拡張します。
ListProductsTable.Action
アクションボタン(商品作成など)のコンテナ。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
createHref | string | コンテキストから | 作成アクションボタンのURL |
labels | TableLabels | コンテキストから | アクションテキスト用のラベル |
onNavigate | (to: string) => void | コンテキストから | ナビゲーションコールバック |
children | ReactNode | デフォルトボタン | アクションレンダリングをオーバーライドするカスタムコンテンツ |
className | string | undefined | 追加CSSクラス名 |
注意: このコンポーネントはMUI StackProps(direction を除く)を拡張します。デフォルトスタイリングには direction="row" と alignItems: 'center' が含まれます。
ListProductsTable.Content
テーブルコンテンツエリアのコンテナ。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
isLoading | boolean | コンテキストから | ローディング状態 |
children | ReactNode | デフォルトレイアウト | レンダリングをオーバーライドするカスタムコンテンツ |
className | string | undefined | 追加CSSクラス名 |
注意: このコンポーネントはMUI BoxProps を拡張します。子要素が提供されない場合、Loader(ローディング中の場合)またはTabs + Tableをレンダリングします。
ListProductsTable.Loader
ローディングインジケーターコンポーネント。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
children | ReactNode | CircularProgress | カスタムローディングインジケーター |
className | string | undefined | 追加CSSクラス名 |
注意: このコンポーネントはMUI StackProps を拡張します。デフォルトスタイリングには alignItems: 'center' が含まれます。
ListProductsTable.Tabs
タブナビゲーションコンポーネント。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
tabs | TabData[] | コンテキストから | タブ設定オブジェクトの配列 |
currentTab | string | コンテキストから | 現在アクティブなタブラベル |
onTabChange | (tab: string) => void | コンテキストから | タブ変更コールバック関数 |
className | string | undefined | 追加CSSクラス名 |
注意: このコンポーネントはMUI TabsProps(value、onChange、variant を除く)を拡張します。内部で variant="fullWidth" を使用します。
ListProductsTable.Table
行アクションとページネーション付きのデータテーブルコンポーネント。
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
labels | TableLabels | コンテキストから | ヘッダーとアクション用のラベル |
data | ListProductsTableRowData[] | コンテキストから | テーブルデータの配列 |
rowHref | (row) => string | コンテキストから | 行リンクURLを生成する関数 |
updateRowHref | (row) => string | コンテキストから | 編集ページURLを生成する関数 |
onNavigate | (to: string) => void | コンテキストから | ナビゲーションコールバック関数 |
onProductPublic | (id, title) => void | コンテキストから | 商品が公開されたときのコールバック |
onProductPrivate | (id, title) => void | コンテキストから | 商品が非公開にされたときのコールバック |
onProductArchive | (id, title) => void | コンテキストから | 商品がアーカイブされたときのコールバック |
onProductUnarchive | (id) => void | コンテキストから | 商品がアーカイブ解除されたときのコールバック |
pagination | PaginationProps | コンテキストから | ページネーション設定 |
spacing | number | 3 | 要素間のスペーシング |
className | string | undefined | 追加CSSクラス名 |
注意: このコンポーネントはMUI StackProps を拡張します。データが空の場合、空状態を表示します。
🎨 設定例
カスタムヘッダーレイアウト:
<ListProductsTable {...props}>
<ListProductsTable.Header sx={{ bgcolor: 'primary.main', p: 2, borderRadius: 2 }}>
<ListProductsTable.Title sx={{ color: 'white' }} />
<ListProductsTable.Action />
</ListProductsTable.Header>
<ListProductsTable.Content />
</ListProductsTable>
ページネーション付き:
const pagination = {
currentPage: currentPage,
totalPages: 10,
onPageChange: page => {
setCurrentPage(page);
console.log('ページを取得中:', page);
},
};
<ListProductsTable
{...props}
pagination={pagination}
>
<ListProductsTable.Header />
<ListProductsTable.Content />
</ListProductsTable>
ブロックオーバーライドパターン:
<ListProductsTable {...props}>
{({defaultBlocks, defaultBlockOrder}) => ({
blocks: {
...defaultBlocks,
header: {
...defaultBlocks.header,
props: {
...defaultBlocks.header.props,
sx: {bgcolor: '#f5f5f5', p: 3, borderRadius: 2},
},
},
},
blockOrder: defaultBlockOrder,
})}
</ListProductsTable>
🔧 TypeScriptサポート
包括的な型定義による完全なTypeScriptサポート:
import {ListProductsTable} from '@nodeblocks/frontend-list-products-table-block';
import {useState} from 'react';
interface ListProductsTableRowData {
id: string;
title: string;
category: string;
createdAt: string;
updatedAt: string;
numberOfApplicants: string;
publication?: {
since?: string;
until?: string;
status?: string;
};
}
interface TabData {
key?: string;
label: string;
isDisabled?: boolean;
subtitle?: string;
}
interface TableLabels {
emptyStateMessage: string;
actions: {
createProduct: string;
};
headerRow: {
title: string;
publicationPeriod: string;
createdAt: string;
updatedAt: string;
numberOfApplicants: string;
};
rowActions: {
public: string;
private: string;
edit: string;
archive: string;
unarchive: string;
};
unsetData: {
since: string;
until: string;
date: string;
};
}
function TypedListProductsTableExample() {
const [currentTab, setCurrentTab] = useState('published');
const [isLoading] = useState(false);
const products: ListProductsTableRowData[] = [
{
id: '1',
title: '商品名',
category: '電子機器',
createdAt: '2024-01-15T10:30:00Z',
updatedAt: '2024-01-16T14:20:00Z',
numberOfApplicants: '5',
publication: {
since: '2024-01-15T00:00:00Z',
until: '2024-03-15T23:59:59Z',
status: 'ACTIVE',
},
},
];
const tabs: TabData[] = [
{ label: '公開済み', key: 'published' },
{ label: '下書き', key: 'draft' },
{ label: 'アーカイブ済み', key: 'archived', isDisabled: false }
];
const labels = {
emptyStateMessage: '利用可能な商品がありません',
actions: { createProduct: '新しい商品' },
headerRow: {
title: '商品名',
publicationPeriod: '公開期間',
createdAt: '作成日',
updatedAt: '更新日',
numberOfApplicants: '申込者数'
},
rowActions: {
public: '公開',
private: '非公開',
edit: '編集',
archive: 'アーカイブ',
unarchive: '復元'
},
unsetData: {
since: '未設定',
until: '未設定',
date: '公開期間なし'
}
};
return (
<ListProductsTable
listProductsTableTitle="商品ダッシュボード"
labels={labels}
isLoading={isLoading}
onNavigate={(path) => console.log('ナビゲート:', path)}
onProductPublic={(id, title) => console.log('公開:', id, title)}
onProductPrivate={(id, title) => console.log('非公開:', id, title)}
onProductArchive={(id, title) => console.log('アーカイブ:', id, title)}
onProductUnarchive={(id) => console.log('復元:', id)}
data={products}
rowHref={row => `/products/${row.id}`}
updateRowHref={row => `/products/${row.id}/edit`}
tabs={tabs}
currentTab={currentTab}
onTabChange={setCurrentTab}
createHref="#products/create"
>
<ListProductsTable.Header />
<ListProductsTable.Content />
</ListProductsTable>
);
}
📝 注意事項
- メインコンポーネントはデフォルト
spacing={3}とp: 3パディングを持つMUIStackPropsを拡張します ListProductsTable.Titleはデフォルトでvariant="h4"とcomponent="h1"を持つMUITypographyを使用しますListProductsTable.Tabsはvariant="fullWidth"を持つMUITabsを使用します - タブはコンテナを埋めるように伸びます- 日付は
luxonライブラリを使用してフォーマットされます: 公開日はyyyy/M/d、作成/更新日はyyyy/M/d HH:mm - 行ドロップダウンメニューは
publication.statusに基づいて異なるアクションを表示します:ACTIVEステータス: 「非公開にする」「編集」「アーカイブ」オプションを表示INACTIVEステータス: 「公開する」「編集」「アーカイブ」オプションを表示ARCHIVEDステータス: 「アーカイブ解除」オプションのみを表示
- 空状態は
emptyStateMessageラベルと「person」アイコンを表示します rowHrefがURLを返す場合、テーブル行全体がホバーエフェクト付きでクリック可能になります- ページネーションは
variant="outlined"とshape="rounded"を持つMUIPaginationコンポーネントを使用します
React、TypeScript、MUIを使用して❤️で構築されました。