app/assets/javascripts/slices/app/views/token_field_view.js in slices-1.0.5 vs app/assets/javascripts/slices/app/views/token_field_view.js in slices-2.0.0
- old
+ new
@@ -11,51 +11,68 @@
var ESC = 27,
ENTER = 13,
BACKSPACE = 8,
UP = 38,
DOWN = 40,
- COMMA = 188,
TAB = 9;
slices.TokenFieldView = Backbone.View.extend({
// -- Config --
events: {
'keydown' : 'onKey',
- 'keypress' : 'onKey',
+ 'input' : 'onInput',
'click .del' : 'onClickDel',
'click .option' : 'onClickOption',
- 'click' : 'onClick'
+ 'click .toggle' : 'onClickToggle'
},
className: 'token-field',
template: Handlebars.compile(
- '<input type="text" class="input" name="{{id}}">' +
+ '<input type="text" class="input" name="{{id}}" list="datalist-{{id}}">' +
'<span class="tokens">' +
'{{#each values}}' +
'<span class="token" data-value="{{this}}" unselectable="true">{{this}} <span class="del">×</span></span>' +
'{{/each}}' +
'</span>' +
'<span class="options">' +
- '{{#each valueOptions}}' +
- '<a href="#" class="option">{{this}}</a>' +
+ '{{#if isShowingAll}}' +
+ '{{#each valueOptions}}' +
+ '<a href="#" class="option">{{this}}</a>' +
+ '{{/each}}' +
+ '{{else}}' +
+ '{{#each someOptions}}' +
+ '<a href="#" class="option">{{this}}</a>' +
+ '{{/each}}' +
+ '<a href="#" class="toggle">Show all…</a>' +
+ '{{/if}}' +
+ '</span>' +
+ '<datalist id="datalist-{{id}}">' +
+ '{{#each remainingOptions}}' +
+ '<option>{{this}}</option>' +
'{{/each}}' +
- '</span>'
+ '</datalist>'
),
// -- Init --
initialize: function() {
_.bindAll(this);
this.values = this.sanitizeValues(this.options.values);
this.prevValues = [].concat(this.values || []);
this.valueOptions = [].concat(this.options.options || []);
+ this.someOptions = this.valueOptions.slice(0, 5);
+ if (this.valueOptions.length <= this.someOptions.length) {
+ this.isShowingAll = true;
+ }
+
$(document).on('slices:willSubmit', this.capture);
+ $(document).on('slices:didShowAdvancedOptions', this.render);
if (this.options.autoAttach) _.defer(this.attach);
},
// -- Rendering --
@@ -76,10 +93,11 @@
this.updateComputedValue();
this.updateValueOptions();
this.updateLayout();
this.updateInput();
+ this.makeSortable();
if (_.isEqual(this.values, this.prevValues)) return;
this.$el.find('input').trigger('change');
this.prevValues = [].concat(this.values);
@@ -98,14 +116,10 @@
this.render();
this.focus();
},
- delayedCapture: _.debounce(function() {
- this.capture();
- }, 1500),
-
focusOrRemoveLastToken: function(e) {
var focusedToken = this.$el.find('.token.focus');
if (focusedToken.length) {
this.values.pop();
@@ -116,21 +130,30 @@
}
},
updateLayout: function() {
var tokens = this.$el.find('.tokens'),
- input = this.$el.find('input');
+ input = this.$el.find('input'),
+ lastToken = tokens.children().last();
+
if (!this.hasOwnProperty('_paddingLeft')) {
- this._paddingLeft = input.css('paddingLeft');
+ this._paddingLeft = parseInt(input.css('paddingLeft'));
}
if (this.values.length) {
- input.css({ paddingLeft: tokens.outerWidth() + 'px' });
+ var left = lastToken.position().left + lastToken.outerWidth();
+ input.css({ paddingLeft: left + this._paddingLeft });
} else {
input.css({ paddingLeft: this._paddingLeft });
}
+
+ if (tokens.outerHeight() > input.outerHeight()) {
+ input.css({
+ paddingTop: tokens.outerHeight() - lastToken.outerHeight()
+ });
+ }
},
updateInput: function() {
if (this.options.singular && this.values.length) {
this.$el.find('input').attr('disabled', true);
@@ -160,25 +183,52 @@
}
this.$el.data('computed-value', computedValue || null);
},
+ makeSortable: function() {
+ var self = this;
+
+ this.$el.find('.tokens').sortable({
+ stop: function(event, ui) {
+ var token = ui.item.data('value'),
+ index = ui.item.index();
+
+ self.moveTokenToIndex(token, index);
+ }
+ });
+ },
+
+ moveTokenToIndex: function(token, toIndex) {
+ var fromIndex = _.indexOf(this.values, token);
+ this.values.splice(toIndex, 0, this.values.splice(fromIndex, 1)[0]);
+
+ this.render();
+ },
+
// -- Event Handlers --
onKey: function(e) {
+ this.lastKey = Date.now();
+
switch (e.which) {
case ENTER:
- case COMMA:
case TAB:
this.onTokenBreaker(e); break;
case BACKSPACE:
this.onBackspace(e); break;
default:
- this.delayedCapture();
+ return;
}
},
+ onInput: function() {
+ if (!this.lastKey || Date.now() - this.lastKey > 10) {
+ this.capture();
+ }
+ },
+
onTokenBreaker: function(e) {
var input = this.$el.find('input'),
value = input.val();
if (value.length) {
@@ -189,23 +239,16 @@
onBackspace: function(e) {
var input = this.$el.find('input'),
value = input.val();
- if (value.length) {
- this.delayedCapture();
- } else {
+ if (!value.length) {
e.preventDefault();
this.focusOrRemoveLastToken();
}
},
- onClick: function(e) {
- this.render();
- this.focus();
- },
-
onClickDel: function(e) {
e.preventDefault();
var token = $(e.target).closest('.token'),
value = token.data('value');
@@ -230,12 +273,20 @@
this.values.push(value);
}
}
this.render();
+ this.focus();
},
+ onClickToggle: function(e) {
+ e.preventDefault();
+
+ this.isShowingAll = true;
+ this.render();
+ },
+
// -- Helpers --
sanitizeValues: function(values) {
values = [values];
values = _.flatten(values);
@@ -249,10 +300,16 @@
return values;
},
prepareValueOptions: function() {
+ var self = this;
+
this.valueOptions = _.uniq(this.valueOptions.concat(this.values)).sort();
+
+ this.remainingOptions = _.reject(this.valueOptions, function(value) {
+ return _.contains(self.values, value);
+ });
}
});
})();