├── .circleci └── config.yml ├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── pull-request-template.md ├── .gitignore ├── .npmrc ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .releaserc ├── Dockerfile.e2e ├── LICENSE.md ├── README.md ├── commitlint.config.js ├── cypress.json ├── cypress ├── integration │ ├── Arrows.test.js │ ├── ClickToChange.test.js │ ├── ControlledInput.test.js │ ├── Dots.test.js │ ├── Infinite.test.js │ ├── MoveSlides.test.js │ └── Thumbnails.test.js ├── plugins │ └── index.js └── support │ ├── commands.js │ └── index.js ├── dangerfile.js ├── docker-compose.e2e.yml ├── docs-www ├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── gatsby-config.js ├── gatsby-docs-kit.yml ├── package.json ├── src │ ├── globalReferences.js │ ├── layouts │ │ └── .gitkeep │ ├── pages │ │ ├── 404.js │ │ ├── landing.js │ │ ├── landing.module.scss │ │ └── variables.scss │ ├── static │ │ ├── favicon.png │ │ ├── mona.jpg │ │ ├── mona_thumbnail.jpg │ │ ├── scream.jpg │ │ ├── scream_thumbnail.jpg │ │ ├── starry-night.jpg │ │ └── starry-night_thumbnail.jpg │ ├── styles │ │ └── carousel.scss │ └── templates │ │ └── .gitkeep └── tools │ ├── deploy-github.sh │ ├── seed.sh │ └── seed │ └── example.md ├── docs ├── CHANGELOG.md ├── api │ ├── carousel.md │ ├── dots.md │ └── plugins.md ├── contributions-guide │ ├── decision-log.md │ ├── labels.md │ └── workflow.md ├── examples │ ├── animation.md │ ├── centered.md │ ├── clickToChange.md │ ├── controlled.md │ ├── customArrows.md │ ├── defaultArrows.md │ ├── dots.md │ ├── flexContainer.md │ ├── infinite.md │ ├── lazyload.md │ ├── multipleSlides.md │ ├── resizableCarousel.md │ ├── responsive.md │ ├── rtl.md │ ├── simpleUsage.md │ ├── slidesToScroll.md │ ├── swipingSlides.md │ └── thumbnails.md ├── gettingStarted.md ├── migrationGuide.md └── plugins │ └── plugins.md ├── license ├── blacklist.js ├── licenses.js └── whitelist.js ├── package.json ├── react-carousel ├── .babelrc ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── jest.config.js ├── jest.setup.js ├── package.json ├── src │ ├── components │ │ ├── Carousel.js │ │ ├── CarouselDots.js │ │ ├── CarouselSlide.js │ │ └── CarouselWrapper.js │ ├── constants │ │ ├── carouselStrategies.js │ │ ├── config.js │ │ ├── plugins.js │ │ └── pluginsOrder.js │ ├── hooks │ │ ├── useEventListener.js │ │ └── useOnResize.js │ ├── index.js │ ├── plugins │ │ ├── arrows.js │ │ ├── arrows.scss │ │ ├── autoplay.js │ │ ├── centered.js │ │ ├── clickToChange.js │ │ ├── fastSwipe.js │ │ ├── index.js │ │ ├── infinite.js │ │ ├── rtl.js │ │ ├── rtl.scss │ │ ├── slidesToScroll.js │ │ └── slidesToShow.js │ ├── state │ │ ├── atoms │ │ │ ├── carouselAtoms.js │ │ │ └── slideAtoms.js │ │ └── selectors │ │ │ └── carouselSelectors.js │ ├── styles │ │ ├── Arrows.scss │ │ ├── Carousel.scss │ │ ├── CarouselDots.scss │ │ └── CarouselItem.scss │ └── tools │ │ ├── carouselPluginResolver.js │ │ ├── clamp.js │ │ ├── getChildren.js │ │ └── simulateEvent.js ├── test │ ├── __mocks__ │ │ └── styleMock.js │ ├── carousel.test.js │ ├── plugins │ │ ├── arrows.test.js │ │ ├── autoplay.test.js │ │ ├── dots.test.js │ │ ├── infinite.test.js │ │ ├── rtl.test.js │ │ └── slidesPerScroll.test.js │ └── tools │ │ └── setupCarousel.js ├── tools │ ├── build.js │ └── chalkConfig.js └── webpack.config.prod.js ├── readme └── assets │ ├── carousel.gif │ ├── logo.gif │ └── thumbnails.gif ├── renovate.json ├── tools ├── deploy-gh-pages-pr.sh ├── deploy-gh-pages.sh ├── skip-all-e2e-tests.sh └── unskip-all-e2e-tests.sh └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | executors: 3 | node-executor: 4 | docker: 5 | - image: circleci/node:12.18.2 6 | working_directory: ~/app 7 | 8 | jobs: 9 | deploy_test_environment: 10 | docker: 11 | - image: circleci/node:12.18.2 12 | working_directory: ~/app 13 | steps: 14 | - checkout 15 | - run: 16 | name: publish gh pages to one of the test environments 17 | command: ./tools/deploy-gh-pages-pr.sh 18 | test: 19 | executor: node-executor 20 | steps: 21 | - checkout 22 | - run: yarn install --non-interactive 23 | - run: yarn danger ci --failOnErrors --id @brainhubeu/react-carousel 24 | - run: yarn lint 25 | - run: yarn test:unit:coverage 26 | - run: 27 | name: Generate coveralls config 28 | command: "echo repo_token: $COVERALLS_REPO_TOKEN > ./react-carousel/.coveralls.yml" 29 | - run: yarn build 30 | - persist_to_workspace: 31 | root: ~/app 32 | paths: 33 | - .git 34 | - node_modules 35 | - react-carousel/node_modules 36 | - react-carousel/lib 37 | - docs-www/node_modules 38 | - react-carousel/.coveralls.yml 39 | - react-carousel/coverage 40 | test-e2e: 41 | docker: 42 | - image: circleci/node:12.18.2 43 | working_directory: ~/app 44 | steps: 45 | - checkout 46 | - setup_remote_docker 47 | - run: 48 | name: Running E2E tests 49 | command: docker-compose -f ./docker-compose.e2e.yml up --build --exit-code-from e2e-test 50 | 51 | publish_package: 52 | executor: node-executor 53 | steps: 54 | - attach_workspace: 55 | at: ~/app 56 | - run: git checkout . 57 | - run: 58 | name: Upload coverage to coveralls 59 | command: | 60 | cd react-carousel 61 | cat ./coverage/lcov.info | ./node_modules/.bin/coveralls 62 | - run: 63 | name: configure GitHub user 64 | command: | 65 | git config --global user.email "robert@brainhub.pl" 66 | git config --global user.name "DevOps Brainhub" 67 | git remote -v 68 | git remote remove origin 69 | git remote add origin https://$GIT_TOKEN@github.com/brainhubeu/react-carousel 70 | git remote -v 71 | - run: git pull --no-edit origin $CIRCLE_BRANCH 72 | - run: git push origin $CIRCLE_BRANCH 73 | - run: 74 | name: npm publish 75 | command: npx semantic-release 76 | - run: sleep 10 77 | - run: git pull --no-edit origin $CIRCLE_BRANCH 78 | - run: git push origin $CIRCLE_BRANCH 79 | - run: 80 | name: publish gh pages 81 | command: ./tools/deploy-gh-pages.sh 82 | 83 | workflows: 84 | version: 2 85 | test_and_deploy: 86 | jobs: 87 | - test 88 | - test-e2e 89 | - deploy_test_environment: 90 | filters: 91 | branches: 92 | ignore: 93 | - master 94 | - rc 95 | - publish_package: 96 | requires: 97 | - test 98 | - test-e2e 99 | - deploy_test_environment 100 | filters: 101 | branches: 102 | only: 103 | - master 104 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | .editorconfig 4 | node_modules 5 | react-carousel/node_modules 6 | docs-www/node_modules 7 | *.DS_Store 8 | *.md 9 | *.log 10 | LICENSE.txt 11 | README.md 12 | screenshots 13 | coverage 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | docs-www/.cache 2 | docs-www/public 3 | react-carousel/coverage 4 | react-carousel/lib 5 | node_modules 6 | react-carousel/node_modules 7 | docs-www/node_modules 8 | 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "brainhub", 5 | "prettier" 6 | ], 7 | "plugins": [ 8 | "cypress", 9 | "prettier" 10 | ], 11 | "env": { 12 | "jest": true, 13 | "browser": true, 14 | "cypress/globals": true 15 | }, 16 | "settings": { 17 | "import/resolve": { 18 | "moduleDirectory": ["node_modules", "src"] 19 | } 20 | }, 21 | "rules": { 22 | "prettier/prettier": "error" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Environment** 24 | - please paste the result of `npx envinfo --system --binaries --browsers --npmPackages '@brainhubeu/*'` 25 | - desktop or mobile (which device & OS if mobile)? 26 | - Browser [e.g. Chrome, Safari] 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/pull-request-template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - [ ] an issue linked to the PR 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | 3 | # Logs 4 | logs 5 | *.log* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | reports 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # node-waf configuration 23 | .lock-wscript 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 30 | node_modules 31 | 32 | # IDEA/Webstorm project files 33 | .idea 34 | *.iml 35 | 36 | #VSCode metadata 37 | .vscode 38 | 39 | # Mac files 40 | .DS_Store 41 | 42 | # Debug files 43 | selenium-debug.log 44 | .npm.debug 45 | package-lock.json 46 | 47 | html-report/ 48 | lib/ 49 | test/cypress/screenshots/ 50 | test/cypress/videos/ 51 | docs-www/reduxcache* 52 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | docs-www/.cache 2 | docs-www/public 3 | react-carousel/coverage 4 | react-carousel/lib 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "all", 8 | "jsxBracketSameLine": false 9 | } 10 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "v1-legacy", 4 | { name: 'master', channel: latest }, 5 | { name: 'v2-beta', prerelease: true }, 6 | { name: 'rc', prerelease: true } 7 | ], 8 | "plugins": [ 9 | "@semantic-release/commit-analyzer", 10 | "@semantic-release/release-notes-generator", 11 | ["@semantic-release/changelog", { 12 | "changelogFile": "docs/CHANGELOG.md", 13 | }], 14 | ["@semantic-release/npm", { 15 | "pkgRoot": "react-carousel" 16 | }], 17 | ["@semantic-release/git", { 18 | "assets": ["react-carousel/package.json", "docs/CHANGELOG.md"], 19 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 20 | }], 21 | "@semantic-release/github" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /Dockerfile.e2e: -------------------------------------------------------------------------------- 1 | FROM cypress/included:4.9.0 2 | 3 | WORKDIR /e2e 4 | 5 | COPY ./package.json ./yarn.lock docs /e2e/ 6 | 7 | COPY . /e2e 8 | 9 | # limit output by setting CI environment variable 10 | # https://github.com/cypress-io/cypress/issues/1243 11 | ENV CI=true 12 | RUN yarn install --frozen-lockfile && \ 13 | npx cypress verify 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2020 [Brainhub](https://brainhub.eu/?utm_source=github) 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 | 5 | 6 |
7 | react-carousel 8 |

9 | 10 |

11 | A pure extendable React carousel, powered by Brainhub (craftsmen who ❤️ JS) 12 |

13 | 14 |

15 | 16 | Live code demo | 17 | v1 migration guide | 18 | Hire us 19 | 20 |

21 | 22 |
23 | 24 | [![CircleCI](https://circleci.com/gh/brainhubeu/react-carousel.svg?style=svg)](https://circleci.com/gh/brainhubeu/react-carousel) 25 | [![Last commit](https://img.shields.io/github/last-commit/brainhubeu/react-carousel.svg)](https://github.com/brainhubeu/react-carousel/commits/master) 26 | [![license](https://img.shields.io/npm/l/@brainhubeu/react-carousel.svg)](https://github.com/brainhubeu/react-carousel/blob/master/LICENSE.md) 27 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) 28 | [![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com/) 29 | 30 | [![Coveralls github](https://img.shields.io/coveralls/github/brainhubeu/react-carousel.svg)](https://coveralls.io/github/brainhubeu/react-carousel?branch=master) 31 | [![Downloads](https://img.shields.io/npm/dm/@brainhubeu/react-carousel?color=blue)](https://www.npmjs.com/package/@brainhubeu/react-carousel) 32 | [![Activity](https://img.shields.io/github/commit-activity/m/brainhubeu/react-carousel.svg)](https://github.com/brainhubeu/react-carousel/commits/master) 33 | [![Minified](https://img.shields.io/bundlephobia/min/@brainhubeu/react-carousel?label=minified)](https://www.npmjs.com/package/@brainhubeu/react-carousel) 34 | [![npm](https://img.shields.io/npm/v/@brainhubeu/react-carousel.svg)](https://www.npmjs.com/package/@brainhubeu/react-carousel) 35 | [![Contributors](https://img.shields.io/github/contributors/brainhubeu/react-carousel?color=blue)](https://github.com/brainhubeu/react-carousel/graphs/contributors) 36 |
37 | 38 | ## Table of Contents 39 | - 🔌 [Installation](#installation) 40 | - 🐥 [Usage](#usage) 41 | - 🔨 [Props](#props) 42 | - 🎠 [Carousel Props](#carousel-props) 43 | - 🐾 [Dots Props](#dots-props) 44 | - 😻 [Contributing](#contributing) 45 | - 💁 [Setting up local development](#setting-up-local-development-which-means-running-the-docsdemo-locally) 46 | - 🐞 [Tests](#tests) 47 | - 🏋️‍ [Workflow](#workflow) 48 | - 🏷 [Labels](#labels) 49 | - 📝 [Decision Log](#decision-log) 50 | 51 | ## Why? 52 | There are some great carousels (like slick) that do not have real React implementations. This library provides you with carousel that is not merely a wrapper for some jQuery solution, can be used as controlled or uncontrolled element (similar to [inputs](https://reactjs.org/docs/uncontrolled-components.html)), and has tons of useful features. 53 | 54 | ## Installation 55 | ### Basic 56 | ``` 57 | npm i @brainhubeu/react-carousel 58 | ``` 59 | 60 | ### Typescript 61 | ``` 62 | npm i @types/brainhubeu__react-carousel -D 63 | ``` 64 | 65 | ### SSR 66 | When using `@brainhubeu/react-carousel` with SSR (Server-side Rendering), we recommend [Next.js](https://github.com/zeit/next.js) as `@brainhubeu/react-carousel` currently doesn't work on the server side so it must be rendered on the client side (maybe we'll provide server-side working in the future). 67 | ```js 68 | import dynamic from 'next/dynamic'; 69 | 70 | const { default: Carousel, Dots } = dynamic( 71 | () => require('@brainhubeu/react-carousel'), 72 | { ssr: false }, 73 | ); 74 | ``` 75 | 76 | ## Usage 77 | By default, the component does not need anything except children to render a simple carousel. 78 | Remember that styles do not have to be imported every time you use carousel, you can do it once in an entry point of your bundle. 79 | ```javascript 80 | import React from 'react'; 81 | import Carousel from '@brainhubeu/react-carousel'; 82 | import '@brainhubeu/react-carousel/lib/style.css'; 83 | 84 | const MyCarousel = () => ( 85 | 86 | 87 | 88 | 89 | 90 | ); 91 | 92 | export default MyCarousel; 93 | ``` 94 | 95 | [![gif](readme/assets/carousel.gif)](https://brainhubeu.github.io/react-carousel/docs/examples/simpleUsage) 96 | 97 | ### Showing dots or thumbnails 98 | There is a separate Dots component that can be used to fully control navigation dots or add thumbnails. 99 | ```javascript 100 | import Carousel, { Dots } from '@brainhubeu/react-carousel'; 101 | import '@brainhubeu/react-carousel/lib/style.css'; import { useState } from 'react'; 102 | 103 | const MyCarouselWithDots = () => { 104 | const [value, setValue] = useState(0); 105 | 106 | const onChange = value => { 107 | setValue(value); 108 | } 109 | 110 | return ( 111 |
112 | 116 | 117 | ... 118 | 119 | 120 | ), 125 | ... 126 | (), 127 | ]} 128 | /> 129 |
130 | ); 131 | }; 132 | 133 | export default MyCarouselWithDots; 134 | ``` 135 | 136 | [![gif](readme/assets/thumbnails.gif)](https://brainhubeu.github.io/react-carousel/docs/examples/thumbnails) 137 | 138 | ## Props 139 | You can access a clickable demo with many examples and a [live code editor](https://brainhubeu.github.io/react-carousel/) by clicking on a Prop name. 140 | 141 | ### Carousel props 142 | 143 | | Prop | Type | Default | Description | 144 | | --- | --- | --- | --- | 145 | | [**value**](https://brainhubeu.github.io/react-carousel/docs/examples/controlled) | *Number* | `undefined` | Current slide's index (zero based, depends on the elements order) | 146 | | [**onChange**](https://brainhubeu.github.io/react-carousel/docs/examples/controlled) | *Function* | `undefined` | Handler triggered when current slide is about to change (e.g. on arrow click or on swipe) | 147 | | **slides** | *Array* | `undefined` | Alternative way to pass slides. This prop expects an array of JSX elements | 148 | | **itemWidth** | *Number* | `undefined` | Determines custom width for every slide in the carousel | 149 | | **offset** | *Number* | `0` | Padding between items | 150 | | [**animationSpeed**](https://brainhubeu.github.io/react-carousel/docs/examples/animation) | *Number* | `500` | Determines transition duration in milliseconds | 151 | | [**draggable**](https://brainhubeu.github.io/react-carousel/docs/examples/draggable) | *Boolean* | `true` | Makes it possible to drag to the next slide with mouse cursor | 152 | | [**breakpoints**](https://brainhubeu.github.io/react-carousel/docs/examples/responsive) | *Object* | `undefined` | All props can be set to different values on different screen resolutions | 153 | 154 | ### Plugins 155 | You can extend react-carousel default behavior by applying plugins shipped within carousel 156 | 157 | [**Plugins documentation**](https://brainhubeu.github.io/react-carousel/docs/plugins/plugins) 158 | 159 | ### Dots props 160 | 161 | | Prop | Type | Default | Description | 162 | | --- | --- | --- | --- | 163 | | [**value**](https://brainhubeu.github.io/react-carousel/docs/examples/dots) | *Number* | slide position in the slides Array | Current `Carousel` value | 164 | | [**onChange**](https://brainhubeu.github.io/react-carousel/docs/examples/dots) | *Function* | `undefined` | `onChange` callback (works the same way as `onChange` in `Carousel` component) | 165 | | [**number**](https://brainhubeu.github.io/react-carousel/docs/examples/dots) | *Number* | Amount of slides | Number of slides in the carousel you want to control | 166 | | [**thumbnails**](https://brainhubeu.github.io/react-carousel/docs/examples/thumbnails) | *Array of ReactElements* | `undefined` | Array of thumbnails to show. If not provided, default dots will be shown | 167 | | [**rtl**](https://brainhubeu.github.io/react-carousel/docs/examples/rtl) | *Boolean* | `false` | Indicating if the dots should have direction from Right to Left | 168 | 169 | ### Setting up local development which means running the docs/demo locally: 170 | - `git clone https://github.com/brainhubeu/react-carousel` 171 | - `cd react-carousel` 172 | - `yarn` 173 | - `yarn start-demo` 174 | - open http://localhost:8000/ 175 | 176 | ### Tests 177 | Each test command should be run from the root directory. 178 | 179 | #### Unit tests 180 | ``` 181 | yarn test:unit:coverage 182 | ``` 183 | 184 | #### E2E tests 185 | ``` 186 | yarn test:e2e 187 | ``` 188 | 189 | ### Workflow 190 | See [the Workflow subsection in our docs](https://brainhubeu.github.io/react-carousel/docs/contributions-guide/workflow) 191 | 192 | ### Labels 193 | See [the Labels subsection in our docs](https://brainhubeu.github.io/react-carousel/docs/contributions-guide/labels) 194 | 195 | ### Decision log 196 | See [the Decision log subsection in our docs](https://brainhubeu.github.io/react-carousel/docs/contributions-guide/decision-log) 197 | 198 | ## License 199 | 200 | react-carousel is copyright © 2018-2020 [Brainhub](https://brainhub.eu/?utm_source=github). It is free software and may be redistributed under the terms specified in the [license](LICENSE.md). 201 | 202 | ## About 203 | 204 | react-carousel is maintained by the Brainhub development team. It is funded by Brainhub and the names and logos for Brainhub are trademarks of Brainhub Sp. z o.o.. You can check other open-source projects supported/developed by our teammates [here](https://github.com/brainhubeu). 205 | 206 | [![Brainhub](https://brainhub.eu/brainhub.svg)](https://brainhub.eu/?utm_source=github) 207 | 208 | We love open-source JavaScript software! See our other projects or hire us to build your next web, desktop and mobile application with JavaScript. 209 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:8000" 3 | } 4 | -------------------------------------------------------------------------------- /cypress/integration/Arrows.test.js: -------------------------------------------------------------------------------- 1 | describe('Arrows', () => { 2 | beforeEach(() => { 3 | cy.visit('/docs/examples/controlled/'); 4 | 5 | cy.get('.BrainhubCarouselItem--active') 6 | .children('img') 7 | .should('have.attr', 'src') 8 | .and('contain', 'mona'); 9 | }); 10 | 11 | it('changes slide when clicks on the arrow', () => { 12 | cy.get('.BrainhubCarousel__arrowRight').trigger('click'); 13 | 14 | cy.wait(1); 15 | 16 | cy.get('.BrainhubCarouselItem--active') 17 | .children('img') 18 | .should('have.attr', 'src') 19 | .and('contain', 'scream'); 20 | 21 | cy.get('.BrainhubCarousel__arrowLeft').trigger('click'); 22 | 23 | cy.wait(1); 24 | 25 | cy.get('.BrainhubCarouselItem--active') 26 | .children('img') 27 | .should('have.attr', 'src') 28 | .and('contain', 'mona'); 29 | }); 30 | 31 | it(`doesn't allow to click on the arrow if there is no more slides`, () => { 32 | for (let i = 0; i < 2; i++) { 33 | cy.get('.BrainhubCarousel__arrowRight').trigger('click'); 34 | cy.wait(1); 35 | } 36 | 37 | cy.get('.BrainhubCarouselItem--active') 38 | .children('img') 39 | .should('have.attr', 'src') 40 | .and('contain', 'starry-night'); 41 | 42 | cy.get('.BrainhubCarousel__arrowRight').should('be.disabled'); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /cypress/integration/ClickToChange.test.js: -------------------------------------------------------------------------------- 1 | // TODO find a better way 2 | Cypress.on('uncaught:exception', (error) => { 3 | console.error('an uncaught eaten exception', error); 4 | return false; 5 | }); 6 | 7 | describe('Click to change', () => { 8 | beforeEach(() => { 9 | cy.visit('/docs/examples/clickToChange/'); 10 | 11 | cy.get('.BrainhubCarouselItem--active') 12 | .children('img') 13 | .should('have.attr', 'src') 14 | .and('contain', 'mona'); 15 | }); 16 | 17 | it('allows to change slide when clicks on the next slide', () => { 18 | cy.get('.BrainhubCarouselItem') 19 | .eq(1) 20 | .trigger('mousedown') 21 | .wait(10) 22 | .trigger('mouseup', { force: true }); 23 | 24 | cy.get('.BrainhubCarouselItem--active') 25 | .children('img') 26 | .should('have.attr', 'src') 27 | .and('contain', 'scream'); 28 | }); 29 | 30 | it('allows to change slide when clicks on the previous slide', () => { 31 | cy.get('.BrainhubCarouselItem') 32 | .eq(1) 33 | .trigger('mousedown') 34 | .wait(10) 35 | .trigger('mouseup', { force: true }); 36 | 37 | cy.get('.BrainhubCarouselItem') 38 | .eq(0) 39 | .trigger('mousedown') 40 | .wait(10) 41 | .trigger('mouseup', { force: true }); 42 | 43 | cy.get('.BrainhubCarouselItem--active') 44 | .children('img') 45 | .should('have.attr', 'src') 46 | .and('contain', 'mona'); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /cypress/integration/ControlledInput.test.js: -------------------------------------------------------------------------------- 1 | describe('Controlled input', () => { 2 | beforeEach(() => { 3 | cy.visit('/docs/examples/controlled/'); 4 | 5 | cy.get('.BrainhubCarouselItem--active') 6 | .children('img') 7 | .should('have.attr', 'src') 8 | .and('contain', 'mona'); 9 | }); 10 | 11 | it('goes to the slide index provided in input', () => { 12 | cy.get('input').type('2'); 13 | 14 | cy.get('.BrainhubCarouselItem--active') 15 | .children('img') 16 | .should('have.attr', 'src') 17 | .and('contain', 'starry-night'); 18 | }); 19 | 20 | it('stays at the last slide if the value in input is greater than number of slides', () => { 21 | cy.get('input').type('50'); 22 | 23 | cy.get('.BrainhubCarouselItem--active') 24 | .children('img') 25 | .should('have.attr', 'src') 26 | .and('contain', 'starry-night'); 27 | }); 28 | 29 | it('stays at the first slide if the value in input is less than 0', () => { 30 | for (let i = 0; i < 5; i++) { 31 | cy.get('input:first').type('{downArrow}'); 32 | } 33 | 34 | cy.get('.BrainhubCarouselItem--active') 35 | .children('img') 36 | .should('have.attr', 'src') 37 | .and('contain', 'mona'); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /cypress/integration/Dots.test.js: -------------------------------------------------------------------------------- 1 | describe('Dots', () => { 2 | beforeEach(() => { 3 | cy.visit('/docs/examples/dots'); 4 | 5 | cy.get('.BrainhubCarouselItem--active') 6 | .children('img') 7 | .should('have.attr', 'src') 8 | .and('contain', 'mona'); 9 | }); 10 | 11 | it('changes slide on dot click', () => { 12 | cy.get('.BrainhubCarousel__dot').eq(2).click(); 13 | 14 | cy.get('.BrainhubCarouselItem--active') 15 | .children('img') 16 | .should('have.attr', 'src') 17 | .and('contain', 'starry-night'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /cypress/integration/Infinite.test.js: -------------------------------------------------------------------------------- 1 | describe('Infinite', () => { 2 | beforeEach(() => { 3 | cy.visit('/docs/examples/infinite'); 4 | 5 | cy.get('.BrainhubCarouselItem--active') 6 | .children('img') 7 | .should('have.attr', 'src') 8 | .and('contain', 'mona'); 9 | }); 10 | 11 | it('repeats slides when swipes right', () => { 12 | for (let i = 0; i < 17; i++) { 13 | cy.get('.BrainhubCarousel__arrowRight').trigger('click'); 14 | 15 | cy.wait(5); 16 | } 17 | 18 | cy.wait(20); 19 | 20 | cy.get('.BrainhubCarouselItem--active') 21 | .children('img') 22 | 23 | .should('have.attr', 'src') 24 | .and('contain', 'starry-night'); 25 | }); 26 | 27 | it('repeats slides when swipes left', () => { 28 | for (let i = 0; i < 17; i++) { 29 | cy.get('.BrainhubCarousel__arrowLeft').trigger('click'); 30 | 31 | cy.wait(5); 32 | } 33 | 34 | cy.wait(20); 35 | 36 | cy.get('.BrainhubCarouselItem--active') 37 | .children('img') 38 | 39 | .should('have.attr', 'src') 40 | .and('contain', 'scream'); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /cypress/integration/MoveSlides.test.js: -------------------------------------------------------------------------------- 1 | describe('Move slides', () => { 2 | beforeEach(() => { 3 | cy.visit('/docs/examples/simpleUsage'); 4 | 5 | cy.get('.BrainhubCarouselItem--active') 6 | .children('img') 7 | .should('have.attr', 'src') 8 | .and('contain', 'mona'); 9 | }); 10 | 11 | it('moves slide to the right if the next slide exists', () => { 12 | cy.get('.BrainhubCarouselItem--active') 13 | .trigger('mousedown') 14 | .trigger('mousemove', { pageX: 100 }) 15 | .wait(10) 16 | .trigger('mouseup', { force: true }); 17 | 18 | cy.get('.BrainhubCarouselItem--active') 19 | .children('img') 20 | .should('have.attr', 'src') 21 | .and('contain', 'scream'); 22 | }); 23 | 24 | it(`stays at the same slide if the previous one doesn't exist`, () => { 25 | cy.get('.BrainhubCarouselItem--active') 26 | .trigger('mousedown') 27 | .trigger('mousemove', { pageX: 990 }) 28 | .trigger('mouseup', { force: true }); 29 | 30 | cy.get('.BrainhubCarouselItem--active') 31 | .children('img') 32 | .should('have.attr', 'src') 33 | .and('contain', 'mona'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /cypress/integration/Thumbnails.test.js: -------------------------------------------------------------------------------- 1 | describe('Thumbnails', () => { 2 | beforeEach(() => { 3 | cy.visit('/docs/examples/thumbnails'); 4 | 5 | cy.get('.BrainhubCarouselItem--active') 6 | .children('img') 7 | .should('have.attr', 'src') 8 | .and('contain', 'mona'); 9 | }); 10 | 11 | it('changes slide on thumbnail click', () => { 12 | cy.get('.BrainhubCarousel__thumbnail').eq(2).click(); 13 | 14 | cy.get('.BrainhubCarouselItem--active') 15 | .children('img') 16 | .should('have.attr', 'src') 17 | .and('contain', 'starry-night'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | // / 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | module.exports = () => { 19 | // `on` is used to hook into various events Cypress emits 20 | // `config` is the resolved Cypress config 21 | }; 22 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /dangerfile.js: -------------------------------------------------------------------------------- 1 | const licenseAuditor = require('@brainhubeu/license-auditor'); 2 | 3 | const whitelist = require('./license/whitelist'); 4 | const blacklist = require('./license/blacklist'); 5 | 6 | licenseAuditor({ 7 | whitelistedLicenses: whitelist, 8 | blacklistedLicenses: blacklist, 9 | projectPath: `.`, 10 | ciManager: { 11 | warn, 12 | fail, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /docker-compose.e2e.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | e2e-test: 4 | build: 5 | context: . 6 | dockerfile: ./Dockerfile.e2e 7 | volumes: 8 | - ${SHARED_PATH:-.}/test-results:/e2e/test-results 9 | entrypoint: yarn test:e2e 10 | -------------------------------------------------------------------------------- /docs-www/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "loose": true, 7 | "modules": false, 8 | "shippedProposals": true, 9 | "useBuiltIns": "usage" 10 | } 11 | ], 12 | "@babel/react" 13 | ], 14 | "plugins": [ 15 | "@babel/plugin-proposal-class-properties" 16 | ], 17 | "env": { 18 | "production": { 19 | "plugins": [ 20 | "@babel/plugin-transform-react-constant-elements", 21 | "transform-react-remove-prop-types" 22 | ] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /docs-www/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log* 4 | 5 | # node-waf configuration 6 | .lock-wscript 7 | 8 | # Dependency directory 9 | node_modules 10 | 11 | # IDEA/Webstorm project files 12 | .idea 13 | *.iml 14 | 15 | #VSCode metadata 16 | .vscode 17 | 18 | # Mac files 19 | .DS_Store 20 | 21 | # Gatsby 22 | .cache 23 | public 24 | -------------------------------------------------------------------------------- /docs-www/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2020 Brainhub 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 | -------------------------------------------------------------------------------- /docs-www/README.md: -------------------------------------------------------------------------------- 1 | # Gatsby Docs Kit Starter 2 | The default Brainhub static docs starter. 3 | 4 | ## Try it out 5 | 6 | Ensure you have the latest version of [Node](https://nodejs.org/en/download/) installed. We also recommend you install [Yarn](https://yarnpkg.com/en/docs/install) as well. 7 | Then run: 8 | 9 | ```bash 10 | yarn install 11 | ``` 12 | 13 | Seed documentation (if you don't have any yet): 14 | 15 | ```bash 16 | yarn seed 17 | ``` 18 | 19 | Run the local webserver via `yarn develop`; 20 | 21 | The example site is available at http://localhost:8000. You should see the example site loaded in your web browser. 22 | Also visit http://localhost:8000/___graphql to explore your site's GraphiQL data and schema. 23 | 24 | Then go to `../docs` to edit and write awesome docs!. 25 | 26 | ## Deploy 27 | 28 | Ready to go? Want to deploy documentation to github pages? Run: 29 | 30 | ```bash 31 | yarn deploy:gh 32 | ``` -------------------------------------------------------------------------------- /docs-www/gatsby-config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const pluginConfigFactory = require('@brainhubeu/gatsby-docs-kit/plugins'); 4 | const _ = require('lodash'); 5 | 6 | const url = process.env.CIRCLE_PULL_REQUEST; 7 | const branch = process.env.CIRCLE_BRANCH; 8 | const githubUrl = 'https://github.com/brainhubeu/react-carousel'; 9 | const getUrl = (url) => (url ? `★☂☀${_.last(url.split('/'))}♞♜♖` : 'local'); 10 | 11 | const postfix = branch === 'master' ? 'master' : getUrl(url); 12 | 13 | module.exports = { 14 | siteMetadata: { 15 | title: `React-carousel ${postfix} built on ${new Date()}`, 16 | description: 'Feature-rich, react-way react component that does not suck', 17 | image: 18 | 'https://cdn-images-1.medium.com/max/1200/1*CLUFZFaXF6NG27NA3d_JkQ.jpeg', 19 | url: url || githubUrl, 20 | type: 'article', 21 | siteName: 'React-carousel', 22 | githubUrl: url || githubUrl, 23 | }, 24 | 25 | // URL prefix on production environment. For more info see https://www.gatsbyjs.org/docs/path-prefix/ 26 | pathPrefix: process.env.PATH_PREFIX || null, 27 | 28 | plugins: [ 29 | ...pluginConfigFactory({ 30 | config: `${__dirname}/gatsby-docs-kit.yml`, 31 | resources: path.resolve(__dirname, '../docs'), 32 | }), 33 | { 34 | resolve: `gatsby-plugin-google-analytics`, 35 | options: { 36 | trackingId: 'UA-62818184-6', 37 | head: false, 38 | anonymize: true, 39 | respectDNT: true, 40 | pageTransitionDelay: 0, 41 | cookieDomain: 'brainhubeu.github.io', 42 | }, 43 | }, 44 | ], 45 | }; 46 | -------------------------------------------------------------------------------- /docs-www/gatsby-docs-kit.yml: -------------------------------------------------------------------------------- 1 | - title: Home 2 | dir: ./src 3 | url: / 4 | file: pages/landing.js 5 | 6 | - title: "DOCS__BUILD_INFO__" 7 | dir: ../docs 8 | url: docs 9 | sidemenu: 10 | - title: Getting started 11 | file: gettingStarted.md 12 | - title: Migration guide 13 | file: migrationGuide.md 14 | - title: Api 15 | dir: api 16 | items: 17 | - title: Carousel props 18 | file: carousel.md 19 | - title: Dots props 20 | file: dots.md 21 | - title: Plugins 22 | dir: plugins 23 | items: 24 | - title: React-carousel plugins 25 | file: plugins.md 26 | - title: Examples 27 | dir: examples 28 | items: 29 | - title: Simple Usage 30 | file: simpleUsage.md 31 | - title: Controlled component 32 | file: controlled.md 33 | - title: Multiple slides to show 34 | file: multipleSlides.md 35 | - title: Click to change 36 | file: clickToChange.md 37 | - title: Default arrows 38 | file: defaultArrows.md 39 | - title: Custom arrows 40 | file: customArrows.md 41 | - title: Infinite 42 | file: infinite.md 43 | - title: RTL layout 44 | file: rtl.md 45 | - title: Centered 46 | file: centered.md 47 | - title: Slides to scroll 48 | file: slidesToScroll.md 49 | - title: Swiping slides 50 | file: swipingSlides.md 51 | - title: Responsive 52 | file: responsive.md 53 | - title: Autoplay & animation speed 54 | file: animation.md 55 | - title: Dots 56 | file: dots.md 57 | - title: Thumbnails 58 | file: thumbnails.md 59 | - title: Resizable carousel 60 | file: resizableCarousel.md 61 | - title: Flex Container 62 | file: flexContainer.md 63 | - title: Contributions Guide 64 | dir: contributions-guide 65 | items: 66 | - title: Workflow 67 | file: workflow.md 68 | - title: Decision log 69 | file: decision-log.md 70 | - title: Labels 71 | file: labels.md 72 | -------------------------------------------------------------------------------- /docs-www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-carousel-docs", 3 | "description": "Gatsby-Docs-Kit starter repository", 4 | "version": "0.0.52", 5 | "author": "Brainhub", 6 | "license": "MIT", 7 | "scripts": { 8 | "build": "rm -rf .cache && rm -rf public && gatsby build --prefix-paths", 9 | "develop": "rm -rf .cache && NODE_ENV=development gatsby develop", 10 | "seed": "bash tools/seed.sh", 11 | "serve": "export RC_ENV=__RC_ENV__; if [[ \"$RC_ENV\" == 'development' ]]; then yarn develop; else gatsby serve; fi", 12 | "lint": "eslint --ext .jsx,.js .", 13 | "lint:autofix": "eslint --ext .jsx,.js . --fix" 14 | }, 15 | "dependencies": { 16 | "@brainhubeu/gatsby-docs-kit": "3.0.8", 17 | "gatsby": "^2.24.2", 18 | "gatsby-link": "2.3.1", 19 | "lodash": "^4.17.21", 20 | "react-fa": "^5.0.0" 21 | }, 22 | "devDependencies": { 23 | "@babel/plugin-proposal-class-properties": "^7.10.4", 24 | "@babel/plugin-transform-react-constant-elements": "^7.10.4", 25 | "@babel/polyfill": "^7.10.4", 26 | "@babel/preset-env": "^7.10.4", 27 | "@babel/preset-react": "^7.10.4", 28 | "@babel/preset-stage-1": "^7.8.3", 29 | "@babel/register": "^7.10.4", 30 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24", 31 | "gatsby-plugin-google-analytics": "2.2.2", 32 | "gh-pages": "^3.1.0", 33 | "react": "^16.13.1", 34 | "react-dom": "^16.13.1" 35 | }, 36 | "peerDependencies": { 37 | "@babel/core": "^7.10.4" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs-www/src/globalReferences.js: -------------------------------------------------------------------------------- 1 | import './styles/carousel.scss'; 2 | import Icon from 'react-fa'; 3 | 4 | import imageOne from './static/mona.jpg'; 5 | import imageTwo from './static/scream.jpg'; 6 | import imageThree from './static/starry-night.jpg'; 7 | import thumbnailOne from './static/mona_thumbnail.jpg'; 8 | import thumbnailTwo from './static/scream_thumbnail.jpg'; 9 | import thumbnailThree from './static/starry-night_thumbnail.jpg'; 10 | 11 | const { 12 | default: Carousel, 13 | Dots, 14 | slidesToShowPlugin, 15 | infinitePlugin, 16 | clickToChangePlugin, 17 | autoplayPlugin, 18 | rtlPlugin, 19 | centeredPlugin, 20 | slidesToScrollPlugin, 21 | arrowsPlugin, 22 | fastSwipePlugin, 23 | } = (() => { 24 | if (!global.window) { 25 | global.window = {}; 26 | } 27 | console.log('connecting with local react-carousel source code'); 28 | return require('../../react-carousel/src'); 29 | })(); 30 | 31 | export { 32 | Carousel, 33 | Dots, 34 | slidesToShowPlugin, 35 | infinitePlugin, 36 | clickToChangePlugin, 37 | autoplayPlugin, 38 | rtlPlugin, 39 | centeredPlugin, 40 | slidesToScrollPlugin, 41 | arrowsPlugin, 42 | fastSwipePlugin, 43 | Icon, 44 | imageOne, 45 | imageTwo, 46 | imageThree, 47 | thumbnailOne, 48 | thumbnailTwo, 49 | thumbnailThree, 50 | }; 51 | -------------------------------------------------------------------------------- /docs-www/src/layouts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/docs-www/src/layouts/.gitkeep -------------------------------------------------------------------------------- /docs-www/src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NotFoundPage = () => ( 4 |
5 |

