From 8e1a533ea29afd2558129dfab3cd977a32a570bd Mon Sep 17 00:00:00 2001 From: Christian Vidal Wolf Date: Tue, 28 Apr 2026 09:26:48 +0200 Subject: [PATCH] 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 --- src/components/PricingView.tsx | 57 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/components/PricingView.tsx b/src/components/PricingView.tsx index 0a57b75..2118d7f 100644 --- a/src/components/PricingView.tsx +++ b/src/components/PricingView.tsx @@ -115,13 +115,15 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit, const [pinnedColumns, setPinnedColumns] = usePersistentState>('pricing-pinnedColumns', new Set(['articleNo', 'articleName'])); const [showPinPanel, setShowPinPanel] = useState(false); - // Drag-to-select state - const [selectedRows, setSelectedRows] = useState>(new Set()); + // Drag-to-check state (Excel-style drag to toggle Check Ying) + const [dragSelectedRows, setDragSelectedRows] = useState>(new Set()); const isDraggingRef = useRef(false); const dragStartIndexRef = useRef(null); - const dragModeRef = useRef<'add' | 'remove'>('add'); + const dragCheckValueRef = useRef(true); // what value to set on mouseup const lastDragIndexRef = useRef(null); const visibleDataIndexesRef = useRef([]); + 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(); 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 ( ยท Click any price cell to edit inline )}

- {selectedRows.size > 0 && ( + {dragSelectedRows.size > 0 && ( - {selectedRows.size} selected + {dragSelectedRows.size} selected