Skip to content

Commit fa3d024

Browse files
committed
feat: connections table virtual scroll
1 parent 50dac03 commit fa3d024

File tree

3 files changed

+110
-69
lines changed

3 files changed

+110
-69
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"typescript-eslint": "^8.44.0",
7676
"unplugin-auto-import": "^20.1.0",
7777
"uuid": "^13.0.0",
78+
"virtua": "^0.43.2",
7879
"vite": "^7.1.6",
7980
"vite-plugin-pwa": "^1.0.3",
8081
"vite-plugin-solid": "^2.11.8",

pnpm-lock.yaml

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/pages/Connections.tsx

Lines changed: 79 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,22 @@ import {
1515
import { rankItem } from '@tanstack/match-sorter-utils'
1616
import {
1717
ColumnDef,
18-
FilterFn,
19-
GroupingState,
20-
SortingState,
2118
createSolidTable,
19+
FilterFn,
2220
flexRender,
2321
getCoreRowModel,
2422
getExpandedRowModel,
2523
getFilteredRowModel,
2624
getGroupedRowModel,
2725
getSortedRowModel,
26+
GroupingState,
27+
SortingState,
2828
} from '@tanstack/solid-table'
2929
import byteSize from 'byte-size'
3030
import dayjs from 'dayjs'
3131
import { uniq } from 'lodash'
3232
import { twMerge } from 'tailwind-merge'
33+
import { Virtualizer } from 'virtua/solid'
3334
import { closeAllConnectionsAPI, closeSingleConnectionAPI } from '~/apis'
3435
import {
3536
Button,
@@ -367,6 +368,8 @@ export default () => {
367368
},
368369
])
369370

371+
let scrollRef: HTMLDivElement | undefined
372+
370373
return (
371374
<>
372375
<DocumentTitle>{t('connections')}</DocumentTitle>
@@ -476,20 +479,23 @@ export default () => {
476479
</div>
477480
</div>
478481

479-
<div class="overflow-x-auto rounded-md bg-base-300 whitespace-nowrap">
482+
<div
483+
class="h-full overflow-x-auto rounded-md bg-base-300"
484+
ref={scrollRef}
485+
>
480486
<table
481487
class={twMerge(
482488
tableSizeClassName(connectionsTableSize()),
483-
'table relative rounded-none table-zebra',
489+
'table-pin-rows table h-full table-zebra',
484490
)}
485491
>
486-
<thead class="sticky top-0 z-10 h-8">
492+
<thead>
487493
<For each={table.getHeaderGroups()}>
488494
{(headerGroup) => (
489-
<tr>
495+
<tr class="flex">
490496
<For each={headerGroup.headers}>
491497
{(header) => (
492-
<th class="bg-base-200">
498+
<th class="bg-base-200" style={{ width: '150px' }}>
493499
<div class={twMerge('flex items-center gap-2')}>
494500
{header.column.getCanGroup() ? (
495501
<button
@@ -531,70 +537,74 @@ export default () => {
531537
</For>
532538
</thead>
533539

534-
<tbody>
535-
<For each={table.getRowModel().rows}>
536-
{(row) => (
537-
<tr class="hover:!bg-primary hover:text-primary-content">
538-
<For each={row.getVisibleCells()}>
539-
{(cell) => {
540-
return (
541-
<td
542-
class="py-2"
543-
onContextMenu={(e) => {
544-
e.preventDefault()
545-
546-
const value = cell.renderValue() as null | string
547-
548-
if (value) writeClipboard(value).catch(() => {})
549-
}}
540+
<Virtualizer
541+
scrollRef={scrollRef}
542+
data={table.getRowModel().rows}
543+
as="tbody"
544+
item="tr"
545+
>
546+
{(row) => (
547+
<For each={row.getVisibleCells()}>
548+
{(cell) => {
549+
return (
550+
<td
551+
class="inline-block py-2 break-words"
552+
style={{
553+
width: '150px',
554+
}}
555+
onContextMenu={(e) => {
556+
e.preventDefault()
557+
558+
const value = cell.renderValue() as null | string
559+
560+
if (value) writeClipboard(value).catch(() => {})
561+
}}
562+
>
563+
{cell.getIsGrouped() ? (
564+
<button
565+
class={twMerge(
566+
row.getCanExpand()
567+
? 'cursor-pointer'
568+
: 'cursor-normal',
569+
'flex items-center gap-2',
570+
)}
571+
onClick={row.getToggleExpandedHandler()}
550572
>
551-
{cell.getIsGrouped() ? (
552-
<button
553-
class={twMerge(
554-
row.getCanExpand()
555-
? 'cursor-pointer'
556-
: 'cursor-normal',
557-
'flex items-center gap-2',
558-
)}
559-
onClick={row.getToggleExpandedHandler()}
560-
>
561-
<div>
562-
{row.getIsExpanded() ? (
563-
<IconZoomOutFilled size={18} />
564-
) : (
565-
<IconZoomInFilled size={18} />
566-
)}
567-
</div>
568-
569-
<div>
570-
{flexRender(
571-
cell.column.columnDef.cell,
572-
cell.getContext(),
573-
)}
574-
</div>
575-
576-
<div>({row.subRows.length})</div>
577-
</button>
578-
) : cell.getIsAggregated() ? (
579-
flexRender(
580-
cell.column.columnDef.aggregatedCell ??
581-
cell.column.columnDef.cell,
582-
cell.getContext(),
583-
)
584-
) : cell.getIsPlaceholder() ? null : (
585-
flexRender(
573+
<div>
574+
{row.getIsExpanded() ? (
575+
<IconZoomOutFilled size={18} />
576+
) : (
577+
<IconZoomInFilled size={18} />
578+
)}
579+
</div>
580+
581+
<div>
582+
{flexRender(
586583
cell.column.columnDef.cell,
587584
cell.getContext(),
588-
)
589-
)}
590-
</td>
591-
)
592-
}}
593-
</For>
594-
</tr>
595-
)}
596-
</For>
597-
</tbody>
585+
)}
586+
</div>
587+
588+
<div>({row.subRows.length})</div>
589+
</button>
590+
) : cell.getIsAggregated() ? (
591+
flexRender(
592+
cell.column.columnDef.aggregatedCell ??
593+
cell.column.columnDef.cell,
594+
cell.getContext(),
595+
)
596+
) : cell.getIsPlaceholder() ? null : (
597+
flexRender(
598+
cell.column.columnDef.cell,
599+
cell.getContext(),
600+
)
601+
)}
602+
</td>
603+
)
604+
}}
605+
</For>
606+
)}
607+
</Virtualizer>
598608
</table>
599609
</div>
600610

0 commit comments

Comments
 (0)