├── .editorconfig
├── .eslintrc.json
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── questions.md
├── .gitignore
├── .nvmrc
├── .prettierrc
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── examples
├── angular
│ ├── .editorconfig
│ ├── .gitignore
│ ├── .vscode
│ │ ├── extensions.json
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── README.md
│ ├── angular.json
│ ├── package.json
│ ├── public
│ │ └── favicon.ico
│ ├── src
│ │ ├── app
│ │ │ ├── app.component.css
│ │ │ ├── app.component.html
│ │ │ ├── app.component.spec.ts
│ │ │ ├── app.component.ts
│ │ │ ├── app.config.ts
│ │ │ └── app.routes.ts
│ │ ├── index.html
│ │ ├── main.ts
│ │ └── styles.css
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ └── yarn.lock
├── jquery
│ └── index.html
├── react
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ └── registerServiceWorker.js
│ └── yarn.lock
├── vue-2.7
│ ├── .babelrc
│ ├── README.md
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.vue
│ │ └── main.js
│ └── yarn.lock
└── vue-3
│ ├── .babelrc
│ ├── README.md
│ ├── package.json
│ ├── public
│ └── index.html
│ ├── src
│ ├── App.vue
│ └── main.js
│ └── yarn.lock
├── jest-setup.ts
├── lerna.json
├── logo.eps
├── package.json
├── packages
├── examples
│ ├── .babelrc
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── Demo.js
│ ├── Playground.js
│ ├── bundle.js.LICENSE.txt
│ ├── index.js
│ ├── package.json
│ ├── public
│ │ ├── css
│ │ │ ├── demo.css
│ │ │ └── normalize.css
│ │ └── index.html
│ └── webpack.config.js
├── simplebar-angular
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── angular.json
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src
│ │ ├── lib
│ │ │ ├── simplebar-angular.component.html
│ │ │ ├── simplebar-angular.component.scss
│ │ │ ├── simplebar-angular.component.spec.ts
│ │ │ ├── simplebar-angular.component.ts
│ │ │ └── simplebar-angular.module.ts
│ │ └── public-api.ts
│ ├── test-setup.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ ├── tsconfig.lib.prod.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── simplebar-core
│ ├── .babelrc.json
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── global-setup.js
│ ├── global-teardown.js
│ ├── jest-e2e.config.js
│ ├── jest-puppeteer.config.js
│ ├── jest-unit.config.js
│ ├── package.json
│ ├── rollup.config.mjs
│ ├── src
│ │ ├── helpers.ts
│ │ ├── index.ts
│ │ ├── scrollbar-width.ts
│ │ └── simplebar.css
│ └── tests
│ │ ├── simplebar.test.e2e.js
│ │ └── simplebar.test.ts
├── simplebar-react
│ ├── .eslintrc.json
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── index.tsx
│ ├── jest.config.js
│ ├── package.json
│ ├── rollup.config.mjs
│ ├── tests
│ │ ├── __snapshots__
│ │ │ └── index.test.tsx.snap
│ │ └── index.test.tsx
│ └── tsconfig.json
├── simplebar-vue
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── component.ts
│ ├── index.ts
│ ├── jest.config.js
│ ├── package.json
│ ├── rollup.config.mjs
│ ├── scripts
│ │ └── swap-vue.js
│ ├── simplebar-vue.d.ts
│ ├── tests
│ │ ├── __snapshots__
│ │ │ └── index.test.ts.snap
│ │ ├── index.test.ts
│ │ └── test-utils.ts
│ ├── tsconfig.json
│ └── utils.ts
├── simplebar
│ ├── .babelrc.json
│ ├── .env
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── jest-unit.config.js
│ ├── package.json
│ ├── rollup.config.mjs
│ ├── src
│ │ └── index.ts
│ └── tests
│ │ └── simplebar.test.js
└── website
│ ├── .gitignore
│ ├── .prettierignore
│ ├── .prettierrc
│ ├── LICENSE
│ ├── README.md
│ ├── gatsby-browser.js
│ ├── gatsby-config.js
│ ├── gatsby-node.js
│ ├── gatsby-ssr.js
│ ├── package.json
│ └── src
│ ├── components
│ ├── Layout.js
│ ├── List.js
│ ├── Playground.js
│ └── SEO.js
│ ├── demo.css
│ ├── html.js
│ ├── images
│ ├── browserstack.png
│ ├── favicon.svg
│ ├── logo.svg
│ └── users
│ │ ├── storybook.svg
│ │ ├── twitch.svg
│ │ └── zulip.svg
│ ├── pages
│ ├── 404.js
│ ├── examples.js
│ └── index.js
│ └── theme.js
├── rollup.config.mjs
├── tsconfig.json
├── web.sketch
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 | indent_style = space
9 |
10 | [**.js]
11 | indent_size = 2
12 |
13 | [**.css]
14 | indent_size = 2
15 |
16 | [**.html]
17 | indent_size = 2
18 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": [
4 | "eslint:recommended"
5 | ],
6 | "env": {
7 | "browser": true,
8 | "node": true,
9 | "es2020": true
10 | },
11 | "parserOptions": {
12 | "sourceType": "module",
13 | "ecmaVersion": 2018
14 | },
15 | "overrides": [
16 | {
17 | "files": ["**/*.ts", "**/*.tsx"],
18 | "parser": "@typescript-eslint/parser",
19 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
20 | "plugins": ["@typescript-eslint"]
21 | },
22 | {
23 | "files": ["**/*.test?(.*).ts", "**/*.test?(.*).tsx", "**/*.test?(.*).js"],
24 | "env": {
25 | "jest": true,
26 | "jest/globals": true
27 | },
28 | "globals": {
29 | "page": true,
30 | "browser": true,
31 | "context": true,
32 | "jestPuppeteer": true
33 | },
34 | "plugins": ["jest"]
35 | }
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: grsmto
2 | buy_me_a_coffee: adriendenat
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## 🔎 Usage Questions
2 |
3 | Before you ask a question, please make sure you've:
4 |
5 | - Looked for prior or closed issues
6 | - Checked the README
7 | - Looked for/asked questions on stack overflow: https://stackoverflow.com/search?q=simplebar
8 |
9 | ## ✨ Feature Requests
10 |
11 | SimpleBar's mission statement is to keep things simple and stick to the native scroll behaviour as much as possible. However if you have a suggestion that falls within this approach that hasn't already been proposed, we'd love to hear it!
12 |
13 | ## 🐛 Bugs
14 |
15 | Please complete the following template:
16 |
17 | ## Current Behavior
18 |
19 |
20 |
21 | ## Expected behavior
22 |
23 |
24 |
25 | ## Reproducible example
26 |
27 |
28 |
29 | ## Suggested solution(s)
30 |
31 |
32 |
33 | ## Additional context
34 |
35 |
36 |
37 | ## Your environment
38 |
39 | | Software | Version(s) |
40 | | ---------------- | ---------- |
41 | | SimpleBar |
42 | | Browser |
43 | | npm/Yarn |
44 | | Operating System |
45 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Reproducible example**
24 |
25 |
26 | **Additional context**
27 | Add any other context about the problem here.
28 |
29 | **Your environment**
30 |
31 | | Software | Version(s) |
32 | | ---------------- | ---------- |
33 | | SimpleBar |
34 | | Browser |
35 | | npm/Yarn |
36 | | Operating System |
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | SimpleBar's mission statement is to keep things simple and stick to the native scroll behaviour as much as possible. However if you have a suggestion that falls within this approach that hasn't already been proposed, we'd love to hear it!
11 |
12 | **Is your feature request related to a problem? Please describe.**
13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
14 |
15 | **Describe the solution you'd like**
16 | A clear and concise description of what you want to happen.
17 |
18 | **Describe alternatives you've considered**
19 | A clear and concise description of any alternative solutions or features you've considered.
20 |
21 | **Additional context**
22 | Add any other context or screenshots about the feature request here.
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/questions.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Questions / Help
3 | about: If you have questions, please read full readme first
4 | title: ''
5 | labels: question
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## 💬 Questions and Help
11 |
12 | Please before asking your question:
13 |
14 | - Read carefully the README of the project
15 | - Search if your answer has already been answered in old issues
16 |
17 | After you can submit your question and we will be happy to help you!
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | .temp
4 | dist
5 | yarn-error.log
6 | .next
7 | .env
8 | .idea
9 | yalc.lock
10 | .yalc
11 | local.log
12 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 20.11.1
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | cache: yarn
3 |
4 | before_install:
5 | - npm i -g lerna
6 |
7 | install:
8 | - yarn
9 |
10 | script:
11 | - yarn build
12 | - yarn test
13 |
14 | deploy:
15 | provider: script
16 | skip_cleanup: true
17 | script: cd packages/website/ && yarn deploy
18 | on:
19 | branch: master
20 |
21 | addons:
22 | chrome: stable
23 | browserstack:
24 | username: 'adriengrsmto'
25 | access_key:
26 | secure: 'HUmCxQG5gsOak0EFqAHTlIBlPL2lLjbKTaYQ0rAEFMu0E4WknH5z1qqZdw9ntAo201u+Pk/jC+YmPqSF3TjpxE5vOqeVCU27YSoUhZid1iI43z7uquuiXXsBlsaQZ0o2PNDSRoWTJ9G5Dfn+YgOIyEnFYEucAIii+tLo2lZVhlY='
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jonathan Nicol
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | SimpleBar does only one thing: replace the browser's default scrollbar with a custom CSS-styled one without losing performances.
14 | Unlike some popular plugins, SimpleBar doesn't mimic scroll with Javascript, causing janks and strange scrolling behaviours...
15 | You keep the awesomeness of native scrolling...with a custom scrollbar!
16 | SimpleBar **does NOT implement a custom scroll behaviour**. It keeps the **native** `overflow: auto` scroll and **only** replace the scrollbar visual appearance.
17 |
18 | - **🐦 Follow me on [Twitter!](https://twitter.com/adriendenat) or [Mastodon!](https://mas.to/@adrien)**
19 | - **👨💻 If you like Simplebar, [buy me a coffee!](adriendenat)**
20 |
21 |
22 |
23 | #### Design it as you want
24 |
25 | SimpleBar uses pure CSS to style the scrollbar. You can easily customize it as you want! Or even have multiple style on the same page...or just keep the default style ("Mac OS" scrollbar style).
26 |
27 | #### Lightweight and performant
28 |
29 | Only 6kb minified. SimpleBar doesn't use Javascript to handle scrolling. You keep the performances/behaviours of the native scroll.
30 |
31 | #### Supported everywhere
32 |
33 | SimpleBar has been tested on the following browsers: Chrome, Firefox, Safari, Edge, IE11.
34 |
35 | ### Getting started
36 |
37 | The easiest way to use SimpleBar is with the default dependency-free version: `npm install simplebar`.
38 |
39 | - [Core documention](https://github.com/Grsmto/simplebar/tree/master/packages/simplebar)
40 |
41 | If you are using a framework, SimpleBar also supports the most popular ones: Vue, Angular and React.
42 |
43 | - [React documention](https://github.com/Grsmto/simplebar/tree/master/packages/simplebar-react)
44 | - [Angular documentation](https://github.com/Grsmto/simplebar/tree/master/packages/simplebar-angular)
45 | - [Vue documentation](https://github.com/Grsmto/simplebar/tree/master/packages/simplebar-vue)
46 |
47 | ### Demo
48 |
49 | You can check our [demo page](https://grsmto.github.io/simplebar/examples) (which is also the one we use for automated tests).
50 |
51 | ### Changelog
52 |
53 | See changelog here : https://github.com/Grsmto/simplebar/releases
54 |
55 | ### Credits
56 |
57 | - [KingSora](https://github.com/KingSora) for multiple features and inspirations (`height: auto` detection, RTL mode cross browser support and more) with [OverlayScrollbars](https://kingsora.github.io/OverlayScrollbars/).
58 | - [Jonathan Nicol](http://www.f6design.com/) for original idea with [Trackpad Scroll Emulator](https://github.com/jnicol/trackpad-scroll-emulator).
59 | - [Cassio Bittencourt](https://cassiobittencourt.com/) for the logo design.
60 |
--------------------------------------------------------------------------------
/examples/angular/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/examples/angular/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/examples/angular/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3 | "recommendations": ["angular.ng-template"]
4 | }
5 |
--------------------------------------------------------------------------------
/examples/angular/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "name": "ng serve",
7 | "type": "chrome",
8 | "request": "launch",
9 | "preLaunchTask": "npm: start",
10 | "url": "http://localhost:4200/"
11 | },
12 | {
13 | "name": "ng test",
14 | "type": "chrome",
15 | "request": "launch",
16 | "preLaunchTask": "npm: test",
17 | "url": "http://localhost:9876/debug.html"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/angular/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "type": "npm",
7 | "script": "start",
8 | "isBackground": true,
9 | "problemMatcher": {
10 | "owner": "typescript",
11 | "pattern": "$tsc",
12 | "background": {
13 | "activeOnStart": true,
14 | "beginsPattern": {
15 | "regexp": "(.*?)"
16 | },
17 | "endsPattern": {
18 | "regexp": "bundle generation complete"
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "type": "npm",
25 | "script": "test",
26 | "isBackground": true,
27 | "problemMatcher": {
28 | "owner": "typescript",
29 | "pattern": "$tsc",
30 | "background": {
31 | "activeOnStart": true,
32 | "beginsPattern": {
33 | "regexp": "(.*?)"
34 | },
35 | "endsPattern": {
36 | "regexp": "bundle generation complete"
37 | }
38 | }
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/examples/angular/README.md:
--------------------------------------------------------------------------------
1 | # angular
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.0.4.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
28 |
--------------------------------------------------------------------------------
/examples/angular/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "angular": {
7 | "projectType": "application",
8 | "schematics": {},
9 | "root": "",
10 | "sourceRoot": "src",
11 | "prefix": "app",
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:application",
15 | "options": {
16 | "outputPath": "dist/angular",
17 | "index": "src/index.html",
18 | "browser": "src/main.ts",
19 | "polyfills": ["zone.js"],
20 | "tsConfig": "tsconfig.app.json",
21 | "assets": [
22 | {
23 | "glob": "**/*",
24 | "input": "public"
25 | }
26 | ],
27 | "styles": ["src/styles.css"],
28 | "scripts": [],
29 | "preserveSymlinks": true
30 | },
31 | "configurations": {
32 | "production": {
33 | "budgets": [
34 | {
35 | "type": "initial",
36 | "maximumWarning": "500kB",
37 | "maximumError": "1MB"
38 | },
39 | {
40 | "type": "anyComponentStyle",
41 | "maximumWarning": "2kB",
42 | "maximumError": "4kB"
43 | }
44 | ],
45 | "outputHashing": "all"
46 | },
47 | "development": {
48 | "optimization": false,
49 | "extractLicenses": false,
50 | "sourceMap": true
51 | }
52 | },
53 | "defaultConfiguration": "production"
54 | },
55 | "serve": {
56 | "builder": "@angular-devkit/build-angular:dev-server",
57 | "configurations": {
58 | "production": {
59 | "buildTarget": "angular:build:production"
60 | },
61 | "development": {
62 | "buildTarget": "angular:build:development"
63 | }
64 | },
65 | "defaultConfiguration": "development"
66 | },
67 | "extract-i18n": {
68 | "builder": "@angular-devkit/build-angular:extract-i18n"
69 | },
70 | "test": {
71 | "builder": "@angular-devkit/build-angular:karma",
72 | "options": {
73 | "polyfills": ["zone.js", "zone.js/testing"],
74 | "tsConfig": "tsconfig.spec.json",
75 | "assets": [
76 | {
77 | "glob": "**/*",
78 | "input": "public"
79 | }
80 | ],
81 | "styles": ["src/styles.css"],
82 | "scripts": []
83 | }
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/examples/angular/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "watch": "ng build --watch --configuration development",
9 | "test": "ng test"
10 | },
11 | "private": true,
12 | "dependencies": {
13 | "@angular/animations": "^18.0.0",
14 | "@angular/common": "^18.0.0",
15 | "@angular/compiler": "^18.0.0",
16 | "@angular/core": "^18.0.0",
17 | "@angular/forms": "^18.0.0",
18 | "@angular/platform-browser": "^18.0.0",
19 | "@angular/platform-browser-dynamic": "^18.0.0",
20 | "@angular/router": "^18.0.0",
21 | "rxjs": "~7.8.0",
22 | "simplebar-angular": "latest",
23 | "tslib": "^2.3.0",
24 | "zone.js": "~0.14.3"
25 | },
26 | "devDependencies": {
27 | "@angular-devkit/build-angular": "^18.0.4",
28 | "@angular/cli": "^18.0.4",
29 | "@angular/compiler-cli": "^18.0.3",
30 | "@types/jasmine": "~5.1.4",
31 | "jasmine-core": "~5.1.0",
32 | "karma": "~6.4.3",
33 | "karma-chrome-launcher": "~3.2.0",
34 | "karma-coverage": "~2.2.1",
35 | "karma-jasmine": "~5.1.0",
36 | "karma-jasmine-html-reporter": "~2.1.0",
37 | "typescript": "~5.4.2"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/angular/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Grsmto/simplebar/3885152e72af29662d613dca32f7b76d4120f184/examples/angular/public/favicon.ico
--------------------------------------------------------------------------------
/examples/angular/src/app/app.component.css:
--------------------------------------------------------------------------------
1 | .container {
2 | border-radius: 6px;
3 | width: 200px;
4 | height: 200px;
5 | position: absolute;
6 | top: 0;
7 | left: 0;
8 | right: 0;
9 | bottom: 0;
10 | border: 1px dashed;
11 | margin: auto;
12 | }
13 |
14 | ngx-simplebar {
15 | height: 100%;
16 | }
17 |
--------------------------------------------------------------------------------
/examples/angular/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
179 |
180 |
181 |
182 |
183 | ngx-simplebar
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
--------------------------------------------------------------------------------
/examples/angular/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 |
4 | describe('AppComponent', () => {
5 | beforeEach(async () => {
6 | await TestBed.configureTestingModule({
7 | imports: [AppComponent],
8 | }).compileComponents();
9 | });
10 |
11 | it('should create the app', () => {
12 | const fixture = TestBed.createComponent(AppComponent);
13 | const app = fixture.componentInstance;
14 | expect(app).toBeTruthy();
15 | });
16 |
17 | it(`should have the 'angular' title`, () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | const app = fixture.componentInstance;
20 | expect(app.title).toEqual('angular');
21 | });
22 |
23 | it('should render title', () => {
24 | const fixture = TestBed.createComponent(AppComponent);
25 | fixture.detectChanges();
26 | const compiled = fixture.nativeElement as HTMLElement;
27 | expect(compiled.querySelector('h1')?.textContent).toContain(
28 | 'Hello, angular'
29 | );
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/examples/angular/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { RouterOutlet } from '@angular/router';
3 | import { NgFor } from '@angular/common';
4 | import { SimplebarAngularModule } from 'simplebar-angular';
5 |
6 | @Component({
7 | selector: 'app-root',
8 | standalone: true,
9 | imports: [RouterOutlet, SimplebarAngularModule, NgFor],
10 | templateUrl: './app.component.html',
11 | styleUrl: './app.component.css',
12 | })
13 | export class AppComponent {
14 | title = 'angular';
15 | numbers = Array(50).fill(0);
16 | options = { autoHide: false };
17 | }
18 |
--------------------------------------------------------------------------------
/examples/angular/src/app/app.config.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
2 | import { provideRouter } from '@angular/router';
3 |
4 | import { routes } from './app.routes';
5 |
6 | export const appConfig: ApplicationConfig = {
7 | providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)]
8 | };
9 |
--------------------------------------------------------------------------------
/examples/angular/src/app/app.routes.ts:
--------------------------------------------------------------------------------
1 | import { Routes } from '@angular/router';
2 |
3 | export const routes: Routes = [];
4 |
--------------------------------------------------------------------------------
/examples/angular/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | angular
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/angular/src/main.ts:
--------------------------------------------------------------------------------
1 | import { bootstrapApplication } from '@angular/platform-browser';
2 | import { appConfig } from './app/app.config';
3 | import { AppComponent } from './app/app.component';
4 |
5 | bootstrapApplication(AppComponent, appConfig).catch((err) =>
6 | console.error(err)
7 | );
8 |
--------------------------------------------------------------------------------
/examples/angular/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/examples/angular/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3 | {
4 | "extends": "./tsconfig.json",
5 | "compilerOptions": {
6 | "outDir": "./out-tsc/app",
7 | "types": []
8 | },
9 | "files": [
10 | "src/main.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/examples/angular/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3 | {
4 | "compileOnSave": false,
5 | "compilerOptions": {
6 | "outDir": "./dist/out-tsc",
7 | "strict": true,
8 | "noImplicitOverride": true,
9 | "noPropertyAccessFromIndexSignature": true,
10 | "noImplicitReturns": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "skipLibCheck": true,
13 | "esModuleInterop": true,
14 | "sourceMap": true,
15 | "declaration": false,
16 | "experimentalDecorators": true,
17 | "moduleResolution": "bundler",
18 | "importHelpers": true,
19 | "target": "ES2022",
20 | "module": "ES2022",
21 | "useDefineForClassFields": false,
22 | "lib": [
23 | "ES2022",
24 | "dom"
25 | ]
26 | },
27 | "angularCompilerOptions": {
28 | "enableI18nLegacyMessageIdFormat": false,
29 | "strictInjectionParameters": true,
30 | "strictInputAccessModifiers": true,
31 | "strictTemplates": true
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/angular/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
3 | {
4 | "extends": "./tsconfig.json",
5 | "compilerOptions": {
6 | "outDir": "./out-tsc/spec",
7 | "types": [
8 | "jasmine"
9 | ]
10 | },
11 | "include": [
12 | "src/**/*.spec.ts",
13 | "src/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/examples/jquery/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
25 |
26 |
27 |
28 |
↓Scrollable!↓
29 |
↓Scrollable!↓
30 |
↓Scrollable!↓
31 |
↓Scrollable!↓
32 |
↓Scrollable!↓
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/examples/react/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/examples/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.4.1",
7 | "react-dom": "^16.4.1",
8 | "react-scripts": "latest",
9 | "simplebar-react": "latest"
10 | },
11 | "scripts": {
12 | "start": "react-scripts start",
13 | "build": "react-scripts build",
14 | "test": "react-scripts test --env=jsdom",
15 | "eject": "react-scripts eject"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/react/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Grsmto/simplebar/3885152e72af29662d613dca32f7b76d4120f184/examples/react/public/favicon.ico
--------------------------------------------------------------------------------
/examples/react/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/examples/react/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/examples/react/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 80px;
8 | }
9 |
10 | .App-header {
11 | background-color: #222;
12 | height: 150px;
13 | padding: 20px;
14 | color: white;
15 | }
16 |
17 | .App-title {
18 | font-size: 1.5em;
19 | }
20 |
21 | .App-intro {
22 | font-size: large;
23 | }
24 |
25 | @keyframes App-logo-spin {
26 | from { transform: rotate(0deg); }
27 | to { transform: rotate(360deg); }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/react/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import SimpleBar from 'simplebar-react';
3 |
4 | import 'simplebar-react/dist/simplebar.min.css';
5 | import logo from './logo.svg';
6 | import './App.css';
7 |
8 | class App extends Component {
9 | render() {
10 | return (
11 |
12 |
13 |
14 | Welcome to React
15 |
16 |
17 | To get started, edit src/App.js
and save to reload.
18 |
19 |
20 | {[...Array(50)].map((x, i) =>
21 | Some content
22 | )}
23 |
24 |
25 | );
26 | }
27 | }
28 |
29 | export default App;
30 |
--------------------------------------------------------------------------------
/examples/react/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render( , div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/examples/react/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/react/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import registerServiceWorker from './registerServiceWorker';
6 |
7 | ReactDOM.render( , document.getElementById('root'));
8 | registerServiceWorker();
9 |
--------------------------------------------------------------------------------
/examples/react/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/react/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (isLocalhost) {
36 | // This is running on localhost. Lets check if a service worker still exists or not.
37 | checkValidServiceWorker(swUrl);
38 |
39 | // Add some additional logging to localhost, pointing developers to the
40 | // service worker/PWA documentation.
41 | navigator.serviceWorker.ready.then(() => {
42 | console.log(
43 | 'This web app is being served cache-first by a service ' +
44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ'
45 | );
46 | });
47 | } else {
48 | // Is not local host. Just register service worker
49 | registerValidSW(swUrl);
50 | }
51 | });
52 | }
53 | }
54 |
55 | function registerValidSW(swUrl) {
56 | navigator.serviceWorker
57 | .register(swUrl)
58 | .then(registration => {
59 | registration.onupdatefound = () => {
60 | const installingWorker = registration.installing;
61 | installingWorker.onstatechange = () => {
62 | if (installingWorker.state === 'installed') {
63 | if (navigator.serviceWorker.controller) {
64 | // At this point, the old content will have been purged and
65 | // the fresh content will have been added to the cache.
66 | // It's the perfect time to display a "New content is
67 | // available; please refresh." message in your web app.
68 | console.log('New content is available; please refresh.');
69 | } else {
70 | // At this point, everything has been precached.
71 | // It's the perfect time to display a
72 | // "Content is cached for offline use." message.
73 | console.log('Content is cached for offline use.');
74 | }
75 | }
76 | };
77 | };
78 | })
79 | .catch(error => {
80 | console.error('Error during service worker registration:', error);
81 | });
82 | }
83 |
84 | function checkValidServiceWorker(swUrl) {
85 | // Check if the service worker can be found. If it can't reload the page.
86 | fetch(swUrl)
87 | .then(response => {
88 | // Ensure service worker exists, and that we really are getting a JS file.
89 | if (
90 | response.status === 404 ||
91 | response.headers.get('content-type').indexOf('javascript') === -1
92 | ) {
93 | // No service worker found. Probably a different app. Reload the page.
94 | navigator.serviceWorker.ready.then(registration => {
95 | registration.unregister().then(() => {
96 | window.location.reload();
97 | });
98 | });
99 | } else {
100 | // Service worker found. Proceed as normal.
101 | registerValidSW(swUrl);
102 | }
103 | })
104 | .catch(() => {
105 | console.log(
106 | 'No internet connection found. App is running in offline mode.'
107 | );
108 | });
109 | }
110 |
111 | export function unregister() {
112 | if ('serviceWorker' in navigator) {
113 | navigator.serviceWorker.ready.then(registration => {
114 | registration.unregister();
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/examples/vue-2.7/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "vue"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/examples/vue-2.7/README.md:
--------------------------------------------------------------------------------
1 | # SimpleBar Vue Example
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Run your tests
19 | ```
20 | npm run test
21 | ```
22 |
23 | ### Lints and fixes files
24 | ```
25 | npm run lint
26 | ```
27 |
--------------------------------------------------------------------------------
/examples/vue-2.7/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-simplebar-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "babel-preset-vue": "^2.0.2",
12 | "simplebar-vue": "file:../../packages/simplebar-vue",
13 | "vue": "2.7.14"
14 | },
15 | "devDependencies": {
16 | "@vue/cli-plugin-babel": "^3.0.5",
17 | "@vue/cli-plugin-eslint": "^3.0.5",
18 | "@vue/cli-service": "^3.0.5",
19 | "vue-template-compiler": "2.7.14"
20 | },
21 | "eslintConfig": {
22 | "root": true,
23 | "env": {
24 | "node": true
25 | },
26 | "extends": [
27 | "plugin:vue/essential",
28 | "eslint:recommended"
29 | ],
30 | "rules": {},
31 | "parserOptions": {
32 | "parser": "babel-eslint"
33 | }
34 | },
35 | "postcss": {
36 | "plugins": {
37 | "autoprefixer": {}
38 | }
39 | },
40 | "browserslist": [
41 | "> 1%",
42 | "last 2 versions",
43 | "not ie <= 8"
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/examples/vue-2.7/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SimpleBar Vue Example
8 |
9 |
10 |
11 | We're sorry but vue doesn't work properly without JavaScript enabled. Please enable it to continue.
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/vue-2.7/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Example content
4 |
5 |
6 |
7 |
24 |
25 |
30 |
--------------------------------------------------------------------------------
/examples/vue-2.7/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import App from './App.vue';
3 |
4 | Vue.config.productionTip = false;
5 |
6 | new Vue({
7 | render: h => h(App)
8 | }).$mount('#app');
9 |
--------------------------------------------------------------------------------
/examples/vue-3/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "vue"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/examples/vue-3/README.md:
--------------------------------------------------------------------------------
1 | # SimpleBar Vue Example
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Run your tests
19 | ```
20 | npm run test
21 | ```
22 |
23 | ### Lints and fixes files
24 | ```
25 | npm run lint
26 | ```
27 |
--------------------------------------------------------------------------------
/examples/vue-3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-v3-simplebar-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "babel-preset-vue": "^2.0.2",
12 | "simplebar-vue": "../../packages/simplebar-vue",
13 | "vue": "^3.2.45"
14 | },
15 | "devDependencies": {
16 | "@vue/cli-plugin-babel": "^5.0.8",
17 | "@vue/cli-plugin-eslint": "^5.0.8",
18 | "@vue/cli-service": "^5.0.8",
19 | "eslint-plugin-vue": "^9.8.0",
20 | "vue-template-compiler": "^2.7.14"
21 | },
22 | "eslintConfig": {
23 | "root": true,
24 | "env": {
25 | "node": true
26 | },
27 | "extends": [
28 | "plugin:vue/essential",
29 | "eslint:recommended"
30 | ],
31 | "rules": {},
32 | "parserOptions": {
33 | "parser": "babel-eslint"
34 | }
35 | },
36 | "postcss": {
37 | "plugins": {
38 | "autoprefixer": {}
39 | }
40 | },
41 | "browserslist": [
42 | "> 1%",
43 | "last 2 versions",
44 | "not ie <= 8"
45 | ]
46 | }
47 |
--------------------------------------------------------------------------------
/examples/vue-3/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SimpleBar Vue Example
8 |
9 |
10 |
11 | We're sorry but vue doesn't work properly without JavaScript enabled. Please enable it to continue.
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/vue-3/src/App.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 | Example content
10 |
11 |
12 |
13 |
14 |
24 |
--------------------------------------------------------------------------------
/examples/vue-3/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import App from './App.vue';
3 |
4 | createApp(App).mount('#app');
5 |
--------------------------------------------------------------------------------
/jest-setup.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "lerna": "2.11.0",
3 | "version": "independent",
4 | "npmClient": "yarn",
5 | "useWorkspaces": true,
6 | "ignoreChanges": ["**/demo/**", "**/*.md"]
7 | }
8 |
--------------------------------------------------------------------------------
/logo.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Grsmto/simplebar/3885152e72af29662d613dca32f7b76d4120f184/logo.eps
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "workspaces": {
4 | "packages": [
5 | "packages/*"
6 | ],
7 | "nohoist": [
8 | "**/webpack/**",
9 | "**/webpack-dev-server/**",
10 | "**/webpack-cli/**"
11 | ]
12 | },
13 | "scripts": {
14 | "build": "lerna exec yarn build",
15 | "test": "lerna run test",
16 | "release": "yarn build && yarn test && auto changelog && lerna publish --pre-dist-tag beta --preid beta",
17 | "start": "lerna exec --scope=simplebar yarn start",
18 | "dev": "lerna exec --scope=simplebar yarn dev"
19 | },
20 | "devDependencies": {
21 | "@babel/core": "^7.20.7",
22 | "@babel/preset-env": "^7.20.2",
23 | "@babel/preset-react": "^7.18.6",
24 | "@babel/preset-typescript": "^7.18.6",
25 | "@rollup/plugin-babel": "^6.0.3",
26 | "@rollup/plugin-commonjs": "^24.0.0",
27 | "@rollup/plugin-node-resolve": "^15.0.1",
28 | "@rollup/plugin-typescript": "^11.0.0",
29 | "@types/jest": "^29.2.5",
30 | "@types/react": "18.0.26",
31 | "@types/react-dom": "18.0.10",
32 | "@typescript-eslint/parser": "^5.48.0",
33 | "auto": "^10.37.6",
34 | "babel-core": "^7.0.0-bridge.0",
35 | "babel-eslint": "10.1.0",
36 | "babel-jest": "^29.3.1",
37 | "babel-plugin-transform-class-properties": "^6.24.1",
38 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
39 | "concurrently": "^4.1.2",
40 | "env-cmd": "^10.1.0",
41 | "eslint": "^7.32.0",
42 | "eslint-plugin-import": "^2.21.2",
43 | "eslint-plugin-jest": "^23.13.2",
44 | "eslint-plugin-prettier": "^3.1.4",
45 | "husky": "^1.1.4",
46 | "jest": "^29.3.1",
47 | "jest-cli": "^29.3.1",
48 | "jest-environment-jsdom": "^29.3.1",
49 | "jest-puppeteer": "^6.2.0",
50 | "lerna": "^6.4.1",
51 | "lint-staged": "^8.1.5",
52 | "minify": "^9.1.0",
53 | "prettier": "^2.0.5",
54 | "prettier-eslint": "^15.0.1",
55 | "prettier-eslint-cli": "^7.1.0",
56 | "puppeteer": "^16.2.0",
57 | "react": "^18.2.0",
58 | "react-dom": "^18.2.0",
59 | "rollup": "^3.9.1",
60 | "rollup-plugin-license": "^3.0.1",
61 | "rollup-plugin-terser": "^7.0.2",
62 | "ts-jest": "^29.0.3",
63 | "typescript": "^4.8.2",
64 | "webpack-cli": "^5.0.1"
65 | },
66 | "resolutions": {
67 | "@types/react": "18.0.26"
68 | },
69 | "husky": {
70 | "hooks": {
71 | "pre-commit": "lerna run --concurrency 1 --stream precommit"
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/packages/examples/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [["@babel/preset-env", {
3 | "useBuiltIns": "entry",
4 | "corejs": "3.27",
5 | "debug": true
6 | }], "@babel/preset-react"],
7 | "plugins": ["transform-class-properties"],
8 | "env": {
9 | "test": {
10 | "presets": [
11 | [
12 | "@babel/preset-env",
13 | {
14 | "targets": {
15 | "node": "current"
16 | }
17 | }
18 | ],
19 | "@babel/preset-react"
20 | ]
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/examples/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:react/recommended"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/examples/.gitignore:
--------------------------------------------------------------------------------
1 | bundle.js
2 | bundle.js.map
3 | .env
4 |
--------------------------------------------------------------------------------
/packages/examples/Playground.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Select from 'react-select';
3 |
4 | const widthOptions = [
5 | { value: 'auto', label: 'auto' },
6 | { value: '100%', label: '100%' },
7 | { value: '100px', label: '100px' },
8 | ];
9 |
10 | const heightOptions = [
11 | { value: 'auto', label: 'auto' },
12 | { value: '100%', label: '100%' },
13 | { value: '100px', label: '100px' },
14 | ];
15 |
16 | const directionOptions = [
17 | { value: 'ltr', label: 'LTR' },
18 | { value: 'rtl', label: 'RTL' },
19 | ];
20 |
21 | export default class Playground extends React.PureComponent {
22 | state = {
23 | height: '100%',
24 | width: '100%',
25 | direction: 'ltr',
26 | };
27 |
28 | render() {
29 | const { width, height, direction } = this.props;
30 |
31 | return (
32 |
33 |
34 | {this.props.children(this.state)}
35 |
36 | {width && (
37 |
38 | Width:
39 |
41 | this.setState({
42 | width: option.value,
43 | })
44 | }
45 | options={widthOptions}
46 | value={widthOptions.find(
47 | (option) => option.value === this.state.width
48 | )}
49 | />
50 |
51 | )}
52 | {height && (
53 |
54 | Height:
55 |
57 | this.setState({
58 | height: option.value,
59 | })
60 | }
61 | options={heightOptions}
62 | value={heightOptions.find(
63 | (option) => option.value === this.state.height
64 | )}
65 | />
66 |
67 | )}
68 | {direction && (
69 |
70 | Direction:
71 |
73 | this.setState({
74 | direction: option.value,
75 | })
76 | }
77 | options={directionOptions}
78 | value={directionOptions.find(
79 | (option) => option.value === this.state.direction
80 | )}
81 | />
82 |
83 | )}
84 |
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/packages/examples/bundle.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | object-assign
3 | (c) Sindre Sorhus
4 | @license MIT
5 | */
6 |
7 | /** @license React v0.19.1
8 | * scheduler.production.min.js
9 | *
10 | * Copyright (c) Facebook, Inc. and its affiliates.
11 | *
12 | * This source code is licensed under the MIT license found in the
13 | * LICENSE file in the root directory of this source tree.
14 | */
15 |
16 | /** @license React v16.14.0
17 | * react-dom.production.min.js
18 | *
19 | * Copyright (c) Facebook, Inc. and its affiliates.
20 | *
21 | * This source code is licensed under the MIT license found in the
22 | * LICENSE file in the root directory of this source tree.
23 | */
24 |
25 | /** @license React v16.14.0
26 | * react.production.min.js
27 | *
28 | * Copyright (c) Facebook, Inc. and its affiliates.
29 | *
30 | * This source code is licensed under the MIT license found in the
31 | * LICENSE file in the root directory of this source tree.
32 | */
33 |
--------------------------------------------------------------------------------
/packages/examples/index.js:
--------------------------------------------------------------------------------
1 | import 'core-js/stable';
2 | import React from 'react';
3 | import { createRoot } from 'react-dom/client';
4 |
5 | import Demo from './Demo';
6 |
7 | const root = createRoot(document.getElementById('root'));
8 | root.render( );
9 |
--------------------------------------------------------------------------------
/packages/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simplebar-examples",
3 | "version": "3.1.1",
4 | "private": true,
5 | "description": "Scrollbars, simpler.",
6 | "author": "Adrien Denat ",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/grsmto/simplebar.git",
10 | "directory": "packages/simplebar-core"
11 | },
12 | "homepage": "https://grsmto.github.io/simplebar/",
13 | "bugs": "https://github.com/grsmto/simplebar/issues",
14 | "license": "MIT",
15 | "scripts": {
16 | "start": "webpack serve --open --mode=development",
17 | "build": "webpack --mode=production",
18 | "serve": "serve",
19 | "precommit": "lint-staged"
20 | },
21 | "dependencies": {
22 | "@babel/runtime": "^7.20.7",
23 | "core-js": "^3.27.1",
24 | "simplebar": "^6.3.1",
25 | "simplebar-react": "^3.3.1"
26 | },
27 | "devDependencies": {
28 | "@babel/plugin-transform-runtime": "^7.19.6",
29 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
30 | "babel-loader": "^9.1.0",
31 | "css-loader": "^0.28.11",
32 | "promise": "^8.0.2",
33 | "react-refresh": "^0.14.0",
34 | "react-select": "^2.4.3",
35 | "react-window": "^1.8.1",
36 | "serve": "^14.1.2",
37 | "style-loader": "^3.3.1",
38 | "ts-loader": "^9.4.2",
39 | "webpack": "^5.75.0",
40 | "webpack-cli": "^5.0.1",
41 | "webpack-dev-server": "^4.11.1"
42 | },
43 | "workspaces": {
44 | "nohoist": [
45 | "webpack",
46 | "webpack-dev-server",
47 | "webpack-cli"
48 | ]
49 | },
50 | "lint-staged": {
51 | "*.{js,jsx,json}": [
52 | "prettier-eslint --write",
53 | "git add"
54 | ]
55 | },
56 | "browserslist": [
57 | "ie 11"
58 | ]
59 | }
60 |
--------------------------------------------------------------------------------
/packages/examples/public/css/demo.css:
--------------------------------------------------------------------------------
1 | /**
2 | * SimpleBar demo.
3 | * Author: Adrien Grsmto
4 | *
5 | * These styles are not required for SimpleBar
6 | * to function. They are used only for styling the demo.
7 | */
8 |
9 | /* General page styles
10 | *****************************************************************/
11 |
12 | * {
13 | box-sizing: border-box;
14 | }
15 |
16 | body {
17 | font-family: Helvetica, Arial, sans-serif;
18 | font-size: 14px;
19 | margin: 20px;
20 | }
21 |
22 | section:after {
23 | content: '';
24 | display: table;
25 | clear: both;
26 | }
27 |
28 | section + section {
29 | margin-top: 40px;
30 | }
31 |
32 | h1 {
33 | font-size: 30px;
34 | margin: 0 0 10px 0;
35 | }
36 | h2 {
37 | font-size: 24px;
38 | margin: 10px 0;
39 | }
40 | p {
41 | margin: 1em 0;
42 | }
43 | .box {
44 | background: #666;
45 | color: #fff;
46 | }
47 | .col {
48 | float: left;
49 | width: calc(50% - 20px);
50 | }
51 | .col + .col {
52 | margin-left: 40px;
53 | }
54 | .btn {
55 | display: inline-block;
56 | background: #000;
57 | color: #fff;
58 | padding: 8px 12px;
59 | line-height: 1;
60 | text-decoration: none;
61 | -moz-border-radius: 5px;
62 | -webkit-border-radius: 5px;
63 | -o-border-radius: 5px;
64 | border-radius: 5px;
65 | }
66 | .btn:hover {
67 | background: #666;
68 | color: #fff;
69 | }
70 | .btn:visited {
71 | color: #fff;
72 | }
73 |
74 | /* Scrollable elements
75 | *****************************************************************/
76 | .demo1,
77 | .demo3 {
78 | margin: 10px 0;
79 | width: 100%;
80 | height: 300px;
81 | }
82 | .demo1 p {
83 | margin: 0;
84 | padding: 10px;
85 | min-width: 230px;
86 | }
87 | .demo1 p.odd:hover {
88 | background: #666;
89 | height: 100px;
90 | }
91 | .demo1 p.odd {
92 | background: #f0f0f0;
93 | }
94 | .demo1.width {
95 | width: 230px;
96 | }
97 | .demo1.height {
98 | height: 200px;
99 | }
100 | #demo1 p {
101 | text-align: right;
102 | padding: 0;
103 | }
104 | .demo1-internal {
105 | width: 50%;
106 | height: 300px;
107 | }
108 | .demo4 {
109 | background: grey;
110 | width: 100%;
111 | margin: 10px 0;
112 | padding: 10px;
113 | white-space: nowrap;
114 | overflow: auto;
115 | }
116 | .demo4 .box {
117 | display: inline-block;
118 | /* margin-right: 10px; */
119 | width: 100px;
120 | height: 150px;
121 | text-align: center;
122 | line-height: 150px;
123 | font-size: 24px;
124 | }
125 | .demo-raw {
126 | margin: 10px 0;
127 | width: 250px;
128 | height: 300px;
129 | overflow: auto;
130 | }
131 |
132 | .demo3 {
133 | height: auto;
134 | max-height: 300px;
135 | }
136 |
137 | .demo5 {
138 | height: 200px;
139 | height: 60px;
140 | direction: rtl;
141 | }
142 |
143 | .demo-both-axis {
144 | overflow: auto;
145 | width: 200px;
146 | height: 200px;
147 | }
148 |
149 | .demo-both-axis--padding {
150 | background: #2f2f2f;
151 | padding: 40px;
152 | }
153 |
154 | .demo-y-axis {
155 | overflow-y: auto;
156 | overflow-x: hidden;
157 | width: 200px;
158 | height: 200px;
159 | }
160 |
161 | .demo-y-axis.simplebar-dragging .simplebar-scrollbar:before {
162 | background-color: red;
163 | }
164 |
165 | .demo-both-axis .box,
166 | .demo-y-axis .box {
167 | width: 600px;
168 | height: 600px;
169 | }
170 |
171 | .height-100 {
172 | position: absolute;
173 | height: 100%;
174 | width: 100%;
175 | background: red;
176 | }
177 |
178 | .sticky {
179 | position: sticky;
180 | top: 0;
181 | background: red;
182 | margin: 0;
183 | }
184 |
185 | .playground {
186 | display: grid;
187 | grid-template-columns: repeat(2, 1fr);
188 | grid-gap: 10px;
189 | }
190 |
191 | .playground__content {
192 | grid-column-start: span 2;
193 | }
194 |
195 | .demo-flex {
196 | display: flex;
197 | width: 100%;
198 | }
199 |
200 | .demo-flex > div {
201 | height: 300px;
202 | }
203 |
204 | .demo-flex > div .content {
205 | height: 600px;
206 | }
207 |
208 | .demo-flex .left {
209 | width: 200px;
210 | background: #c5e0f7;
211 | }
212 |
213 | .demo-flex .center {
214 | flex: 1;
215 | }
216 |
217 | .demo-flex .right {
218 | width: 200px;
219 | transition: width 0.3s;
220 | background: #ffcfcf;
221 | }
222 |
223 | .demo-height-auto {
224 | width: 25vw;
225 | }
226 |
227 | .demo-height-auto .inner {
228 | margin-bottom: 2em;
229 | width: 25vw;
230 | }
231 |
232 | .demo-hidden {
233 | height: 300px;
234 | }
235 |
236 | .demo-hidden p {
237 | margin: 0;
238 | padding: 10px;
239 | min-width: 230px;
240 | }
241 |
--------------------------------------------------------------------------------
/packages/examples/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SimpleBar demo
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/packages/examples/webpack.config.js:
--------------------------------------------------------------------------------
1 | const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
2 |
3 | module.exports = (env, argv) => {
4 | const isDevelopment = argv.mode === 'development';
5 |
6 | return {
7 | entry: './index.js',
8 | devtool: 'cheap-module-source-map',
9 | output: {
10 | filename: 'dist/bundle.js',
11 | },
12 | performance: {
13 | hints: false,
14 | },
15 | devServer: {
16 | port: 8090,
17 | hot: isDevelopment && true,
18 | },
19 | module: {
20 | rules: [
21 | {
22 | test: /\.js?$/,
23 | exclude: /node_modules/,
24 | use: [
25 | {
26 | loader: require.resolve('babel-loader'),
27 | options: {
28 | plugins: [
29 | isDevelopment && require.resolve('react-refresh/babel'),
30 | ].filter(Boolean),
31 | },
32 | },
33 | ],
34 | },
35 | {
36 | test: /\.css$/,
37 | use: ['style-loader', 'css-loader'],
38 | },
39 | ],
40 | },
41 | plugins: [isDevelopment && new ReactRefreshWebpackPlugin()].filter(Boolean),
42 | };
43 | };
44 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | # SimplebarAngular
14 |
15 | - **🐦 Follow me on [Twitter!](https://twitter.com/adriendenat) or [Mastodon!](https://mas.to/@adrien)**
16 | - **👨💻 I'm available for hire! [Reach out to me!](https://adriendenat.com/)**
17 | - **🚧 Check out my new project [Scroll Snap Carousel](https://github.com/Grsmto/scroll-snap-carousel)!**
18 |
19 | ### Installation
20 |
21 | **- Via npm**
22 | `npm install simplebar-angular --save`
23 |
24 | **- Via Yarn**
25 | `yarn add simplebar-angular`
26 |
27 | ### Usage
28 |
29 | #### Import module
30 |
31 | ```js
32 | import { BrowserModule } from '@angular/platform-browser';
33 | import { NgModule } from '@angular/core';
34 | import { SimplebarAngularModule } from 'simplebar-angular';
35 | import { AppComponent } from './app.component';
36 |
37 | @NgModule({
38 | declarations: [AppComponent],
39 | imports: [BrowserModule, SimplebarAngularModule],
40 | providers: [],
41 | bootstrap: [AppComponent],
42 | })
43 | export class AppModule {}
44 | ```
45 |
46 | #### Use component
47 |
48 | ```js
49 |
50 | ngx-simplebar
51 |
52 | ```
53 |
54 | ### Options
55 |
56 | Find the list of available options on [the core documentation](https://github.com/Grsmto/simplebar/blob/master/packages/simplebar/README.md#options).
57 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "simplebar-angular": {
7 | "projectType": "library",
8 | "root": "",
9 | "sourceRoot": "src",
10 | "prefix": "lib",
11 | "architect": {
12 | "build": {
13 | "builder": "@angular-devkit/build-angular:ng-packagr",
14 | "options": {
15 | "project": "ng-package.json"
16 | },
17 | "configurations": {
18 | "production": {
19 | "tsConfig": "tsconfig.lib.prod.json"
20 | },
21 | "development": {
22 | "tsConfig": "tsconfig.lib.json"
23 | }
24 | },
25 | "defaultConfiguration": "production"
26 | },
27 | "test": {
28 | "builder": "@angular-devkit/build-angular:karma",
29 | "options": {
30 | "main": "test-setup.ts",
31 | "tsConfig": "tsconfig.spec.json",
32 | "karmaConfig": "karma.conf.js"
33 | }
34 | }
35 | }
36 | }
37 | },
38 | "schematics": {
39 | "@schematics/angular:component": {
40 | "styleext": "scss"
41 | }
42 | },
43 | "cli": {
44 | "analytics": false
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | process.env.CHROME_BIN = require('puppeteer').executablePath();
5 |
6 | module.exports = function (config) {
7 | config.set({
8 | basePath: '',
9 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
10 | plugins: [
11 | require('karma-jasmine'),
12 | require('karma-chrome-launcher'),
13 | require('karma-jasmine-html-reporter'),
14 | require('karma-coverage'),
15 | require('@angular-devkit/build-angular/plugins/karma'),
16 | ],
17 | browsers: ['Chrome'],
18 | customLaunchers: {
19 | ChromeNoSandBox: {
20 | base: 'Chrome',
21 | flags: ['--no-sandbox'],
22 | },
23 | HeadlessChrome: {
24 | base: 'ChromeHeadless',
25 | flags: ['--no-sandbox', '--headless'],
26 | },
27 | },
28 | singleRun: true,
29 | restartOnFileChange: true,
30 | failOnEmptyTestSuite: false,
31 | });
32 |
33 | if (process.env.TRAVIS) {
34 | config.browsers = ['ChromeHeadless'];
35 | config.singleRun = true;
36 | config.browserDisconnectTimeout = 10000;
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "./dist",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | },
7 | "allowedNonPeerDependencies": ["simplebar-core"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simplebar-angular",
3 | "version": "3.3.1",
4 | "description": "Angular component for SimpleBar",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng serve",
8 | "build": "ng build",
9 | "lint": "ng lint",
10 | "test": "ng test --no-watch --no-progress",
11 | "e2e": "ng e2e",
12 | "version": "yarn build",
13 | "precommit": "lint-staged"
14 | },
15 | "publishConfig": {
16 | "directory": "dist"
17 | },
18 | "lint-staged": {
19 | "*.{js,jsx,json}": [
20 | "prettier-eslint --write",
21 | "git add"
22 | ]
23 | },
24 | "license": "MIT",
25 | "keywords": [
26 | "simplebar-angular",
27 | "angular",
28 | "simplebar"
29 | ],
30 | "dependencies": {
31 | "simplebar-core": "^1.3.1",
32 | "tslib": "^2.3.0"
33 | },
34 | "peerDependencies": {
35 | "@angular/common": ">=12.0.0",
36 | "@angular/core": ">=12.0.0"
37 | },
38 | "devDependencies": {
39 | "@angular-devkit/build-angular": "18",
40 | "@angular/cli": "18",
41 | "@angular/common": "18",
42 | "@angular/compiler": "18",
43 | "@angular/compiler-cli": "18",
44 | "@angular/core": "18",
45 | "@angular/platform-browser": "18",
46 | "@angular/platform-browser-dynamic": "18",
47 | "@types/jasmine": "~4.3.0",
48 | "jasmine-core": "~4.5.0",
49 | "karma": "~6.4.3",
50 | "karma-chrome-launcher": "~3.2.0",
51 | "karma-coverage": "~2.2.1",
52 | "karma-jasmine": "~5.1.0",
53 | "karma-jasmine-html-reporter": "~2.1.0",
54 | "ng-packagr": "18",
55 | "typescript": "~5.4.5",
56 | "zone.js": "~0.14.7"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/src/lib/simplebar-angular.component.html:
--------------------------------------------------------------------------------
1 |
16 |
19 |
22 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/src/lib/simplebar-angular.component.scss:
--------------------------------------------------------------------------------
1 | ngx-simplebar {
2 | display: block;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/src/lib/simplebar-angular.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { SimplebarAngularComponent } from './simplebar-angular.component';
4 |
5 | describe('SimplebarAngularComponent', () => {
6 | let component: SimplebarAngularComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [SimplebarAngularComponent],
12 | }).compileComponents();
13 |
14 | fixture = TestBed.createComponent(SimplebarAngularComponent);
15 | component = fixture.componentInstance;
16 | fixture.detectChanges();
17 | });
18 |
19 | it('should create', () => {
20 | expect(component).toBeTruthy();
21 | });
22 |
23 | it('should create with options', () => {
24 | component.options = { clickOnTrack: false };
25 | expect(component).toBeTruthy();
26 | });
27 |
28 | it('should define simplebar instance', () => {
29 | expect(component.SimpleBar).toBeDefined();
30 | });
31 |
32 | it('should unmount', () => {
33 | fixture.destroy();
34 | expect(component.SimpleBar).toBeNull();
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/src/lib/simplebar-angular.component.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Component,
3 | OnInit,
4 | Input,
5 | AfterViewInit,
6 | ElementRef,
7 | ViewEncapsulation,
8 | NgZone,
9 | } from '@angular/core';
10 |
11 | import SimpleBar, { SimpleBarOptions } from 'simplebar-core';
12 |
13 | @Component({
14 | selector: 'ngx-simplebar',
15 | host: { 'data-simplebar': 'init' },
16 | templateUrl: './simplebar-angular.component.html',
17 | styleUrls: [
18 | '../../../simplebar-core/src/simplebar.css',
19 | './simplebar-angular.component.scss',
20 | ],
21 | encapsulation: ViewEncapsulation.None,
22 | })
23 | export class SimplebarAngularComponent implements OnInit, AfterViewInit {
24 | @Input('options') options: Partial = {};
25 |
26 | elRef: ElementRef;
27 | SimpleBar: any;
28 | ariaLabel!: string;
29 | tabIndex!: string;
30 |
31 | constructor(elRef: ElementRef, private zone: NgZone) {
32 | this.elRef = elRef;
33 | }
34 |
35 | ngOnInit(): void {
36 | this.ariaLabel =
37 | this.options.ariaLabel || SimpleBar.defaultOptions.ariaLabel;
38 | this.tabIndex =
39 | (this.options.tabIndex || SimpleBar.defaultOptions.tabIndex).toString();
40 | }
41 |
42 | ngAfterViewInit(): void {
43 | this.zone.runOutsideAngular(() => {
44 | this.SimpleBar = new SimpleBar(
45 | this.elRef.nativeElement,
46 | this.options || {}
47 | );
48 | });
49 | }
50 |
51 | ngOnDestroy(): void {
52 | this.SimpleBar?.unMount();
53 | this.SimpleBar = null;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/src/lib/simplebar-angular.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { SimplebarAngularComponent } from './simplebar-angular.component';
3 |
4 | @NgModule({
5 | declarations: [SimplebarAngularComponent],
6 | imports: [],
7 | exports: [SimplebarAngularComponent],
8 | schemas: [],
9 | })
10 | export class SimplebarAngularModule {}
11 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of simplebar-angular
3 | */
4 |
5 | export * from './lib/simplebar-angular.component';
6 | export * from './lib/simplebar-angular.module';
7 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'zone.js'; // Included with Angular CLI.
2 | import { TestBed } from '@angular/core/testing';
3 | import {
4 | BrowserDynamicTestingModule,
5 | platformBrowserDynamicTesting,
6 | } from '@angular/platform-browser-dynamic/testing';
7 |
8 | TestBed.initTestEnvironment(
9 | BrowserDynamicTestingModule,
10 | platformBrowserDynamicTesting()
11 | );
12 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "outDir": "./dist/out-tsc",
7 | "forceConsistentCasingInFileNames": true,
8 | "strict": true,
9 | "noImplicitOverride": true,
10 | "noPropertyAccessFromIndexSignature": true,
11 | "noImplicitReturns": true,
12 | "noFallthroughCasesInSwitch": true,
13 | "sourceMap": true,
14 | "declaration": false,
15 | "downlevelIteration": true,
16 | "experimentalDecorators": true,
17 | "moduleResolution": "node",
18 | "importHelpers": true,
19 | "target": "ES2015",
20 | "module": "es2020",
21 | "lib": ["ES2015", "dom"],
22 | "useDefineForClassFields": false,
23 | "paths": {
24 | "simplebar-angular": ["dist/simplebar-angular"],
25 | "simplebar-angular/*": ["dist/simplebar-angular/*"]
26 | }
27 | },
28 | "angularCompilerOptions": {
29 | "enableI18nLegacyMessageIdFormat": false,
30 | "strictInjectionParameters": true,
31 | "strictInputAccessModifiers": true,
32 | "strictTemplates": true
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/lib",
5 | "declaration": true,
6 | "declarationMap": true,
7 | "inlineSources": true,
8 | "types": []
9 | },
10 | "exclude": ["src/test.ts", "**/*.spec.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.lib.json",
4 | "compilerOptions": {
5 | "declarationMap": false
6 | },
7 | "angularCompilerOptions": {
8 | "compilationMode": "partial"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": ["jasmine"]
6 | },
7 | "files": ["test-setup.ts"],
8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/simplebar-angular/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint:recommended",
3 | "rulesDirectory": ["codelyzer"],
4 | "rules": {
5 | "array-type": false,
6 | "arrow-parens": false,
7 | "deprecation": {
8 | "severity": "warning"
9 | },
10 | "import-blacklist": [true, "rxjs/Rx"],
11 | "interface-name": false,
12 | "max-classes-per-file": false,
13 | "max-line-length": [true, 140],
14 | "member-access": false,
15 | "member-ordering": [
16 | true,
17 | {
18 | "order": [
19 | "static-field",
20 | "instance-field",
21 | "static-method",
22 | "instance-method"
23 | ]
24 | }
25 | ],
26 | "no-consecutive-blank-lines": false,
27 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
28 | "no-empty": false,
29 | "no-inferrable-types": [true, "ignore-params"],
30 | "no-non-null-assertion": true,
31 | "no-redundant-jsdoc": true,
32 | "no-switch-case-fall-through": true,
33 | "no-use-before-declare": true,
34 | "no-var-requires": false,
35 | "object-literal-key-quotes": [true, "as-needed"],
36 | "object-literal-sort-keys": false,
37 | "ordered-imports": false,
38 | "quotemark": [true, "single"],
39 | "trailing-comma": false,
40 | "component-class-suffix": true,
41 | "contextual-lifecycle": true,
42 | "directive-class-suffix": true,
43 | "no-conflicting-lifecycle": true,
44 | "no-host-metadata-property": true,
45 | "no-input-rename": true,
46 | "no-inputs-metadata-property": true,
47 | "no-output-native": true,
48 | "no-output-on-prefix": true,
49 | "no-output-rename": true,
50 | "no-outputs-metadata-property": true,
51 | "template-banana-in-box": true,
52 | "template-no-negated-async": true,
53 | "use-lifecycle-interface": true,
54 | "use-pipe-transform-interface": true,
55 | "directive-selector": [true, "attribute", "ngx", "camelCase"],
56 | "component-selector": [true, "element", "ngx", "kebab-case"]
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/packages/simplebar-core/.babelrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-typescript"],
3 | "plugins": ["transform-class-properties"],
4 | "env": {
5 | "test": {
6 | "presets": [
7 | [
8 | "@babel/preset-env",
9 | {
10 | "targets": {
11 | "node": "current"
12 | }
13 | }
14 | ]
15 | ]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/simplebar-core/.gitignore:
--------------------------------------------------------------------------------
1 | bundle.js
2 | bundle.js.map
3 | .env
4 |
--------------------------------------------------------------------------------
/packages/simplebar-core/README.md:
--------------------------------------------------------------------------------
1 | # SimpleBar Core
2 |
3 | - **🐦 Follow me on [Twitter!](https://twitter.com/adriendenat) or [Mastodon!](https://mas.to/@adrien)**
4 | - **👨💻 I'm available for hire! [Reach out to me!](https://adriendenat.com/)**
5 | - **🚧 Check out my new project [Scroll Snap Carousel](https://github.com/Grsmto/scroll-snap-carousel)!**
6 |
7 | ### This is the core library. You should not use it directly. If you want the vanilla plugin, see [SimpleBar](https://github.com/Grsmto/simplebar/tree/master/packages/simplebar).
8 |
--------------------------------------------------------------------------------
/packages/simplebar-core/global-setup.js:
--------------------------------------------------------------------------------
1 | const BrowserStackLocal = require('browserstack-local');
2 | const { setup: setupPuppeteer } = require('jest-environment-puppeteer');
3 |
4 | const bsLocal = new BrowserStackLocal.Local();
5 |
6 | globalThis.__BROWSERSTACK_LOCAL__ = bsLocal;
7 |
8 | const BS_LOCAL_ARGS = {
9 | key: process.env.BROWSERSTACK_ACCESS_KEY,
10 | };
11 |
12 | // Starts the Local instance with the required arguments
13 |
14 | module.exports = async function globalSetup(globalConfig) {
15 | await new Promise((resolve) => {
16 | bsLocal.start(BS_LOCAL_ARGS, () => {
17 | // Check if BrowserStack local instance is running
18 | console.log(`BrowserStackLocal running: ${bsLocal.isRunning()}`);
19 | resolve();
20 | });
21 | });
22 | await setupPuppeteer(globalConfig);
23 | };
24 |
--------------------------------------------------------------------------------
/packages/simplebar-core/global-teardown.js:
--------------------------------------------------------------------------------
1 | const { teardown: teardownPuppeteer } = require('jest-environment-puppeteer');
2 |
3 | module.exports = async function globalTeardown(globalConfig) {
4 | await new Promise((resolve) => {
5 | globalThis.__BROWSERSTACK_LOCAL__.stop(() => {
6 | resolve();
7 | });
8 | });
9 | await teardownPuppeteer(globalConfig);
10 | };
11 |
--------------------------------------------------------------------------------
/packages/simplebar-core/jest-e2e.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'jest-puppeteer',
3 | testRegex: '^.+\\.test\\.e2e\\.js$',
4 | transform: {
5 | '^.+\\.js?$': 'babel-jest',
6 | },
7 | transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$'],
8 | moduleFileExtensions: ['js'],
9 | moduleNameMapper: {
10 | '^lodash-es$': 'lodash',
11 | },
12 | ...(process.env.CI === 'true' && { maxWorkers: 2, testTimeout: 15000 }),
13 | globalSetup: './global-setup.js',
14 | };
15 |
--------------------------------------------------------------------------------
/packages/simplebar-core/jest-puppeteer.config.js:
--------------------------------------------------------------------------------
1 | const caps = {
2 | browser: 'chrome', // You can choose `chrome`, `edge` or `firefox` in this capability
3 | browser_version: 'latest', // We support v83 and above. You can choose `latest`, `latest-beta`, `latest-1`, `latest-2` and so on, in this capability
4 | os: 'os x',
5 | os_version: 'big sur',
6 | project: 'simplebar',
7 | build:
8 | process.env.CI === 'true'
9 | ? `${process.env.TRAVIS_BRANCH}-${process.env.TRAVIS_COMMIT}`
10 | : 'local-tests',
11 | name: 'SimpleBar examples Puppeteer test', // The name of your test and build. See browserstack.com/docs/automate/puppeteer/organize tests for more details
12 | 'browserstack.localIdentifier': process.env.BROWSERSTACK_LOCAL_IDENTIFIER,
13 | 'browserstack.username': process.env.BROWSERSTACK_USERNAME,
14 | 'browserstack.accessKey': process.env.BROWSERSTACK_ACCESS_KEY,
15 | 'browserstack.local': 'true',
16 | };
17 |
18 | module.exports = {
19 | launch: {
20 | headless: process.env.CI === 'true',
21 | devtools: process.env.CI === 'false',
22 | },
23 | server: {
24 | command: 'cd ../examples && webpack server --mode=production',
25 | port: 8090,
26 | },
27 | connect: {
28 | browserWSEndpoint: `wss://cdp.browserstack.com/puppeteer?caps=${encodeURIComponent(
29 | JSON.stringify(caps)
30 | )}`, // The BrowserStack CDP endpoint gives you a `browser` instance based on the `caps` that you specified
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/packages/simplebar-core/jest-unit.config.js:
--------------------------------------------------------------------------------
1 | const { defaults: tsjPreset } = require('ts-jest/presets');
2 |
3 | /** @type {import('ts-jest').JestConfigWithTsJest} */
4 | module.exports = {
5 | preset: 'ts-jest',
6 | transform: {
7 | ...tsjPreset.transform,
8 | '^.+\\.js?$': 'babel-jest',
9 | },
10 | moduleNameMapper: {
11 | '^lodash-es/*(.*)$': 'lodash/$1',
12 | },
13 | testEnvironment: 'jsdom',
14 | };
15 |
--------------------------------------------------------------------------------
/packages/simplebar-core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.3.1",
3 | "name": "simplebar-core",
4 | "title": "SimpleBar.js",
5 | "description": "Scrollbars, simpler.",
6 | "files": [
7 | "dist",
8 | "README.md"
9 | ],
10 | "author": "Adrien Denat from a fork by Jonathan Nicol",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/grsmto/simplebar.git",
14 | "directory": "packages/simplebar-core"
15 | },
16 | "main": "./dist/index.cjs",
17 | "module": "./dist/index.mjs",
18 | "style": "./dist/simplebar.min.css",
19 | "typings": "./dist/index.d.ts",
20 | "sideEffects": [
21 | "*.css"
22 | ],
23 | "homepage": "https://grsmto.github.io/simplebar/",
24 | "bugs": "https://github.com/grsmto/simplebar/issues",
25 | "license": "MIT",
26 | "scripts": {
27 | "serve": "serve -s demo",
28 | "build": "rollup -c && cp src/simplebar.css dist/simplebar.css",
29 | "dev": "rollup -c -w --environment BUILD:development",
30 | "test": "yarn test:unit && yarn test:e2e",
31 | "test:unit": "jest -c jest-unit.config.js",
32 | "test:e2e": "env-cmd --silent jest -c jest-e2e.config.js --runInBand",
33 | "version": "yarn build",
34 | "precommit": "lint-staged"
35 | },
36 | "dependencies": {
37 | "lodash": "^4.17.21",
38 | "lodash-es": "^4.17.21"
39 | },
40 | "devDependencies": {
41 | "@types/lodash-es": "^4.17.12",
42 | "browserstack-local": "^1.5.1"
43 | },
44 | "lint-staged": {
45 | "*.{js,jsx,json}": [
46 | "prettier-eslint --write",
47 | "git add"
48 | ]
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/simplebar-core/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import { createRequire } from 'node:module';
2 | import typescript from '@rollup/plugin-typescript';
3 | import license from 'rollup-plugin-license';
4 | import { getExternals, getBanner, tsConfig } from '../../rollup.config.mjs';
5 |
6 | const require = createRequire(import.meta.url);
7 | const pkg = require('./package.json');
8 |
9 | export default [
10 | // ES module (for bundlers) build
11 | {
12 | input: 'src/index.ts',
13 | external: getExternals(pkg),
14 | output: [
15 | {
16 | file: pkg.module,
17 | format: 'esm',
18 | sourcemap: true,
19 | },
20 | ],
21 | plugins: [typescript(tsConfig), license(getBanner(pkg))],
22 | },
23 | // CommonJS (for Node)
24 | {
25 | input: 'src/index.ts',
26 | external: getExternals(pkg),
27 | output: [
28 | {
29 | name: 'SimpleBarCore',
30 | file: pkg.main,
31 | format: 'umd',
32 | sourcemap: true,
33 | globals: {
34 | 'lodash-es': '_',
35 | },
36 | // Lodash-es won't work for UMD build, so we alias to lodash
37 | paths: (id) => id.replace('lodash-es/', 'lodash/'),
38 | },
39 | ],
40 | plugins: [typescript(tsConfig), license(getBanner(pkg))],
41 | },
42 | ];
43 |
--------------------------------------------------------------------------------
/packages/simplebar-core/src/helpers.ts:
--------------------------------------------------------------------------------
1 | import type { SimpleBarOptions } from './index';
2 |
3 | export function getElementWindow(element: Element) {
4 | if (
5 | !element ||
6 | !element.ownerDocument ||
7 | !element.ownerDocument.defaultView
8 | ) {
9 | return window;
10 | }
11 | return element.ownerDocument.defaultView;
12 | }
13 |
14 | export function getElementDocument(element: Element) {
15 | if (!element || !element.ownerDocument) {
16 | return document;
17 | }
18 | return element.ownerDocument;
19 | }
20 |
21 | // Helper function to retrieve options from element attributes
22 | export const getOptions = function (obj: any) {
23 | const initialObj: SimpleBarOptions = {};
24 |
25 | const options = Array.prototype.reduce.call(
26 | obj,
27 | (acc: any, attribute) => {
28 | const option = attribute.name.match(/data-simplebar-(.+)/);
29 | if (option) {
30 | const key: keyof SimpleBarOptions = option[1].replace(
31 | /\W+(.)/g,
32 | (_: any, chr: string) => chr.toUpperCase()
33 | );
34 |
35 | switch (attribute.value) {
36 | case 'true':
37 | acc[key] = true;
38 | break;
39 | case 'false':
40 | acc[key] = false;
41 | break;
42 | case undefined:
43 | acc[key] = true;
44 | break;
45 | default:
46 | acc[key] = attribute.value;
47 | }
48 | }
49 | return acc;
50 | },
51 | initialObj
52 | );
53 | return options as SimpleBarOptions;
54 | };
55 |
56 | export function addClasses(el: HTMLElement | null, classes: string) {
57 | if (!el) return;
58 | el.classList.add(...classes.split(' '));
59 | }
60 |
61 | export function removeClasses(el: HTMLElement | null, classes: string) {
62 | if (!el) return;
63 | classes.split(' ').forEach((className) => {
64 | el.classList.remove(className);
65 | });
66 | }
67 |
68 | export function classNamesToQuery(classNames: string) {
69 | return `.${classNames.split(' ').join('.')}`;
70 | }
71 |
72 | export const canUseDOM = !!(
73 | typeof window !== 'undefined' &&
74 | window.document &&
75 | window.document.createElement
76 | );
77 |
--------------------------------------------------------------------------------
/packages/simplebar-core/src/scrollbar-width.ts:
--------------------------------------------------------------------------------
1 | import { canUseDOM } from './helpers';
2 |
3 | let cachedScrollbarWidth: number | null = null;
4 | let cachedDevicePixelRatio: number | null = null;
5 |
6 | if (canUseDOM) {
7 | window.addEventListener('resize', () => {
8 | if (cachedDevicePixelRatio !== window.devicePixelRatio) {
9 | cachedDevicePixelRatio = window.devicePixelRatio;
10 | cachedScrollbarWidth = null;
11 | }
12 | });
13 | }
14 |
15 | export default function scrollbarWidth() {
16 | if (cachedScrollbarWidth === null) {
17 | if (typeof document === 'undefined') {
18 | cachedScrollbarWidth = 0;
19 | return cachedScrollbarWidth;
20 | }
21 |
22 | const body = document.body;
23 | const box = document.createElement('div');
24 |
25 | box.classList.add('simplebar-hide-scrollbar');
26 |
27 | body.appendChild(box);
28 |
29 | const width = box.getBoundingClientRect().right;
30 |
31 | body.removeChild(box);
32 |
33 | cachedScrollbarWidth = width;
34 | }
35 |
36 | return cachedScrollbarWidth;
37 | }
38 |
--------------------------------------------------------------------------------
/packages/simplebar-core/src/simplebar.css:
--------------------------------------------------------------------------------
1 | [data-simplebar] {
2 | position: relative;
3 | flex-direction: column;
4 | flex-wrap: wrap;
5 | justify-content: flex-start;
6 | align-content: flex-start;
7 | align-items: flex-start;
8 | }
9 |
10 | .simplebar-wrapper {
11 | overflow: hidden;
12 | width: inherit;
13 | height: inherit;
14 | max-width: inherit;
15 | max-height: inherit;
16 | }
17 |
18 | .simplebar-mask {
19 | direction: inherit;
20 | position: absolute;
21 | overflow: hidden;
22 | padding: 0;
23 | margin: 0;
24 | left: 0;
25 | top: 0;
26 | bottom: 0;
27 | right: 0;
28 | width: auto !important;
29 | height: auto !important;
30 | z-index: 0;
31 | }
32 |
33 | .simplebar-offset {
34 | direction: inherit !important;
35 | box-sizing: inherit !important;
36 | resize: none !important;
37 | position: absolute;
38 | top: 0;
39 | left: 0;
40 | bottom: 0;
41 | right: 0;
42 | padding: 0;
43 | margin: 0;
44 | -webkit-overflow-scrolling: touch;
45 | }
46 |
47 | .simplebar-content-wrapper {
48 | direction: inherit;
49 | box-sizing: border-box !important;
50 | position: relative;
51 | display: block;
52 | height: 100%; /* Required for horizontal native scrollbar to not appear if parent is taller than natural height */
53 | width: auto;
54 | max-width: 100%; /* Not required for horizontal scroll to trigger */
55 | max-height: 100%; /* Needed for vertical scroll to trigger */
56 | overflow: auto;
57 | scrollbar-width: none;
58 | -ms-overflow-style: none;
59 | }
60 |
61 | .simplebar-content-wrapper::-webkit-scrollbar,
62 | .simplebar-hide-scrollbar::-webkit-scrollbar {
63 | display: none;
64 | width: 0;
65 | height: 0;
66 | }
67 |
68 | .simplebar-content:before,
69 | .simplebar-content:after {
70 | content: ' ';
71 | display: table;
72 | }
73 |
74 | .simplebar-placeholder {
75 | max-height: 100%;
76 | max-width: 100%;
77 | width: 100%;
78 | pointer-events: none;
79 | }
80 |
81 | .simplebar-height-auto-observer-wrapper {
82 | box-sizing: inherit !important;
83 | height: 100%;
84 | width: 100%;
85 | max-width: 1px;
86 | position: relative;
87 | float: left;
88 | max-height: 1px;
89 | overflow: hidden;
90 | z-index: -1;
91 | padding: 0;
92 | margin: 0;
93 | pointer-events: none;
94 | flex-grow: inherit;
95 | flex-shrink: 0;
96 | flex-basis: 0;
97 | }
98 |
99 | .simplebar-height-auto-observer {
100 | box-sizing: inherit;
101 | display: block;
102 | opacity: 0;
103 | position: absolute;
104 | top: 0;
105 | left: 0;
106 | height: 1000%;
107 | width: 1000%;
108 | min-height: 1px;
109 | min-width: 1px;
110 | overflow: hidden;
111 | pointer-events: none;
112 | z-index: -1;
113 | }
114 |
115 | .simplebar-track {
116 | z-index: 1;
117 | position: absolute;
118 | right: 0;
119 | bottom: 0;
120 | pointer-events: none;
121 | overflow: hidden;
122 | }
123 |
124 | [data-simplebar].simplebar-dragging {
125 | pointer-events: none;
126 | -webkit-touch-callout: none;
127 | -webkit-user-select: none;
128 | -khtml-user-select: none;
129 | -moz-user-select: none;
130 | -ms-user-select: none;
131 | user-select: none;
132 | }
133 |
134 | [data-simplebar].simplebar-dragging .simplebar-content {
135 | pointer-events: none;
136 | -webkit-touch-callout: none;
137 | -webkit-user-select: none;
138 | -khtml-user-select: none;
139 | -moz-user-select: none;
140 | -ms-user-select: none;
141 | user-select: none;
142 | }
143 |
144 | [data-simplebar].simplebar-dragging .simplebar-track {
145 | pointer-events: all;
146 | }
147 |
148 | .simplebar-scrollbar {
149 | position: absolute;
150 | left: 0;
151 | right: 0;
152 | min-height: 10px;
153 | }
154 |
155 | .simplebar-scrollbar:before {
156 | position: absolute;
157 | content: '';
158 | background: black;
159 | border-radius: 7px;
160 | left: 2px;
161 | right: 2px;
162 | opacity: 0;
163 | transition: opacity 0.2s 0.5s linear;
164 | }
165 |
166 | .simplebar-scrollbar.simplebar-visible:before {
167 | opacity: 0.5;
168 | transition-delay: 0s;
169 | transition-duration: 0s;
170 | }
171 |
172 | .simplebar-track.simplebar-vertical {
173 | top: 0;
174 | width: 11px;
175 | }
176 |
177 | .simplebar-scrollbar:before {
178 | top: 2px;
179 | bottom: 2px;
180 | left: 2px;
181 | right: 2px;
182 | }
183 |
184 | .simplebar-track.simplebar-horizontal {
185 | left: 0;
186 | height: 11px;
187 | }
188 |
189 | .simplebar-track.simplebar-horizontal .simplebar-scrollbar {
190 | right: auto;
191 | left: 0;
192 | top: 0;
193 | bottom: 0;
194 | min-height: 0;
195 | min-width: 10px;
196 | width: auto;
197 | }
198 |
199 | /* Rtl support */
200 | [data-simplebar-direction='rtl'] .simplebar-track.simplebar-vertical {
201 | right: auto;
202 | left: 0;
203 | }
204 |
205 | .simplebar-dummy-scrollbar-size {
206 | direction: rtl;
207 | position: fixed;
208 | opacity: 0;
209 | visibility: hidden;
210 | height: 500px;
211 | width: 500px;
212 | overflow-y: hidden;
213 | overflow-x: scroll;
214 | -ms-overflow-style: scrollbar !important;
215 | }
216 |
217 | .simplebar-dummy-scrollbar-size > div {
218 | width: 200%;
219 | height: 200%;
220 | margin: 10px 0;
221 | }
222 |
223 | .simplebar-hide-scrollbar {
224 | position: fixed;
225 | left: 0;
226 | visibility: hidden;
227 | overflow-y: scroll;
228 | scrollbar-width: none;
229 | -ms-overflow-style: none;
230 | }
231 |
--------------------------------------------------------------------------------
/packages/simplebar-core/tests/simplebar.test.e2e.js:
--------------------------------------------------------------------------------
1 | import 'expect-puppeteer';
2 | import jestPuppeteerConfig from '../jest-puppeteer.config';
3 |
4 | /**
5 | * Mark test status on BrowserStack.
6 | *
7 | * @param {Page} page - Page object created by Puppeteer context.
8 | * @param {String} status - Status string can be either passed|failed.
9 | * @param {String} reason - Explanatory reason for the status marked.
10 | * @return {Promise} Stringified response from BrowserStack regarding the
11 | * execution of the jsExecutor.
12 | */
13 | function markTest(page, status, reason) {
14 | return page.evaluate(
15 | (_) => {},
16 | `browserstack_executor: ${JSON.stringify({
17 | action: 'setSessionStatus',
18 | arguments: { status, reason },
19 | })}`
20 | );
21 | }
22 |
23 | try {
24 | beforeAll(async () => {
25 | await page.goto(`http://localhost:${jestPuppeteerConfig.server.port}`);
26 | await page.waitForSelector('h1');
27 | }, 20000);
28 |
29 | beforeEach(async () => {
30 | await page.reload();
31 | await page.mouse.move(0, 0);
32 | });
33 |
34 | afterAll(async () => {
35 | await markTest(page, 'passed', 'All test passed!');
36 | });
37 |
38 | test('should render demo page', async () => {
39 | await expect(page).toMatch('Simplebar demo page');
40 | });
41 |
42 | test('should render SimpleBar on data-simplebar elements', async () => {
43 | await expect(page).toMatchElement('[data-simplebar] .simplebar-content');
44 | });
45 |
46 | test('should force scrollbar track to be visible but scrollbar to be hidden', async () => {
47 | const trackSelector =
48 | '[data-simplebar-force-visible] .simplebar-track.simplebar-vertical';
49 |
50 | await page.waitForSelector(trackSelector, { visible: true });
51 | await page.waitForSelector(`${trackSelector} .simplebar-scrollbar`, {
52 | hidden: true,
53 | });
54 | });
55 |
56 | test('should display SimpleBar in "rtl" mode', async () => {
57 | const el = await expect(page).toMatchElement('.demo-rtl');
58 | const scrollbarEl = await expect(el).toMatchElement(
59 | '.simplebar-track.simplebar-horizontal .simplebar-scrollbar'
60 | );
61 | const transformStyle = await page.evaluate(
62 | (el) => el.style.transform,
63 | scrollbarEl
64 | );
65 | const elBoundingBox = await el.boundingBox();
66 | const scrollbarElBoundingBox = await scrollbarEl.boundingBox();
67 |
68 | expect(transformStyle).toEqual(
69 | `translate3d(${
70 | elBoundingBox.width - scrollbarElBoundingBox.width
71 | }px, 0px, 0px)`
72 | );
73 | });
74 |
75 | test('should add class "dragging" when dragging', async () => {
76 | const el = await expect(page).toMatchElement('#demo2');
77 |
78 | await page.click(
79 | '#demo2 .simplebar-track.simplebar-vertical .simplebar-scrollbar'
80 | );
81 | await page.mouse.down();
82 |
83 | const isDragging = await page.evaluate(
84 | (el) => el.classList.contains('simplebar-dragging'),
85 | el
86 | );
87 |
88 | expect(isDragging).toBeTruthy();
89 | });
90 |
91 | test('should recalculate scrollbar size when content size changes', async () => {
92 | const el = await expect(page).toMatchElement('#demo2');
93 | const scrollbarEl = await expect(el).toMatchElement(
94 | '.simplebar-track.simplebar-vertical .simplebar-scrollbar'
95 | );
96 |
97 | const scrollbarHeight = await page.evaluate(
98 | (el) => parseInt(el.style.height),
99 | scrollbarEl
100 | );
101 |
102 | // await jestPuppeteer.debug();
103 |
104 | await page.hover('#demo2 p');
105 | await page.click('#demo2 p', { delay: 1000 }); // wait for Simplebar to recalculate
106 |
107 | const scrollbarHeightAfterHover = await page.evaluate(
108 | (el) => parseInt(el.style.height),
109 | scrollbarEl
110 | );
111 |
112 | expect(scrollbarHeightAfterHover).toBeLessThan(scrollbarHeight);
113 | });
114 | } catch (err) {
115 | markTest(page, 'failed', err.message);
116 | }
117 |
--------------------------------------------------------------------------------
/packages/simplebar-core/tests/simplebar.test.ts:
--------------------------------------------------------------------------------
1 | import SimpleBar from '../src';
2 |
3 | const template = `
16 |
19 | `;
22 |
23 | beforeEach(() => {
24 | jest.resetModules();
25 |
26 | // Set up our document body
27 | document.body.innerHTML = `${template}
`;
28 | });
29 |
30 | test('should call constructor', () => {
31 | const SimpleBar = require('../src/index').default;
32 | jest.mock('../src/index');
33 |
34 | new SimpleBar(document.getElementById('simplebar'));
35 |
36 | expect(SimpleBar).toHaveBeenCalledTimes(1);
37 | });
38 |
39 | test('should return the content element', () => {
40 | const simpleBar = new SimpleBar(
41 | document.getElementById('simplebar') as HTMLElement
42 | );
43 | const contentElement = simpleBar.getContentElement();
44 |
45 | expect(contentElement).toBe(simpleBar.contentEl);
46 | });
47 |
48 | test('should return the scroll element', () => {
49 | const simpleBar = new SimpleBar(
50 | document.getElementById('simplebar') as HTMLElement
51 | );
52 | const scrollElement = simpleBar.getScrollElement();
53 |
54 | expect(scrollElement).toBe(simpleBar.contentWrapperEl);
55 | });
56 |
57 | test('should unmount SimpleBar', () => {
58 | const simpleBar = new SimpleBar(
59 | document.getElementById('simplebar') as HTMLElement
60 | );
61 | const mock = jest.spyOn(simpleBar.mutationObserver as any, 'disconnect');
62 |
63 | simpleBar.unMount();
64 |
65 | expect(mock).toHaveBeenCalled();
66 | });
67 |
68 | test('should return the element options', () => {
69 | const simpleBar = new SimpleBar(
70 | document.getElementById('simplebar') as HTMLElement
71 | );
72 |
73 | expect(SimpleBar.getOptions(simpleBar.el.attributes)).toEqual({
74 | forceVisible: true,
75 | });
76 | });
77 |
78 | test('getOptions accepts objects', () => {
79 | expect(
80 | SimpleBar.getOptions([
81 | {
82 | name: 'data-simplebar-class-names',
83 | value: {
84 | wrapper: 'custom-class',
85 | },
86 | },
87 | ])
88 | ).toEqual({ classNames: { wrapper: 'custom-class' } });
89 | });
90 |
91 | test('classNames option works with multiple classes', () => {
92 | const simpleBar = new SimpleBar(
93 | document.getElementById('simplebar') as HTMLElement,
94 | { classNames: { contentWrapper: 'simplebar-content-wrapper custom-class' } }
95 | );
96 | const scrollElement = simpleBar.getScrollElement();
97 |
98 | expect(scrollElement?.className).toEqual(
99 | 'simplebar-content-wrapper custom-class'
100 | );
101 | });
102 |
103 | test('mouse should be within bounds', () => {
104 | const simpleBar = new SimpleBar(
105 | document.getElementById('simplebar') as HTMLElement
106 | );
107 |
108 | simpleBar.mouseX = 20;
109 | simpleBar.mouseY = 20;
110 |
111 | const isWithinBounds = simpleBar.isWithinBounds({
112 | bottom: 110,
113 | height: 100,
114 | left: 10,
115 | right: 110,
116 | top: 10,
117 | width: 100,
118 | x: 10,
119 | y: 10,
120 | } as DOMRect);
121 |
122 | expect(isWithinBounds).toBeTruthy();
123 | });
124 |
125 | test('onPointerEvent listener should be unsubscribed on unmount', () => {
126 | const element = document.getElementById('simplebar');
127 | const init = SimpleBar.prototype.init;
128 |
129 | if (!element) return;
130 |
131 | SimpleBar.prototype.init = () => {};
132 |
133 | const simpleBar = new SimpleBar(element);
134 |
135 | simpleBar.init = init;
136 |
137 | jest.spyOn(simpleBar, 'onPointerEvent');
138 |
139 | simpleBar.init();
140 |
141 | simpleBar.unMount();
142 |
143 | element.dispatchEvent(new MouseEvent('mousedown'));
144 |
145 | expect(simpleBar.onPointerEvent).not.toHaveBeenCalled();
146 | });
147 |
148 | describe('nested SimpleBars with initiated DOM', () => {
149 | let parent: SimpleBar;
150 | let child: SimpleBar;
151 |
152 | beforeEach(() => {
153 | // Set up our document body
154 | document.body.innerHTML = ``;
155 |
156 | parent = new SimpleBar(
157 | document.getElementById('simplebar-parent') as HTMLElement
158 | );
159 | child = new SimpleBar(
160 | document.getElementById('simplebar-child') as HTMLElement
161 | );
162 |
163 | parent.initDOM(); // trigger DOM selectors
164 | });
165 |
166 | test('should select correct wrapper element', () => {
167 | expect(parent.wrapperEl).not.toBe(child.wrapperEl);
168 | });
169 |
170 | test('should select correct content wrapper element', () => {
171 | expect(parent.contentWrapperEl).not.toBe(child.contentWrapperEl);
172 | });
173 |
174 | test('should select correct content element', () => {
175 | expect(parent.contentEl).not.toBe(child.contentEl);
176 | });
177 |
178 | test('should select correct offset element', () => {
179 | expect(parent.offsetEl).not.toBe(child.offsetEl);
180 | });
181 |
182 | test('should select correct mask element', () => {
183 | expect(parent.maskEl).not.toBe(child.maskEl);
184 | });
185 |
186 | test('should select correct placeholder element', () => {
187 | expect(parent.placeholderEl).not.toBe(child.placeholderEl);
188 | });
189 |
190 | test('should select correct height auto observer wrapper element', () => {
191 | expect(parent.heightAutoObserverWrapperEl).not.toBe(
192 | child.heightAutoObserverWrapperEl
193 | );
194 | });
195 |
196 | test('should select correct height auto observer element', () => {
197 | expect(parent.heightAutoObserverEl).not.toBe(child.heightAutoObserverEl);
198 | });
199 |
200 | test('should select correct x track element', () => {
201 | expect(parent.axis.x.track.el).not.toBe(child.axis.x.track.el);
202 | });
203 |
204 | test('should select correct y track element', () => {
205 | expect(parent.axis.y.track.el).not.toBe(child.axis.y.track.el);
206 | });
207 | });
208 |
--------------------------------------------------------------------------------
/packages/simplebar-react/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:react/recommended"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/simplebar-react/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | # SimplebarReact
14 |
15 | - **🐦 Follow me on [Twitter!](https://twitter.com/adriendenat) or [Mastodon!](https://mas.to/@adrien)**
16 | - **👨💻 I'm available for hire! [Reach out to me!](https://adriendenat.com/)**
17 | - **🚧 Check out my new project [Scroll Snap Carousel](https://github.com/Grsmto/scroll-snap-carousel)!**
18 |
19 | ### Installation
20 |
21 | **- Via npm**
22 | `npm install simplebar-react --save`
23 |
24 | **- Via Yarn**
25 | `yarn add simplebar-react`
26 |
27 | ### Usage
28 |
29 | Check out the [Demo project](https://github.com/Grsmto/simplebar/blob/master/examples/react/src/App.js) or our live [Codesandbox example](https://codesandbox.io/s/simplebar-react-gwgyw).
30 |
31 | If you are using a module loader (like Webpack) you first need to load SimpleBar:
32 |
33 | ```js
34 | import SimpleBar from 'simplebar-react';
35 | import 'simplebar-react/dist/simplebar.min.css';
36 |
37 | const App = () => (
38 | // your content
39 | );
40 | ```
41 |
42 | **Don't forget to import both css and js in your project!**
43 |
44 | ### :warning: Warning!
45 |
46 | SimpleBar is **not intended to be used on the `body` element!** I don't recommend wrapping your entire web page inside a custom scroll as it will often badly affect the user experience (slower scroll performance compared to the native body scroll, no native scroll behaviours like click on track, etc.). Do it at your own risk! SimpleBar is meant to improve the experience of **internal web page scrolling**; such as a chat box or a small scrolling area. **Please read the [caveats](#5-caveats) section first to be aware of the limitations!**
47 |
48 | ### Troubleshooting
49 |
50 | If you are experiencing issues when setting up SimpleBar, it is most likely because your styles are clashing with SimpleBar ones. Make sure the element you are setting SimpleBar on does not override any SimpleBar css properties! **We recommend to not style that element at all and use an inner element instead.**
51 |
52 | ### Sponsors
53 |
54 | Thanks to BrowserStack for sponsoring open source projects and letting us test SimpleBar for free.
55 |
56 |
57 |
58 |
59 | 1. [Documentation](#1-documentation)
60 |
61 | ## 1. Documentation
62 |
63 | ### Passing options
64 |
65 | Find the list of available options on [the core documentation](https://github.com/Grsmto/simplebar/blob/master/packages/simplebar/README.md#options).
66 |
67 | ```js
68 |
69 | // your content
70 |
71 | ```
72 |
73 | ### Extra options
74 |
75 | #### scrollableNodeProps
76 |
77 | You can pass props to the underlying scrollable `div` element. This is useful for example to get a `ref` of it, if you want to access the `scroll` event or apply imperative directive (like scrolling SimpleBar to the bottom, etc.).
78 |
79 | ```js
80 | const scrollableNodeRef = React.createRef();
81 |
82 |
83 | // your content
84 | ;
85 | ```
86 |
87 | ### Accessing SimpleBar instance
88 |
89 | You can pass a ref to the SimpleBar element:
90 |
91 | ```js
92 | const ref = useRef();
93 |
94 | useEffect(() => {
95 | ref.current.recalculate();
96 | console.log(ref.current.el); // <- the root element you applied SimpleBar on
97 | })
98 |
99 |
100 | // your content
101 |
102 | ```
103 |
104 | ### Usage with 3rd party libraries
105 |
106 | For advanced usage, in some cases (like using react-window) you can forward the scrollable and content elements using a render prop pattern:
107 |
108 | ```js
109 |
110 | {({ scrollableNodeRef, contentNodeRef }) => {
111 | // Now you have access to scrollable and content nodes
112 | return
;
113 | }}
114 |
115 | ```
116 |
117 | If you define your own elements you should do:
118 |
119 | ```js
120 |
121 | {({ scrollableNodeProps, contentNodeProps }) => {
122 | return (
123 |
124 | outer/scrollable element
125 |
inner element
126 |
127 | );
128 | }}
129 |
130 | ```
131 |
--------------------------------------------------------------------------------
/packages/simplebar-react/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import type { ReactNode, MutableRefObject } from 'react';
3 | import SimpleBarCore from 'simplebar-core';
4 | import type { SimpleBarOptions } from 'simplebar-core';
5 |
6 | type RenderFunc = (props: {
7 | scrollableNodeRef: MutableRefObject;
8 | scrollableNodeProps: {
9 | className: string;
10 | ref: MutableRefObject;
11 | };
12 | contentNodeRef: MutableRefObject;
13 | contentNodeProps: {
14 | className: string;
15 | ref: MutableRefObject;
16 | };
17 | }) => ReactNode;
18 |
19 | export interface Props
20 | extends Omit, 'children'>,
21 | SimpleBarOptions {
22 | children?: ReactNode | RenderFunc;
23 | scrollableNodeProps?: {
24 | ref?: any;
25 | className?: string;
26 | [key: string]: any;
27 | };
28 | }
29 |
30 | const SimpleBar = React.forwardRef(
31 | ({ children, scrollableNodeProps = {}, ...otherProps }, ref) => {
32 | const elRef = React.useRef();
33 | const scrollableNodeRef = React.useRef();
34 | const contentNodeRef = React.useRef();
35 | const options: Partial = {};
36 | const rest: any = {};
37 |
38 | Object.keys(otherProps).forEach((key) => {
39 | if (
40 | Object.prototype.hasOwnProperty.call(SimpleBarCore.defaultOptions, key)
41 | ) {
42 | (options as any)[key] = otherProps[key as keyof SimpleBarOptions];
43 | } else {
44 | rest[key] = otherProps[key as keyof SimpleBarOptions];
45 | }
46 | });
47 |
48 | const classNames = {
49 | ...SimpleBarCore.defaultOptions.classNames,
50 | ...options.classNames,
51 | } as Required<(typeof SimpleBarCore.defaultOptions)['classNames']>;
52 |
53 | const scrollableNodeFullProps = {
54 | ...scrollableNodeProps,
55 | className: `${classNames.contentWrapper}${
56 | scrollableNodeProps.className ? ` ${scrollableNodeProps.className}` : ''
57 | }`,
58 | tabIndex: options.tabIndex || SimpleBarCore.defaultOptions.tabIndex,
59 | role: 'region',
60 | 'aria-label': options.ariaLabel || SimpleBarCore.defaultOptions.ariaLabel,
61 | };
62 |
63 | React.useEffect(() => {
64 | let instance: SimpleBarCore | null;
65 | scrollableNodeRef.current = scrollableNodeFullProps.ref
66 | ? scrollableNodeFullProps.ref.current
67 | : scrollableNodeRef.current;
68 |
69 | if (elRef.current) {
70 | instance = new SimpleBarCore(elRef.current, {
71 | ...options,
72 | ...(scrollableNodeRef.current && {
73 | scrollableNode: scrollableNodeRef.current,
74 | }),
75 | ...(contentNodeRef.current && {
76 | contentNode: contentNodeRef.current,
77 | }),
78 | });
79 |
80 | if (typeof ref === 'function') {
81 | ref(instance);
82 | } else if (ref) {
83 | ref.current = instance;
84 | }
85 | }
86 |
87 | return () => {
88 | instance?.unMount();
89 | instance = null;
90 | if (typeof ref === 'function') {
91 | ref(null);
92 | }
93 | };
94 | }, []);
95 |
96 | return (
97 |
98 |
99 |
102 |
103 |
104 | {typeof children === 'function' ? (
105 | children({
106 | scrollableNodeRef,
107 | scrollableNodeProps: {
108 | ...scrollableNodeFullProps,
109 | ref: scrollableNodeRef,
110 | },
111 | contentNodeRef,
112 | contentNodeProps: {
113 | className: classNames.contentEl,
114 | ref: contentNodeRef,
115 | },
116 | })
117 | ) : (
118 |
121 | )}
122 |
123 |
124 |
125 |
126 |
129 |
132 |
133 | );
134 | }
135 | );
136 |
137 | SimpleBar.displayName = 'SimpleBar';
138 |
139 | export default SimpleBar;
140 |
--------------------------------------------------------------------------------
/packages/simplebar-react/jest.config.js:
--------------------------------------------------------------------------------
1 | const { defaults: tsjPreset } = require('ts-jest/presets');
2 |
3 | /** @type {import('ts-jest').JestConfigWithTsJest} */
4 | module.exports = {
5 | preset: 'ts-jest',
6 | transform: {
7 | ...tsjPreset.transform,
8 | '^.+\\.js?$': 'babel-jest',
9 | },
10 | testEnvironment: 'jsdom',
11 | setupFilesAfterEnv: ['/../../jest-setup.ts'],
12 | };
13 |
--------------------------------------------------------------------------------
/packages/simplebar-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simplebar-react",
3 | "version": "3.3.1",
4 | "description": "React component for SimpleBar",
5 | "files": [
6 | "dist",
7 | "README.md"
8 | ],
9 | "author": "Adrien Denat",
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/grsmto/simplebar.git",
13 | "directory": "packages/simplebar-react"
14 | },
15 | "main": "./dist/index.cjs",
16 | "module": "./dist/index.mjs",
17 | "types": "./dist/index.d.ts",
18 | "exports": {
19 | ".": {
20 | "import": "./dist/index.mjs",
21 | "require": "./dist/index.cjs"
22 | },
23 | "./dist/simplebar.min.css": "./dist/simplebar.min.css"
24 | },
25 | "sideEffects": [
26 | "*.css"
27 | ],
28 | "bugs": "https://github.com/grsmto/simplebar/issues",
29 | "homepage": "https://grsmto.github.io/simplebar/",
30 | "license": "MIT",
31 | "scripts": {
32 | "build": "rollup -c && minify ../simplebar-core/src/simplebar.css > dist/simplebar.min.css",
33 | "dev": "rollup -c -w",
34 | "test": "jest",
35 | "version": "yarn build",
36 | "precommit": "lint-staged"
37 | },
38 | "dependencies": {
39 | "simplebar-core": "^1.3.1"
40 | },
41 | "peerDependencies": {
42 | "react": ">=16.8.0"
43 | },
44 | "devDependencies": {
45 | "@testing-library/jest-dom": "^5.16.5",
46 | "@testing-library/react": "^13.4.0",
47 | "eslint-config-react-app": "^5.0.2",
48 | "eslint-plugin-react": "^7.14.3"
49 | },
50 | "lint-staged": {
51 | "*.{js,jsx,json}": [
52 | "prettier-eslint --write",
53 | "git add"
54 | ]
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/packages/simplebar-react/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import { createRequire } from 'node:module';
2 | import resolve from '@rollup/plugin-node-resolve';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import typescript from '@rollup/plugin-typescript';
5 | import license from 'rollup-plugin-license';
6 | import { getBanner, getExternals, tsConfig } from '../../rollup.config.mjs';
7 |
8 | const require = createRequire(import.meta.url);
9 | const pkg = require('./package.json');
10 |
11 | const globals = {
12 | react: 'React',
13 | 'simplebar-core': 'SimpleBarCore',
14 | };
15 |
16 | export default [
17 | {
18 | input: 'index.tsx',
19 | external: getExternals(pkg),
20 | output: {
21 | name: 'SimpleBarReact',
22 | file: pkg.main,
23 | globals: globals,
24 | format: 'umd',
25 | sourcemap: true,
26 | },
27 | plugins: [
28 | resolve(), // so Rollup can find dependencies
29 | commonjs(), // so Rollup can convert dependencies to an ES module
30 | typescript({ ...tsConfig, tsconfig: './tsconfig.json' }),
31 | license(getBanner(pkg)),
32 | ],
33 | },
34 | {
35 | input: 'index.tsx',
36 | external: getExternals(pkg),
37 | output: {
38 | file: pkg.module,
39 | format: 'esm',
40 | },
41 | plugins: [
42 | typescript({ ...tsConfig, tsconfig: './tsconfig.json' }),
43 | license(getBanner(pkg)),
44 | ],
45 | },
46 | ];
47 |
--------------------------------------------------------------------------------
/packages/simplebar-react/tests/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders with object as option 1`] = `
4 |
7 |
10 |
17 |
20 |
24 |
31 |
34 |
35 | Some content
36 |
37 |
38 | Some content
39 |
40 |
41 | Some content
42 |
43 |
44 | Some content
45 |
46 |
47 | Some content
48 |
49 |
50 |
51 |
52 |
53 |
57 |
58 |
67 |
76 |
77 | `;
78 |
79 | exports[`renders with options 1`] = `
80 |
154 | `;
155 |
156 | exports[`renders without crashing 1`] = `
157 |
158 |
161 |
164 |
171 |
174 |
178 |
185 |
188 |
189 | Some content
190 |
191 |
192 | Some content
193 |
194 |
195 | Some content
196 |
197 |
198 | Some content
199 |
200 |
201 | Some content
202 |
203 |
204 |
205 |
206 |
207 |
211 |
212 |
221 |
230 |
231 |
232 | `;
233 |
--------------------------------------------------------------------------------
/packages/simplebar-react/tests/index.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { render, waitFor } from '@testing-library/react';
3 | import SimpleBar from '../index';
4 | import SimpleBarCore from 'simplebar-core';
5 |
6 | test('renders without crashing', () => {
7 | const { container } = render(
8 |
9 |
10 | {[...Array(5)].map((_, i) => (
11 | Some content
12 | ))}
13 |
14 |
15 | );
16 | expect(container.firstChild).toMatchSnapshot();
17 | });
18 |
19 | test('renders with options', () => {
20 | const { container } = render(
21 |
22 | {[...Array(5)].map((x, i) => (
23 | Some content
24 | ))}
25 |
26 | );
27 | expect(container.firstChild).toMatchSnapshot();
28 |
29 | expect(
30 | container.querySelector('.simplebar-track.simplebar-vertical')
31 | ).toBeVisible();
32 | });
33 |
34 | test('renders with object as option', () => {
35 | const { container } = render(
36 |
41 | {[...Array(5)].map((x, i) => (
42 | Some content
43 | ))}
44 |
45 | );
46 | expect(container.firstChild).toMatchSnapshot();
47 |
48 | expect(container.querySelector('.custom-class')).toBeVisible();
49 | });
50 |
51 | test('renders with scrollableNodeProps', async () => {
52 | const { getByTestId } = render(
53 |
58 | {[...Array(5)].map((x, i) => (
59 | Some content
60 | ))}
61 |
62 | );
63 |
64 | await waitFor(() =>
65 | // getByTestId throws an error if it cannot find an element
66 | getByTestId('scrollable-node-props')
67 | );
68 | });
69 |
70 | test('renders with ref', async () => {
71 | const ref = React.createRef();
72 |
73 | const { unmount } = render(
74 | {
76 | //
77 | }}
78 | >
79 | {[...Array(5)].map((x, i) => (
80 | Some content
81 | ))}
82 |
83 | );
84 |
85 | if (!ref.current) return;
86 |
87 | jest.spyOn(ref.current, 'unMount');
88 |
89 | unmount();
90 |
91 | expect(ref.current.unMount).toHaveBeenCalled();
92 | });
93 |
94 | test('works on unmount', async () => {
95 | const ref = React.createRef();
96 |
97 | const { unmount } = render(
98 |
99 | {[...Array(5)].map((x, i) => (
100 | Some content
101 | ))}
102 |
103 | );
104 |
105 | jest.spyOn(ref.current, 'unMount');
106 |
107 | unmount();
108 |
109 | expect(ref.current.unMount).toHaveBeenCalled();
110 | });
111 |
112 | test('renders with ref callback', async () => {
113 | const callback = jest.fn((node) => {
114 | return node;
115 | });
116 |
117 | const { unmount } = render(
118 |
119 | {[...Array(5)].map((x, i) => (
120 | Some content
121 | ))}
122 |
123 | );
124 |
125 | expect(callback).toHaveBeenCalled();
126 | unmount();
127 | expect(callback).toHaveBeenCalledWith(null);
128 | });
129 |
130 | test('renders with render function', async () => {
131 | let ref1;
132 | let ref2;
133 |
134 | render(
135 |
136 | {({ scrollableNodeRef, contentNodeRef }) => {
137 | ref1 = scrollableNodeRef;
138 | ref2 = contentNodeRef;
139 | return null;
140 | }}
141 |
142 | );
143 |
144 | expect(ref1).toBeDefined();
145 | expect(ref2).toBeDefined();
146 | });
147 |
--------------------------------------------------------------------------------
/packages/simplebar-react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": ["**/*.ts", "**/*.tsx", "**/*.js", "../../jest-setup.ts"],
4 | "compilerOptions": {
5 | "jsx": "react"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | # SimplebarVue
14 |
15 | - **🐦 Follow me on [Twitter!](https://twitter.com/adriendenat) or [Mastodon!](https://mas.to/@adrien)**
16 | - **👨💻 I'm available for hire! [Reach out to me!](https://adriendenat.com/)**
17 | - **🚧 Check out my new project [Scroll Snap Carousel](https://github.com/Grsmto/scroll-snap-carousel)!**
18 |
19 | ### Installation
20 |
21 | **- Via npm**
22 | `npm install simplebar-vue --save`
23 |
24 | **- Via Yarn**
25 | `yarn add simplebar-vue`
26 |
27 | ### Usage
28 |
29 | Check out the [Demo project(vue2)](https://github.com/Grsmto/simplebar/blob/master/examples/vue-2.7/src/App.vue) and the [Demo project(vue3)](https://github.com/Grsmto/simplebar/blob/master/examples/vue-3/src/App.vue)
30 |
31 | First, register it in your Vue app:
32 |
33 | ```js
34 | import simplebar from 'simplebar-vue';
35 | import 'simplebar-vue/dist/simplebar.min.css';
36 |
37 | export default {
38 | components: {
39 | simplebar,
40 | },
41 | };
42 | ```
43 |
44 | Then you can use it in your template
45 |
46 | ```js
47 |
48 |
49 | // your content elements
50 |
51 |
52 | ```
53 |
54 | **Don't forget to import both css and js in your project!**
55 |
56 | ### :warning: Warning!
57 |
58 | SimpleBar is **not intended to be used on the `body` element!** I don't recommend wrapping your entire web page inside a custom scroll as it will often badly affect the user experience (slower scroll performance compared to the native body scroll, no native scroll behaviours like click on track, etc.). Do it at your own risk! SimpleBar is meant to improve the experience of **internal web page scrolling**; such as a chat box or a small scrolling area. **Please read the [caveats](#5-caveats) section first to be aware of the limitations!**
59 |
60 | ### Options
61 |
62 | Find the list of available options on [the core documentation](https://github.com/Grsmto/simplebar/blob/master/packages/simplebar/README.md#options).
63 |
64 | ### Troubleshooting
65 |
66 | If you are experiencing issues when setting up SimpleBar, it is most likely because your styles are clashing with SimpleBar ones. Make sure the element you are setting SimpleBar on does not override any SimpleBar css properties! **We recommend to not style that element at all and use an inner element instead.**
67 |
68 | ### Sponsors
69 |
70 | Thanks to BrowserStack for sponsoring open source projects and letting us test SimpleBar for free.
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/index.ts:
--------------------------------------------------------------------------------
1 | import simplebar from './component';
2 |
3 | export default simplebar;
4 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/jest.config.js:
--------------------------------------------------------------------------------
1 | const { defaults: tsjPreset } = require('ts-jest/presets');
2 | const { isVue3 } = require('vue-demi');
3 |
4 | /** @type {import('ts-jest').JestConfigWithTsJest} */
5 | module.exports = {
6 | preset: 'ts-jest',
7 | transform: {
8 | ...tsjPreset.transform,
9 | '^.+\\.js?$': 'babel-jest',
10 | '^.+\\.vue$': isVue3 ? 'vue-jest' : 'vue-jest2',
11 | },
12 | moduleNameMapper: {
13 | '^@/(.*)$': '/src/$1',
14 | },
15 | testEnvironment: 'jsdom',
16 | snapshotSerializers: ['jest-serializer-vue'],
17 | testEnvironmentOptions: {
18 | url: 'http://localhost/',
19 | customExportConditions: ['node', 'node-addons'], // See https://github.com/vuejs/vue-jest/issues/479
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simplebar-vue",
3 | "version": "2.4.1",
4 | "description": "Vue component for SimpleBar",
5 | "files": [
6 | "dist",
7 | "README.md"
8 | ],
9 | "author": "Piers Olenski",
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/grsmto/simplebar.git",
13 | "directory": "packages/simplebar-vue"
14 | },
15 | "main": "dist/simplebar-vue.js",
16 | "module": "dist/simplebar-vue.esm.js",
17 | "sideEffects": [
18 | "*.css"
19 | ],
20 | "types": "dist/index.d.ts",
21 | "bugs": "https://github.com/grsmto/simplebar/issues",
22 | "homepage": "https://grsmto.github.io/simplebar/",
23 | "license": "MIT",
24 | "scripts": {
25 | "build": "rollup -c && minify ../simplebar-core/src/simplebar.css > dist/simplebar.min.css && cp simplebar-vue.d.ts dist/simplebar-vue.d.ts",
26 | "jest": "jest",
27 | "version": "yarn build",
28 | "precommit": "lint-staged",
29 | "test:2.7": "yarn use-vue:2.7 && yarn jest",
30 | "test:3": "yarn use-vue:3 && yarn jest",
31 | "test": "yarn test:2.7 && yarn test:3",
32 | "use-vue:2.7": "node scripts/swap-vue.js 2.7 && vue-demi-switch 2.7",
33 | "use-vue:3": "node scripts/swap-vue.js 3 && vue-demi-switch 3"
34 | },
35 | "dependencies": {
36 | "simplebar-core": "^1.3.1",
37 | "vue-demi": "^0.13.11"
38 | },
39 | "peerDependencies": {
40 | "vue": ">=2.5.17"
41 | },
42 | "devDependencies": {
43 | "@babel/core": "^7.4.3",
44 | "@vue/compiler-sfc": "^3.2.45",
45 | "@vue/composition-api": "^1.7.1",
46 | "@vue/test-utils": "^2.2.7",
47 | "@vue/test-utils-vue2": "npm:@vue/test-utils@~1",
48 | "babel-core": "^7.0.0-bridge.0",
49 | "babel-jest": "^28.1.0",
50 | "eslint-plugin-vue": "^9.8.0",
51 | "jest-serializer-vue": "^2.0.2",
52 | "rollup-plugin-vue": "^6.0.0",
53 | "vue": "^3.2.45",
54 | "vue-jest": "^5.0.0-alpha.0",
55 | "vue-jest2": "npm:vue-jest@4",
56 | "vue-template-compiler2.6": "npm:vue-template-compiler@2.6.14",
57 | "vue-template-compiler2.7": "npm:vue-template-compiler@2.7.14",
58 | "vue2.7": "npm:vue@2.7.14"
59 | },
60 | "lint-staged": {
61 | "*.{js,jsx,json}": [
62 | "prettier-eslint --write",
63 | "git add"
64 | ]
65 | },
66 | "eslintConfig": {
67 | "root": true,
68 | "env": {
69 | "node": true,
70 | "jest": true
71 | },
72 | "extends": [
73 | "plugin:vue/essential",
74 | "eslint:recommended"
75 | ],
76 | "rules": {},
77 | "parserOptions": {
78 | "parser": "babel-eslint"
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import { createRequire } from 'node:module';
2 | import resolve from '@rollup/plugin-node-resolve';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import typescript from '@rollup/plugin-typescript';
5 | import license from 'rollup-plugin-license';
6 | import { getBanner, getExternals, tsConfig } from '../../rollup.config.mjs';
7 |
8 | const require = createRequire(import.meta.url);
9 | const pkg = require('./package.json');
10 |
11 | const globals = {
12 | 'simplebar-core': 'SimpleBar',
13 | };
14 |
15 | export default [
16 | {
17 | input: 'index.ts',
18 | output: {
19 | name: 'SimpleBarVue',
20 | file: pkg.main,
21 | globals: globals,
22 | format: 'umd',
23 | sourcemap: true,
24 | },
25 | external: getExternals(pkg),
26 | plugins: [
27 | resolve(), // so Rollup can find dependencies
28 | commonjs(), // so Rollup can convert dependencies to an ES module
29 | typescript(tsConfig),
30 | license(getBanner(pkg)),
31 | ],
32 | },
33 | {
34 | input: 'index.ts',
35 | external: getExternals(pkg),
36 | output: {
37 | file: pkg.module,
38 | format: 'esm',
39 | },
40 | plugins: [typescript(tsConfig), license(getBanner(pkg))],
41 | },
42 | ];
43 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/scripts/swap-vue.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Vuelidate
3 | */
4 | /* eslint-disable camelcase */
5 | const fs = require('fs');
6 | const path = require('path');
7 |
8 | const Vue2_7 = path.join(__dirname, '../../../node_modules/vue2.7');
9 | const DefaultVue = path.join(__dirname, '../../../node_modules/vue');
10 | const Vue3 = path.join(__dirname, '../../../node_modules/vue3');
11 | const vueTemplateCompiler = path.join(
12 | __dirname,
13 | '../../../node_modules/vue-template-compiler'
14 | );
15 | const vueTemplateCompiler2_6 = path.join(
16 | __dirname,
17 | '../../../node_modules/vue-template-compiler2.6'
18 | );
19 | const vueTemplateCompiler2_7 = path.join(
20 | __dirname,
21 | '../../../node_modules/vue-template-compiler2.7'
22 | );
23 |
24 | const version = Number(process.argv[2]) || 3;
25 |
26 | useVueVersion(version);
27 |
28 | function useVueVersion(version) {
29 | console.log('[SWAP-VUE] SETTING VERSION:', version);
30 |
31 | if (!fs.existsSync(DefaultVue)) {
32 | console.log('There is no default Vue version, finding it');
33 | if (version === 2 && fs.existsSync(Vue3)) {
34 | rename(Vue3, DefaultVue);
35 | console.log('Renamed "vue3" to "vue"');
36 | } else if (version === 2.7 && fs.existsSync(Vue2_7)) {
37 | rename(Vue2_7, DefaultVue);
38 | useTemplateCompilerVersion(2.7);
39 | console.log('Renamed "vue2.7" to "vue"');
40 | }
41 | }
42 |
43 | if (version === 3 && fs.existsSync(Vue3)) {
44 | resetPackageNames();
45 | rename(Vue3, DefaultVue);
46 | } else if (version === 2.7 && fs.existsSync(Vue2_7)) {
47 | resetPackageNames();
48 | rename(Vue2_7, DefaultVue);
49 | useTemplateCompilerVersion(2.7);
50 | } else {
51 | console.log(`Vue ${version} is already in use`);
52 | }
53 | }
54 |
55 | function resetPackageNames() {
56 | if (!fs.existsSync(Vue3)) {
57 | rename(DefaultVue, Vue3);
58 | } else if (!fs.existsSync(Vue2_7)) {
59 | rename(DefaultVue, Vue2_7);
60 | } else {
61 | console.error('Unable to reset package names');
62 | }
63 | }
64 |
65 | function useTemplateCompilerVersion(version) {
66 | if (!fs.existsSync(vueTemplateCompiler)) {
67 | console.log(
68 | 'There is no default vue-template-compiler version, finding it',
69 | version
70 | );
71 | if (version === 2.7 && fs.existsSync(vueTemplateCompiler2_7)) {
72 | rename(vueTemplateCompiler2_7, vueTemplateCompiler);
73 | console.log(
74 | 'Renamed "vue-template-compliler2.7" to "vue-template-compliler"'
75 | );
76 | } else {
77 | rename(vueTemplateCompiler2_6, vueTemplateCompiler);
78 | console.log(
79 | 'Renamed "vue-template-compliler2.6" to "vue-template-compliler"'
80 | );
81 | }
82 | }
83 | if (version === 2.7 && fs.existsSync(vueTemplateCompiler2_7)) {
84 | rename(vueTemplateCompiler, vueTemplateCompiler2_6);
85 | rename(vueTemplateCompiler2_7, vueTemplateCompiler);
86 | } else if (version === 2 && fs.existsSync(vueTemplateCompiler2_6)) {
87 | rename(vueTemplateCompiler, vueTemplateCompiler2_7);
88 | rename(vueTemplateCompiler2_6, vueTemplateCompiler);
89 | } else {
90 | console.log(`vue-template-compliler ${version} is already in use`);
91 | }
92 | }
93 |
94 | function rename(fromPath, toPath) {
95 | if (!fs.existsSync(fromPath)) return;
96 | try {
97 | fs.renameSync(fromPath, toPath);
98 | console.log(`Successfully renamed ${fromPath} to ${toPath} .`);
99 | } catch (err) {
100 | console.log(err);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/simplebar-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'simplebar-vue' {
2 | import { DefineComponent } from 'vue';
3 | import SimpleBar from 'simplebar';
4 |
5 | export type SimpleBarComponent = DefineComponent<{ // Props
6 |
7 | /**
8 | * By default SimpleBar automatically hides the scrollbar if the user is not scrolling
9 | * (it emulates Mac OSX Lion's scrollbar). You can make the scrollbar always visible
10 | * by passing `false`.
11 | *
12 | * Default value is `true`.
13 | *
14 | * You can also control the animation via CSS as it's a simple CSS opacity transition.
15 | */
16 | autoHide?: boolean;
17 |
18 | /**
19 | * It is possible to change the default class names that SimpleBar uses.
20 | * To get your own styles to work refer to simplebar.css to get an idea how to setup your css.
21 | * - `content` represents the wrapper for the content being scrolled.
22 | * - `scrollContent` represents the container containing the elements being scrolled.
23 | * - `scrollbar` defines the style of the scrollbar with which the user can interact to scroll the content.
24 | * - `track` styles the area surrounding the `scrollbar`.
25 | *
26 | * ```js
27 | * classNames: {
28 | * // defaults
29 | * content: 'simplebar-content',
30 | * scrollContent: 'simplebar-scroll-content',
31 | * scrollbar: 'simplebar-scrollbar',
32 | * track: 'simplebar-track'
33 | * }
34 | * ```
35 | */
36 | classNames?: Partial<{ content: string; scrollContent: string; scrollbar: string; track: string }>;
37 |
38 | /**
39 | * Force the track to be visible (same behaviour as `overflow: scroll`).
40 | * Can be `boolean | 'x' | 'y'`, defaults to `false`, which behaves like `overflow: auto`.
41 | */
42 | forceVisible?: boolean | 'x' | 'y';
43 |
44 | /**
45 | * Set custom aria-label attribute for users with screen reader.
46 | */
47 | ariaLabel?: string;
48 |
49 | /**
50 | * Set custom tabIndex attribute.
51 | */
52 | tabIndex?: number;
53 |
54 | /**
55 | * Activate RTL support by passing `'rtl'`.
56 | * You will also need a css rule with `direction: rtl`.
57 | */
58 | direction?: 'ltr' | 'rtl';
59 |
60 | /**
61 | * Define the delay until the scrollbar hides. Has no effect if `autoHide` is `false`.
62 | * Default value is `1000`.
63 | */
64 | timeout?: number;
65 |
66 | /**
67 | * Controls the click on track behaviour.
68 | * Default to `true`.
69 | */
70 | clickOnTrack?: boolean;
71 |
72 | /**
73 | * Controls the min size of the scrollbar in `px`.
74 | * Default is `25`.
75 | */
76 | scrollbarMinSize?: number;
77 |
78 | /**
79 | * Controls the max size of the scrollbar in `px`.
80 | * Default is `0` (no max size).
81 | */
82 | scrollbarMaxSize?: number;
83 | },
84 | { }, // RawBindings
85 | { // Data
86 | SimpleBar: SimpleBar;
87 | contentElement: HTMLDivElement;
88 | scrollElement: HTMLDivElement;
89 | },
90 | { }, // Computed
91 | { // Methods
92 | recalculate (): void;
93 | },
94 | { }, // Mixins
95 | { }, // Extends
96 | { // Emits
97 | scroll: ($event: Event & { target: HTMLElement }) => void
98 | }>;
99 |
100 | const Component: SimpleBarComponent;
101 | export default Component;
102 | }
103 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/tests/__snapshots__/index.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`simplebar snapshots renders with default slot 1`] = `
4 |
27 | `;
28 |
29 | exports[`simplebar snapshots renders without crashing 1`] = `
30 |
51 | `;
52 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/tests/index.test.ts:
--------------------------------------------------------------------------------
1 | import { shallowMount, destroyWrapper } from './test-utils';
2 | import simplebar from '../component';
3 | import SimpleBar from 'simplebar-core';
4 |
5 | describe('simplebar', () => {
6 | describe('snapshots', () => {
7 | /**
8 | * jest-serializer-vue is not compatible with vue3 yet, so you
9 | * cannot pass the wrapper object directly. Call .html() before
10 | * @see {@link https://github.com/eddyerburgh/jest-serializer-vue/pull/56}
11 | */
12 | it('renders without crashing', () => {
13 | const wrapper = shallowMount(simplebar);
14 | expect(wrapper.html()).toMatchSnapshot();
15 | destroyWrapper(wrapper);
16 | });
17 |
18 | it('renders with default slot', () => {
19 | const wrapper = shallowMount(simplebar, {
20 | slots: {
21 | default: '
',
22 | },
23 | });
24 | expect(wrapper.html()).toMatchSnapshot();
25 | });
26 | });
27 |
28 | it('can access SimpleBar instance', () => {
29 | const wrapper = shallowMount(simplebar);
30 | expect(wrapper.vm.SimpleBar).toBeInstanceOf(SimpleBar);
31 | });
32 |
33 | it('can access root element ref property', () => {
34 | const wrapper = shallowMount(simplebar);
35 | expect(wrapper.element).toEqual(wrapper.vm.$refs.element);
36 | });
37 |
38 | it('can access scrollElement property', () => {
39 | const wrapper = shallowMount(simplebar);
40 | const scrollElement = wrapper.vm.scrollElement;
41 |
42 | expect(scrollElement).toEqual(
43 | wrapper.find('.simplebar-content-wrapper').element
44 | );
45 | });
46 |
47 | it('can access contentElement property', () => {
48 | const wrapper = shallowMount(simplebar);
49 | const scrollElement = wrapper.vm.contentElement;
50 |
51 | expect(scrollElement).toEqual(wrapper.find('.simplebar-content').element);
52 | });
53 |
54 | it('works with options as data attributes', () => {
55 | const wrapper = shallowMount(simplebar, {
56 | attrs: { 'data-simplebar-force-visible': 'true', 'tabIndex': -1 },
57 | });
58 | expect(wrapper.vm.SimpleBar.options.forceVisible).toEqual(true);
59 | expect(wrapper.vm.SimpleBar.options.tabIndex).toEqual(-1);
60 | });
61 |
62 | it('works with options as props', () => {
63 | const wrapper = shallowMount(simplebar, {
64 | propsData: { forceVisible: true },
65 | });
66 | expect(wrapper.vm.SimpleBar.options.forceVisible).toEqual(true);
67 | });
68 |
69 | it('emits a scroll event', async () => {
70 | const wrapper = shallowMount(simplebar);
71 | const scrollElement = wrapper.find('.simplebar-content-wrapper');
72 |
73 | expect(wrapper.emitted()).not.toHaveProperty('scroll');
74 | await scrollElement.trigger('scroll');
75 | expect(wrapper.emitted()).toHaveProperty('scroll');
76 | });
77 |
78 | it('destroys Simplebar instance when component is unmounted to prevent memory leaks', () => {
79 | // expect.assertions(1);
80 | const wrapper = shallowMount(simplebar);
81 | const instance = wrapper.vm.SimpleBar;
82 | jest.spyOn(instance, 'unMount');
83 |
84 | destroyWrapper(wrapper);
85 | expect(instance.unMount).toHaveBeenCalledTimes(1);
86 | });
87 | });
88 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/tests/test-utils.ts:
--------------------------------------------------------------------------------
1 | import { isVue3 } from 'vue-demi';
2 | import { lifecycleEventNames } from '../utils';
3 |
4 | export const vtu = isVue3
5 | ? require('@vue/test-utils')
6 | : require('@vue/test-utils-vue2');
7 |
8 | const { config } = vtu;
9 |
10 | if (isVue3) {
11 | config.global.renderStubDefaultSlot = true;
12 | }
13 |
14 | export function destroyWrapper(wrapper: any) {
15 | if (wrapper) wrapper[lifecycleEventNames.unmount]();
16 | }
17 |
18 | export const { shallowMount } = vtu;
19 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": ["**/*.ts", "**/*.tsx", "**/*.js", "../../jest-setup.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/simplebar-vue/utils.ts:
--------------------------------------------------------------------------------
1 | import { isVue3 } from 'vue-demi';
2 |
3 | export const lifecycleEventNames = {
4 | beforeUnmount: isVue3 ? 'beforeUnmount' : 'beforeDestroy',
5 | unmount: isVue3 ? 'unmount' : 'destroy',
6 | };
7 |
--------------------------------------------------------------------------------
/packages/simplebar/.babelrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"],
3 | "plugins": ["transform-class-properties"],
4 | "env": {
5 | "test": {
6 | "presets": [
7 | [
8 | "@babel/preset-env",
9 | {
10 | "targets": {
11 | "node": "current"
12 | }
13 | }
14 | ]
15 | ]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/simplebar/.env:
--------------------------------------------------------------------------------
1 | BROWSERSTACK_USERNAME=
2 | BROWSERSTACK_ACCESS_KEY=
3 |
--------------------------------------------------------------------------------
/packages/simplebar/.gitignore:
--------------------------------------------------------------------------------
1 | bundle.js
2 | bundle.js.map
3 | .env
4 |
--------------------------------------------------------------------------------
/packages/simplebar/jest-unit.config.js:
--------------------------------------------------------------------------------
1 | const { defaults: tsjPreset } = require('ts-jest/presets');
2 |
3 | /** @type {import('ts-jest').JestConfigWithTsJest} */
4 | module.exports = {
5 | preset: 'ts-jest',
6 | transform: {
7 | ...tsjPreset.transform,
8 | '^.+\\.js?$': 'babel-jest',
9 | },
10 | testEnvironment: 'jsdom',
11 | };
12 |
--------------------------------------------------------------------------------
/packages/simplebar/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "6.3.1",
3 | "name": "simplebar",
4 | "title": "SimpleBar.js",
5 | "description": "Scrollbars, simpler.",
6 | "files": [
7 | "dist",
8 | "src",
9 | "README.md"
10 | ],
11 | "author": "Adrien Denat from a fork by Jonathan Nicol",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/grsmto/simplebar.git",
15 | "directory": "packages/simplebar"
16 | },
17 | "main": "./dist/index.cjs",
18 | "module": "./dist/index.mjs",
19 | "types": "./dist/index.d.ts",
20 | "unpkg": "./dist/simplebar.min.js",
21 | "style": "./dist/simplebar.min.css",
22 | "exports": {
23 | ".": {
24 | "import": "./dist/index.mjs",
25 | "require": "./dist/index.cjs"
26 | },
27 | "./dist/simplebar.min.css": "./dist/simplebar.min.css"
28 | },
29 | "homepage": "https://grsmto.github.io/simplebar/",
30 | "bugs": "https://github.com/grsmto/simplebar/issues",
31 | "license": "MIT",
32 | "scripts": {
33 | "build": "rollup -c && cp ../simplebar-core/src/simplebar.css dist/simplebar.css && minify dist/simplebar.css > dist/simplebar.min.css",
34 | "dev": "rollup -c -w --environment BUILD:development",
35 | "test": "jest -c jest-unit.config.js",
36 | "version": "yarn build",
37 | "precommit": "lint-staged"
38 | },
39 | "dependencies": {
40 | "simplebar-core": "^1.3.1"
41 | },
42 | "devDependencies": {
43 | "@babel/plugin-transform-runtime": "^7.19.6",
44 | "intern": "^4.4.2",
45 | "minify": "^3.0.5",
46 | "promise": "^8.0.2"
47 | },
48 | "lint-staged": {
49 | "*.{js,jsx,json}": [
50 | "prettier-eslint --write",
51 | "git add"
52 | ]
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/simplebar/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import { createRequire } from 'node:module';
2 | import resolve from '@rollup/plugin-node-resolve';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import typescript from '@rollup/plugin-typescript';
5 | import license from 'rollup-plugin-license';
6 | import { terser } from 'rollup-plugin-terser';
7 | import { getBanner, getExternals, tsConfig } from '../../rollup.config.mjs';
8 |
9 | const require = createRequire(import.meta.url);
10 | const pkg = require('./package.json');
11 |
12 | const builds = [
13 | // ES module (for bundlers) build.
14 | {
15 | input: 'src/index.ts',
16 | external: getExternals(pkg),
17 | output: {
18 | file: pkg.module,
19 | format: 'esm',
20 | sourcemap: true,
21 | },
22 | plugins: [typescript(tsConfig), license(getBanner(pkg))],
23 | },
24 | ];
25 |
26 | if (process.env.BUILD !== 'development') {
27 | // UMD build
28 | builds.push({
29 | input: 'src/index.ts',
30 | external: getExternals(pkg),
31 | output: {
32 | name: 'SimpleBar',
33 | file: pkg.main,
34 | format: 'umd',
35 | exports: 'named',
36 | globals: {
37 | 'simplebar-core': 'SimpleBar',
38 | },
39 | },
40 | plugins: [typescript(tsConfig), license(getBanner(pkg))],
41 | });
42 |
43 | // browser script tag build, minified
44 | builds.push({
45 | input: 'src/index.ts',
46 | output: {
47 | name: 'SimpleBar',
48 | file: pkg.unpkg,
49 | format: 'iife',
50 | },
51 | plugins: [
52 | resolve(),
53 | commonjs(),
54 | typescript(tsConfig),
55 | terser(),
56 | license(getBanner(pkg)),
57 | ],
58 | });
59 |
60 | builds.push(
61 | // browser script tag build, non-minified
62 | {
63 | input: 'src/index.ts',
64 | output: {
65 | name: 'SimpleBar',
66 | file: 'dist/simplebar.js',
67 | format: 'iife',
68 | },
69 | plugins: [
70 | resolve(),
71 | commonjs(),
72 | typescript(tsConfig),
73 | license(getBanner(pkg)),
74 | ],
75 | }
76 | );
77 | }
78 |
79 | export default builds;
80 |
--------------------------------------------------------------------------------
/packages/simplebar/tests/simplebar.test.js:
--------------------------------------------------------------------------------
1 | import { waitFor, waitForElementToBeRemoved } from '@testing-library/dom';
2 |
3 | let SimpleBar;
4 |
5 | beforeEach(() => {
6 | jest.isolateModules(() => {
7 | SimpleBar = require('../src').default;
8 | });
9 |
10 | // Set up our document body
11 | document.body.innerHTML =
12 | '
';
13 | });
14 |
15 | test('should return the element options', async () => {
16 | const simpleBar = new SimpleBar(document.getElementById('simplebar'));
17 | await new Promise(process.nextTick);
18 | expect(SimpleBar.getOptions(simpleBar.el.attributes)).toEqual({
19 | autoHide: true,
20 | });
21 | });
22 |
23 | test('unmount on node removed from DOM', async () => {
24 | const simpleBar = new SimpleBar(document.getElementById('simplebar'));
25 |
26 | await waitFor(() =>
27 | expect(
28 | document.getElementById('simplebar').getAttribute('data-simplebar')
29 | ).toEqual('init')
30 | );
31 |
32 | waitForElementToBeRemoved(document.getElementById('simplebar')).then(() => {
33 | expect(SimpleBar.instances.get(simpleBar.el)).toBeUndefined();
34 | });
35 |
36 | document.getElementById('simplebar').remove();
37 | });
38 |
--------------------------------------------------------------------------------
/packages/website/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # dotenv environment variables file
55 | .env
56 |
57 | # gatsby files
58 | .cache/
59 | public
60 |
61 | # Mac files
62 | .DS_Store
63 |
64 | # Yarn
65 | yarn-error.log
66 | .pnp/
67 | .pnp.js
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
--------------------------------------------------------------------------------
/packages/website/.prettierignore:
--------------------------------------------------------------------------------
1 | .cache
2 | package.json
3 | package-lock.json
4 | public
5 |
--------------------------------------------------------------------------------
/packages/website/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "semi": false,
4 | "singleQuote": false,
5 | "tabWidth": 2,
6 | "trailingComma": "es5"
7 | }
8 |
--------------------------------------------------------------------------------
/packages/website/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 gatsbyjs
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 |
23 |
--------------------------------------------------------------------------------
/packages/website/README.md:
--------------------------------------------------------------------------------
1 | ../../README.md
--------------------------------------------------------------------------------
/packages/website/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implement Gatsby's Browser APIs in this file.
3 | *
4 | * See: https://www.gatsbyjs.org/docs/browser-apis/
5 | */
6 |
7 | export const onClientEntry = () => {
8 | return new Promise((resolve, reject) => {
9 | // Polyfill.io stuff
10 | window.__polyfillio__ = () => {
11 | resolve()
12 | }
13 |
14 | const features = []
15 |
16 | if (!("fetch" in window)) {
17 | features.push("fetch")
18 | }
19 |
20 | if (features.length) {
21 | const s = document.createElement("script")
22 | s.src = `https://cdn.polyfill.io/v3/polyfill.min.js?features=${features.join(
23 | ","
24 | )}&callback=__polyfillio__`
25 | s.async = true
26 | s.onerror = reject
27 | document.head.appendChild(s)
28 | } else {
29 | resolve()
30 | }
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/packages/website/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | pathPrefix: "/simplebar",
3 | siteMetadata: {
4 | title: `SimpleBar · Custom scrollbars made simple.`,
5 | description: `SimpleBar does only one thing: replace the browser's default scrollbar with a custom CSS-styled one without losing performances. For React, Vue, Angular or VanillaJS!`,
6 | author: `@adriendenat`,
7 | },
8 | plugins: [
9 | `gatsby-plugin-react-helmet`,
10 | {
11 | resolve: `gatsby-source-filesystem`,
12 | options: {
13 | name: `images`,
14 | path: `${__dirname}/src/images`,
15 | },
16 | },
17 | {
18 | resolve: `gatsby-source-filesystem`,
19 | options: {
20 | name: `markdown`,
21 | path: `${__dirname}/README.md`,
22 | },
23 | },
24 | `gatsby-transformer-remark`,
25 | ],
26 | }
27 |
--------------------------------------------------------------------------------
/packages/website/gatsby-node.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implement Gatsby's Node APIs in this file.
3 | *
4 | * See: https://www.gatsbyjs.org/docs/node-apis/
5 | */
6 |
--------------------------------------------------------------------------------
/packages/website/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
3 | *
4 | * See: https://www.gatsbyjs.org/docs/ssr-apis/
5 | */
6 |
7 | // You can delete this file if you're not using it
8 |
--------------------------------------------------------------------------------
/packages/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simplebar-website",
3 | "private": true,
4 | "description": "A simple starter to get up and developing quickly with Gatsby",
5 | "version": "0.2.1",
6 | "author": "Adrien Denat ",
7 | "scripts": {
8 | "build": "gatsby build --prefix-paths",
9 | "develop": "gatsby develop",
10 | "format": "prettier --write \"**/*.{js,jsx,json,md}\"",
11 | "start": "yarn develop",
12 | "serve": "gatsby serve --prefix-paths",
13 | "deploy": "gh-pages -d public -r https://$GITHUB_TOKEN@github.com/grsmto/simplebar.git"
14 | },
15 | "dependencies": {
16 | "@emotion/core": "^10.0.28",
17 | "@mdx-js/react": "^1.6.4",
18 | "@theme-ui/preset-base": "^0.2.40",
19 | "gatsby": "^4.25.2",
20 | "modern-normalize": "^0.5.0",
21 | "prop-types": "^15.7.2",
22 | "react-helmet": "^5.2.1",
23 | "react-icons": "^3.7.0",
24 | "react-select": "^2.4.3",
25 | "react-window": "^1.8.1",
26 | "simplebar": "^6.3.1",
27 | "simplebar-react": "^3.3.1",
28 | "theme-ui": "^0.3.1",
29 | "typeface-nunito": "^1.1.3",
30 | "whatwg-fetch": "^3.0.0"
31 | },
32 | "devDependencies": {
33 | "gatsby-cli": "4.25.0",
34 | "gatsby-plugin-react-helmet": "^5.25.0",
35 | "gatsby-source-filesystem": "^4.25.0",
36 | "gatsby-transformer-remark": "^5.25.1",
37 | "gh-pages": "^2.1.1"
38 | },
39 | "keywords": [
40 | "gatsby"
41 | ],
42 | "license": "MIT",
43 | "repository": {
44 | "type": "git",
45 | "url": "https://github.com/gatsbyjs/gatsby-starter-default"
46 | },
47 | "bugs": {
48 | "url": "https://github.com/gatsbyjs/gatsby/issues"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/website/src/components/Layout.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx, ThemeProvider } from "theme-ui"
3 | import PropTypes from "prop-types"
4 | import { Global } from "@emotion/core"
5 | import { withPrefix } from "gatsby"
6 |
7 | import theme from "../theme"
8 |
9 | import browserstackLogoUrl from "../images/browserstack.png"
10 |
11 | import "modern-normalize/modern-normalize.css"
12 | import "typeface-nunito"
13 |
14 | const Layout = ({ children, ...otherProps }) => {
15 | return (
16 |
17 | ({
19 | html: {
20 | WebkitFontSmoothing: "antialiased",
21 | MozOsxFontSmoothing: "grayscale",
22 | fontSize: "62.5%",
23 | },
24 | body: {
25 | fontSize: theme.fontSizes.body,
26 | fontFamily: theme.fonts.body,
27 | fontWeight: theme.fontWeights.body,
28 | lineHeight: theme.lineHeights.body,
29 | color: theme.colors.body,
30 | },
31 | "b, strong": {
32 | fontWeight: 700,
33 | },
34 | a: {
35 | textDecoration: "none",
36 | color: theme.colors.primary,
37 | "&:hover": {
38 | textDecoration: "underline",
39 | },
40 | },
41 | "button, a": {
42 | transition: "all 150ms",
43 | },
44 | img: {
45 | display: "block",
46 | maxWidth: "100%",
47 | },
48 | "h1, h2, h3": {
49 | fontWeight: theme.fontWeights.heading,
50 | },
51 | h3: {
52 | fontSize: theme.fontSizes[5],
53 | },
54 | ".simplebar-scrollbar:before": {
55 | backgroundImage:
56 | "linear-gradient(-131deg, #E7B02B 0%, #C13E51 100%)",
57 | },
58 | ".simplebar-track .simplebar-scrollbar.simplebar-visible::before": {
59 | opacity: 1,
60 | },
61 | ".simplebar-horizontal": {
62 | height: "11px",
63 | },
64 | ".simplebar-content-wrapper": {
65 | overflow: "hidden",
66 | },
67 | })}
68 | />
69 |
81 | {children}
82 |
92 | Released under the MIT License Copyright © {new Date().getFullYear()}{" "}
93 | Adrien Denat
94 |
101 | Sponsored by{" "}
102 |
108 |
113 |
114 |
115 |
116 |
117 |
118 | )
119 | }
120 |
121 | Layout.propTypes = {
122 | children: PropTypes.node.isRequired,
123 | }
124 |
125 | export default Layout
126 |
--------------------------------------------------------------------------------
/packages/website/src/components/List.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 |
4 | const List = ({ children, ...otherProps }) => (
5 |
14 | )
15 |
16 | export default List
17 |
--------------------------------------------------------------------------------
/packages/website/src/components/Playground.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Select from 'react-select';
3 |
4 | const widthOptions = [
5 | { value: 'auto', label: 'auto' },
6 | { value: '100%', label: '100%' },
7 | { value: '100px', label: '100px' }
8 | ];
9 |
10 | const heightOptions = [
11 | { value: 'auto', label: 'auto' },
12 | { value: '100%', label: '100%' },
13 | { value: '100px', label: '100px' }
14 | ];
15 |
16 | const directionOptions = [
17 | { value: 'ltr', label: 'LTR' },
18 | { value: 'rtl', label: 'RTL' }
19 | ];
20 |
21 | export default class Playground extends React.PureComponent {
22 | state = {
23 | height: '100%',
24 | width: '100%',
25 | direction: 'ltr'
26 | };
27 |
28 | render() {
29 | const { width, height, direction } = this.props;
30 |
31 | return (
32 |
33 |
34 | {this.props.children(this.state)}
35 |
36 | {width && (
37 |
38 | Width:
39 |
41 | this.setState({
42 | width: option.value
43 | })
44 | }
45 | options={widthOptions}
46 | value={widthOptions.find(
47 | option => option.value === this.state.width
48 | )}
49 | />
50 |
51 | )}
52 | {height && (
53 |
54 | Height:
55 |
57 | this.setState({
58 | height: option.value
59 | })
60 | }
61 | options={heightOptions}
62 | value={heightOptions.find(
63 | option => option.value === this.state.height
64 | )}
65 | />
66 |
67 | )}
68 | {direction && (
69 |
70 | Direction:
71 |
73 | this.setState({
74 | direction: option.value
75 | })
76 | }
77 | options={directionOptions}
78 | value={directionOptions.find(
79 | option => option.value === this.state.direction
80 | )}
81 | />
82 |
83 | )}
84 |
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/packages/website/src/components/SEO.js:
--------------------------------------------------------------------------------
1 | /**
2 | * SEO component that queries for data with
3 | * Gatsby's useStaticQuery React hook
4 | *
5 | * See: https://www.gatsbyjs.org/docs/use-static-query/
6 | */
7 |
8 | import React from "react"
9 | import PropTypes from "prop-types"
10 | import Helmet from "react-helmet"
11 | import { useStaticQuery, graphql } from "gatsby"
12 |
13 | function SEO({ description, lang, meta, title }) {
14 | const { site } = useStaticQuery(
15 | graphql`
16 | query {
17 | site {
18 | siteMetadata {
19 | title
20 | description
21 | author
22 | }
23 | }
24 | }
25 | `
26 | )
27 |
28 | const metaDescription = description || site.siteMetadata.description
29 |
30 | return (
31 |
72 | )
73 | }
74 |
75 | SEO.defaultProps = {
76 | lang: `en`,
77 | meta: [],
78 | description: ``,
79 | }
80 |
81 | SEO.propTypes = {
82 | description: PropTypes.string,
83 | lang: PropTypes.string,
84 | meta: PropTypes.arrayOf(PropTypes.object),
85 | title: PropTypes.string.isRequired,
86 | }
87 |
88 | export default SEO
89 |
--------------------------------------------------------------------------------
/packages/website/src/demo.css:
--------------------------------------------------------------------------------
1 | /**
2 | * SimpleBar demo.
3 | * Author: Adrien Grsmto
4 | *
5 | * These styles are not required for SimpleBar
6 | * to function. They are used only for styling the demo.
7 | */
8 |
9 | /* General page styles
10 | *****************************************************************/
11 | section:after {
12 | content: "";
13 | display: table;
14 | clear: both;
15 | }
16 |
17 | section + section {
18 | margin-top: 40px;
19 | }
20 | .box {
21 | background: #666;
22 | color: #fff;
23 | }
24 | .col {
25 | float: left;
26 | width: calc(50% - 20px);
27 | }
28 | .col + .col {
29 | margin-left: 40px;
30 | }
31 | .btn {
32 | display: inline-block;
33 | background: #000;
34 | color: #fff;
35 | padding: 8px 12px;
36 | line-height: 1;
37 | text-decoration: none;
38 | -moz-border-radius: 5px;
39 | -webkit-border-radius: 5px;
40 | -o-border-radius: 5px;
41 | border-radius: 5px;
42 | }
43 | .btn:hover {
44 | background: #666;
45 | color: #fff;
46 | }
47 | .btn:visited {
48 | color: #fff;
49 | }
50 |
51 | /* Scrollable elements
52 | *****************************************************************/
53 | .demo1,
54 | .demo3 {
55 | margin: 10px 0;
56 | width: 100%;
57 | height: 300px;
58 | }
59 | .demo1 p {
60 | margin: 0;
61 | padding: 10px;
62 | min-width: 230px;
63 | }
64 | .demo1 p.odd:hover {
65 | background: #666;
66 | height: 100px;
67 | }
68 | .demo1 p.odd {
69 | background: #f0f0f0;
70 | }
71 | .demo1.width {
72 | width: 230px;
73 | }
74 | .demo1.height {
75 | height: 200px;
76 | }
77 | #demo1 p {
78 | text-align: right;
79 | padding: 0;
80 | }
81 | .demo1-internal {
82 | width: 50%;
83 | height: 300px;
84 | }
85 | .demo4 {
86 | background: grey;
87 | width: 100%;
88 | margin: 10px 0;
89 | padding: 10px;
90 | white-space: nowrap;
91 | overflow: auto;
92 | }
93 | .demo4 .box {
94 | display: inline-block;
95 | /* margin-right: 10px; */
96 | width: 100px;
97 | height: 150px;
98 | text-align: center;
99 | line-height: 150px;
100 | font-size: 24px;
101 | }
102 | .demo-raw {
103 | margin: 10px 0;
104 | width: 250px;
105 | height: 300px;
106 | overflow: auto;
107 | }
108 |
109 | .demo3 {
110 | height: auto;
111 | max-height: 300px;
112 | }
113 |
114 | .demo5 {
115 | height: 200px;
116 | height: 60px;
117 | direction: rtl;
118 | }
119 |
120 | .demo-both-axis {
121 | overflow: auto;
122 | width: 200px;
123 | height: 200px;
124 | }
125 |
126 | .demo-both-axis--padding {
127 | background: #2f2f2f;
128 | padding: 40px;
129 | }
130 |
131 | .demo-y-axis {
132 | overflow-y: auto;
133 | overflow-x: hidden;
134 | width: 200px;
135 | height: 200px;
136 | }
137 |
138 | .demo-y-axis.simplebar-dragging .simplebar-scrollbar:before {
139 | background-color: red;
140 | }
141 |
142 | .demo-both-axis .box,
143 | .demo-y-axis .box {
144 | width: 600px;
145 | height: 600px;
146 | }
147 |
148 | .height-100 {
149 | position: absolute;
150 | height: 100%;
151 | width: 100%;
152 | background: red;
153 | }
154 |
155 | .sticky {
156 | position: sticky;
157 | top: 0;
158 | background: red;
159 | margin: 0;
160 | }
161 |
162 | .playground {
163 | display: grid;
164 | grid-template-columns: repeat(2, 1fr);
165 | grid-gap: 10px;
166 | }
167 |
168 | .playground__content {
169 | grid-column-start: span 2;
170 | }
171 |
172 | .demo-flex {
173 | display: flex;
174 | width: 100%;
175 | }
176 |
177 | .demo-flex > div {
178 | height: 300px;
179 | }
180 |
181 | .demo-flex > div .content {
182 | height: 600px;
183 | }
184 |
185 | .demo-flex .left {
186 | width: 200px;
187 | background: #c5e0f7;
188 | }
189 |
190 | .demo-flex .center {
191 | flex: 1;
192 | }
193 |
194 | .demo-flex .right {
195 | width: 200px;
196 | transition: width 0.3s;
197 | background: #ffcfcf;
198 | }
199 |
200 | .demo-height-auto {
201 | width: 25vw;
202 | }
203 |
204 | .demo-height-auto .inner {
205 | margin-bottom: 2em;
206 | width: 25vw;
207 | }
208 |
--------------------------------------------------------------------------------
/packages/website/src/html.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import PropTypes from "prop-types"
3 |
4 | const simpleBarFallbackCss = `
5 | .simplebar-content-wrapper {
6 | overflow: auto;
7 | }
8 | `
9 |
10 | export default function HTML(props) {
11 | return (
12 |
13 |
14 |
15 |
16 |
20 | {props.headComponents}
21 |
22 |
23 | {props.preBodyComponents}
24 |
25 |
26 | This app works best with JavaScript enabled.
27 |
28 |
33 | {props.postBodyComponents}
34 |
35 |
36 | )
37 | }
38 |
39 | HTML.propTypes = {
40 | htmlAttributes: PropTypes.object,
41 | headComponents: PropTypes.array,
42 | bodyAttributes: PropTypes.object,
43 | preBodyComponents: PropTypes.array,
44 | body: PropTypes.string,
45 | postBodyComponents: PropTypes.array,
46 | }
47 |
--------------------------------------------------------------------------------
/packages/website/src/images/browserstack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Grsmto/simplebar/3885152e72af29662d613dca32f7b76d4120f184/packages/website/src/images/browserstack.png
--------------------------------------------------------------------------------
/packages/website/src/images/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/packages/website/src/images/users/storybook.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/website/src/images/users/twitch.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/website/src/images/users/zulip.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/website/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import Layout from "../components/Layout"
4 | import SEO from "../components/SEO"
5 |
6 | const NotFoundPage = () => (
7 |
8 |
9 | NOT FOUND
10 | You just hit a route that doesn't exist... the sadness.
11 |
12 | )
13 |
14 | export default NotFoundPage
15 |
--------------------------------------------------------------------------------
/packages/website/src/pages/examples.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx } from "theme-ui"
3 |
4 | import Layout from "../components/Layout"
5 | import SEO from "../components/SEO"
6 | import Demo from "../../../examples/Demo"
7 |
8 | import "../demo.css"
9 |
10 | const ExamplePage = () => {
11 | return (
12 |
13 |
14 |
20 |
25 | Simplebar examples
26 |
27 |
28 |
29 |
30 | )
31 | }
32 |
33 | export default ExamplePage
34 |
--------------------------------------------------------------------------------
/packages/website/src/pages/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { jsx, Styled } from "theme-ui"
3 | import { useEffect, useState } from "react"
4 | import { graphql, withPrefix } from "gatsby"
5 | import SimpleBar from "simplebar-react"
6 | import "simplebar-react/dist/simplebar.min.css"
7 | import { FiGithub } from "react-icons/fi"
8 | import { MdStar } from "react-icons/md"
9 |
10 | import Layout from "../components/Layout"
11 | import SEO from "../components/SEO"
12 | import List from "../components/List"
13 |
14 | import LogoUrl from "../images/logo.svg"
15 | import TwitchUrl from "../images/users/twitch.svg"
16 | import StorybookUrl from "../images/users/storybook.svg"
17 | import ZulipUrl from "../images/users/zulip.svg"
18 |
19 | const verticalCenter = {
20 | display: "inline-flex",
21 | alignItems: "center",
22 | }
23 |
24 | function useGitHubStars() {
25 | const [stars, setStars] = useState(null)
26 | useEffect(() => {
27 | fetch("https://api.github.com/repos/grsmto/simplebar")
28 | .then((result) => result.json())
29 | .then((response) => setStars(response.stargazers_count))
30 | }, [])
31 | return stars
32 | }
33 |
34 | const IndexPage = ({ data }) => {
35 | const {
36 | markdownRemark: { html },
37 | } = data
38 | const stars = useGitHubStars()
39 |
40 | return (
41 |
42 |
43 |
44 |
45 |
52 |
53 |
54 |
63 | Custom scrollbars made simple, lightweight, easy to use and
64 | cross-browser.
65 |
66 |
85 |
86 |
102 |
103 |
104 |
105 | Who is using it?
106 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | )
129 | }
130 |
131 | export default IndexPage
132 |
133 | export const pageQuery = graphql`
134 | {
135 | markdownRemark {
136 | html
137 | }
138 | }
139 | `
140 |
--------------------------------------------------------------------------------
/packages/website/src/theme.js:
--------------------------------------------------------------------------------
1 | import baseTheme from "@theme-ui/preset-base"
2 |
3 | const theme = {
4 | ...baseTheme,
5 | colors: {
6 | body: "#2e2e2e",
7 | background: "#fff",
8 | primary: "#DC8D37",
9 | },
10 | fonts: {
11 | body: '"Nunito", sans-serif',
12 | },
13 | fontWeights: {
14 | body: 500,
15 | bold: 700,
16 | heading: 800,
17 | },
18 | fontSizes: {
19 | 0: "1rem", // 10px
20 | 1: "1.2rem", // 12px
21 | 2: "1.4rem", // 14px
22 | 3: "1.6rem", // 16px
23 | 4: "1.9rem", // 19px
24 | 5: "2.3rem", // 23px
25 | 6: "2.8rem", // 28px
26 | 7: "3.8rem", // 38px
27 | 8: "4.8rem", // 48px
28 | 9: "7rem", // 70px
29 | 10: "9rem", // 90px
30 | },
31 | styles: {
32 | root: {
33 | h1: {
34 | display: "none",
35 | },
36 | h3: {
37 | mt: 4,
38 | },
39 | h4: {
40 | mt: 4,
41 | mb: 0,
42 | fontSize: 4,
43 | },
44 | "p:first-of-type": {
45 | mt: 0,
46 | },
47 | },
48 | },
49 | }
50 |
51 | theme.fontSizes.body = theme.fontSizes[3]
52 |
53 | export default theme
54 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | export const getExternals = (pkg) => (id) => {
2 | if (
3 | Object.keys(pkg.dependencies).find((dep) => id === dep) ||
4 | Object.keys(pkg.peerDependencies || {}).find((dep) => id === dep) ||
5 | id.match(/(lodash).+/) ||
6 | id.match(/(lodash-es).+/) ||
7 | id.match(/(core-js).+/) ||
8 | id.match(/(@babel).+/)
9 | ) {
10 | return true;
11 | }
12 |
13 | return false;
14 | };
15 |
16 | export const getBanner = (pkg) => ({
17 | banner: `
18 | ${pkg.name} - v${pkg.version}
19 | ${pkg.description}
20 | ${pkg.homepage}
21 |
22 | Made by ${pkg.author}
23 | Under ${pkg.license} License
24 | `,
25 | });
26 |
27 | export const babelConfig = {
28 | extensions: ['.js', '.ts'],
29 | exclude: ['**/node_modules/**'],
30 | babelHelpers: 'runtime',
31 | };
32 |
33 | export const tsConfig = {
34 | outDir: 'dist',
35 | tsconfig: '../../tsconfig.json',
36 | exclude: ['**/*.test.ts', '**/*.test.tsx', 'tests/*'],
37 | };
38 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["**/*.ts", "**/*.tsx", "**/*.js"],
3 | "compilerOptions": {
4 | "strict": true,
5 | "esModuleInterop": true,
6 | "skipLibCheck": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "outDir": "dist",
9 | "declaration": true,
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/web.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Grsmto/simplebar/3885152e72af29662d613dca32f7b76d4120f184/web.sketch
--------------------------------------------------------------------------------