import './index.css'
import {
constructTable,
createColumnHelper,
createSortedRowModel,
rowSortingFeature,
sortFn_alphanumeric,
sortFn_datetime,
sortFn_text,
tableFeatures,
} from '@tanstack/table-core'
import { FlexRender } from '@tanstack/table-core/flex-render'
import { storeReactivityBindings } from '@tanstack/table-core/store-reactivity-bindings'
import { makeData } from './makeData'
import type { SortFn } from '@tanstack/table-core'
import type { Person } from './makeData'
let data = makeData(1_000)
const features = tableFeatures({
rowSortingFeature,
coreReactivityFeature: storeReactivityBindings(),
sortedRowModel: createSortedRowModel(),
sortFns: {
alphanumeric: sortFn_alphanumeric,
datetime: sortFn_datetime,
text: sortFn_text,
},
})
const sortStatusFn: SortFn<typeof features, Person> = (
rowA,
rowB,
_columnId,
) => {
const statusA = rowA.original.status
const statusB = rowB.original.status
const statusOrder = ['single', 'complicated', 'relationship']
return statusOrder.indexOf(statusA) - statusOrder.indexOf(statusB)
}
const columnHelper = createColumnHelper<typeof features, Person>()
const columns = columnHelper.columns([
columnHelper.display({
id: 'rowNumber',
header: '#',
cell: ({ row }) => row.getDisplayIndex() + 1,
}),
columnHelper.accessor('firstName', {
cell: (info) => info.getValue(),
}),
columnHelper.accessor((row) => row.lastName, {
id: 'lastName',
cell: (info) => `<i>${info.getValue()}</i>`,
header: () => '<span>Last Name</span>',
sortUndefined: 'last',
sortDescFirst: false,
}),
columnHelper.accessor('email', {
header: 'Email',
sortFn: 'alphanumeric',
}),
columnHelper.accessor('age', {
header: () => 'Age',
cell: (info) => info.renderValue(),
}),
columnHelper.accessor('visits', {
header: () => '<span>Visits</span>',
sortUndefined: 'last',
}),
columnHelper.accessor('status', {
header: 'Status',
sortFn: sortStatusFn,
}),
columnHelper.accessor('progress', {
header: 'Profile Progress',
enableSorting: false,
}),
columnHelper.accessor('rank', {
header: 'Rank',
invertSorting: true,
}),
columnHelper.accessor('createdAt', {
header: 'Created At',
}),
])
const renderTable = () => {
const buttonsDiv = document.createElement('div')
const regenerateBtn = document.createElement('button')
regenerateBtn.textContent = 'Regenerate Data'
regenerateBtn.addEventListener('click', () => {
data = makeData(1_000)
table.setOptions((prev) => ({ ...prev, data }))
renderTable()
})
const stressTestBtn = document.createElement('button')
stressTestBtn.textContent = 'Stress Test (1M rows)'
stressTestBtn.addEventListener('click', () => {
data = makeData(1_000_000)
table.setOptions((prev) => ({ ...prev, data }))
renderTable()
})
buttonsDiv.appendChild(regenerateBtn)
buttonsDiv.appendChild(stressTestBtn)
const tableElement = document.createElement('table')
const theadElement = document.createElement('thead')
const tbodyElement = document.createElement('tbody')
tableElement.classList.add('table-spacer')
tableElement.appendChild(theadElement)
tableElement.appendChild(tbodyElement)
table.getHeaderGroups().forEach((headerGroup) => {
const trElement = document.createElement('tr')
headerGroup.headers.forEach((header) => {
const thElement = document.createElement('th')
thElement.colSpan = header.colSpan
const divElement = document.createElement('div')
divElement.classList.add(
'w-36',
...(header.column.getCanSort()
? ['sortable-header', 'sortable-header']
: []),
)
;((divElement.onclick = (e) =>
header.column.getToggleSortingHandler()?.(e)),
(divElement.innerHTML = header.isPlaceholder
? ''
: String(FlexRender({ header }) ?? '')))
divElement.innerHTML +=
{
asc: ' 🔼',
desc: ' 🔽',
}[header.column.getIsSorted() as string] ?? ''
thElement.appendChild(divElement)
trElement.appendChild(thElement)
})
theadElement.appendChild(trElement)
})
table
.getRowModel()
.rows.slice(0, 10)
.forEach((row) => {
const trElement = document.createElement('tr')
row.getAllCells().forEach((cell) => {
const tdElement = document.createElement('td')
tdElement.innerHTML = String(FlexRender({ cell }) ?? '')
trElement.appendChild(tdElement)
})
tbodyElement.appendChild(trElement)
})
const stateInfoElement = document.createElement('pre')
stateInfoElement.textContent = JSON.stringify(
{
sorting: table.store.state.sorting,
},
null,
2,
)
const wrapperElement = document.getElementById('wrapper') as HTMLDivElement
wrapperElement.innerHTML = ''
wrapperElement.appendChild(buttonsDiv)
wrapperElement.appendChild(tableElement)
wrapperElement.appendChild(stateInfoElement)
}
const table = constructTable({
features,
data,
columns,
debugTable: true,
})
table.store.subscribe(() => renderTable())
renderTable()