ListOrderブロック
ListOrder は、MUI の Stack とカード上に構築された注文リストで、任意のチップ、ステータスバナー、もっと読み込む動作、複合子またはブロックのオーバーライドによる構成を備えています。
インストール
- npm
- yarn
- pnpm
- bun
npm install @nodeblocks/frontend-list-order-block
yarn add @nodeblocks/frontend-list-order-block
pnpm add @nodeblocks/frontend-list-order-block
bun add @nodeblocks/frontend-list-order-block
必要なもの
| 項目 | 用途 |
|---|---|
orders | レンダリングする行(id、logoUrl、title、subtitle、date、任意の chips、disabled、status) |
hasMore | LoadMoreButton がレンダリングするかどうかを判断するために使用 |
onClickOrder (任意) | デフォルトの OrderCard / OrderListSection で使用される行クリックハンドラー |
onClickLoadMore (任意) | LoadMoreButton が使用するもっと読み込むハンドラー |
isLoadingList (任意) | OrderLoadingCircle を切り替え、LoadMoreButton を無効化 |
ListOrder はリストの状態を保持しません。orders、hasMore、ローディング状態をアプリ側で管理し、ブロックに渡してください。一般的なパターンは、onClickLoadMore で次のページをスライス/取得し、行を追加して hasMore を更新することです。
コード例
- クイックスタート
- 状態とアクション
- 複合コンポーネント
- ブロックのオーバーライド
function Example() { const allOrders = Array.from({length: 12}, (_, i) => ({ id: String(i + 1), logoUrl: 'https://unsplash.it/16', title: `注文 #${i + 1}`, subtitle: `組織 ${i + 1}`, date: new Date(2024, 0, i + 1).toLocaleDateString(), chips: i % 3 === 0 ? [{label: '保留中'}] : [{label: '承認済み'}], })); const [displayedOrders, setDisplayedOrders] = React.useState(allOrders.slice(0, 5)); const [hasMore, setHasMore] = React.useState(true); const [lastAction, setLastAction] = React.useState('注文をクリックすると、ここにフィードバックが表示されます。'); const handleLoadMore = () => { const next = allOrders.slice(0, displayedOrders.length + 5); setDisplayedOrders(next); setHasMore(next.length < allOrders.length); }; return ( <> <ListOrder orders={displayedOrders} hasMore={hasMore} onClickOrder={(order) => setLastAction(`クリック: ${order.title}`)} onClickLoadMore={handleLoadMore} sx={{bgcolor: 'background.default'}} > <ListOrder.OrderListSection /> <ListOrder.OrderLoadingCircle /> <ListOrder.LoadMoreButton /> </ListOrder> <div style={{marginTop: 12, color: '#475569', fontSize: 13}}>{lastAction}</div> </> ); }
行の状態(chips、disabled、任意の status)とローディング動作をカスタマイズします。
function Example() { const [isLoadingList, setIsLoadingList] = React.useState(false); const [orders, setOrders] = React.useState([ { id: '1', logoUrl: 'https://unsplash.it/16', title: '注文 #1', subtitle: '組織 1', date: '2024年1月1日', chips: [{label: '保留中'}], status: 'Awaiting confirmation', }, { id: '2', logoUrl: 'https://unsplash.it/16', title: '注文 #2', subtitle: '組織 2', date: '2024年1月2日', chips: [{label: '拒否済み'}], disabled: true, }, ]); const [hasMore, setHasMore] = React.useState(true); const [lastAction, setLastAction] = React.useState('注文クリックのフィードバックがここに表示されます。'); const handleLoadMore = () => { setIsLoadingList(true); setTimeout(() => { setOrders((prev) => [ ...prev, { id: String(prev.length + 1), logoUrl: 'https://unsplash.it/16', title: `注文 #${prev.length + 1}`, subtitle: `組織 ${prev.length + 1}`, date: '2024年1月3日', chips: [{label: '承認済み'}], }, ]); setHasMore(false); setIsLoadingList(false); }, 600); }; return ( <> <ListOrder orders={orders} hasMore={hasMore} isLoadingList={isLoadingList} onClickOrder={(order) => setLastAction(`${order.id} を開く`)} onClickLoadMore={handleLoadMore} > <ListOrder.OrderListSection /> <ListOrder.OrderLoadingCircle /> <ListOrder.LoadMoreButton /> </ListOrder> <div style={{marginTop: 12, color: '#475569', fontSize: 13}}>{lastAction}</div> </> ); }
OrderCard は order.status が存在する場合にのみステータスバナーをレンダリングし、sm+ で表示されます(display: { xs: 'none', sm: 'block' })。
子ブロックを直接使用してセクションレイアウトを制御します(Storybook と同じパターン)。
function Example() { const allOrders = Array.from({length: 10}, (_, i) => ({ id: String(i + 1), logoUrl: 'https://unsplash.it/16', title: `注文 #${i + 1}`, subtitle: `組織 ${i + 1}`, date: new Date(2024, 0, i + 1).toLocaleDateString(), chips: i % 2 === 0 ? [{label: '保留中'}] : [{label: '承認済み'}], })); const [displayedOrders, setDisplayedOrders] = React.useState(allOrders.slice(0, 5)); const [hasMore, setHasMore] = React.useState(true); const [isLoading, setIsLoading] = React.useState(false); const handleLoadMore = () => { setIsLoading(true); setTimeout(() => { const next = allOrders.slice(0, displayedOrders.length + 5); setDisplayedOrders(next); setHasMore(next.length < allOrders.length); setIsLoading(false); }, 500); }; return ( <> <ListOrder orders={displayedOrders} hasMore={hasMore} > {isLoading ? ( <ListOrder.OrderLoadingCircle isLoadingList={isLoading} /> ) : ( <ListOrder.OrderListSection orders={displayedOrders}> {displayedOrders.map((order, index) => ( <ListOrder.OrderCard key={order.id} order={order} sx={index === 1 ? {bgcolor: '#EBF7FF'} : undefined} /> ))} </ListOrder.OrderListSection> )} <ListOrder.LoadMoreButton hasMore={hasMore} isLoadingList={isLoading} onClick={handleLoadMore} /> </ListOrder> </> ); }
関数の子を使用してカスタムブロックを先頭に追加し、デフォルトの順序を変更します。
function Example() { const allOrders = Array.from({length: 10}, (_, i) => ({ id: String(i + 1), logoUrl: 'https://unsplash.it/16', title: `注文 #${i + 1}`, subtitle: `組織 ${i + 1}`, date: new Date(2024, 0, i + 1).toLocaleDateString(), chips: i % 2 === 0 ? [{label: '保留中'}] : [{label: '承認済み'}], })); const [displayedOrders, setDisplayedOrders] = React.useState(allOrders.slice(0, 5)); const [hasMore, setHasMore] = React.useState(true); const pendingCount = displayedOrders.filter((o) => o.chips?.some((c) => c.label === '保留中')).length; const handleLoadMore = () => { const next = allOrders.slice(0, displayedOrders.length + 5); setDisplayedOrders(next); setHasMore(next.length < allOrders.length); }; return ( <ListOrder orders={displayedOrders} hasMore={hasMore} onClickLoadMore={handleLoadMore} > {({defaultBlocks, defaultBlockOrder}) => ({ blocks: { ...defaultBlocks, notificationBanner: ( <div style={{ marginBottom: 12, padding: 12, background: '#eef4ff', border: '1px solid #cddcff', borderRadius: 8, fontSize: 14, }} > 保留中の注文: {pendingCount} </div> ), }, blockOrder: ['notificationBanner', ...defaultBlockOrder], })} </ListOrder> ); }
ブロックのオーバーライドを使うタイミング
リストの上にバナーや統計情報を表示したり、順序をカスタマイズしたりする場合にオーバーライドを使用します。defaultBlockOrder は orderListSection、orderLoadingCircle、loadMoreButton、orderCard、orderLogo、orderSubtitle、orderDate、orderStatusBanner です。ルートのレンダリングでは、構成されたリストブロックは orderListSection と orderLoadingCircle です。LoadMoreButton は通常、複合子として追加します。
主要プロパティ
コアプロパティ
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
orders | Order[] | はい | - | 注文行 |
hasMore | boolean | はい | - | LoadMoreButton が自身の表示/非表示を判断するために使用するコンテキスト値 |
onClickOrder | (order: Order) => void | いいえ | undefined | 行がクリックされたときに呼び出される |
onClickLoadMore | () => void | いいえ | undefined | もっと読み込むアクションのハンドラー |
isLoadingList | boolean | いいえ | undefined | OrderLoadingCircle を表示し、LoadMoreButton を無効化する |
Order の形状:
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
id | string | はい | - | 一意の行 ID |
logoUrl | string | はい | - | 組織ロゴの URL |
title | string | はい | - | メインテキスト |
subtitle | string | はい | - | サブテキスト |
date | string | はい | - | 日付テキスト |
chips | ({ label: string } & ChipProps)[] | いいえ | undefined | 日付の上に表示する任意のチップ |
disabled | boolean | いいえ | undefined | 無効化されたカードスタイルを適用する(bgcolor: grey.75) |
status | string | いいえ | undefined | 任意のステータスバナーテキスト(sm+ のみ) |
コンテンツプロパティ
| コンポーネント | プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|---|
OrderListSection | orders | Order[] | いいえ | ルートの orders | デフォルトのリストマッピングで使用する行 |
OrderListSection | onClickOrder | (order: Order) => void | いいえ | ルートのハンドラー | 子が省略された場合の行クリックフォールバック |
OrderListSection | children | ReactNode | いいえ | orders を OrderCard にマッピング | カスタムリストコンテンツ |
OrderCard | order | Order | はい | - | 1 枚のカード用の行データ |
OrderCard | onClick | CardProps['onClick'] | いいえ | ルートの onClickOrder(order) | クリックハンドラーのオーバーライド |
OrderCard | children | ReactNode | いいえ | order から構築されたカードコンテンツ | カスタムカード本体 |
OrderLoadingCircle | isLoadingList | boolean | いいえ | ルートの isLoadingList | true の場合のみスピナーをレンダリング |
LoadMoreButton | hasMore | boolean | いいえ | ルートの hasMore | true の場合のみレンダリング |
LoadMoreButton | onClick | ButtonProps['onClick'] | いいえ | ルートの onClickLoadMore | クリックハンドラーのオーバーライド |
LoadMoreButton | isLoadingList | boolean | いいえ | ルートの isLoadingList | ローディング中にボタンを無効化 |
LoadMoreButton | children | ReactNode | いいえ | Read More | ボタンのラベル/コンテンツ |
OrderLogo | src | string | いいえ | order.logoUrl から | ロゴ画像のソース |
OrderLogo | alt | string | いいえ | undefined | 画像の alt テキスト |
OrderTitle | children | ReactNode | いいえ | order.title から | タイトルコンテンツ |
OrderSubtitle | children | ReactNode | いいえ | order.subtitle から | サブタイトルコンテンツ |
OrderDate | children | ReactNode | いいえ | order.date から | 日付コンテンツ |
OrderStatusBanner | children | ReactNode | いいえ | order.status から | ステータスコンテンツ |
OrderListSection、OrderCard、OrderLoadingCircle、LoadMoreButton、OrderLogo、OrderTitle、OrderSubtitle、OrderDate、OrderStatusBanner は ListOrder.OrderListSection などです。
レイアウトと構成プロパティ
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
children | BlocksOverride | ReactNode | いいえ | undefined | 複合 JSX の子、または blocks と blockOrder を返す関数オーバーライド |
className | string | いいえ | undefined | ルートクラス(デフォルトで nbb-list-order が適用される) |
sx | SxProps | いいえ | undefined | ルートリスト用の MUI システムスタイル |
ListOrder は StackProps(children を除く)を継承し、ルートのデフォルトは spacing={2} と p: 2 です。defaultBlockOrder は orderListSection、orderLoadingCircle、loadMoreButton、orderCard、orderLogo、orderSubtitle、orderDate、orderStatusBanner です。
デフォルト UI ブロック
| ブロック | ベース | 備考 |
|---|---|---|
ListOrder (ルート) | Stack | ルートリストコンテナ |
ListOrder.OrderListSection | Stack | デフォルトの注文リストセクション |
ListOrder.OrderCard | Card + CardActionArea + CardContent | order から構築されたカード行 |
ListOrder.OrderLogo | Box (component="img") | 円形のロゴ画像 |
ListOrder.OrderTitle | Typography | タイトル(variant="h6") |
ListOrder.OrderSubtitle | Typography | サブタイトル(variant="body1") |
ListOrder.OrderDate | Box | セカンダリカラーの日付テキスト |
ListOrder.OrderStatusBanner | Box + Typography | ステータスバナー(sm+) |
ListOrder.OrderLoadingCircle | CircularProgress | ローディングスピナー |
ListOrder.LoadMoreButton | Button | 全幅アクション、デフォルトテキスト Read More |
デフォルト OrderCard 内のチップ | Chip | order.chips のエントリを使用(label 必須) |
TypeScript
import { ListOrder, type Order } from '@nodeblocks/frontend-list-order-block';
const orders: Order[] = [
{
id: '1',
logoUrl: 'https://unsplash.it/16',
title: '注文 #1',
subtitle: '組織 1',
date: '2024年1月1日',
chips: [{label: '保留中'}],
status: 'Processing',
},
];
<ListOrder
orders={orders}
hasMore={false}
onClickOrder={() => {}}
/>;