mixvideo-v2/apps/desktop/src/hooks/useBatchSelection.ts

147 lines
3.9 KiB
TypeScript

import { useState, useCallback, useMemo } from 'react';
/**
* 批量选择Hook
* 用于管理列表项的批量选择状态
*/
export interface UseBatchSelectionOptions {
/** 是否启用批量选择模式 */
enabled?: boolean;
/** 最大选择数量限制 */
maxSelection?: number;
}
export interface UseBatchSelectionReturn {
/** 当前选中的项目ID列表 */
selectedIds: string[];
/** 是否处于批量选择模式 */
isSelectionMode: boolean;
/** 是否全选状态 */
isAllSelected: boolean;
/** 是否部分选中状态 */
isIndeterminate: boolean;
/** 选中项目数量 */
selectedCount: number;
/** 切换批量选择模式 */
toggleSelectionMode: () => void;
/** 选择/取消选择单个项目 */
toggleItem: (id: string) => void;
/** 全选/取消全选 */
toggleAll: (allIds: string[]) => void;
/** 清空选择 */
clearSelection: () => void;
/** 检查项目是否被选中 */
isSelected: (id: string) => boolean;
/** 设置选中的项目ID列表 */
setSelectedIds: (ids: string[]) => void;
}
export function useBatchSelection(
options: UseBatchSelectionOptions = {}
): UseBatchSelectionReturn {
const { enabled = true, maxSelection } = options;
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const [isSelectionMode, setIsSelectionMode] = useState(false);
// 切换批量选择模式
const toggleSelectionMode = useCallback(() => {
if (!enabled) return;
setIsSelectionMode(prev => {
const newMode = !prev;
// 退出选择模式时清空选择
if (!newMode) {
setSelectedIds([]);
}
return newMode;
});
}, [enabled]);
// 选择/取消选择单个项目
const toggleItem = useCallback((id: string) => {
if (!enabled || !isSelectionMode) return;
setSelectedIds(prev => {
const isCurrentlySelected = prev.includes(id);
if (isCurrentlySelected) {
// 取消选择
return prev.filter(selectedId => selectedId !== id);
} else {
// 选择项目
if (maxSelection && prev.length >= maxSelection) {
console.warn(`最多只能选择 ${maxSelection} 个项目`);
return prev;
}
return [...prev, id];
}
});
}, [enabled, isSelectionMode, maxSelection]);
// 全选/取消全选
const toggleAll = useCallback((allIds: string[]) => {
if (!enabled || !isSelectionMode) return;
setSelectedIds(prev => {
const isAllSelected = allIds.length > 0 && allIds.every(id => prev.includes(id));
if (isAllSelected) {
// 取消全选
return [];
} else {
// 全选(考虑最大选择数量限制)
const idsToSelect = maxSelection ? allIds.slice(0, maxSelection) : allIds;
return idsToSelect;
}
});
}, [enabled, isSelectionMode, maxSelection]);
// 清空选择
const clearSelection = useCallback(() => {
setSelectedIds([]);
}, []);
// 检查项目是否被选中
const isSelected = useCallback((id: string) => {
return selectedIds.includes(id);
}, [selectedIds]);
// 计算全选状态
const { isAllSelected, isIndeterminate } = useMemo(() => {
return {
isAllSelected: false, // 这里需要外部传入allIds才能计算
isIndeterminate: selectedIds.length > 0
};
}, [selectedIds]);
return {
selectedIds,
isSelectionMode,
isAllSelected,
isIndeterminate,
selectedCount: selectedIds.length,
toggleSelectionMode,
toggleItem,
toggleAll,
clearSelection,
isSelected,
setSelectedIds
};
}
/**
* 计算全选状态的辅助函数
*/
export function calculateSelectionState(selectedIds: string[], allIds: string[]) {
const selectedCount = selectedIds.length;
const totalCount = allIds.length;
return {
isAllSelected: totalCount > 0 && selectedCount === totalCount,
isIndeterminate: selectedCount > 0 && selectedCount < totalCount,
selectedCount,
totalCount
};
}