Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 24 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
# react-card-scroll
A React component to horizontally navigate between components of same width (Bootstrap cards for example).
It is responsive and support card adds and removes.
It is responsive and support dynamic card adds and removes.

You decide how many cards will be simultaneously visible depending on screen size, in the bootstrap style. You can then navigate to the other cards depending on how you want to implement it. If you don't want to implement anything, you can use default arrows.

For example you can have 3 cards on the screen and there is a total of 5 cards, you can navigate right or left to the cards not displayed.
You resize and you display only 2 cards, you can still navigate to the other cards.
Visually you have a left stack of cards, visible cards in the middle, and a right stack.

Navigation can also be triggered programmatically.

## New in 1.x
No need to give card width and card count anymore
## New in 2.x
Using stacks instead of sliding cards out of the screen
Doesn't use react-motion anymore

## Installation
```bash
Expand All @@ -18,24 +17,33 @@ npm i -S react-card-scroll

## Usage

Import css ```~react-card-scroll/lib/assets/styles.css```
Import css either in sass ```~react-card-scroll/lib/assets/styles.css``` or in javascript with webpack

Use bootstrap style to decide how many cards will be visible (just add ```rcs-```): ```rcs-col-*-*```. You don't have to include bootstrap.

Choose how you want to implement the navigation by using ```scrollCards({toLeft: true/false, number:*})```.For example, you can scroll when you click on a stack or when you wheel your mouse with the pointer on a card title.
See the example: ```npm run example``` and open localhost:8081. And get inspiration from the source code.

You can know where a card is with the ```getCardOffset``` function in JavaScript, or with the CSS classes ```rcs-left-stack rcs-center rcs-right-stack```

Use arrows with props ```showArrows={true}```

## Some (very) basic usage
```jsx
<CardScroll ref="cardScroll">
<div className="col-sm-6 col-md-4">
<div className="rcs-col-sm-6 rcs-col-md-4">
</div>

<div className="col-sm-6 col-md-4">
<div className="rcs-col-sm-6 rcs-col-md-4">
</div>

<div className="col-sm-6 col-md-4">
<div className="rcs-col-sm-6 rcs-col-md-4">
</div>

<div className="col-sm-6 col-md-4">
<div className="rcs-col-sm-6 rcs-col-md-4">
</div>

<div className="col-sm-6 col-md-4">
<div className="rcs-col-sm-6 rcs-col-md-4">
</div>
</CardScroll>
```
Expand All @@ -46,15 +54,14 @@ From parent, trigger navigation when you want (add or remove cards, click somewh
this.refs.cardScroll.scrollCards({toLeft: true, number:1})
```