NOT FOUND

6 |

You just hit a route that doesn't exist... the sadness.

7 |
8 | ); 9 | 10 | export default NotFoundPage; 11 | -------------------------------------------------------------------------------- /docs-www/src/pages/landing.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'gatsby-link'; 3 | 4 | import styles from './landing.module.scss'; 5 | 6 | const LandingPage = () => ( 7 |
8 |
9 |

React-carousel

10 |

11 | A pure extendable React carousel, powered by{' '} 12 | Brainhub (craftsmen who ❤️ JS) 13 |

14 |
15 | 16 | Get started! 17 | 18 |
19 |
20 |
21 | ); 22 | 23 | export default LandingPage; 24 | -------------------------------------------------------------------------------- /docs-www/src/pages/landing.module.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | 3 | .landing { 4 | text-align: center; 5 | display: flex; 6 | align-items: center; 7 | justify-content: center; 8 | min-height: calc(100vh - 2*#{$mainNavHeight} - 64px); 9 | } 10 | 11 | .landing__header { 12 | text-transform: uppercase; 13 | font-size: 3rem; 14 | letter-spacing: 2px; 15 | margin-bottom: 1rem; 16 | margin-top: 0; 17 | } 18 | 19 | .btn__wrapper { 20 | display: flex; 21 | justify-content: center; 22 | } 23 | 24 | .landing__btn { 25 | display: inline-block; 26 | background-color: $colorBHBg; 27 | text-decoration: none; 28 | color: $white; 29 | padding: 1rem 2rem; 30 | margin: 2rem; 31 | position: relative; 32 | overflow: hidden; 33 | 34 | span { 35 | position: relative; 36 | z-index: 1; 37 | } 38 | 39 | &:after, 40 | &:before { 41 | content: ''; 42 | position: absolute; 43 | top: 0; 44 | bottom: 0; 45 | width: 50%; 46 | background-color: $colorBHMain; 47 | transition: width .3s ease-out; 48 | } 49 | 50 | &:after { 51 | left: 0; 52 | } 53 | 54 | &:before { 55 | right: 0; 56 | } 57 | 58 | &:hover { 59 | color: $white; 60 | 61 | &:after, 62 | &:before { 63 | width: 0; 64 | } 65 | } 66 | } 67 | 68 | .landing__btn--alt { 69 | composes: landing__btn; 70 | background-color: transparent; 71 | color: rgba($colorBHBg, .7); 72 | transition: color .3s; 73 | 74 | &:after, 75 | &:before { 76 | background-color: transparent; 77 | border-style: solid; 78 | border-color: $colorBHMain; 79 | } 80 | 81 | &:after { 82 | border-width: 1px 0 1px 1px; 83 | } 84 | 85 | &:before { 86 | border-width: 1px 1px 1px 0; 87 | } 88 | 89 | &:hover { 90 | color: $colorBHMain; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /docs-www/src/pages/variables.scss: -------------------------------------------------------------------------------- 1 | $colorBHBg: #150940; 2 | $colorBHMain: #7b59ff; 3 | $white: #ffffff; 4 | 5 | $mainNavHeight: 80px; 6 | -------------------------------------------------------------------------------- /docs-www/src/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/docs-www/src/static/favicon.png -------------------------------------------------------------------------------- /docs-www/src/static/mona.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/docs-www/src/static/mona.jpg -------------------------------------------------------------------------------- /docs-www/src/static/mona_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/docs-www/src/static/mona_thumbnail.jpg -------------------------------------------------------------------------------- /docs-www/src/static/scream.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/docs-www/src/static/scream.jpg -------------------------------------------------------------------------------- /docs-www/src/static/scream_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/docs-www/src/static/scream_thumbnail.jpg -------------------------------------------------------------------------------- /docs-www/src/static/starry-night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/docs-www/src/static/starry-night.jpg -------------------------------------------------------------------------------- /docs-www/src/static/starry-night_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/docs-www/src/static/starry-night_thumbnail.jpg -------------------------------------------------------------------------------- /docs-www/src/styles/carousel.scss: -------------------------------------------------------------------------------- 1 | h2 { 2 | text-align: center; 3 | } -------------------------------------------------------------------------------- /docs-www/src/templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/docs-www/src/templates/.gitkeep -------------------------------------------------------------------------------- /docs-www/tools/deploy-github.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | GITHUB_REPO_NAME=$(basename -s .git `git config --get remote.origin.url`); 4 | 5 | PATH_PREFIX="/$GITHUB_REPO_NAME" npm run build 6 | 7 | # deploy to github pags 8 | node ./node_modules/.bin/gh-pages -d public 9 | -------------------------------------------------------------------------------- /docs-www/tools/seed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CONFIG_FILE="gatsby-docs-kit.yml" 4 | DIR_NAME="docs" 5 | 6 | # Create docs directory 7 | if [ -d "../${DIR_NAME}" ]; then 8 | echo '"docs" directory already exists. Can not seed it. Remove it and try again.' 9 | exit 1; 10 | fi 11 | 12 | mkdir -p "../${DIR_NAME}" 13 | 14 | # Create gastsby docs kit config file if do not exists 15 | if [ ! -f "./${CONFIG_FILE}" ]; then 16 | echo "" > $CONFIG_FILE; 17 | fi 18 | 19 | # Create example Mk 20 | cp ./tools/seed/example.md ../${DIR_NAME}/example.md 21 | 22 | # Seeding gastsby docs kit config file 23 | echo "- title: Home 24 | dir: ../docs 25 | url: docs 26 | file: example.md" >> $CONFIG_FILE; 27 | 28 | # Done 29 | echo "Done!" -------------------------------------------------------------------------------- /docs-www/tools/seed/example.md: -------------------------------------------------------------------------------- 1 | # CLI Commands 2 | 3 | ### Running an app 4 | 1. running an application on locally 5 | ```bash 6 | npm run develop # or yarn develop 7 | ``` 8 | 9 | Gatsby will start a hot-reloading development environment accessible at [http://localhost:8000](http://localhost:8000) 10 | 11 | 2. preparing optimized production build 12 | ```bash 13 | npm run build 14 | ``` 15 | 16 | 3. serving locally production build 17 | ```bash 18 | npm run serve 19 | ``` 20 | 21 | > Note that your site by default will be available on [http://localhost:9000](http://localhost:9000) 22 | 23 | 4. deploying to github pages 24 | ```bash 25 | npm run deploy:gh 26 | ``` 27 | 28 | > See more [HERE](../getting-started/publishing.md) 29 | 30 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.0.4](https://github.com/brainhubeu/react-carousel/compare/v2.0.3...v2.0.4) (2021-05-26) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * autoplay call onChange ([#694](https://github.com/brainhubeu/react-carousel/issues/694)) ([#713](https://github.com/brainhubeu/react-carousel/issues/713)) ([97ab35f](https://github.com/brainhubeu/react-carousel/commit/97ab35f00c8dbc37ac202d7957575039bc0291e1)) 7 | 8 | ## [2.0.3](https://github.com/brainhubeu/react-carousel/compare/v2.0.2...v2.0.3) (2021-04-12) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * production builds using webpack ([0cb444b](https://github.com/brainhubeu/react-carousel/commit/0cb444b777558494c5790ade0dface85e66bacbe)) 14 | 15 | ## [2.0.2](https://github.com/brainhubeu/react-carousel/compare/v2.0.1...v2.0.2) (2020-11-19) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * child re render ([b427cda](https://github.com/brainhubeu/react-carousel/commit/b427cda8d41e36045aaad92abbaa0f747480d110)) 21 | 22 | ## [2.0.1](https://github.com/brainhubeu/react-carousel/compare/v2.0.0...v2.0.1) (2020-07-29) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * temporarily disable creating custom plugins functionality ([971a5ac](https://github.com/brainhubeu/react-carousel/commit/971a5ac3e3c50b920f61443bea368e1a4b89d959)) 28 | 29 | # [2.0.0](https://github.com/brainhubeu/react-carousel/compare/v1.0.1...v2.0.0) (2020-07-27) 30 | 31 | 32 | * Carousel 2.0 introduce plugins (#622) ([f4774f1](https://github.com/brainhubeu/react-carousel/commit/f4774f14a7c746ab64afc942a9274bbd774010c7)), closes [#622](https://github.com/brainhubeu/react-carousel/issues/622) [#451](https://github.com/brainhubeu/react-carousel/issues/451) [#584](https://github.com/brainhubeu/react-carousel/issues/584) [#613](https://github.com/brainhubeu/react-carousel/issues/613) [#614](https://github.com/brainhubeu/react-carousel/issues/614) [#394](https://github.com/brainhubeu/react-carousel/issues/394) [#394](https://github.com/brainhubeu/react-carousel/issues/394) [#394](https://github.com/brainhubeu/react-carousel/issues/394) [#618](https://github.com/brainhubeu/react-carousel/issues/618) [#394](https://github.com/brainhubeu/react-carousel/issues/394) [#618](https://github.com/brainhubeu/react-carousel/issues/618) 33 | 34 | 35 | ### Bug Fixes 36 | 37 | * Setting offset breaks "centered" ([#394](https://github.com/brainhubeu/react-carousel/issues/394)) ([5f69446](https://github.com/brainhubeu/react-carousel/commit/5f694466442587eb09ca1e17ba61406bb77e8960)) 38 | 39 | # [2.0.0-v2-beta.1](https://github.com/brainhubeu/react-carousel/compare/v1.0.1...v2.0.0-v2-beta.1) (2020-07-22) 40 | 41 | ### Bug Fixes 42 | 43 | * Adding 'rtl' to breakpoints prop-type shape ([6de56a9](https://github.com/brainhubeu/react-carousel/commit/6de56a90d7101f222c8c90bd2835b12b12f05832)) 44 | * carousel breaks when one of the carousel children is an array of nodes ([88a6e0f](https://github.com/brainhubeu/react-carousel/commit/88a6e0f8d61f25c61f5a43c2fe8ac22124e11817)) 45 | * pass null instead of boolean to drag event listener if disabled ([2a884bf](https://github.com/brainhubeu/react-carousel/commit/2a884bf1b7165ad53e8a1f678e1931982c44482f)) 46 | * pkgRoot path ([1ce7ffd](https://github.com/brainhubeu/react-carousel/commit/1ce7ffd0360a942e98ed6a57532b777dadfe808f)) 47 | 48 | # [2.0.0-rc.3](https://github.com/brainhubeu/react-carousel/compare/v2.0.0-rc.2...v2.0.0-rc.3) (2020-07-27) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * publish package with CHANGELOG.md LICENSE.md and README.md ([29f2016](https://github.com/brainhubeu/react-carousel/commit/29f201661bc9f2f20453387179b51caeea81dc5a)) 54 | 55 | # [2.0.0-rc.2](https://github.com/brainhubeu/react-carousel/compare/v2.0.0-rc.1...v2.0.0-rc.2) (2020-07-24) 56 | 57 | 58 | ### Bug Fixes 59 | 60 | * setting offset breaks "centered" plugin [#394](https://github.com/brainhubeu/react-carousel/issues/394) ([#618](https://github.com/brainhubeu/react-carousel/issues/618)) ([cfae59f](https://github.com/brainhubeu/react-carousel/commit/cfae59f46609b26441ceba0d910b1ef02c1f1c5c)) 61 | -------------------------------------------------------------------------------- /docs/api/carousel.md: -------------------------------------------------------------------------------- 1 | ## Carousel component props 2 | 3 | * ```plugins: Func[]|String[]```: An array of plugins which will modify and extend carousel behavior. More details [**here**](https://brainhubeu.github.io/react-carousel/docs/plugins/plugins) 4 | 5 | * ```value: Number```: Current slide's index (zero based, depends on the elements order). 6 | 7 | * ```onChange: Function```: Handler triggered when current slide is about to change (e.g. on arrow click or on swipe). 8 | 9 | * ```slides: Array``` Alternative way to pass slides. This prop expects an array of JSX \ elements. 10 | 11 | * ```itemWidth: Number```: Determines custom width for every slide in the carousel. 12 | 13 | * ```offset: Number```: Padding between items. 14 | 15 | * ```animationSpeed: Number``` Determines transition duration in milliseconds. 16 | 17 | * ```draggable: Boolean``` Makes it possible to drag to the next slide with mouse cursor. 18 | 19 | * ```breakpoints: Object``` All props can be set to different values on different screen resolutions 20 | 21 | ## Default Properties: 22 | 23 | - ```offset: 0``` 24 | - ```animationSpeed: 500``` 25 | - ```draggable: true``` 26 | -------------------------------------------------------------------------------- /docs/api/dots.md: -------------------------------------------------------------------------------- 1 | ## Dots component props 2 | 3 | * ```value: Number```: Current Carousel value 4 | 5 | * ```onChange: PropTypes.func```: OnChange callback (works in the same way as onChange in Carousel component) 6 | 7 | * ```number: Number```: Number of slides in carousel you want to control. By default it will be set to the number of thumbnails (if provided). 8 | 9 | * ```thumbnails: Array```: Array of thumbnails to show. If not provided, default dots will be shown. 10 | 11 | * ```rtl: PropTypes.bool```: Indicating if the dots should have direction from Right to Left 12 | -------------------------------------------------------------------------------- /docs/api/plugins.md: -------------------------------------------------------------------------------- 1 | ## plugins 2 | 3 | ### slidesToShow plugin 4 | 5 | options: 6 | * ```numberOfSlides: Number```: Number of slides visible at once. 7 | 8 | usage: 9 | ```jsx 10 | 20 | {/ *carousel items... */} 21 | 22 | ``` 23 | 24 |

25 | 26 | ### slidesToScroll plugin 27 | 28 | options: 29 | * ```numberOfSlides: Number```: Number by which value will change on scroll (autoPlay, arrow click, drag). 30 | 31 | usage: 32 | ```jsx 33 | 43 | {/ *carousel items... */} 44 | 45 | ``` 46 | 47 |

48 | 49 | ### arrows plugin 50 | 51 | options: 52 | * ```arrowLeft: React element```, ```arrowLeftDisabled: React element```, ```arrowRightDisabled: React element```, ```arrowRight: React element```: React elements to be used instead of default arrows. 53 | 54 | * ```addArrowClickHandler: Boolean``` Has to be added for arrowLeft and arrowRight to work. 55 | 56 | simple usage: 57 | ```jsx 58 | 61 | {/ *carousel items... */} 62 | 63 | ``` 64 | 65 | advanced usage: 66 | ```jsx 67 | , 73 | arrowLeftDisabled: , 74 | arrowRight: , 75 | arrowRightDisabled: , 76 | addArrowClickHandler: true, 77 | } 78 | } 79 | ]} 80 | > 81 | {/ *carousel items... */} 82 | 83 | ``` 84 | 85 |

86 | 87 | ### autoplay plugin 88 | 89 | options: 90 | * ```interval: Number```: Slide change interval in milliseconds. Defaults to 2000 91 | 92 | * ```stopAutoPlayOnHover: Boolean```: Determines if autoPlay should stop when mouse hover over carousel, defaults to `true` 93 | 94 | simple usage: 95 | ```jsx 96 | 99 | {/ *carousel items... */} 100 | 101 | ``` 102 | 103 | advanced usage: 104 | ```jsx 105 | 116 | {/ *carousel items... */} 117 | 118 | ``` 119 | 120 |

121 | 122 | ### clickToChange plugin 123 | Clicking on a slide changes current slide to the clicked one. 124 | 125 | usage: 126 | ```jsx 127 | 130 | {/ *carousel items... */} 131 | 132 | ``` 133 | 134 |

135 | 136 | ### centered plugin 137 | Alignes active slide to the center of the carousel. 138 | 139 | usage: 140 | ```jsx 141 | 144 | {/ *carousel items... */} 145 | 146 | ``` 147 | 148 |

149 | 150 | ### infinite plugin 151 | Creates an infinite carousel width. 152 | 153 | options: 154 | * ```numberOfInfiniteClones: Number```: Number of clones created before and after original carousel slides. Defaults to 1 155 | 156 | usage: 157 | ```jsx 158 | 161 | {/ *carousel items... */} 162 | 163 | ``` 164 | 165 | advanced usage: 166 | ```jsx 167 | 177 | {/ *carousel items... */} 178 | 179 | ``` 180 | 181 |

182 | 183 | ### fastSwipe plugin 184 | While dragging, it doesn't matter which slide is the nearest one, but in what direction you dragged. 185 | 186 | usage: 187 | ```jsx 188 | 191 | {/ *carousel items... */} 192 | 193 | ``` 194 | -------------------------------------------------------------------------------- /docs/contributions-guide/decision-log.md: -------------------------------------------------------------------------------- 1 | # Decision log 2 | 3 | ### React (2 January 2018) 4 | We love [React](https://github.com/facebook/react) so we'd like to focus on React only and in the nearest future, we don't plan to make this library working in another framework like [Vue.js](https://github.com/vuejs/vue). 5 | 6 | ### Renovate (18 February 2019) 7 | We've decided that [Renovate](https://github.com/renovatebot/renovate) is better than [Greenkeeper](https://github.com/greenkeeperio/greenkeeper) because Renovate is very configurable and has a great support. 8 | 9 | ### IssueHunt (7 January 2020) 10 | We've decided to use [IssueHunt](https://issuehunt.io/) to fund issues so we can get more contributors (more contributors, more popular a given project), assign a value to issues and reward active contributors. 11 | 12 | ### Testing environment deployment (31 January 2020) 13 | We've decided to use http://beghp.github.io/ domain to deploy each branch there because in a version deployed to [Netlify](https://www.netlify.com/) we've noticed broken fonts so deploying to GitHub Pages gives an environment the most possibly similar to the [production version](https://brainhubeu.github.io/react-carousel/). `beghp` is an acronym from Brainhub.eu GitHub Pages and we use this organization in order to keep only real repos in the `brainhubeu` organization. 14 | 15 | ### Cypress (13 March 2020) 16 | We've decided that [Cypress](https://github.com/cypress-io/cypress) is better than [Hermione](https://github.com/gemini-testing/hermione) (Hermione predecessor is [Gemini](https://github.com/gemini-testing/gemini)) because Hermione required setting a very large tolerance in order to pass both locally and in CI. Moreover, Cypress is much more popular. 17 | 18 | ### Recoil (14 July 2020) 19 | We've decided that [recoil](https://recoiljs.org/) will be used for managing state of the react-carousel component because of its simplicity to set up and maintain. 20 | 21 | ### Plugins (14 July 2020) 22 | We've decided to use plugins to make carousel easier to maintain and to allow users to extend carousel possibilities. 23 | -------------------------------------------------------------------------------- /docs/contributions-guide/labels.md: -------------------------------------------------------------------------------- 1 | # Labels 2 | 3 | ### Issue labels: 4 | - issue type (mutually exclusive): 5 | - `bug` 6 | - `enhancement` - a feature request or a proposal to improve tests or to improve README or to improve anything beside fixing a bug 7 | - `question` 8 | - answering labels (mutually exclusive): 9 | - `answering: reported by brainhubeu` if the issue is created by any member of the `brainhubeu` organization with no comments by external contributors 10 | - otherwise `answering: answered` if the last comment is by a `brainhubeu` member 11 | - otherwise `answering: not answered` 12 | - severity (only for bugs, mutually exclusive): 13 | - `severity: blocked` - nothing is working 14 | - `severity: critical` - the most important features are often broken 15 | - `severity: major` - the most important features are sometimes broken or medium important features are often broken 16 | - `severity: medium` - medium important features are sometimes broken or less important features are often broken 17 | - `severity: minor` - less important features are sometimes broken 18 | - `severity: trivial` - it'd be nice to fix but we can live without fixing it 19 | - used by third-party GitHub apps: 20 | - `💵 Funded on Issuehunt` - funded on IssueHunt so you can earn money, fixing the given issue 21 | - `🎁 Rewarded on Issuehunt` - already rewarded on IssueHunt 22 | - other labels: 23 | - `duplicate` - if the given issue is a duplicate of another issue 24 | - `no reproduction details` - if we miss details needed to reproduce the given issue 25 | - `needs discussion` - if we need to discuss details of the given issue 26 | - `proposed issuehunt` if we consider the given issue to fund on IssueHunt 27 | - `hacktoberfest` - used in [Hacktoberfest](https://hacktoberfest.digitalocean.com/) during October, each year so you can obtain a T-shirt according to the Hacktoberfest rules 28 | 29 | ### PRs labels: 30 | - testing (mutually exclusive): 31 | - `tested & works` 32 | - `tested & fails` 33 | - used by third-party GitHub apps: 34 | - `renovate` for PRs opened by Renovate 35 | - `dependencies` for PRs opened by Dependabot 36 | - other labels: 37 | - `wip` - Work in Progress so don't merge 38 | 39 | ### Labels used for both issues and PRs: 40 | - `blocked` if a given issue or PR is blocked by another issue or PR 41 | -------------------------------------------------------------------------------- /docs/contributions-guide/workflow.md: -------------------------------------------------------------------------------- 1 | # Workflow 2 | 3 | 1. A contributor opens a PR. 4 | 1. A `brainhubeu` organization member creates a new branch from `master` with one of the following prefixes: 5 | - `fix/` for a bug fix 6 | - `feature/` for a new feature 7 | - `breaking/` for breaking changes 8 | 1. A `brainhubeu` organization member changes the PR base branch. 9 | 1. If there are no vulnerabilities, a `brainhubeu` organization member merges the PR to the created branch (other than `master`) in the main repo. 10 | 1. The CI deploys the PR to one of the testing environments. 11 | 1. A `brainhubeu` organization member tests the PR: 12 | - what to test? 13 | - whether the bug is really fixed or the newly implemented feature is working correctly 14 | - whether it doesn't break anything (doesn't cause any new bug) 15 | - where? 16 | - if a manual code analysis tells the PR doesn't affect the mobile and it's a simple change, only desktop (the newest Chrome in any OS) 17 | - otherwise both desktop and mobile (Safari for iOS or the newest Chrome for Android) 18 | - which examples? 19 | - all the examples from the docs 20 | - make sure to resize the screen for the `Responsive` example, desktop 21 | - add `rtl` to the `Autoplay & Animation speed` example 22 | 1. Other `brainhubeu` organization members (at least one person other than the one who has opened the PR) review the PR (and test if they want to). 23 | 1. If the problem is correctly resolved, no breaking changes, and the code approved, a `brainhubeu` organization member merges the PR to the `master` branch. 24 | 1. The CI publishes to NPM. 25 | 1. The CI deploys the production version of docs. 26 | 1. If the issue resolved by the merged PR is funded on IssueHunt, a `brainhubeu` organization member rewards it for the PR author. 27 | -------------------------------------------------------------------------------- /docs/examples/animation.md: -------------------------------------------------------------------------------- 1 | ## Autoplay & Animation speed 2 | You can set how often slides will change automatically using the `autoPlay` plugin and interval option. Animation speed can also be changed, using animationSpeed prop (which is actually animation duration in ms). 3 | ```jsx render 4 | // import Carousel, { autoplayPlugin } from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | 19 | 20 | 21 | 22 | 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/examples/centered.md: -------------------------------------------------------------------------------- 1 | ## Centered 2 | By default, the current slide is aligned to the left. You can change that behaviour with the `centered` plugin. 3 | ```jsx render 4 | // import Carousel, { slidesToShowPlugin } from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | 20 | 21 | 22 | 23 | 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/examples/clickToChange.md: -------------------------------------------------------------------------------- 1 | ## Click to change 2 | By default, clicking the slides does nothing. You can change that behavior with the `clickToChange` plugin. 3 | ```jsx render 4 | // import Carousel, { slidesToShowPlugin } from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | 19 | 20 | 21 | 22 | 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/examples/controlled.md: -------------------------------------------------------------------------------- 1 | ## Controlled component 2 | You can use Carousel as a controlled component. Provided value will be clamped depending on the number of slides. E.g. if there are 3 slides, all values greater than 2 (index of the last element) will evaluate to 2. 3 | ```jsx render 4 | // import Carousel from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | class MyCarousel extends React.Component { 8 | constructor() { 9 | super() 10 | this.state = { value: 0 }; 11 | this.onChange = this.onChange.bind(this); 12 | } 13 | 14 | onChange(value) { 15 | this.setState({ value }); 16 | } 17 | 18 | render() { 19 | return ( 20 |
21 | this.onChange(parseInt(e.target.value || 0))} 25 | /> 26 | ), 31 | (), 32 | (), 33 | ]} 34 | plugins={[ 35 | 'arrows', 36 | 'clickToChange' 37 | ]} 38 | /> 39 |
40 | ); 41 | } 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/examples/customArrows.md: -------------------------------------------------------------------------------- 1 | ## Custom arrows 2 | You can set custom arrows, using `arrowLeft` and arrowRight options in the `arrows` plugin. You can set custom arrows for the disabled state of `arrowLeft` and `arrowRight`, using `arrrowLeftDisabled` and `arrowRightDisabled` options. You can handle an arrow click event by yourself, or you can tell Carousel to do that for you (using `addArrowClickHandler` option). 3 | ```jsx render 4 | // import Carousel, { arrowsPlugin } from '@brainhubeu/react-carousel'; 5 | // import Icon from 'react-fa'; 6 | 7 | // import '@brainhubeu/react-carousel/lib/style.css'; 8 | 9 | , 15 | arrowLeftDisabled:, 16 | arrowRight: , 17 | arrowRightDisabled: , 18 | addArrowClickHandler: true, 19 | } 20 | } 21 | ]} 22 | 23 | > 24 | 25 | 26 | 27 | 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/examples/defaultArrows.md: -------------------------------------------------------------------------------- 1 | ## Default Arrows 2 | You can turn default arrows on, using the `arrows` plugin. 3 | ```jsx render 4 | // import Carousel from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | 10 | 11 | 12 | 13 | 14 | ``` 15 | -------------------------------------------------------------------------------- /docs/examples/dots.md: -------------------------------------------------------------------------------- 1 | ## Dots 2 | Dots are a separate component which you can use with controlled carousel as well. 3 | ```jsx render 4 | // import Carousel, { Dots } from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | class MyCarousel extends React.Component { 8 | constructor() { 9 | super() 10 | this.state = { 11 | value: 0, 12 | slides: [ 13 | (), 14 | (), 15 | (), 16 | ], 17 | } 18 | this.onchange = this.onchange.bind(this); 19 | } 20 | 21 | 22 | onchange(value) { 23 | this.setState({ value }); 24 | } 25 | 26 | render() { 27 | return ( 28 |
29 | 34 | 35 |
36 | ); 37 | } 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/examples/flexContainer.md: -------------------------------------------------------------------------------- 1 | ## Flex Container 2 | You can use a Carousel within a flex container 3 | ```jsx render 4 | // import Carousel from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 |
14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 15 |
16 |
17 | ``` 18 | -------------------------------------------------------------------------------- /docs/examples/infinite.md: -------------------------------------------------------------------------------- 1 | ## Infinite 2 | ```jsx render 3 | // import Carousel from '@brainhubeu/react-carousel'; 4 | // import '@brainhubeu/react-carousel/lib/style.css'; 5 | 6 | 12 | 13 | 14 | 15 | 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/examples/lazyload.md: -------------------------------------------------------------------------------- 1 | ## Lazy load slides 2 | Loads only the nearest next/prev slides to the current one based on the value of the slidesPerPage variable. Check network tab, to see that images are preloaded on demand. 3 | ```jsx render 4 | class MyCarousel extends React.Component { 5 | constructor() { 6 | super() 7 | this.state = { value: 0 }; 8 | this.onChange = this.onChange.bind(this); 9 | } 10 | 11 | onChange(value) { 12 | this.setState({ value }); 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 | this.onChange(parseInt(e.target.value || 0))} 22 | /> 23 | ), 39 | (), 40 | (), 41 | (), 42 | (), 43 | (), 44 | (), 45 | (), 46 | (), 47 | (), 48 | (), 49 | ]} 50 | /> 51 |
52 | ); 53 | } 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/examples/multipleSlides.md: -------------------------------------------------------------------------------- 1 | ## Multiple slides 2 | You can show more than one slide per page, using the `slidesToShow` plugin. 3 | ```jsx render 4 | // import Carousel, { slidesToShowPlugin } from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | 19 | 20 | 21 | 22 | 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/examples/resizableCarousel.md: -------------------------------------------------------------------------------- 1 | ## Resizable carousel 2 | You can surround the Carousel by a container, which can be resized 3 | independently of the window. You should only make sure, 4 | that the container doesn't exceed the viewport. 5 | ```jsx render 6 | // import Carousel from '@brainhubeu/react-carousel'; 7 | // import '@brainhubeu/react-carousel/lib/style.css'; 8 | 9 | class MyCarousel extends React.Component { 10 | 11 | resizeSurrounding() { 12 | const width = document.querySelector('#width').value; 13 | document.querySelector('#surrounding').style.width = `${width}px`; 14 | } 15 | 16 | render() { 17 | return ( 18 |
19 | 20 | 21 |
22 | 29 | 30 | 31 | 32 | 33 |
34 |
35 | ); 36 | } 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/examples/responsive.md: -------------------------------------------------------------------------------- 1 | ## Responsive 2 | You can set all props and plugins to different values on different screen resolutions. The props set will override the existing prop (if already set). 3 | 4 | ```jsx render 5 | // import Carousel, { slidesToShowPlugin } from '@brainhubeu/react-carousel'; 6 | // import '@brainhubeu/react-carousel/lib/style.css'; 7 | 8 | 41 | 42 | 43 | 44 | 45 | ``` 46 | -------------------------------------------------------------------------------- /docs/examples/rtl.md: -------------------------------------------------------------------------------- 1 | ## RTL layout 2 | Carousel supports the right to left layout. 3 | ```jsx render 4 | // import Carousel from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | 10 | 11 | 12 | 13 | 14 | ``` 15 | -------------------------------------------------------------------------------- /docs/examples/simpleUsage.md: -------------------------------------------------------------------------------- 1 | ## Simple slide 2 | For simple usage, just pass slides as children. You don't need any configuration! 3 | ```jsx render 4 | // import Carousel from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | 8 | 9 | 10 | 11 | 12 | ``` 13 | -------------------------------------------------------------------------------- /docs/examples/slidesToScroll.md: -------------------------------------------------------------------------------- 1 | ## Slides to scroll 2 | You can change how far the carousel should move when you click arrow or swipe, using the `slidesToScroll` plugin. The default value is 3. 3 | ```jsx render 4 | // import Carousel, { slidesToShowPlugin, slidesToScrollPlugin } from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | 26 | 27 | 28 | 29 | 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/examples/swipingSlides.md: -------------------------------------------------------------------------------- 1 | ## Swiping slides 2 | You can change the swiping behaviour with the `fastSwipe` plugin, and you can disable swiping by setting props `draggable={false}`. 3 | ```jsx render 4 | // import Carousel, { slidesToShowPlugin } from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | 19 | 20 | 21 | 22 | 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/examples/thumbnails.md: -------------------------------------------------------------------------------- 1 | ## Thumbnails 2 | You can use Dots component to show thumbnails. 3 | ```jsx render 4 | // import Carousel from '@brainhubeu/react-carousel'; 5 | // import '@brainhubeu/react-carousel/lib/style.css'; 6 | 7 | class MyCarousel extends React.Component { 8 | constructor() { 9 | super() 10 | this.state = { 11 | value: 0, 12 | slides: [ 13 | (), 14 | (), 15 | (), 16 | ], 17 | thumbnails: [ 18 | (), 19 | (), 20 | (), 21 | ], 22 | } 23 | this.onchange = this.onchange.bind(this); 24 | } 25 | 26 | 27 | onchange(value) { 28 | this.setState({ value }); 29 | } 30 | 31 | render() { 32 | return ( 33 |
34 | 39 | 40 |
41 | ); 42 | } 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/gettingStarted.md: -------------------------------------------------------------------------------- 1 | ## Why? 2 | There are some great carousels (like slick) that do not have real React implementations. This library provides you with carousel that is not merely a wrapper for some jQuery solution, can be used as controlled or uncontrolled element (similar to [inputs](https://reactjs.org/docs/uncontrolled-components.html)), and has tons of useful features. 3 | 4 | ## Installation 5 | Install the library: 6 | ``` 7 | $ npm i @brainhubeu/react-carousel 8 | ``` 9 | 10 | And then you can import Carousel and Dots components: 11 | ``` 12 | import Carousel, { Dots } from '@brainhubeu/react-carousel'; 13 | import '@brainhubeu/react-carousel/lib/style.css'; 14 | ``` 15 | 16 | You can find usage examples [here](/docs/examples/simpleUsage/) 17 | -------------------------------------------------------------------------------- /docs/migrationGuide.md: -------------------------------------------------------------------------------- 1 | ## Migration guide 2 | In the carousel v2 in order to make carousel more maintainable, many props have been replaced with the plugins. In the following section, you can find out how to migrate the carousel v1 props into plugins. 3 | 4 | ### slidesPerPage 5 | 6 | ####v1 7 | ```jsx 8 | 11 | 12 | 13 | 14 | 15 | ``` 16 | 17 | ####v2 18 | ```jsx 19 | import { slidesToShowPlugin } from '@brainhubeu/react-carousel'; 20 | 21 | 31 | 32 | 33 | 34 | 35 | ``` 36 | 37 | ### clickToChange 38 | 39 | ####v1 40 | ```jsx 41 | 44 | 45 | 46 | 47 | 48 | ``` 49 | 50 | ####v2 51 | ```jsx 52 | 57 | 58 | 59 | 60 | 61 | ``` 62 | 63 | ### arrows 64 | 65 | ####v1 66 | ```jsx 67 | 70 | 71 | 72 | 73 | 74 | ``` 75 | 76 | ####v2 77 | ```jsx 78 | 81 | 82 | 83 | 84 | 85 | ``` 86 | 87 | ### infinite 88 | 89 | ####v1 90 | ```jsx 91 | 94 | 95 | 96 | 97 | 98 | ``` 99 | 100 | ####v2 101 | ```jsx 102 | 107 | 108 | 109 | 110 | 111 | ``` 112 | 113 | 114 | ### rtl 115 | 116 | ####v1 117 | ```jsx 118 | 119 | 120 | 121 | 122 | 123 | ``` 124 | 125 | ####v2 126 | ```jsx 127 | 132 | 133 | 134 | 135 | 136 | ``` 137 | 138 | ### centered 139 | 140 | ####v1 141 | ```jsx 142 | 143 | 144 | 145 | 146 | 147 | ``` 148 | 149 | ####v2 150 | ```jsx 151 | 156 | 157 | 158 | 159 | 160 | ``` 161 | 162 | ### slidesPerScroll 163 | 164 | ####v1 165 | ```jsx 166 | 169 | 170 | 171 | 172 | 173 | ``` 174 | 175 | ####v2 176 | ```jsx 177 | import { slidesToScrollPlugin } from '@brainhubeu/react-carousel'; 178 | 179 | 189 | 190 | 191 | 192 | 193 | ``` 194 | 195 | ### keepDirectionWhenDragging 196 | 197 | ####v1 198 | ```jsx 199 | 202 | 203 | 204 | 205 | 206 | ``` 207 | 208 | ####v2 209 | ```jsx 210 | 215 | 216 | 217 | 218 | 219 | ``` 220 | 221 | ### autoplay 222 | 223 | ####v1 224 | ```jsx 225 | 229 | 230 | 231 | 232 | 233 | ``` 234 | 235 | ####v2 236 | ```jsx 237 | import { autoplayPlugin } from '@brainhubeu/react-carousel'; 238 | 239 | 250 | 251 | 252 | 253 | 254 | ``` 255 | 256 | ### dots 257 | 258 | In v2, the dots property has been removed. Please use [**dots component**](https://brainhubeu.github.io/react-carousel/docs/examples/dots) instead. 259 | 260 | ### lazyLoad 261 | 262 | We are working on implementing this feature in the carousel v2. Stay tuned... 263 | -------------------------------------------------------------------------------- /docs/plugins/plugins.md: -------------------------------------------------------------------------------- 1 | ## plugins 2 | 3 | ### slidesToShow plugin 4 | 5 | options: 6 | * ```numberOfSlides: Number```: Number of slides visible at once. 7 | 8 | usage: 9 | ```jsx 10 | 20 | {/ *carousel items... */} 21 | 22 | ``` 23 | 24 |

