Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-carousel

Package Overview
Dependencies
Maintainers
2
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-carousel - npm Package Compare versions

Comparing version 3.3.0 to 4.0.0

.eslintrc

55

package.json
{
"description": "React carousel.",
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-core": "^6.24.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"chai": "^3.5.0",
"eslint": "^3.19.0",
"eslint-config-canonical": "^8.1.1",
"mocha": "^3.2.0",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"sinon": "^2.1.0",
"sinon-chai": "^2.9.0"
},
"engines": {
"node": ">4"
},
"keywords": [
"react",
"carousel"
],
"license": "BSD-3-Clause",
"main": "./dist/index.js",
"name": "react-carousel",
"version": "3.3.0",
"description": "React carousel component.",
"main": "./src/index.js",
"license": "BSD-3-Clause",
"scripts": {
"lint": "eslint ./tests ./src",
"test": "echo \"Error: Fix tests script\""
},
"repository": {

@@ -15,20 +32,8 @@ "type": "git",

},
"engines": {
"node": "~5.2.0"
"scripts": {
"build": "rm -fr ./dist && NODE_ENV=production babel ./src --source-maps --copy-files --out-dir ./dist",
"lint": "eslint ./test ./src ./demo/src",
"test": "mocha --compilers js:babel-core/register"
},
"dependencies": {
"babel-plugin-lodash-modularize": "^0.3.2",
"babel-plugin-typecheck": "^3.9.0",
"babel-preset-es2015": "^6.13.2",
"babel-preset-react": "^6.11.1",
"babel-preset-stage-0": "^6.5.0",
"lodash": "^4.15.0"
},
"devDependencies": {
"chai": "^3.5.0",
"react": "^15.3.0",
"react-dom": "^15.3.0",
"sinon": "^1.17.5",
"sinon-chai": "^2.8.0"
}
"version": "4.0.0"
}

@@ -7,47 +7,19 @@ # react-carousel

