finnaly created a carousel
This commit is contained in:
36
front-ui/app/actions/itemDetailsActions.js
Normal file
36
front-ui/app/actions/itemDetailsActions.js
Normal file
@@ -0,0 +1,36 @@
|
||||
var AppDispatcher = require('../dispatcher/appDispatcher');
|
||||
var ItemDetailsConstants = require('../constants/itemDetailsConstants');
|
||||
|
||||
// Define action methods
|
||||
var ItemDetailsActions = {
|
||||
|
||||
|
||||
loadItemWithDetails: function() {
|
||||
AppDispatcher.handleAction({
|
||||
actionType: ItemDetailsConstants.LOAD_ITEM_WITH_DETAILS
|
||||
})
|
||||
},
|
||||
|
||||
nextCarouselImage: function() {
|
||||
AppDispatcher.handleAction({
|
||||
actionType: ItemDetailsConstants.NEXT_CAROUSEL_IMAGE
|
||||
})
|
||||
},
|
||||
|
||||
previousCarouselImage: function() {
|
||||
AppDispatcher.handleAction({
|
||||
actionType: ItemDetailsConstants.PREVIOUS_CAROUSEL_IMAGE
|
||||
})
|
||||
},
|
||||
|
||||
selectCarouselImage: function(index) {
|
||||
AppDispatcher.handleAction({
|
||||
actionType: ItemDetailsConstants.SELECT_CAROUSEL_IMAGE,
|
||||
index: index
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
module.exports = ItemDetailsActions;
|
||||
@@ -15,9 +15,6 @@ var ItemMultimediaDescriptions = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
//getInitialState: function () {
|
||||
//return { descriptions: [] };
|
||||
//}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
var React = require('react'),
|
||||
ItemMultiMediaDescriptions = require('./itemMultiMediaDescriptions'),
|
||||
ItemActions = require('../../actions/itemActions'),
|
||||
NavigationStore = require('../../stores/NavigationStore'),
|
||||
ItemStore = require('../../stores/itemStore');
|
||||
Carousel = require('../shared/carousel'),
|
||||
ItemDetailsActions = require('../../actions/itemDetailsActions'),
|
||||
NavigationStore = require('../../stores/navigationStore'),
|
||||
ItemDetailsStore = require('../../stores/itemDetailsStore');
|
||||
|
||||
var Router = require('react-router');
|
||||
|
||||
@@ -13,47 +13,59 @@ var ItemWithDetailsPage = React.createClass({
|
||||
|
||||
return (
|
||||
|
||||
|
||||
<div className="item-with-details row-fluid center">
|
||||
<div className="span3">
|
||||
|
||||
|
||||
<div className="item-with-details row-fluid center">
|
||||
<div className="span3">
|
||||
<h3> {this.state.item.get('name')} </h3>
|
||||
<div>
|
||||
|
||||
<div className='h4'> {this.state.item.get('list_price')} KM</div>
|
||||
<div> {this.state.item.get('description')}</div>
|
||||
<div className='h4'> {this.state.item.get('list_price')} KM</div>
|
||||
<div> {this.state.item.get('description')}</div>
|
||||
</div>
|
||||
<ItemMultiMediaDescriptions descriptions={this.state.item.get('multi_media_descriptions')} />
|
||||
</div>
|
||||
<div className="span4">
|
||||
<Carousel images={this.state.images}
|
||||
selected={this.state.currentImage}
|
||||
onClickLeft={this.onClickLeft}
|
||||
onClickRight={this.onClickRight}
|
||||
onSelectImage={this.onSelectImage} />
|
||||
</div>
|
||||
<div className="span4">
|
||||
quantitative descriptions
|
||||
</div>
|
||||
</div>
|
||||
) ;
|
||||
</div>
|
||||
);
|
||||
|
||||
},
|
||||
|
||||
// Add change listeners to stores
|
||||
componentDidMount: function() {
|
||||
ItemStore.addChangeListener(this._onChange);
|
||||
ItemDetailsStore.addChangeListener(this._onChange);
|
||||
NavigationStore.addChangeListener(this._onChange);
|
||||
ItemActions.loadItemWithDetails();
|
||||
ItemDetailsActions.loadItemWithDetails();
|
||||
},
|
||||
|
||||
|
||||
onClickLeft: function() {
|
||||
ItemDetailsActions.previousCarouselImage();
|
||||
|
||||
},
|
||||
|
||||
onClickRight: function() {
|
||||
ItemDetailsActions.nextCarouselImage();
|
||||
},
|
||||
|
||||
onSelectImage: function(i) {
|
||||
ItemDetailsActions.selectCarouselImage(i);
|
||||
},
|
||||
|
||||
_onChange: function () {
|
||||
|
||||
if (this.isMounted()) {
|
||||
this.setState({
|
||||
item: ItemStore.getLoadedItemWithDetails()
|
||||
});
|
||||
this.setState(ItemDetailsStore.getState());
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
getInitialState: function () {
|
||||
return {
|
||||
item : ItemStore.getLoadedItemWithDetails()
|
||||
};
|
||||
return ItemDetailsStore.getState();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
51
front-ui/app/components/shared/carousel.js
Normal file
51
front-ui/app/components/shared/carousel.js
Normal file
@@ -0,0 +1,51 @@
|
||||
var React = require("react");
|
||||
|
||||
var Carousel = React.createClass({
|
||||
propTypes: {
|
||||
images: React.PropTypes.array.isRequired,
|
||||
selected: React.PropTypes.number.isRequired,
|
||||
onClickLeft: React.PropTypes.func.isRequired,
|
||||
onClickRight: React.PropTypes.func.isRequired,
|
||||
onSelectImage: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var left = this.props.selected * 300 * -1,
|
||||
ulStyle = {
|
||||
width: this.props.images.length * 300,
|
||||
"-ms-transform": "translate(" + left + "px,0px)",
|
||||
"-webkit-transform": "translate(" + left + "px,0px)",
|
||||
transform: "translate(" + left + "px,0px)"
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span className="arrow left"
|
||||
onClick={this.props.onClickLeft}>◄</span>
|
||||
<div className="carousel-stage">
|
||||
<ul style={ulStyle} className="carousel-list">
|
||||
{this.props.images.map(function(image, i) {
|
||||
return <li key={i}><img src={image} /></li>;
|
||||
})}
|
||||
</ul>
|
||||
<ul className="dots">
|
||||
{this.props.images.map(function(image, i) {
|
||||
var activeClass = i === this.props.selected ? "active" : "";
|
||||
return <li key={i}
|
||||
className={"circle " + activeClass}
|
||||
onClick={this.onClickDot.bind(this, i)}></li>;
|
||||
}.bind(this))}
|
||||
</ul>
|
||||
</div>
|
||||
<span className="arrow right"
|
||||
onClick={this.props.onClickRight}>►</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
onClickDot: function(index) {
|
||||
this.props.onSelectImage(index);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Carousel;
|
||||
9
front-ui/app/constants/itemDetailsConstants.js
Normal file
9
front-ui/app/constants/itemDetailsConstants.js
Normal file
@@ -0,0 +1,9 @@
|
||||
var keyMirror = require('react/lib/keyMirror');
|
||||
|
||||
// Define action constants
|
||||
module.exports = keyMirror({
|
||||
LOAD_ITEM_WITH_DETAILS: null,
|
||||
NEXT_CAROUSEL_IMAGE: null,
|
||||
PREVIOUS_CAROUSEL_IMAGE: null,
|
||||
SELECT_CAROUSEL_IMAGE: null
|
||||
});
|
||||
72
front-ui/app/css/carousel.css
Normal file
72
front-ui/app/css/carousel.css
Normal file
@@ -0,0 +1,72 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.application-container {
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.arrow,
|
||||
.circle {
|
||||
cursor: pointer;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.arrow {
|
||||
position: relative;
|
||||
top: -100px;
|
||||
padding: 10px;
|
||||
}
|
||||
.carousel-stage {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
border: 1px solid black;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
.carousel-stage ul.carousel-list {
|
||||
display: inline-block;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
transition: 300ms ease-in-out;
|
||||
}
|
||||
.carousel-stage ul.carousel-list li {
|
||||
display: inline-block;
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
float: left;
|
||||
}
|
||||
.carousel-stage ul.carousel-list li img {
|
||||
object-fit: cover;
|
||||
object-position: center center;
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
}
|
||||
.carousel-stage ul.dots {
|
||||
width: 300px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.carousel-stage ul.dots li {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-color: white;
|
||||
margin: 2px;
|
||||
border-radius: 50%;
|
||||
line-height: 5px;
|
||||
border: 2px solid black;
|
||||
}
|
||||
.carousel-stage ul.dots li.active {
|
||||
background-color: #ccc;
|
||||
}
|
||||
147
front-ui/app/stores/itemDetailsStore.js
Normal file
147
front-ui/app/stores/itemDetailsStore.js
Normal file
@@ -0,0 +1,147 @@
|
||||
var AppDispatcher = require('../dispatcher/appDispatcher');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var ItemDetailsConstants = require('../constants/itemDetailsConstants');
|
||||
var ItemWithDetails = require('../models/itemWithDetails');
|
||||
var _ = require('underscore');
|
||||
|
||||
|
||||
var _itemWithDetails = new ItemWithDetails();
|
||||
var _images = [];
|
||||
var _currentImage = 0;
|
||||
var _animating = false;
|
||||
var _count;
|
||||
|
||||
|
||||
var getItemIdFromUrl = function() {
|
||||
// ugly but it seems to me that
|
||||
// router does not want to expose its
|
||||
// state (for phylosophical reasons)
|
||||
var url = document.URL;
|
||||
var itemIdRegex = /artikal\/(\d+)\//g;
|
||||
var match = itemIdRegex.exec(url);
|
||||
console.log(match);
|
||||
return match[1];
|
||||
};
|
||||
|
||||
var fetchItemWithDetails = function() {
|
||||
var id = getItemIdFromUrl();
|
||||
if (id !== undefined && _itemWithDetails.id !== id) {
|
||||
var item = new ItemWithDetails({
|
||||
id: id
|
||||
});
|
||||
item.fetch({
|
||||
success: function() {
|
||||
_itemWithDetails = item;
|
||||
_images = (_itemWithDetails.get("multi_media_descriptions") || []).map(function(mmd) {
|
||||
return mmd.url;
|
||||
});
|
||||
_count = _images.length;
|
||||
_currentImage = 0;
|
||||
_animating = false;
|
||||
ItemWithDetailsStore.emitChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var handlNextImage = function() {
|
||||
if (_animating) return;
|
||||
var next = _currentImage + 1;
|
||||
if (next >= _count) next = 0;
|
||||
handleSelectImage(next);
|
||||
};
|
||||
|
||||
var handlePrevImage = function() {
|
||||
if (_animating) return;
|
||||
var next = _currentImage - 1;
|
||||
if (next < 0) next = _count - 1;
|
||||
handleSelectImage(next);
|
||||
};
|
||||
|
||||
|
||||
var handleSelectImage = function(index) {
|
||||
if (_animating) return;
|
||||
_animating = true;
|
||||
_currentImage = index;
|
||||
setTimeout(function() {
|
||||
_animating = false;
|
||||
}, 300);
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Extend ItemWithDetailsStore with EventEmitter to add eventing capabilities
|
||||
var ItemWithDetailsStore = _.extend({}, EventEmitter.prototype, {
|
||||
|
||||
getState: function() {
|
||||
|
||||
|
||||
|
||||
return {
|
||||
item: _itemWithDetails,
|
||||
images: _images,
|
||||
currentImage: _currentImage,
|
||||
count: _count
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
// item with details
|
||||
getLoadedItemWithDetails: function() {
|
||||
return _itemWithDetails;
|
||||
},
|
||||
|
||||
// Emit Change event
|
||||
emitChange: function() {
|
||||
console.log("Emitting change!");
|
||||
this.emit('change');
|
||||
},
|
||||
|
||||
// Add change listener
|
||||
addChangeListener: function(callback) {
|
||||
this.on('change', callback);
|
||||
},
|
||||
|
||||
// Remove change listener
|
||||
removeChangeListener: function(callback) {
|
||||
this.removeListener('change', callback);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Register callback with AppDispatcher
|
||||
AppDispatcher.register(function(payload) {
|
||||
var action = payload.action;
|
||||
var text;
|
||||
|
||||
switch (action.actionType) {
|
||||
|
||||
case ItemDetailsConstants.LOAD_ITEM_WITH_DETAILS:
|
||||
fetchItemWithDetails();
|
||||
break;
|
||||
|
||||
case ItemDetailsConstants.NEXT_CAROUSEL_IMAGE:
|
||||
handlNextImage();
|
||||
break;
|
||||
|
||||
case ItemDetailsConstants.PREVIOUS_CAROUSEL_IMAGE:
|
||||
handlePrevImage();
|
||||
break;
|
||||
|
||||
case ItemDetailsConstants.SELECT_CAROUSEL_IMAGE:
|
||||
handleSelectImage(action.index);
|
||||
break;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
// If action was responded to, emit change event
|
||||
ItemWithDetailsStore.emitChange();
|
||||
return true;
|
||||
|
||||
});
|
||||
|
||||
module.exports = ItemWithDetailsStore;
|
||||
@@ -8,16 +8,7 @@ var _ = require('underscore');
|
||||
// Extend ItemStore with EventEmitter to add eventing capabilities
|
||||
var NavigationStore = _.extend({}, EventEmitter.prototype, {
|
||||
|
||||
getItemIdFromUrl: function () {
|
||||
// ugly but it seems to me that
|
||||
// router does not want to expose its
|
||||
// state
|
||||
var url = document.URL;
|
||||
var itemIdRegex = /artikal\/(\d+)\//;
|
||||
var match = itemIdRegex.exec(url);
|
||||
console.log(match);
|
||||
return match[0];
|
||||
},
|
||||
|
||||
|
||||
// Emit Change event
|
||||
emitChange: function() {
|
||||
|
||||
Reference in New Issue
Block a user