25 | 26 | ### slidesToScroll plugin 27 | 28 | options: 29 | * ```numberOfSlides: Number```: Number by which value will change on scroll (autoPlay, arrow click, drag). 30 | 31 | usage: 32 | ```jsx 33 | 43 | {/ *carousel items... */} 44 | 45 | ``` 46 | 47 |

48 | 49 | ### arrows plugin 50 | 51 | options: 52 | * ```arrowLeft: React element```, ```arrowLeftDisabled: React element```, ```arrowRightDisabled: React element```, ```arrowRight: React element```: React elements to be used instead of default arrows. 53 | 54 | * ```addArrowClickHandler: Boolean``` Has to be added for arrowLeft and arrowRight to work. 55 | 56 | simple usage: 57 | ```jsx 58 | 61 | {/ *carousel items... */} 62 | 63 | ``` 64 | 65 | advanced usage: 66 | ```jsx 67 | , 73 | arrowLeftDisabled:, 74 | arrowRight: , 75 | arrowRightDisabled: , 76 | addArrowClickHandler: true, 77 | } 78 | } 79 | ]} 80 | > 81 | {/ *carousel items... */} 82 | 83 | ``` 84 | 85 |

86 | 87 | ### autoplay plugin 88 | 89 | options: 90 | * ```interval: Number```: Slide change interval in milliseconds. Defaults to 2000 91 | 92 | * ```stopAutoPlayOnHover: Boolean```: Determines if autoPlay should stop when mouse hover over carousel, defaults to `true` 93 | 94 | * ```direction: 'right' | 'left'```: Determines direction of changing slides, defaults to `right` 95 | 96 | simple usage: 97 | ```jsx 98 | 101 | {/ *carousel items... */} 102 | 103 | ``` 104 | 105 | advanced usage: 106 | ```jsx 107 | 118 | {/ *carousel items... */} 119 | 120 | ``` 121 | 122 |

123 | 124 | ### clickToChange plugin 125 | Clicking on a slide changes current slide to the clicked one. 126 | 127 | usage: 128 | ```jsx 129 | 132 | {/ *carousel items... */} 133 | 134 | ``` 135 | 136 |

137 | 138 | ### centered plugin 139 | Alignes active slide to the center of the carousel. 140 | 141 | usage: 142 | ```jsx 143 | 146 | {/ *carousel items... */} 147 | 148 | ``` 149 | 150 |

151 | 152 | ### infinite plugin 153 | Creates an infinite carousel width. 154 | 155 | options: 156 | * ```numberOfInfiniteClones: Number```: Number of clones created before and after original carousel slides. Defaults to 1 157 | 158 | usage: 159 | ```jsx 160 | 163 | {/ *carousel items... */} 164 | 165 | ``` 166 | 167 | advanced usage: 168 | ```jsx 169 | 179 | {/ *carousel items... */} 180 | 181 | ``` 182 | 183 |

