Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

backbone-combobox

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

backbone-combobox - npm Package Compare versions

Comparing version 0.1.0 to 0.1.1

demo/bundle.js

12

index.js

@@ -1,5 +0,7 @@

module.exports = {
View: require('./src/comboboxView'),
Model: require('./src/comboboxModel'),
DropdownView: require('./src/dropdownView')
};
define(function(require, exports, module) {
module.exports = {
View: require('./src/comboboxView'),
Model: require('./src/comboboxModel'),
DropdownView: require('./src/dropdownView')
};
});
{
"name": "backbone-combobox",
"version": "0.1.0",
"version": "0.1.1",
"description": "Backbone Combobox component",

@@ -8,3 +8,3 @@ "main": "index.js",

"test": "echo \"Error: no test specified\" && exit 1",
"build-sass": "node-sass src/style.scss dist/style.css"
"dev": "webpack-dev-server --content-base demo"
},

@@ -30,5 +30,11 @@ "repository": {

"classnames": "^2.2.5",
"css-loader": "^0.23.1",
"jquery": "^2.2.4",
"lodash": "^4.13.1"
"lodash": "^4.13.1",
"node-sass": "^3.7.0",
"sass-loader": "^3.2.0",
"style-loader": "^0.13.1",
"text-loader": "0.0.1",
"webpack": "^1.13.1"
}
}
backbone-tree
=============
A node module providing mixins, model and collection which may help working with tree structures.
Backbone based Select component.

@@ -14,2 +14,6 @@ More info soon...

## Sample
![Sample](https://raw.githubusercontent.com/SlideWorx/backbone-combobox/master/demo/combobox.gif)
## Usage

@@ -21,18 +25,18 @@

// create model instance, fill it with data and select item
var model = new Combobox.Model();
var model = new Combobox.Model({});
model.setData([
{id: '1', text: 'Main'},
{id: '1.0', text: 'Subelement', parentId: '1'},
{id: '1.1', text: 'Subelement', parendId: '1'}
{id: '1', text: '1 (title specified)', title: 'Title text'},
{id: '2', text: '2'},
{id: '3', text: '3 (disabled)', disabled: true},
{id: '1.1', text: '1.1', parentId: '1'},
{id: '1.2', text: '1.2', parentId: '1'}
// ...
]);
model.set('selectedId', '1.1');
model.set({selectedId: '1.1'});
// create view instance and pass it model created above
var new Combobox.View({
model: model
});
// create view instance and pass it model created above
var view = new Combobox.View({ model: model });
// render and append
$('#combobox-target').empty().append(this.view.render().el);
view.render().$el.appendTo('#target');
```

@@ -42,2 +46,6 @@

First of all: `npm install`
Then to see example use of combobox please install `webpack-dev-server` and then run `npm run dev`.
Please use [mversion](https://github.com/mikaelbr/mversion) (`npm install -g mversion`) to bump version numbers.

@@ -47,2 +55,9 @@

* 0.1.0 Initial release
### 0.1.1 (03.06.2016)
* Updated README.md
* Added demo code
* `npm run dev` for development environment
* Changed way of including template files (tpl -> text)
### 0.1.0 (02.06.2016)
Initial release

@@ -1,208 +0,210 @@

var Backbone = require('backbone');
var _ = require('lodash');
var TreeCollection = require('backbone-tree').Collection;
define(function(require, exports, module) {
var Backbone = require('backbone');
var _ = require('lodash');
var TreeCollection = require('backbone-tree').Collection;
var VALIDATE = {validate: true};
var validateMsgTmpl = _.template(
'Property `<%- prop %>` by convention should contain <%- type %> value (<%- value %>)'
);
var VALIDATE = {validate: true};
var validateMsgTmpl = _.template(
'Property `<%- prop %>` by convention should contain <%- type %> value (<%- value %>)'
);
var startingWithIsOrHasRegex = /^(is|has).+$/;
var endingWithTextRegex = /^.+Text$/;
var startingWithIsOrHasRegex = /^(is|has).+$/;
var endingWithTextRegex = /^.+Text$/;
module.exports = Backbone.Model.extend({
defaults: function() {
return {
// Opens or closes dropdowns
isOpen: false,
// Put toggle into loading state. Also causes closing opened dropdown.
isLoading: false,
// disables toggle element. Immediately.
isDisabled: false,
// displays dropdowns animating them (adding specific CSS classes to dropdowns)
isAnimated: true,
// indicates if toggle element should be highlighted
isWarning: false,
// indicates that something wrong has happen
hasError: false,
module.exports = Backbone.Model.extend({
defaults: function() {
return {
// Opens or closes dropdowns
isOpen: false,
// Put toggle into loading state. Also causes closing opened dropdown.
isLoading: false,
// disables toggle element. Immediately.
isDisabled: false,
// displays dropdowns animating them (adding specific CSS classes to dropdowns)
isAnimated: true,
// indicates if toggle element should be highlighted
isWarning: false,
// indicates that something wrong has happen
hasError: false,
// if hasNotSelectedItem === true, null points to that value
selectedId: null,
// if hasNotSelectedItem === true, null points to that value
selectedId: null,
// creates an item to represent not selected value
hasNotSelectedItem: true,
// this text is displayed as not selected value. More details
notSelectedText: '- select -',
// creates an item to represent not selected value
hasNotSelectedItem: true,
// this text is displayed as not selected value. More details
notSelectedText: '- select -',
// text displayed to user when loading state has been switched on
loadingText: 'loading...',
// text displayed to user when loading state has been switched on
loadingText: 'loading...',
// use this to style toggle and dropdowns. Eg. try 'inline'.
theme: '',
// use this to style toggle and dropdowns. Eg. try 'inline'.
theme: '',
_data: new TreeCollection()
};
},
_data: new TreeCollection()
};
},
initialize: function() {
// if items has been changed (potentially removed) check if selected item still exists
this.listenTo(this.getData(), 'reset remove update change:id', this.checkIfSelectedIdStillExists);
initialize: function() {
// if items has been changed (potentially removed) check if selected item still exists
this.listenTo(this.getData(), 'reset remove update change:id', this.checkIfSelectedIdStillExists);
// listen to change on disabled property
this.listenTo(this, 'change:isDisabled change:isLoading', this.closeDropdownWhenTrue);
},
// listen to change on disabled property
this.listenTo(this, 'change:isDisabled change:isLoading', this.closeDropdownWhenTrue);
},
/* @TODO move to provider */
validate: function(attrs) {
var errors = [];
/* @TODO move to provider */
validate: function(attrs) {
var errors = [];
// check conventions
_.forEach(attrs, function(value, property) {
// starting with 'is' or 'has' should be boolean type
if (startingWithIsOrHasRegex.test(property) && !_.isBoolean(value)) {
errors.push(validateMsgTmpl({prop: property, value: value, type: 'boolean'}));
}
// check conventions
_.forEach(attrs, function(value, property) {
// starting with 'is' or 'has' should be boolean type
if (startingWithIsOrHasRegex.test(property) && !_.isBoolean(value)) {
errors.push(validateMsgTmpl({prop: property, value: value, type: 'boolean'}));
}
if (endingWithTextRegex.test(property) && !_.isString(value)) {
errors.push(validateMsgTmpl({prop: property, value: value, type: 'string'}));
if (endingWithTextRegex.test(property) && !_.isString(value)) {
errors.push(validateMsgTmpl({prop: property, value: value, type: 'string'}));
}
});
if (errors.length) {
return errors;
}
});
},
if (errors.length) {
return errors;
}
},
checkIfSelectedIdStillExists: function() {
var newProps = {hasError: false};
checkIfSelectedIdStillExists: function() {
var newProps = {hasError: false};
if (this.get('selectedId')) {
var selectedItem = this.findItem(this.get('selectedId'));
if (this.get('selectedId')) {
var selectedItem = this.findItem(this.get('selectedId'));
if (!selectedItem) {
newProps.hasError = true;
newProps.selectedId = null;
} else {
newProps.hasError = !!selectedItem.get('disabled')
if (!selectedItem) {
newProps.hasError = true;
newProps.selectedId = null;
} else {
newProps.hasError = !!selectedItem.get('disabled')
}
}
}
this.set(newProps, VALIDATE);
},
this.set(newProps, VALIDATE);
},
getLabel: function() {
var selectedItem = this.getSelectedItem();
var label = selectedItem && selectedItem.get('text') ? selectedItem.get('text') : '&nbsp;';
getLabel: function() {
var selectedItem = this.getSelectedItem();
var label = selectedItem && selectedItem.get('text') ? selectedItem.get('text') : '&nbsp;';
return this.get('isLoading') && this.get('loadingText') ? this.get('loadingText') : label;
},
return this.get('isLoading') && this.get('loadingText') ? this.get('loadingText') : label;
},
closeDropdownWhenTrue: function(model, property) {
if (!!property) {
this.toggleOpen(false);
}
},
closeDropdownWhenTrue: function(model, property) {
if (!!property) {
this.toggleOpen(false);
}
},
isLoading: function() {
return this.get('isLoading') === true;
},
isLoading: function() {
return this.get('isLoading') === true;
},
hasData: function() {
return !this.getData().isEmpty();
},
hasData: function() {
return !this.getData().isEmpty();
},
getData: function() {
return this.get('_data');
},
getData: function() {
return this.get('_data');
},
setData: function(data) {
this.getData().reset(data);
},
setData: function(data) {
this.getData().reset(data);
},
findItem: function(searchFor) {
if (this.isLoading()) {
return this.getLoadingItem();
}
findItem: function(searchFor) {
if (this.isLoading()) {
return this.getLoadingItem();
}
if (_.isString(searchFor) || _.isNumber(searchFor)) {
return this.getData().get(searchFor);
}
if (_.isString(searchFor) || _.isNumber(searchFor)) {
return this.getData().get(searchFor);
}
// search for item on current item
return this.getData().findWhere(searchFor);
},
// search for item on current item
return this.getData().findWhere(searchFor);
},
/**
* Selects item.
*
* @param {string|number|object} id Id of selected item or query which finds item.
* @param {boolean} force Force select item. May be used when you want to select disabled item.
* @param {boolean} remainOpen Should dropdown remain in the same open state after setting selected item.
*/
selectItem: function(id, force, remainOpen) {
var item = this.findItem(id);
/**
* Selects item.
*
* @param {string|number|object} id Id of selected item or query which finds item.
* @param {boolean} force Force select item. May be used when you want to select disabled item.
* @param {boolean} remainOpen Should dropdown remain in the same open state after setting selected item.
*/
selectItem: function(id, force, remainOpen) {
var item = this.findItem(id);
if (!item) {
if (this.get('hasNotSelectedItem') === true) {
item = this.getNotSelectedItem();
} else {
throw new Error('Can\'t select item which couldn\'t be found');
if (!item) {
if (this.get('hasNotSelectedItem') === true) {
item = this.getNotSelectedItem();
} else {
throw new Error('Can\'t select item which couldn\'t be found');
}
}
}
if (item.get('disabled') === true && !force) {
return;
}
if (item.get('disabled') === true && !force) {
return;
}
this.set({
selectedId: item.get('id'),
hasError: item.get('disabled') === true
}, VALIDATE);
this.set({
selectedId: item.get('id'),
hasError: item.get('disabled') === true
}, VALIDATE);
if (!remainOpen) {
this.toggleOpen(false);
}
},
if (!remainOpen) {
this.toggleOpen(false);
}
},
getSelectedItem: function() {
if (this.get('selectedId') === null && this.get('hasNotSelectedItem') === true) {
return this.getNotSelectedItem();
}
getSelectedItem: function() {
if (this.get('selectedId') === null && this.get('hasNotSelectedItem') === true) {
return this.getNotSelectedItem();
}
return this.findItem(this.get('selectedId'));
},
return this.findItem(this.get('selectedId'));
},
getNotSelectedItem: function() {
return new Backbone.Model({
id: null,
text: this.get('notSelectedText')
});
},
getNotSelectedItem: function() {
return new Backbone.Model({
id: null,
text: this.get('notSelectedText')
});
},
getLoadingItem: function() {
return new Backbone.Model({
id: null,
text: this.get('loadingText')
});
},
getLoadingItem: function() {
return new Backbone.Model({
id: null,
text: this.get('loadingText')
});
},
getRootItems: function() {
var items = this.getData().filter(function(item) {
var pId = item.get('parentId');
return _.isNull(pId) || _.isUndefined(pId) || pId === false;
});
getRootItems: function() {
var items = this.getData().filter(function(item) {
var pId = item.get('parentId');
return _.isNull(pId) || _.isUndefined(pId) || pId === false;
});
if (this.get('hasNotSelectedItem') === true) {
var notSelecteItem = this.getNotSelectedItem();
items = [notSelecteItem].concat(items);
}
if (this.get('hasNotSelectedItem') === true) {
var notSelecteItem = this.getNotSelectedItem();
items = [notSelecteItem].concat(items);
}
return items;
},
return items;
},
toggleOpen: function(forceValue) {
this.set({
isOpen: typeof forceValue === 'undefined' ? !this.get('isOpen') : !!forceValue
}, VALIDATE);
}
toggleOpen: function(forceValue) {
this.set({
isOpen: typeof forceValue === 'undefined' ? !this.get('isOpen') : !!forceValue
}, VALIDATE);
}
});
});

@@ -1,98 +0,100 @@

var Backbone = require('backbone');
var _ = require('lodash');
var toggleTemplate = _.template(require('txt!./comboboxView.ejs'));
var dropdownTemplate = _.template(require('txt!./dropdownView.ejs'));
var DropdownView = require('./dropdownView');
var classnames = require('classnames');
define(function(require, exports, module) {
var Backbone = require('backbone');
var _ = require('lodash');
var toggleTemplate = _.template(require('text!./comboboxView.ejs'));
var dropdownTemplate = _.template(require('text!./dropdownView.ejs'));
var DropdownView = require('./dropdownView');
var classnames = require('classnames');
// properties of model which changes causes call of render function
var renderProperties = ['selectedId', 'isDisabled', 'isLoading', 'isOpen', 'theme', 'isWarning', 'hasError'];
var renderProperties = ['selectedId', 'isDisabled', 'isLoading', 'isOpen', 'theme', 'isWarning', 'hasError'];
module.exports = Backbone.View.extend({
toggleTemplate: toggleTemplate,
dropdownTemplate: dropdownTemplate,
dropdownView: DropdownView,
module.exports = Backbone.View.extend({
toggleTemplate: toggleTemplate,
dropdownTemplate: dropdownTemplate,
dropdownView: DropdownView,
className: 'c-combobox',
className: 'c-combobox',
events: {
'click .js-combobox__toggle:not(.is-disabled)': 'toggle'
},
events: {
'click .js-combobox__toggle:not(.is-disabled)': 'toggle'
},
initialize: function() {
this._dropdownView = null;
this
.listenTo(this.model, 'change:isOpen', this.toggleDropDown)
.listenTo(this.model, 'change:' + renderProperties.join(' change:'), this.render)
.listenTo(this.model.getData(), 'reset', this.render)
},
initialize: function() {
this._dropdownView = null;
this
.listenTo(this.model, 'change:isOpen', this.toggleDropDown)
.listenTo(this.model, 'change:' + renderProperties.join(' change:'), this.render)
.listenTo(this.model.getData(), 'reset', this.render)
},
toggleDropDown: function(model, isOpen) {
if (isOpen) {
this.renderDropdown();
} else {
this.removeDropdown();
}
},
toggleDropDown: function(model, isOpen) {
if (isOpen) {
this.renderDropdown();
} else {
this.removeDropdown();
}
},
render: function() {
var classes = {
'is-disabled': this.model.get('isDisabled'),
'is-open': this.model.get('isOpen'),
'is-loading': this.model.get('isLoading'),
'is-warning': this.model.get('isWarning'),
'has-error': this.model.get('hasError')
};
render: function() {
var classes = {
'is-disabled': this.model.get('isDisabled'),
'is-open': this.model.get('isOpen'),
'is-loading': this.model.get('isLoading'),
'is-warning': this.model.get('isWarning'),
'has-error': this.model.get('hasError')
};
if (!!this.model.get('theme')) {
this.$el.addClass('t-combobox--' + this.model.get('theme'));
} else {
// removes all found theme classes
// this.$el.removeClass(function(index, css) {
// return (css.match() || []).join(' ');
// });
this.el.setAttribute('class', this.el.getAttribute('class').replace(/\bt-combobox--\S+/g, ''));
}
if (!!this.model.get('theme')) {
this.$el.addClass('t-combobox--' + this.model.get('theme'));
} else {
// removes all found theme classes
// this.$el.removeClass(function(index, css) {
// return (css.match() || []).join(' ');
// });
this.el.setAttribute('class', this.el.getAttribute('class').replace(/\bt-combobox--\S+/g, ''));
}
var selectedItem = this.model.getSelectedItem();
var selectedItem = this.model.getSelectedItem();
this.$el.html(
this.toggleTemplate({
classNames: classnames(classes),
selected: selectedItem,
label: this.model.getLabel()
})
);
this.$el.html(
this.toggleTemplate({
classNames: classnames(classes),
selected: selectedItem,
label: this.model.getLabel()
})
);
return this;
},
return this;
},
toggle: function(e) {
e.stopPropagation();
this.model.toggleOpen();
},
toggle: function(e) {
e.stopPropagation();
this.model.toggleOpen();
},
close: function() {
this.model.toggleOpen(false);
},
close: function() {
this.model.toggleOpen(false);
},
renderDropdown: function() {
this.removeDropdown();
renderDropdown: function() {
this.removeDropdown();
this._dropdownView = new this.dropdownView({
template: this.dropdownTemplate,
model: this.model,
items: this.model.getRootItems(),
right: false
});
this._dropdownView = new this.dropdownView({
template: this.dropdownTemplate,
model: this.model,
items: this.model.getRootItems(),
right: false
});
this._dropdownView.render().$el.appendTo('body');
this._dropdownView.setPosition(this.$el, true);
},
this._dropdownView.render().$el.appendTo('body');
this._dropdownView.setPosition(this.$el, true);
},
removeDropdown: function() {
if (this._dropdownView) {
this._dropdownView.remove();
removeDropdown: function() {
if (this._dropdownView) {
this._dropdownView.remove();
}
}
}
});
});

@@ -1,295 +0,297 @@

var Backbone = require('backbone');
var $ = require('jquery');
var _ = require('lodash');
var classnames = require('classnames');
define(function(require, exports, module) {
var Backbone = require('backbone');
var $ = require('jquery');
var _ = require('lodash');
var classnames = require('classnames');
var WINDOW = $(window);
var DOCUMENT = $(document);
var BODY = $('body');
var WINDOW = $(window);
var DOCUMENT = $(document);
var BODY = $('body');
var ANIMATION_CLASSES = {
below: 'c-combobox__dropdown--from-top',
onRight: 'c-combobox__dropdown--from-left',
onLeft: 'c-combobox__dropdown--from-right',
above: 'c-combobox__dropdown--from-bottom'
};
var ANIMATION_CLASSES = {
below: 'c-combobox__dropdown--from-top',
onRight: 'c-combobox__dropdown--from-left',
onLeft: 'c-combobox__dropdown--from-right',
above: 'c-combobox__dropdown--from-bottom'
};
module.exports = Backbone.View.extend({
className: 'c-combobox__dropdown',
tagName: 'ul',
module.exports = Backbone.View.extend({
className: 'c-combobox__dropdown',
tagName: 'ul',
events: {
'click .js-combobox__item': 'selectItem',
'mouseenter .js-combobox__item': 'makeActive',
'mouseleave .js-combobox__item': 'removeActive',
'mouseenter .js-combobox__item.has-children': 'displayChildren'
},
events: {
'click .js-combobox__item': 'selectItem',
'mouseenter .js-combobox__item': 'makeActive',
'mouseleave .js-combobox__item': 'removeActive',
'mouseenter .js-combobox__item.has-children': 'displayChildren'
},
initialize: function(options) {
this.template = options.template;
this.items = options.items;
this.right = typeof options.right !== 'undefined' ? !!options.right : true;
this.first = typeof options.first !== 'undefined' ? !!options.first : true;
this.active = false;
initialize: function(options) {
this.template = options.template;
this.items = options.items;
this.right = typeof options.right !== 'undefined' ? !!options.right : true;
this.first = typeof options.first !== 'undefined' ? !!options.first : true;
this.active = false;
if (this.first) {
this.captureClickOnBodyHandler = this._closeCombobox.bind(this);
BODY[0].addEventListener('click', this.captureClickOnBodyHandler, true);
if (this.first) {
this.captureClickOnBodyHandler = this._closeCombobox.bind(this);
BODY[0].addEventListener('click', this.captureClickOnBodyHandler, true);
this.listenTo(this.model, 'change:isOpen', this.toggleEventListener);
}
},
this.listenTo(this.model, 'change:isOpen', this.toggleEventListener);
}
},
// event handlers
// --------------
selectItem: function(ev) {
var index = ev.target.getAttribute('cid');
var selectedItem = this.items[index];
// event handlers
// --------------
selectItem: function(ev) {
var index = ev.target.getAttribute('cid');
var selectedItem = this.items[index];
this.model.selectItem(selectedItem.get('id'));
},
this.model.selectItem(selectedItem.get('id'));
},
makeActive: function() {
this.active = true;
this.removeDropdown();
},
makeActive: function() {
this.active = true;
this.removeDropdown();
},
removeActive: function() {
this.active = false;
},
removeActive: function() {
this.active = false;
},
displayChildren: function(event) {
var $currentTarget = this.$(event.currentTarget);
var hoveredModel = this.items[$currentTarget.attr('cid')];
var children = hoveredModel.treeGetChildren();
displayChildren: function(event) {
var $currentTarget = this.$(event.currentTarget);
var hoveredModel = this.items[$currentTarget.attr('cid')];
var children = hoveredModel.treeGetChildren();
this.renderDropdown(children, $currentTarget);
},
this.renderDropdown(children, $currentTarget);
},
/**
* Depending on model's isOpen property function will add or remove eventListener at capturing phase.
*
* @param {object} model Dropdown (Backbone) model.
* @param {boolean} isOpen Flag indicating that dropdown is open.
*/
toggleEventListener: function(model, isOpen) {
if (isOpen) {
// handle event at capturing phase (first phase going from bottom up)
this.captureClickOnBodyHandler = this._closeCombobox.bind(this);
BODY[0].addEventListener('click', this.captureClickOnBodyHandler, true);
} else {
// unbind click listener from body
BODY[0].removeEventListener('click', this.captureClickOnBodyHandler, true);
}
},
/**
* Depending on model's isOpen property function will add or remove eventListener at capturing phase.
*
* @param {object} model Dropdown (Backbone) model.
* @param {boolean} isOpen Flag indicating that dropdown is open.
*/
toggleEventListener: function(model, isOpen) {
if (isOpen) {
// handle event at capturing phase (first phase going from bottom up)
this.captureClickOnBodyHandler = this._closeCombobox.bind(this);
BODY[0].addEventListener('click', this.captureClickOnBodyHandler, true);
} else {
// unbind click listener from body
BODY[0].removeEventListener('click', this.captureClickOnBodyHandler, true);
}
},
_closeCombobox: function(event) {
// if something else than dropdown item has been clicked...
if (!$(event.target).hasClass('js-combobox__item')) {
// prevent propagation of event to bubble up
event.stopImmediatePropagation();
_closeCombobox: function(event) {
// if something else than dropdown item has been clicked...
if (!$(event.target).hasClass('js-combobox__item')) {
// prevent propagation of event to bubble up
event.stopImmediatePropagation();
// hide all dropdowns
this.closeWithoutSelecting();
}
},
// hide all dropdowns
this.closeWithoutSelecting();
}
},
render: function() {
this.$el.html(this.template(
this.items.map(this._itemMapper.bind(this))
));
render: function() {
this.$el.html(this.template(
this.items.map(this._itemMapper.bind(this))
));
if (!!this.model.get('theme')) {
this.$el.addClass('t-combobox--' + this.model.get('theme'));
}
if (!!this.model.get('theme')) {
this.$el.addClass('t-combobox--' + this.model.get('theme'));
}
WINDOW
.one('resize.combobox', this.closeWithoutSelecting.bind(this))
.one('keyup.combobox', this.closeWithoutSelecting.bind(this));
WINDOW
.one('resize.combobox', this.closeWithoutSelecting.bind(this))
.one('keyup.combobox', this.closeWithoutSelecting.bind(this));
DOCUMENT
.on('wheel.combobox', this.checkWheelEvent.bind(this));
DOCUMENT
.on('wheel.combobox', this.checkWheelEvent.bind(this));
return this;
},
return this;
},
closeWithoutSelecting: function() {
this.model.set({isOpen: false}, {validate: true});
},
closeWithoutSelecting: function() {
this.model.set({isOpen: false}, {validate: true});
},
checkWheelEvent: function() {
// event will be called on each opened dropdown so that we have to distinguish if it's active dropdown
if (this.active) {
this.removeDropdown();
}
},
/* @todo Move to some provider or model */
_itemMapper: function(item) {
var selectedItem = this.model.getSelectedItem();
var mItem = item.toJSON();
var additionalClasses = {};
checkWheelEvent: function() {
// event will be called on each opened dropdown so that we have to distinguish if it's active dropdown
if (this.active) {
this.removeDropdown();
}
},
/* @todo Move to some provider or model */
_itemMapper: function(item) {
var selectedItem = this.model.getSelectedItem();
var mItem = item.toJSON();
var additionalClasses = {};
if (!item.get('title')) { // if there is not specified title - copy text value.
mItem.title = item.get('text');
}
if (!item.get('title')) { // if there is not specified title - copy text value.
mItem.title = item.get('text');
}
// has-children
additionalClasses['has-children'] = _.isFunction(item.treeGetChildren) && !_.isEmpty(item.treeGetChildren());
// has-children
additionalClasses['has-children'] = _.isFunction(item.treeGetChildren) && !_.isEmpty(item.treeGetChildren());
// is-selected & is-selected-path
if (selectedItem && selectedItem.get('id') !== null) { // if has selected item
if (item.get('id') !== null) { // if mapped item isn't notSelectedItem
additionalClasses['is-selected'] = item.get('id') === selectedItem.get('id');
additionalClasses['is-selected-path'] = item.hasTreeAncestor(selectedItem);
// is-selected & is-selected-path
if (selectedItem && selectedItem.get('id') !== null) { // if has selected item
if (item.get('id') !== null) { // if mapped item isn't notSelectedItem
additionalClasses['is-selected'] = item.get('id') === selectedItem.get('id');
additionalClasses['is-selected-path'] = item.hasTreeAncestor(selectedItem);
}
}
}
// is-not-selectable
additionalClasses['is-not-selectable'] = item.get('disabled') === true;
// is-not-selectable
additionalClasses['is-not-selectable'] = item.get('disabled') === true;
mItem.class = classnames(mItem.class, additionalClasses);
mItem.class = classnames(mItem.class, additionalClasses);
return mItem;
},
return mItem;
},
remove: function() {
// take off attached combobox events when first dropdown has been removed
if (this.first) {
WINDOW.off('.combobox');
DOCUMENT.off('.combobox');
}
remove: function() {
// take off attached combobox events when first dropdown has been removed
if (this.first) {
WINDOW.off('.combobox');
DOCUMENT.off('.combobox');
}
if (this._dropdownView) {
this._dropdownView.remove();
this._dropdownView = undefined;
}
if (this._dropdownView) {
this._dropdownView.remove();
this._dropdownView = undefined;
}
Backbone.View.prototype.remove.call(this);
},
Backbone.View.prototype.remove.call(this);
},
renderDropdown: function(children, $target) {
this.removeDropdown();
renderDropdown: function(children, $target) {
this.removeDropdown();
this._dropdownView = new this.constructor({
template: this.template,
first: false,
model: this.model,
items: children,
right: (this.first) ? true : this.right // passing this value causes that 'zig-zag' submenu effect
});
this._dropdownView = new this.constructor({
template: this.template,
first: false,
model: this.model,
items: children,
right: (this.first) ? true : this.right // passing this value causes that 'zig-zag' submenu effect
});
this._dropdownView.render().$el.appendTo('body');
this._dropdownView.setPosition($target, false);
},
this._dropdownView.render().$el.appendTo('body');
this._dropdownView.setPosition($target, false);
},
removeDropdown: function() {
if (this._dropdownView) {
this._dropdownView.remove();
}
},
removeDropdown: function() {
if (this._dropdownView) {
this._dropdownView.remove();
}
},
/**
* Sets position of view according to some other view.
*
* Function in general try to position view the way it fits on screen. It will will bump menu to other side
* if it won't fit. If this happens it will store corresponding value in local variable (this.right).
*
* Depending if it's first or next item position View on the side or below/above parent item.
*
* Also if model has `isAnimated` set to true it will add correct classes to dropdown.
*
* @param {object} $parentView JQuery pointer to DOM element.
* @param {boolean} first Flag indicating if this view should be treated as first dropdown.
*
* @todo Move to provider.
*/
setPosition: function($parentView, first) {
var animationClass;
this.first = first;
/**
* Sets position of view according to some other view.
*
* Function in general try to position view the way it fits on screen. It will will bump menu to other side
* if it won't fit. If this happens it will store corresponding value in local variable (this.right).
*
* Depending if it's first or next item position View on the side or below/above parent item.
*
* Also if model has `isAnimated` set to true it will add correct classes to dropdown.
*
* @param {object} $parentView JQuery pointer to DOM element.
* @param {boolean} first Flag indicating if this view should be treated as first dropdown.
*
* @todo Move to provider.
*/
setPosition: function($parentView, first) {
var animationClass;
this.first = first;
if (this.model.get('isAnimated')) {
this.$el.removeClass(_.values(ANIMATION_CLASSES).join(' '));
}
if (this.model.get('isAnimated')) {
this.$el.removeClass(_.values(ANIMATION_CLASSES).join(' '));
}
var parentRect = $parentView[0].getBoundingClientRect();
var parentRect = $parentView[0].getBoundingClientRect();
// It's required to set `min-width` here because width of element can be too small.
this.$el.css({minWidth: parentRect.width});
// It's required to set `min-width` here because width of element can be too small.
this.$el.css({minWidth: parentRect.width});
var dropdownRect = this.$el[0].getBoundingClientRect();
var dropdownRect = this.$el[0].getBoundingClientRect();
var properties = {
top: this.first ? parentRect.bottom : parentRect.top
};
var properties = {
top: this.first ? parentRect.bottom : parentRect.top
};
// Logic below flips dropdown when it won't fit within window and applies correct animation classes
// ===
// Logic below flips dropdown when it won't fit within window and applies correct animation classes
// ===
// Vertical checks
// ---
if (this.first) {
animationClass = ANIMATION_CLASSES.below;
// Vertical checks
// ---
if (this.first) {
animationClass = ANIMATION_CLASSES.below;
if (parentRect.bottom + dropdownRect.height > window.innerHeight) {
properties.top = parentRect.top - dropdownRect.height;
animationClass = ANIMATION_CLASSES.above;
if (parentRect.bottom + dropdownRect.height > window.innerHeight) {
properties.top = parentRect.top - dropdownRect.height;
animationClass = ANIMATION_CLASSES.above;
}
}
}
// Horizontal checks
// ---
if (this.right === true && this.first === true) {
properties.left = parentRect.right - dropdownRect.width;
// Horizontal checks
// ---
if (this.right === true && this.first === true) {
properties.left = parentRect.right - dropdownRect.width;
// dropdown won't fit within window
if (parentRect.right - dropdownRect.width < 0) {
this.right = false;
// dropdown won't fit within window
if (parentRect.right - dropdownRect.width < 0) {
this.right = false;
properties.left = parentRect.left;
properties.left = parentRect.left;
}
}
}
if (this.right === true && this.first === false) {
animationClass = ANIMATION_CLASSES.onRight;
properties.left = parentRect.right;
if (this.right === true && this.first === false) {
animationClass = ANIMATION_CLASSES.onRight;
properties.left = parentRect.right;
// dropdown won't fit within window
if (properties.left + dropdownRect.width > window.innerWidth) {
this.right = false;
// dropdown won't fit within window
if (properties.left + dropdownRect.width > window.innerWidth) {
this.right = false;
properties.left = parentRect.left - dropdownRect.width;
animationClass = ANIMATION_CLASSES.onLeft;
properties.left = parentRect.left - dropdownRect.width;
animationClass = ANIMATION_CLASSES.onLeft;
}
}
}
if (this.right === false && this.first === true) {
properties.left = parentRect.left;
if (this.right === false && this.first === true) {
properties.left = parentRect.left;
// dropdown won't fit within window
if (parentRect.left + dropdownRect.width > window.innerWidth) {
this.right = true;
// dropdown won't fit within window
if (parentRect.left + dropdownRect.width > window.innerWidth) {
this.right = true;
properties.left = parentRect.right;
properties.left = parentRect.right;
}
}
}
if (this.right === false && this.first === false) {
animationClass = ANIMATION_CLASSES.onLeft;
properties.left = parentRect.left - dropdownRect.width;
if (this.right === false && this.first === false) {
animationClass = ANIMATION_CLASSES.onLeft;
properties.left = parentRect.left - dropdownRect.width;
// dropdown won't fit within window
if (properties.left < 0) {
this.right = true;
// dropdown won't fit within window
if (properties.left < 0) {
this.right = true;
properties.left = parentRect.right;
animationClass = ANIMATION_CLASSES.onLeft;
properties.left = parentRect.right;
animationClass = ANIMATION_CLASSES.onLeft;
}
}
}
this.$el.css(properties);
this.$el.css(properties);
if (this.model.get('isAnimated') === true) {
this.$el.addClass(animationClass);
if (this.model.get('isAnimated') === true) {
this.$el.addClass(animationClass);
}
}
}
});
});

@@ -0,6 +1,11 @@

var path = require('path');
module.exports = {
entry: './entry.js',
entry: {
app: ['./demo/demo.js']
},
output: {
path: './dist',
filename: 'index.js'
path: path.resolve(__dirname, 'demo'),
publicPath: '/assets/',
filename: 'bundle.js'
},

@@ -7,0 +12,0 @@ module: {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc