Initial commit
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
var Backbone = require('backbone');
|
||||
var CustomCarouselModel = require('./custom-carousel-item-model');
|
||||
|
||||
/*
|
||||
* Custom list collection, it parses pairs like:
|
||||
*
|
||||
* [{ val, label }]
|
||||
* ["string"]
|
||||
*/
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
|
||||
model: function (attrs, opts) {
|
||||
var d = {};
|
||||
if (typeof attrs === 'string') {
|
||||
d.val = attrs;
|
||||
} else {
|
||||
d = attrs;
|
||||
}
|
||||
return new CustomCarouselModel(d);
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.bind('change:selected', this._onSelectedChange, this);
|
||||
},
|
||||
|
||||
_onSelectedChange: function (changedModel, isSelected) {
|
||||
if (isSelected) {
|
||||
this.each(function (m) {
|
||||
if (m.cid !== changedModel.cid && m.get('selected')) {
|
||||
m.set('selected', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getSelected: function () {
|
||||
return this.findWhere({ selected: true });
|
||||
},
|
||||
|
||||
getSelectedValue: function () {
|
||||
var selectedModel = this.getSelected();
|
||||
return selectedModel && selectedModel.get('val');
|
||||
},
|
||||
|
||||
getHighlighted: function () {
|
||||
return this.findWhere({ highlighted: true });
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
/*
|
||||
* List item model
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
|
||||
defaults: {
|
||||
selected: false,
|
||||
label: '',
|
||||
template: function () {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
getName: function () {
|
||||
return this.get('label') || this.getValue();
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
return this.get('val');
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var template = require('./custom-carousel-item.tpl');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
className: 'Carousel-item',
|
||||
tagName: 'li',
|
||||
|
||||
events: {
|
||||
'mouseenter': '_onMouseEnter',
|
||||
'mouseleave': '_onMouseLeave',
|
||||
'click': '_onClick'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
this._itemClassName = opts && opts.itemOptions ? opts.itemOptions.className : '';
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
// NOTE: this function calls destroyTipsy for each tooltip, which calls:
|
||||
// this.$el.unbind('mouseleave mouseenter');
|
||||
// So we need to bind the events again after calling clearSubViews
|
||||
this.clearSubViews();
|
||||
|
||||
this.$el.bind('mouseenter', this._onMouseEnter.bind(this));
|
||||
this.$el.bind('mouseleave', this._onMouseLeave.bind(this));
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
name: this.model.getName(),
|
||||
className: this._itemClassName,
|
||||
template: this.model.get('template')()
|
||||
})
|
||||
);
|
||||
|
||||
if (this.model.getValue()) {
|
||||
this.$el.addClass('js-' + this.model.getValue());
|
||||
}
|
||||
this.$el.toggleClass('is-selected', !!this.model.get('selected'));
|
||||
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
if (this.model.get('tooltip')) {
|
||||
var tooltip = new TipsyTooltipView({
|
||||
el: this.$el,
|
||||
gravity: 's',
|
||||
title: function () {
|
||||
return this.model.get('tooltip');
|
||||
}.bind(this)
|
||||
});
|
||||
this.addView(tooltip);
|
||||
}
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.model.bind('change:selected', this.render, this);
|
||||
},
|
||||
|
||||
_onMouseEnter: function () {
|
||||
this.model.set('highlighted', true);
|
||||
},
|
||||
|
||||
_onMouseLeave: function () {
|
||||
this.model.set('highlighted', false);
|
||||
},
|
||||
|
||||
_onClick: function (e) {
|
||||
this.killEvent(e);
|
||||
this.model.set('selected', true);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
<button<% if (className) { %> class="<%- className %>"<% } %>>
|
||||
<%= template %>
|
||||
</button>
|
||||
@@ -0,0 +1,137 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var Ps = require('perfect-scrollbar');
|
||||
var template = require('./custom-carousel.tpl');
|
||||
var CarouselCollection = require('./custom-carousel-collection');
|
||||
var CarouselItemView = require('./custom-carousel-item-view');
|
||||
var _ = require('underscore');
|
||||
/*
|
||||
* A custom carousel selector
|
||||
*
|
||||
* It accepts a collection of (val, label) model attributes or a values array
|
||||
* with the same content or only strings.
|
||||
*
|
||||
* new CustomCarousel({
|
||||
* options: [
|
||||
* {
|
||||
* val: 'hello',
|
||||
* label: 'hi'
|
||||
* }
|
||||
* ]
|
||||
* });
|
||||
*/
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
className: 'Carousel',
|
||||
tagName: 'div',
|
||||
|
||||
initialize: function (opts) {
|
||||
if (!opts.collection) {
|
||||
if (!opts.options) { throw new Error('options array {value, label} is required'); }
|
||||
this.collection = new CarouselCollection(opts.options);
|
||||
this.options = opts;
|
||||
}
|
||||
this._bindedCheckShadows = this._checkShadows.bind(this);
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.html(template());
|
||||
this._renderList();
|
||||
this._applyCustomScroll();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.collection.bind('change:selected', this._checkScroll, this);
|
||||
},
|
||||
|
||||
_renderList: function () {
|
||||
this.collection.each(function (model) {
|
||||
this._createItem(model);
|
||||
}, this);
|
||||
},
|
||||
|
||||
_createItem: function (model) {
|
||||
var opts = this.options;
|
||||
var className = opts && opts.listItemOptions ? opts.listItemOptions.className : 'Carousel-item';
|
||||
var view = new CarouselItemView({
|
||||
className: className,
|
||||
model: model,
|
||||
itemOptions: this.options.itemOptions
|
||||
});
|
||||
|
||||
this._listContainer().append(view.render().el);
|
||||
this.addView(view);
|
||||
},
|
||||
|
||||
_applyCustomScroll: function () {
|
||||
Ps.initialize(this._listContainer().get(0), {
|
||||
wheelSpeed: 1,
|
||||
wheelPropagation: false,
|
||||
swipePropagation: true,
|
||||
suppressScrollY: true,
|
||||
stopPropagationOnClick: false,
|
||||
minScrollbarLength: 120,
|
||||
useBothWheelAxes: true
|
||||
});
|
||||
this._checkScroll();
|
||||
this._bindScroll();
|
||||
},
|
||||
|
||||
_destroyCustomScroll: function () {
|
||||
this._unbindScroll();
|
||||
Ps.destroy(this._listContainer().get(0));
|
||||
},
|
||||
|
||||
_bindScroll: function () {
|
||||
this._listContainer()
|
||||
.on('ps-x-reach-start', this._bindedCheckShadows)
|
||||
.on('ps-x-reach-end', this._bindedCheckShadows)
|
||||
.on('ps-scroll-x', this._bindedCheckShadows);
|
||||
},
|
||||
|
||||
_unbindScroll: function () {
|
||||
this._listContainer()
|
||||
.off('ps-x-reach-start', this._bindedCheckShadows)
|
||||
.off('ps-x-reach-end', this._bindedCheckShadows)
|
||||
.off('ps-scroll-x', this._bindedCheckShadows);
|
||||
},
|
||||
|
||||
_checkScroll: function () {
|
||||
var position = this.$('.is-selected').position();
|
||||
if (position) {
|
||||
this._listContainer().scrollLeft(position.left - 20);
|
||||
this._bindedCheckShadows();
|
||||
}
|
||||
},
|
||||
|
||||
_listContainer: function () {
|
||||
return this.$('.js-list');
|
||||
},
|
||||
|
||||
_checkShadows: function () {
|
||||
var currentPos = this._listContainer().scrollLeft();
|
||||
var max = this._listContainer().get(0).scrollWidth;
|
||||
var width = this._listContainer().outerWidth();
|
||||
var maxPos = max - width;
|
||||
|
||||
this.$('.js-leftShadow').toggleClass('is-visible', currentPos > 0);
|
||||
this.$('.js-rightShadow').toggleClass('is-visible', currentPos < maxPos);
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._destroyCustomScroll();
|
||||
CoreView.prototype.clean.apply(this);
|
||||
},
|
||||
|
||||
initScroll: function () {
|
||||
setTimeout(_.bind(function () {
|
||||
this._checkShadows();
|
||||
this._checkScroll();
|
||||
Ps.update(this._listContainer().get(0));
|
||||
}, this), 0);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="Carousel-shadow Carousel-shadow--left js-leftShadow"></div>
|
||||
<div class="Carousel-shadow Carousel-shadow--right is-visible js-rightShadow"></div>
|
||||
<ul class="Carousel-list js-list"></ul>
|
||||
Reference in New Issue
Block a user