Skip to content

Commit 80d92d6

Browse files
authoredJan 3, 2024
docs: add solid filters example (#5252)
1 parent 960513b commit 80d92d6

12 files changed

+406
-0
lines changed
 

‎docs/config.json

+4
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@
326326
"to": "examples/solid/column-visibility",
327327
"label": "Column Visibility"
328328
},
329+
{
330+
"to": "examples/solid/filters",
331+
"label": "Filters"
332+
},
329333
{
330334
"to": "examples/solid/sorting",
331335
"label": "Sorting"

‎examples/solid/filters/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
dist

‎examples/solid/filters/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Example
2+
3+
To run this example:
4+
5+
- `npm install` or `yarn`
6+
- `npm run start` or `yarn start`

‎examples/solid/filters/index.html

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<meta name="theme-color" content="#000000" />
7+
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" />
8+
<title>Solid App</title>
9+
</head>
10+
<body>
11+
<noscript>You need to enable JavaScript to run this app.</noscript>
12+
<div id="root"></div>
13+
14+
<script src="/src/index.tsx" type="module"></script>
15+
</body>
16+
</html>

‎examples/solid/filters/package.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "tanstack-table-example-solid-filters",
3+
"version": "0.0.0",
4+
"description": "",
5+
"scripts": {
6+
"start": "vite",
7+
"dev": "vite",
8+
"build": "vite build",
9+
"serve": "vite preview"
10+
},
11+
"license": "MIT",
12+
"devDependencies": {
13+
"@faker-js/faker": "^8.3.1",
14+
"typescript": "5.2.2",
15+
"vite": "^3.2.3",
16+
"vite-plugin-solid": "^2.2.6"
17+
},
18+
"dependencies": {
19+
"@solid-primitives/scheduled": "^1.4.1",
20+
"@tanstack/solid-table": "8.11.2",
21+
"solid-js": "^1.6.2"
22+
}
23+
}

