Skip to content

Commit b8c6146

Browse files
committed
feat: enhance table performance with improved row header handling and expanded pagination options
1 parent 8d03f37 commit b8c6146

3 files changed

Lines changed: 45 additions & 22 deletions

File tree

packages/backend/src/config/app-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export function loadAppConfig(): AppConfig {
6969
serverName:
7070
process.env.STARQUERY_SERVER_NAME ?? (mode === 'hosted' ? 'Hosted Server' : 'Local Computer'),
7171
mode,
72-
requestBodyLimit: process.env.STARQUERY_REQUEST_BODY_LIMIT ?? '100mb',
72+
requestBodyLimit: process.env.STARQUERY_REQUEST_BODY_LIMIT ?? '99gb',
7373
auth: {
7474
enabled: mode !== 'local',
7575
sessionTtlHours: Number(process.env.STARQUERY_AUTH_SESSION_TTL_HOURS ?? '720'),

packages/frontend/src/components/common/DataPaginationBar.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const props = withDefaults(
1515
summary?: string | null
1616
}>(),
1717
{
18-
pageSizeOptions: () => [5, 10, 15, 25, 50, 100, 200, 500, 1000, 2000],
18+
pageSizeOptions: () => [5, 10, 15, 25, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 1000000],
1919
canPrevious: false,
2020
canNext: false,
2121
disabled: false,

packages/frontend/src/components/table/ExtendedDataTable.vue

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ const rows = defineModel<SQLTableRowDraft[]>('rows', { required: true })
3636
const toast = useToast()
3737
const gridContainer = useTemplateRef<HTMLDivElement>('gridContainer')
3838
const tableHead = useTemplateRef<HTMLTableSectionElement>('tableHead')
39-
const rowHeaderCell = useTemplateRef<HTMLTableCellElement>('rowHeaderCell')
4039
const editingEditor = useTemplateRef<InstanceType<typeof SQLTableCellEditor>>('editingEditor')
4140
const contextMenu = useTemplateRef<ContextMenuMethods>('contextMenu')
4241
@@ -52,7 +51,6 @@ const dragPointer = ref<{ x: number; y: number } | null>(null)
5251
const scrollTop = ref(0)
5352
const viewportHeight = ref(0)
5453
const headerHeight = ref(0)
55-
const rowHeaderWidth = ref(48)
5654
const rowHeight = ref(DEFAULT_ROW_HEIGHT)
5755
5856
let dragScrollFrame = 0
@@ -173,6 +171,10 @@ const virtualPaddingTop = computed(() => visibleRowStart.value * rowHeight.value
173171
const 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+
})
176178
const 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+
284305
const 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

Comments
 (0)