Skip to content

Commit 9ad8b12

Browse files
waleedlatif1claude
andcommitted
refactor(tables): phase 1 — gutter numbering from array index
Delete the PositionGapRows component, the gap-fill loop, and the GAP_CHECKBOX_CLASS / GAP_ROW_LIMIT / PositionGapRowsProps surface area. The server's recompactPositions() guarantees positions are 0..N-1 contiguous in the unfiltered view, so the phantom-row machinery has been defending against a state that essentially never happens. DataRow now receives an arrayIndex prop and renders {arrayIndex + 1} in the gutter. Selection coordinates still flow through row.position; that switches in phase 2. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent e14a3a5 commit 9ad8b12

1 file changed

Lines changed: 38 additions & 208 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: 38 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,62 +2690,40 @@ export function Table({
26902690
<TableBodySkeleton colCount={displayColCount} />
26912691
) : (
26922692
<>
2693-
{rows.map((row, index) => {
2694-
const prevPosition = index > 0 ? rows[index - 1].position : -1
2695-
const gapCount =
2696-
queryOptions.filter || queryOptions.sort
2697-
? 0
2698-
: row.position - prevPosition - 1
2699-
return (
2700-
<React.Fragment key={row.id}>
2701-
{gapCount > 0 && (
2702-
<PositionGapRows
2703-
count={gapCount}
2704-
startPosition={prevPosition + 1}
2705-
columns={displayColumns}
2706-
normalizedSelection={normalizedSelection}
2707-
checkedRows={checkedRows}
2708-
firstRowUnderHeader={prevPosition === -1}
2709-
onCellMouseDown={handleCellMouseDown}
2710-
onCellMouseEnter={handleCellMouseEnter}
2711-
onRowToggle={handleRowToggle}
2712-
/>
2713-
)}
2714-
<DataRow
2715-
row={row}
2716-
columns={displayColumns}
2717-
rowIndex={row.position}
2718-
isFirstRow={row.position === 0}
2719-
editingColumnName={
2720-
editingCell?.rowId === row.id ? editingCell.columnName : null
2721-
}
2722-
initialCharacter={
2723-
editingCell?.rowId === row.id ? initialCharacter : null
2724-
}
2725-
pendingCellValue={
2726-
pendingUpdate && pendingUpdate.rowId === row.id
2727-
? pendingUpdate.data
2728-
: null
2729-
}
2730-
normalizedSelection={normalizedSelection}
2731-
onClick={handleCellClick}
2732-
onDoubleClick={handleCellDoubleClick}
2733-
onSave={handleInlineSave}
2734-
onCancel={handleInlineCancel}
2735-
onContextMenu={handleRowContextMenu}
2736-
onCellMouseDown={handleCellMouseDown}
2737-
onCellMouseEnter={handleCellMouseEnter}
2738-
isRowChecked={checkedRows.has(row.position)}
2739-
onRowToggle={handleRowToggle}
2740-
runningCount={runningByRowId.get(row.id) ?? 0}
2741-
hasWorkflowColumns={hasWorkflowColumns}
2742-
onStopRow={handleStopRow}
2743-
onRunRow={handleRunRow}
2744-
workflowNameById={workflowNameById}
2745-
/>
2746-
</React.Fragment>
2747-
)
2748-
})}
2693+
{rows.map((row, index) => (
2694+
<DataRow
2695+
key={row.id}
2696+
row={row}
2697+
columns={displayColumns}
2698+
rowIndex={row.position}
2699+
arrayIndex={index}
2700+
isFirstRow={row.position === 0}
2701+
editingColumnName={
2702+
editingCell?.rowId === row.id ? editingCell.columnName : null
2703+
}
2704+
initialCharacter={editingCell?.rowId === row.id ? initialCharacter : null}
2705+
pendingCellValue={
2706+
pendingUpdate && pendingUpdate.rowId === row.id
2707+
? pendingUpdate.data
2708+
: null
2709+
}
2710+
normalizedSelection={normalizedSelection}
2711+
onClick={handleCellClick}
2712+
onDoubleClick={handleCellDoubleClick}
2713+
onSave={handleInlineSave}
2714+
onCancel={handleInlineCancel}
2715+
onContextMenu={handleRowContextMenu}
2716+
onCellMouseDown={handleCellMouseDown}
2717+
onCellMouseEnter={handleCellMouseEnter}
2718+
isRowChecked={checkedRows.has(row.position)}
2719+
onRowToggle={handleRowToggle}
2720+
runningCount={runningByRowId.get(row.id) ?? 0}
2721+
hasWorkflowColumns={hasWorkflowColumns}
2722+
onStopRow={handleStopRow}
2723+
onRunRow={handleRunRow}
2724+
workflowNameById={workflowNameById}
2725+
/>
2726+
))}
27492727
</>
27502728
)}
27512729
</tbody>
@@ -2947,157 +2925,6 @@ export function Table({
29472925
)
29482926
}
29492927

