├── .babelrc
├── .editorconfig
├── .eslintrc
├── .github
└── workflows
│ └── npmpublish.yml
├── .gitignore
├── .npmignore
├── .nvmrc
├── .prettierrc
├── LICENSE
├── README.md
├── close.svg
├── example
├── .gitignore
├── index.html
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── public
│ └── favicon.svg
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── Example.module.css
│ ├── Example.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── logo.svg
│ └── vite-env.d.ts
├── tsconfig.json
└── vite.config.ts
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── src
├── .eslintrc
├── globals.d.ts
├── index.module.css
├── index.tsx
└── useCustomScroller.ts
├── tsconfig.json
└── tsup.config.ts
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/env",
5 | {
6 | "modules": false
7 | }
8 | ],
9 | "@babel/react"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": ["prettier", "prettier/react"],
4 | "env": {
5 | "es6": true
6 | },
7 | "plugins": ["prettier", "react", "react-hooks"],
8 | "parserOptions": {
9 | "sourceType": "module"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.github/workflows/npmpublish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to npm
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v1
12 | - uses: actions/setup-node@v1
13 | with:
14 | node-version: 12
15 | - run: yarn
16 | - run: yarn test
17 |
18 | publish-npm:
19 | needs: build
20 | runs-on: ubuntu-latest
21 | steps:
22 | - uses: actions/checkout@v1
23 | - uses: actions/setup-node@v1
24 | with:
25 | node-version: 12
26 | registry-url: https://registry.npmjs.org/
27 | - run: yarn
28 | - run: npm publish
29 | env:
30 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # builds
8 | build
9 | dist
10 | .rpt2_cache
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .git
2 | yarn.lock
3 | rollup*
4 | .eslint*
5 | .babelrc
6 | .prettierrc
7 | .gitignore
8 | src
9 | example
10 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v16
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Elastic Inc. (Close.io)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-custom-scroller
2 |
3 | [](https://www.npmjs.com/package/react-custom-scroller) [](https://prettier.io)
4 |
5 | Super simple React component for creating a custom scrollbar cross-browser and cross-devices.
6 |
7 | [**Check the live DEMO**](https://closeio.github.io/react-custom-scroller/).
8 |
9 | ###
10 |
11 | Interested in working on projects like this? [Close](https://close.com) is looking for [great engineers](https://jobs.close.com) to join our team!
12 |
13 | ## Install
14 |
15 | ```bash
16 | yarn add react-custom-scroller
17 | ```
18 |
19 | ## Benefits
20 |
21 | - Extremely lightweight (less than 2KB minzipped).
22 | - It uses the native scroll events, so all the events work and are smooth (mouse wheel, space, page down, page up, arrows etc).
23 | - No other 3rd-party dependencies.
24 | - The performance is excellent!
25 |
26 | ## Usage
27 |
28 | ```jsx
29 | import React from 'react';
30 | import CustomScroller from 'react-custom-scroller';
31 | import 'react-custom-scroller/dist/index.css';
32 |
33 | const MyScrollableDiv = () => (
34 | Content goes here.
35 | );
36 | ```
37 |
38 | ## License
39 |
40 | MIT © [Close](https://github.com/closeio)
41 |
--------------------------------------------------------------------------------
/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
17 |
19 |
21 |
23 |
25 |
26 |
29 |
32 |
35 |
37 |
40 |
43 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | React Custom Scroller
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-custom-scroller-example",
3 | "homepage": "https://closeio.github.io/react-custom-scroller",
4 | "version": "0.0.0",
5 | "license": "MIT",
6 | "private": true,
7 | "type": "module",
8 | "scripts": {
9 | "dev": "vite",
10 | "build": "tsc -b && vite build",
11 | "lint": "eslint .",
12 | "preview": "vite preview",
13 | "prepare": "pnpm build"
14 | },
15 | "dependencies": {
16 | "react": "^18.3.1",
17 | "react-dom": "^18.3.1",
18 | "react-custom-scroller": "2.0.0"
19 | },
20 | "devDependencies": {
21 | "@types/react": "^18.3.12",
22 | "@types/react-dom": "^18.3.1",
23 | "@vitejs/plugin-react-swc": "^3.5.0",
24 | "globals": "^15.11.0",
25 | "typescript": "^5.7.2",
26 | "vite": "^5.4.10"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/example/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '9.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | importers:
8 |
9 | .:
10 | dependencies:
11 | react:
12 | specifier: ^18.3.1
13 | version: 18.3.1
14 | react-custom-scroller:
15 | specifier: 2.0.0
16 | version: 2.0.0(react-dom@18.3.1)(react@18.3.1)
17 | react-dom:
18 | specifier: ^18.3.1
19 | version: 18.3.1(react@18.3.1)
20 | devDependencies:
21 | '@types/react':
22 | specifier: ^18.3.12
23 | version: 18.3.12
24 | '@types/react-dom':
25 | specifier: ^18.3.1
26 | version: 18.3.1
27 | '@vitejs/plugin-react-swc':
28 | specifier: ^3.5.0
29 | version: 3.7.1(vite@5.4.11)
30 | globals:
31 | specifier: ^15.11.0
32 | version: 15.12.0
33 | typescript:
34 | specifier: ^5.7.2
35 | version: 5.7.2
36 | vite:
37 | specifier: ^5.4.10
38 | version: 5.4.11
39 |
40 | packages:
41 |
42 | '@esbuild/aix-ppc64@0.21.5':
43 | resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
44 | engines: {node: '>=12'}
45 | cpu: [ppc64]
46 | os: [aix]
47 |
48 | '@esbuild/android-arm64@0.21.5':
49 | resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
50 | engines: {node: '>=12'}
51 | cpu: [arm64]
52 | os: [android]
53 |
54 | '@esbuild/android-arm@0.21.5':
55 | resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
56 | engines: {node: '>=12'}
57 | cpu: [arm]
58 | os: [android]
59 |
60 | '@esbuild/android-x64@0.21.5':
61 | resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
62 | engines: {node: '>=12'}
63 | cpu: [x64]
64 | os: [android]
65 |
66 | '@esbuild/darwin-arm64@0.21.5':
67 | resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
68 | engines: {node: '>=12'}
69 | cpu: [arm64]
70 | os: [darwin]
71 |
72 | '@esbuild/darwin-x64@0.21.5':
73 | resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
74 | engines: {node: '>=12'}
75 | cpu: [x64]
76 | os: [darwin]
77 |
78 | '@esbuild/freebsd-arm64@0.21.5':
79 | resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
80 | engines: {node: '>=12'}
81 | cpu: [arm64]
82 | os: [freebsd]
83 |
84 | '@esbuild/freebsd-x64@0.21.5':
85 | resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
86 | engines: {node: '>=12'}
87 | cpu: [x64]
88 | os: [freebsd]
89 |
90 | '@esbuild/linux-arm64@0.21.5':
91 | resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
92 | engines: {node: '>=12'}
93 | cpu: [arm64]
94 | os: [linux]
95 |
96 | '@esbuild/linux-arm@0.21.5':
97 | resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
98 | engines: {node: '>=12'}
99 | cpu: [arm]
100 | os: [linux]
101 |
102 | '@esbuild/linux-ia32@0.21.5':
103 | resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
104 | engines: {node: '>=12'}
105 | cpu: [ia32]
106 | os: [linux]
107 |
108 | '@esbuild/linux-loong64@0.21.5':
109 | resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
110 | engines: {node: '>=12'}
111 | cpu: [loong64]
112 | os: [linux]
113 |
114 | '@esbuild/linux-mips64el@0.21.5':
115 | resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
116 | engines: {node: '>=12'}
117 | cpu: [mips64el]
118 | os: [linux]
119 |
120 | '@esbuild/linux-ppc64@0.21.5':
121 | resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
122 | engines: {node: '>=12'}
123 | cpu: [ppc64]
124 | os: [linux]
125 |
126 | '@esbuild/linux-riscv64@0.21.5':
127 | resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
128 | engines: {node: '>=12'}
129 | cpu: [riscv64]
130 | os: [linux]
131 |
132 | '@esbuild/linux-s390x@0.21.5':
133 | resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
134 | engines: {node: '>=12'}
135 | cpu: [s390x]
136 | os: [linux]
137 |
138 | '@esbuild/linux-x64@0.21.5':
139 | resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
140 | engines: {node: '>=12'}
141 | cpu: [x64]
142 | os: [linux]
143 |
144 | '@esbuild/netbsd-x64@0.21.5':
145 | resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
146 | engines: {node: '>=12'}
147 | cpu: [x64]
148 | os: [netbsd]
149 |
150 | '@esbuild/openbsd-x64@0.21.5':
151 | resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
152 | engines: {node: '>=12'}
153 | cpu: [x64]
154 | os: [openbsd]
155 |
156 | '@esbuild/sunos-x64@0.21.5':
157 | resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
158 | engines: {node: '>=12'}
159 | cpu: [x64]
160 | os: [sunos]
161 |
162 | '@esbuild/win32-arm64@0.21.5':
163 | resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
164 | engines: {node: '>=12'}
165 | cpu: [arm64]
166 | os: [win32]
167 |
168 | '@esbuild/win32-ia32@0.21.5':
169 | resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
170 | engines: {node: '>=12'}
171 | cpu: [ia32]
172 | os: [win32]
173 |
174 | '@esbuild/win32-x64@0.21.5':
175 | resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
176 | engines: {node: '>=12'}
177 | cpu: [x64]
178 | os: [win32]
179 |
180 | '@rollup/rollup-android-arm-eabi@4.27.4':
181 | resolution: {integrity: sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==}
182 | cpu: [arm]
183 | os: [android]
184 |
185 | '@rollup/rollup-android-arm64@4.27.4':
186 | resolution: {integrity: sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==}
187 | cpu: [arm64]
188 | os: [android]
189 |
190 | '@rollup/rollup-darwin-arm64@4.27.4':
191 | resolution: {integrity: sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==}
192 | cpu: [arm64]
193 | os: [darwin]
194 |
195 | '@rollup/rollup-darwin-x64@4.27.4':
196 | resolution: {integrity: sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==}
197 | cpu: [x64]
198 | os: [darwin]
199 |
200 | '@rollup/rollup-freebsd-arm64@4.27.4':
201 | resolution: {integrity: sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==}
202 | cpu: [arm64]
203 | os: [freebsd]
204 |
205 | '@rollup/rollup-freebsd-x64@4.27.4':
206 | resolution: {integrity: sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==}
207 | cpu: [x64]
208 | os: [freebsd]
209 |
210 | '@rollup/rollup-linux-arm-gnueabihf@4.27.4':
211 | resolution: {integrity: sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==}
212 | cpu: [arm]
213 | os: [linux]
214 |
215 | '@rollup/rollup-linux-arm-musleabihf@4.27.4':
216 | resolution: {integrity: sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==}
217 | cpu: [arm]
218 | os: [linux]
219 |
220 | '@rollup/rollup-linux-arm64-gnu@4.27.4':
221 | resolution: {integrity: sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==}
222 | cpu: [arm64]
223 | os: [linux]
224 |
225 | '@rollup/rollup-linux-arm64-musl@4.27.4':
226 | resolution: {integrity: sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==}
227 | cpu: [arm64]
228 | os: [linux]
229 |
230 | '@rollup/rollup-linux-powerpc64le-gnu@4.27.4':
231 | resolution: {integrity: sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==}
232 | cpu: [ppc64]
233 | os: [linux]
234 |
235 | '@rollup/rollup-linux-riscv64-gnu@4.27.4':
236 | resolution: {integrity: sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==}
237 | cpu: [riscv64]
238 | os: [linux]
239 |
240 | '@rollup/rollup-linux-s390x-gnu@4.27.4':
241 | resolution: {integrity: sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==}
242 | cpu: [s390x]
243 | os: [linux]
244 |
245 | '@rollup/rollup-linux-x64-gnu@4.27.4':
246 | resolution: {integrity: sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==}
247 | cpu: [x64]
248 | os: [linux]
249 |
250 | '@rollup/rollup-linux-x64-musl@4.27.4':
251 | resolution: {integrity: sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==}
252 | cpu: [x64]
253 | os: [linux]
254 |
255 | '@rollup/rollup-win32-arm64-msvc@4.27.4':
256 | resolution: {integrity: sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==}
257 | cpu: [arm64]
258 | os: [win32]
259 |
260 | '@rollup/rollup-win32-ia32-msvc@4.27.4':
261 | resolution: {integrity: sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==}
262 | cpu: [ia32]
263 | os: [win32]
264 |
265 | '@rollup/rollup-win32-x64-msvc@4.27.4':
266 | resolution: {integrity: sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==}
267 | cpu: [x64]
268 | os: [win32]
269 |
270 | '@swc/core-darwin-arm64@1.9.3':
271 | resolution: {integrity: sha512-hGfl/KTic/QY4tB9DkTbNuxy5cV4IeejpPD4zo+Lzt4iLlDWIeANL4Fkg67FiVceNJboqg48CUX+APhDHO5G1w==}
272 | engines: {node: '>=10'}
273 | cpu: [arm64]
274 | os: [darwin]
275 |
276 | '@swc/core-darwin-x64@1.9.3':
277 | resolution: {integrity: sha512-IaRq05ZLdtgF5h9CzlcgaNHyg4VXuiStnOFpfNEMuI5fm5afP2S0FHq8WdakUz5WppsbddTdplL+vpeApt/WCQ==}
278 | engines: {node: '>=10'}
279 | cpu: [x64]
280 | os: [darwin]
281 |
282 | '@swc/core-linux-arm-gnueabihf@1.9.3':
283 | resolution: {integrity: sha512-Pbwe7xYprj/nEnZrNBvZfjnTxlBIcfApAGdz2EROhjpPj+FBqBa3wOogqbsuGGBdCphf8S+KPprL1z+oDWkmSQ==}
284 | engines: {node: '>=10'}
285 | cpu: [arm]
286 | os: [linux]
287 |
288 | '@swc/core-linux-arm64-gnu@1.9.3':
289 | resolution: {integrity: sha512-AQ5JZiwNGVV/2K2TVulg0mw/3LYfqpjZO6jDPtR2evNbk9Yt57YsVzS+3vHSlUBQDRV9/jqMuZYVU3P13xrk+g==}
290 | engines: {node: '>=10'}
291 | cpu: [arm64]
292 | os: [linux]
293 |
294 | '@swc/core-linux-arm64-musl@1.9.3':
295 | resolution: {integrity: sha512-tzVH480RY6RbMl/QRgh5HK3zn1ZTFsThuxDGo6Iuk1MdwIbdFYUY034heWUTI4u3Db97ArKh0hNL0xhO3+PZdg==}
296 | engines: {node: '>=10'}
297 | cpu: [arm64]
298 | os: [linux]
299 |
300 | '@swc/core-linux-x64-gnu@1.9.3':
301 | resolution: {integrity: sha512-ivXXBRDXDc9k4cdv10R21ccBmGebVOwKXT/UdH1PhxUn9m/h8erAWjz5pcELwjiMf27WokqPgaWVfaclDbgE+w==}
302 | engines: {node: '>=10'}
303 | cpu: [x64]
304 | os: [linux]
305 |
306 | '@swc/core-linux-x64-musl@1.9.3':
307 | resolution: {integrity: sha512-ILsGMgfnOz1HwdDz+ZgEuomIwkP1PHT6maigZxaCIuC6OPEhKE8uYna22uU63XvYcLQvZYDzpR3ms47WQPuNEg==}
308 | engines: {node: '>=10'}
309 | cpu: [x64]
310 | os: [linux]
311 |
312 | '@swc/core-win32-arm64-msvc@1.9.3':
313 | resolution: {integrity: sha512-e+XmltDVIHieUnNJHtspn6B+PCcFOMYXNJB1GqoCcyinkEIQNwC8KtWgMqUucUbEWJkPc35NHy9k8aCXRmw9Kg==}
314 | engines: {node: '>=10'}
315 | cpu: [arm64]
316 | os: [win32]
317 |
318 | '@swc/core-win32-ia32-msvc@1.9.3':
319 | resolution: {integrity: sha512-rqpzNfpAooSL4UfQnHhkW8aL+oyjqJniDP0qwZfGnjDoJSbtPysHg2LpcOBEdSnEH+uIZq6J96qf0ZFD8AGfXA==}
320 | engines: {node: '>=10'}
321 | cpu: [ia32]
322 | os: [win32]
323 |
324 | '@swc/core-win32-x64-msvc@1.9.3':
325 | resolution: {integrity: sha512-3YJJLQ5suIEHEKc1GHtqVq475guiyqisKSoUnoaRtxkDaW5g1yvPt9IoSLOe2mRs7+FFhGGU693RsBUSwOXSdQ==}
326 | engines: {node: '>=10'}
327 | cpu: [x64]
328 | os: [win32]
329 |
330 | '@swc/core@1.9.3':
331 | resolution: {integrity: sha512-oRj0AFePUhtatX+BscVhnzaAmWjpfAeySpM1TCbxA1rtBDeH/JDhi5yYzAKneDYtVtBvA7ApfeuzhMC9ye4xSg==}
332 | engines: {node: '>=10'}
333 | peerDependencies:
334 | '@swc/helpers': '*'
335 | peerDependenciesMeta:
336 | '@swc/helpers':
337 | optional: true
338 |
339 | '@swc/counter@0.1.3':
340 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
341 |
342 | '@swc/types@0.1.17':
343 | resolution: {integrity: sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==}
344 |
345 | '@types/estree@1.0.6':
346 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
347 |
348 | '@types/prop-types@15.7.13':
349 | resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
350 |
351 | '@types/react-dom@18.3.1':
352 | resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==}
353 |
354 | '@types/react@18.3.12':
355 | resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==}
356 |
357 | '@vitejs/plugin-react-swc@3.7.1':
358 | resolution: {integrity: sha512-vgWOY0i1EROUK0Ctg1hwhtC3SdcDjZcdit4Ups4aPkDcB1jYhmo+RMYWY87cmXMhvtD5uf8lV89j2w16vkdSVg==}
359 | peerDependencies:
360 | vite: ^4 || ^5
361 |
362 | csstype@3.1.3:
363 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
364 |
365 | esbuild@0.21.5:
366 | resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
367 | engines: {node: '>=12'}
368 | hasBin: true
369 |
370 | fsevents@2.3.3:
371 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
372 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
373 | os: [darwin]
374 |
375 | globals@15.12.0:
376 | resolution: {integrity: sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==}
377 | engines: {node: '>=18'}
378 |
379 | js-tokens@4.0.0:
380 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
381 |
382 | loose-envify@1.4.0:
383 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
384 | hasBin: true
385 |
386 | nanoid@3.3.7:
387 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
388 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
389 | hasBin: true
390 |
391 | picocolors@1.1.1:
392 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
393 |
394 | postcss@8.4.49:
395 | resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
396 | engines: {node: ^10 || ^12 || >=14}
397 |
398 | react-custom-scroller@2.0.0:
399 | resolution: {integrity: sha512-uoqPdSout/9JUlYwE44mEm7pLPsEA8CfknuOCb/C3MHrVPIPBK+z66xIZ3KompCrjGOY16IPFLOkrwHqQiqqnQ==}
400 | engines: {node: '>=8', npm: '>=5'}
401 | peerDependencies:
402 | react: '>=16.8.0'
403 | react-dom: '>=16.8.0'
404 |
405 | react-dom@18.3.1:
406 | resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
407 | peerDependencies:
408 | react: ^18.3.1
409 |
410 | react@18.3.1:
411 | resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
412 | engines: {node: '>=0.10.0'}
413 |
414 | rollup@4.27.4:
415 | resolution: {integrity: sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==}
416 | engines: {node: '>=18.0.0', npm: '>=8.0.0'}
417 | hasBin: true
418 |
419 | scheduler@0.23.2:
420 | resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
421 |
422 | source-map-js@1.2.1:
423 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
424 | engines: {node: '>=0.10.0'}
425 |
426 | typescript@5.7.2:
427 | resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==}
428 | engines: {node: '>=14.17'}
429 | hasBin: true
430 |
431 | vite@5.4.11:
432 | resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==}
433 | engines: {node: ^18.0.0 || >=20.0.0}
434 | hasBin: true
435 | peerDependencies:
436 | '@types/node': ^18.0.0 || >=20.0.0
437 | less: '*'
438 | lightningcss: ^1.21.0
439 | sass: '*'
440 | sass-embedded: '*'
441 | stylus: '*'
442 | sugarss: '*'
443 | terser: ^5.4.0
444 | peerDependenciesMeta:
445 | '@types/node':
446 | optional: true
447 | less:
448 | optional: true
449 | lightningcss:
450 | optional: true
451 | sass:
452 | optional: true
453 | sass-embedded:
454 | optional: true
455 | stylus:
456 | optional: true
457 | sugarss:
458 | optional: true
459 | terser:
460 | optional: true
461 |
462 | snapshots:
463 |
464 | '@esbuild/aix-ppc64@0.21.5':
465 | optional: true
466 |
467 | '@esbuild/android-arm64@0.21.5':
468 | optional: true
469 |
470 | '@esbuild/android-arm@0.21.5':
471 | optional: true
472 |
473 | '@esbuild/android-x64@0.21.5':
474 | optional: true
475 |
476 | '@esbuild/darwin-arm64@0.21.5':
477 | optional: true
478 |
479 | '@esbuild/darwin-x64@0.21.5':
480 | optional: true
481 |
482 | '@esbuild/freebsd-arm64@0.21.5':
483 | optional: true
484 |
485 | '@esbuild/freebsd-x64@0.21.5':
486 | optional: true
487 |
488 | '@esbuild/linux-arm64@0.21.5':
489 | optional: true
490 |
491 | '@esbuild/linux-arm@0.21.5':
492 | optional: true
493 |
494 | '@esbuild/linux-ia32@0.21.5':
495 | optional: true
496 |
497 | '@esbuild/linux-loong64@0.21.5':
498 | optional: true
499 |
500 | '@esbuild/linux-mips64el@0.21.5':
501 | optional: true
502 |
503 | '@esbuild/linux-ppc64@0.21.5':
504 | optional: true
505 |
506 | '@esbuild/linux-riscv64@0.21.5':
507 | optional: true
508 |
509 | '@esbuild/linux-s390x@0.21.5':
510 | optional: true
511 |
512 | '@esbuild/linux-x64@0.21.5':
513 | optional: true
514 |
515 | '@esbuild/netbsd-x64@0.21.5':
516 | optional: true
517 |
518 | '@esbuild/openbsd-x64@0.21.5':
519 | optional: true
520 |
521 | '@esbuild/sunos-x64@0.21.5':
522 | optional: true
523 |
524 | '@esbuild/win32-arm64@0.21.5':
525 | optional: true
526 |
527 | '@esbuild/win32-ia32@0.21.5':
528 | optional: true
529 |
530 | '@esbuild/win32-x64@0.21.5':
531 | optional: true
532 |
533 | '@rollup/rollup-android-arm-eabi@4.27.4':
534 | optional: true
535 |
536 | '@rollup/rollup-android-arm64@4.27.4':
537 | optional: true
538 |
539 | '@rollup/rollup-darwin-arm64@4.27.4':
540 | optional: true
541 |
542 | '@rollup/rollup-darwin-x64@4.27.4':
543 | optional: true
544 |
545 | '@rollup/rollup-freebsd-arm64@4.27.4':
546 | optional: true
547 |
548 | '@rollup/rollup-freebsd-x64@4.27.4':
549 | optional: true
550 |
551 | '@rollup/rollup-linux-arm-gnueabihf@4.27.4':
552 | optional: true
553 |
554 | '@rollup/rollup-linux-arm-musleabihf@4.27.4':
555 | optional: true
556 |
557 | '@rollup/rollup-linux-arm64-gnu@4.27.4':
558 | optional: true
559 |
560 | '@rollup/rollup-linux-arm64-musl@4.27.4':
561 | optional: true
562 |
563 | '@rollup/rollup-linux-powerpc64le-gnu@4.27.4':
564 | optional: true
565 |
566 | '@rollup/rollup-linux-riscv64-gnu@4.27.4':
567 | optional: true
568 |
569 | '@rollup/rollup-linux-s390x-gnu@4.27.4':
570 | optional: true
571 |
572 | '@rollup/rollup-linux-x64-gnu@4.27.4':
573 | optional: true
574 |
575 | '@rollup/rollup-linux-x64-musl@4.27.4':
576 | optional: true
577 |
578 | '@rollup/rollup-win32-arm64-msvc@4.27.4':
579 | optional: true
580 |
581 | '@rollup/rollup-win32-ia32-msvc@4.27.4':
582 | optional: true
583 |
584 | '@rollup/rollup-win32-x64-msvc@4.27.4':
585 | optional: true
586 |
587 | '@swc/core-darwin-arm64@1.9.3':
588 | optional: true
589 |
590 | '@swc/core-darwin-x64@1.9.3':
591 | optional: true
592 |
593 | '@swc/core-linux-arm-gnueabihf@1.9.3':
594 | optional: true
595 |
596 | '@swc/core-linux-arm64-gnu@1.9.3':
597 | optional: true
598 |
599 | '@swc/core-linux-arm64-musl@1.9.3':
600 | optional: true
601 |
602 | '@swc/core-linux-x64-gnu@1.9.3':
603 | optional: true
604 |
605 | '@swc/core-linux-x64-musl@1.9.3':
606 | optional: true
607 |
608 | '@swc/core-win32-arm64-msvc@1.9.3':
609 | optional: true
610 |
611 | '@swc/core-win32-ia32-msvc@1.9.3':
612 | optional: true
613 |
614 | '@swc/core-win32-x64-msvc@1.9.3':
615 | optional: true
616 |
617 | '@swc/core@1.9.3':
618 | dependencies:
619 | '@swc/counter': 0.1.3
620 | '@swc/types': 0.1.17
621 | optionalDependencies:
622 | '@swc/core-darwin-arm64': 1.9.3
623 | '@swc/core-darwin-x64': 1.9.3
624 | '@swc/core-linux-arm-gnueabihf': 1.9.3
625 | '@swc/core-linux-arm64-gnu': 1.9.3
626 | '@swc/core-linux-arm64-musl': 1.9.3
627 | '@swc/core-linux-x64-gnu': 1.9.3
628 | '@swc/core-linux-x64-musl': 1.9.3
629 | '@swc/core-win32-arm64-msvc': 1.9.3
630 | '@swc/core-win32-ia32-msvc': 1.9.3
631 | '@swc/core-win32-x64-msvc': 1.9.3
632 |
633 | '@swc/counter@0.1.3': {}
634 |
635 | '@swc/types@0.1.17':
636 | dependencies:
637 | '@swc/counter': 0.1.3
638 |
639 | '@types/estree@1.0.6': {}
640 |
641 | '@types/prop-types@15.7.13': {}
642 |
643 | '@types/react-dom@18.3.1':
644 | dependencies:
645 | '@types/react': 18.3.12
646 |
647 | '@types/react@18.3.12':
648 | dependencies:
649 | '@types/prop-types': 15.7.13
650 | csstype: 3.1.3
651 |
652 | '@vitejs/plugin-react-swc@3.7.1(vite@5.4.11)':
653 | dependencies:
654 | '@swc/core': 1.9.3
655 | vite: 5.4.11
656 | transitivePeerDependencies:
657 | - '@swc/helpers'
658 |
659 | csstype@3.1.3: {}
660 |
661 | esbuild@0.21.5:
662 | optionalDependencies:
663 | '@esbuild/aix-ppc64': 0.21.5
664 | '@esbuild/android-arm': 0.21.5
665 | '@esbuild/android-arm64': 0.21.5
666 | '@esbuild/android-x64': 0.21.5
667 | '@esbuild/darwin-arm64': 0.21.5
668 | '@esbuild/darwin-x64': 0.21.5
669 | '@esbuild/freebsd-arm64': 0.21.5
670 | '@esbuild/freebsd-x64': 0.21.5
671 | '@esbuild/linux-arm': 0.21.5
672 | '@esbuild/linux-arm64': 0.21.5
673 | '@esbuild/linux-ia32': 0.21.5
674 | '@esbuild/linux-loong64': 0.21.5
675 | '@esbuild/linux-mips64el': 0.21.5
676 | '@esbuild/linux-ppc64': 0.21.5
677 | '@esbuild/linux-riscv64': 0.21.5
678 | '@esbuild/linux-s390x': 0.21.5
679 | '@esbuild/linux-x64': 0.21.5
680 | '@esbuild/netbsd-x64': 0.21.5
681 | '@esbuild/openbsd-x64': 0.21.5
682 | '@esbuild/sunos-x64': 0.21.5
683 | '@esbuild/win32-arm64': 0.21.5
684 | '@esbuild/win32-ia32': 0.21.5
685 | '@esbuild/win32-x64': 0.21.5
686 |
687 | fsevents@2.3.3:
688 | optional: true
689 |
690 | globals@15.12.0: {}
691 |
692 | js-tokens@4.0.0: {}
693 |
694 | loose-envify@1.4.0:
695 | dependencies:
696 | js-tokens: 4.0.0
697 |
698 | nanoid@3.3.7: {}
699 |
700 | picocolors@1.1.1: {}
701 |
702 | postcss@8.4.49:
703 | dependencies:
704 | nanoid: 3.3.7
705 | picocolors: 1.1.1
706 | source-map-js: 1.2.1
707 |
708 | react-custom-scroller@2.0.0(react-dom@18.3.1)(react@18.3.1):
709 | dependencies:
710 | react: 18.3.1
711 | react-dom: 18.3.1(react@18.3.1)
712 |
713 | react-dom@18.3.1(react@18.3.1):
714 | dependencies:
715 | loose-envify: 1.4.0
716 | react: 18.3.1
717 | scheduler: 0.23.2
718 |
719 | react@18.3.1:
720 | dependencies:
721 | loose-envify: 1.4.0
722 |
723 | rollup@4.27.4:
724 | dependencies:
725 | '@types/estree': 1.0.6
726 | optionalDependencies:
727 | '@rollup/rollup-android-arm-eabi': 4.27.4
728 | '@rollup/rollup-android-arm64': 4.27.4
729 | '@rollup/rollup-darwin-arm64': 4.27.4
730 | '@rollup/rollup-darwin-x64': 4.27.4
731 | '@rollup/rollup-freebsd-arm64': 4.27.4
732 | '@rollup/rollup-freebsd-x64': 4.27.4
733 | '@rollup/rollup-linux-arm-gnueabihf': 4.27.4
734 | '@rollup/rollup-linux-arm-musleabihf': 4.27.4
735 | '@rollup/rollup-linux-arm64-gnu': 4.27.4
736 | '@rollup/rollup-linux-arm64-musl': 4.27.4
737 | '@rollup/rollup-linux-powerpc64le-gnu': 4.27.4
738 | '@rollup/rollup-linux-riscv64-gnu': 4.27.4
739 | '@rollup/rollup-linux-s390x-gnu': 4.27.4
740 | '@rollup/rollup-linux-x64-gnu': 4.27.4
741 | '@rollup/rollup-linux-x64-musl': 4.27.4
742 | '@rollup/rollup-win32-arm64-msvc': 4.27.4
743 | '@rollup/rollup-win32-ia32-msvc': 4.27.4
744 | '@rollup/rollup-win32-x64-msvc': 4.27.4
745 | fsevents: 2.3.3
746 |
747 | scheduler@0.23.2:
748 | dependencies:
749 | loose-envify: 1.4.0
750 |
751 | source-map-js@1.2.1: {}
752 |
753 | typescript@5.7.2: {}
754 |
755 | vite@5.4.11:
756 | dependencies:
757 | esbuild: 0.21.5
758 | postcss: 8.4.49
759 | rollup: 4.27.4
760 | optionalDependencies:
761 | fsevents: 2.3.3
762 |
--------------------------------------------------------------------------------
/example/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | autoprefixer: {},
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/example/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
10 |
12 |
13 |
15 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/example/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import Example from './Example';
2 |
3 | import logo from './logo.svg';
4 |
5 | export default function App() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/example/src/Example.module.css:
--------------------------------------------------------------------------------
1 | .scroller {
2 | max-width: 450px;
3 | height: 300px;
4 | text-align: justify;
5 | padding: 0 16px 0 0;
6 | }
7 |
8 | .content {
9 | display: grid;
10 | row-gap: 12px;
11 | }
12 |
13 | .content > p {
14 | margin: 0;
15 | }
16 |
--------------------------------------------------------------------------------
/example/src/Example.tsx:
--------------------------------------------------------------------------------
1 | import CustomScroller from 'react-custom-scroller';
2 | import 'react-custom-scroller/index.css';
3 |
4 | import styles from './Example.module.css';
5 |
6 | export default function Example() {
7 | return (
8 |
9 |
10 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
11 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
12 | veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
13 | commodo consequat.
14 |
15 |
16 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
17 | dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
18 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
19 | Sed ut perspiciatis unde omnis iste natus error sit voluptatem
20 | accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab
21 | illo inventore veritatis et quasi architecto beatae vitae dicta sunt
22 | explicabo.
23 |
24 |
25 | Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut
26 | fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem
27 | sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor
28 | sit amet, consectetur, adipisci velit, sed quia non numquam eius modi
29 | tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.
30 | Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis
31 | suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis
32 | autem vel eum iure reprehenderit qui in ea voluptate velit esse quam
33 | nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo
34 | voluptas nulla pariatur?
35 |
36 |
37 | But I must explain to you how all this mistaken idea of denouncing
38 | pleasure and praising pain was born and I will give you a complete
39 | account of the system, and expound the actual teachings of the great
40 | explorer of the truth, the master-builder of human happiness. No one
41 | rejects, dislikes, or avoids pleasure itself, because it is pleasure,
42 | but because those who do not know how to pursue pleasure rationally
43 | encounter consequences that are extremely painful. Nor again is there
44 | anyone who loves or pursues or desires to obtain pain of itself, because
45 | it is pain, but because occasionally circumstances occur in which toil
46 | and pain can procure him some great pleasure. To take a trivial example,
47 | which of us ever undertakes laborious physical exercise, except to
48 | obtain some advantage from it? But who has any right to find fault with
49 | a man who chooses to enjoy a pleasure that has no annoying consequences,
50 | or one who avoids a pain that produces no resultant pleasure?
51 |
52 |
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/example/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0 20px;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/example/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client';
2 |
3 | import './index.css';
4 | import App from './App';
5 |
6 | const root = createRoot(document.getElementById('root')!);
7 | root.render( );
8 |
--------------------------------------------------------------------------------
/example/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
13 |
16 |
19 |
22 |
25 |
28 |
31 |
33 |
36 |
39 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/example/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "esModuleInterop": true,
5 | "skipLibCheck": true,
6 | "jsx": "preserve",
7 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
8 | "sourceMap": true,
9 | "module": "esnext",
10 | "target": "ES2018",
11 | "moduleResolution": "node",
12 | "forceConsistentCasingInFileNames": true,
13 | "outDir": "./dist",
14 | "noImplicitReturns": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "strictNullChecks": true,
17 | "noEmit": true,
18 | "strict": true,
19 | "noImplicitAny": true,
20 | "resolveJsonModule": true
21 | },
22 | "include": ["**/*.ts", "**/*.tsx"],
23 | "exclude": ["node_modules", "**/node_modules/*", "dist", "**/dist/*"]
24 | }
25 |
--------------------------------------------------------------------------------
/example/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react-swc';
3 |
4 | export default defineConfig({
5 | base: '/react-custom-scroller/',
6 | plugins: [react()],
7 | });
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-custom-scroller",
3 | "version": "2.2.0",
4 | "description": "Super simple React component for creating a custom scrollbar cross-browser and cross-devices",
5 | "author": "Vitor Buzinaro ",
6 | "license": "MIT",
7 | "repository": "closeio/react-custom-scroller",
8 | "main": "dist/index.js",
9 | "module": "dist/index.mjs",
10 | "types": "dist/index.d.ts",
11 | "exports": {
12 | ".": {
13 | "import": {
14 | "types": "./dist/index.d.mts",
15 | "default": "./dist/index.mjs"
16 | },
17 | "require": {
18 | "types": "./dist/index.d.ts",
19 | "default": "./dist/index.js"
20 | }
21 | },
22 | "./dist/index.css": {
23 | "default": "./dist/index.css"
24 | }
25 | },
26 | "engines": {
27 | "node": ">=8",
28 | "npm": ">=5"
29 | },
30 | "scripts": {
31 | "build": "tsup src/index.tsx",
32 | "start": "tsup src/index.tsx --watch",
33 | "prepare": "pnpm build",
34 | "deploy": "cd example && pnpm i && gh-pages -d dist -u \"github-actions-bot \""
35 | },
36 | "peerDependencies": {
37 | "react": ">=16.8.0",
38 | "react-dom": ">=16.8.0"
39 | },
40 | "devDependencies": {
41 | "@babel/core": "^7.6.4",
42 | "@svgr/rollup": "^4.3.3",
43 | "@types/react": "^18.3.12",
44 | "@types/react-dom": "^18.3.1",
45 | "autoprefixer": "^10.4.20",
46 | "cross-env": "^6.0.3",
47 | "eslint": "^6.5.1",
48 | "eslint-config-prettier": "^6.4.0",
49 | "eslint-plugin-import": "^2.13.0",
50 | "eslint-plugin-react": "^7.16.0",
51 | "eslint-plugin-react-hooks": "^2.1.2",
52 | "gh-pages": "^2.1.1",
53 | "postcss": "^8.4.49",
54 | "tsup": "^8.3.5",
55 | "typescript": "^5.7.2"
56 | },
57 | "files": [
58 | "dist"
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {},
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/src/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/globals.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.module.css' {
2 | const styles: Record;
3 | export default styles;
4 | }
5 |
--------------------------------------------------------------------------------
/src/index.module.css:
--------------------------------------------------------------------------------
1 | .main {
2 | position: relative;
3 | }
4 |
5 | .main:hover .track {
6 | opacity: 0.75;
7 | }
8 |
9 | .wrapper {
10 | position: relative;
11 | height: 100%;
12 | overflow: hidden;
13 | }
14 |
15 | .inner {
16 | position: relative;
17 | height: 100%;
18 | box-sizing: border-box;
19 | overflow-y: scroll;
20 | }
21 |
22 | .track {
23 | position: absolute;
24 | top: 0;
25 | right: 3px;
26 | cursor: default;
27 | user-select: none;
28 | width: 6px;
29 | min-height: 30px;
30 | max-height: 100%;
31 | background: rgba(0, 0, 0, 0.4);
32 | border-radius: 4px;
33 | opacity: 0;
34 | transition: opacity 0.25s ease-in;
35 | z-index: 1;
36 |
37 | &:hover {
38 | width: 10px;
39 | right: 1px;
40 | }
41 | }
42 |
43 | [data-color-mode='dark'] .track {
44 | background: rgba(255, 255, 255, 0.3);
45 |
46 | &:hover {
47 | background: rgba(255, 255, 255, 0.5);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import type { HTMLAttributes, ReactNode } from 'react';
2 | import { forwardRef } from 'react';
3 |
4 | import useCustomScroller from './useCustomScroller';
5 |
6 | import styles from './index.module.css';
7 |
8 | const cx = (...args: (string | undefined | boolean)[]) =>
9 | args.filter(Boolean).join(' ');
10 |
11 | type CustomScrollerProps = {
12 | scrollDisabled?: boolean;
13 | className?: string;
14 | innerClassName?: string;
15 | children: ReactNode;
16 | } & HTMLAttributes;
17 |
18 | const CustomScroller = forwardRef(
19 | (
20 | { scrollDisabled = false, className, innerClassName, children, ...props },
21 | ref,
22 | ) => {
23 | const [wrapperProps, scrollerProps, trackProps] = useCustomScroller(
24 | ref !== null && 'current' in ref ? ref : undefined,
25 | { disabled: scrollDisabled },
26 | );
27 |
28 | return (
29 |
30 |
31 |
32 | {children}
33 |
34 |
35 |
36 |
37 | );
38 | },
39 | );
40 |
41 | CustomScroller.displayName = 'CustomScroller';
42 |
43 | export default CustomScroller;
44 |
--------------------------------------------------------------------------------
/src/useCustomScroller.ts:
--------------------------------------------------------------------------------
1 | import type { MouseEvent as ReactMouseEvent, MutableRefObject } from 'react';
2 | import { useLayoutEffect, useRef, useState, useCallback } from 'react';
3 |
4 | /**
5 | * We use a negative right on the content to hide original OS scrollbars
6 | */
7 | const OS_SCROLLBAR_WIDTH = (() => {
8 | const outer = document.createElement('div');
9 | const inner = document.createElement('div');
10 | outer.style.overflow = 'scroll';
11 | outer.style.width = '100%';
12 | inner.style.width = '100%';
13 |
14 | document.body.appendChild(outer);
15 | outer.appendChild(inner);
16 | const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
17 | outer.removeChild(inner);
18 | document.body.removeChild(outer);
19 |
20 | return scrollbarWidth;
21 | })();
22 |
23 | /**
24 | * We need this for OSs that automatically hide the scrollbar (so the offset
25 | * doesn't change in such case). Eg: macOS with "Automatically based on mouse".
26 | */
27 | const SCROLLBAR_WIDTH = OS_SCROLLBAR_WIDTH || 20;
28 |
29 | export default function useCustomScroller(
30 | customRef: MutableRefObject | undefined,
31 | { disabled }: { disabled: boolean } = { disabled: false },
32 | ) {
33 | const [scrollRatio, setScrollRatio] = useState(1);
34 | const [isDraggingTrack, setIsDraggingTrack] = useState(false);
35 |
36 | const ref = useRef(null);
37 | const scrollerRef = customRef || ref;
38 | const trackRef = useRef(null);
39 | const trackAnimationRef = useRef(0);
40 | const memoizedProps = useRef<{
41 | clientHeight: number;
42 | scrollHeight: number;
43 | trackHeight: number;
44 | }>();
45 |
46 | useLayoutEffect(() => {
47 | const el = scrollerRef.current;
48 | if (!el) return;
49 |
50 | let scrollbarAnimation: number;
51 |
52 | const updateScrollbar = () => {
53 | cancelAnimationFrame(scrollbarAnimation);
54 | scrollbarAnimation = requestAnimationFrame(() => {
55 | const { clientHeight, scrollHeight } = el;
56 | setScrollRatio(clientHeight / scrollHeight);
57 | memoizedProps.current = {
58 | clientHeight,
59 | scrollHeight,
60 | trackHeight: trackRef.current?.clientHeight ?? 0,
61 | };
62 | });
63 | };
64 |
65 | // Whenever scroller and its children resizes, update scrollbar
66 | const resizeObserver = new ResizeObserver(updateScrollbar);
67 | const observeScroller = () => {
68 | resizeObserver.observe(el);
69 | for (const node of el.children) {
70 | resizeObserver.observe(node);
71 | }
72 | };
73 | observeScroller();
74 |
75 | // Whenever children is added/removed, re-observe resizes, update scrollbar
76 | const mutationObserver = new MutationObserver(() => {
77 | resizeObserver.disconnect();
78 | observeScroller();
79 | updateScrollbar();
80 | });
81 | mutationObserver.observe(el, { childList: true, subtree: true });
82 |
83 | window.addEventListener('resize', updateScrollbar);
84 | updateScrollbar();
85 |
86 | return () => {
87 | cancelAnimationFrame(scrollbarAnimation);
88 | window.removeEventListener('resize', updateScrollbar);
89 | resizeObserver.disconnect();
90 | mutationObserver.disconnect();
91 | };
92 | }, [scrollerRef]);
93 |
94 | useLayoutEffect(() => {
95 | if (!disabled) return;
96 | const el = scrollerRef.current;
97 | if (!el) return;
98 |
99 | const onWheel = (e: WheelEvent) => e.preventDefault();
100 | el.addEventListener('wheel', onWheel, { passive: false });
101 |
102 | return () => {
103 | el.removeEventListener('wheel', onWheel);
104 | };
105 | }, [scrollerRef, disabled]);
106 |
107 | const onScroll = useCallback(() => {
108 | if (scrollRatio === 1) return;
109 | const el = scrollerRef.current;
110 | const track = trackRef.current;
111 |
112 | if (!el || !track) return;
113 |
114 | cancelAnimationFrame(trackAnimationRef.current);
115 |
116 | trackAnimationRef.current = requestAnimationFrame(() => {
117 | if (!memoizedProps.current) return;
118 | const { clientHeight, scrollHeight, trackHeight } = memoizedProps.current;
119 | const ratio = el.scrollTop / (scrollHeight - clientHeight);
120 | const y = ratio * (clientHeight - trackHeight);
121 | track.style.transform = `translateY(${y}px)`;
122 | });
123 | }, [scrollerRef, scrollRatio]);
124 |
125 | const moveTrack = useCallback(
126 | (e: ReactMouseEvent) => {
127 | const el = scrollerRef.current;
128 | if (!el) return;
129 |
130 | let moveAnimation: number;
131 | let lastPageY = e.pageY;
132 | let lastScrollTop = el.scrollTop;
133 |
134 | setIsDraggingTrack(true);
135 |
136 | const drag = ({ pageY }: MouseEvent) => {
137 | cancelAnimationFrame(moveAnimation);
138 | moveAnimation = requestAnimationFrame(() => {
139 | const delta = pageY - lastPageY;
140 | lastScrollTop += delta / scrollRatio;
141 | lastPageY = pageY;
142 | el.scrollTop = lastScrollTop;
143 | });
144 | };
145 |
146 | const stop = () => {
147 | setIsDraggingTrack(false);
148 | window.removeEventListener('mousemove', drag);
149 | };
150 |
151 | window.addEventListener('mousemove', drag);
152 | window.addEventListener('mouseup', stop, { once: true });
153 | },
154 | [scrollerRef, scrollRatio],
155 | );
156 |
157 | const wrapperProps = {
158 | style: {
159 | marginLeft: `-${SCROLLBAR_WIDTH}px`,
160 | },
161 | };
162 |
163 | const scrollerProps = {
164 | ref: scrollerRef,
165 | onScroll: disabled ? undefined : onScroll,
166 | style: {
167 | right: `-${SCROLLBAR_WIDTH}px`,
168 | padding: `0 ${SCROLLBAR_WIDTH}px 0 0`,
169 | width: `calc(100% + ${OS_SCROLLBAR_WIDTH}px)`,
170 | },
171 | };
172 |
173 | const trackProps = {
174 | ref: trackRef,
175 | onMouseDown: disabled ? undefined : moveTrack,
176 | style: {
177 | right: isDraggingTrack ? 1 : undefined,
178 | width: isDraggingTrack ? 10 : undefined,
179 | height: `${scrollRatio * 100}%`,
180 | opacity: isDraggingTrack ? 1 : undefined,
181 | display: disabled || scrollRatio === 1 ? 'none' : undefined,
182 | },
183 | };
184 |
185 | return [wrapperProps, scrollerProps, trackProps] as const;
186 | }
187 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "esModuleInterop": true,
5 | "skipLibCheck": true,
6 | "jsx": "react-jsx",
7 | "lib": ["ESNext", "DOM", "DOM.Iterable"],
8 | "sourceMap": true,
9 | "module": "esnext",
10 | "target": "ES2018",
11 | "moduleResolution": "node",
12 | "forceConsistentCasingInFileNames": true,
13 | "outDir": "./dist",
14 | "noImplicitReturns": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "strictNullChecks": true,
17 | "noEmit": true,
18 | "strict": true,
19 | "noImplicitAny": true,
20 | "resolveJsonModule": true
21 | },
22 | "include": ["**/*.ts", "**/*.tsx"],
23 | "exclude": ["node_modules", "**/node_modules/*", "dist", "**/dist/*"]
24 | }
25 |
--------------------------------------------------------------------------------
/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup';
2 |
3 | export default defineConfig(({ watch }) => ({
4 | entry: ['src/index.ts'],
5 | format: watch ? ['iife'] : ['cjs', 'esm'],
6 | cjsInterop: true,
7 | sourcemap: true,
8 | clean: true,
9 | dts: !watch,
10 | loader: {
11 | '.css': 'local-css',
12 | },
13 | }));
14 |
--------------------------------------------------------------------------------