diff --git a/docs/config.json b/docs/config.json index c2a170878a..6243d87d59 100644 --- a/docs/config.json +++ b/docs/config.json @@ -463,6 +463,10 @@ { "to": "framework/lit/examples/sorting", "label": "Sorting" + }, + { + "to": "framework/lit/examples/virtualized-rows", + "label": "Virtualized Rows" } ] }, diff --git a/examples/lit/virtualized-rows/.gitignore b/examples/lit/virtualized-rows/.gitignore new file mode 100644 index 0000000000..d451ff16c1 --- /dev/null +++ b/examples/lit/virtualized-rows/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local diff --git a/examples/lit/virtualized-rows/README.md b/examples/lit/virtualized-rows/README.md new file mode 100644 index 0000000000..b168d3c4b1 --- /dev/null +++ b/examples/lit/virtualized-rows/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` or `yarn` +- `npm run start` or `yarn start` diff --git a/examples/lit/virtualized-rows/index.html b/examples/lit/virtualized-rows/index.html new file mode 100644 index 0000000000..9807874fae --- /dev/null +++ b/examples/lit/virtualized-rows/index.html @@ -0,0 +1,14 @@ + + + + + + Vite App + + + +
+ + + + diff --git a/examples/lit/virtualized-rows/package.json b/examples/lit/virtualized-rows/package.json new file mode 100644 index 0000000000..2a6df304dd --- /dev/null +++ b/examples/lit/virtualized-rows/package.json @@ -0,0 +1,22 @@ +{ + "name": "tanstack-lit-table-virtualized-rows", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview", + "start": "vite" + }, + "dependencies": { + "@faker-js/faker": "^8.4.1", + "@lit-labs/virtualizer": "^2.0.13", + "@tanstack/lit-table": "^8.19.1", + "lit": "^3.1.3" + }, + "devDependencies": { + "@rollup/plugin-replace": "^5.0.5", + "typescript": "5.4.5", + "vite": "^5.2.10" + } +} diff --git a/examples/lit/virtualized-rows/src/main.ts b/examples/lit/virtualized-rows/src/main.ts new file mode 100644 index 0000000000..a33c18b877 --- /dev/null +++ b/examples/lit/virtualized-rows/src/main.ts @@ -0,0 +1,211 @@ +import { customElement } from 'lit/decorators.js' +import { html, LitElement } from 'lit' +import { repeat } from 'lit/directives/repeat.js' +import { + ColumnDef, + flexRender, + getCoreRowModel, + getSortedRowModel, + TableController, +} from '@tanstack/lit-table' +import config from '../twind.config' +import { styleMap } from 'lit/directives/style-map.js' +import { virtualize, virtualizerRef } from '@lit-labs/virtualizer/virtualize.js' +import { makeData, Person } from './makeData.ts' + +const columns: ColumnDef[] = [ + { + accessorKey: 'id', + header: 'ID', + size: 60, + }, + { + accessorKey: 'firstName', + cell: info => info.getValue(), + }, + { + accessorFn: row => row.lastName, + id: 'lastName', + cell: info => info.getValue(), + header: () => html`Last Name`, + }, + { + accessorKey: 'age', + header: () => 'Age', + size: 50, + }, + { + accessorKey: 'visits', + header: () => html`Visits`, + size: 50, + }, + { + accessorKey: 'status', + header: 'Status', + }, + { + accessorKey: 'progress', + header: 'Profile Progress', + size: 80, + }, + { + accessorKey: 'createdAt', + header: 'Created At', + cell: info => info.getValue().toLocaleString(), + size: 250, + }, +] +const data = makeData(50_000) + +@customElement('lit-table-example') +class LitTableExample extends LitElement { + private tableController = new TableController(this) + + protected render(): unknown { + const table = this.tableController.table({ + columns, + data, + getSortedRowModel: getSortedRowModel(), + getCoreRowModel: getCoreRowModel(), + }) + const { rows } = table.getRowModel() + return html` +
+ (${data.length} rows) +
+ + + ${repeat( + table.getHeaderGroups(), + headerGroup => headerGroup.id, + headerGroup => html` + + ${repeat( + headerGroup.headers, + header => header.id, + header => html` + + ` + )} + + ` + )} + + + ${virtualize({ + items: data, + renderItem: (_, index) => { + const row = rows[index] + return html` + + ${repeat( + row.getVisibleCells(), + cell => cell.id, + cell => html` + + ` + )} + + ` + }, + })} + +
+ ${flexRender( + header.column.columnDef.header, + header.getContext() + )} + ${{ asc: ' 🔼', desc: ' 🔽' }[ + header.column.getIsSorted() as string + ] ?? null} +
+ ${flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} +
+
+
+ + + ` + } +} diff --git a/examples/lit/virtualized-rows/src/makeData.ts b/examples/lit/virtualized-rows/src/makeData.ts new file mode 100644 index 0000000000..e5695467f5 --- /dev/null +++ b/examples/lit/virtualized-rows/src/makeData.ts @@ -0,0 +1,50 @@ +import { faker } from '@faker-js/faker' + +export type Person = { + id: number + firstName: string + lastName: string + age: number + visits: number + progress: number + status: 'relationship' | 'complicated' | 'single' + createdAt: Date +} + +const range = (len: number) => { + const arr: number[] = [] + for (let i = 0; i < len; i++) { + arr.push(i) + } + return arr +} + +const newPerson = (index: number): Person => { + return { + id: index + 1, + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + age: faker.number.int(40), + visits: faker.number.int(1000), + progress: faker.number.int(100), + createdAt: faker.date.anytime(), + status: faker.helpers.shuffle([ + 'relationship', + 'complicated', + 'single', + ])[0]!, + } +} + +export function makeData(...lens: number[]) { + const makeDataLevel = (depth = 0): Person[] => { + const len = lens[depth]! + return range(len).map((d): Person => { + return { + ...newPerson(d), + } + }) + } + + return makeDataLevel() +} diff --git a/examples/lit/virtualized-rows/tsconfig.json b/examples/lit/virtualized-rows/tsconfig.json new file mode 100644 index 0000000000..3141563c8a --- /dev/null +++ b/examples/lit/virtualized-rows/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "emitDecoratorMetadata": true, + "noEmit": true, + "jsx": "react-jsx", + "experimentalDecorators": true, + "useDefineForClassFields": false, + + /* Linting */ + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/examples/lit/virtualized-rows/twind.config.ts b/examples/lit/virtualized-rows/twind.config.ts new file mode 100644 index 0000000000..f7e85ff4ae --- /dev/null +++ b/examples/lit/virtualized-rows/twind.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from '@twind/core' +import presetAutoprefix from '@twind/preset-autoprefix' +import presetTailwind from '@twind/preset-tailwind/base' + +export default defineConfig({ + presets: [presetAutoprefix(), presetTailwind()], +}) diff --git a/examples/lit/virtualized-rows/vite.config.js b/examples/lit/virtualized-rows/vite.config.js new file mode 100644 index 0000000000..fa3b238ac6 --- /dev/null +++ b/examples/lit/virtualized-rows/vite.config.js @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import rollupReplace from '@rollup/plugin-replace' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + rollupReplace({ + preventAssignment: true, + values: { + __DEV__: JSON.stringify(true), + 'process.env.NODE_ENV': JSON.stringify('development'), + }, + }), + ], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a9b7f50e5b..c9f0c0e40f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -942,6 +942,31 @@ importers: specifier: ^5.2.10 version: 5.3.1(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(terser@5.31.1) + examples/lit/virtualized-rows: + dependencies: + '@faker-js/faker': + specifier: ^8.4.1 + version: 8.4.1 + '@lit-labs/virtualizer': + specifier: ^2.0.13 + version: 2.0.13 + '@tanstack/lit-table': + specifier: ^8.19.1 + version: link:../../../packages/lit-table + lit: + specifier: ^3.1.3 + version: 3.1.4 + devDependencies: + '@rollup/plugin-replace': + specifier: ^5.0.5 + version: 5.0.7(rollup@4.18.0) + typescript: + specifier: 5.4.5 + version: 5.4.5 + vite: + specifier: ^5.2.10 + version: 5.3.1(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(terser@5.31.1) + examples/qwik/basic: dependencies: '@tanstack/qwik-table': @@ -4565,6 +4590,9 @@ packages: '@lit-labs/ssr-dom-shim@1.2.0': resolution: {integrity: sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==} + '@lit-labs/virtualizer@2.0.13': + resolution: {integrity: sha512-OKojbIFohfrRpWd3OxcVSc2nyTd0jx10ZSQobuOW9H9jYkad02OJ1uFvV/sHJey8hoh95FIO4d43h+Ry/G4nGw==} + '@lit/reactive-element@2.0.4': resolution: {integrity: sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==} @@ -12382,6 +12410,11 @@ snapshots: '@lit-labs/ssr-dom-shim@1.2.0': {} + '@lit-labs/virtualizer@2.0.13': + dependencies: + lit: 3.1.4 + tslib: 2.6.2 + '@lit/reactive-element@2.0.4': dependencies: '@lit-labs/ssr-dom-shim': 1.2.0