From 8e6a00b8d96c1cfcf35b044ca91627b126daa7dc Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Sat, 21 Jan 2017 18:51:56 -0600 Subject: [PATCH] tags input in editor, #41 --- .../app/frontend/controllers/editor.js | 31 ++++- .../app/frontend/controllers/home.js | 31 ++--- .../app/frontend/controllers/tags.js | 5 - .../app/frontend/models/api/item.js | 1 + .../app/services/directives/draggable.js | 109 ------------------ .../javascripts/app/services/modelManager.js | 11 ++ app/assets/stylesheets/app/_editor.scss | 24 ++-- .../templates/frontend/editor.html.haml | 3 + app/assets/templates/frontend/home.html.haml | 4 +- app/assets/templates/frontend/notes.html.haml | 3 +- app/assets/templates/frontend/tags.html.haml | 3 +- 11 files changed, 77 insertions(+), 148 deletions(-) delete mode 100644 app/assets/javascripts/app/services/directives/draggable.js diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index 2c89c93c3..6266db8a7 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -5,7 +5,8 @@ angular.module('app.frontend') scope: { save: "&", remove: "&", - note: "=" + note: "=", + updateTags: "&" }, templateUrl: 'frontend/editor.html', replace: true, @@ -16,7 +17,7 @@ angular.module('app.frontend') link:function(scope, elem, attrs, ctrl) { /** - * Insert 4 spaces when a tab key is pressed, + * Insert 4 spaces when a tab key is pressed, * only used when inside of the text editor. */ var handleTab = function (event) { @@ -85,6 +86,7 @@ angular.module('app.frontend') this.editorMode = 'edit'; this.showExtensions = false; this.showMenu = false; + this.loadTagsString(); if(note.safeText().length == 0 && note.dummy) { this.focusTitle(100); @@ -236,4 +238,29 @@ angular.module('app.frontend') this.focusEditor(100); } + /* Tags */ + + this.loadTagsString = function() { + var string = ""; + for(var tag of this.note.tags) { + string += "#" + tag.title + " "; + } + this.tagsString = string; + } + + this.updateTagsFromTagsString = function($event) { + $event.target.blur(); + + var tags = this.tagsString.split("#"); + tags = _.filter(tags, function(tag){ + return tag.length > 0; + }) + tags = _.map(tags, function(tag){ + return tag.trim(); + }) + + this.note.dummy = false; + this.updateTags()(this.note, tags); + } + }); diff --git a/app/assets/javascripts/app/frontend/controllers/home.js b/app/assets/javascripts/app/frontend/controllers/home.js index 999afd03d..9665c3186 100644 --- a/app/assets/javascripts/app/frontend/controllers/home.js +++ b/app/assets/javascripts/app/frontend/controllers/home.js @@ -17,6 +17,23 @@ angular.module('app.frontend') $scope.tags = modelManager.tags; $scope.allTag.notes = modelManager.notes; + /* + Editor Callbacks + */ + + $scope.updateTagsForNote = function(note, stringTags) { + note.removeAllRelationships(); + var tags = []; + for(var tagString of stringTags) { + tags.push(modelManager.findOrCreateTagByTitle(tagString)); + } + for(var tag of tags) { + modelManager.createRelationshipBetweenItems(note, tag); + } + console.log("updating tags for note", note, tags); + apiController.sync(); + } + /* Tags Ctrl Callbacks */ @@ -43,20 +60,6 @@ angular.module('app.frontend') apiController.sync(callback); } - /* - Called to update the tag of a note after drag and drop change - The note object is a copy of the original - */ - $scope.tagsUpdateNoteTag = function(noteCopy, newTag, oldTag) { - - var originalNote = _.find(modelManager.notes, {uuid: noteCopy.uuid}); - if(!newTag.all) { - modelManager.createRelationshipBetweenItems(newTag, originalNote); - } - - apiController.sync(function(){}); - } - /* Notes Ctrl Callbacks */ diff --git a/app/assets/javascripts/app/frontend/controllers/tags.js b/app/assets/javascripts/app/frontend/controllers/tags.js index ef94b595d..5f673c2da 100644 --- a/app/assets/javascripts/app/frontend/controllers/tags.js +++ b/app/assets/javascripts/app/frontend/controllers/tags.js @@ -102,9 +102,4 @@ angular.module('app.frontend') return validNotes.length; } - this.handleDrop = function(e, newTag, note) { - this.updateNoteTag()(note, newTag, this.selectedTag); - }.bind(this) - - }); diff --git a/app/assets/javascripts/app/frontend/models/api/item.js b/app/assets/javascripts/app/frontend/models/api/item.js index 90c180943..b87e66c8d 100644 --- a/app/assets/javascripts/app/frontend/models/api/item.js +++ b/app/assets/javascripts/app/frontend/models/api/item.js @@ -109,6 +109,7 @@ class Item { removeAllRelationships() { // must override + this.setDirty(true); } mergeMetadataFromItem(item) { diff --git a/app/assets/javascripts/app/services/directives/draggable.js b/app/assets/javascripts/app/services/directives/draggable.js deleted file mode 100644 index 3f531dfed..000000000 --- a/app/assets/javascripts/app/services/directives/draggable.js +++ /dev/null @@ -1,109 +0,0 @@ -angular - .module('app.frontend') - .directive('draggable', function() { - return { - scope: { - note: "=" - }, - link: function(scope, element) { - // this gives us the native JS object - var el = element[0]; - - el.draggable = true; - - el.addEventListener( - 'dragstart', - function(e) { - e.dataTransfer.effectAllowed = 'move'; - e.dataTransfer.setData('Note', JSON.stringify(scope.note)); - this.classList.add('drag'); - return false; - }, - false - ); - - el.addEventListener( - 'dragend', - function(e) { - this.classList.remove('drag'); - return false; - }, - false - ); - } - } -}); - -angular - .module('app.frontend') - .directive('droppable', function() { - return { - scope: { - drop: '&', - bin: '=', - tag: "=" - }, - link: function(scope, element) { - // again we need the native object - var el = element[0]; - - el.addEventListener( - 'dragover', - function(e) { - e.dataTransfer.dropEffect = 'move'; - // allows us to drop - if (e.preventDefault) e.preventDefault(); - this.classList.add('over'); - return false; - }, - false - ); - - var counter = 0; - - el.addEventListener( - 'dragenter', - function(e) { - counter++; - this.classList.add('over'); - return false; - }, - false - ); - - el.addEventListener( - 'dragleave', - function(e) { - counter--; - if (counter === 0) { - this.classList.remove('over'); - } - return false; - }, - false - ); - - el.addEventListener( - 'drop', - function(e) { - // Stops some browsers from redirecting. - if (e.stopPropagation) e.stopPropagation(); - - this.classList.remove('over'); - - var binId = this.uuid; - var note = new Note(JSON.parse(e.dataTransfer.getData('Note'))); - scope.$apply(function(scope) { - var fn = scope.drop(); - if ('undefined' !== typeof fn) { - fn(e, scope.tag, note); - } - }); - - return false; - }, - false - ); - } - } -}); diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index 7ddf1d3b7..473eae791 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -32,6 +32,17 @@ class ModelManager { return _.find(this.items, {uuid: itemId}); } + findOrCreateTagByTitle(title) { + console.log("looking for tag", title); + var tag = _.find(this.tags, {title: title}) + if(!tag) { + console.log("not found, creating"); + tag = this.createItem({content_type: "Tag", title: title}); + this.addItem(tag); + } + return tag; + } + mapResponseItemsToLocalModels(items) { return this.mapResponseItemsToLocalModelsOmittingFields(items, null); } diff --git a/app/assets/stylesheets/app/_editor.scss b/app/assets/stylesheets/app/_editor.scss index 4b019f454..20b7b4e44 100644 --- a/app/assets/stylesheets/app/_editor.scss +++ b/app/assets/stylesheets/app/_editor.scss @@ -29,7 +29,7 @@ } } - $heading-height: 90px; + $heading-height: 100px; .editor-heading { @@ -77,7 +77,17 @@ color: rgba(black, 0.23); } + .tags { + clear: left; + width: 100%; + height: 25px; + .tags-input { + background-color: transparent; + width: 100%; + border: none; + } + } } .fullscreen-ghost-bar { @@ -86,17 +96,6 @@ width: 20%; height: 200px; z-index: 100; - // - // &:hover + .section-title-bar { - // // show section title bar when hover over ghost bar - // opacity: 1.0; - // z-index: 100; - // } - - // &:not(:hover) + .section-title-bar { - // // display: none; - // opacity: 1.0; - // } } @@ -167,6 +166,7 @@ } } + .markdown { margin-left: 15px; float: right; diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index 0f90c8687..502f5b278 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -6,6 +6,9 @@ "ng-change" => "ctrl.nameChanged()", "ng-focus" => "ctrl.onNameFocus()", "select-on-click" => "true"} .save-status {{ctrl.noteStatus}} + .tags + %input.tags-input{"type" => "text", "ng-keyup" => "$event.keyCode == 13 && ctrl.updateTagsFromTagsString($event, ctrl.tagsString)", + "ng-model" => "ctrl.tagsString", "placeholder" => "#tags"} .section-menu %ul.nav.nav-pills %li.dropdown diff --git a/app/assets/templates/frontend/home.html.haml b/app/assets/templates/frontend/home.html.haml index 174ebca06..f2f40d0bf 100644 --- a/app/assets/templates/frontend/home.html.haml +++ b/app/assets/templates/frontend/home.html.haml @@ -2,10 +2,10 @@ .app-container .app %tags-section{"save" => "tagsSave", "add-new" => "tagsAddNew", "will-select" => "tagsWillMakeSelection", "selection-made" => "tagsSelectionMade", "all-tag" => "allTag", - "tags" => "tags", "update-note-tag" => "tagsUpdateNoteTag"} + "tags" => "tags"} %notes-section{"remove-tag" => "notesRemoveTag", "add-new" => "notesAddNew", "selection-made" => "notesSelectionMade", "tag" => "selectedTag", "remove" => "deleteNote"} - %editor-section{"ng-if" => "selectedNote", "note" => "selectedNote", "remove" => "deleteNote", "save" => "saveNote"} + %editor-section{"ng-if" => "selectedNote", "note" => "selectedNote", "remove" => "deleteNote", "save" => "saveNote", "update-tags" => "updateTagsForNote"} %header{"user" => "defaultUser"} diff --git a/app/assets/templates/frontend/notes.html.haml b/app/assets/templates/frontend/notes.html.haml index cfd9fc52b..48f42dc83 100644 --- a/app/assets/templates/frontend/notes.html.haml +++ b/app/assets/templates/frontend/notes.html.haml @@ -19,8 +19,7 @@ %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedTagDelete()"} Delete Tag .note{"ng-repeat" => "note in ctrl.tag.notes | filter: ctrl.filterNotes", - "ng-click" => "ctrl.selectNote(note)", "ng-class" => "{'selected' : ctrl.selectedNote == note}", - "ng-attr-draggable" => "{{note.dummy ? undefined : 'true'}}", "note" => "note"} + "ng-click" => "ctrl.selectNote(note)", "ng-class" => "{'selected' : ctrl.selectedNote == note}"} .name {{note.title}} .date {{(note.created_at | appDateTime) || 'Now'}} diff --git a/app/assets/templates/frontend/tags.html.haml b/app/assets/templates/frontend/tags.html.haml index d90791f8f..69e96b946 100644 --- a/app/assets/templates/frontend/tags.html.haml +++ b/app/assets/templates/frontend/tags.html.haml @@ -4,8 +4,7 @@ .title Tags .add-button.tag-add-button{"ng-click" => "ctrl.clickedAddNewTag()"} + {{ctrl.test}} - .tag{"ng-if" => "ctrl.allTag", "ng-click" => "ctrl.selectTag(ctrl.allTag)", "ng-class" => "{'selected' : ctrl.selectedTag == ctrl.allTag}", - "droppable" => true, "drop" => "ctrl.handleDrop", "tag" => "ctrl.allTag"} + .tag{"ng-if" => "ctrl.allTag", "ng-click" => "ctrl.selectTag(ctrl.allTag)", "ng-class" => "{'selected' : ctrl.selectedTag == ctrl.allTag}"} %input.title{"ng-disabled" => "true", "ng-model" => "ctrl.allTag.title"} .count {{ctrl.noteCount(ctrl.allTag)}}