134 lines
4.7 KiB
TypeScript
134 lines
4.7 KiB
TypeScript
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<CarouselUIMode>("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);
|
||
// カルーセルでの card 中心位置 → グリッドセルの中心位置 との差分を初期オフセットに
|
||
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; // Y は変わらない
|
||
return (
|
||
<SortGridCard
|
||
key={item[0].StationNumber}
|
||
item={item}
|
||
cellW={cellW}
|
||
cellH={cellH}
|
||
startX={startX}
|
||
startY={startY}
|
||
exitX={exitX}
|
||
exitY={exitY}
|
||
startScale={origW / cellW}
|
||
isExiting={uiMode === "sort-exiting"}
|
||
exitDelay={Math.min(index * 40, 180)}
|
||
onPress={() => {
|
||
exitTargetIndexRef.current = index;
|
||
exitSortMode();
|
||
}}
|
||
/>
|
||
);
|
||
},
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
[cellW, cellH, gridGap, gridPad, origW, origH, 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,
|
||
};
|
||
}
|