From ac460c68833b40b2c10510b11bfbea7280068260 Mon Sep 17 00:00:00 2001 From: Antelle Date: Sun, 18 Oct 2015 18:43:16 +0300 Subject: [PATCH] tags autocomplete --- TODO.md | 1 - app/scripts/views/fields/field-view-tags.js | 72 +++++++++++++++++++-- app/styles/areas/_details.scss | 15 +++++ 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/TODO.md b/TODO.md index 5a56f70c..77473cef 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,6 @@ - [ ] add/edit groups - [ ] dropbox - [ ] generate -- [ ] tags autocomplete - [ ] file settings # FUTURE diff --git a/app/scripts/views/fields/field-view-tags.js b/app/scripts/views/fields/field-view-tags.js index 05576860..227a42d1 100644 --- a/app/scripts/views/fields/field-view-tags.js +++ b/app/scripts/views/fields/field-view-tags.js @@ -11,17 +11,75 @@ var FieldViewTags = FieldViewText.extend({ return value ? value.join(', ') : ''; }, + valueToTags: function(val) { + var allTags = {}; + this.model.tags.forEach(function(tag) { + allTags[tag.toLowerCase()] = tag; + }); + return _.unique(val.split(/\s*[;,:]\s*/).filter(_.identity).map(function (tag) { + return allTags[tag.toLowerCase()] || tag; + })); + }, + endEdit: function(newVal, extra) { + if (this.selectedTag) { + newVal += (newVal ? ', ' : '') + this.selectedTag; + this.input.val(newVal); + this.input.focus(); + this.setTags(); + delete this.selectedTag; + return; + } if (newVal !== undefined) { - var allTags = {}; - this.model.tags.forEach(function(tag) { - allTags[tag.toLowerCase()] = tag; - }); - newVal = _.unique(newVal.split(/\s*[;,:]\s*/).filter(_.identity).map(function (tag) { - return allTags[tag.toLowerCase()] || tag; - })); + newVal = this.valueToTags(newVal); + } + if (this.tagsAutocomplete) { + this.tagsAutocomplete.remove(); + this.tagsAutocomplete = null; } FieldViewText.prototype.endEdit.call(this, newVal, extra); + }, + + startEdit: function() { + FieldViewText.prototype.startEdit.call(this); + var fieldRect = this.input[0].getBoundingClientRect(); + this.tagsAutocomplete = $('
').appendTo('body'); + this.tagsAutocomplete.css({ + top: fieldRect.bottom, + left: fieldRect.left, + width: fieldRect.width - 2 + }); + this.tagsAutocomplete.mousedown(this.tagsAutocompleteClick.bind(this)); + this.setTags(); + }, + + fieldValueInput: function(e) { + e.stopPropagation(); + this.setTags(); + FieldViewText.prototype.fieldValueInput.call(this, e); + }, + + getAvailableTags: function() { + var tags = this.valueToTags(this.input.val()); + return this.model.tags.filter(function(tag) { + return tags.indexOf(tag) < 0; + }); + }, + + setTags: function() { + var availableTags = this.getAvailableTags(); + var tagsHtml = availableTags.map(function(tag) { + return '
' + _.escape(tag) + '
'; + }).join(''); + this.tagsAutocomplete.html(tagsHtml); + this.tagsAutocomplete.toggle(tagsHtml); + }, + + tagsAutocompleteClick: function(e) { + e.stopPropagation(); + if (e.target.classList.contains('details__tags-autocomplete-tag')) { + this.selectedTag = $(e.target).text(); + } } }); diff --git a/app/styles/areas/_details.scss b/app/styles/areas/_details.scss index 7a2db7b5..08795c49 100644 --- a/app/styles/areas/_details.scss +++ b/app/styles/areas/_details.scss @@ -446,4 +446,19 @@ margin-top: 1em; } } + + &__tags-autocomplete { + position: absolute; + @include th { + color: text-color(); + background: background-color(); + border: 1px solid light-border-color(); + box-shadow: dropdown-box-shadow(); + } + &-tag { + padding: $base-padding; + display: inline-block; + @include area-selectable(bottom); + } + } }