Skip to content

Commit 7541745

Browse files
authored
docs: add sticky example (TanStack#169)
1 parent 7a799a2 commit 7541745

File tree

11 files changed

+956
-0
lines changed

11 files changed

+956
-0
lines changed

docs/src/manifests/manifest.json

+6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@
5858
"title": "Smooth Scroll",
5959
"path": "/examples/smooth-scroll",
6060
"editUrl": "/examples/smooth-scroll.md"
61+
},
62+
63+
{
64+
"title": "Sticky",
65+
"path": "/examples/sticky",
66+
"editUrl": "/examples/sticky.md"
6167
}
6268
]
6369
}

docs/src/pages/examples/sticky.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
id: sticky
3+
title: Sticky
4+
toc: false
5+
---
6+
7+
- [Open in CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-virtual/tree/master/examples/sticky)
8+
- [View Source](https://github.com/tannerlinsley/react-virtual/tree/master/examples/sticky)
9+
10+
<iframe
11+
src="https://codesandbox.io/embed/github/tannerlinsley/react-virtual/tree/master/examples/sticky?autoresize=1&fontsize=14&theme=dark"
12+
title="tannerlinsley/react-virtual: sticky"
13+
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
14+
style={{
15+
width: '100%',
16+
height: '80vh',
17+
border: '0',
18+
borderRadius: 8,
19+
overflow: 'hidden',
20+
position: 'static',
21+
zIndex: 0,
22+
}}
23+
></iframe>

examples/sticky/.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
.DS_Store
3+
dist
4+
dist-ssr
5+
*.local

examples/sticky/index.html

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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.0" />
6+
<title>Vite App</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="/src/main.jsx"></script>
11+
</body>
12+
</html>

examples/sticky/package.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "sticky",
3+
"private": true,
4+
"scripts": {
5+
"dev": "vite",
6+
"build": "vite build",
7+
"serve": "vite preview",
8+
"start": "vite"
9+
},
10+
"dependencies": {
11+
"faker": "^5.5.3",
12+
"lodash": "^4.17.21",
13+
"react": "^17.0.2",
14+
"react-dom": "^17.0.2",
15+
"react-virtual": "2.8.2"
16+
},
17+
"devDependencies": {
18+
"@rollup/plugin-replace": "^3.0.0",
19+
"@vitejs/plugin-react": "^1.0.8",
20+
"vite": "^2.6.14"
21+
}
22+
}

examples/sticky/src/App.css

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.List {
2+
border: 1px solid #e6e4dc;
3+
max-width: 100%;
4+
}
5+
6+
.ListItem {
7+
display: flex;
8+
align-items: center;
9+
justify-content: center;
10+
}

examples/sticky/src/App.jsx

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import './App.css'
2+
import * as React from 'react'
3+
import faker from 'faker'
4+
import { findIndex, groupBy } from 'lodash'
5+
import { useVirtual, defaultRangeExtractor } from 'react-virtual'
6+
7+
const groupedNames = groupBy(
8+
Array.from({ length: 1000 })
9+
.map(() => faker.name.firstName())
10+
.sort(),
11+
name => name[0]
12+
)
13+
const groups = Object.keys(groupedNames)
14+
const rows = groups.reduce((acc, k) => [...acc, k, ...groupedNames[k]], [])
15+
16+
const App = () => {
17+
const parentRef = React.useRef()
18+
19+
const activeStickyIndexRef = React.useRef(0)
20+
21+
const stickyIndexes = React.useMemo(
22+
() => groups.map(gn => findIndex(rows, n => n === gn)),
23+
[]
24+
)
25+
26+
const isSticky = index => stickyIndexes.includes(index)
27+
28+
const isActiveSticky = index => activeStickyIndexRef.current === index
29+
30+
const rowVirtualizer = useVirtual({
31+
estimateSize: React.useCallback(() => 50, []),
32+
size: rows.length,
33+
parentRef,
34+
rangeExtractor: React.useCallback(
35+
range => {
36+
activeStickyIndexRef.current = [...stickyIndexes]
37+
.reverse()
38+
.find(index => range.start >= index)
39+
40+
const next = new Set([
41+
activeStickyIndexRef.current,
42+
...defaultRangeExtractor(range),
43+
])
44+
45+
return [...next].sort((a, b) => a - b)
46+
},
47+
[stickyIndexes]
48+
),
49+
})
50+
51+
return (
52+
<div>
53+
<div
54+
ref={parentRef}
55+
className="List"
56+
style={{
57+
height: `300px`,
58+
width: `400px`,
59+
overflow: 'auto',
60+
}}
61+
>
62+
<div
63+
style={{
64+
height: `${rowVirtualizer.totalSize}px`,
65+
width: '100%',
66+
position: 'relative',
67+
}}
68+
>
69+
{rowVirtualizer.virtualItems.map(virtualRow => (
70+
<div
71+
key={virtualRow.index}
72+
className={'ListItem'}
73+
style={{
74+
...(isSticky(virtualRow.index)
75+
? {
76+
background: '#fff',
77+
borderBottom: '1px solid #ddd',
78+
zIndex: 1,
79+
}
80+
: {}),
81+
...(isActiveSticky(virtualRow.index)
82+
? {
83+
position: 'sticky',
84+
}
85+
: {
86+
position: 'absolute',
87+
transform: `translateY(${virtualRow.start}px)`,
88+
}),
89+
top: 0,
90+
left: 0,
91+
width: '100%',
92+
height: `${virtualRow.size}px`,
93+
}}
94+
>
95+
{rows[virtualRow.index]}
96+
</div>
97+
))}
98+
</div>
99+
</div>
100+
{process.env.NODE_ENV === 'development' ? (
101+
<p>
102+
<strong>Notice:</strong> You are currently running React in
103+
development mode. Rendering performance will be slightly degraded
104+
until this application is build for production.
105+
</p>
106+
) : null}
107+
</div>
108+
)
109+
}
110+
111+
export default App

examples/sticky/src/index.css

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
body {
2+
margin: 0;
3+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5+
sans-serif;
6+
-webkit-font-smoothing: antialiased;
7+
-moz-osx-font-smoothing: grayscale;
8+
}

examples/sticky/src/main.jsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom'
3+
import './index.css'
4+
import App from './App'
5+
6+
ReactDOM.render(
7+
<React.StrictMode>
8+
<App />
9+
</React.StrictMode>,
10+
document.getElementById('root')
11+
)

examples/sticky/vite.config.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import * as path from 'path'
2+
import { defineConfig } from 'vite'
3+
import react from '@vitejs/plugin-react'
4+
import rollupReplace from '@rollup/plugin-replace'
5+
6+
// https://vitejs.dev/config/
7+
export default defineConfig({
8+
plugins: [
9+
rollupReplace({
10+
preventAssignment: true,
11+
values: {
12+
__DEV__: JSON.stringify(true),
13+
'process.env.NODE_ENV': JSON.stringify('development'),
14+
},
15+
}),
16+
react(),
17+
],
18+
resolve: process.env.USE_SOURCE
19+
? {
20+
alias: {
21+
'react-virtual': path.resolve(__dirname, '../../src/index.js'),
22+
},
23+
}
24+
: {},
25+
})

0 commit comments

Comments
 (0)