Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | 16x 16x 16x 4x 1x 3x 3x 16x 3x 3x 3x 16x 3x 1x 16x 1x 16x 9x 8x 9x 9x 2x 2x 3x 3x 1x 1x 2x 2x 8x 1x 7x 16x 16x 11x 10x 10x | import { useCallback, useRef } from 'react';
import type { CellPosition, Selection, CsvAction } from './types';
interface UseSelectionProps {
dispatch: (action: CsvAction) => void;
totalRows: number;
totalCols: number;
}
export function useSelection({ dispatch, totalRows, totalCols }: UseSelectionProps) {
const isDragging = useRef(false);
const dragStart = useRef<CellPosition | null>(null);
const selectCell = useCallback((pos: CellPosition, extend = false) => {
if (extend && dragStart.current) {
dispatch({
type: 'SET_SELECTION',
selection: { start: dragStart.current, end: pos },
});
} else {
dragStart.current = pos;
dispatch({
type: 'SET_SELECTION',
selection: { start: pos, end: pos },
});
}
}, [dispatch]);
const startDrag = useCallback((pos: CellPosition) => {
isDragging.current = true;
dragStart.current = pos;
dispatch({
type: 'SET_SELECTION',
selection: { start: pos, end: pos },
});
}, [dispatch]);
const updateDrag = useCallback((pos: CellPosition) => {
if (!isDragging.current || !dragStart.current) return;
dispatch({
type: 'SET_SELECTION',
selection: { start: dragStart.current, end: pos },
});
}, [dispatch]);
const endDrag = useCallback(() => {
isDragging.current = false;
}, []);
const moveSelection = useCallback((
current: Selection | null,
direction: 'up' | 'down' | 'left' | 'right',
extend = false
) => {
if (!current) return;
const anchor = extend ? current.start : current.end;
const moving = current.end;
let newPos: CellPosition;
switch (direction) {
case 'up':
newPos = { row: Math.max(0, moving.row - 1), col: moving.col };
break;
case 'down':
newPos = { row: Math.min(totalRows - 1, moving.row + 1), col: moving.col };
break;
case 'left':
newPos = { row: moving.row, col: Math.max(0, moving.col - 1) };
break;
case 'right':
newPos = { row: moving.row, col: Math.min(totalCols - 1, moving.col + 1) };
break;
}
if (extend) {
dispatch({ type: 'SET_SELECTION', selection: { start: anchor, end: newPos } });
} else {
dispatch({ type: 'SET_SELECTION', selection: { start: newPos, end: newPos } });
}
}, [dispatch, totalRows, totalCols]);
return { selectCell, startDrag, updateDrag, endDrag, moveSelection };
}
export function getSelectionRange(selection: Selection): { minRow: number; maxRow: number; minCol: number; maxCol: number } {
return {
minRow: Math.min(selection.start.row, selection.end.row),
maxRow: Math.max(selection.start.row, selection.end.row),
minCol: Math.min(selection.start.col, selection.end.col),
maxCol: Math.max(selection.start.col, selection.end.col),
};
}
export function isCellInSelection(row: number, col: number, selection: Selection | null): boolean {
if (!selection) return false;
const { minRow, maxRow, minCol, maxCol } = getSelectionRange(selection);
return row >= minRow && row <= maxRow && col >= minCol && col <= maxCol;
}
|