mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 14:05:25 +02:00
feat: drag-to-check rows in PricingView (Excel-style)
Dragging the mouse over rows now directly toggles Check Ying on all covered rows on mouseup. The check value (on/off) is determined by the state of the first row touched, so dragging over unchecked rows marks them and vice versa. Rows light up blue during the drag to show what will be affected. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
co-authored by
claude-flow
parent
d1074f78de
commit
8e1a533ea2
@@ -115,13 +115,15 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
const [pinnedColumns, setPinnedColumns] = usePersistentState<Set<string>>('pricing-pinnedColumns', new Set(['articleNo', 'articleName']));
|
||||
const [showPinPanel, setShowPinPanel] = useState(false);
|
||||
|
||||
// Drag-to-select state
|
||||
const [selectedRows, setSelectedRows] = useState<Set<number>>(new Set());
|
||||
// Drag-to-check state (Excel-style drag to toggle Check Ying)
|
||||
const [dragSelectedRows, setDragSelectedRows] = useState<Set<number>>(new Set());
|
||||
const isDraggingRef = useRef(false);
|
||||
const dragStartIndexRef = useRef<number | null>(null);
|
||||
const dragModeRef = useRef<'add' | 'remove'>('add');
|
||||
const dragCheckValueRef = useRef<boolean>(true); // what value to set on mouseup
|
||||
const lastDragIndexRef = useRef<number | null>(null);
|
||||
const visibleDataIndexesRef = useRef<number[]>([]);
|
||||
const dataRef = useRef(data);
|
||||
dataRef.current = data;
|
||||
|
||||
const handleRowMouseDown = useCallback((e: React.MouseEvent, dataIndex: number) => {
|
||||
if ((e.target as HTMLElement).closest('input, button, a')) return;
|
||||
@@ -129,20 +131,10 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
isDraggingRef.current = true;
|
||||
dragStartIndexRef.current = dataIndex;
|
||||
lastDragIndexRef.current = dataIndex;
|
||||
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
dragModeRef.current = selectedRows.has(dataIndex) ? 'remove' : 'add';
|
||||
setSelectedRows(prev => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(dataIndex)) next.delete(dataIndex); else next.add(dataIndex);
|
||||
return next;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
dragModeRef.current = selectedRows.has(dataIndex) ? 'remove' : 'add';
|
||||
setSelectedRows(new Set([dataIndex]));
|
||||
}, [selectedRows]);
|
||||
// Toggle based on current state of the first row touched
|
||||
dragCheckValueRef.current = !dataRef.current[dataIndex]?.[COLUMNS.VALIDATED_CHECK];
|
||||
setDragSelectedRows(new Set([dataIndex]));
|
||||
}, [COLUMNS.VALIDATED_CHECK]);
|
||||
|
||||
const handleRowMouseEnter = useCallback((dataIndex: number) => {
|
||||
if (!isDraggingRef.current || dragStartIndexRef.current === null) return;
|
||||
@@ -153,23 +145,30 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
const end = dataIndex;
|
||||
const [lo, hi] = start <= end ? [start, end] : [end, start];
|
||||
|
||||
setSelectedRows(prev => {
|
||||
const next = new Set(prev);
|
||||
setDragSelectedRows(() => {
|
||||
const next = new Set<number>();
|
||||
visibleDataIndexesRef.current.forEach((idx: number) => {
|
||||
if (idx >= lo && idx <= hi) {
|
||||
if (dragModeRef.current === 'add') next.add(idx);
|
||||
else next.delete(idx);
|
||||
}
|
||||
if (idx >= lo && idx <= hi) next.add(idx);
|
||||
});
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseUp = () => { isDraggingRef.current = false; };
|
||||
const handleMouseUp = async () => {
|
||||
if (!isDraggingRef.current) return;
|
||||
isDraggingRef.current = false;
|
||||
const rows = new Set(dragSelectedRows);
|
||||
if (rows.size === 0) return;
|
||||
setDragSelectedRows(new Set());
|
||||
const targetValue = dragCheckValueRef.current;
|
||||
for (const idx of Array.from(rows)) {
|
||||
await handleToggleCheck(idx as number, targetValue);
|
||||
}
|
||||
};
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
return () => document.removeEventListener('mouseup', handleMouseUp);
|
||||
}, []);
|
||||
}, [dragSelectedRows]);
|
||||
|
||||
// Close search dropdown on click outside
|
||||
useEffect(() => {
|
||||
@@ -1900,7 +1899,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
const isValidated = !!row[COLUMNS.VALIDATED_CHECK];
|
||||
const note = String(row[COLUMNS.VALIDATED_NOTE] || '');
|
||||
|
||||
const isRowSelected = selectedRows.has(dataIndex);
|
||||
const isRowSelected = dragSelectedRows.has(dataIndex);
|
||||
|
||||
return (
|
||||
<tr
|
||||
@@ -2233,11 +2232,11 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
|
||||
<> · Click any <span className="text-blue-400">price cell</span> to edit inline</>
|
||||
)}
|
||||
</p>
|
||||
{selectedRows.size > 0 && (
|
||||
{dragSelectedRows.size > 0 && (
|
||||
<span className="flex items-center gap-1.5 text-xs text-blue-400 bg-blue-500/10 border border-blue-500/20 px-2 py-0.5 rounded mb-1">
|
||||
{selectedRows.size} selected
|
||||
{dragSelectedRows.size} selected
|
||||
<button
|
||||
onClick={() => setSelectedRows(new Set())}
|
||||
onClick={() => setDragSelectedRows(new Set())}
|
||||
className="ml-1 text-blue-300 hover:text-white transition-colors"
|
||||
title="Clear selection"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user