import { useCallback, useEffect, useRef, useState } from "react"; import { AS } from "@/storageControl"; import { STORAGE_KEYS } from "@/constants"; import { useFavoriteStation } from "@/stateBox/useFavoriteStation"; import { SortGridCard } from "./SortGridCard"; import { CarouselUIMode, StationSource } from "@/types"; type SortModeConfig = { listUpStation: any[][]; setListIndex: (i: number) => void; width: number; origW: number; origH: number; cols: number; gridPad: number; gridGap: number; cellW: number; cellH: number; carouselHeight: number; stationSource: StationSource; }; /** カルーセルの並び替えモードに関わる状態・ロジックをまとめたカスタムフック */ export function useSortMode({ listUpStation, setListIndex, width, origW, origH, cols, gridPad, gridGap, cellW, cellH, carouselHeight, stationSource, }: SortModeConfig) { const { setFavoriteStation } = useFavoriteStation(); // "carousel" | "sort" | "sort-exiting" の 3 値で UI モードを管理 const [uiMode, setUiMode] = useState("carousel"); // ソート開始時のカルーセル位置を保存(setListIndex(-1) される前の値) const sortModeStartIndexRef = useRef(0); // ソート終了後に移動するインデックス(タップで上書き可、デフォルト 0) const exitTargetIndexRef = useRef(0); // carousel に戻ったら指定インデックスへ移動 useEffect(() => { if (uiMode === "carousel") { setListIndex(exitTargetIndexRef.current); } }, [uiMode]); /** 並び替えモード開始(現在のカルーセル位置を渡す) */ const startSortMode = useCallback((currentIndex: number) => { sortModeStartIndexRef.current = currentIndex; exitTargetIndexRef.current = 0; // デフォルトは先頭 setListIndex(-1); // 未選択状態にして LED を非表示 setUiMode("sort"); }, [setListIndex]); /** 退場アニメーション完了後にモードを終了 */ const exitSortMode = useCallback(() => { setUiMode("sort-exiting"); // 退場スプリングが収束するまで待ってから carousel へ setTimeout(() => { setUiMode("carousel"); }, listUpStation.length * 40 + 500); }, [listUpStation.length]); /** Sortable.Grid の renderItem(useCallback でメモ化) */ const sortGridRenderItem = useCallback( ({ item, index }: { item: any; index: number }) => { const col = index % cols; const row = Math.floor(index / cols); const carouselCardCenterX = (index - sortModeStartIndexRef.current) * width + width / 2 - gridPad; const carouselCardCenterY = carouselHeight / 2; const cellCenterX = col * (cellW + gridGap) + cellW / 2; const cellCenterY = row * (cellH + gridGap) + cellH / 2; const startX = carouselCardCenterX - cellCenterX; const startY = carouselCardCenterY - cellCenterY; const exitCarouselCardCenterX = (index - exitTargetIndexRef.current) * width + width / 2 - gridPad; const exitX = exitCarouselCardCenterX - cellCenterX; const exitY = carouselCardCenterY - cellCenterY; return ( { exitTargetIndexRef.current = index; exitSortMode(); }} /> ); }, // eslint-disable-next-line react-hooks/exhaustive-deps [cellW, cellH, gridGap, gridPad, origW, width, carouselHeight, uiMode] ); /** Sortable.Grid の onDragEnd */ const onSortDragEnd = useCallback( (newOrder: { indexToKey: string[] }) => { // お気に入りモード以外はデータを書き換えない(安全策) if (stationSource.type !== "favorite") return; const newList = newOrder.indexToKey.map( (key) => listUpStation.find((s) => s[0].StationNumber === key) ?? [] ); setFavoriteStation(newList); AS.setItem(STORAGE_KEYS.FAVORITE_STATION, JSON.stringify(newList)); }, [listUpStation, setFavoriteStation, stationSource] ); return { uiMode, startSortMode, exitSortMode, sortGridRenderItem, onSortDragEnd, }; }