184 | 185 | ### fastSwipe plugin 186 | While dragging, it doesn't matter which slide is the nearest one, but in what direction you drag. 187 | 188 | usage: 189 | ```jsx 190 | 193 | {/ *carousel items... */} 194 | 195 | ``` 196 | -------------------------------------------------------------------------------- /license/blacklist.js: -------------------------------------------------------------------------------- 1 | /* Provide a list of blacklisted licenses for the project below */ 2 | const blacklist = [ 3 | 'UNKNOWN', 4 | '389-exception', 5 | 'AAL', 6 | 'ADSL', 7 | 'AFL-1.1', 8 | 'AFL-1.2', 9 | 'AFL-2.0', 10 | 'AFL-2.1', 11 | 'AFL-3.0', 12 | 'AGPL-1.0-only', 13 | 'AGPL-1.0-or-later', 14 | 'AGPL-3.0-only', 15 | 'AGPL-3.0-or-later', 16 | 'AMDPLPA', 17 | 'AML', 18 | 'AMPAS', 19 | 'ANTLR-PD', 20 | 'APAFML', 21 | 'APL-1.0', 22 | 'APSL-1.0', 23 | 'APSL-1.1', 24 | 'APSL-1.2', 25 | 'APSL-2.0', 26 | 'Abstyles', 27 | 'Adobe-2006', 28 | 'Adobe-Glyph', 29 | 'Afmparse', 30 | 'Aladdin', 31 | 'Apache-1.0', 32 | 'Apache-1.1', 33 | 'Artistic-1.0-Perl', 34 | 'Artistic-1.0-cl8', 35 | 'Artistic-1.0', 36 | 'Autoconf-exception-2.0', 37 | 'Autoconf-exception-3.0', 38 | 'BSD-1-Clause', 39 | 'BSD-2-Clause-FreeBSD', 40 | 'BSD-2-Clause-NetBSD', 41 | 'BSD-2-Clause-Patent', 42 | 'BSD-3-Clause-Attribution', 43 | 'BSD-3-Clause-Clear', 44 | 'BSD-3-Clause-LBNL', 45 | 'BSD-3-Clause-No-Nuclear-License-2014', 46 | 'BSD-3-Clause-No-Nuclear-License', 47 | 'BSD-3-Clause-No-Nuclear-Warranty', 48 | 'BSD-3-Clause-Open-MPI', 49 | 'BSD-4-Clause-UC', 50 | 'BSD-4-Clause', 51 | 'BSD-Protection', 52 | 'BSD-Source-Code', 53 | 'BSL-1.0', 54 | 'Bahyph', 55 | 'Barr', 56 | 'Beerware', 57 | 'Bison-exception-2.2', 58 | 'BitTorrent-1.0', 59 | 'BitTorrent-1.1', 60 | 'BlueOak-1.0.0', 61 | 'Bootloader-exception', 62 | 'Borceux', 63 | 'CATOSL-1.1', 64 | 'CC-BY-1.0', 65 | 'CC-BY-2.0', 66 | 'CC-BY-2.5', 67 | 'CC-BY-NC-1.0', 68 | 'CC-BY-NC-2.0', 69 | 'CC-BY-NC-2.5', 70 | 'CC-BY-NC-3.0', 71 | 'CC-BY-NC-4.0', 72 | 'CC-BY-NC-ND-1.0', 73 | 'CC-BY-NC-ND-2.0', 74 | 'CC-BY-NC-ND-2.5', 75 | 'CC-BY-NC-ND-3.0', 76 | 'CC-BY-NC-ND-4.0', 77 | 'CC-BY-NC-SA-1.0', 78 | 'CC-BY-NC-SA-2.0', 79 | 'CC-BY-NC-SA-2.5', 80 | 'CC-BY-NC-SA-3.0', 81 | 'CC-BY-NC-SA-4.0', 82 | 'CC-BY-ND-1.0', 83 | 'CC-BY-ND-2.0', 84 | 'CC-BY-ND-2.5', 85 | 'CC-BY-ND-3.0', 86 | 'CC-BY-ND-4.0', 87 | 'CC-BY-SA-1.0', 88 | 'CC-BY-SA-2.0', 89 | 'CC-BY-SA-2.5', 90 | 'CC-BY-SA-3.0', 91 | 'CC-BY-SA-4.0', 92 | 'CC-PDDC', 93 | 'CDDL-1.0', 94 | 'CDDL-1.1', 95 | 'CDLA-Permissive-1.0', 96 | 'CDLA-Sharing-1.0', 97 | 'CECILL-1.0', 98 | 'CECILL-1.1', 99 | 'CECILL-2.0', 100 | 'CECILL-2.1', 101 | 'CECILL-B', 102 | 'CECILL-C', 103 | 'CERN-OHL-1.1', 104 | 'CERN-OHL-1.2', 105 | 'CLISP-exception-2.0', 106 | 'CNRI-Jython', 107 | 'CNRI-Python-GPL-Compatible', 108 | 'CNRI-Python', 109 | 'CPAL-1.0', 110 | 'CPL-1.0', 111 | 'CPOL-1.02', 112 | 'CUA-OPL-1.0', 113 | 'Caldera', 114 | 'ClArtistic', 115 | 'Classpath-exception-2.0', 116 | 'Condor-1.1', 117 | 'Crossword', 118 | 'CrystalStacker', 119 | 'Cube', 120 | 'D-FSL-1.0', 121 | 'DOC', 122 | 'DSDP', 123 | 'DigiRule-FOSS-exception', 124 | 'Dotseqn', 125 | 'ECL-1.0', 126 | 'ECL-2.0', 127 | 'EFL-1.0', 128 | 'EFL-2.0', 129 | 'EPL-1.0', 130 | 'EPL-2.0', 131 | 'EUDatagrid', 132 | 'EUPL-1.0', 133 | 'EUPL-1.1', 134 | 'EUPL-1.2', 135 | 'Entessa', 136 | 'ErlPL-1.1', 137 | 'Eurosym', 138 | 'FLTK-exception', 139 | 'FSFAP', 140 | 'FSFUL', 141 | 'FSFULLR', 142 | 'FTL', 143 | 'Fair', 144 | 'Fawkes-Runtime-exception', 145 | 'Font-exception-2.0', 146 | 'Frameworx-1.0', 147 | 'FreeImage', 148 | 'GCC-exception-2.0', 149 | 'GCC-exception-3.1', 150 | 'GFDL-1.1-only', 151 | 'GFDL-1.1-or-later', 152 | 'GFDL-1.2-only', 153 | 'GFDL-1.2-or-later', 154 | 'GFDL-1.3-only', 155 | 'GFDL-1.3-or-later', 156 | 'GL2PS', 157 | 'GPL-1.0-only', 158 | 'GPL-1.0-or-later', 159 | 'GPL-2.0-only', 160 | 'GPL-2.0-or-later', 161 | 'GPL-3.0-linking-exception', 162 | 'GPL-3.0-linking-source-exception', 163 | 'GPL-3.0-only', 164 | 'GPL-3.0-or-later', 165 | 'GPL-CC-1.0', 166 | 'Giftware', 167 | 'Glide', 168 | 'Glulxe', 169 | 'HPND-sell-variant', 170 | 'HPND', 171 | 'HaskellReport', 172 | 'IBM-pibs', 173 | 'ICU', 174 | 'IJG', 175 | 'IPA', 176 | 'IPL-1.0', 177 | 'ImageMagick', 178 | 'Imlib2', 179 | 'Info-ZIP', 180 | 'Intel-ACPI', 181 | 'Intel', 182 | 'Interbase-1.0', 183 | 'JPNIC', 184 | 'JSON', 185 | 'JasPer-2.0', 186 | 'LAL-1.2', 187 | 'LAL-1.3', 188 | 'LGPL-2.0-only', 189 | 'LGPL-2.0-or-later', 190 | 'LGPL-2.1-only', 191 | 'LGPL-2.1-or-later', 192 | 'LGPL-3.0-only', 193 | 'LGPL-3.0-or-later', 194 | 'LGPLLR', 195 | 'LLVM-exception', 196 | 'LPL-1.0', 197 | 'LPL-1.02', 198 | 'LPPL-1.0', 199 | 'LPPL-1.1', 200 | 'LPPL-1.2', 201 | 'LPPL-1.3a', 202 | 'LPPL-1.3c', 203 | 'LZMA-exception', 204 | 'Latex2e', 205 | 'Leptonica', 206 | 'LiLiQ-P-1.1', 207 | 'LiLiQ-R-1.1', 208 | 'LiLiQ-Rplus-1.1', 209 | 'Libpng', 210 | 'Libtool-exception', 211 | 'Linux-OpenIB', 212 | 'Linux-syscall-note', 213 | 'MIT-0', 214 | 'MIT-CMU', 215 | 'MIT-advertising', 216 | 'MIT-enna', 217 | 'MIT-feh', 218 | 'MITNFA', 219 | 'MS-PL', 220 | 'MS-RL', 221 | 'MTLL', 222 | 'MakeIndex', 223 | 'MirOS', 224 | 'Motosoto', 225 | 'MulanPSL-1.0', 226 | 'Multics', 227 | 'Mup', 228 | 'NASA-1.3', 229 | 'NBPL-1.0', 230 | 'NCSA', 231 | 'NGPL', 232 | 'NLOD-1.0', 233 | 'NLPL', 234 | 'NOSL', 235 | 'NPL-1.0', 236 | 'NPL-1.1', 237 | 'NPOSL-3.0', 238 | 'NRL', 239 | 'NTP-0', 240 | 'NTP', 241 | 'Naumen', 242 | 'Net-SNMP', 243 | 'NetCDF', 244 | 'Newsletr', 245 | 'Nokia-Qt-exception-1.1', 246 | 'Nokia', 247 | 'Noweb', 248 | 'OCCT-PL', 249 | 'OCCT-exception-1.0', 250 | 'OCLC-2.0', 251 | 'OCaml-LGPL-linking-exception', 252 | 'ODbL-1.0', 253 | 'OFL-1.0-RFN', 254 | 'OFL-1.0-no-RFN', 255 | 'OFL-1.0', 256 | 'OFL-1.1-RFN', 257 | 'OFL-1.1-no-RFN', 258 | 'OFL-1.1', 259 | 'OGL-Canada-2.0', 260 | 'OGL-UK-1.0', 261 | 'OGL-UK-2.0', 262 | 'OGL-UK-3.0', 263 | 'OGTSL', 264 | 'OLDAP-1.1', 265 | 'OLDAP-1.2', 266 | 'OLDAP-1.3', 267 | 'OLDAP-1.4', 268 | 'OLDAP-2.0.1', 269 | 'OLDAP-2.0', 270 | 'OLDAP-2.1', 271 | 'OLDAP-2.2.1', 272 | 'OLDAP-2.2.2', 273 | 'OLDAP-2.2', 274 | 'OLDAP-2.3', 275 | 'OLDAP-2.4', 276 | 'OLDAP-2.5', 277 | 'OLDAP-2.6', 278 | 'OLDAP-2.7', 279 | 'OLDAP-2.8', 280 | 'OML', 281 | 'OPL-1.0', 282 | 'OSET-PL-2.1', 283 | 'OSL-1.0', 284 | 'OSL-1.1', 285 | 'OSL-2.0', 286 | 'OSL-2.1', 287 | 'OSL-3.0', 288 | 'OpenJDK-assembly-exception-1.0', 289 | 'OpenSSL', 290 | 'PDDL-1.0', 291 | 'PHP-3.0', 292 | 'PHP-3.01', 293 | 'PS-or-PDF-font-exception-20170817', 294 | 'PSF-2.0', 295 | 'Parity-6.0.0', 296 | 'Plexus', 297 | 'PostgreSQL', 298 | 'Python-2.0', 299 | 'QPL-1.0', 300 | 'Qhull', 301 | 'Qt-GPL-exception-1.0', 302 | 'Qt-LGPL-exception-1.1', 303 | 'Qwt-exception-1.0', 304 | 'RHeCos-1.1', 305 | 'RPL-1.1', 306 | 'RPL-1.5', 307 | 'RPSL-1.0', 308 | 'RSA-MD', 309 | 'RSCPL', 310 | 'Rdisc', 311 | 'Ruby', 312 | 'SAX-PD', 313 | 'SCEA', 314 | 'SGI-B-1.0', 315 | 'SGI-B-1.1', 316 | 'SGI-B-2.0', 317 | 'SHL-0.5', 318 | 'SHL-0.51', 319 | 'SISSL-1.2', 320 | 'SISSL', 321 | 'SMLNJ', 322 | 'SMPPL', 323 | 'SNIA', 324 | 'SPL-1.0', 325 | 'SSH-OpenSSH', 326 | 'SSH-short', 327 | 'SSPL-1.0', 328 | 'SWL', 329 | 'Saxpath', 330 | 'Sendmail-8.23', 331 | 'Sendmail', 332 | 'SimPL-2.0', 333 | 'Sleepycat', 334 | 'Spencer-86', 335 | 'Spencer-94', 336 | 'Spencer-99', 337 | 'SugarCRM-1.1.3', 338 | 'Swift-exception', 339 | 'TAPR-OHL-1.0', 340 | 'TCL', 341 | 'TCP-wrappers', 342 | 'TMate', 343 | 'TORQUE-1.1', 344 | 'TOSL', 345 | 'TU-Berlin-1.0', 346 | 'TU-Berlin-2.0', 347 | 'UCL-1.0', 348 | 'UPL-1.0', 349 | 'Unicode-DFS-2015', 350 | 'Unicode-DFS-2016', 351 | 'Unicode-TOU', 352 | 'Universal-FOSS-exception-1.0', 353 | 'VOSTROM', 354 | 'VSL-1.0', 355 | 'Vim', 356 | 'W3C-19980720', 357 | 'W3C-20150513', 358 | 'W3C', 359 | 'Watcom-1.0', 360 | 'Wsuipa', 361 | 'WxWindows-exception-3.1', 362 | 'X11', 363 | 'XFree86-1.1', 364 | 'XSkat', 365 | 'Xerox', 366 | 'Xnet', 367 | 'YPL-1.0', 368 | 'YPL-1.1', 369 | 'ZPL-1.1', 370 | 'ZPL-2.0', 371 | 'ZPL-2.1', 372 | 'Zed', 373 | 'Zend-2.0', 374 | 'Zimbra-1.3', 375 | 'Zimbra-1.4', 376 | 'blessing', 377 | 'bzip2-1.0.5', 378 | 'bzip2-1.0.6', 379 | 'copyleft-next-0.3.0', 380 | 'copyleft-next-0.3.1', 381 | 'curl', 382 | 'deprecated_AGPL-1.0', 383 | 'deprecated_AGPL-3.0', 384 | 'deprecated_GFDL-1.1', 385 | 'deprecated_GFDL-1.2', 386 | 'deprecated_GFDL-1.3', 387 | 'deprecated_GPL-1.0+', 388 | 'deprecated_GPL-1.0', 389 | 'deprecated_GPL-2.0+', 390 | 'deprecated_GPL-2.0-with-GCC-exception', 391 | 'deprecated_GPL-2.0-with-autoconf-exception', 392 | 'deprecated_GPL-2.0-with-bison-exception', 393 | 'deprecated_GPL-2.0-with-classpath-exception', 394 | 'deprecated_GPL-2.0-with-font-exception', 395 | 'deprecated_GPL-2.0', 396 | 'deprecated_GPL-3.0+', 397 | 'deprecated_GPL-3.0-with-GCC-exception', 398 | 'deprecated_GPL-3.0-with-autoconf-exception', 399 | 'deprecated_GPL-3.0', 400 | 'deprecated_LGPL-2.0+', 401 | 'deprecated_LGPL-2.0', 402 | 'deprecated_LGPL-2.1+', 403 | 'deprecated_LGPL-2.1', 404 | 'deprecated_LGPL-3.0+', 405 | 'deprecated_LGPL-3.0', 406 | 'deprecated_Nunit', 407 | 'deprecated_StandardML-NJ', 408 | 'deprecated_eCos-2.0', 409 | 'deprecated_wxWindows', 410 | 'diffmark', 411 | 'dvipdfm', 412 | 'eCos-exception-2.0', 413 | 'eGenix', 414 | 'etalab-2.0', 415 | 'freertos-exception-2.0', 416 | 'gSOAP-1.3b', 417 | 'gnu-javamail-exception', 418 | 'gnuplot', 419 | 'i2p-gpl-java-exception', 420 | 'iMatix', 421 | 'libpng-2.0', 422 | 'libselinux-1.0', 423 | 'libtiff', 424 | 'mif-exception', 425 | 'mpich2', 426 | 'openvpn-openssl-exception', 427 | 'psfrag', 428 | 'psutils', 429 | 'u-boot-exception-2.0', 430 | 'xinetd', 431 | 'xpp', 432 | 'zlib-acknowledgement', 433 | ]; 434 | 435 | module.exports = blacklist; 436 | -------------------------------------------------------------------------------- /license/licenses.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | const licenses = [ 3 | '0BSD', 4 | '389-exception', 5 | 'AAL', 6 | 'ADSL', 7 | 'AFL-1.1', 8 | 'AFL-1.2', 9 | 'AFL-2.0', 10 | 'AFL-2.1', 11 | 'AFL-3.0', 12 | 'AGPL-1.0-only', 13 | 'AGPL-1.0-or-later', 14 | 'AGPL-3.0-only', 15 | 'AGPL-3.0-or-later', 16 | 'AMDPLPA', 17 | 'AML', 18 | 'AMPAS', 19 | 'ANTLR-PD', 20 | 'APAFML', 21 | 'APL-1.0', 22 | 'APSL-1.0', 23 | 'APSL-1.1', 24 | 'APSL-1.2', 25 | 'APSL-2.0', 26 | 'Abstyles', 27 | 'Adobe-2006', 28 | 'Adobe-Glyph', 29 | 'Afmparse', 30 | 'Aladdin', 31 | 'Apache-1.0', 32 | 'Apache-1.1', 33 | 'Apache-2.0', 34 | 'Artistic-1.0-Perl', 35 | 'Artistic-1.0-cl8', 36 | 'Artistic-1.0', 37 | 'Artistic-2.0', 38 | 'Autoconf-exception-2.0', 39 | 'Autoconf-exception-3.0', 40 | 'BSD-1-Clause', 41 | 'BSD-2-Clause-FreeBSD', 42 | 'BSD-2-Clause-NetBSD', 43 | 'BSD-2-Clause-Patent', 44 | 'BSD-2-Clause', 45 | 'BSD-3-Clause-Attribution', 46 | 'BSD-3-Clause-Clear', 47 | 'BSD-3-Clause-LBNL', 48 | 'BSD-3-Clause-No-Nuclear-License-2014', 49 | 'BSD-3-Clause-No-Nuclear-License', 50 | 'BSD-3-Clause-No-Nuclear-Warranty', 51 | 'BSD-3-Clause-Open-MPI', 52 | 'BSD-3-Clause', 53 | 'BSD-4-Clause-UC', 54 | 'BSD-4-Clause', 55 | 'BSD-Protection', 56 | 'BSD-Source-Code', 57 | 'BSL-1.0', 58 | 'Bahyph', 59 | 'Barr', 60 | 'Beerware', 61 | 'Bison-exception-2.2', 62 | 'BitTorrent-1.0', 63 | 'BitTorrent-1.1', 64 | 'BlueOak-1.0.0', 65 | 'Bootloader-exception', 66 | 'Borceux', 67 | 'CATOSL-1.1', 68 | 'CC-BY-1.0', 69 | 'CC-BY-2.0', 70 | 'CC-BY-2.5', 71 | 'CC-BY-3.0', 72 | 'CC-BY-4.0', 73 | 'CC-BY-NC-1.0', 74 | 'CC-BY-NC-2.0', 75 | 'CC-BY-NC-2.5', 76 | 'CC-BY-NC-3.0', 77 | 'CC-BY-NC-4.0', 78 | 'CC-BY-NC-ND-1.0', 79 | 'CC-BY-NC-ND-2.0', 80 | 'CC-BY-NC-ND-2.5', 81 | 'CC-BY-NC-ND-3.0', 82 | 'CC-BY-NC-ND-4.0', 83 | 'CC-BY-NC-SA-1.0', 84 | 'CC-BY-NC-SA-2.0', 85 | 'CC-BY-NC-SA-2.5', 86 | 'CC-BY-NC-SA-3.0', 87 | 'CC-BY-NC-SA-4.0', 88 | 'CC-BY-ND-1.0', 89 | 'CC-BY-ND-2.0', 90 | 'CC-BY-ND-2.5', 91 | 'CC-BY-ND-3.0', 92 | 'CC-BY-ND-4.0', 93 | 'CC-BY-SA-1.0', 94 | 'CC-BY-SA-2.0', 95 | 'CC-BY-SA-2.5', 96 | 'CC-BY-SA-3.0', 97 | 'CC-BY-SA-4.0', 98 | 'CC-PDDC', 99 | 'CC0-1.0', 100 | 'CDDL-1.0', 101 | 'CDDL-1.1', 102 | 'CDLA-Permissive-1.0', 103 | 'CDLA-Sharing-1.0', 104 | 'CECILL-1.0', 105 | 'CECILL-1.1', 106 | 'CECILL-2.0', 107 | 'CECILL-2.1', 108 | 'CECILL-B', 109 | 'CECILL-C', 110 | 'CERN-OHL-1.1', 111 | 'CERN-OHL-1.2', 112 | 'CLISP-exception-2.0', 113 | 'CNRI-Jython', 114 | 'CNRI-Python-GPL-Compatible', 115 | 'CNRI-Python', 116 | 'CPAL-1.0', 117 | 'CPL-1.0', 118 | 'CPOL-1.02', 119 | 'CUA-OPL-1.0', 120 | 'Caldera', 121 | 'ClArtistic', 122 | 'Classpath-exception-2.0', 123 | 'Condor-1.1', 124 | 'Crossword', 125 | 'CrystalStacker', 126 | 'Cube', 127 | 'D-FSL-1.0', 128 | 'DOC', 129 | 'DSDP', 130 | 'DigiRule-FOSS-exception', 131 | 'Dotseqn', 132 | 'ECL-1.0', 133 | 'ECL-2.0', 134 | 'EFL-1.0', 135 | 'EFL-2.0', 136 | 'EPL-1.0', 137 | 'EPL-2.0', 138 | 'EUDatagrid', 139 | 'EUPL-1.0', 140 | 'EUPL-1.1', 141 | 'EUPL-1.2', 142 | 'Entessa', 143 | 'ErlPL-1.1', 144 | 'Eurosym', 145 | 'FLTK-exception', 146 | 'FSFAP', 147 | 'FSFUL', 148 | 'FSFULLR', 149 | 'FTL', 150 | 'Fair', 151 | 'Fawkes-Runtime-exception', 152 | 'Font-exception-2.0', 153 | 'Frameworx-1.0', 154 | 'FreeImage', 155 | 'GCC-exception-2.0', 156 | 'GCC-exception-3.1', 157 | 'GFDL-1.1-only', 158 | 'GFDL-1.1-or-later', 159 | 'GFDL-1.2-only', 160 | 'GFDL-1.2-or-later', 161 | 'GFDL-1.3-only', 162 | 'GFDL-1.3-or-later', 163 | 'GL2PS', 164 | 'GPL-1.0-only', 165 | 'GPL-1.0-or-later', 166 | 'GPL-2.0-only', 167 | 'GPL-2.0-or-later', 168 | 'GPL-3.0-linking-exception', 169 | 'GPL-3.0-linking-source-exception', 170 | 'GPL-3.0-only', 171 | 'GPL-3.0-or-later', 172 | 'GPL-CC-1.0', 173 | 'Giftware', 174 | 'Glide', 175 | 'Glulxe', 176 | 'HPND-sell-variant', 177 | 'HPND', 178 | 'HaskellReport', 179 | 'IBM-pibs', 180 | 'ICU', 181 | 'IJG', 182 | 'IPA', 183 | 'IPL-1.0', 184 | 'ISC', 185 | 'ImageMagick', 186 | 'Imlib2', 187 | 'Info-ZIP', 188 | 'Intel-ACPI', 189 | 'Intel', 190 | 'Interbase-1.0', 191 | 'JPNIC', 192 | 'JSON', 193 | 'JasPer-2.0', 194 | 'LAL-1.2', 195 | 'LAL-1.3', 196 | 'LGPL-2.0-only', 197 | 'LGPL-2.0-or-later', 198 | 'LGPL-2.1-only', 199 | 'LGPL-2.1-or-later', 200 | 'LGPL-3.0-only', 201 | 'LGPL-3.0-or-later', 202 | 'LGPLLR', 203 | 'LLVM-exception', 204 | 'LPL-1.0', 205 | 'LPL-1.02', 206 | 'LPPL-1.0', 207 | 'LPPL-1.1', 208 | 'LPPL-1.2', 209 | 'LPPL-1.3a', 210 | 'LPPL-1.3c', 211 | 'LZMA-exception', 212 | 'Latex2e', 213 | 'Leptonica', 214 | 'LiLiQ-P-1.1', 215 | 'LiLiQ-R-1.1', 216 | 'LiLiQ-Rplus-1.1', 217 | 'Libpng', 218 | 'Libtool-exception', 219 | 'Linux-OpenIB', 220 | 'Linux-syscall-note', 221 | 'MIT-0', 222 | 'MIT-CMU', 223 | 'MIT-advertising', 224 | 'MIT-enna', 225 | 'MIT-feh', 226 | 'MIT', 227 | 'MITNFA', 228 | 'MPL-1.0', 229 | 'MPL-1.1', 230 | 'MPL-2.0-no-copyleft-exception', 231 | 'MPL-2.0', 232 | 'MS-PL', 233 | 'MS-RL', 234 | 'MTLL', 235 | 'MakeIndex', 236 | 'MirOS', 237 | 'Motosoto', 238 | 'MulanPSL-1.0', 239 | 'Multics', 240 | 'Mup', 241 | 'NASA-1.3', 242 | 'NBPL-1.0', 243 | 'NCSA', 244 | 'NGPL', 245 | 'NLOD-1.0', 246 | 'NLPL', 247 | 'NOSL', 248 | 'NPL-1.0', 249 | 'NPL-1.1', 250 | 'NPOSL-3.0', 251 | 'NRL', 252 | 'NTP-0', 253 | 'NTP', 254 | 'Naumen', 255 | 'Net-SNMP', 256 | 'NetCDF', 257 | 'Newsletr', 258 | 'Nokia-Qt-exception-1.1', 259 | 'Nokia', 260 | 'Noweb', 261 | 'OCCT-PL', 262 | 'OCCT-exception-1.0', 263 | 'OCLC-2.0', 264 | 'OCaml-LGPL-linking-exception', 265 | 'ODC-By-1.0', 266 | 'ODbL-1.0', 267 | 'OFL-1.0-RFN', 268 | 'OFL-1.0-no-RFN', 269 | 'OFL-1.0', 270 | 'OFL-1.1-RFN', 271 | 'OFL-1.1-no-RFN', 272 | 'OFL-1.1', 273 | 'OGL-Canada-2.0', 274 | 'OGL-UK-1.0', 275 | 'OGL-UK-2.0', 276 | 'OGL-UK-3.0', 277 | 'OGTSL', 278 | 'OLDAP-1.1', 279 | 'OLDAP-1.2', 280 | 'OLDAP-1.3', 281 | 'OLDAP-1.4', 282 | 'OLDAP-2.0.1', 283 | 'OLDAP-2.0', 284 | 'OLDAP-2.1', 285 | 'OLDAP-2.2.1', 286 | 'OLDAP-2.2.2', 287 | 'OLDAP-2.2', 288 | 'OLDAP-2.3', 289 | 'OLDAP-2.4', 290 | 'OLDAP-2.5', 291 | 'OLDAP-2.6', 292 | 'OLDAP-2.7', 293 | 'OLDAP-2.8', 294 | 'OML', 295 | 'OPL-1.0', 296 | 'OSET-PL-2.1', 297 | 'OSL-1.0', 298 | 'OSL-1.1', 299 | 'OSL-2.0', 300 | 'OSL-2.1', 301 | 'OSL-3.0', 302 | 'OpenJDK-assembly-exception-1.0', 303 | 'OpenSSL', 304 | 'PDDL-1.0', 305 | 'PHP-3.0', 306 | 'PHP-3.01', 307 | 'PS-or-PDF-font-exception-20170817', 308 | 'PSF-2.0', 309 | 'Parity-6.0.0', 310 | 'Plexus', 311 | 'PostgreSQL', 312 | 'Python-2.0', 313 | 'QPL-1.0', 314 | 'Qhull', 315 | 'Qt-GPL-exception-1.0', 316 | 'Qt-LGPL-exception-1.1', 317 | 'Qwt-exception-1.0', 318 | 'RHeCos-1.1', 319 | 'RPL-1.1', 320 | 'RPL-1.5', 321 | 'RPSL-1.0', 322 | 'RSA-MD', 323 | 'RSCPL', 324 | 'Rdisc', 325 | 'Ruby', 326 | 'SAX-PD', 327 | 'SCEA', 328 | 'SGI-B-1.0', 329 | 'SGI-B-1.1', 330 | 'SGI-B-2.0', 331 | 'SHL-0.5', 332 | 'SHL-0.51', 333 | 'SISSL-1.2', 334 | 'SISSL', 335 | 'SMLNJ', 336 | 'SMPPL', 337 | 'SNIA', 338 | 'SPL-1.0', 339 | 'SSH-OpenSSH', 340 | 'SSH-short', 341 | 'SSPL-1.0', 342 | 'SWL', 343 | 'Saxpath', 344 | 'Sendmail-8.23', 345 | 'Sendmail', 346 | 'SimPL-2.0', 347 | 'Sleepycat', 348 | 'Spencer-86', 349 | 'Spencer-94', 350 | 'Spencer-99', 351 | 'SugarCRM-1.1.3', 352 | 'Swift-exception', 353 | 'TAPR-OHL-1.0', 354 | 'TCL', 355 | 'TCP-wrappers', 356 | 'TMate', 357 | 'TORQUE-1.1', 358 | 'TOSL', 359 | 'TU-Berlin-1.0', 360 | 'TU-Berlin-2.0', 361 | 'UCL-1.0', 362 | 'UPL-1.0', 363 | 'Unicode-DFS-2015', 364 | 'Unicode-DFS-2016', 365 | 'Unicode-TOU', 366 | 'Universal-FOSS-exception-1.0', 367 | 'Unlicense', 368 | 'VOSTROM', 369 | 'VSL-1.0', 370 | 'Vim', 371 | 'W3C-19980720', 372 | 'W3C-20150513', 373 | 'W3C', 374 | 'WTFPL', 375 | 'Watcom-1.0', 376 | 'Wsuipa', 377 | 'WxWindows-exception-3.1', 378 | 'X11', 379 | 'XFree86-1.1', 380 | 'XSkat', 381 | 'Xerox', 382 | 'Xnet', 383 | 'YPL-1.0', 384 | 'YPL-1.1', 385 | 'ZPL-1.1', 386 | 'ZPL-2.0', 387 | 'ZPL-2.1', 388 | 'Zed', 389 | 'Zend-2.0', 390 | 'Zimbra-1.3', 391 | 'Zimbra-1.4', 392 | 'Zlib', 393 | 'blessing', 394 | 'bzip2-1.0.5', 395 | 'bzip2-1.0.6', 396 | 'copyleft-next-0.3.0', 397 | 'copyleft-next-0.3.1', 398 | 'curl', 399 | 'deprecated_AGPL-1.0', 400 | 'deprecated_AGPL-3.0', 401 | 'deprecated_GFDL-1.1', 402 | 'deprecated_GFDL-1.2', 403 | 'deprecated_GFDL-1.3', 404 | 'deprecated_GPL-1.0+', 405 | 'deprecated_GPL-1.0', 406 | 'deprecated_GPL-2.0+', 407 | 'deprecated_GPL-2.0-with-GCC-exception', 408 | 'deprecated_GPL-2.0-with-autoconf-exception', 409 | 'deprecated_GPL-2.0-with-bison-exception', 410 | 'deprecated_GPL-2.0-with-classpath-exception', 411 | 'deprecated_GPL-2.0-with-font-exception', 412 | 'deprecated_GPL-2.0', 413 | 'deprecated_GPL-3.0+', 414 | 'deprecated_GPL-3.0-with-GCC-exception', 415 | 'deprecated_GPL-3.0-with-autoconf-exception', 416 | 'deprecated_GPL-3.0', 417 | 'deprecated_LGPL-2.0+', 418 | 'deprecated_LGPL-2.0', 419 | 'deprecated_LGPL-2.1+', 420 | 'deprecated_LGPL-2.1', 421 | 'deprecated_LGPL-3.0+', 422 | 'deprecated_LGPL-3.0', 423 | 'deprecated_Nunit', 424 | 'deprecated_StandardML-NJ', 425 | 'deprecated_eCos-2.0', 426 | 'deprecated_wxWindows', 427 | 'diffmark', 428 | 'dvipdfm', 429 | 'eCos-exception-2.0', 430 | 'eGenix', 431 | 'etalab-2.0', 432 | 'freertos-exception-2.0', 433 | 'gSOAP-1.3b', 434 | 'gnu-javamail-exception', 435 | 'gnuplot', 436 | 'i2p-gpl-java-exception', 437 | 'iMatix', 438 | 'libpng-2.0', 439 | 'libselinux-1.0', 440 | 'libtiff', 441 | 'mif-exception', 442 | 'mpich2', 443 | 'openvpn-openssl-exception', 444 | 'psfrag', 445 | 'psutils', 446 | 'u-boot-exception-2.0', 447 | 'xinetd', 448 | 'xpp', 449 | 'zlib-acknowledgement', 450 | ]; 451 | -------------------------------------------------------------------------------- /license/whitelist.js: -------------------------------------------------------------------------------- 1 | /* Provide a list of whitelisted licenses for the project below */ 2 | const whitelist = [ 3 | '(MIT AND BSD-3-Clause)', 4 | '(MIT AND CC-BY-3.0)', 5 | '(MIT AND Zlib)', 6 | '(MIT OR Apache-2.0)', 7 | '(MIT OR CC0-1.0)', 8 | '(WTFPL OR MIT)', 9 | '0BSD', 10 | 'AFLv2.1', 11 | 'Apache-2.0', 12 | 'Apache2', 13 | 'Artistic-2.0', 14 | 'BSD-2-Clause', 15 | 'BSD-3-Clause OR MIT', 16 | 'BSD-3-Clause', 17 | 'CC-BY-3.0', 18 | 'CC-BY-4.0', 19 | 'CC0-1.0', 20 | 'ISC', 21 | 'MIT', 22 | 'MIT,Apache2', 23 | 'MPL-1.0', 24 | 'MPL-1.1', 25 | 'MPL-2.0-no-copyleft-exception', 26 | 'MPL-2.0', 27 | 'ODC-By-1.0', 28 | 'Unlicense', 29 | 'WTFPL', 30 | 'Zlib', 31 | ]; 32 | 33 | module.exports = whitelist; 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "docs-www", 5 | "react-carousel" 6 | ], 7 | "devDependencies": { 8 | "@brainhubeu/license-auditor": "^1.0.30", 9 | "@commitlint/cli": "^9.1.1", 10 | "@commitlint/config-conventional": "^9.1.1", 11 | "@commitlint/prompt": "^9.1.1", 12 | "@semantic-release/commit-analyzer": "^8.0.1", 13 | "@semantic-release/changelog": "^5.0.1", 14 | "@semantic-release/git": "^9.0.0", 15 | "@semantic-release/github": "^7.0.7", 16 | "@semantic-release/release-notes-generator": "^9.0.1", 17 | "@semantic-release/npm": "^7.0.5", 18 | "commitizen": "^4.1.2", 19 | "cypress": "^4.9.0", 20 | "danger": "^10.2.1", 21 | "eslint": "^6.8.0", 22 | "eslint-config-brainhub": "^1.12.0", 23 | "eslint-config-prettier": "^6.11.0", 24 | "eslint-plugin-babel": "^5.3.1", 25 | "eslint-plugin-cypress": "^2.10.3", 26 | "eslint-plugin-prettier": "^3.1.4", 27 | "eslint-watch": "^6.0.1", 28 | "husky": "^4.2.5", 29 | "lint-staged": "^10.2.11", 30 | "prettier": "^2.0.5", 31 | "semantic-release": "^17.1.1", 32 | "start-server-and-test": "^1.11.0" 33 | }, 34 | "scripts": { 35 | "preinstall": "node -e \"if (process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('Use yarn for installing: https://yarnpkg.com/en/docs/install')\"", 36 | "start-demo": "yarn workspace react-carousel-docs develop", 37 | "format": "prettier --write \"./**/*.{js,json}\"", 38 | "lint": "eslint docs-www/ react-carousel/ cypress/ --color", 39 | "build": "yarn workspace @brainhubeu/react-carousel build", 40 | "test:e2e": "start-server-and-test start-demo http://localhost:8000 cypress:run", 41 | "test:unit:coverage": "yarn workspace @brainhubeu/react-carousel test:coverage", 42 | "test-dockerized:e2e": "docker-compose -f ./docker-compose.e2e.yml up --build --exit-code-from e2e-test", 43 | "cypress:run": "yarn cypress run", 44 | "commit": "yarn git-cz" 45 | }, 46 | "husky": { 47 | "hooks": { 48 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", 49 | "pre-commit": "lint-staged", 50 | "pre-push": "yarn test:unit:coverage" 51 | } 52 | }, 53 | "lint-staged": { 54 | "**/*.+(js|jsx)": [ 55 | "yarn format", 56 | "yarn lint --fix" 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /react-carousel/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | "@babel/plugin-proposal-class-properties" 8 | ], 9 | "env": { 10 | "production": { 11 | "plugins": [ 12 | "transform-react-constant-elements", 13 | "transform-react-remove-prop-types" 14 | ] 15 | }, 16 | "test": { 17 | "plugins": ["@babel/plugin-transform-runtime"] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /react-carousel/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [2.0.0-rc.2](https://github.com/brainhubeu/react-carousel/compare/v2.0.0-rc.1...v2.0.0-rc.2) (2020-07-24) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * setting offset breaks "centered" plugin [#394](https://github.com/brainhubeu/react-carousel/issues/394) ([#618](https://github.com/brainhubeu/react-carousel/issues/618)) ([cfae59f](https://github.com/brainhubeu/react-carousel/commit/cfae59f46609b26441ceba0d910b1ef02c1f1c5c)) 7 | -------------------------------------------------------------------------------- /react-carousel/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2020 [Brainhub](https://brainhub.eu/?utm_source=github) 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 | -------------------------------------------------------------------------------- /react-carousel/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'jsdom', 3 | testMatch: ['/test/**/**.test.js'], 4 | moduleNameMapper: { 5 | '\\.(css|scss)$': '/test/__mocks__/styleMock.js', 6 | }, 7 | setupFilesAfterEnv: ['/jest.setup.js'], 8 | }; 9 | -------------------------------------------------------------------------------- /react-carousel/jest.setup.js: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | 4 | configure({ adapter: new Adapter() }); 5 | -------------------------------------------------------------------------------- /react-carousel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@brainhubeu/react-carousel", 3 | "version": "2.0.4", 4 | "description": "Carousel component for React", 5 | "engines": { 6 | "npm": ">=6.14.3" 7 | }, 8 | "files": [ 9 | "/lib/*" 10 | ], 11 | "main": "./lib/react-carousel.js", 12 | "scripts": { 13 | "lint": "esw webpack.config.* src tools test --color", 14 | "clean-dist": "rimraf ./lib && mkdir lib", 15 | "prebuild": "npm run clean-dist", 16 | "build": "node tools/build.js", 17 | "prepack": "cp ../LICENSE.md ../README.md ../docs/CHANGELOG.md .", 18 | "test": "jest", 19 | "test:coverage": "yarn jest --coverage" 20 | }, 21 | "author": "Brainhub", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "@babel/cli": "^7.8.4", 25 | "@babel/core": "^7.8.7", 26 | "@babel/plugin-proposal-class-properties": "^7.8.3", 27 | "@babel/plugin-transform-runtime": "^7.10.4", 28 | "@babel/polyfill": "^7.8.7", 29 | "@babel/preset-env": "^7.8.7", 30 | "@babel/preset-react": "^7.8.3", 31 | "@babel/register": "^7.8.6", 32 | "autoprefixer": "^9.7.4", 33 | "babel-eslint": "^10.1.0", 34 | "babel-loader": "^8.0.6", 35 | "babel-plugin-transform-react-constant-elements": "^6.23.0", 36 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24", 37 | "chalk": "^3.0.0", 38 | "classnames": "^2.2.6", 39 | "coveralls": "^3.1.0", 40 | "css-loader": "^3.4.2", 41 | "enzyme": "^3.11.0", 42 | "enzyme-adapter-react-16": "^1.15.2", 43 | "extract-text-webpack-plugin": "^4.0.0-beta.0", 44 | "file-loader": "^5.1.0", 45 | "html-webpack-plugin": "^3.2.0", 46 | "husky": "^4.2.3", 47 | "jest": "^26.1.0", 48 | "jest-enzyme": "^7.1.2", 49 | "jsdom": "^16.2.1", 50 | "lodash": "^4.17.21", 51 | "node-sass": "^4.13.1", 52 | "postcss-loader": "^3.0.0", 53 | "prop-types": "^15.7.2", 54 | "react-resize-detector": "^5.0.6", 55 | "react-test-renderer": "^16.13.1", 56 | "recoil": "^0.0.13", 57 | "resize-observer-polyfill": "^1.5.1", 58 | "rimraf": "3.0.2", 59 | "sass-loader": "^8.0.2", 60 | "style-loader": "^1.1.3", 61 | "terser-webpack-plugin": "^3.0.6", 62 | "url-loader": "^3.0.0", 63 | "webpack": "^4.42.0" 64 | }, 65 | "peerDependencies": { 66 | "react": "^16.8.0", 67 | "react-dom": "^16.8.0" 68 | }, 69 | "keywords": [ 70 | "react", 71 | "carousel", 72 | "react-carousel", 73 | "react-component", 74 | "component", 75 | "image", 76 | "image-gallery", 77 | "image-slider", 78 | "responsive", 79 | "gallery", 80 | "rwd" 81 | ], 82 | "repository": { 83 | "type": "git", 84 | "url": "https://github.com/brainhubeu/react-carousel" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /react-carousel/src/components/CarouselDots.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classnames from 'classnames'; 4 | import '../styles/CarouselDots.scss'; 5 | 6 | const CarouselDots = ({ 7 | value, 8 | thumbnails, 9 | number, 10 | onChange, 11 | rtl, 12 | className, 13 | }) => { 14 | const calculateButtonValue = () => { 15 | const numberOfSlides = number || thumbnails.length; 16 | return value >= 0 17 | ? value 18 | : value + numberOfSlides * Math.ceil(Math.abs(value / numberOfSlides)); 19 | }; 20 | 21 | const onDotClick = (index) => () => { 22 | const numberOfSlides = number || thumbnails.length; 23 | const moduloItem = calculateButtonValue() % numberOfSlides; 24 | 25 | return onChange(value - (moduloItem - index)); 26 | }; 27 | 28 | const renderCarouselDots = () => { 29 | if (thumbnails) { 30 | const dotsLength = isNaN(number) ? thumbnails.length : number; 31 | 32 | return thumbnails.slice(0, dotsLength).map((thumbnail, index) => ( 33 |
  • 34 |
    42 | {thumbnail} 43 |
    44 |
  • 45 | )); 46 | } 47 | 48 | const dots = []; 49 | for (let i = 0; i < number; i++) { 50 | dots.push( 51 |
  • 52 |
    60 | {i + 1} 61 |
    62 |
  • , 63 | ); 64 | } 65 | return dots; 66 | }; 67 | 68 | return ( 69 |
      76 | {renderCarouselDots()} 77 |
    78 | ); 79 | }; 80 | 81 | CarouselDots.propTypes = { 82 | number: PropTypes.number, 83 | thumbnails: PropTypes.arrayOf(PropTypes.node), 84 | value: PropTypes.number, 85 | onChange: PropTypes.func, 86 | rtl: PropTypes.bool, 87 | className: PropTypes.string, 88 | }; 89 | 90 | export default React.memo(CarouselDots); 91 | -------------------------------------------------------------------------------- /react-carousel/src/components/CarouselSlide.js: -------------------------------------------------------------------------------- 1 | import React, { memo, useEffect, useRef } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classname from 'classnames'; 4 | import ResizeObserver from 'resize-observer-polyfill'; 5 | 6 | import '../styles/CarouselItem.scss'; 7 | 8 | const CarouselSlide = ({ 9 | index, 10 | onMouseDown, 11 | onTouchStart, 12 | currentSlideIndex, 13 | isDraggingEnabled, 14 | itemClassNames, 15 | width, 16 | offset, 17 | isDragging, 18 | children, 19 | }) => { 20 | const childrenRef = useRef(null); 21 | const isInitialMount = useRef(true); 22 | 23 | const resizeChildren = () => { 24 | if (childrenRef.current) { 25 | childrenRef.current.style = null; 26 | if (childrenRef.current.offsetWidth > width) { 27 | childrenRef.current.style.width = `${width}px`; 28 | } 29 | } 30 | }; 31 | 32 | const observeWidth = () => { 33 | const resizeObserver = new ResizeObserver(() => { 34 | resizeChildren(); 35 | childrenRef.current && resizeObserver.unobserve(childrenRef.current); 36 | }); 37 | childrenRef.current && resizeObserver.observe(childrenRef.current); 38 | }; 39 | 40 | const getChildren = () => 41 | childrenRef.current 42 | ? React.cloneElement(children, { ref: childrenRef }) 43 | : children; 44 | 45 | const onItemMouseDown = (event) => { 46 | onMouseDown(event, index); 47 | }; 48 | 49 | const onItemTouchStart = (event) => { 50 | onTouchStart(event, index); 51 | }; 52 | 53 | useEffect(() => { 54 | if (isInitialMount.current) { 55 | isInitialMount.current = false; 56 | } else { 57 | observeWidth(); 58 | } 59 | }, []); 60 | 61 | useEffect(() => { 62 | resizeChildren(); 63 | }, [width]); 64 | 65 | return ( 66 |
  • 85 | {getChildren()} 86 |
  • 87 | ); 88 | }; 89 | 90 | CarouselSlide.propTypes = { 91 | onMouseDown: PropTypes.func, 92 | onTouchStart: PropTypes.func, 93 | clickable: PropTypes.bool, 94 | children: PropTypes.node, 95 | width: PropTypes.number, 96 | offset: PropTypes.number, 97 | index: PropTypes.number, 98 | currentSlideIndex: PropTypes.number, 99 | isDragging: PropTypes.bool, 100 | isDraggingEnabled: PropTypes.bool, 101 | itemClassNames: PropTypes.arrayOf(PropTypes.string), 102 | }; 103 | 104 | export default memo(CarouselSlide); 105 | -------------------------------------------------------------------------------- /react-carousel/src/components/CarouselWrapper.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { useRecoilValue, RecoilRoot, useSetRecoilState } from 'recoil'; 3 | import _isNil from 'lodash/isNil'; 4 | import _omit from 'lodash/omit'; 5 | import PropTypes from 'prop-types'; 6 | 7 | import { 8 | carouselValueState, 9 | transitionEnabledState, 10 | } from '../state/atoms/carouselAtoms'; 11 | import { 12 | getCurrentValueSelector, 13 | nearestSlideSelector, 14 | transformOffsetSelector, 15 | } from '../state/selectors/carouselSelectors'; 16 | 17 | import Carousel from './Carousel'; 18 | 19 | const CarouselWrapper = (props) => { 20 | const changeSlide = useSetRecoilState(getCurrentValueSelector); 21 | const value = useRecoilValue(carouselValueState); 22 | const setTransitionEnabled = useSetRecoilState(transitionEnabledState); 23 | 24 | useEffect(() => { 25 | if (!_isNil(props.value)) { 26 | changeSlide(props.value); 27 | } 28 | }, [props.value, setTransitionEnabled]); 29 | 30 | const { onChange, value: customValue, ...rest } = props; 31 | const transformOffset = useRecoilValue(transformOffsetSelector); 32 | const nearestSlideIndex = useRecoilValue(nearestSlideSelector); 33 | 34 | const carouselProps = Object.entries(rest.breakpoints || {}) 35 | .filter(([resolution]) => window.innerWidth <= resolution) 36 | .sort(([prevRes], [nextRes]) => nextRes - prevRes) 37 | .reduce( 38 | // eslint-disable-next-line no-unused-vars 39 | (prev, [_, props]) => ({ 40 | ...prev, 41 | ...props, 42 | }), 43 | _omit(rest, ['breakpoints']), 44 | ); 45 | 46 | const isControlled = !_isNil(customValue); 47 | return ( 48 | 56 | ); 57 | }; 58 | 59 | CarouselWrapper.propTypes = { 60 | value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 61 | onChange: PropTypes.func, 62 | }; 63 | 64 | const RecoiledComponent = (props) => ( 65 | 66 | 67 | 68 | ); 69 | 70 | export default RecoiledComponent; 71 | -------------------------------------------------------------------------------- /react-carousel/src/constants/carouselStrategies.js: -------------------------------------------------------------------------------- 1 | const CAROUSEL_STRATEGIES = { 2 | CHANGE_SLIDE: 'CHANGE_SLIDE', 3 | GET_CURRENT_VALUE: 'GET_CURRENT_VALUE', 4 | GET_TRANSFORM_OFFSET: 'GET_TRANSFORM_OFFSET', 5 | GET_NEAREST_SLIDE: 'GET_NEAREST_SLIDE', 6 | }; 7 | 8 | export default CAROUSEL_STRATEGIES; 9 | -------------------------------------------------------------------------------- /react-carousel/src/constants/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | resizeEventListenerThrottle: 300, // event listener onResize will not be triggered more frequently than once per given number of miliseconds 3 | clickDragThreshold: 10, 4 | }; 5 | -------------------------------------------------------------------------------- /react-carousel/src/constants/plugins.js: -------------------------------------------------------------------------------- 1 | import pluginsFunc from '../plugins'; 2 | 3 | const { 4 | slidesToShow, 5 | infinite, 6 | clickToChange, 7 | autoplay, 8 | rtl, 9 | centered, 10 | slidesToScroll, 11 | arrows, 12 | fastSwipe, 13 | } = pluginsFunc; 14 | 15 | export const pluginNames = { 16 | SLIDES_TO_SHOW: 'SLIDESTOSHOW', 17 | CLICK_TO_CHANGE: 'CLICKTOCHANGE', 18 | INFINITE: 'INFINITE', 19 | AUTOPLAY: 'AUTOPLAY', 20 | RTL: 'RTL', 21 | CENTERED: 'CENTERED', 22 | SLIDES_TO_SCROLL: 'SLIDESTOSCROLL', 23 | ARROWS: 'ARROWS', 24 | FAST_SWIPE: 'FASTSWIPE', 25 | }; 26 | 27 | export const plugins = { 28 | [pluginNames.SLIDES_TO_SHOW]: slidesToShow, 29 | [pluginNames.CLICK_TO_CHANGE]: clickToChange, 30 | [pluginNames.INFINITE]: infinite, 31 | [pluginNames.RTL]: rtl, 32 | [pluginNames.AUTOPLAY]: autoplay, 33 | [pluginNames.CENTERED]: centered, 34 | [pluginNames.SLIDES_TO_SCROLL]: slidesToScroll, 35 | [pluginNames.ARROWS]: arrows, 36 | [pluginNames.FAST_SWIPE]: fastSwipe, 37 | }; 38 | -------------------------------------------------------------------------------- /react-carousel/src/constants/pluginsOrder.js: -------------------------------------------------------------------------------- 1 | import { pluginNames } from './plugins'; 2 | 3 | const pluginsOrder = [ 4 | pluginNames.FAST_SWIPE, 5 | pluginNames.ARROWS, 6 | pluginNames.SLIDES_TO_SHOW, 7 | pluginNames.AUTOPLAY, 8 | pluginNames.INFINITE, 9 | pluginNames.RTL, 10 | pluginNames.CLICK_TO_CHANGE, 11 | pluginNames.CENTERED, 12 | pluginNames.SLIDES_TO_SCROLL, 13 | ]; 14 | 15 | export default pluginsOrder; 16 | -------------------------------------------------------------------------------- /react-carousel/src/hooks/useEventListener.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | 3 | function useEventListener(eventName, handler, options = {}, element = window) { 4 | // Create a ref that stores handler 5 | const savedHandler = useRef(); 6 | 7 | // Update ref.current value if handler changes. 8 | // This allows our effect below to always get latest handler ... 9 | // ... without us needing to pass it in effect deps array ... 10 | // ... and potentially cause effect to re-run every render. 11 | useEffect(() => { 12 | savedHandler.current = handler; 13 | }, [handler]); 14 | 15 | useEffect( 16 | () => { 17 | // Make sure element supports addEventListener 18 | // On 19 | const isSupported = element && element.addEventListener; 20 | if (isSupported) { 21 | // Create event listener that calls handler function stored in ref 22 | const eventListener = (event) => savedHandler.current(event); 23 | 24 | // Add event listener 25 | element.addEventListener(eventName, eventListener, options); 26 | 27 | // Remove event listener on cleanup 28 | return () => { 29 | element.removeEventListener(eventName, eventListener); 30 | }; 31 | } 32 | }, 33 | [eventName, element], // Re-run if eventName or element changes 34 | ); 35 | } 36 | 37 | export default useEventListener; 38 | -------------------------------------------------------------------------------- /react-carousel/src/hooks/useOnResize.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | import _throttle from 'lodash/throttle'; 3 | 4 | import config from '../constants/config'; 5 | 6 | /** 7 | * hook setting the carouselWidth value in state (used to set proper width of track and slides) 8 | * throttled to improve performance 9 | * 10 | * @param {node} carouselRef 11 | * @param {number} itemWidth 12 | * @param {function} setItemWidth 13 | * @param {node} trackContainerRef 14 | */ 15 | const useOnResize = ({ 16 | width, 17 | carouselRef, 18 | setItemWidth, 19 | trackContainerRef, 20 | }) => { 21 | const isInitialMount = useRef(true); 22 | const onResize = _throttle(() => { 23 | if (!carouselRef || !trackContainerRef) { 24 | return; 25 | } 26 | 27 | setItemWidth(trackContainerRef.current.offsetWidth); 28 | }, config.resizeEventListenerThrottle); 29 | 30 | useEffect(() => { 31 | if (isInitialMount.current) { 32 | isInitialMount.current = false; 33 | } else { 34 | onResize(); 35 | } 36 | }, [width, trackContainerRef.current]); 37 | }; 38 | 39 | export default useOnResize; 40 | -------------------------------------------------------------------------------- /react-carousel/src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-namespace */ 2 | import Carousel from './components/CarouselWrapper'; 3 | import CarouselDots from './components/CarouselDots'; 4 | import slidesToShow from './plugins/slidesToShow'; 5 | import infinite from './plugins/infinite'; 6 | import clickToChange from './plugins/clickToChange'; 7 | import autoplay from './plugins/autoplay'; 8 | import rtl from './plugins/rtl'; 9 | import centered from './plugins/centered'; 10 | import slidesToScroll from './plugins/slidesToScroll'; 11 | import arrows from './plugins/arrows'; 12 | import fastSwipe from './plugins/fastSwipe'; 13 | 14 | export const slidesToShowPlugin = slidesToShow; 15 | export const infinitePlugin = infinite; 16 | export const clickToChangePlugin = clickToChange; 17 | export const autoplayPlugin = autoplay; 18 | export const rtlPlugin = rtl; 19 | export const centeredPlugin = centered; 20 | export const slidesToScrollPlugin = slidesToScroll; 21 | export const arrowsPlugin = arrows; 22 | export const fastSwipePlugin = fastSwipe; 23 | 24 | export const Dots = CarouselDots; 25 | export default Carousel; 26 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/arrows.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react'; 2 | import { useRecoilValue } from 'recoil'; 3 | import classnames from 'classnames'; 4 | 5 | import { pluginNames } from '../constants/plugins'; 6 | import { slidesState } from '../state/atoms/slideAtoms'; 7 | 8 | import './arrows.scss'; 9 | 10 | /** 11 | * Adds onClick handler to the arrow if possible (if it does not already have one) 12 | * @param {ReactElement} element to render 13 | * @param {function} onClick handler to be added to element 14 | * @param {string} name of an element 15 | * @param {boolean} addArrowClickHandler decide whether to add action to onClick 16 | * @param {string} key unique element key 17 | * @param {boolean} disable info whether the arrow is disabled 18 | * @return {ReactElement} element with added handler 19 | */ 20 | const renderArrowWithAddedHandler = ( 21 | element, 22 | onClick, 23 | name, 24 | addArrowClickHandler, 25 | key, 26 | disable = false, 27 | ) => ( 28 |
    39 | {element} 40 |
    41 | ); 42 | 43 | const arrows = ({ carouselProps, options = {} }) => ({ 44 | name: pluginNames.ARROWS, 45 | // eslint-disable-next-line react/display-name 46 | beforeCarouselItems: () => { 47 | const slides = useRecoilValue(slidesState); 48 | 49 | const prevSlide = useCallback( 50 | () => carouselProps.onChange(carouselProps.value - 1), 51 | [carouselProps.value, carouselProps.onChange], 52 | ); 53 | 54 | const disabled = 55 | carouselProps.value <= 0 && 56 | carouselProps?.children?.length === slides.length; 57 | 58 | if (options.arrowLeft) { 59 | if (!disabled) { 60 | return renderArrowWithAddedHandler( 61 | options.arrowLeft, 62 | prevSlide, 63 | 'arrowLeft', 64 | options.addArrowClickHandler, 65 | '@brainhubeu/react-carousel/custom-arrow-left', 66 | ); 67 | } 68 | const arrow = options.arrowLeftDisabled 69 | ? options.arrowLeftDisabled 70 | : options.arrowLeft; 71 | return renderArrowWithAddedHandler( 72 | arrow, 73 | prevSlide, 74 | 'arrowLeft', 75 | options.addArrowClickHandler, 76 | '@brainhubeu/react-carousel/custom-arrow-left', 77 | disabled, 78 | ); 79 | } 80 | return ( 81 | 89 | ); 90 | }, 91 | // eslint-disable-next-line react/display-name 92 | afterCarouselItems: () => { 93 | const slides = useRecoilValue(slidesState); 94 | 95 | const nextSlide = useCallback( 96 | () => carouselProps.onChange(carouselProps.value + 1), 97 | [carouselProps.value, carouselProps.onChange], 98 | ); 99 | 100 | const disabled = 101 | carouselProps.value >= slides.length - 1 && 102 | carouselProps?.children?.length === slides.length; 103 | 104 | if (options.arrowRight) { 105 | if (!disabled) { 106 | return renderArrowWithAddedHandler( 107 | options.arrowRight, 108 | nextSlide, 109 | 'arrowLeft', 110 | options.addArrowClickHandler, 111 | '@brainhubeu/react-carousel/custom-arrow-right', 112 | ); 113 | } 114 | const arrow = options.arrowRightDisabled 115 | ? options.arrowRightDisabled 116 | : options.arrowRight; 117 | return renderArrowWithAddedHandler( 118 | arrow, 119 | nextSlide, 120 | 'arrowLeft', 121 | options.addArrowClickHandler, 122 | '@brainhubeu/react-carousel/custom-arrow-right', 123 | disabled, 124 | ); 125 | } 126 | return ( 127 | 135 | ); 136 | }, 137 | }); 138 | 139 | export default arrows; 140 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/arrows.scss: -------------------------------------------------------------------------------- 1 | 2 | $clickableAreaSize: 42px; 3 | $clickableAreaColor: #7b59ff; 4 | $clickableAreaColor__hover: lighten(#7b59ff, 3%); 5 | $disabledButtonColor: #cccccc; 6 | 7 | $arrowColor: #fff; 8 | $arrowColor__hover: #fff; 9 | $arrowSize: 10px; 10 | $arrowWeight: 3px; 11 | 12 | .BrainhubCarousel__arrows { 13 | position: relative; 14 | padding: $clickableAreaSize/2; 15 | border: none; 16 | overflow: hidden; 17 | outline: 0; 18 | font-size: 0; 19 | line-height: 0; 20 | background-color: $clickableAreaColor; 21 | 22 | span { 23 | display: block; 24 | position: absolute; 25 | top: 50%; 26 | left: 50%; 27 | border-style: solid; 28 | border-color: $arrowColor; 29 | border-width: $arrowWeight $arrowWeight 0 0; 30 | padding: $arrowSize/2; 31 | transition: 0.3s; 32 | font-size: 0; 33 | } 34 | 35 | &:hover { 36 | background-color: $clickableAreaColor__hover; 37 | span { 38 | display: block; 39 | position: absolute; 40 | top: 50%; 41 | left: 50%; 42 | border-style: solid; 43 | border-color: $arrowColor; 44 | border-width: $arrowWeight $arrowWeight 0 0; 45 | padding: $arrowSize/2; 46 | transition: 0.3s; 47 | font-size: 0; 48 | } 49 | 50 | &:enabled { 51 | background-color: $clickableAreaColor__hover; 52 | span { 53 | border-color: $arrowColor__hover; 54 | margin: 0; 55 | } 56 | } 57 | } 58 | 59 | &:disabled { 60 | background-color: $disabledButtonColor; 61 | } 62 | } 63 | 64 | .BrainhubCarousel__arrowLeft { 65 | span { 66 | transform: translate(-50%, -50%) rotate(-135deg); 67 | margin-left: 0.35 * ($arrowSize - $arrowWeight); 68 | } 69 | } 70 | 71 | .BrainhubCarousel__arrowRight { 72 | span { 73 | transform: translate(-50%, -50%) rotate(45deg); 74 | margin-left: -0.35 * ($arrowSize - $arrowWeight); 75 | } 76 | } 77 | 78 | .BrainhubCarousel--isRTL { 79 | .BrainhubCarousel__arrowLeft { 80 | span { 81 | transform: translate(-50%, -50%) rotate(45deg); 82 | margin-left: -0.35 * ($arrowSize - $arrowWeight); 83 | } 84 | } 85 | 86 | .BrainhubCarousel__custom-arrowLeft { 87 | span { 88 | transform: rotate(180deg); 89 | } 90 | } 91 | 92 | .BrainhubCarousel__arrowRight { 93 | span { 94 | transform: translate(-50%, -50%) rotate(-135deg); 95 | margin-left: 0.35 * ($arrowSize - $arrowWeight); 96 | } 97 | } 98 | 99 | .BrainhubCarousel__custom-arrowRight { 100 | span { 101 | transform: rotate(-180deg); 102 | } 103 | } 104 | 105 | .BrainhubCarousel__arrows { 106 | &:hover { 107 | span { 108 | margin: 0; 109 | } 110 | } 111 | } 112 | } 113 | 114 | .BrainhubCarousel__arrow--disable { 115 | pointer-events: none; 116 | } 117 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/autoplay.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { useSetRecoilState } from 'recoil'; 3 | 4 | import { pluginNames } from '../constants/plugins'; 5 | import { getCurrentValueSelector } from '../state/selectors/carouselSelectors'; 6 | 7 | let interval = null; 8 | 9 | const DIRECTION = { 10 | LEFT: 'LEFT', 11 | RIGHT: 'RIGHT', 12 | }; 13 | 14 | const getDirection = (direction) => { 15 | if (direction === DIRECTION.LEFT) { 16 | return -1; 17 | } else if (direction === DIRECTION.RIGHT) { 18 | return 1; 19 | } 20 | return 0; 21 | }; 22 | 23 | const defaultOptions = { 24 | interval: 2000, 25 | stopAutoPlayOnHover: true, 26 | direction: DIRECTION.RIGHT, 27 | }; 28 | 29 | const autoplay = ({ carouselProps, options = {} }) => { 30 | const pluginOptions = { ...defaultOptions, ...options }; 31 | 32 | return { 33 | name: pluginNames.AUTOPLAY, 34 | trackCustomProps: () => { 35 | const changeSlide = useSetRecoilState(getCurrentValueSelector); 36 | const [autoPlayStopped, setAutoPlayStopped] = useState(false); 37 | 38 | const resetInterval = () => { 39 | if (interval) { 40 | clearInterval(interval); 41 | } 42 | interval = setInterval(() => { 43 | if (!document.hidden && !autoPlayStopped) { 44 | const newSlide = 45 | carouselProps.value + 46 | getDirection(pluginOptions.direction.toUpperCase()); 47 | carouselProps.onChange(newSlide); 48 | changeSlide(newSlide); 49 | } 50 | }, pluginOptions.interval); 51 | }; 52 | 53 | // setting autoplay interval 54 | resetInterval(); 55 | 56 | /** 57 | * Function handling mouse hover over element 58 | * Stops auto play 59 | */ 60 | const onMouseEnter = () => { 61 | setAutoPlayStopped(true); 62 | }; 63 | 64 | /** 65 | * Function handling mouse leaving element 66 | * Resumes auto play 67 | */ 68 | const onMouseLeave = () => { 69 | setAutoPlayStopped(false); 70 | resetInterval(); 71 | }; 72 | 73 | return { 74 | onMouseEnter: pluginOptions.stopAutoPlayOnHover ? onMouseEnter : null, 75 | onMouseLeave: pluginOptions.stopAutoPlayOnHover ? onMouseLeave : null, 76 | }; 77 | }, 78 | }; 79 | }; 80 | 81 | export default autoplay; 82 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/centered.js: -------------------------------------------------------------------------------- 1 | import { useRecoilValue } from 'recoil'; 2 | 3 | import { pluginNames } from '../constants/plugins'; 4 | import { slideOffsetState, slideWidthState } from '../state/atoms/slideAtoms'; 5 | import CAROUSEL_STRATEGIES from '../constants/carouselStrategies'; 6 | 7 | const centered = ({ refs }) => ({ 8 | name: pluginNames.CENTERED, 9 | strategies: () => { 10 | const itemWidth = useRecoilValue(slideWidthState); 11 | const itemOffset = useRecoilValue(slideOffsetState); 12 | const trackContainerWidth = refs.trackContainerRef?.current?.offsetWidth; 13 | 14 | return { 15 | [CAROUSEL_STRATEGIES.GET_TRANSFORM_OFFSET]: ( 16 | originalValue, 17 | prevValue, 18 | ) => { 19 | const elementWidthWithOffset = itemWidth + itemOffset; 20 | const additionalOffset = 21 | trackContainerWidth / 2 - elementWidthWithOffset / 2; 22 | return prevValue + additionalOffset; 23 | }, 24 | }; 25 | }, 26 | }); 27 | 28 | export default centered; 29 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/clickToChange.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | import { useRecoilState, useSetRecoilState, useRecoilValue } from 'recoil'; 3 | 4 | import CAROUSEL_STRATEGIES from '../constants/carouselStrategies'; 5 | import { pluginNames } from '../constants/plugins'; 6 | import { 7 | slideMovementState, 8 | transitionEnabledState, 9 | } from '../state/atoms/carouselAtoms'; 10 | import { activeSlideIndexState } from '../state/atoms/slideAtoms'; 11 | 12 | let previousClicked = 0; 13 | 14 | const DIRECTION = { 15 | LEFT: 'LEFT', 16 | RIGHT: 'RIGHT', 17 | NONE: 'NONE', 18 | }; 19 | 20 | const getDirection = (dragStart, dragOffset) => { 21 | if (dragStart < dragOffset) { 22 | return DIRECTION.LEFT; 23 | } else if (dragStart > dragOffset) { 24 | return DIRECTION.RIGHT; 25 | } 26 | return DIRECTION.NONE; 27 | }; 28 | 29 | const clickToChange = ({ carouselProps }) => ({ 30 | name: pluginNames.CLICK_TO_CHANGE, 31 | strategies: () => { 32 | const slideMovement = useRecoilValue(slideMovementState); 33 | const activeSlideIndex = useRecoilValue(activeSlideIndexState); 34 | 35 | return { 36 | [CAROUSEL_STRATEGIES.CHANGE_SLIDE]: (originalValue, prevValue) => { 37 | const direction = getDirection( 38 | Math.abs(slideMovement.dragStart), 39 | Math.abs(slideMovement.dragEnd) || 0, 40 | ); 41 | 42 | if (direction === DIRECTION.NONE) { 43 | return prevValue; 44 | } 45 | 46 | if (previousClicked !== slideMovement.clicked) { 47 | if ( 48 | direction === DIRECTION.LEFT && 49 | prevValue <= slideMovement.clicked 50 | ) { 51 | previousClicked = prevValue; 52 | return prevValue; 53 | } 54 | 55 | previousClicked = slideMovement.clicked; 56 | if (activeSlideIndex) { 57 | return ( 58 | carouselProps.value + slideMovement.clicked - activeSlideIndex 59 | ); 60 | } 61 | return slideMovement.clicked; 62 | } 63 | previousClicked = prevValue || originalValue; 64 | return prevValue || originalValue; 65 | }, 66 | }; 67 | }, 68 | slideCustomProps: () => { 69 | const setTransitionEnabled = useSetRecoilState(transitionEnabledState); 70 | const [slideMovement, setSlideMovement] = useRecoilState( 71 | slideMovementState, 72 | ); 73 | 74 | const onMouseDown = useCallback( 75 | (event, index) => { 76 | event.preventDefault(); 77 | event.stopPropagation(); 78 | setTransitionEnabled(true); 79 | const { pageX } = event; 80 | 81 | setSlideMovement({ 82 | ...slideMovement, 83 | clicked: index, 84 | dragStart: pageX, 85 | }); 86 | }, 87 | [slideMovement, setTransitionEnabled], 88 | ); 89 | return { 90 | onMouseDown, 91 | }; 92 | }, 93 | itemClassNames: () => ['BrainhubCarouselItem--clickable'], 94 | }); 95 | 96 | export default clickToChange; 97 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/fastSwipe.js: -------------------------------------------------------------------------------- 1 | import { useRecoilValue } from 'recoil'; 2 | 3 | import { pluginNames } from '../constants/plugins'; 4 | import CAROUSEL_STRATEGIES from '../constants/carouselStrategies'; 5 | import { 6 | carouselValueState, 7 | slideMovementState, 8 | } from '../state/atoms/carouselAtoms'; 9 | 10 | import './rtl.scss'; 11 | 12 | const fastSwipe = ({ carouselProps }) => ({ 13 | name: pluginNames.FAST_SWIPE, 14 | strategies: () => { 15 | const dragOffset = useRecoilValue(slideMovementState).dragOffset; 16 | const value = useRecoilValue(carouselValueState); 17 | 18 | return { 19 | [CAROUSEL_STRATEGIES.GET_NEAREST_SLIDE]: () => { 20 | const slideIndexOffset = 21 | dragOffset > 0 22 | ? -Math.ceil(dragOffset / carouselProps.width) 23 | : -Math.floor(dragOffset / carouselProps.width); 24 | 25 | return value + slideIndexOffset; 26 | }, 27 | }; 28 | }, 29 | }); 30 | 31 | export default fastSwipe; 32 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/index.js: -------------------------------------------------------------------------------- 1 | import slidesToShow from './slidesToShow'; 2 | import infinite from './infinite'; 3 | import clickToChange from './clickToChange'; 4 | import autoplay from './autoplay'; 5 | import rtl from './rtl'; 6 | import centered from './centered'; 7 | import slidesToScroll from './slidesToScroll'; 8 | import arrows from './arrows'; 9 | import fastSwipe from './fastSwipe'; 10 | 11 | export default { 12 | slidesToShow, 13 | infinite, 14 | clickToChange, 15 | autoplay, 16 | rtl, 17 | centered, 18 | slidesToScroll, 19 | arrows, 20 | fastSwipe, 21 | }; 22 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/infinite.js: -------------------------------------------------------------------------------- 1 | import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; 2 | import _isNil from 'lodash/isNil'; 3 | import _times from 'lodash/times'; 4 | import _concat from 'lodash/concat'; 5 | import { useEffect } from 'react'; 6 | 7 | import getChildren from '../tools/getChildren'; 8 | import CAROUSEL_STRATEGIES from '../constants/carouselStrategies'; 9 | import { pluginNames } from '../constants/plugins'; 10 | import { 11 | activeSlideIndexState, 12 | slideWidthState, 13 | slidesState, 14 | } from '../state/atoms/slideAtoms'; 15 | import { 16 | slideMovementState, 17 | trackStylesState, 18 | trackWidthState, 19 | } from '../state/atoms/carouselAtoms'; 20 | 21 | export const defaultOptions = { 22 | numberOfInfiniteClones: 2, 23 | }; 24 | 25 | const infinite = ({ options = defaultOptions, carouselProps }) => { 26 | const itemWidth = useRecoilValue(slideWidthState); 27 | const children = getChildren(carouselProps.children, carouselProps.slides); 28 | 29 | const getTargetMod = (customValue = null) => { 30 | const value = _isNil(customValue) ? carouselProps.value : customValue; 31 | const length = children.length; 32 | 33 | return value >= 0 34 | ? value % length 35 | : (length - Math.abs(value % length)) % length; 36 | }; 37 | 38 | const getTargetSlide = () => { 39 | const mod = getTargetMod(0); 40 | 41 | return mod + carouselProps.value; 42 | }; 43 | 44 | const getNeededAdditionalClones = () => { 45 | if (Math.abs(carouselProps.value) > children.length) { 46 | return Math.ceil(carouselProps.value / children.length); 47 | } 48 | return 0; 49 | }; 50 | 51 | const getAdditionalClonesLeft = () => { 52 | const additionalClones = getNeededAdditionalClones(); 53 | return additionalClones < 0 ? -additionalClones : 0; 54 | }; 55 | 56 | const getAdditionalClonesOffset = () => 57 | -children.length * itemWidth * getAdditionalClonesLeft(); 58 | 59 | return { 60 | name: pluginNames.INFINITE, 61 | plugin: () => { 62 | const setTrackWidth = useSetRecoilState(trackWidthState); 63 | const setActiveSlideIndex = useSetRecoilState(activeSlideIndexState); 64 | const [trackStyles, setTrackStyles] = useRecoilState(trackStylesState); 65 | const setSlides = useSetRecoilState(slidesState); 66 | 67 | const numberOfInfiniteClones = options.numberOfInfiniteClones; 68 | 69 | const getAdditionalClonesRight = () => { 70 | const additionalClones = getNeededAdditionalClones(); 71 | return additionalClones > 0 ? additionalClones : 0; 72 | }; 73 | const getClonesLeft = () => 74 | numberOfInfiniteClones + getAdditionalClonesLeft(); 75 | const getClonesRight = () => 76 | numberOfInfiniteClones + getAdditionalClonesRight(); 77 | 78 | useEffect(() => { 79 | setActiveSlideIndex( 80 | getTargetSlide() + getClonesLeft() * children.length, 81 | ); 82 | 83 | setTrackStyles({ 84 | ...trackStyles, 85 | marginLeft: getAdditionalClonesOffset(), 86 | }); 87 | }, [carouselProps.value]); 88 | 89 | useEffect(() => { 90 | const trackLengthMultiplier = 1 + getClonesLeft() + getClonesRight(); 91 | 92 | const clonesLeft = _times(getClonesLeft(), () => children); 93 | const clonesRight = _times(getClonesRight(), () => children); 94 | 95 | setTrackWidth( 96 | carouselProps.width * children.length * trackLengthMultiplier, 97 | ); 98 | setSlides(_concat(...clonesLeft, children, ...clonesRight)); 99 | }, [carouselProps.width, children.length, carouselProps.value]); 100 | }, 101 | 102 | strategies: () => { 103 | const slideMovement = useRecoilValue(slideMovementState); 104 | const activeSlideIndex = useRecoilValue(activeSlideIndexState); 105 | const slideWidth = useRecoilValue(slideWidthState); 106 | 107 | const marginLeft = (slideMovement.marginLeft || '0') 108 | .match(/\d/g) 109 | .join(''); 110 | 111 | return { 112 | [CAROUSEL_STRATEGIES.CHANGE_SLIDE]: (originalValue) => originalValue, 113 | [CAROUSEL_STRATEGIES.GET_NEAREST_SLIDE]: (originalValue) => { 114 | const slideIndexOffset = -Math.round( 115 | slideMovement.dragOffset / slideWidth, 116 | ); 117 | 118 | return originalValue + slideIndexOffset; 119 | }, 120 | [CAROUSEL_STRATEGIES.GET_CURRENT_VALUE]: () => carouselProps.value, 121 | [CAROUSEL_STRATEGIES.GET_TRANSFORM_OFFSET]: () => { 122 | const elementWidthWithOffset = itemWidth; 123 | const dragOffset = slideMovement.dragOffset; 124 | 125 | return ( 126 | dragOffset - 127 | activeSlideIndex * elementWidthWithOffset - 128 | marginLeft - 129 | getAdditionalClonesOffset() 130 | ); 131 | }, 132 | }; 133 | }, 134 | }; 135 | }; 136 | 137 | export default infinite; 138 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/rtl.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect } from 'react'; 2 | import { useRecoilState, useRecoilValue } from 'recoil'; 3 | 4 | import { pluginNames } from '../constants/plugins'; 5 | import CAROUSEL_STRATEGIES from '../constants/carouselStrategies'; 6 | import clamp from '../tools/clamp'; 7 | import { slidesState } from '../state/atoms/slideAtoms'; 8 | import { 9 | carouselValueState, 10 | slideMovementState, 11 | trackStylesState, 12 | } from '../state/atoms/carouselAtoms'; 13 | 14 | import './rtl.scss'; 15 | 16 | const rtl = ({ carouselProps }) => ({ 17 | name: pluginNames.RTL, 18 | plugin: () => { 19 | const [trackStyles, setTrackStyles] = useRecoilState(trackStylesState); 20 | const slides = useRecoilValue(slidesState); 21 | 22 | useEffect(() => { 23 | if (carouselProps?.children?.length !== slides.length) { 24 | setTrackStyles({ 25 | ...trackStyles, 26 | transform: -carouselProps.transformOffset, 27 | }); 28 | } 29 | }, [carouselProps.transformOffset]); 30 | }, 31 | strategies: () => { 32 | const slides = useRecoilValue(slidesState); 33 | const slideMovement = useRecoilValue(slideMovementState); 34 | const value = useRecoilValue(carouselValueState); 35 | 36 | return { 37 | [CAROUSEL_STRATEGIES.CHANGE_SLIDE]: (originalValue, prevValue) => { 38 | if (slideMovement.dragOffset) { 39 | return clamp(originalValue, slides); 40 | } 41 | const slidesDiff = prevValue - value; 42 | 43 | // if prev and original are the same, we assume we use the infinite plugin 44 | const rtlValue = value + slidesDiff; 45 | if (originalValue !== prevValue) { 46 | return clamp(rtlValue, slides); 47 | } 48 | return rtlValue; 49 | }, 50 | [CAROUSEL_STRATEGIES.GET_TRANSFORM_OFFSET]: (originalValue, prevValue) => 51 | -prevValue, 52 | }; 53 | }, 54 | carouselClassNames: () => { 55 | const slides = useRecoilValue(slidesState); 56 | const rtlClassName = 'BrainhubCarousel--isRTL'; 57 | const classNames = []; 58 | 59 | if (carouselProps.children.length === slides.length) { 60 | classNames.push(rtlClassName); 61 | } 62 | 63 | return classNames; 64 | }, 65 | carouselCustomProps: () => { 66 | const [slideMovement, setSlideMovement] = useRecoilState( 67 | slideMovementState, 68 | ); 69 | 70 | /** 71 | * Function handling mouse move if drag has started. Sets dragOffset in the state. 72 | * @param {event} event event 73 | */ 74 | const onMouseMove = useCallback( 75 | (event) => { 76 | const { pageX } = event; 77 | if (slideMovement.dragStart !== null) { 78 | setSlideMovement((previousState) => ({ 79 | ...slideMovement, 80 | dragOffset: previousState.dragStart - pageX, 81 | dragEnd: pageX, 82 | })); 83 | } 84 | }, 85 | [slideMovement], 86 | ); 87 | 88 | return { 89 | onMouseMove, 90 | }; 91 | }, 92 | }); 93 | 94 | export default rtl; 95 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/rtl.scss: -------------------------------------------------------------------------------- 1 | .BrainhubCarousel { 2 | 3 | &.BrainhubCarousel--isRTL { 4 | direction: rtl; 5 | 6 | .BrainhubCarousel__trackContainer { 7 | .BrainhubCarousel__track { 8 | direction: rtl; 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/slidesToScroll.js: -------------------------------------------------------------------------------- 1 | import { useRecoilValue } from 'recoil'; 2 | 3 | import { pluginNames } from '../constants/plugins'; 4 | import CAROUSEL_STRATEGIES from '../constants/carouselStrategies'; 5 | import clamp from '../tools/clamp'; 6 | import { carouselValueState } from '../state/atoms/carouselAtoms'; 7 | import { slidesState } from '../state/atoms/slideAtoms'; 8 | 9 | const getAdditionalScroll = (baseToScroll, customToScroll) => { 10 | if (baseToScroll < 0) { 11 | return -baseToScroll - customToScroll; 12 | } else if (baseToScroll > 0) { 13 | return customToScroll - baseToScroll; 14 | } 15 | return 0; 16 | }; 17 | 18 | const defaultOptions = { 19 | numberOfSlides: 3, 20 | }; 21 | 22 | const slidesToScroll = ({ carouselProps, options = defaultOptions }) => ({ 23 | name: pluginNames.SLIDES_TO_SCROLL, 24 | strategies: () => { 25 | const currentValue = useRecoilValue(carouselValueState); 26 | const slides = useRecoilValue(slidesState); 27 | return { 28 | [CAROUSEL_STRATEGIES.CHANGE_SLIDE]: (originalValue, prevValue) => { 29 | const baseToScroll = prevValue - currentValue; 30 | 31 | const additionalToScroll = getAdditionalScroll( 32 | baseToScroll, 33 | options.numberOfSlides, 34 | ); 35 | 36 | if (carouselProps?.children?.length !== slides.length) { 37 | return prevValue + additionalToScroll; 38 | } 39 | return clamp(prevValue + additionalToScroll, slides); 40 | }, 41 | }; 42 | }, 43 | }); 44 | 45 | export default slidesToScroll; 46 | -------------------------------------------------------------------------------- /react-carousel/src/plugins/slidesToShow.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | import { useSetRecoilState } from 'recoil'; 3 | 4 | import useEventListener from '../hooks/useEventListener'; 5 | import { pluginNames } from '../constants/plugins'; 6 | import { slideWidthState } from '../state/atoms/slideAtoms'; 7 | 8 | const defaultOptions = { 9 | numberOfSlides: 3, 10 | }; 11 | 12 | const slidesToShow = ({ carouselProps, refs, options = defaultOptions }) => ({ 13 | name: pluginNames.SLIDES_TO_SHOW, 14 | plugin: () => { 15 | const isInitialMount = useRef(true); 16 | 17 | const setItemWidth = useSetRecoilState(slideWidthState); 18 | 19 | const onResize = () => { 20 | setItemWidth( 21 | refs.trackContainerRef.current.offsetWidth / options.numberOfSlides, 22 | ); 23 | }; 24 | 25 | useEffect(() => { 26 | if (isInitialMount.current) { 27 | isInitialMount.current = false; 28 | } else { 29 | onResize(); 30 | } 31 | }, [carouselProps.width, refs.trackContainerRef.current]); 32 | 33 | useEventListener('resize', onResize); 34 | useEventListener('load', onResize); 35 | }, 36 | }); 37 | 38 | export default slidesToShow; 39 | -------------------------------------------------------------------------------- /react-carousel/src/state/atoms/carouselAtoms.js: -------------------------------------------------------------------------------- 1 | import { atom } from 'recoil'; 2 | 3 | export const slideMovementState = atom({ 4 | key: '@brainhubeu/react-carousel/slideMovementState', 5 | default: { 6 | clicked: null, // index of the clicked slide 7 | dragStart: null, // X position of drag event start 8 | dragEnd: null, // X position of drag event end 9 | dragOffset: 0, // distance of the drag 10 | }, 11 | }); 12 | 13 | export const transitionEnabledState = atom({ 14 | key: '@brainhubeu/react-carousel/transitionEnabledState', 15 | default: false, 16 | }); 17 | 18 | export const trackWidthState = atom({ 19 | key: '@brainhubeu/react-carousel/trackWidthState', 20 | default: 0, 21 | }); 22 | 23 | export const trackStylesState = atom({ 24 | key: '@brainhubeu/react-carousel/trackStylesState', 25 | default: { 26 | marginLeft: 0, 27 | transform: 0, 28 | }, 29 | }); 30 | 31 | export const carouselStrategiesState = atom({ 32 | key: '@brainhubeu/react-carousel/carouselStrategiesState', 33 | default: [], 34 | }); 35 | 36 | export const carouselValueState = atom({ 37 | key: '@brainhubeu/react-carousel/carouselValueState', 38 | default: 0, 39 | }); 40 | -------------------------------------------------------------------------------- /react-carousel/src/state/atoms/slideAtoms.js: -------------------------------------------------------------------------------- 1 | import { atom } from 'recoil'; 2 | 3 | export const activeSlideIndexState = atom({ 4 | key: '@brainhubeu/react-carousel/activeSlideIndexState', 5 | default: 0, 6 | }); 7 | 8 | export const slideWidthState = atom({ 9 | key: '@brainhubeu/react-carousel/itemWidthState', 10 | default: 0, 11 | }); 12 | 13 | export const slideOffsetState = atom({ 14 | key: '@brainhubeu/react-carousel/itemOffsetState', 15 | default: 0, 16 | }); 17 | 18 | export const slidesState = atom({ 19 | key: '@brainhubeu/react-carousel/slidesState', 20 | default: [], 21 | }); 22 | -------------------------------------------------------------------------------- /react-carousel/src/state/selectors/carouselSelectors.js: -------------------------------------------------------------------------------- 1 | import { selector } from 'recoil'; 2 | import _flow from 'lodash/flow'; 3 | import _bind from 'lodash/bind'; 4 | 5 | import clamp from '../../tools/clamp'; 6 | import CAROUSEL_STRATEGIES from '../../constants/carouselStrategies'; 7 | import { 8 | carouselStrategiesState, 9 | carouselValueState, 10 | slideMovementState, 11 | } from '../atoms/carouselAtoms'; 12 | import { 13 | slideOffsetState, 14 | slideWidthState, 15 | slidesState, 16 | } from '../atoms/slideAtoms'; 17 | 18 | export const getCurrentValueSelector = selector({ 19 | key: '@brainhubeu/react-carousel/getCurrentSlideSelector', 20 | get: ({ get }) => { 21 | const value = get(carouselValueState); 22 | 23 | const slides = get(slidesState); 24 | const getCurrentValueBase = () => clamp(value, slides); 25 | 26 | const strategies = get(carouselStrategiesState) 27 | .map( 28 | (strategy) => 29 | strategy && strategy[CAROUSEL_STRATEGIES.GET_CURRENT_VALUE], 30 | ) 31 | .filter((strategy) => typeof strategy === 'function'); 32 | 33 | const enhancedStrategies = strategies.map((strategy) => 34 | _bind(strategy, null, value), 35 | ); 36 | 37 | return enhancedStrategies.length 38 | ? _flow([getCurrentValueBase, ...strategies])() 39 | : getCurrentValueBase(); 40 | }, 41 | set: ({ set, get }, value) => { 42 | const slides = get(slidesState); 43 | const getCurrentValueBase = () => clamp(value, slides); 44 | 45 | const strategies = get(carouselStrategiesState) 46 | .map((strategy) => strategy && strategy[CAROUSEL_STRATEGIES.CHANGE_SLIDE]) 47 | .filter((strategy) => typeof strategy === 'function'); 48 | 49 | const enhancedStrategies = strategies.map((strategy) => 50 | _bind(strategy, null, value), 51 | ); 52 | 53 | const newValue = strategies.length 54 | ? _flow([getCurrentValueBase, ...enhancedStrategies])() 55 | : getCurrentValueBase(); 56 | 57 | set(carouselValueState, newValue); 58 | }, 59 | }); 60 | 61 | /** 62 | * Calculates offset in pixels to be applied to track element in order to show current slide correctly 63 | * @return {number} offset in px 64 | */ 65 | export const transformOffsetSelector = selector({ 66 | key: '@brainhubeu/react-carousel/transformOffsetSelector', 67 | get: ({ get }) => { 68 | const slideWidth = get(slideWidthState); 69 | const slideOffset = get(slideOffsetState); 70 | const dragOffset = get(slideMovementState).dragOffset; 71 | const value = get(carouselValueState); 72 | 73 | const getTransformOffsetBase = () => { 74 | const elementWidthWithOffset = slideWidth + slideOffset; 75 | 76 | return dragOffset - value * elementWidthWithOffset; 77 | }; 78 | 79 | const strategies = get(carouselStrategiesState) 80 | .map( 81 | (strategy) => 82 | strategy && strategy[CAROUSEL_STRATEGIES.GET_TRANSFORM_OFFSET], 83 | ) 84 | .filter((strategy) => typeof strategy === 'function'); 85 | 86 | const enhancedStrategies = strategies.map((strategy) => 87 | _bind(strategy, null, value), 88 | ); 89 | 90 | return strategies.length 91 | ? _flow([getTransformOffsetBase, ...enhancedStrategies])() 92 | : getTransformOffsetBase(); 93 | }, 94 | }); 95 | 96 | /** 97 | * Checks what slide index is the nearest to the current position (to calculate the result of dragging the slider) 98 | * @return {number} index 99 | */ 100 | export const nearestSlideSelector = selector({ 101 | key: '@brainhubeu/react-carousel/nearestSlideSelector', 102 | get: ({ get }) => { 103 | const slideWidth = get(slideWidthState); 104 | const dragOffset = get(slideMovementState).dragOffset; 105 | const value = get(carouselValueState); 106 | const slides = get(slidesState); 107 | 108 | const getNearestSlideBase = () => { 109 | const slideIndexOffset = -Math.round(dragOffset / slideWidth); 110 | 111 | return clamp(value + slideIndexOffset, slides); 112 | }; 113 | 114 | const strategies = get(carouselStrategiesState) 115 | .map( 116 | (strategy) => 117 | strategy && strategy[CAROUSEL_STRATEGIES.GET_NEAREST_SLIDE], 118 | ) 119 | .filter((strategy) => typeof strategy === 'function'); 120 | 121 | const enhancedStrategies = strategies.map((strategy) => 122 | _bind(strategy, null, value), 123 | ); 124 | 125 | return strategies.length 126 | ? _flow([getNearestSlideBase, ...enhancedStrategies])() 127 | : getNearestSlideBase(); 128 | }, 129 | }); 130 | -------------------------------------------------------------------------------- /react-carousel/src/styles/Arrows.scss: -------------------------------------------------------------------------------- 1 | $clickableAreaSize: 42px; 2 | $clickableAreaColor: #7b59ff; 3 | $clickableAreaColor__hover: lighten(#7b59ff, 3%); 4 | $disabledButtonColor: #cccccc; 5 | 6 | $arrowColor: #fff; 7 | $arrowColor__hover: #fff; 8 | $arrowSize: 10px; 9 | $arrowWeight: 3px; 10 | 11 | .BrainhubCarousel__arrows { 12 | position: relative; 13 | padding: $clickableAreaSize/2; 14 | border: none; 15 | overflow: hidden; 16 | outline: 0; 17 | font-size: 0; 18 | line-height: 0; 19 | background-color: $clickableAreaColor; 20 | 21 | span { 22 | display: block; 23 | position: absolute; 24 | top: 50%; 25 | left: 50%; 26 | border-style: solid; 27 | border-color: $arrowColor; 28 | border-width: $arrowWeight $arrowWeight 0 0; 29 | padding: $arrowSize/2; 30 | transition: 0.3s; 31 | font-size: 0; 32 | } 33 | 34 | &:hover { 35 | background-color: $clickableAreaColor__hover; 36 | span { 37 | display: block; 38 | position: absolute; 39 | top: 50%; 40 | left: 50%; 41 | border-style: solid; 42 | border-color: $arrowColor; 43 | border-width: $arrowWeight $arrowWeight 0 0; 44 | padding: $arrowSize/2; 45 | transition: 0.3s; 46 | font-size: 0; 47 | } 48 | 49 | &:enabled { 50 | background-color: $clickableAreaColor__hover; 51 | span { 52 | border-color: $arrowColor__hover; 53 | margin: 0; 54 | } 55 | } 56 | } 57 | 58 | &:disabled { 59 | background-color: $disabledButtonColor; 60 | } 61 | } 62 | 63 | .BrainhubCarousel__arrowLeft { 64 | span { 65 | transform: translate(-50%, -50%) rotate(-135deg); 66 | margin-left: 0.35 * ($arrowSize - $arrowWeight); 67 | } 68 | } 69 | 70 | .BrainhubCarousel__arrowRight { 71 | span { 72 | transform: translate(-50%, -50%) rotate(45deg); 73 | margin-left: -0.35 * ($arrowSize - $arrowWeight); 74 | } 75 | } 76 | 77 | .BrainhubCarousel--isRTL { 78 | .BrainhubCarousel__arrowLeft { 79 | span { 80 | transform: translate(-50%, -50%) rotate(45deg); 81 | margin-left: -0.35 * ($arrowSize - $arrowWeight); 82 | } 83 | } 84 | 85 | .BrainhubCarousel__custom-arrowLeft { 86 | span { 87 | transform: rotate(180deg); 88 | } 89 | } 90 | 91 | .BrainhubCarousel__arrowRight { 92 | span { 93 | transform: translate(-50%, -50%) rotate(-135deg); 94 | margin-left: 0.35 * ($arrowSize - $arrowWeight); 95 | } 96 | } 97 | 98 | .BrainhubCarousel__custom-arrowRight { 99 | span { 100 | transform: rotate(-180deg); 101 | } 102 | } 103 | 104 | .BrainhubCarousel__arrows { 105 | &:hover { 106 | span { 107 | margin: 0; 108 | } 109 | } 110 | } 111 | } 112 | 113 | .BrainhubCarousel__arrow--disable { 114 | pointer-events: none; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /react-carousel/src/styles/Carousel.scss: -------------------------------------------------------------------------------- 1 | $loaderColor: #7b59ff; 2 | 3 | .BrainhubCarousel__container { 4 | width: 100%; 5 | overflow: hidden; 6 | } 7 | 8 | .BrainhubCarousel { 9 | overflow: hidden; 10 | display: flex; 11 | align-items: center; 12 | 13 | &.BrainhubCarousel--isRTL { 14 | direction: rtl; 15 | 16 | .BrainhubCarousel__trackContainer { 17 | .BrainhubCarousel__track { 18 | direction: rtl; 19 | } 20 | } 21 | } 22 | 23 | .BrainhubCarousel__trackContainer { 24 | width: 100%; 25 | overflow: hidden; 26 | .BrainhubCarousel__track { 27 | display: flex; 28 | overflow: hidden; 29 | list-style: none; 30 | margin: 0; 31 | padding: 0; 32 | 33 | &.BrainhubCarousel__track--transition { 34 | transition: transform; 35 | } 36 | } 37 | } 38 | } 39 | 40 | /* arrows */ 41 | .BrainhubCarousel__arrows { 42 | cursor: pointer; 43 | } 44 | 45 | /* loader */ 46 | .BrainhubCarousel__loader { 47 | width: 50px; 48 | height: 50px; 49 | border-radius: 100%; 50 | border: 4px solid $loaderColor; 51 | border-left-color: transparent; 52 | animation: loader 1s infinite linear; 53 | } 54 | 55 | @keyframes loader { 56 | 0% { 57 | transform: rotate(0deg); 58 | } 59 | 100% { 60 | transform: rotate(360deg); 61 | } 62 | } -------------------------------------------------------------------------------- /react-carousel/src/styles/CarouselDots.scss: -------------------------------------------------------------------------------- 1 | /* variables */ 2 | $dots-color: black; 3 | 4 | .BrainhubCarousel__dots { 5 | display: flex; 6 | justify-content: center; 7 | list-style: none; 8 | margin: 0; 9 | padding: 0; 10 | font-size: 0; 11 | line-height: 0; 12 | 13 | &.BrainhubCarousel__dots--isRTL { 14 | direction: rtl; 15 | } 16 | 17 | .BrainhubCarousel__dot { 18 | outline: 0; 19 | padding: 10px; 20 | border: none; 21 | opacity: 0.5; 22 | font-size: 0; 23 | cursor: pointer; 24 | -webkit-appearance: none; 25 | 26 | &.BrainhubCarousel__dot--selected { 27 | opacity: 1 !important; 28 | } 29 | 30 | &:hover { 31 | opacity: 1; 32 | } 33 | 34 | &:before { 35 | content: ''; 36 | display: block; 37 | width: 5px; 38 | height: 5px; 39 | border-radius: 50%; 40 | padding: 0; 41 | border: none; 42 | background: $dots-color; 43 | } 44 | } 45 | 46 | .BrainhubCarousel__thumbnail { 47 | outline: 0; 48 | padding: 10px; 49 | border: none; 50 | opacity: 0.5; 51 | font-size: 0; 52 | cursor: pointer; 53 | 54 | &.BrainhubCarousel__thumbnail--selected { 55 | opacity: 1 !important; 56 | } 57 | 58 | &:hover { 59 | opacity: 1; 60 | } 61 | } 62 | } 63 | 64 | .BrainhubCarousel__thumbnail[type=button] { 65 | -webkit-appearance: none; 66 | } 67 | 68 | .BrainhubCarousel--isRTL { 69 | + .BrainhubCarousel__dots { 70 | direction: rtl; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /react-carousel/src/styles/CarouselItem.scss: -------------------------------------------------------------------------------- 1 | .BrainhubCarouselItem { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | position: relative; 6 | &.BrainhubCarouselItem--clickable { 7 | cursor: pointer; 8 | } 9 | &.BrainhubCarouselItem--active { 10 | 11 | } 12 | .debug-number { 13 | position: absolute; 14 | top: 0; 15 | bottom: 0; 16 | left: 0; 17 | right: 0; 18 | display: flex; 19 | justify-content: center; 20 | font-size: 2em; 21 | text-shadow: 0px 0px 9px white; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /react-carousel/src/tools/carouselPluginResolver.js: -------------------------------------------------------------------------------- 1 | import _flatten from 'lodash/flatten'; 2 | import _sortBy from 'lodash/sortBy'; 3 | 4 | import { plugins as pluginsFunc } from '../constants/plugins'; 5 | import pluginsOrder from '../constants/pluginsOrder'; 6 | 7 | const carouselPluginResolver = ( 8 | plugins, 9 | carouselProps, 10 | trackContainerRef, 11 | carouselRef, 12 | ) => { 13 | const carouselPlugins = carouselProps?.plugins.map((plugin) => { 14 | if (typeof plugin === 'string') { 15 | return ( 16 | pluginsFunc[plugin.toUpperCase()] && 17 | pluginsFunc[plugin.toUpperCase()]({ 18 | carouselProps: { 19 | ...carouselProps, 20 | children: carouselProps.children 21 | ? carouselProps.children 22 | : carouselProps.slides, 23 | }, 24 | options: plugin.options, 25 | refs: { trackContainerRef, carouselRef }, 26 | }) 27 | ); 28 | } 29 | return plugin.resolve({ 30 | carouselProps, 31 | options: plugin.options, 32 | refs: { trackContainerRef, carouselRef }, 33 | }); 34 | }); 35 | const itemClassNames = _flatten( 36 | carouselPlugins.map( 37 | (plugin) => 38 | plugin.itemClassNames && 39 | plugin.itemClassNames({ 40 | carouselProps, 41 | options: plugin.options, 42 | refs: { trackContainerRef, carouselRef }, 43 | }), 44 | ), 45 | ).filter((className) => typeof className === 'string'); 46 | 47 | const carouselClassNames = _flatten( 48 | carouselPlugins.map( 49 | (plugin) => 50 | plugin.carouselClassNames && 51 | plugin.carouselClassNames({ 52 | carouselProps, 53 | options: plugin.options, 54 | refs: { trackContainerRef, carouselRef }, 55 | }), 56 | ), 57 | ).filter((className) => typeof className === 'string'); 58 | 59 | const carouselCustomProps = Object.assign( 60 | {}, 61 | ...carouselPlugins.map( 62 | (plugin) => plugin.carouselCustomProps && plugin.carouselCustomProps(), 63 | ), 64 | ); 65 | 66 | const trackCustomProps = Object.assign( 67 | {}, 68 | ...carouselPlugins.map( 69 | (plugin) => plugin.trackCustomProps && plugin.trackCustomProps(), 70 | ), 71 | ); 72 | 73 | const slideCustomProps = Object.assign( 74 | {}, 75 | ...carouselPlugins.map( 76 | (plugin) => plugin.slideCustomProps && plugin.slideCustomProps(), 77 | ), 78 | ); 79 | 80 | const beforeCarouselItems = 81 | carouselPlugins.map( 82 | (plugin) => plugin.beforeCarouselItems && plugin.beforeCarouselItems(), 83 | ) || []; 84 | 85 | const afterCarouselItems = 86 | carouselPlugins.map( 87 | (plugin) => plugin.afterCarouselItems && plugin.afterCarouselItems(), 88 | ) || []; 89 | 90 | const strategies = _sortBy(carouselPlugins, (plugin) => 91 | pluginsOrder.indexOf(plugin.name), 92 | ).map((plugin) => plugin.strategies && plugin.strategies()); 93 | 94 | return { 95 | itemClassNames, 96 | carouselClassNames, 97 | beforeCarouselItems, 98 | afterCarouselItems, 99 | strategies, 100 | carouselCustomProps, 101 | trackCustomProps, 102 | slideCustomProps, 103 | carouselPlugins, 104 | }; 105 | }; 106 | 107 | export default carouselPluginResolver; 108 | -------------------------------------------------------------------------------- /react-carousel/src/tools/clamp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Clamps number between 0 and last slide index. 3 | * @param {number} value to be clamped 4 | * @param {[node]} slides of react-carousel component 5 | * @param {boolean} rtl decide whether to clamp value for rtl carousel 6 | * @return {number} new value 7 | */ 8 | const clamp = (value, slides) => { 9 | const getChildren = () => { 10 | if (slides) { 11 | return slides; 12 | } 13 | return []; 14 | }; 15 | 16 | const maxValue = getChildren().length - 1; 17 | if (value > maxValue) { 18 | return maxValue; 19 | } 20 | if (value < 0) { 21 | return 0; 22 | } 23 | return value; 24 | }; 25 | 26 | export default clamp; 27 | -------------------------------------------------------------------------------- /react-carousel/src/tools/getChildren.js: -------------------------------------------------------------------------------- 1 | const getChildren = (children, slides) => { 2 | if (!children) { 3 | if (slides) { 4 | return slides; 5 | } 6 | return []; 7 | } 8 | if (Array.isArray(children)) { 9 | return children; 10 | } 11 | return [children]; 12 | }; 13 | 14 | export default getChildren; 15 | -------------------------------------------------------------------------------- /react-carousel/src/tools/simulateEvent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simulates mouse events when touch events occur 3 | * @param {event} event A touch event 4 | */ 5 | const simulateEvent = (event) => { 6 | const touch = event.changedTouches[0]; 7 | const { screenX, screenY, clientX, clientY } = touch; 8 | const touchEventMap = { 9 | touchstart: 'mousedown', 10 | touchmove: 'mousemove', 11 | touchend: 'mouseup', 12 | }; 13 | const simulatedEvent = new MouseEvent(touchEventMap[event.type], { 14 | bubbles: true, 15 | cancelable: true, 16 | view: window, 17 | detail: 1, 18 | screenX, 19 | screenY, 20 | clientX, 21 | clientY, 22 | }); 23 | touch.target.dispatchEvent(simulatedEvent); 24 | }; 25 | 26 | export default simulateEvent; 27 | -------------------------------------------------------------------------------- /react-carousel/test/__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /react-carousel/test/carousel.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { mount } from 'enzyme'; 3 | 4 | import CarouselWrapper from '../src/components/CarouselWrapper'; 5 | 6 | import setupCarousel from './tools/setupCarousel'; 7 | 8 | const resizeEvent = document.createEvent('Event'); 9 | resizeEvent.initEvent('resize', true, true); 10 | 11 | window.resizeTo = (width, height) => { 12 | window.innerWidth = width || global.window.innerWidth; 13 | window.innerHeight = height || global.window.innerHeight; 14 | window.dispatchEvent(resizeEvent); 15 | }; 16 | 17 | describe('Carousel', () => { 18 | describe('plain carousel', () => { 19 | test('renders carousel items', () => { 20 | const wrapper = setupCarousel(); 21 | 22 | expect(wrapper.find('.BrainhubCarouselItem')).toHaveLength(3); 23 | }); 24 | 25 | test('renders carousel items when slides are result of a function', () => { 26 | const renderName = (name) =>
    {name}
    ; 27 | 28 | const names = ['Dave', 'Kanye', 'Adam']; 29 | 30 | const wrapper = mount( 31 | 32 |
    Party guests:
    33 | {names.map((name) => renderName(name))} 34 |
    , 35 | ); 36 | 37 | expect(wrapper.find('.BrainhubCarouselItem')).toHaveLength(2); 38 | }); 39 | 40 | test('renders carousel items when passed as a prop', () => { 41 | const wrapper = mount(]} />); 42 | 43 | expect(wrapper.find('.BrainhubCarouselItem')).toHaveLength(1); 44 | }); 45 | 46 | test('overwrites carousel item width', () => { 47 | const wrapper = setupCarousel({ 48 | itemWidth: 250, 49 | }); 50 | 51 | const item = wrapper.find('.BrainhubCarouselItem').first(); 52 | 53 | expect(item.props().style).toHaveProperty('width', '250px'); 54 | }); 55 | 56 | test('picks up correct props to set window size', () => { 57 | const wrapper = setupCarousel({ 58 | breakpoints: { 59 | 1200: { 60 | itemWidth: 250, 61 | }, 62 | 2400: { 63 | itemWidth: 500, 64 | }, 65 | }, 66 | }); 67 | window.resizeTo(2000, 2000); 68 | 69 | const item = wrapper.find('.BrainhubCarouselItem').first(); 70 | 71 | expect(item.props().style).toHaveProperty('width', '250px'); 72 | }); 73 | 74 | test('picks up correct props to set window size', () => { 75 | const wrapper = setupCarousel({ 76 | breakpoints: { 77 | 1200: { 78 | itemWidth: 250, 79 | }, 80 | 2400: { 81 | itemWidth: 500, 82 | }, 83 | }, 84 | }); 85 | window.resizeTo(2500, 2500); 86 | 87 | const item = wrapper.find('.BrainhubCarouselItem').first(); 88 | 89 | expect(item.props().style).toHaveProperty('width', '500px'); 90 | }); 91 | 92 | test(`uses default value if it's not declared in the breakpoint`, () => { 93 | window.resizeTo(2000, 2000); 94 | 95 | const declaredWidth = 400; 96 | 97 | const wrapper = setupCarousel({ 98 | itemWidth: declaredWidth, 99 | breakpoints: { 100 | 1200: { 101 | value: 1, 102 | }, 103 | 2400: { 104 | value: 2, 105 | }, 106 | }, 107 | }); 108 | 109 | expect( 110 | wrapper.find('.BrainhubCarouselItem').first().prop('style').width, 111 | ).toEqual(`${declaredWidth}px`); 112 | }); 113 | 114 | test('sets carousel initial slide index', () => { 115 | const wrapper = setupCarousel({ 116 | value: 2, 117 | }); 118 | 119 | expect( 120 | wrapper 121 | .find('.BrainhubCarouselItem') 122 | .at(2) 123 | .hasClass('BrainhubCarouselItem--active'), 124 | ).toBeTruthy(); 125 | }); 126 | 127 | test('displays the last slide if the value is greater than the number of slides', () => { 128 | const wrapper = mount( 129 | 130 |
    131 |
    132 |
    133 | , 134 | ); 135 | 136 | expect( 137 | wrapper 138 | .find('.BrainhubCarouselItem--active') 139 | .children() 140 | .hasClass('third'), 141 | ).toBeTruthy(); 142 | }); 143 | 144 | test('displays the first slide if the value is lower than 0', () => { 145 | const wrapper = mount( 146 | 147 |
    148 |
    149 |
    150 | , 151 | ); 152 | 153 | expect( 154 | wrapper 155 | .find('.BrainhubCarouselItem--active') 156 | .children() 157 | .hasClass('first'), 158 | ).toBeTruthy(); 159 | }); 160 | }); 161 | }); 162 | -------------------------------------------------------------------------------- /react-carousel/test/plugins/arrows.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { arrowsPlugin } from '../../src'; 4 | import setupCarousel from '../tools/setupCarousel'; 5 | 6 | describe('arrows', () => { 7 | test('renders arrows', () => { 8 | const wrapper = setupCarousel({ 9 | plugins: ['arrows'], 10 | }); 11 | 12 | expect(wrapper.find('.BrainhubCarousel__arrows')).toHaveLength(2); 13 | }); 14 | 15 | test('arrow left is disabled when there are no more slides on the left', () => { 16 | const wrapper = setupCarousel({ 17 | plugins: ['arrows'], 18 | }); 19 | 20 | expect( 21 | wrapper.find('.BrainhubCarousel__arrowRight').props().disabled, 22 | ).toBeFalsy(); 23 | expect( 24 | wrapper.find('.BrainhubCarousel__arrowLeft').props().disabled, 25 | ).toBeTruthy(); 26 | }); 27 | 28 | test('arrow right is disabled when there are no more slides on the right', () => { 29 | const wrapper = setupCarousel({ 30 | value: 2, 31 | plugins: ['arrows'], 32 | }); 33 | 34 | expect( 35 | wrapper.find('.BrainhubCarousel__arrowRight').props().disabled, 36 | ).toBeTruthy(); 37 | expect( 38 | wrapper.find('.BrainhubCarousel__arrowLeft').props().disabled, 39 | ).toBeFalsy(); 40 | }); 41 | 42 | test('both arrows are enabled with infinite plugin', () => { 43 | const wrapper = setupCarousel({ 44 | plugins: ['arrows', 'infinite'], 45 | }); 46 | 47 | expect( 48 | wrapper.find('.BrainhubCarousel__arrowRight').props().disabled, 49 | ).toBeFalsy(); 50 | expect( 51 | wrapper.find('.BrainhubCarousel__arrowLeft').props().disabled, 52 | ).toBeFalsy(); 53 | }); 54 | 55 | test('changes slide on active arrow click', () => { 56 | const wrapper = setupCarousel({ 57 | plugins: ['arrows'], 58 | }); 59 | 60 | expect( 61 | wrapper 62 | .find('.BrainhubCarouselItem') 63 | .first() 64 | .hasClass('BrainhubCarouselItem--active'), 65 | ).toBeTruthy(); 66 | 67 | wrapper.find('.BrainhubCarousel__arrowRight').simulate('click'); 68 | 69 | expect( 70 | wrapper 71 | .find('.BrainhubCarouselItem') 72 | .at(1) 73 | .hasClass('BrainhubCarouselItem--active'), 74 | ).toBeTruthy(); 75 | }); 76 | 77 | test('custom arrows work as expected', () => { 78 | const wrapper = setupCarousel({ 79 | plugins: [ 80 | { 81 | resolve: arrowsPlugin, 82 | options: { 83 | arrowLeft:
    , 84 | arrowLeftDisabled:
    , 85 | arrowRight:
    , 86 | arrowRightDisabled:
    , 87 | addArrowClickHandler: true, 88 | }, 89 | }, 90 | ], 91 | }); 92 | 93 | expect(wrapper.find('.left-disabled')).toHaveLength(1); 94 | 95 | wrapper.find('.right').simulate('click'); 96 | 97 | expect(wrapper.find('.left-disabled')).toHaveLength(0); 98 | expect(wrapper.find('.left')).toHaveLength(1); 99 | 100 | wrapper.find('.right').simulate('click'); 101 | 102 | expect(wrapper.find('.right-disabled')).toHaveLength(1); 103 | expect(wrapper.find('.right')).toHaveLength(0); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /react-carousel/test/plugins/autoplay.test.js: -------------------------------------------------------------------------------- 1 | import { autoplayPlugin } from '../../src'; 2 | import setupCarousel from '../tools/setupCarousel'; 3 | 4 | describe('autoplay', () => { 5 | test('clears interval on unmount when autoplay was enabled', () => { 6 | jest.useFakeTimers(); 7 | 8 | const wrapper = setupCarousel({ 9 | plugins: ['autoplay'], 10 | animationSpeed: 1, 11 | }); 12 | 13 | wrapper.unmount(); 14 | 15 | expect(clearInterval).toHaveBeenCalled(); 16 | }); 17 | 18 | test('changes slides automatically', () => { 19 | jest.useFakeTimers(); 20 | 21 | Object.defineProperty(document, 'hidden', { 22 | configurable: true, 23 | get: () => false, 24 | }); 25 | 26 | const wrapper = setupCarousel({ 27 | plugins: [ 28 | { 29 | resolve: autoplayPlugin, 30 | options: { 31 | interval: 10, 32 | }, 33 | }, 34 | ], 35 | animationSpeed: 1, 36 | }); 37 | 38 | expect( 39 | wrapper 40 | .find('.BrainhubCarouselItem') 41 | .at(0) 42 | .hasClass('BrainhubCarouselItem--active'), 43 | ).toBeTruthy(); 44 | 45 | jest.advanceTimersByTime(11); 46 | 47 | wrapper.update(); 48 | 49 | expect( 50 | wrapper 51 | .find('.BrainhubCarouselItem') 52 | .at(1) 53 | .hasClass('BrainhubCarouselItem--active'), 54 | ).toBeTruthy(); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /react-carousel/test/plugins/dots.test.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useState } from 'react'; 2 | import { mount } from 'enzyme'; 3 | 4 | import { Dots } from '../../src'; 5 | import CarouselWrapper from '../../src/components/CarouselWrapper'; 6 | 7 | const CarouselWithDots = () => { 8 | const [value, setValue] = useState(0); 9 | 10 | const onChange = useCallback( 11 | (value) => { 12 | setValue(value); 13 | }, 14 | [setValue], 15 | ); 16 | 17 | const slides = Array.from({ length: 5 }, (val, index) =>
    ); 18 | 19 | return ( 20 |
    21 | 22 | 23 |
    24 | ); 25 | }; 26 | 27 | const CarouselWithThumbnails = () => { 28 | const [value, setValue] = useState(0); 29 | 30 | const onChange = useCallback( 31 | (value) => { 32 | setValue(value); 33 | }, 34 | [setValue], 35 | ); 36 | 37 | const slides = Array.from({ length: 5 }, (val, index) =>
    ); 38 | 39 | return ( 40 |
    41 | 42 | 48 |
    49 | ); 50 | }; 51 | 52 | describe('dots', () => { 53 | test('renders dots', () => { 54 | const wrapper = mount(); 55 | 56 | expect(wrapper.find('.BrainhubCarousel__dot')).toHaveLength(5); 57 | }); 58 | 59 | test('changes slide on dot click', () => { 60 | const wrapper = mount(); 61 | 62 | expect( 63 | wrapper 64 | .find('.BrainhubCarouselItem') 65 | .first() 66 | .hasClass('BrainhubCarouselItem--active'), 67 | ).toBeTruthy(); 68 | 69 | wrapper.find('.BrainhubCarousel__dot').at(1).simulate('click'); 70 | 71 | expect( 72 | wrapper 73 | .find('.BrainhubCarouselItem') 74 | .at(1) 75 | .hasClass('BrainhubCarouselItem--active'), 76 | ).toBeTruthy(); 77 | }); 78 | 79 | test('changes slide on thumbnail click', () => { 80 | const wrapper = mount(); 81 | 82 | expect( 83 | wrapper 84 | .find('.BrainhubCarouselItem') 85 | .first() 86 | .hasClass('BrainhubCarouselItem--active'), 87 | ).toBeTruthy(); 88 | 89 | wrapper.find('.BrainhubCarousel__thumbnail').at(1).simulate('click'); 90 | 91 | expect( 92 | wrapper 93 | .find('.BrainhubCarouselItem') 94 | .at(1) 95 | .hasClass('BrainhubCarouselItem--active'), 96 | ).toBeTruthy(); 97 | }); 98 | }); 99 | -------------------------------------------------------------------------------- /react-carousel/test/plugins/infinite.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import setupCarousel from '../tools/setupCarousel'; 4 | import { defaultOptions as infiniteDefaultOptions } from '../../src/plugins/infinite'; 5 | 6 | describe('infinite', () => { 7 | it('renders additional clones when in infinite mode', () => { 8 | const sidesToClone = 2; // we create clones for the left and for the right side 9 | const slides = [
    ,
    ,
    ]; 10 | const wrapper = setupCarousel({ 11 | plugins: ['infinite'], 12 | slides, 13 | }); 14 | 15 | const expectedNumberOfSlides = 16 | slides.length + 17 | slides.length * 18 | infiniteDefaultOptions.numberOfInfiniteClones * 19 | sidesToClone; 20 | 21 | expect(wrapper.find('.BrainhubCarouselItem')).toHaveLength( 22 | expectedNumberOfSlides, 23 | ); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /react-carousel/test/plugins/rtl.test.js: -------------------------------------------------------------------------------- 1 | import setupCarousel from '../tools/setupCarousel'; 2 | 3 | describe('rtl', () => { 4 | test('adds BrainhubCarousel--isRTL when in RTL mode', () => { 5 | const wrapper = setupCarousel({ 6 | plugins: ['rtl'], 7 | }); 8 | 9 | expect(wrapper.find('.BrainhubCarousel--isRTL')).toHaveLength(1); 10 | }); 11 | 12 | test('left arrow is initially enabled with rtl plugin', () => { 13 | const wrapper = setupCarousel({ 14 | plugins: ['rtl', 'arrows'], 15 | }); 16 | 17 | expect( 18 | wrapper.find('.BrainhubCarousel__arrowLeft').props().disabled, 19 | ).toBeTruthy(); 20 | expect( 21 | wrapper.find('.BrainhubCarousel__arrowRight').props().disabled, 22 | ).toBeFalsy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /react-carousel/test/plugins/slidesPerScroll.test.js: -------------------------------------------------------------------------------- 1 | import setupCarousel from '../tools/setupCarousel'; 2 | import slidesToScroll from '../../src/plugins/slidesToScroll'; 3 | 4 | describe('slidesPerScroll plugin', () => { 5 | test('slides forward as many slides as described in plugin options', () => { 6 | const wrapper = setupCarousel({ 7 | plugins: [ 8 | 'arrows', 9 | { 10 | resolve: slidesToScroll, 11 | options: { 12 | numberOfSlides: 2, 13 | }, 14 | }, 15 | ], 16 | }); 17 | 18 | expect( 19 | wrapper 20 | .find('.BrainhubCarouselItem') 21 | .at(0) 22 | .hasClass('BrainhubCarouselItem--active'), 23 | ).toBeTruthy(); 24 | 25 | wrapper.find('.BrainhubCarousel__arrowRight').simulate('click'); 26 | 27 | expect( 28 | wrapper 29 | .find('.BrainhubCarouselItem') 30 | .at(2) 31 | .hasClass('BrainhubCarouselItem--active'), 32 | ).toBeTruthy(); 33 | }); 34 | 35 | test('slides backward as many slides as described in plugin options', () => { 36 | const wrapper = setupCarousel({ 37 | plugins: [ 38 | 'arrows', 39 | { 40 | resolve: slidesToScroll, 41 | options: { 42 | numberOfSlides: 2, 43 | }, 44 | }, 45 | ], 46 | }); 47 | 48 | expect( 49 | wrapper 50 | .find('.BrainhubCarouselItem') 51 | .at(0) 52 | .hasClass('BrainhubCarouselItem--active'), 53 | ).toBeTruthy(); 54 | 55 | wrapper.find('.BrainhubCarousel__arrowRight').simulate('click'); 56 | 57 | expect( 58 | wrapper 59 | .find('.BrainhubCarouselItem') 60 | .at(2) 61 | .hasClass('BrainhubCarouselItem--active'), 62 | ).toBeTruthy(); 63 | 64 | wrapper.find('.BrainhubCarousel__arrowLeft').simulate('click'); 65 | 66 | expect( 67 | wrapper 68 | .find('.BrainhubCarouselItem') 69 | .at(0) 70 | .hasClass('BrainhubCarouselItem--active'), 71 | ).toBeTruthy(); 72 | }); 73 | 74 | test('goes to the last slide if numberOfSlides exceeds the total number of slides', () => { 75 | const wrapper = setupCarousel({ 76 | plugins: [ 77 | 'arrows', 78 | { 79 | resolve: slidesToScroll, 80 | options: { 81 | numberOfSlides: 5, 82 | }, 83 | }, 84 | ], 85 | }); 86 | 87 | expect( 88 | wrapper 89 | .find('.BrainhubCarouselItem') 90 | .at(0) 91 | .hasClass('BrainhubCarouselItem--active'), 92 | ).toBeTruthy(); 93 | 94 | wrapper.find('.BrainhubCarousel__arrowRight').simulate('click'); 95 | 96 | expect( 97 | wrapper 98 | .find('.BrainhubCarouselItem') 99 | .at(2) 100 | .hasClass('BrainhubCarouselItem--active'), 101 | ).toBeTruthy(); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /react-carousel/test/tools/setupCarousel.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { mount } from 'enzyme'; 3 | 4 | import CarouselWrapper from '../../src/components/CarouselWrapper'; 5 | 6 | const setup = (options = {}) => 7 | mount( 8 | 9 |
    16 |
    23 |
    30 | , 31 | ); 32 | 33 | export default setup; 34 | -------------------------------------------------------------------------------- /react-carousel/tools/build.js: -------------------------------------------------------------------------------- 1 | // More info on Webpack's Node API here: https://webpack.github.io/docs/node.js-api.html 2 | // Allowing console calls below since this is a build file. 3 | /* eslint-disable no-console */ 4 | const webpack = require('webpack'); 5 | 6 | const config = require('../webpack.config.prod'); 7 | 8 | const { 9 | chalkError, 10 | chalkSuccess, 11 | chalkWarning, 12 | chalkProcessing, 13 | } = require('./chalkConfig'); 14 | 15 | process.env.NODE_ENV = 'production'; // this assures React is built in prod mode and that the Babel dev config doesn't apply. 16 | 17 | console.log( 18 | chalkProcessing('Generating minified bundle. This will take a moment...'), 19 | ); 20 | 21 | webpack(config).run((error, stats) => { 22 | if (error) { 23 | // so a fatal error occurred. Stop here. 24 | console.log(chalkError(error)); 25 | process.exit(1); 26 | } 27 | 28 | const jsonStats = stats.toJson(); 29 | 30 | if (stats.hasErrors()) { 31 | jsonStats.errors.forEach((error) => console.log(chalkError(error))); 32 | process.exit(1); 33 | } 34 | 35 | if (stats.hasWarnings()) { 36 | console.log(chalkWarning('Webpack generated the following warnings: ')); 37 | jsonStats.warnings.forEach((warning) => console.log(chalkWarning(warning))); 38 | } 39 | 40 | console.log(`Webpack stats: ${stats}`); 41 | 42 | // if we got this far, the build succeeded. 43 | console.log( 44 | chalkSuccess( 45 | `Your app is compiled in production mode in /lib. It's ready to roll!`, 46 | ), 47 | ); 48 | }); 49 | -------------------------------------------------------------------------------- /react-carousel/tools/chalkConfig.js: -------------------------------------------------------------------------------- 1 | // Centralized configuration for chalk, which is used to add color to console.log statements. 2 | const chalk = require('chalk'); 3 | 4 | module.exports = { 5 | chalkError: chalk.red, 6 | chalkSuccess: chalk.green, 7 | chalkWarning: chalk.yellow, 8 | chalkProcessing: chalk.blue, 9 | }; 10 | -------------------------------------------------------------------------------- /react-carousel/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const TerserPlugin = require('terser-webpack-plugin'); 4 | const webpack = require('webpack'); 5 | const autoprefixer = require('autoprefixer'); 6 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 7 | 8 | module.exports = { 9 | mode: 'production', 10 | externals: [ 11 | { 12 | 'react-dom': { 13 | root: 'ReactDOM', 14 | commonjs2: 'react-dom', 15 | commonjs: 'react-dom', 16 | amd: 'react-dom', 17 | }, 18 | }, 19 | { 20 | react: { 21 | root: 'React', 22 | commonjs2: 'react', 23 | commonjs: 'react', 24 | amd: 'react', 25 | }, 26 | }, 27 | ], 28 | resolve: { 29 | extensions: ['.js', '.jsx', '.json'], 30 | modules: ['node_modules', path.join(__dirname, 'src')], 31 | }, 32 | devtool: 'source-map', 33 | entry: './src/index.js', 34 | output: { 35 | filename: 'react-carousel.js', 36 | library: 'react-carousel', 37 | libraryTarget: 'umd', 38 | path: path.resolve(__dirname, 'lib'), 39 | umdNamedDefine: true, 40 | globalObject: 'this', 41 | }, 42 | plugins: [ 43 | new TerserPlugin({ 44 | parallel: true, 45 | terserOptions: { 46 | ecma: 6, 47 | }, 48 | }), 49 | new ExtractTextPlugin('style.css'), 50 | new webpack.DefinePlugin({ 51 | // This fixes https://github.com/brainhubeu/react-carousel/issues/115 52 | 'process.env': { 53 | NODE_ENV: JSON.stringify('production'), 54 | }, 55 | }), 56 | ], 57 | module: { 58 | rules: [ 59 | { 60 | test: /\.jsx?$/, 61 | exclude: /node_modules/, 62 | use: ['babel-loader'], 63 | }, 64 | { 65 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, 66 | use: [ 67 | { 68 | loader: 'url-loader', 69 | options: { 70 | limit: 10000, 71 | mimetype: 'image/svg+xml', 72 | }, 73 | }, 74 | ], 75 | }, 76 | { 77 | test: /(\.css|\.scss)$/, 78 | use: ExtractTextPlugin.extract({ 79 | fallback: 'style-loader', 80 | use: [ 81 | { 82 | loader: 'css-loader', 83 | options: { 84 | sourceMap: true, 85 | }, 86 | }, 87 | { 88 | loader: 'postcss-loader', 89 | options: { 90 | sourceMap: true, 91 | plugins: () => [autoprefixer], 92 | }, 93 | }, 94 | { 95 | loader: 'sass-loader', 96 | options: { 97 | sourceMap: true, 98 | }, 99 | }, 100 | ], 101 | }), 102 | }, 103 | ], 104 | }, 105 | }; 106 | -------------------------------------------------------------------------------- /readme/assets/carousel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/readme/assets/carousel.gif -------------------------------------------------------------------------------- /readme/assets/logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/readme/assets/logo.gif -------------------------------------------------------------------------------- /readme/assets/thumbnails.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brainhubeu/react-carousel/be0aeda9804f16fd7482e42a1b1010a7ec9bb302/readme/assets/thumbnails.gif -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "bumpVersion": "patch", 3 | "commitMessagePrefix": "[renovate] ", 4 | "groupName": "NPM dependencies", 5 | "labels": ["renovate"], 6 | "rangeStrategy": "bump", 7 | "branchPrefix": "fix/renovate/", 8 | "docker": { 9 | "major": { 10 | "enabled": true 11 | } 12 | }, 13 | "packageRules": [ 14 | { 15 | "groupName": "Docker dependencies", 16 | "managers": ["circleci"] 17 | }, 18 | { 19 | "groupName": "gatsby", 20 | "managerBranchPrefix": "gatsby", 21 | "packagePatterns": ["gatsby"], 22 | "rangeStrategy": "pin" 23 | } 24 | ], 25 | "schedule": ["before 3am on the first day of the month"] 26 | } 27 | -------------------------------------------------------------------------------- /tools/deploy-gh-pages-pr.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | failure() { 5 | local lineno=$1 6 | echo "Failed at $lineno" 7 | } 8 | trap 'failure ${LINENO}' ERR 9 | 10 | for page_number in {1..20} 11 | do 12 | echo "page_number=$page_number" 13 | pr_number=`curl -s "https://beghp.github.io/gh-pages-rc-$page_number/" | grep -o '★☂☀[0-9]\+♞♜♖' | grep -o '[0-9]\+' | head -1 || echo ''` 14 | echo "pr_number=$pr_number" 15 | if [[ "$pr_number" == '' ]] 16 | then 17 | echo "no PR exists for page no $page_number" 18 | if [[ "$first_free_page_number" == '' ]] 19 | then 20 | first_free_page_number=$page_number 21 | fi 22 | elif [[ "https://github.com/brainhubeu/react-carousel/pull/$pr_number" == "$CIRCLE_PULL_REQUEST" ]] 23 | then 24 | echo "this PR is already deployed to the page no $page_number" 25 | already_deployed_page_number=$page_number 26 | break 27 | else 28 | echo "a PR (no $pr_number) exists for page no $page_number" 29 | pr_state=`curl -s "https://api.github.com/repos/brainhubeu/react-carousel/pulls/$pr_number" | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["state"]'` 30 | echo "PR state: $pr_state" 31 | if [[ "$pr_state" == 'open' ]] 32 | then 33 | echo 'the PR is open, continue searching an empty page' 34 | else 35 | echo 'the PR is not open' 36 | if [[ "$first_free_page_number" == '' ]] 37 | then 38 | first_free_page_number=$page_number 39 | fi 40 | fi 41 | fi 42 | done 43 | if [[ "$page_number" == '20' ]] 44 | then 45 | if [[ "$first_free_page_number" == '' || "$first_free_page_number" == '20' ]] 46 | then 47 | echo 'no free page' 48 | exit 1 49 | fi 50 | echo "deploying to $first_free_page_number as it's the first free place" 51 | final_page_number=$first_free_page_number 52 | else 53 | echo "the PR was deployed to the page no $page_number so keeping the same place" 54 | final_page_number=$already_deployed_page_number 55 | fi 56 | echo "final_page_number=$final_page_number" 57 | 58 | page_url="https://beghp.github.io/gh-pages-rc-$final_page_number" 59 | echo "page_url=$page_url" 60 | 61 | 62 | sed -i 's/__RC_ENV__/development/g' docs-www/src/globalReferences.js 63 | sed -i 's/__RC_ENV__/development/g' docs-www/package.json 64 | sed -i "s/__BUILD_INFO__/ (PR #$pr_number, built on `date +'%Y-%m-%d %H:%M:%S'`)/g" docs-www/gatsby-docs-kit.yml 65 | cat docs-www/src/globalReferences.js 66 | cat docs-www/package.json 67 | remote=https://$GIT_TOKEN@github.com/beghp/gh-pages-rc-$final_page_number.git 68 | 69 | yarn install --non-interactive 70 | 71 | PATH_PREFIX=gh-pages-rc-$final_page_number yarn workspace react-carousel-docs build 72 | 73 | mkdir -p gh-pages-branch 74 | cd gh-pages-branch 75 | 76 | git config --global user.email "robert@brainhub.eu" > /dev/null 2>&1 77 | git config --global user.name "DevOps Brainhub" > /dev/null 2>&1 78 | git init 79 | git remote add origin $remote 80 | git remote -v 81 | git fetch origin 82 | 83 | if git rev-parse --verify origin/gh-pages > /dev/null 2>&1 84 | then 85 | echo 'rev-parse true' 86 | git checkout gh-pages 87 | git rm -rf . || echo 'nothing to remove' 88 | cp -r ../docs-www/public/* . 89 | else 90 | echo 'rev-parse false' 91 | git checkout --orphan gh-pages 92 | git rm -rf . || echo 'nothing to remove' 93 | cp -r ../docs-www/public/* . 94 | fi 95 | 96 | git add -A 97 | git commit --allow-empty -m "Deploy to GitHub pages [ci skip]" 98 | git push --force --quiet origin gh-pages 99 | 100 | cd .. 101 | rm -rf gh-pages-branch 102 | 103 | api_pr_url=`echo "$CIRCLE_PULL_REQUEST" | sed 's@https://github.com/brainhubeu/react-carousel/pull/@https://api.github.com/repos/brainhubeu/react-carousel/pulls/@g'` 104 | echo api_pr_url=$api_pr_url 105 | pr_body=`curl -s $api_pr_url | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["body"]' | perl -pe 's/\r?\n/
    /' | sed 's@
    @
    @g'` 106 | echo "pr_body=$pr_body" 107 | deployed_match=`echo $pr_body | grep 'Deployed to https://beghp.github.io' || echo ''` 108 | if [[ "$deployed_match" != '' ]] 109 | then 110 | echo 'page URL already added to the PR description' 111 | else 112 | curl -i -H "Authorization: token $GIT_TOKEN" -X PATCH -d "{\"body\":\"Deployed to $page_url
    $pr_body\"}" $api_pr_url 113 | fi 114 | 115 | echo "Finished Deployment of gh pages to $page_url" 116 | -------------------------------------------------------------------------------- /tools/deploy-gh-pages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | failure() { 5 | local lineno=$1 6 | echo "Failed at $lineno" 7 | } 8 | trap 'failure ${LINENO}' ERR 9 | 10 | remote=https://$GIT_TOKEN@github.com/brainhubeu/react-carousel.git 11 | 12 | sed -i "s/__BUILD_INFO__/ (master, built on `date +'%Y-%m-%d %H:%M:%S'`)/g" docs-www/gatsby-docs-kit.yml 13 | 14 | yarn install --non-interactive 15 | 16 | PATH_PREFIX=react-carousel yarn workspace react-carousel-docs build 17 | 18 | mkdir -p gh-pages-branch 19 | cd gh-pages-branch 20 | 21 | git config --global user.email "robert@brainhub.eu" > /dev/null 2>&1 22 | git config --global user.name "DevOps Brainhub" > /dev/null 2>&1 23 | git init 24 | git remote add origin $remote 25 | git remote -v 26 | git fetch origin 27 | 28 | if git rev-parse --verify origin/gh-pages > /dev/null 2>&1 29 | then 30 | echo 'rev-parse true' 31 | git checkout gh-pages 32 | git rm -rf . || echo 'nothing to remove' 33 | cp -r ../docs-www/public/* . 34 | else 35 | echo 'rev-parse false' 36 | git checkout --orphan gh-pages 37 | git rm -rf . || echo 'nothing to remove' 38 | cp -r ../docs-www/public/* . 39 | fi 40 | 41 | git add -A 42 | git commit --allow-empty -m "Deploy to GitHub pages [ci skip]" 43 | git push --force --quiet origin gh-pages 44 | 45 | cd .. 46 | rm -rf gh-pages-branch 47 | 48 | echo "Finished Deployment of gh pages!" 49 | -------------------------------------------------------------------------------- /tools/skip-all-e2e-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | sed -i '' 's/describe/describe.skip/g' test/cypress/integration/* 5 | -------------------------------------------------------------------------------- /tools/unskip-all-e2e-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | sed -i '' 's/describe.skip/describe/g' test/cypress/integration/* 5 | --------------------------------------------------------------------------------