Skip to content

Commit ef27082

Browse files
author
wfbn8821
committed
start to move to stack layout
1 parent 18fe20d commit ef27082

File tree

8 files changed

+160
-57
lines changed

8 files changed

+160
-57
lines changed

example/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ const ScrollableTitleCard = React.createClass({
1010
})
1111
},
1212
render() {
13-
const {title, children} = this.props
13+
const {title, children, className, style} = this.props
1414
return (
15-
<div className="col-sm-6 col-md-4">
15+
<div className={"col-sm-6 col-md-4 "+className} style={style}>
1616
<div className="card">
1717
<div ref="title" className="card-header">
1818
{title}
1919
</div>
20-
<div className="card-body">
20+
<div className="card-block">
2121
{children}
2222
</div>
2323
</div>

lib/assets/styles.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
.row._1H7C65wd8WJqBtCRmAfokb {
2-
display: flex;
3-
flex-wrap: nowrap;
42
position: relative; }
53

4+
._1H7C65wd8WJqBtCRmAfokb ._358enaiolY7BZPuXLuxdRA {
5+
position: absolute;
6+
transition: left 1s; }
7+
68
._22yQcZhJvmCSA52vE9VrUe {
79
position: fixed;
810
top: 40%;

lib/index.js

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@ var _reactMotion = require('react-motion');
1818

1919
var _styles = {
2020
"container": "_1H7C65wd8WJqBtCRmAfokb",
21+
"stack": "_358enaiolY7BZPuXLuxdRA",
2122
"rightArrow": "_22yQcZhJvmCSA52vE9VrUe",
2223
"arrow": "_1y88-b9gQ_tGRfPBQpxMfo",
2324
"leftArrow": "_1bIeR72ojeoWY-MxcIhjT-"
2425
};
2526

2627
var _styles2 = _interopRequireDefault(_styles);
2728

29+
var _offset = require('./offset');
30+
2831
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2932

3033
var defaultWidths = {
@@ -35,13 +38,14 @@ var defaultWidths = {
3538
var CardScroll = _react2.default.createClass({
3639
displayName: 'CardScroll',
3740
getInitialState: function getInitialState() {
38-
return { currentLeft: 0, currentCard: 0 };
41+
return { currentCard: 0 };
3942
},
4043
componentWillMount: function componentWillMount() {
4144
this.widths = defaultWidths;
4245
this.children = {};
4346
},
4447
componentDidMount: function componentDidMount() {
48+
this.maxOffset = (0, _offset.getMaxOffset)();
4549
this.widths = this.computeWidths();
4650
this.setState(this.getInitialState());
4751
window.addEventListener('resize', this.handleResize);
@@ -82,14 +86,16 @@ var CardScroll = _react2.default.createClass({
8286
render: function render() {
8387
var _this2 = this;
8488

85-
var currentLeft = this.state.currentLeft;
89+
var currentCard = this.state.currentCard;
8690

91+
var lastCard = this.lastVisibleCardIndex();
8792
var updateContainer = function updateContainer(c) {
8893
return c && (_this2._container = c);
8994
};
9095
var updateChild = function updateChild(c) {
9196
return c && (_this2._child = c);
9297
};
98+
var first = true;
9399
return _react2.default.createElement(
94100
'div',
95101
null,
@@ -106,27 +112,35 @@ var CardScroll = _react2.default.createClass({
106112
_react2.default.createElement('div', { className: _styles2.default.arrow })
107113
) : null,
108114
_react2.default.createElement(
109-
_reactMotion.Motion,
110-
{ style: { left: (0, _reactMotion.spring)(currentLeft) } },
111-
function (value) {
112-
var first = true;
113-
return _react2.default.createElement(
114-
'div',
115-
{ className: 'row ' + _styles2.default.container,
116-
style: value,
117-
ref: updateContainer },
118-
_react2.default.Children.map(_this2.props.children, function (child) {
119-
if (first) {
120-
first = false;
121-
return _react2.default.cloneElement(child, {
122-
ref: updateChild
123-
});
124-
} else {
125-
return child;
126-
}
127-
})
128-
);
129-
}
115+
'div',
116+
{ className: 'row ' + _styles2.default.container,
117+
style: { marginLeft: this.maxOffset, marginRight: this.maxOffset },
118+
ref: updateContainer },
119+
_react2.default.Children.map(this.props.children, function (child, index) {
120+
var props = { className: _styles2.default.stack };
121+
122+
if (first) {
123+
first = false;
124+
props.ref = updateChild;
125+
}
126+
127+
var offset = (0, _offset.getOffset)({ index: index, firstVisibleIndex: currentCard, lastVisibleIndex: lastCard, cardWidth: _this2.widths.card });
128+
var position = offset;
129+
var zIndex = 0;
130+
if (offset == 0) {
131+
position = (index - currentCard) * _this2.widths.card;
132+
if (index == lastCard) {
133+
zIndex = -1;
134+
}
135+
} else if (offset > 0) {
136+
position = offset + (lastCard - currentCard) * _this2.widths.card;
137+
zIndex = lastCard - index - 1;
138+
}
139+
props.style = { left: position, zIndex: zIndex };
140+
props.className = _styles2.default.stack;
141+
142+
return _react2.default.cloneElement(child, props);
143+
})
130144
)
131145
);
132146
},

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "react-card-scroll",
3-
"version": "1.1.1",
3+
"version": "2.0.0",
44
"description": "A React component to navigate horizontally between cards of same width",
55
"main": "lib/index.js",
66
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1",
7+
"test": "babel-tape-runner test/**.js",
88
"build": "BABEL_DISABLE_CACHE=1 NODE_ENV=LIB babel -d lib/ src",
99
"dev": "BABEL_DISABLE_CACHE=1 NODE_ENV=LIB babel -d lib/ src -w",
1010
"example": "node webpack.example.js",
@@ -31,6 +31,7 @@
3131
"babel-preset-es2015": "^6.6.0",
3232
"babel-preset-react": "^6.5.0",
3333
"babel-preset-stage-1": "^6.5.0",
34+
"babel-tape-runner": "^2.0.1",
3435
"bootstrap": "^4.0.0-alpha.2",
3536
"css-loader": "^0.23.1",
3637
"extract-text-webpack-plugin": "^1.0.1",
@@ -40,11 +41,11 @@
4041
"react-dom": "^15.0.1",
4142
"sass-loader": "^3.2.0",
4243
"style-loader": "^0.13.1",
44+
"tape": "^4.5.1",
4345
"webpack": "^1.13.0",
4446
"webpack-dev-server": "^1.14.1"
4547
},
4648
"dependencies": {
47-
"lodash": "^4.11.1",
48-
"react-motion": "^0.4.2"
49+
"lodash": "^4.11.1"
4950
}
5051
}

src/index.js

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom'
33
import {throttle} from 'lodash'
44
import {Motion, spring} from 'react-motion'
55
import s from './styles.scss'
6+
import {getOffset, getMaxOffset} from './offset'
67

78
const defaultWidths = {
89
card: 0,
@@ -11,7 +12,7 @@ const defaultWidths = {
1112

1213
let CardScroll = React.createClass({
1314
getInitialState(){
14-
return {currentLeft: 0, currentCard: 0}
15+
return {currentCard: 0}
1516
},
1617

1718
componentWillMount() {
@@ -20,11 +21,12 @@ let CardScroll = React.createClass({
2021
},
2122

2223
componentDidMount() {
24+
this.maxOffset = getMaxOffset()
2325
this.widths = this.computeWidths()
2426
this.setState(this.getInitialState())
2527
window.addEventListener('resize', this.handleResize);
2628
},
27-
29+
2830
componentDidUpdate(){
2931
// case children were removed
3032
if(this.canScrollLeft() && this.lastVisibleCardIndex()>=React.Children.count(this.props.children)){
@@ -61,9 +63,11 @@ let CardScroll = React.createClass({
6163
},
6264

6365
render() {
64-
const {currentLeft} = this.state
66+
const {currentCard} = this.state
67+
const lastCard = this.lastVisibleCardIndex()
6568
const updateContainer = c => c && (this._container = c)
6669
const updateChild = c => c && (this._child= c)
70+
let first = true
6771
return (
6872
<div>
6973
{this.canScrollLeft()?
@@ -78,27 +82,35 @@ let CardScroll = React.createClass({
7882
<div className={s.arrow}></div>
7983
</div>
8084
:null}
81-
<Motion style={{left: spring(currentLeft)}}>
82-
{value => {
83-
let first = true
84-
return (<div className={`row ${s.container}`}
85-
style={value}
86-
ref={updateContainer}>
87-
{React.Children.map(this.props.children, child => {
88-
if(first){
89-
first = false
90-
return React.cloneElement(child, {
91-
ref: updateChild
92-
});
93-
} else {
94-
return child
85+
<div className={`row ${s.container}`}
86+
style={{marginLeft: this.maxOffset, marginRight: this.maxOffset}}
87+
ref={updateContainer}>
88+
{React.Children.map(this.props.children, (child, index) => {
89+
let props = {className: s.stack}
90+
91+
if(first){
92+
first = false
93+
props.ref= updateChild
94+
}
95+
96+
const offset = getOffset({index, firstVisibleIndex:currentCard, lastVisibleIndex:lastCard, cardWidth:this.widths.card})
97+
let position = offset
98+
let zIndex = 0
99+
if(offset==0){
100+
position = (index-currentCard)*this.widths.card
101+
if(index == lastCard){
102+
zIndex = -1
95103
}
96-
97-
})}
98-
99-
</div>)
100-
}}
101-
</Motion>
104+
} else if(offset>0){
105+
position = offset + (lastCard-currentCard)*this.widths.card
106+
zIndex = lastCard-index-1
107+
}
108+
props.style = {left: position, zIndex}
109+
props.className = s.stack
110+
111+
return React.cloneElement(child, props);
112+
})}
113+
</div>
102114
</div>
103115
)
104116
},

src/offset.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
3+
export const getOffset = ({index, firstVisibleIndex, lastVisibleIndex, visibleStack=3, stackSpace=5}) => {
4+
if(index >= firstVisibleIndex && index <=lastVisibleIndex){
5+
return 0
6+
}
7+
let offset = index - firstVisibleIndex
8+
if(offset>0){
9+
offset = index - lastVisibleIndex
10+
}
11+
if(Math.abs(offset) > visibleStack +1){
12+
return Math.sign(offset)*((visibleStack+1)*stackSpace+1)
13+
}
14+
return offset*stackSpace
15+
}
16+
17+
export const getMaxOffset = ({visibleStack=3, stackSpace=5}={}) => ((visibleStack+1)*stackSpace+1)

src/styles.scss

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
:global(.row).container {
2-
display: flex;
3-
flex-wrap: nowrap;
42
position: relative;
53
}
64

5+
.container .stack{
6+
position: absolute;
7+
transition: left 1s;
8+
}
9+
710
$border-color: #ccc;
811
$padding: 4px;
912
$outMargin: -15px;

test/index.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import test from 'tape'
2+
import {getOffset} from '../src/offset'
3+
4+
test('getOffset() all visible', assert => {
5+
const msg = "no offset for all cards"
6+
const expected = [0, 0, 0]
7+
const testGetOffset = index => getOffset({index, firstVisibleIndex:0, lastVisibleIndex:2})
8+
const actual = [0,1,2].map((e, i) => testGetOffset(i))
9+
assert.deepEqual(actual, expected, msg)
10+
assert.end()
11+
})
12+
13+
test('getOffset() 1 hidden on left', assert => {
14+
const msg = "only first card offset of -2"
15+
const stackSpace = 2
16+
const expected = [-stackSpace, 0, 0, 0]
17+
const testGetOffset = index => getOffset({index, firstVisibleIndex:1, lastVisibleIndex:3, stackSpace})
18+
const actual = [0,1,2,3].map(i => testGetOffset(i))
19+
assert.deepEqual(actual, expected, msg)
20+
assert.end()
21+
})
22+
23+
test('getOffset() 6 hidden on left', assert => {
24+
const msg = "two most left are on top of each others"
25+
const stackSpace = 2
26+
const visibleStack = 3
27+
const expected = [-4*stackSpace-1, -4*stackSpace-1,-4*stackSpace,-3*stackSpace,-2*stackSpace,-stackSpace, 0, 0, 0]
28+
const testGetOffset = index => getOffset({index, firstVisibleIndex:6, lastVisibleIndex:8, stackSpace, visibleStack})
29+
const actual = [0,1,2,3,4,5,6,7,8].map(i => testGetOffset(i))
30+
assert.deepEqual(actual, expected, msg)
31+
assert.end()
32+
})
33+
34+
test('getOffset() 1 hidden on each side', assert => {
35+
const msg = "first and last card offset"
36+
const stackSpace = 2
37+
const expected = [-stackSpace, 0, 0, 0, stackSpace]
38+
const testGetOffset = index => getOffset({index, firstVisibleIndex:1, lastVisibleIndex:3, stackSpace})
39+
const actual = [0,1,2,3,4].map(i => testGetOffset(i))
40+
assert.deepEqual(actual, expected, msg)
41+
assert.end()
42+
})
43+
44+
test('getOffset() 6 hidden on each side', assert => {
45+
const msg = "offsets"
46+
const stackSpace = 2
47+
const visibleStack = 3
48+
const expected = [-4*stackSpace-1, -4*stackSpace-1,-4*stackSpace,-3*stackSpace,-2*stackSpace,-stackSpace, 0, 0, 0,
49+
stackSpace, 2*stackSpace, 3*stackSpace,4*stackSpace, 4*stackSpace+1, 4*stackSpace+1]
50+
const testGetOffset = index => getOffset({index, firstVisibleIndex:6, lastVisibleIndex:8, stackSpace, visibleStack})
51+
const actual = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14].map(i => testGetOffset(i))
52+
assert.deepEqual(actual, expected, msg)
53+
assert.end()
54+
})

0 commit comments

Comments
 (0)