From 924f43433fbf986c934563de8bf8a0ab1bf9bbca Mon Sep 17 00:00:00 2001 From: Mor Kadosh Date: Sat, 4 May 2024 20:08:57 +0300 Subject: [PATCH] feat: initial integration for lit --- .gitignore | 1 + examples/lit/basic/.gitignore | 5 + examples/lit/basic/README.md | 6 + examples/lit/basic/index.html | 14 ++ examples/lit/basic/package.json | 20 +++ examples/lit/basic/src/index.css | 26 ++++ examples/lit/basic/src/main.ts | 225 +++++++++++++++++++++++++++ examples/lit/basic/tsconfig.json | 26 ++++ examples/lit/basic/vite.config.js | 15 ++ packages/lit-table/package.json | 62 ++++++++ packages/lit-table/rollup.config.mjs | 17 ++ packages/lit-table/src/index.ts | 67 ++++++++ packages/lit-table/tsconfig.json | 11 ++ packages/lit-table/vitest.config.ts | 10 ++ pnpm-lock.yaml | 67 +++++++- pnpm-workspace.yaml | 1 + 16 files changed, 569 insertions(+), 4 deletions(-) create mode 100644 examples/lit/basic/.gitignore create mode 100644 examples/lit/basic/README.md create mode 100644 examples/lit/basic/index.html create mode 100644 examples/lit/basic/package.json create mode 100644 examples/lit/basic/src/index.css create mode 100644 examples/lit/basic/src/main.ts create mode 100644 examples/lit/basic/tsconfig.json create mode 100644 examples/lit/basic/vite.config.js create mode 100644 packages/lit-table/package.json create mode 100644 packages/lit-table/rollup.config.mjs create mode 100755 packages/lit-table/src/index.ts create mode 100644 packages/lit-table/tsconfig.json create mode 100644 packages/lit-table/vitest.config.ts diff --git a/.gitignore b/.gitignore index 1ef516313f..14ada71ef6 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ stats-hydration.json stats-react.json stats.html .vscode/settings.json +.idea *.log .DS_Store diff --git a/examples/lit/basic/.gitignore b/examples/lit/basic/.gitignore new file mode 100644 index 0000000000..d451ff16c1 --- /dev/null +++ b/examples/lit/basic/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local diff --git a/examples/lit/basic/README.md b/examples/lit/basic/README.md new file mode 100644 index 0000000000..b168d3c4b1 --- /dev/null +++ b/examples/lit/basic/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/basic/index.html b/examples/lit/basic/index.html new file mode 100644 index 0000000000..9807874fae --- /dev/null +++ b/examples/lit/basic/index.html @@ -0,0 +1,14 @@ + + + + + + Vite App + + + +
+ + + + diff --git a/examples/lit/basic/package.json b/examples/lit/basic/package.json new file mode 100644 index 0000000000..482df5ea48 --- /dev/null +++ b/examples/lit/basic/package.json @@ -0,0 +1,20 @@ +{ + "name": "tanstack-lit-table-example-basic", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview", + "start": "vite" + }, + "dependencies": { + "@tanstack/lit-table": "workspace:*", + "lit": "^3.1.3" + }, + "devDependencies": { + "@rollup/plugin-replace": "^5.0.5", + "typescript": "5.4.5", + "vite": "^5.2.10" + } +} diff --git a/examples/lit/basic/src/index.css b/examples/lit/basic/src/index.css new file mode 100644 index 0000000000..43c09e0f6b --- /dev/null +++ b/examples/lit/basic/src/index.css @@ -0,0 +1,26 @@ +html { + font-family: sans-serif; + font-size: 14px; +} + +table { + border: 1px solid lightgray; +} + +tbody { + border-bottom: 1px solid lightgray; +} + +th { + border-bottom: 1px solid lightgray; + border-right: 1px solid lightgray; + padding: 2px 4px; +} + +tfoot { + color: gray; +} + +tfoot th { + font-weight: normal; +} diff --git a/examples/lit/basic/src/main.ts b/examples/lit/basic/src/main.ts new file mode 100644 index 0000000000..d462cc01b5 --- /dev/null +++ b/examples/lit/basic/src/main.ts @@ -0,0 +1,225 @@ +import { customElement } from 'lit/decorators.js' +import { html, LitElement } from 'lit' +import { repeat } from 'lit/directives/repeat.js' +import { + createColumnHelper, + flexRender, + getCoreRowModel, + TableController, +} from '@tanstack/lit-table' + +type Person = { + firstName: string + lastName: string + age: number + visits: number + status: string + progress: number +} + +const defaultData: Person[] = [ + { + firstName: 'tanner', + lastName: 'linsley', + age: 24, + visits: 100, + status: 'In Relationship', + progress: 50, + }, + { + firstName: 'tandy', + lastName: 'miller', + age: 40, + visits: 40, + status: 'Single', + progress: 80, + }, + { + firstName: 'joe', + lastName: 'dirte', + age: 45, + visits: 20, + status: 'Complicated', + progress: 10, + }, +] + +const columnHelper = createColumnHelper() + +const columns = [ + columnHelper.accessor('firstName', { + cell: info => info.getValue(), + footer: info => info.column.id, + }), + columnHelper.accessor(row => row.lastName, { + id: 'lastName', + cell: info => html`${info.getValue()}`, + header: () => html`Last Name`, + footer: info => info.column.id, + }), + columnHelper.accessor('age', { + header: () => 'Age', + cell: info => info.renderValue(), + footer: info => info.column.id, + }), + columnHelper.accessor('visits', { + header: () => html`Visits`, + footer: info => info.column.id, + }), + columnHelper.accessor('status', { + header: 'Status', + footer: info => info.column.id, + }), + columnHelper.accessor('progress', { + header: 'Profile Progress', + footer: info => info.column.id, + }), +] + +const data: Person[] = [ + { + firstName: 'tanner', + lastName: 'linsley', + age: 24, + visits: 100, + status: 'In Relationship', + progress: 50, + }, + { + firstName: 'tandy', + lastName: 'miller', + age: 40, + visits: 40, + status: 'Single', + progress: 80, + }, + { + firstName: 'joe', + lastName: 'dirte', + age: 45, + visits: 20, + status: 'Complicated', + progress: 10, + }, + { + firstName: 'mor', + lastName: 'kadosh', + age: 31, + visits: 30, + status: 'In Relationship', + progress: 90, + }, +] + +@customElement('lit-table-example') +class LitTableExample extends LitElement { + private tableController = new TableController(this) + + protected render(): unknown { + const table = this.tableController.getTable({ + columns, + data, + getCoreRowModel: getCoreRowModel(), + }) + + return html` + + + ${repeat( + table.getHeaderGroups(), + headerGroup => headerGroup.id, + headerGroup => + html`${repeat( + headerGroup.headers, + header => header.id, + header => + html` ` + )}` + )} + + + ${repeat( + table.getRowModel().rows, + row => row.id, + row => html` + + ${repeat( + row.getVisibleCells(), + cell => cell.id, + cell => + html`` + )} + + ` + )} + + + ${repeat( + table.getFooterGroups(), + footerGroup => footerGroup.id, + footerGroup => html` + + ${repeat( + footerGroup.headers, + header => header.id, + header => html` + + ` + )} + + ` + )} + +
+ ${header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} +
+ ${flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} +
+ ${header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.footer, + header.getContext() + )} +
+ + ` + } +} diff --git a/examples/lit/basic/tsconfig.json b/examples/lit/basic/tsconfig.json new file mode 100644 index 0000000000..3141563c8a --- /dev/null +++ b/examples/lit/basic/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/basic/vite.config.js b/examples/lit/basic/vite.config.js new file mode 100644 index 0000000000..ab1e1380f8 --- /dev/null +++ b/examples/lit/basic/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/packages/lit-table/package.json b/packages/lit-table/package.json new file mode 100644 index 0000000000..fde205cfab --- /dev/null +++ b/packages/lit-table/package.json @@ -0,0 +1,62 @@ +{ + "name": "@tanstack/lit-table", + "version": "0.0.0", + "description": "Headless UI for building powerful tables & datagrids for Lit.", + "author": "Tanner Linsley", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/TanStack/table.git", + "directory": "packages/lit-table" + }, + "homepage": "https://tanstack.com/table", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "keywords": [ + "lit", + "table", + "lit-table", + "datagrid" + ], + "type": "commonjs", + "module": "build/lib/index.esm.js", + "main": "build/lib/index.js", + "types": "build/lib/index.d.ts", + "exports": { + ".": { + "types": "./build/lib/index.d.ts", + "import": "./build/lib/index.mjs", + "default": "./build/lib/index.js" + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "engines": { + "node": ">=12" + }, + "files": [ + "build/lib/*", + "build/umd/*", + "src" + ], + "scripts": { + "clean": "rimraf ./build", + "test:lib": "vitest", + "test:lib:dev": "pnpm test:lib --watch", + "test:types": "tsc --noEmit", + "build": "pnpm build:rollup && pnpm build:types", + "build:rollup": "rollup --config rollup.config.mjs", + "build:types": "tsc --emitDeclarationOnly" + }, + "dependencies": { + "@tanstack/table-core": "workspace:*" + }, + "devDependencies": { + "lit": "^3.1.3" + }, + "peerDependencies": { + "lit": "^3.1.3" + } +} diff --git a/packages/lit-table/rollup.config.mjs b/packages/lit-table/rollup.config.mjs new file mode 100644 index 0000000000..0e4b3b9a92 --- /dev/null +++ b/packages/lit-table/rollup.config.mjs @@ -0,0 +1,17 @@ +// @ts-check + +import { defineConfig } from 'rollup' +import { buildConfigs } from '../../scripts/getRollupConfig.js' + +export default defineConfig( + buildConfigs({ + name: 'lit-table', + jsName: 'LitTable', + outputFile: 'index', + entryFile: 'src/index.ts', + external: ['lit'], + globals: { + lit: 'lit', + }, + }) +) diff --git a/packages/lit-table/src/index.ts b/packages/lit-table/src/index.ts new file mode 100755 index 0000000000..5cb3b5cdbe --- /dev/null +++ b/packages/lit-table/src/index.ts @@ -0,0 +1,67 @@ +import { ReactiveController, ReactiveControllerHost } from 'lit' +import { + RowData, + Table, + TableOptions, + TableOptionsResolved, + TableState, + createTable, +} from '@tanstack/table-core' + +export * from '@tanstack/table-core' + +export function flexRender( + Comp: ((props: TProps) => string) | string | undefined, + props: TProps +): string | null { + if (!Comp) return null + + if (typeof Comp === 'function') { + return Comp(props) + } + + return Comp +} + +export class TableController + implements ReactiveController +{ + host: ReactiveControllerHost + + private table: Table | null = null + + private _tableState: TableState | null = null + + constructor(host: ReactiveControllerHost) { + ;(this.host = host).addController(this) + } + + public getTable(options: TableOptions) { + const resolvedOptions: TableOptionsResolved = { + state: {}, + onStateChange: () => {}, // noop + renderFallbackValue: null, + ...options, + } + + if (!this.table) { + this.table = createTable(resolvedOptions) + this._tableState = this.table.initialState + } + + this.table.setOptions({ + ...resolvedOptions, + state: { ...this._tableState, ...(options.state || {}) }, + onStateChange: (updater: any) => { + this._tableState = updater + this.host.requestUpdate() + + options.onStateChange?.(updater) + }, + }) + + return this.table + } + + hostConnected() {} +} diff --git a/packages/lit-table/tsconfig.json b/packages/lit-table/tsconfig.json new file mode 100644 index 0000000000..062f8964b3 --- /dev/null +++ b/packages/lit-table/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "jsx": "react", + "rootDir": "./src", + "outDir": "./build/lib", + "experimentalDecorators": true, + "emitDecoratorMetadata": true + }, + "include": ["src"] +} diff --git a/packages/lit-table/vitest.config.ts b/packages/lit-table/vitest.config.ts new file mode 100644 index 0000000000..20690b6ad0 --- /dev/null +++ b/packages/lit-table/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + watch: false, + environment: 'jsdom', + globals: true, + dir: '__tests__', + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac0cf2611c..a206c25b3c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,6 +99,25 @@ importers: specifier: ^1.5.2 version: 1.5.2(@types/node@20.12.7)(jsdom@24.0.0) + examples/lit/basic: + dependencies: + '@tanstack/lit-table': + specifier: workspace:* + version: link:../../../packages/lit-table + lit: + specifier: ^3.1.3 + version: 3.1.3 + devDependencies: + '@rollup/plugin-replace': + specifier: ^5.0.5 + version: 5.0.5(rollup@4.16.4) + typescript: + specifier: 5.4.5 + version: 5.4.5 + vite: + specifier: ^5.2.10 + version: 5.2.10(@types/node@20.12.7) + examples/qwik/basic: dependencies: '@tanstack/qwik-table': @@ -1904,6 +1923,16 @@ importers: specifier: ^2.0.14 version: 2.0.14(typescript@5.4.5) + packages/lit-table: + dependencies: + '@tanstack/table-core': + specifier: workspace:* + version: link:../table-core + devDependencies: + lit: + specifier: ^3.1.3 + version: 3.1.3 + packages/match-sorter-utils: dependencies: remove-accents: @@ -4004,6 +4033,14 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@lit-labs/ssr-dom-shim@1.2.0: + resolution: {integrity: sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==} + + /@lit/reactive-element@2.0.4: + resolution: {integrity: sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==} + dependencies: + '@lit-labs/ssr-dom-shim': 1.2.0 + /@microsoft/api-extractor-model@7.28.13(@types/node@20.12.7): resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==} dependencies: @@ -4510,7 +4547,7 @@ packages: optional: true dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.16.4) - magic-string: 0.30.8 + magic-string: 0.30.10 rollup: 4.16.4 dev: true @@ -5071,6 +5108,9 @@ packages: resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} dev: true + /@types/trusted-types@2.0.7: + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + /@types/warning@3.0.3: resolution: {integrity: sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==} dev: false @@ -5137,7 +5177,7 @@ packages: /@vitest/snapshot@1.5.2: resolution: {integrity: sha512-CTEp/lTYos8fuCc9+Z55Ga5NVPKUgExritjF5VY7heRFUfheoAqBneUlvXSUJHUZPjnPmyZA96yLRJDP1QATFQ==} dependencies: - magic-string: 0.30.8 + magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 dev: true @@ -5277,7 +5317,7 @@ packages: '@vue/compiler-ssr': 3.4.21 '@vue/shared': 3.4.21 estree-walker: 2.0.2 - magic-string: 0.30.8 + magic-string: 0.30.10 postcss: 8.4.38 source-map-js: 1.2.0 dev: true @@ -7402,6 +7442,25 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true + /lit-element@4.0.5: + resolution: {integrity: sha512-iTWskWZEtn9SyEf4aBG6rKT8GABZMrTWop1+jopsEOgEcugcXJGKuX5bEbkq9qfzY+XB4MAgCaSPwnNpdsNQ3Q==} + dependencies: + '@lit-labs/ssr-dom-shim': 1.2.0 + '@lit/reactive-element': 2.0.4 + lit-html: 3.1.3 + + /lit-html@3.1.3: + resolution: {integrity: sha512-FwIbqDD8O/8lM4vUZ4KvQZjPPNx7V1VhT7vmRB8RBAO0AU6wuTVdoXiu2CivVjEGdugvcbPNBLtPE1y0ifplHA==} + dependencies: + '@types/trusted-types': 2.0.7 + + /lit@3.1.3: + resolution: {integrity: sha512-l4slfspEsnCcHVRTvaP7YnkTZEZggNFywLEIhQaGhYDczG+tu/vlgm/KaWIEjIp+ZyV20r2JnZctMb8LeLCG7Q==} + dependencies: + '@lit/reactive-element': 2.0.4 + lit-element: 4.0.5 + lit-html: 3.1.3 + /local-pkg@0.5.0: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} @@ -9147,7 +9206,7 @@ packages: '@babel/core': 7.24.4 '@types/pug': 2.0.10 detect-indent: 6.1.0 - magic-string: 0.30.8 + magic-string: 0.30.10 sorcery: 0.11.0 strip-indent: 3.0.0 svelte: 4.2.15 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 201a1c573f..d4836d8d08 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,7 @@ packages: - 'packages/*' - 'examples/qwik/*' + - 'examples/lit/*' - 'examples/react/*' - 'examples/solid/*' - 'examples/svelte/*'