@@ -231,3 +231,237 @@ Tailwind CSSのユーティリティクラスと`@zenkigen-inc/component-config`
231231| 日付 | 内容 | 担当者 |
232232| -------------------- | -------- | ------ |
233233| 2025-12-03 09:14 JST | 新規作成 | - |
234+
235+ # PaginationSelect コンポーネント仕様書
236+
237+ ## 目次
238+
239+ 1 . [ 概要] ( #概要 )
240+ 2 . [ インポート] ( #インポート )
241+ 3 . [ 基本的な使用方法] ( #基本的な使用方法 )
242+ 4 . [ Props] ( #props )
243+ - [ 必須プロパティ] ( #必須プロパティ )
244+ - [ オプションプロパティ] ( #オプションプロパティ )
245+ - [ 排他的プロパティグループ] ( #排他的プロパティグループ )
246+ - [ 特殊機能の詳細] ( #特殊機能の詳細 )
247+ - [ 継承プロパティ] ( #継承プロパティ )
248+ 5 . [ 状態とスタイル] ( #状態とスタイル )
249+ - [ サイズバリエーション] ( #サイズバリエーション )
250+ - [ 状態に応じたスタイル] ( #状態に応じたスタイル )
251+ - [ その他のスタイル仕様] ( #その他のスタイル仕様 )
252+ 6 . [ 使用例] ( #使用例 )
253+ - [ 基本的な使用例] ( #基本的な使用例 )
254+ - [ バリエーション例1] ( #バリエーション例1 )
255+ - [ バリエーション例2] ( #バリエーション例2 )
256+ 7 . [ アクセシビリティ] ( #アクセシビリティ )
257+ 8 . [ 技術的な詳細] ( #技術的な詳細 )
258+ - [ 実装について] ( #実装について )
259+ 9 . [ 注意事項] ( #注意事項 )
260+ 10 . [ スタイルのカスタマイズ] ( #スタイルのカスタマイズ )
261+ 11 . [ 更新履歴] ( #更新履歴 )
262+
263+ ---
264+
265+ ## 概要
266+
267+ PaginationSelectコンポーネントは、検索結果などの大量データをページング表示している場面で、現在表示しているアイテム件数の範囲と総ページ数を示しつつ、Selectによるページ直接指定と前後ボタンによるページ遷移を提供するUIである。表示領域が限られているケースでも省スペースにページ選択を集約できる。
268+
269+ ## インポート
270+
271+ ``` typescript
272+ import { PaginationSelect } from ' @zenkigen-inc/component-ui' ;
273+ ```
274+
275+ ## 基本的な使用方法
276+
277+ ``` typescript
278+ import { useState } from ' react' ;
279+ import { PaginationSelect } from ' @zenkigen-inc/component-ui' ;
280+
281+ const totalSize = 320 ;
282+
283+ export const Example = () => {
284+ const [page, setPage] = useState (1 );
285+
286+ return (
287+ < PaginationSelect
288+ totalSize = {totalSize }
289+ sizePerPage = {20 }
290+ currentPage = {page }
291+ onChange = {(value) => setPage(value )}
292+ onClickPrevButton = {() => setPage((prev ) => Math .max (1, prev - 1))}
293+ onClickNextButton = {() => setPage((prev ) => Math .min (Math .ceil (totalSize / 20 ), prev + 1 ))}
294+ / >
295+ );
296+ };
297+ ```
298+
299+ ## Props
300+
301+ ### 必須プロパティ
302+
303+ | プロパティ | 型 | 説明 |
304+ | ------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
305+ | ` totalSize ` | ` number ` | レコードの総件数。` sizePerPage ` と組み合わせて全ページ数(` pageMax ` )と表示件数の上下限を算出する。0を渡すと件数表示は0件となり、Selectは非活性化される。 |
306+ | ` sizePerPage ` | ` number ` | 1ページあたりに表示する件数。` Math.ceil(totalSize / sizePerPage) ` でページ数を求めるため、1以上の値を渡す。 |
307+ | ` currentPage ` | ` number ` | 現在表示中のページ番号(1起算)。Selectの選択状態と、前後ボタンの活性状態を決める。 |
308+ | ` onClickPrevButton ` | ` () => void ` | 前ページボタンがクリックされた際に呼ばれるハンドラ。呼び出し側で` currentPage ` の状態遷移を制御する。 |
309+ | ` onClickNextButton ` | ` () => void ` | 次ページボタンがクリックされた際に呼ばれるハンドラ。 |
310+ | ` onChange ` | ` (value: number) => void ` | Selectで任意のページ番号が選択された際に受け取るハンドラ。Selectの選択値(文字列)を数値に変換した結果が渡される。 |
311+
312+ ### オプションプロパティ
313+
314+ | プロパティ | 型 | デフォルト値 | 説明 |
315+ | --------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------ |
316+ | ` countLabel ` | ` string ` | ` '件' ` | 表示件数テキストの末尾に付与する単位ラベル。例: ` "〜件" ` 、` "results" ` 。 |
317+ | ` pageLabel ` | ` string ` | ` 'ページ' ` | ` / {pageMax} ` 表示に付与するページ単位のラベル。 |
318+ | ` optionListMaxHeight ` | ` number ` | ` 190 ` | Selectのドロップダウンリストに適用する最大高さ(px)。件数が増えた場合のスクロール量を制限できる。 |
319+
320+ ### 排他的プロパティグループ
321+
322+ なし。
323+
324+ ### 特殊機能の詳細
325+
326+ #### ページ計算と表示レンジ
327+
328+ - 総ページ数は` pageMax = Math.ceil(totalSize / sizePerPage) ` で算出する。端数は切り上げられ、最終ページは不足件数のみを表示する。
329+ - ` totalSize ` が0の場合は` pageMax ` も0となり、カウンターは` 0件 / 0ページ ` を表示する。同時にSelectが` disabled ` となり、当該状態でのみ次ページボタンも非活性となる。
330+ - 表示中件数は` minCount ` (1ページ目であれば1)と` maxCount ` (` currentPage * sizePerPage ` を上限` totalSize ` でクランプ)で求められ、` "min - max" ` 形式で描画する。1ページ目で空の場合は` 0 ` のみが描画される。
331+
332+ #### Selectオプション生成
333+
334+ - ` totalSize ` に基づいて` 1 ` 〜` pageMax ` の連番を` SelectOption ` 配列として自動生成し、` Select.Option ` で描画する。
335+ - 選択肢は常にページ番号の文字列と一致しており、` currentPage ` と同じ値のオプションが選択状態になる。
336+
337+ #### ナビゲーションボタン
338+
339+ - 前後ボタンは` IconButton ` を` variant="text" ` 、` size="small" ` で利用し、` currentPage === 1 ` で戻るボタンが、` currentPage === pageMax ` または` pageMax === 0 ` で進むボタンが` disabled ` になる。
340+ - アプリケーション側で` currentPage ` の境界チェックを行い、ボタンハンドラ内で無効なページ番号に更新しないようにする。
341+
342+ ### 継承プロパティ
343+
344+ 追加のHTML属性は公開していない。` nav ` 要素や各子要素に任意の属性を付加することはできない。
345+
346+ ## 状態とスタイル
347+
348+ ### サイズバリエーション
349+
350+ 単一サイズで提供する。
351+
352+ - 全体: ` nav.flex.items-center.gap-x-1 ` でSelectブロックとナビゲーションを横並びにする。
353+ - 件数テキスト: ` typography-label14regular text-text01 ` 。
354+ - Select: ` size="medium" ` , ` variant="outline" ` (Selectコンポーネントの既定スタイルを継承)。
355+ - ` / ページ ` 表記: ` typography-label14regular text-text02 ` 。
356+
357+ ### 状態に応じたスタイル
358+
359+ - ** 通常状態** : Selectは` variant="outline" ` 、IconButtonは` variant="text" ` で、コンポーネントテーマに定義されたテキスト色(` text-text01 ` /` text-text02 ` )を用いる。
360+ - ** ホバー状態** : Select・IconButtonともに各コンポーネント固有のhoverスタイルが適用される。
361+ - ** 選択状態** : Selectは選択中のオプションが強調表示される(Selectコンポーネントに準ずる)。
362+ - ** 無効状態** : ` pageMax === 0 ` 時のSelect、および境界に到達したIconButtonに` isDisabled ` が渡り、` disabled ` 属性と` pointer-events-none ` が付与される。
363+
364+ ### その他のスタイル仕様
365+
366+ - カウンターテキストとSelectの間隔は` gap-x-2 ` で、IconButtonとの間は` gap-x-1 ` で揃えている。
367+ - Selectのドロップダウンは` optionListMaxHeight ` でスクロール量を制御できる。未指定時は190pxで縦スクロールバーが現れる。
368+ - ナビゲーション領域は` div.flex.items-center ` で水平方向に密に配置され、左右の余白はIconButton内部の` size="small" ` 設定に依存する。
369+
370+ ## 使用例
371+
372+ ### 基本的な使用例
373+
374+ ``` typescript
375+ import { useState } from ' react' ;
376+ import { PaginationSelect } from ' @zenkigen-inc/component-ui' ;
377+
378+ const PaginatedTable = ({ totalSize }: { totalSize: number }) => {
379+ const [page, setPage] = useState (1 );
380+ const sizePerPage = 25 ;
381+ const pageMax = Math .ceil (totalSize / sizePerPage );
382+
383+ return (
384+ < PaginationSelect
385+ totalSize = {totalSize }
386+ sizePerPage = {sizePerPage }
387+ currentPage = {page }
388+ onChange = {(value) => setPage(value )}
389+ onClickPrevButton = {() => setPage((prev ) => Math .max (1, prev - 1))}
390+ onClickNextButton = {() => setPage((prev ) => Math .min (pageMax , prev + 1))}
391+ / >
392+ );
393+ };
394+ ```
395+
396+ ### バリエーション例1
397+
398+ ``` typescript
399+ import { useState } from ' react' ;
400+
401+ const CustomLabels = () => {
402+ const totalSize = 980 ;
403+ const sizePerPage = 50 ;
404+ const [page, setPage] = useState (5 );
405+ const pageMax = Math .ceil (totalSize / sizePerPage );
406+
407+ return (
408+ < PaginationSelect
409+ totalSize = {totalSize }
410+ sizePerPage = {sizePerPage }
411+ currentPage = {page }
412+ countLabel = " 件中"
413+ pageLabel = " ページ目"
414+ optionListMaxHeight = {240 }
415+ onChange = {(value) => setPage(value )}
416+ onClickPrevButton = {() => setPage((prev ) => Math .max (1, prev - 1))}
417+ onClickNextButton = {() => setPage((prev ) => Math .min (pageMax , prev + 1))}
418+ / >
419+ );
420+ };
421+ ```
422+
423+ ### バリエーション例2
424+
425+ ``` typescript
426+ // 件数がまだ確定していない検索画面の初期状態
427+ < PaginationSelect
428+ totalSize = {0 }
429+ sizePerPage = {25 }
430+ currentPage = {1 }
431+ onChange = {(value) => console.log(' ページ選択: ' , value)}
432+ onClickPrevButton = {() => {}}
433+ onClickNextButton = {() => {}}
434+ / >
435+ ```
436+
437+ ## アクセシビリティ
438+
439+ - ルート要素は` <nav aria-label="pagination"> ` として描画され、スクリーンリーダーにナビゲーション領域であることを明示する。
440+ - Selectは既存のSelectコンポーネントのアクセシビリティ仕様(キーボード操作、ロール付与、` aria-expanded ` 管理など)を継承する。
441+ - IconButtonは` button ` 要素として描画され、` disabled ` 属性が適切に付与されることでフォーカス不可となる。
442+ - ページ範囲のテキストはライブリージョンではなく静的表示のため、ページ切り替え時にユーザーへ更新を伝える場合はアプリケーション側で補助を検討する。
443+
444+ ## 技術的な詳細
445+
446+ ### 実装について
447+
448+ - ` pageMax ` は` Math.ceil ` で算出し、Selectのオプション数と次ボタンの活性条件に再利用している。
449+ - Selectの` onChange ` では取得した` SelectOption ` の` value ` (文字列)を` Number ` で変換してから` onChange ` コールバックへ渡す。
450+ - 件数テキストの下限は` totalSize ` が0のとき` 0 ` とし、それ以外は` (currentPage - 1) * sizePerPage + 1 ` で算出している。
451+ - ` IconButton ` と` Select ` は既存コンポーネントへの依存であり、個別のフォーカス・キーボード処理を本コンポーネント内では保持しない。
452+
453+ ## 注意事項
454+
455+ 1 . ` sizePerPage ` に0または負の値を渡すとページ数計算が破綻するため、1以上の整数を指定すること。
456+ 2 . ` currentPage ` はコンポーネント内部でクランプされない。状態管理側で` 1 ` 〜` Math.max(pageMax, 1) ` の範囲に制御する。
457+ 3 . ` totalSize ` の更新に伴い` pageMax ` が減少した場合、` currentPage ` が範囲外になる可能性があるためハンドラ内で再計算する。
458+
459+ ## スタイルのカスタマイズ
460+
461+ Tailwind CSSのユーティリティクラスと` @zenkigen-inc/component-config ` で定義されたデザイントークンに依存する。色(` text-text01/02 ` )、タイポグラフィ(` typography-label14regular ` )、IconButton/Selectのvariant・sizeはテーマで管理されているため、変更が必要な場合は` component-config ` や` component-theme ` を更新して一貫性を保つ。
462+
463+ ## 更新履歴
464+
465+ | 日付 | 内容 | 担当者 |
466+ | -------------------- | -------- | ------ |
467+ | 2025-12-03 09:14 JST | 新規作成 | - |
0 commit comments