General purpose carousel component build with and for React.js. Check a [demo here](http://gajus.github.io/react-carousel/)
A carousel.
This component is built for react apps that strictly follow flux (or redux) architecture. It tries to be a pure component in that all its requirements are fed to it as `props` of the component.
## Behaviour
#### Impurity
* The component does very little by itself. You provide it the input with `props`, and it renders it. To make any changes (like change active item, scroll items), you need to change `props` that you pass to it
This component is however not fully pure because it uses some state internally. It uses state to store the max-width of its container. There is no way around this. For the behavior required it need to know when its parent's width changes (so it can resize itself accordingly), and it need to re-render. Instead of delegating this operation (like all others) to parent, I've decided to do this internally using state.
Ideally, you keep state in a (redux) store, and pass that state as props to this component. The callbacks are used to manipulate the store which shall eventually change the props passed, and render the component.
## Usage
* The component will show scroll buttons (two buttons in front and end of the carousel) if number of items in the carousel exceed the total number of items that can be displayed at a time.
* Total number if items that can be displayed in the carousel at a time is determined by the width of the container in which this component is kept and the `itemWidth` property value.
* Component reacts to change in size of its container such that there is never a partial item dispayed in the carousel. Carousel will shrink in size if the container width can't be filled with "full" items. Please cehck the example for demonstration of this behavior and try resizing the window
* Previous scroll button is only displayed if there are items before `firstVisibleIndex`.
* Next scroll button is only displayed if there are items which can be scrolled left.
* `onItemActivate` will be given the `key` of the item that is clicked by the user
* `onItemsScroll` gets called when user clicks on next/previous scroll button. It gives **index** of the item which should become new `firstVisibleIndex`. This index' calculation depends on `scrollStepDistance` and total number of items displayed in the carousel
* Carousel performs no animations as of now during the transition (scroll)
This component exports es6 module, not es5. So you'll have to make some adjustments in your build system if you're compiling es6 to es5/3.
If you're using webpack, put this in your webpack config:
```js
.
.
loaders: [
{
include: [
/react-carousel\/src/
],
loader: 'babel',
test: /\.js$/
},
.
.
```
Similar setup would need to be done for browserify or other build system you're using.
## Behaviour
- The component does very little by itself. You provide it the input with `props`, and it renders it. To make any changes (like change active item, scroll items), you need to change `props` that you pass to it
Ideally, you keep state in a (flux/redux) store, and pass that state as props to this component. The callbacks should be used to manipulate the store which shall eventually change the props passed, and re-render the component with new changes
- The component will show scroll buttons (two buttons in front and end of carousel) if number of items in the carousel exceed the total number of items that can be displayed at a time
- Total number if items that can be displayed in the carousel at a time is determined by the width of the container in which this component is kept, and `itemWidth` prop
- Component reacts to change in size of its container such that there is never a partial item dispayed in the carousel. Carousel will shrink in size if the container width can't be filled with "full" items. Please cehck the example for demonstration of this behavior and try resizing the window
- Previous scroll button is only displayed if there are items before `firstVisibleIndex`
- Next scroll button is only displayed if there are items which can be scrolled left
- `onItemActivate` will be given the `key` of the item that is clicked by the user
- `onItemsScroll` gets called when user clicks on next/previous scroll button. It gives **index** of the item which should become new `firstVisibleIndex`. This index' calculation depends on `scrollStepDistance` and total number of items displayed in the carousel
- Carousel performs no animations as of now during the transition (scroll)
## Properties

@@ -84,5 +56,5 @@

## Example
## Demo
```sh
```bash
git clone git@github.com:applaudience/react-carousel.git

@@ -92,13 +64,3 @@ cd ./react-carousel/example

npm start
```
### Developing Example
For making changes in the component and checking them in development, you can run the example in dev mode. Dev mode assume you have exactly same directory structure for `example` and `src` directories's location as this repo. In dev mode, we add the component as an alias in webpack which allow us to check the changes instantaneously.
```sh
git clone git@github.com:gajus/react-carousel.git
cd ./react-carousel/example
npm install
npm run dev
```

@@ -1,261 +0,265 @@

/* eslint-disable valid-jsdoc, jsdoc/require-description-complete-sentence, jsdoc/require-param, jsdoc/check-param-names */
/* eslint-disable valid-jsdoc, jsdoc/require-description-complete-sentence, jsdoc/require-param, jsdoc/check-param-names, filenames/match-exported */
import React, {
Component,
PropTypes
Component,
PropTypes
} from 'react';
import _ from 'lodash';
import ReactDOM from 'react-dom';
/* eslint-disable react/no-set-state, react/no-did-mount-set-state */
/* eslint-disable react/no-set-state, react/no-did-mount-set-state, react/sort-prop-types */
class Carousel extends Component {
static propTypes = {
activeItemId: PropTypes.string,
controlWidth: PropTypes.number,
firstVisibleIndex: PropTypes.number,
itemMargin: PropTypes.number,
itemWidth: PropTypes.number,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
onItemActivate: PropTypes.func,
onItemsScroll: PropTypes.func,
scrollStepDistance: PropTypes.number
};
static propTypes = {
activeItemId: PropTypes.string,
controlWidth: PropTypes.number,
firstVisibleIndex: PropTypes.number,
itemMargin: PropTypes.number,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
itemWidth: PropTypes.number,
onItemActivate: PropTypes.func,
onItemsScroll: PropTypes.func,
scrollStepDistance: PropTypes.number
};
static defaultProps = {
controlWidth: 30,
firstVisibleIndex: 0,
itemMargin: 1,
itemWidth: 50,
scrollStepDistance: 3
};
static defaultProps = {
controlWidth: 30,
firstVisibleIndex: 0,
itemMargin: 1,
itemWidth: 50,
scrollStepDistance: 3
};
constructor (props) {
super(props);
constructor (props) {
super(props);
this.state = {maxWidth: null};
this.resizeEventListener = _.debounce(() => {
this.setState({maxWidth: ReactDOM.findDOMNode(this.refs.wrapper).offsetWidth});
}, 100);
}
this.state = {maxWidth: null};
this.resizeEventListener = _.debounce(() => {
this.setState({maxWidth: this.wrapperElement.offsetWidth});
}, 100);
}
componentDidMount () {
this.setState({maxWidth: ReactDOM.findDOMNode(this.refs.wrapper).offsetWidth});
window.addEventListener('resize', this.resizeEventListener);
}
componentDidMount () {
this.setState({maxWidth: this.wrapperElement.offsetWidth});
window.addEventListener('resize', this.resizeEventListener);
}
componentWillUnmount () {
window.removeEventListener('resize', this.resizeEventListener);
}
componentWillUnmount () {
window.removeEventListener('resize', this.resizeEventListener);
}
/**
* Get the index component should safely scroll to.
*
* @param {Object} options
* @param {number} options.totalItems Total number of items being displayed
* @param {number} options.currentVisibleIndex Index of item in front
* @param {string} options.direction next|previous Direction in which to scroll
* @param {number} options.visibleItemsCount Number of items displayed at one time in component
* @param {number} options.stepDistance Number of items to scroll in one step
*
* @returns {number} Index of item to which to scroll
*/
getIndexToScrollTo = ({totalItems, firstVisibleIndex, direction, visibleItemsCount, scrollStepDistance}) => {
let index;
/**
* Get the index component should safely scroll to.
*
* @param {Object} options
* @param {number} options.totalItems Total number of items being displayed
* @param {number} options.currentVisibleIndex Index of item in front
* @param {string} options.direction next|previous Direction in which to scroll
* @param {number} options.visibleItemsCount Number of items displayed at one time in component
* @param {number} options.stepDistance Number of items to scroll in one step
*
* @returns {number} Index of item to which to scroll
*/
getIndexToScrollTo = ({totalItems, firstVisibleIndex, direction, visibleItemsCount, scrollStepDistance}) => {
let index;
const itemsBeyondVisible = totalItems - firstVisibleIndex - visibleItemsCount;
const itemsBehindVisible = firstVisibleIndex;
const itemsBeyondVisible = totalItems - firstVisibleIndex - visibleItemsCount;
const itemsBehindVisible = firstVisibleIndex;
if (direction === 'next') {
index = firstVisibleIndex + (itemsBeyondVisible > scrollStepDistance ? scrollStepDistance : itemsBeyondVisible);
} else if (direction === 'previous') {
index = firstVisibleIndex - (itemsBehindVisible > scrollStepDistance ? scrollStepDistance : itemsBehindVisible);
}
if (direction === 'next') {
index = firstVisibleIndex + (itemsBeyondVisible > scrollStepDistance ? scrollStepDistance : itemsBeyondVisible);
} else if (direction === 'previous') {
index = firstVisibleIndex - (itemsBehindVisible > scrollStepDistance ? scrollStepDistance : itemsBehindVisible);
}
return index;
};
return index;
};
// Tells if the next button shall be visible or not.
isNextButtonVisible = (firstVisibleIndex: number, totalItems: number, visibleItemsCount: number) : boolean => {
return firstVisibleIndex < totalItems - visibleItemsCount;
};
/**
* Tells if the next button shall be visible or not.
*/
isNextButtonVisible = (firstVisibleIndex: number, totalItems: number, visibleItemsCount: number) : boolean => {
return firstVisibleIndex < totalItems - visibleItemsCount;
};
/**
* Tells if the previous control button shall be visible or not.
*/
isPrevButtonVisible = (firstVisibleIndex: number) : boolean => {
return firstVisibleIndex > 0;
};
/**
* Tells if the previous control button shall be visible or not.
*/
isPrevButtonVisible = (firstVisibleIndex: number) : boolean => {
return firstVisibleIndex > 0;
};
/**
* Get number of items that can be shown at a point in time.
*
* @param {Object} options Arguments in an object because they are too many
* @property {number} firstVisibleIndex Index to which the carousel is scrolled to i.e first visible index
* @property {number} maxWidth Maximum width available to the carousel
* @property {number} totalItems Total number of items to be shown
* @property {number} itemWidth Width of each item
* @property {number} controlWidth Width of each control button
* @property {number} itemMargin Margin between items
* @returns {number}
*/
getVisibleItemsCount = ({firstVisibleIndex, maxWidth, totalItems, itemWidth, controlWidth, itemMargin}) => {
let availableWidth;
let visibleItemsCount;
/**
* Get number of items that can be shown at a point in time.
* Get the number of visible items which can be shown at a time.
*
* @param {Object} options Arguments in an object because they are too many
* @property {number} firstVisibleIndex Index to which the carousel is scrolled to i.e first visible index
* @property {number} maxWidth Maximum width available to the carousel
* @property {number} totalItems Total number of items to be shown
* @property {number} itemWidth Width of each item
* @property {number} controlWidth Width of each control button
* @property {number} itemMargin Margin between items
* @param {number} fullWidth Total available width in which items are to be shown
* @param {number} elementWidth Width of each individual item to be inserted
* @param {number} elementMargin Margin between two items
* @returns {number}
*/
getVisibleItemsCount = ({firstVisibleIndex, maxWidth, totalItems, itemWidth, controlWidth, itemMargin}) => {
let availableWidth,
visibleItemsCount;
// Keeping this function inside because
// a) it's only need to be used internally in this function
// b) All names I could come up for it can confuse user for this.getVisibleItemsCount
const calculateVisibleItemsCount = (fullWidth, elementWidth, elementMargin) => {
return Math.floor(fullWidth / (elementWidth + elementMargin));
};
/**
* Get the number of visible items which can be shown at a time.
*
* @param {number} fullWidth Total available width in which items are to be shown
* @param {number} elementWidth Width of each individual item to be inserted
* @param {number} elementMargin Margin between two items
* @returns {number}
*/
// Keeping this function inside because
// a) it's only need to be used internally in this function
// b) All names I could come up for it can confuse user for this.getVisibleItemsCount
const calculateVisibleItemsCount = (fullWidth, elementWidth, elementMargin) => {
return Math.floor(fullWidth / (elementWidth + elementMargin));
};
availableWidth = maxWidth;
const prevButtonVisible = this.isPrevButtonVisible(firstVisibleIndex);
availableWidth = maxWidth;
const prevButtonVisible = this.isPrevButtonVisible(firstVisibleIndex);
if (prevButtonVisible) {
availableWidth -= controlWidth + itemMargin;
}
if (prevButtonVisible) {
availableWidth -= controlWidth + itemMargin;
}
visibleItemsCount = calculateVisibleItemsCount(availableWidth, itemWidth, itemMargin);
const nextButtonVisible = this.isNextButtonVisible(firstVisibleIndex, totalItems, visibleItemsCount);
visibleItemsCount = calculateVisibleItemsCount(availableWidth, itemWidth, itemMargin);
const nextButtonVisible = this.isNextButtonVisible(firstVisibleIndex, totalItems, visibleItemsCount);
if (nextButtonVisible) {
availableWidth -= controlWidth + itemMargin;
visibleItemsCount = calculateVisibleItemsCount(availableWidth, itemWidth, itemMargin);
}
if (nextButtonVisible) {
availableWidth -= controlWidth + itemMargin;
visibleItemsCount = calculateVisibleItemsCount(availableWidth, itemWidth, itemMargin);
}
return visibleItemsCount;
};
return visibleItemsCount;
};
/**
* Helper function to avoid typing so many variables twice for two control buttons since most of the variables are already in scope.
*
* @param {string} direction direction in which to scroll
* @param {number} visibleItemsCount Total number of visible items
* @returns {undefined}
*/
handleScrollToDirection = (direction: string, visibleItemsCount: number) => {
return () => {
const index = this.getIndexToScrollTo({
direction,
firstVisibleIndex: this.props.firstVisibleIndex,
scrollStepDistance: this.props.scrollStepDistance,
totalItems: this.props.items.length,
visibleItemsCount
});
/**
* Helper function to avoid typing so many variables twice for two control buttons since most of the variables are already in scope.
*
* @param {string} direction direction in which to scroll
* @param {number} visibleItemsCount Total number of visible items
* @returns {undefined}
*/
handleScrollToDirection = (direction: string, visibleItemsCount: number) => {
return () => {
const index = this.getIndexToScrollTo({
direction,
firstVisibleIndex: this.props.firstVisibleIndex,
scrollStepDistance: this.props.scrollStepDistance,
totalItems: this.props.items.length,
visibleItemsCount
});
this.props.onItemsScroll(index);
};
this.props.onItemsScroll(index);
};
};
handleActivateItem = (item) => {
this.props.onItemActivate(item.key);
};
handleActivateItem = (item) => {
this.props.onItemActivate(item.key);
};
/**
* Creates functoin that creates JSX for one item
*
* @param {number} visibleItemsCount Total number of visible items
*
* @returns {Function}
*/
getItemJsx = (visibleItemsCount: number) => {
/**
* Creates functoin that creates JSX for one item
* Creates JSX for one item
*
* @param {JSX} item Valid JSX for the item's content
* @param {number} index Index of item in the items array
* @param {number} visibleItemsCount Total number of visible items
*
* @returns {Function}
* @returns {JSX}
*/
getItemJsx = (visibleItemsCount: number) => {
/**
* Creates JSX for one item
*
* @param {JSX} item Valid JSX for the item's content
* @param {number} index Index of item in the items array
* @param {number} visibleItemsCount Total number of visible items
*
* @returns {JSX}
*/
return (item, index) => {
const self = this;
const visibleItemIndeces = _.range(this.props.firstVisibleIndex, visibleItemsCount + this.props.firstVisibleIndex);
const isVisible = _.includes(visibleItemIndeces, index);
return (item, index) => {
const self = this;
const visibleItemIndeces = _.range(this.props.firstVisibleIndex, visibleItemsCount + this.props.firstVisibleIndex);
const isVisible = _.includes(visibleItemIndeces, index);
/*
* Here we could return only those items which need be visible, but I am returning all.
* Since React does optimal DOM operations, rendering all items at once and then updating their
* style is much less expensive then rendering a set of new list-items everytime.
*/
return <li
className={'react-carousel__cell' + (this.props.activeItemId === item.key ? ' react-carousel__cell--active' : '')}
key={item.key}
onClick={function () {
self.handleActivateItem(item);
}}
style={{
display: isVisible ? 'list-item' : 'none',
marginRight: this.props.itemMargin,
width: this.props.itemWidth
}}
>
{item}
</li>;
};
/*
* Here we could return only those items which need be visible, but I am returning all.
* Since React does optimal DOM operations, rendering all items at once and then updating their
* style is much less expensive then rendering a set of new list-items everytime.
*/
return <li
className={'react-carousel__cell' + (this.props.activeItemId === item.key ? ' react-carousel__cell--active' : '')}
key={item.key}
onClick={function () {
self.handleActivateItem(item);
}}
style={{
display: isVisible ? 'list-item' : 'none',
marginRight: this.props.itemMargin,
width: this.props.itemWidth
}}
>
{item}
</li>;
};
};
render () {
// declaring so many variables instead of directly using props to stay consistent with the coding
// style adopted for other components
const maxWidth = this.state.maxWidth;
const items = this.props.items;
const controlWidth = this.props.controlWidth;
const firstVisibleIndex = this.props.firstVisibleIndex;
const itemWidth = this.props.itemWidth;
const itemMargin = this.props.itemMargin;
const totalItems = items.length;
const prevButtonVisible = this.isPrevButtonVisible(firstVisibleIndex);
const visibleItemsCount = this.getVisibleItemsCount({
controlWidth,
firstVisibleIndex,
itemMargin,
itemWidth,
maxWidth,
totalItems
});
const nextButtonVisible = this.isNextButtonVisible(firstVisibleIndex, totalItems, visibleItemsCount);
render () {
const {
controlWidth,
firstVisibleIndex,
itemMargin,
items,
itemWidth,
maxWidth
} = this.props;
const totalItems = items.length;
const prevButtonVisible = this.isPrevButtonVisible(firstVisibleIndex);
const visibleItemsCount = this.getVisibleItemsCount({
controlWidth,
firstVisibleIndex,
itemMargin,
itemWidth,
maxWidth,
totalItems
});
const nextButtonVisible = this.isNextButtonVisible(firstVisibleIndex, totalItems, visibleItemsCount);
return <div className='react-carousel' ref='wrapper'>
<ul className='clearfix react-carousel__body'>
return <div
className='react-carousel' ref={(element) => {
this.wrapperElement = element;
}}>
<ul className='clearfix react-carousel__body'>
<li
className='react-carousel__control-cell carousel__control-cell--previous clearfix'
onClick={this.handleScrollToDirection('previous', visibleItemsCount)}
style={{
display: prevButtonVisible ? 'list-item' : 'none',
marginRight: itemMargin,
width: controlWidth
}}
>
<span className='react-carousel__control-cell__icon--previous'></span>
</li>
<li
className='react-carousel__control-cell carousel__control-cell--previous clearfix'
onClick={this.handleScrollToDirection('previous', visibleItemsCount)}
style={{
display: prevButtonVisible ? 'list-item' : 'none',
marginRight: itemMargin,
width: controlWidth
}}
>
<span className='react-carousel__control-cell__icon--previous' />
</li>
{_.map(items, this.getItemJsx(visibleItemsCount))}
{_.map(items, this.getItemJsx(visibleItemsCount))}
<li
className='react-carousel__control-cell react-carousel__control-cell--next clearfix'
onClick={this.handleScrollToDirection('next', visibleItemsCount)}
style={{
display: nextButtonVisible ? 'list-item' : 'none',
width: controlWidth
}}
>
<span className='react-carousel__control-cell__icon--next'></span>
</li>
<li
className='react-carousel__control-cell react-carousel__control-cell--next clearfix'
onClick={this.handleScrollToDirection('next', visibleItemsCount)}
style={{
display: nextButtonVisible ? 'list-item' : 'none',
width: controlWidth
}}
>
<span className='react-carousel__control-cell__icon--next' />
</li>
</ul>
</div>;
}
</ul>
</div>;
}
}
export default Carousel;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc