| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154 |
134x
134x
134x
134x
268x
9x
9x
9x
9x
2x
7x
7x
7x
30x
7x
30x
7x
7x
7x
60x
| import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { lucidClassNames } from '../../util/style-helpers';
import { createClass, getFirst, omitProps } from '../../util/component-types';
import SlidePanel from '../SlidePanel/SlidePanel';
const cx = lucidClassNames.bind('&-InfiniteSlidePanel');
const { func, node, number, oneOfType, string } = PropTypes;
const modulo = (n, a) => a - n * Math.floor(a / n);
/** {"categories": ["helpers"], "madeFrom": ["SlidePanel"]}
*
* A container for rendering an infinite set of horizontal slides. Translation
* between slides is controlled by passing in a new `offset`. Can hook into
* touch events to update the `offset`. This component is made from SlidePanel,
* so it accepts the same props.
*/
const InfiniteSlidePanel = createClass({
displayName: 'InfiniteSlidePanel',
_isPrivate: true,
components: {
Slide: createClass({
displayName: 'InfiniteSlidePanel.Slide',
propName: 'Slide',
}),
},
propTypes: {
/**
* Appended to the component-specific class names set on the root element.
*/
className: string,
/**
* The only allowed child is a render function which is passed the current
* slide's offset and returns the slide contents:
* `(slideOffset) => { //returns React.PropTypes.node }`
* Alternatively, you could pass one <InfiniteSlidePanel.Slide {...}>
* element with the render function. The only reason do to the latter is to
* pass addiontal props to the slide element.
*/
children: oneOfType([node, func]),
/**
* The offset of the left-most rendered slide.
*/
offset: number,
/**
* Max number of viewable slides to show simultaneously.
*/
slidesToShow: number,
/**
* Called when a user's swipe would change the offset. Callback passes
* number of slides by the user (positive for forward swipes, negative for
* backwards swipes).
*
* Signature: `(slidesSwiped, { event, props }) => {}`
*/
onSwipe: func,
/**
* The number of slides rendered at any given time. A good rule-of-thumb is
* that this should be at least 4 times the `slidesToShow` value.
*/
totalSlides: number,
},
getDefaultProps() {
return {
offset: 0,
slidesToShow: 1,
onSwipe: _.noop,
totalSlides: 8,
};
},
render: function() {
const {
children,
className,
offset,
slidesToShow,
onSwipe,
totalSlides,
...passThroughs
} = this.props;
const slide = getFirst(
this.props,
InfiniteSlidePanel.Slide,
<InfiniteSlidePanel.Slide>{children}</InfiniteSlidePanel.Slide>
);
const slideChildRenderFunction = slide.props.children;
if (!_.isFunction(slideChildRenderFunction)) {
throw new Error(
'InfiniteSlidePanel children must be a single function `(slideOffset) => { /* returns React.PropTypes.node */ }`'
);
}
const halfSlides = Math.floor(totalSlides / 2);
const circularOffset = modulo(totalSlides, offset);
const forwardSlideOffsets = _.times(
totalSlides - halfSlides,
n => offset + n
);
const backwardSlideOffsets = _.times(
halfSlides,
n => offset + n - halfSlides
);
const transposedSlideOffsets = forwardSlideOffsets.concat(
backwardSlideOffsets
);
const slideOffsetArray = _.takeRight(
transposedSlideOffsets,
circularOffset
).concat(_.take(transposedSlideOffsets, totalSlides - circularOffset));
return (
<SlidePanel
{...omitProps(passThroughs, InfiniteSlidePanel, [], false)}
className={cx('&', className)}
offset={offset}
slidesToShow={slidesToShow}
onSwipe={onSwipe}
isLooped
>
{_.map(slideOffsetArray, (slideOffset, elementOffset) => (
<SlidePanel.Slide
key={elementOffset}
{...slide.props}
className={cx(
{
'&-Slide-in-frame': slideOffset - offset < slidesToShow &&
slideOffset - offset >= 0,
},
slide.props.className
)}
>
{slideChildRenderFunction(slideOffset)}
</SlidePanel.Slide>
))}
</SlidePanel>
);
},
});
export default InfiniteSlidePanel;
|