/**
* 要レスキュー店舗を★画面で何件見せるか★を決める純粋関数(指示書AJ 段階1 / §4-4)。
*
* ★★なぜ画面コンポーネントの中に書かずに `lib/` へ出すのか★★:
* `vitest.config.ts` の `include` は `lib/**` と `app/**` なので、
* ★`components/` に置いたテストは走りません★(D-120 項2)。
* 「何件表示するか」は `>` と `>=` を取り違えるだけで静かに壊れる種類の判定
* (★ちょうど 5 件のときに「もっと表示」が出る★というバグは、
* 例外もテスト失敗も出ないまま運用担当だけが困る)。
* ★だから期待値表で固定できる場所に置く★(`CLAUDE.md` §6-2 / §9.4 の思想)。
*
* ★★API は件数で切らない★★(`lib/db/rescue-mapping.ts`。指示書AJ 段階1)—
* ここで絞るのは ★見せ方だけ★ で、`items` には常に全件が入っています。
* ★`items.length` がそのまま総数★なので、カードの件数表示は絞る前を使うこと。
*/
import { RESCUE_INITIAL_VISIBLE } from "@/lib/config";
export interface RescueVisibility<T> {
/** 実際に並べる行(`expanded` なら全件、そうでなければ先頭 `RESCUE_INITIAL_VISIBLE` 件) */
visible: readonly T[];
/** まだ見えていない件数(`expanded` のときは 0)。★「もっと表示(あと{n}件)」の {n}★ */
remaining: number;
/**
* 開閉ボタンを出すか。
* ★総数が初期表示件数を「超える」ときだけ★ — ★ちょうど同数なら出さない★
* (出すと押しても何も増えない)。`expanded` の有無では変わらない
* (開いたあとは「閉じる」として同じボタンを使うため)。
*/
canToggle: boolean;
}
/**
* @param items API が返した要レスキュー店舗(★全件★。並び替え済み)
* @param expanded 「もっと表示」が押された状態か(★画面のローカル state。URL に持たせない★ —
* D-131 申し送り⑤「ページ番号を URL に持たせる件」と混ざらないようにする)
*/
export function rescueVisibility<T>(
items: readonly T[],
expanded: boolean
): RescueVisibility<T> {
const canToggle = items.length > RESCUE_INITIAL_VISIBLE;
const visible = expanded || !canToggle ? items : items.slice(0, RESCUE_INITIAL_VISIBLE);
return {
visible,
remaining: items.length - visible.length,
canToggle,
};
}