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'),
|
var React = require('react'),
|
||||||
ItemMultiMediaDescriptions = require('./itemMultiMediaDescriptions'),
|
Carousel = require('../shared/carousel'),
|
||||||
ItemActions = require('../../actions/itemActions'),
|
ItemDetailsActions = require('../../actions/itemDetailsActions'),
|
||||||
NavigationStore = require('../../stores/NavigationStore'),
|
NavigationStore = require('../../stores/navigationStore'),
|
||||||
ItemStore = require('../../stores/itemStore');
|
ItemDetailsStore = require('../../stores/itemDetailsStore');
|
||||||
|
|
||||||
var Router = require('react-router');
|
var Router = require('react-router');
|
||||||
|
|
||||||
@@ -14,46 +14,58 @@ var ItemWithDetailsPage = React.createClass({
|
|||||||
return (
|
return (
|
||||||
|
|
||||||
|
|
||||||
<div className="item-with-details row-fluid center">
|
<div className="item-with-details row-fluid center">
|
||||||
<div className="span3">
|
<div className="span3">
|
||||||
|
|
||||||
<h3> {this.state.item.get('name')} </h3>
|
<h3> {this.state.item.get('name')} </h3>
|
||||||
<div>
|
<div>
|
||||||
|
<div className='h4'> {this.state.item.get('list_price')} KM</div>
|
||||||
<div className='h4'> {this.state.item.get('list_price')} KM</div>
|
<div> {this.state.item.get('description')}</div>
|
||||||
<div> {this.state.item.get('description')}</div>
|
|
||||||
</div>
|
</div>
|
||||||
<ItemMultiMediaDescriptions descriptions={this.state.item.get('multi_media_descriptions')} />
|
<Carousel images={this.state.images}
|
||||||
</div>
|
selected={this.state.currentImage}
|
||||||
<div className="span4">
|
onClickLeft={this.onClickLeft}
|
||||||
|
onClickRight={this.onClickRight}
|
||||||
|
onSelectImage={this.onSelectImage} />
|
||||||
|
</div>
|
||||||
|
<div className="span4">
|
||||||
quantitative descriptions
|
quantitative descriptions
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) ;
|
);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Add change listeners to stores
|
// Add change listeners to stores
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
ItemStore.addChangeListener(this._onChange);
|
ItemDetailsStore.addChangeListener(this._onChange);
|
||||||
NavigationStore.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 () {
|
_onChange: function () {
|
||||||
|
|
||||||
if (this.isMounted()) {
|
if (this.isMounted()) {
|
||||||
this.setState({
|
this.setState(ItemDetailsStore.getState());
|
||||||
item: ItemStore.getLoadedItemWithDetails()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function () {
|
getInitialState: function () {
|
||||||
return {
|
return ItemDetailsStore.getState();
|
||||||
item : ItemStore.getLoadedItemWithDetails()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
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
|
// Extend ItemStore with EventEmitter to add eventing capabilities
|
||||||
var NavigationStore = _.extend({}, EventEmitter.prototype, {
|
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
|
// Emit Change event
|
||||||
emitChange: function() {
|
emitChange: function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user