‎examples/solid/filters/src/App.tsx

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import {
2+
flexRender,
3+
getCoreRowModel,
4+
getFilteredRowModel,
5+
getFacetedRowModel,
6+
getFacetedUniqueValues,
7+
getFacetedMinMaxValues,
8+
ColumnDef,
9+
ColumnFiltersState,
10+
createSolidTable,
11+
} from '@tanstack/solid-table'
12+
import { debounce } from '@solid-primitives/scheduled'
13+
import { makeData, Person } from './makeData'
14+
import ColumnFilter from './ColumnFilter'
15+
import { createSignal, For } from 'solid-js'
16+
17+
18+
const columns: ColumnDef<Person>[] = [
19+
{
20+
header: 'Name',
21+
footer: props => props.column.id,
22+
columns: [
23+
{
24+
accessorKey: 'firstName',
25+
cell: info => info.getValue(),
26+
footer: props => props.column.id,
27+
},
28+
{
29+
accessorFn: row => row.lastName,
30+
id: 'lastName',
31+
cell: info => info.getValue(),
32+
header: () => <span>Last Name</span>,
33+
footer: props => props.column.id,
34+
},
35+
],
36+
},
37+
{
38+
header: 'Info',
39+
footer: props => props.column.id,
40+
columns: [
41+
{
42+
accessorKey: 'age',
43+
header: () => 'Age',
44+
footer: props => props.column.id,
45+
},
46+
{
47+
header: 'More Info',
48+
columns: [
49+
{
50+
accessorKey: 'visits',
51+
header: () => <span>Visits</span>,
52+
footer: props => props.column.id,
53+
},
54+
{
55+
accessorKey: 'status',
56+
header: 'Status',
57+
footer: props => props.column.id,
58+
},
59+
{
60+
accessorKey: 'progress',
61+
header: 'Profile Progress',
62+
footer: props => props.column.id,
63+
},
64+
],
65+
},
66+
],
67+
},
68+
]
69+
70+
function App() {
71+
const [data, setData] = createSignal(makeData(50000))
72+
const [columnFilters, setColumnFilters] = createSignal<ColumnFiltersState>([])
73+
const [globalFilter, setGlobalFilter] = createSignal("")
74+
const debounceSetGlobalFilter = debounce((value:string) => setGlobalFilter(value), 500)
75+
const refreshData = () => setData(makeData(50000))
76+
77+
const table = createSolidTable({
78+
get data() {
79+
return data()
80+
},
81+
columns,
82+
state: {
83+
get columnFilters() {
84+
return columnFilters()
85+
},
86+
get globalFilter() {
87+
return globalFilter()
88+
},
89+
},
90+
onGlobalFilterChange: setGlobalFilter,
91+
globalFilterFn: "includesString",
92+
onColumnFiltersChange: setColumnFilters,
93+
getCoreRowModel: getCoreRowModel(),
94+
getFilteredRowModel: getFilteredRowModel(),
95+
getFacetedRowModel: getFacetedRowModel(),
96+
getFacetedUniqueValues: getFacetedUniqueValues(),
97+
getFacetedMinMaxValues: getFacetedMinMaxValues(),
98+
debugTable: true,
99+
debugHeaders: true,
100+
debugColumns: false,
101+
})
102+
103+
return (
104+
<div class="p-2">
105+
<input
106+
class="p-2 font-lg shadow border border-block"
107+
value={globalFilter() ?? ""}
108+
onInput={(e) => debounceSetGlobalFilter(e.currentTarget.value)}
109+
placeholder="Search all columns..."
110+
/>
111+
<div className="h-2" />
112+
<table>
113+
<thead>
114+
<For each={table.getHeaderGroups()}>
115+
{headerGroup => (
116+
<tr>
117+
<For each={headerGroup.headers}>
118+
{header => (
119+
<th colSpan={header.colSpan}>
120+
{header.isPlaceholder ? null : (
121+
<>
122+
{flexRender(
123+
header.column.columnDef.header,
124+
header.getContext()
125+
)}
126+
{header.column.getCanFilter() ? (
127+
<div>
128+
<ColumnFilter column={header.column} table={table} />
129+
</div>
130+
) : null}
131+
</>
132+
)}
133+
</th>
134+
)}
135+
</For>
136+
</tr>
137+
)}
138+
</For>
139+
</thead>
140+
<tbody>
141+
<For each={table.getRowModel().rows.slice(0, 10)}>
142+
{row => (
143+
<tr>
144+
<For each={row.getVisibleCells()}>
145+
{cell => (
146+
<td>
147+
{flexRender(
148+
cell.column.columnDef.cell,
149+
cell.getContext()
150+
)}
151+
</td>
152+
)}
153+
</For>
154+
</tr>
155+
)}
156+
</For>
157+
</tbody>
158+
</table>
159+
<div>{table.getRowModel().rows.length} Rows</div>
160+
<div>
161+
<button onClick={() => refreshData()}>Refresh Data</button>
162+
</div>
163+
<pre>{JSON.stringify(table.getState(), null, 2)}</pre>
164+
</div>
165+
)
166+
}
167+
168+
export default App
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import {
2+
Column,
3+
Table,
4+
} from '@tanstack/solid-table'
5+
import { debounce } from '@solid-primitives/scheduled'
6+
import { createMemo } from 'solid-js'
7+
8+
function ColumnFilter({
9+
column,
10+
table,
11+
}: {
12+
column: Column<any, unknown>
13+
table: Table<any>
14+
}) {
15+
const firstValue = table
16+
.getPreFilteredRowModel()
17+
.flatRows[0]?.getValue(column.id)
18+
19+
const columnFilterValue = column.getFilterValue()
20+
21+
const sortedUniqueValues = createMemo(
22+
() =>
23+
typeof firstValue === 'number'
24+
? []
25+
: Array.from(column.getFacetedUniqueValues().keys()).sort(),
26+
[column.getFacetedUniqueValues()]
27+
)
28+
29+
const debounceColumnFilter = debounce(value => column.setFilterValue(value), 500)
30+
31+
return typeof firstValue === 'number' ? (
32+
<div>
33+
<div className="flex space-x-2">
34+
<input
35+
type="number"
36+
min={Number(column.getFacetedMinMaxValues()?.[0] ?? '')}
37+
max={Number(column.getFacetedMinMaxValues()?.[1] ?? '')}
38+
value={(columnFilterValue as [number, number])?.[0] ?? ''}
39+
onInput={(e) => debounceColumnFilter((old: [number, number]) => [e.target.value, old?.[1]])}
40+
placeholder={`Min ${
41+
column.getFacetedMinMaxValues()?.[0]
42+
? `(${column.getFacetedMinMaxValues()?.[0]})`
43+
: ''
44+
}`}
45+
className="w-24 border shadow rounded"
46+
/>
47+
<input
48+
type="number"
49+
min={Number(column.getFacetedMinMaxValues()?.[0] ?? '')}
50+
max={Number(column.getFacetedMinMaxValues()?.[1] ?? '')}
51+
value={(columnFilterValue as [number, number])?.[1] ?? ''}
52+
onInput={(e) => debounceColumnFilter((old: [number, number]) => [old?.[0], e.target.value])}
53+
placeholder={`Max ${
54+
column.getFacetedMinMaxValues()?.[1]
55+
? `(${column.getFacetedMinMaxValues()?.[1]})`
56+
: ''
57+
}`}
58+
className="w-24 border shadow rounded"
59+
/>
60+
</div>
61+
<div className="h-1" />
62+
</div>
63+
) : (
64+
<>
65+
<datalist id={column.id + 'list'}>
66+
{sortedUniqueValues().slice(0, 5000).map((value: any) => (
67+
<option value={value} key={value} />
68+
))}
69+
</datalist>
70+
<input
71+
type="text"
72+
value={(columnFilterValue ?? '') as string}
73+
onInput={(e) => debounceColumnFilter(e.target.value)}
74+
placeholder={`Search... (${column.getFacetedUniqueValues().size})`}
75+
className="w-36 border shadow rounded"
76+
list={column.id + 'list'}
77+
/>
78+
<div className="h-1" />
79+
</>
80+
)
81+
}
82+
83+
export default ColumnFilter