2950-
const GAP_ROW_LIMIT = 200
2951-
const GAP_CHECKBOX_CLASS = cn(CELL_CHECKBOX, 'cursor-pointer')
2952-
2953-
interface PositionGapRowsProps {
2954-
count: number
2955-
startPosition: number
2956-
columns: DisplayColumn[]
2957-
normalizedSelection: NormalizedSelection | null
2958-
checkedRows: Set<number>
2959-
firstRowUnderHeader?: boolean
2960-
onCellMouseDown: (rowIndex: number, colIndex: number, shiftKey: boolean) => void
2961-
onCellMouseEnter: (rowIndex: number, colIndex: number) => void
2962-
onRowToggle: (rowIndex: number, shiftKey: boolean) => void
2963-
}
2964-
2965-
const PositionGapRows = React.memo(
2966-
function PositionGapRows({
2967-
count,
2968-
startPosition,
2969-
columns,
2970-
normalizedSelection,
2971-
checkedRows,
2972-
firstRowUnderHeader = false,
2973-
onCellMouseDown,
2974-
onCellMouseEnter,
2975-
onRowToggle,
2976-
}: PositionGapRowsProps) {
2977-
const capped = Math.min(count, GAP_ROW_LIMIT)
2978-
const sel = normalizedSelection
2979-
const isMultiCell = sel !== null && (sel.startRow !== sel.endRow || sel.startCol !== sel.endCol)
2980-
2981-
return (
2982-
<>
2983-
{Array.from({ length: capped }).map((_, i) => {
2984-
const position = startPosition + i
2985-
const isGapChecked = checkedRows.has(position)
2986-
return (
2987-
<tr key={`gap-${position}`}>
2988-
<td className={GAP_CHECKBOX_CLASS}>
2989-
<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-
>
2997-
<span
2998-
className={cn(
2999-
'text-[var(--text-tertiary)] text-xs tabular-nums',
3000-
isGapChecked ? 'hidden' : 'block group-hover/checkbox:hidden'
3001-
)}
3002-
>
3003-
{position + 1}
3004-
</span>
3005-
<div
3006-
className={cn(
3007-
'items-center justify-center',
3008-
isGapChecked ? 'flex' : 'hidden group-hover/checkbox:flex'
3009-
)}
3010-
>
3011-
<Checkbox size='sm' checked={isGapChecked} className='pointer-events-none' />
3012-
</div>
3013-
</div>
3014-
</div>
3015-
</td>
3016-
{columns.map((col, colIndex) => {
3017-
const inRange =
3018-
sel !== null &&
3019-
position >= sel.startRow &&
3020-
position <= sel.endRow &&
3021-
colIndex >= sel.startCol &&
3022-
colIndex <= sel.endCol
3023-
const isAnchor =
3024-
sel !== null && position === sel.anchorRow && colIndex === sel.anchorCol
3025-
const isHighlighted = inRange || isGapChecked
3026-
3027-
const isTopEdge = inRange ? position === sel!.startRow : isGapChecked
3028-
const isBottomEdge = inRange ? position === sel!.endRow : isGapChecked
3029-
const isLeftEdge = inRange ? colIndex === sel!.startCol : colIndex === 0
3030-
const isRightEdge = inRange
3031-
? colIndex === sel!.endCol
3032-
: colIndex === columns.length - 1
3033-
const belowHeader = firstRowUnderHeader && i === 0
3034-
3035-
return (
3036-
<td
3037-
key={col.key}
3038-
data-row={position}
3039-
data-col={colIndex}
3040-
className={cn(CELL, (isHighlighted || isAnchor) && 'relative')}
3041-
onMouseDown={(e) => {
3042-
if (e.button !== 0) return
3043-
onCellMouseDown(position, colIndex, e.shiftKey)
3044-
}}
3045-
onMouseEnter={() => onCellMouseEnter(position, colIndex)}
3046-
>
3047-
{isHighlighted && (isMultiCell || isGapChecked) && (
3048-
<div
3049-
className={cn(
3050-
'-top-px -right-px -bottom-px -left-px pointer-events-none absolute z-[4]',
3051-
SELECTION_TINT_BG,
3052-
belowHeader && isTopEdge && 'top-0',
3053-
isTopEdge && 'border-t border-t-[var(--selection)]',
3054-
isBottomEdge && 'border-b border-b-[var(--selection)]',
3055-
isLeftEdge && 'border-l border-l-[var(--selection)]',
3056-
isRightEdge && 'border-r border-r-[var(--selection)]'
3057-
)}
3058-
/>
3059-
)}
3060-
{isAnchor && <div className={cn(SELECTION_OVERLAY, belowHeader && 'top-0')} />}
3061-
<div className='min-h-[20px]' />
3062-
</td>
3063-
)
3064-
})}
3065-
</tr>
3066-
)
3067-
})}
3068-
{count > GAP_ROW_LIMIT && (
3069-
<tr>
3070-
<td
3071-
colSpan={columns.length + 2}
3072-
className='border-[var(--border)] border-r border-b p-0'
3073-
style={{ height: `${(count - GAP_ROW_LIMIT) * ROW_HEIGHT_ESTIMATE}px` }}
3074-
/>
3075-
</tr>
3076-
)}
3077-
</>
3078-
)
3079-
},
3080-
(prev, next) => {
3081-
if (
3082-
prev.count !== next.count ||
3083-
prev.startPosition !== next.startPosition ||
3084-
prev.columns !== next.columns ||
3085-
prev.normalizedSelection !== next.normalizedSelection ||
3086-
prev.firstRowUnderHeader !== next.firstRowUnderHeader ||
3087-
prev.onCellMouseDown !== next.onCellMouseDown ||
3088-
prev.onCellMouseEnter !== next.onCellMouseEnter ||
3089-
prev.onRowToggle !== next.onRowToggle
3090-
) {
3091-
return false
3092-
}
3093-
const end = prev.startPosition + Math.min(prev.count, GAP_ROW_LIMIT)
3094-
for (let p = prev.startPosition; p < end; p++) {
3095-
if (prev.checkedRows.has(p) !== next.checkedRows.has(p)) return false
3096-
}
3097-
return true
3098-
}
3099-
)
3100-
31012928
const TableColGroup = React.memo(function TableColGroup({
31022929
columns,
31032930
columnWidths,
@@ -3120,6 +2947,7 @@ interface DataRowProps {
31202947
row: TableRowType
31212948
columns: DisplayColumn[]
31222949
rowIndex: number
2950+
arrayIndex: number
31232951
isFirstRow: boolean
31242952
editingColumnName: string | null
31252953
initialCharacter: string | null
@@ -3180,6 +3008,7 @@ function dataRowPropsAreEqual(prev: DataRowProps, next: DataRowProps): boolean {
31803008
prev.row !== next.row ||
31813009
prev.columns !== next.columns ||
31823010
prev.rowIndex !== next.rowIndex ||
3011+
prev.arrayIndex !== next.arrayIndex ||
31833012
prev.isFirstRow !== next.isFirstRow ||
31843013
prev.editingColumnName !== next.editingColumnName ||
31853014
prev.pendingCellValue !== next.pendingCellValue ||
@@ -3219,6 +3048,7 @@ const DataRow = React.memo(function DataRow({
32193048
row,
32203049
columns,
32213050
rowIndex,
3051+
arrayIndex,
32223052
isFirstRow,
32233053
editingColumnName,
32243054
initialCharacter,
@@ -3266,7 +3096,7 @@ const DataRow = React.memo(function DataRow({
32663096
isRowSelected ? 'hidden' : 'block group-hover/checkbox:hidden'
32673097
)}
32683098
>
3269-
{row.position + 1}
3099+
{arrayIndex + 1}
32703100
</span>
32713101
<div
32723102
className={cn(

0 commit comments

Comments
 (0)