@@ -36,7 +36,6 @@ const rows = defineModel<SQLTableRowDraft[]>('rows', { required: true })
3636const toast = useToast ()
3737const gridContainer = useTemplateRef <HTMLDivElement >(' gridContainer' )
3838const tableHead = useTemplateRef <HTMLTableSectionElement >(' tableHead' )
39- const rowHeaderCell = useTemplateRef <HTMLTableCellElement >(' rowHeaderCell' )
4039const editingEditor = useTemplateRef <InstanceType <typeof SQLTableCellEditor >>(' editingEditor' )
4140const contextMenu = useTemplateRef <ContextMenuMethods >(' contextMenu' )
4241
@@ -52,7 +51,6 @@ const dragPointer = ref<{ x: number; y: number } | null>(null)
5251const scrollTop = ref (0 )
5352const viewportHeight = ref (0 )
5453const headerHeight = ref (0 )
55- const rowHeaderWidth = ref (48 )
5654const rowHeight = ref (DEFAULT_ROW_HEIGHT )
5755
5856let dragScrollFrame = 0
@@ -173,6 +171,10 @@ const virtualPaddingTop = computed(() => visibleRowStart.value * rowHeight.value
173171const virtualPaddingBottom = computed (() =>
174172 Math .max (0 , (rows .value .length - visibleRowEnd .value ) * rowHeight .value ),
175173)
174+ const rowHeaderColumnWidth = computed (() => {
175+ const rowDigits = Math .max (3 , String (Math .max (rows .value .length , 1 )).length )
176+ return Math .max (56 , 48 + rowDigits * 8 )
177+ })
176178const focusedRowIndex = computed (() => {
177179 if (focusCell .value === null ) {
178180 return null
@@ -227,7 +229,7 @@ const selectionOverlayStyle = computed(() => {
227229 width += widths .value [columns .value [columnIndex ]! .field ] ?? 220
228230 }
229231
230- let left = rowHeaderWidth .value
232+ let left = rowHeaderColumnWidth .value
231233 for (let columnIndex = 0 ; columnIndex < startColumn ; columnIndex += 1 ) {
232234 left += widths .value [columns .value [columnIndex ]! .field ] ?? 220
233235 }
@@ -255,11 +257,6 @@ const measureGridMetrics = () => {
255257 headerHeight .value = nextHeaderHeight
256258 }
257259
258- const nextRowHeaderWidth = rowHeaderCell .value ?.getBoundingClientRect ().width
259- if (nextRowHeaderWidth && Number .isFinite (nextRowHeaderWidth )) {
260- rowHeaderWidth .value = nextRowHeaderWidth
261- }
262-
263260 const nextRowHeight = gridContainer .value
264261 ?.querySelector <HTMLTableRowElement >(' tr[data-row-index]' )
265262 ?.getBoundingClientRect ().height
@@ -281,6 +278,30 @@ const getCellElement = (cell: CellPosition) =>
281278 ` td[data-cell-row="${cell .row }"][data-cell-column="${cell .column }"] ` ,
282279 ) ?? null
283280
281+ const scrollRenderedCellIntoView = (
282+ container : HTMLDivElement ,
283+ targetCell : HTMLTableCellElement ,
284+ ) => {
285+ const containerRect = container .getBoundingClientRect ()
286+ const cellRect = targetCell .getBoundingClientRect ()
287+ const viewportLeft = containerRect .left + rowHeaderColumnWidth .value
288+ const viewportTop = containerRect .top + headerHeight .value
289+
290+ if (cellRect .left < viewportLeft ) {
291+ container .scrollLeft = Math .max (0 , container .scrollLeft - (viewportLeft - cellRect .left ))
292+ } else if (cellRect .right > containerRect .right ) {
293+ container .scrollLeft += cellRect .right - containerRect .right
294+ }
295+
296+ if (cellRect .top < viewportTop ) {
297+ container .scrollTop = Math .max (0 , container .scrollTop - (viewportTop - cellRect .top ))
298+ } else if (cellRect .bottom > containerRect .bottom ) {
299+ container .scrollTop += cellRect .bottom - containerRect .bottom
300+ }
301+
302+ scrollTop .value = container .scrollTop
303+ }
304+
284305const ensureCellVisible = (cell : CellPosition ) => {
285306 nextTick (() => {
286307 const container = gridContainer .value
@@ -290,10 +311,7 @@ const ensureCellVisible = (cell: CellPosition) => {
290311
291312 const targetCell = getCellElement (cell )
292313 if (targetCell ) {
293- targetCell .scrollIntoView ({
294- block: ' nearest' ,
295- inline: ' nearest' ,
296- })
314+ scrollRenderedCellIntoView (container , targetCell )
297315 return
298316 }
299317
@@ -312,10 +330,9 @@ const ensureCellVisible = (cell: CellPosition) => {
312330
313331 requestAnimationFrame (() => {
314332 const nextTargetCell = getCellElement (cell )
315- nextTargetCell ?.scrollIntoView ({
316- block: ' nearest' ,
317- inline: ' nearest' ,
318- })
333+ if (nextTargetCell ) {
334+ scrollRenderedCellIntoView (container , nextTargetCell )
335+ }
319336 })
320337 })
321338}
@@ -1051,9 +1068,6 @@ onMounted(() => {
10511068 if (tableHead .value ) {
10521069 resizeObserver .observe (tableHead .value )
10531070 }
1054- if (rowHeaderCell .value ) {
1055- resizeObserver .observe (rowHeaderCell .value )
1056- }
10571071 window .addEventListener (' mousemove' , onDocumentMouseMove )
10581072 window .addEventListener (' mouseup' , onDocumentMouseUp )
10591073})
@@ -1112,8 +1126,12 @@ defineExpose({
11121126 <thead ref =" tableHead" >
11131127 <tr class =" sticky top-0 left-0 bg-[#F9F9F9] dark:bg-[#202020] z-20" >
11141128 <th
1115- ref =" rowHeaderCell"
11161129 class =" sticky left-0 z-30 bg-[#F9F9F9] dark:bg-[#202020] border app-border border-t-0 border-l-0"
1130+ :style =" {
1131+ width: `${rowHeaderColumnWidth}px`,
1132+ minWidth: `${rowHeaderColumnWidth}px`,
1133+ maxWidth: `${rowHeaderColumnWidth}px`,
1134+ }"
11171135 >
11181136 <button
11191137 class =" w-full h-full min-w-[3rem] hover:bg-neutral-500/10 text-xs uppercase tracking-[0.14em] rounded-md py-2 opacity-55"
@@ -1177,6 +1195,11 @@ defineExpose({
11771195 >
11781196 <td
11791197 class =" border app-border px-3 text-center sticky left-0 z-10 bg-neutral-50 dark:bg-neutral-900 mono text-xs border-l-0"
1198+ :style =" {
1199+ width: `${rowHeaderColumnWidth}px`,
1200+ minWidth: `${rowHeaderColumnWidth}px`,
1201+ maxWidth: `${rowHeaderColumnWidth}px`,
1202+ }"
11801203 @click =" selectRow(rowIndex)"
11811204 >
11821205 <div class =" flex items-center gap-2 py-1" >
0 commit comments