usernames autocomplete

This commit is contained in:
Antelle 2016-02-28 14:16:05 +03:00
parent 35bd1ab3be
commit 8f35f64148
6 changed files with 88 additions and 7 deletions

View File

@ -224,6 +224,25 @@ var AppModel = Backbone.Model.extend({
return { group: group, file: file };
},
completeUserNames: function(part) {
var userNames = {};
this.files.forEach(function(file) {
file.forEachEntry({ text: part, textLower: part.toLowerCase(), advanced: { user: true } }, function(entry) {
var userName = entry.user;
if (userName) {
userNames[userName] = (userNames[userName] || 0) + 1;
}
});
});
var matches = _.pairs(userNames);
matches.sort(function(x, y) { return y[1] - x[1]; });
var maxResults = 5;
if (matches.length > maxResults) {
matches.length = maxResults;
}
return matches.map(function(m) { return m[0]; });
},
createNewEntry: function() {
var sel = this.getFirstSelectedGroup();
return EntryModel.newEntry(sel.group, sel.file);

View File

@ -5,6 +5,7 @@ var Backbone = require('backbone'),
AppSettingsModel = require('../../models/app-settings-model'),
Scrollable = require('../../mixins/scrollable'),
FieldViewText = require('../fields/field-view-text'),
FieldViewAutocomplete = require('../fields/field-view-autocomplete'),
FieldViewDate = require('../fields/field-view-date'),
FieldViewTags = require('../fields/field-view-tags'),
FieldViewUrl = require('../fields/field-view-url'),
@ -118,8 +119,8 @@ var DetailsView = Backbone.View.extend({
addFieldViews: function() {
var model = this.model;
this.userEditView = new FieldViewText({ model: { name: '$UserName', title: Locale.detUser,
value: function() { return model.user; } } });
this.userEditView = new FieldViewAutocomplete({ model: { name: '$UserName', title: Locale.detUser,
value: function() { return model.user; }, getCompletions: this.getUserNameCompletions.bind(this) } });
this.fieldViews.push(this.userEditView);
this.passEditView = new FieldViewText({ model: { name: '$Password', title: Locale.detPassword, canGen: true,
value: function() { return model.password; } } });
@ -167,6 +168,10 @@ var DetailsView = Backbone.View.extend({
}, this);
},
getUserNameCompletions: function(part) {
return this.appModel.completeUserNames(part);
},
setSelectedColor: function(color) {
this.$el.find('.details__colors-popup > .details__colors-popup-item').removeClass('details__colors-popup-item--active');
var colorEl = this.$el.find('.details__header-color')[0];

View File

@ -0,0 +1,56 @@
'use strict';
var FieldViewText = require('./field-view-text');
var FieldViewAutocomplete = FieldViewText.extend({
endEdit: function(newVal, extra) {
if (this.autocomplete) {
this.autocomplete.remove();
this.autocomplete = null;
}
FieldViewText.prototype.endEdit.call(this, newVal, extra);
},
startEdit: function() {
FieldViewText.prototype.startEdit.call(this);
var fieldRect = this.input[0].getBoundingClientRect();
this.autocomplete = $('<div class="details__field-autocomplete"></div>').appendTo('body');
this.autocomplete.css({
top: fieldRect.bottom,
left: fieldRect.left,
width: fieldRect.width - 2
});
this.autocomplete.mousedown(this.autocompleteClick.bind(this));
if (!this.input.val()) {
this.updateAutocomplete();
}
},
fieldValueInput: function(e) {
e.stopPropagation();
this.updateAutocomplete();
FieldViewText.prototype.fieldValueInput.call(this, e);
},
updateAutocomplete: function() {
var completions = this.model.getCompletions(this.input.val());
var completionsHtml = completions.map(function(item) {
return '<div class="details__field-autocomplete-item">' + _.escape(item) + '</div>';
}).join('');
this.autocomplete.html(completionsHtml);
this.autocomplete.toggle(!!completionsHtml);
},
autocompleteClick: function(e) {
e.stopPropagation();
if (e.target.classList.contains('details__field-autocomplete-item')) {
var selectedItem = $(e.target).text();
this.input.val(selectedItem);
this.endEdit(selectedItem);
} else {
this.afterPaint(function () { this.input.focus(); });
}
}
});
module.exports = FieldViewAutocomplete;

View File

@ -35,7 +35,7 @@ var FieldViewTags = FieldViewText.extend({
startEdit: function() {
FieldViewText.prototype.startEdit.call(this);
var fieldRect = this.input[0].getBoundingClientRect();
this.tagsAutocomplete = $('<div class="details__tags-autocomplete"></div>').appendTo('body');
this.tagsAutocomplete = $('<div class="details__field-autocomplete"></div>').appendTo('body');
this.tagsAutocomplete.css({
top: fieldRect.bottom,
left: fieldRect.left,
@ -63,7 +63,7 @@ var FieldViewTags = FieldViewText.extend({
setTags: function() {
var availableTags = this.getAvailableTags();
var tagsHtml = availableTags.map(function(tag) {
return '<div class="details__tags-autocomplete-tag">' + _.escape(tag) + '</div>';
return '<div class="details__field-autocomplete-item">' + _.escape(tag) + '</div>';
}).join('');
this.tagsAutocomplete.html(tagsHtml);
this.tagsAutocomplete.toggle(!!tagsHtml);
@ -71,7 +71,7 @@ var FieldViewTags = FieldViewText.extend({
tagsAutocompleteClick: function(e) {
e.stopPropagation();
if (e.target.classList.contains('details__tags-autocomplete-tag')) {
if (e.target.classList.contains('details__field-autocomplete-item')) {
var selectedTag = $(e.target).text(), newVal = this.input.val();
if (newVal) {
var tags = this.valueToTags(newVal);

View File

@ -485,10 +485,10 @@
}
}
&__tags-autocomplete {
&__field-autocomplete {
position: absolute;
@include common-dropdown;
&-tag {
&-item {
padding: $base-padding;
display: inline-block;
@include area-selectable(bottom);

View File

@ -11,6 +11,7 @@ Storage providers, usability improvements
`+` regenerate password button
`+` option to search in title
`+` open files created without password
`+` usernames autocomplete
`-` fix #88: capslock indicator
##### v1.0.4 (2016-02-25)