‎examples/solid/filters/src/index.css

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
html {
2+
font-family: sans-serif;
3+
font-size: 14px;
4+
}
5+
6+
table {
7+
border: 1px solid lightgray;
8+
}
9+
10+
tbody {
11+
border-bottom: 1px solid lightgray;
12+
}
13+
14+
th {
15+
border-bottom: 1px solid lightgray;
16+
border-right: 1px solid lightgray;
17+
padding: 2px 4px;
18+
}
19+
20+
tfoot {
21+
color: gray;
22+
}
23+
24+
tfoot th {
25+
font-weight: normal;
26+
}

‎examples/solid/filters/src/index.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* @refresh reload */
2+
import { render } from 'solid-js/web'
3+
4+
import './index.css'
5+
import App from './App'
6+
7+
render(() => <App />, document.getElementById('root') as HTMLElement)
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { faker } from '@faker-js/faker'
2+
3+
export type Person = {
4+
firstName: string
5+
lastName: string
6+
age: number
7+
visits: number
8+
progress: number
9+
status: 'relationship' | 'complicated' | 'single'
10+
subRows?: Person[]
11+
}
12+
13+
const range = (len: number) => {
14+
const arr = []
15+
for (let i = 0; i < len; i++) {
16+
arr.push(i)
17+
}
18+
return arr
19+
}
20+
21+
const newPerson = (): Person => {
22+
return {
23+
firstName: faker.person.firstName(),
24+
lastName: faker.person.lastName(),
25+
age: faker.number.int(40),
26+
visits: faker.number.int(1000),
27+
progress: faker.number.int(100),
28+
status: faker.helpers.shuffle<Person['status']>([
29+
'relationship',
30+
'complicated',
31+
'single',
32+
])[0]!,
33+
}
34+
}
35+
36+
export function makeData(...lens: number[]) {
37+
const makeDataLevel = (depth = 0): Person[] => {
38+
const len = lens[depth]!
39+
return range(len).map((d): Person => {
40+
return {
41+
...newPerson(),
42+
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
43+
}
44+
})
45+
}
46+
47+
return makeDataLevel()
48+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": "../../../tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "./build/types",
5+
"jsxImportSource": "solid-js",
6+
"jsx": "preserve"
7+
},
8+
"files": ["src/index.tsx"],
9+
"include": [
10+
"src"
11+
// "__tests__/**/*.test.*"
12+
]
13+
}

‎examples/solid/filters/vite.config.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineConfig } from 'vite'
2+
import solidPlugin from 'vite-plugin-solid'
3+
4+
export default defineConfig({
5+
plugins: [solidPlugin()],
6+
build: {
7+
target: 'esnext',
8+
polyfillDynamicImport: false,
9+
},
10+
})

0 commit comments

Comments
 (0)
Please sign in to comment.