Skip to content

Commit

Permalink
Merge 781a8dc into 3e215d4
Browse files Browse the repository at this point in the history
  • Loading branch information
jussi-kalliokoski committed Dec 5, 2014
2 parents 3e215d4 + 781a8dc commit 2deb4ec
Show file tree
Hide file tree
Showing 8 changed files with 572 additions and 92 deletions.
99 changes: 8 additions & 91 deletions Carousel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,14 @@

var React = require("react");
var Swipable = require("../Swipable");
var range = require("../utils/range");
var CarouselMixin = require("../CarouselMixin");
var getClassName = require("../utils/getClassName");
var setPosition = require("../utils/setPosition").setPosition;
var noop = require("../utils/noop");

module.exports = React.createClass({
displayName: "Carousel",

mixins: [Swipable],

propTypes: {
baseClass: React.PropTypes.string,
cacheSize: React.PropTypes.number,
embedWidth: React.PropTypes.number,
embedHeight: React.PropTypes.number,
pageIndex: React.PropTypes.number,
previousPageIndex: React.PropTypes.number,
loop: React.PropTypes.bool,
renderEmptyPages: React.PropTypes.bool,
pages: React.PropTypes.arrayOf(React.PropTypes.any).isRequired,
pageView: React.PropTypes.func.isRequired,
onSwiped: React.PropTypes.func,
swipeThreshold: React.PropTypes.number,
swipeCancelThreshold: React.PropTypes.number,
},

getDefaultProps: function () {
return {
baseClass: "merry-go-round",
cacheSize: 1,
embedWidth: 0,
embedHeight: 0,
pageIndex: 0,
previousPageIndex: 0,
loop: false,
renderEmptyPages: false,
onSwiped: noop,
swipeThreshold: 10,
swipeCancelThreshold: 10,
};
},
mixins: [Swipable, CarouselMixin],

isMoving: function () {
return this.props.pageIndex !== this.props.previousPageIndex;
Expand All @@ -56,62 +23,12 @@ module.exports = React.createClass({
};
},

isIndexWithinBounds: function (index) {
return index >= 0 && index < this.props.pages.length;
},

normalizeIndex: function (index) {
if ( !this.props.loop ) { return index; }

while ( index < 0 ) {
index += this.props.pages.length;
}

return index % this.props.pages.length;
},

isIndexInView: function (index, viewIndex) {
return Math.abs(index - viewIndex) <= this.props.cacheSize;
},

calculateBuffers: function () {
var first = Math.min(this.props.previousPageIndex, this.props.pageIndex) - this.props.cacheSize;
var last = Math.max(this.props.previousPageIndex, this.props.pageIndex) + this.props.cacheSize;
var indices = range(first, last + 1);

return indices.map(function calculateBuffer (index) {
return {
index: index,
pageIndex: this.normalizeIndex(index),
willBeDiscarded: !this.isIndexInView(this.props.pageIndex, index),
isNew: !this.isIndexInView(this.props.previousPageIndex, index),
};
}.bind(this));
},

renderPages: function () {

var PageView = this.props.pageView;
var buffers = this.calculateBuffers();

return buffers.map(function renderBuffer (buffer) {
var pageView;

if ( this.props.renderEmptyPages || this.isIndexWithinBounds(buffer.pageIndex) ) {
pageView = PageView({
page: this.props.pages[buffer.pageIndex],
index: buffer.pageIndex,
willBeDiscarded: buffer.willBeDiscarded,
isNew: buffer.isNew,
});
}

return React.DOM.div({
className: this.props.baseClass + "__page",
key: buffer.index,
style: this.calculatePageStyle(buffer.index),
}, pageView);
}.bind(this));
renderPage: function (buffer, pageView) {
return React.DOM.div({
className: this.props.baseClass + "__page",
key: buffer.index,
style: this.calculatePageStyle(buffer.index),
}, pageView);
},

calculateSliderStyle: function () {
Expand Down
94 changes: 94 additions & 0 deletions CarouselMixin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"use strict";

var React = require("react");
var range = require("../utils/range");
var noop = require("../utils/noop");

var CarouselMixin = {
propTypes: {
baseClass: React.PropTypes.string,
cacheSize: React.PropTypes.number,
embedWidth: React.PropTypes.number,
embedHeight: React.PropTypes.number,
pageIndex: React.PropTypes.number,
previousPageIndex: React.PropTypes.number,
loop: React.PropTypes.bool,
renderEmptyPages: React.PropTypes.bool,
pages: React.PropTypes.arrayOf(React.PropTypes.any).isRequired,
pageView: React.PropTypes.func.isRequired,
onSwiped: React.PropTypes.func,
swipeThreshold: React.PropTypes.number,
swipeCancelThreshold: React.PropTypes.number,
},

getDefaultProps: function () {
return {
baseClass: "merry-go-round",
cacheSize: 1,
embedWidth: 0,
embedHeight: 0,
pageIndex: 0,
previousPageIndex: 0,
loop: false,
renderEmptyPages: false,
onSwiped: noop,
swipeThreshold: 10,
swipeCancelThreshold: 10,
};
},

isIndexWithinBounds: function (index) {
return index >= 0 && index < this.props.pages.length;
},

normalizeIndex: function (index) {
if ( !this.props.loop ) { return index; }

while ( index < 0 ) {
index += this.props.pages.length;
}

return index % this.props.pages.length;
},

isIndexInView: function (index, viewIndex) {
return Math.abs(index - viewIndex) <= this.props.cacheSize;
},

calculateBuffers: function () {
var first = Math.min(this.props.previousPageIndex, this.props.pageIndex) - this.props.cacheSize;
var last = Math.max(this.props.previousPageIndex, this.props.pageIndex) + this.props.cacheSize;
var indices = range(first, last + 1);

return indices.map(function calculateBuffer (index) {
return {
index: index,
pageIndex: this.normalizeIndex(index),
willBeDiscarded: !this.isIndexInView(this.props.pageIndex, index),
isNew: !this.isIndexInView(this.props.previousPageIndex, index),
};
}.bind(this));
},

renderPages: function () {
var PageView = this.props.pageView;
var buffers = this.calculateBuffers();

return buffers.map(function renderBuffer (buffer) {
var pageView;

if ( this.props.renderEmptyPages || this.isIndexWithinBounds(buffer.pageIndex) ) {
pageView = PageView({
page: this.props.pages[buffer.pageIndex],
index: buffer.pageIndex,
willBeDiscarded: buffer.willBeDiscarded,
isNew: buffer.isNew,
});
}

return this.renderPage(buffer, pageView);
}.bind(this));
},
};

module.exports = CarouselMixin;
55 changes: 55 additions & 0 deletions Fader/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"use strict";

var React = require("react");
var Swipable = require("../Swipable");
var CarouselMixin = require("../CarouselMixin");
var getClassName = require("../utils/getClassName");

module.exports = React.createClass({
displayName: "Carousel",

mixins: [Swipable, CarouselMixin],

calculatePageStyle: function (index) {
return {
width: this.props.pageWidth + "px",
height: this.props.pageHeight + "px",
opacity: index === this.props.pageIndex ? "1.0" : "0.0",
};
},

renderPage: function (buffer, pageView) {
return React.DOM.div({
className: this.props.baseClass + "__page",
key: buffer.index,
style: this.calculatePageStyle(buffer.index),
}, pageView);
},

calculateStyle: function () {
return {
width: (this.props.width) + "px",
height: (this.props.height) + "px",
left: (-this.props.embedWidth) + "px",
top: (-this.props.embedHeight) + "px",
};
},

render: function () {
if ( this.props.pages.length === 0 ) {
// (jussi-kalliokoski): Nothing to render. Avoids infinite loop in calculateBuffers().
return React.DOM.div();
}

return React.DOM.div({
className: this.props.baseClass,
onTouchStart: this.handleTouchStart,
onTouchMove: this.handleTouchMove,
onTouchEnd: this.handleTouchEnd,
onTouchCancel: this.handleTouchCancel,
style: this.calculateStyle(),
},
this.renderPages()
);
},
});
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Courtesy of the test suite that's run on [BrowserStack](https://www.browserstack

## Usage

Merry-go-Round exposes two components, `Carousel` and `Container`:
Merry-go-Round exposes three components, `Carousel`, `Fader`, and `Container`:

### Carousel

Expand All @@ -70,6 +70,10 @@ var Carousel = require("merry-go-round/Carousel");
* `embedHeight` (Integer, optional, defaults to `0`): The number of pixels to "embed" into the vertically. Basically reverses the container's padding, so that you can have things such as partially revealed pages that come outside the margin.
* `renderEmptyPages` (Boolean, optional, defaults to `false`): Whether to render empty pages. This is useful when you want to have special views for pages that don't have any content.

### Fader

The Fader is otherwise identical to the Carousel component and implements the same API, but instead of vertically sliding the pages, the pages fade in and out.

### Container

The Container is a general purpose mixin for creating containers of the Carousels. It provides some useful functionality such as auto-rotation, marker-based page navigation and passing on the carousel-related events to your controller view.
Expand Down
2 changes: 2 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ var spawn = require("child_process").spawn;

var files = [
"./Carousel/index.js",
"./Fader/index.js",
"./CarouselMixin/index.js",
"./Container/index.js",
"./utils/*.js",
"./gulpfile.js",
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"use strict";

module.exports.Carousel = require("./Carousel");
module.exports.Fader = require("./Fader");
module.exports.Container = require("./Container");
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
"files": [
"index.js",
"Carousel",
"Fader",
"Container",
"Swipable",
"CarouselMixin",
"utils"
],
"scripts": {
Expand Down
Loading

0 comments on commit 2deb4ec

Please sign in to comment.