## TODO
- [ ] Tests
- [x] Tests
- [x] Example
- Features
- [x] CSS arrows
- [x] Disable arrows if unable to navigate
~~- [ ] Mouse scroll triggers horizontal scroll~~ (it is up to the user, see example)
- [ ] ~~Mouse scroll triggers horizontal scroll~~ (it is up to the user, see example)
- [x] Stacks
- Fix
- [x] Remove horizontal scroll bar
- Technical
Expand Down
2 changes: 1 addition & 1 deletion example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8">
<title>React-card-scroll example</title>
</head>
<body style="overflow-x: hidden">
<body>
<div id="example"></div>
</body>
</html>
22 changes: 15 additions & 7 deletions example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,26 @@ import CardScroll from '../lib'
const ScrollableTitleCard = React.createClass({
componentDidMount() {
this.refs.title.addEventListener('wheel', event => {
this.props.onScrollTitle({toLeft:event.wheelDelta>0})
this.props.scrollCards({toLeft:event.wheelDelta>0})
event.preventDefault()
})
},
render() {
const {title, children} = this.props
const {title, children, className, style} = this.props
const onClick = ev => {
const offset = this.props.getCardOffset()
if(offset!=0){
ev.preventDefault()
this.props.scrollCards({toLeft:offset<0})
}
}
return (
<div className="col-sm-6 col-md-4">
<div className="card">
<div className={"col rcs-col-sm-6 rcs-col-md-4 rcs-col-lg-3 "+className} style={style}>
<div onClick={onClick} className="card">
<div ref="title" className="card-header">
{title}
</div>
<div className="card-body">
<div className="card-block">
{children}
</div>
</div>
Expand Down Expand Up @@ -64,13 +71,14 @@ const Example = React.createClass({
},

render() {
const onScrollTitle = params => this.refs.cardScroll.scrollCards(params)
const scrollCards = params => this.refs.cardScroll.scrollCards(params)
const getCardOffset = index => () => this.refs.cardScroll.getCardOffset(index)
return (
<div>
<button onClick={this.addCard}>Add card</button>
<button onClick={this.removeCard}>Remove last card</button>
<CardScroll ref="cardScroll">
{this.state.cards.map(el => React.cloneElement(el, {onScrollTitle}))}
{this.state.cards.map((el, index) => React.cloneElement(el, {scrollCards, getCardOffset:getCardOffset(index)}))}
</CardScroll>
</div>
)
Expand Down
16 changes: 16 additions & 0 deletions example/theme.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
$enable-flex: true;

@import "../node_modules/bootstrap/scss/bootstrap";

@import "../lib/assets/styles.css";

.col{
pointer-events: none;
}

.card {
pointer-events: auto;
}

.card-header{
&:hover{
background-image: linear-gradient(0, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
}
}

.rcs-left-stack,.rcs-right-stack{
.card:hover {
background-image: linear-gradient(0, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
cursor: pointer;
}
}
158 changes: 154 additions & 4 deletions lib/assets/styles.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
.row._1H7C65wd8WJqBtCRmAfokb {
display: flex;
flex-wrap: nowrap;
position: relative; }
._1H7C65wd8WJqBtCRmAfokb {
position: relative;
box-sizing: border-box; }

._1H7C65wd8WJqBtCRmAfokb ._358enaiolY7BZPuXLuxdRA {
position: absolute;
transition: left 1s cubic-bezier(0, 0.7, 0.7, 1);
box-sizing: border-box; }

._22yQcZhJvmCSA52vE9VrUe {
position: fixed;
Expand Down Expand Up @@ -49,3 +53,149 @@
._1bIeR72ojeoWY-MxcIhjT-:hover {
background: linear-gradient(to left, #ddd, #ccc);
margin-left: 0; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-1, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-2, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-3, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-4, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-5, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-6, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-7, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-8, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-9, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-10, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-11, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-12, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-1, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-2, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-3, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-4, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-5, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-6, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-7, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-8, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-9, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-10, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-11, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-12, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-1, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-2, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-3, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-4, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-5, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-6, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-7, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-8, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-9, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-10, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-11, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-12, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-1, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-2, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-3, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-4, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-5, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-6, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-7, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-8, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-9, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-10, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-11, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-12, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-1, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-2, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-3, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-4, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-5, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-6, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-7, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-8, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-9, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-10, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-11, ._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-12 {
min-height: 1px;
padding-left: 0.9375rem;
padding-right: 0.9375rem;
width: 100%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-1 {
width: 8.33333%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-2 {
width: 16.66667%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-3 {
width: 25%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-4 {
width: 33.33333%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-5 {
width: 41.66667%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-6 {
width: 50%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-7 {
width: 58.33333%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-8 {
width: 66.66667%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-9 {
width: 75%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-10 {
width: 83.33333%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-11 {
width: 91.66667%; }

._1H7C65wd8WJqBtCRmAfokb .rcs-col-xs-12 {
width: 100%; }

@media (min-width: 544px) {
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-1 {
width: 8.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-2 {
width: 16.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-3 {
width: 25%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-4 {
width: 33.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-5 {
width: 41.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-6 {
width: 50%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-7 {
width: 58.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-8 {
width: 66.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-9 {
width: 75%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-10 {
width: 83.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-11 {
width: 91.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-sm-12 {
width: 100%; } }

@media (min-width: 768px) {
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-1 {
width: 8.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-2 {
width: 16.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-3 {
width: 25%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-4 {
width: 33.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-5 {
width: 41.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-6 {
width: 50%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-7 {
width: 58.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-8 {
width: 66.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-9 {
width: 75%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-10 {
width: 83.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-11 {
width: 91.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-md-12 {
width: 100%; } }

@media (min-width: 992px) {
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-1 {
width: 8.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-2 {
width: 16.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-3 {
width: 25%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-4 {
width: 33.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-5 {
width: 41.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-6 {
width: 50%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-7 {
width: 58.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-8 {
width: 66.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-9 {
width: 75%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-10 {
width: 83.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-11 {
width: 91.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-lg-12 {
width: 100%; } }

@media (min-width: 1200px) {
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-1 {
width: 8.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-2 {
width: 16.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-3 {
width: 25%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-4 {
width: 33.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-5 {
width: 41.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-6 {
width: 50%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-7 {
width: 58.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-8 {
width: 66.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-9 {
width: 75%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-10 {
width: 83.33333%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-11 {
width: 91.66667%; }
._1H7C65wd8WJqBtCRmAfokb .rcs-col-xl-12 {
width: 100%; } }
Loading