Skip to content

Commit 9a4e61a

Browse files
waleedlatif1claude
andcommitted
fix(tables): row gutter click toggles select; select-all works under sort/filter
- Move row-toggle onMouseDown from inner checkbox div to entire gutter <td> so clicking anywhere in the gutter cell toggles the row, matching boolean cell behavior. Stop propagation on the per-row run button so it doesn't double as a row toggle. - Compute maxPosition via Math.max over loaded rows instead of rows[last].position. Under sort, the last visual row's position is not the largest position, so select-all/Ctrl+A produced a range that missed rows whose position fell outside [0, lastVisualRow.position]. - Add minPosition for the same reason and use it as the select-all anchor — under filter, position 0 may not be loaded, so anchoring at 0 produced a range that visually appeared empty. - Re-derive isAllRowsSelected from "every loaded row's position is within selection range" rather than checking exact start/end equality against maxPosition, so the top-left checkbox highlight is correct under any sort/filter state. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent e14a3a5 commit 9a4e61a

1 file changed

Lines changed: 51 additions & 28 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table/table.tsx

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,24 @@ export function Table({
321321
)
322322
const hasWorkflowGroup = headerGroups.some((g) => g.kind === 'workflow')
323323

324-
const maxPosition = useMemo(() => (rows.length > 0 ? rows[rows.length - 1].position : -1), [rows])
324+
const maxPosition = useMemo(() => {
325+
if (rows.length === 0) return -1
326+
let max = rows[0].position
327+
for (let i = 1; i < rows.length; i++) {
328+
if (rows[i].position > max) max = rows[i].position
329+
}
330+
return max
331+
}, [rows])
332+
const minPosition = useMemo(() => {
333+
if (rows.length === 0) return -1
334+
let min = rows[0].position
335+
for (let i = 1; i < rows.length; i++) {
336+
if (rows[i].position < min) min = rows[i].position
337+
}
338+
return min
339+
}, [rows])
340+
const minPositionRef = useRef(minPosition)
341+
minPositionRef.current = minPosition
325342
const maxPositionRef = useRef(maxPosition)
326343
maxPositionRef.current = maxPosition
327344

@@ -405,15 +422,20 @@ export function Table({
405422
}
406423
return true
407424
}
408-
return (
409-
normalizedSelection !== null &&
410-
maxPosition >= 0 &&
411-
normalizedSelection.startRow === 0 &&
412-
normalizedSelection.endRow === maxPosition &&
413-
normalizedSelection.startCol === 0 &&
414-
normalizedSelection.endCol === displayColumns.length - 1
415-
)
416-
}, [checkedRows, normalizedSelection, maxPosition, displayColumns.length, rows])
425+
if (
426+
normalizedSelection === null ||
427+
rows.length === 0 ||
428+
normalizedSelection.startCol !== 0 ||
429+
normalizedSelection.endCol !== displayColumns.length - 1
430+
) {
431+
return false
432+
}
433+
const { startRow, endRow } = normalizedSelection
434+
for (const row of rows) {
435+
if (row.position < startRow || row.position > endRow) return false
436+
}
437+
return true
438+
}, [checkedRows, normalizedSelection, displayColumns.length, rows])
417439

418440
const isAllRowsSelectedRef = useRef(isAllRowsSelected)
419441
isAllRowsSelectedRef.current = isAllRowsSelected
@@ -798,7 +820,7 @@ export function Table({
798820
setEditingCell(null)
799821
setCheckedRows((prev) => (prev.size === 0 ? prev : EMPTY_CHECKED_ROWS))
800822
suppressFocusScrollRef.current = true
801-
setSelectionAnchor({ rowIndex: 0, colIndex: 0 })
823+
setSelectionAnchor({ rowIndex: minPositionRef.current, colIndex: 0 })
802824
setSelectionFocus({
803825
rowIndex: maxPositionRef.current,
804826
colIndex: currentCols.length - 1,
@@ -1305,7 +1327,7 @@ export function Table({
13051327
suppressFocusScrollRef.current = true
13061328
setEditingCell(null)
13071329
setCheckedRows((prev) => (prev.size === 0 ? prev : EMPTY_CHECKED_ROWS))
1308-
setSelectionAnchor({ rowIndex: 0, colIndex: 0 })
1330+
setSelectionAnchor({ rowIndex: minPositionRef.current, colIndex: 0 })
13091331
setSelectionFocus({
13101332
rowIndex: maxPositionRef.current,
13111333
colIndex: currentCols.length - 1,
@@ -2985,15 +3007,15 @@ const PositionGapRows = React.memo(
29853007
const isGapChecked = checkedRows.has(position)
29863008
return (
29873009
<tr key={`gap-${position}`}>
2988-
<td className={GAP_CHECKBOX_CLASS}>
3010+
<td
3011+
className={GAP_CHECKBOX_CLASS}
3012+
onMouseDown={(e) => {
3013+
if (e.button !== 0) return
3014+
onRowToggle(position, e.shiftKey)
3015+
}}
3016+
>
29893017
<div className='flex items-center justify-center gap-1'>
2990-
<div
2991-
className='group/checkbox flex h-[20px] w-[24px] shrink-0 items-center justify-center'
2992-
onMouseDown={(e) => {
2993-
if (e.button !== 0) return
2994-
onRowToggle(position, e.shiftKey)
2995-
}}
2996-
>
3018+
<div className='group/checkbox flex h-[20px] w-[24px] shrink-0 items-center justify-center'>
29973019
<span
29983020
className={cn(
29993021
'text-[var(--text-tertiary)] text-xs tabular-nums',
@@ -3251,15 +3273,15 @@ const DataRow = React.memo(function DataRow({
32513273

32523274
return (
32533275
<tr onContextMenu={(e) => onContextMenu(e, row)}>
3254-
<td className={cn(CELL_CHECKBOX, 'cursor-pointer')}>
3276+
<td
3277+
className={cn(CELL_CHECKBOX, 'cursor-pointer')}
3278+
onMouseDown={(e) => {
3279+
if (e.button !== 0) return
3280+
onRowToggle(rowIndex, e.shiftKey)
3281+
}}
3282+
>
32553283
<div className='flex items-center justify-center gap-1'>
3256-
<div
3257-
className='group/checkbox flex h-[20px] w-[24px] shrink-0 items-center justify-center'
3258-
onMouseDown={(e) => {
3259-
if (e.button !== 0) return
3260-
onRowToggle(rowIndex, e.shiftKey)
3261-
}}
3262-
>
3284+
<div className='group/checkbox flex h-[20px] w-[24px] shrink-0 items-center justify-center'>
32633285
<span
32643286
className={cn(
32653287
'text-[var(--text-tertiary)] text-xs tabular-nums',
@@ -3283,6 +3305,7 @@ const DataRow = React.memo(function DataRow({
32833305
aria-label={runningCount > 0 ? `Stop ${runningCount} running` : 'Run row'}
32843306
title={runningCount > 0 ? `Stop ${runningCount} running` : 'Run row'}
32853307
className='ml-auto flex h-[20px] w-[20px] shrink-0 items-center justify-center rounded text-[var(--text-primary)] transition-colors hover-hover:bg-[var(--surface-2)]'
3308+
onMouseDown={(e) => e.stopPropagation()}
32863309
onClick={() => {
32873310
if (runningCount > 0) {
32883311
onStopRow(row.id)

0 commit comments

Comments
 (0)