From e1bb801e9f059b015258e90689be606c5fa80b91 Mon Sep 17 00:00:00 2001 From: Is Isilon Date: Sun, 31 Jan 2016 00:03:55 +0800 Subject: [PATCH] Fixed indenting, up and down arrows, basic sorting --- public/javascripts/collections/list.js | 5 ++ public/javascripts/util/constants.js | 1 + public/javascripts/views/list.js | 70 ++------------------ public/javascripts/views/task.js | 89 ++++++++++++++++++-------- 4 files changed, 74 insertions(+), 91 deletions(-) diff --git a/public/javascripts/collections/list.js b/public/javascripts/collections/list.js index a2467e5..b702b45 100644 --- a/public/javascripts/collections/list.js +++ b/public/javascripts/collections/list.js @@ -29,6 +29,11 @@ localforageBackbone return Backbone.sync.apply(this, arguments); }, + /** sort by priority then created date **/ + comporator: function(child){ + return [child.get('priority'),child.get('createdAt')]; + }, + url: '/tasks' }); diff --git a/public/javascripts/util/constants.js b/public/javascripts/util/constants.js index 52cc4bb..b6c8006 100644 --- a/public/javascripts/util/constants.js +++ b/public/javascripts/util/constants.js @@ -9,6 +9,7 @@ function() { UP_ARROW: 38, DOWN_ARROW: 40, TAB: 9, + CNTRL: 17, }; diff --git a/public/javascripts/views/list.js b/public/javascripts/views/list.js index c295db6..81cb339 100644 --- a/public/javascripts/views/list.js +++ b/public/javascripts/views/list.js @@ -18,8 +18,7 @@ define( Marionette ) { - // The tree's root: a simple collection view that renders - // a recursive tree structure for each item in the collection + // renders recursive tree structure for each item in collection var ListView = Backbone.Marionette.CollectionView.extend({ el: $("#main .children"), @@ -33,9 +32,7 @@ define( initialize: function () { var self = this; - Tasks = new List(); - - // this.listenTo(this.collection, 'add', this.renderTask); + this.collection = Tasks = new List(); /** Load demo data **/ function loadDemoData() { @@ -50,7 +47,6 @@ define( if (children.length === 0) loadDemoData(); else { - this.collection = {models:Tasks.filter({'parentId':0})}; this.render(); } } @@ -60,7 +56,8 @@ define( error: function () { // switch to localforage database if server isn't present and fetch again - window.hackflowyOffline=true; + // from there + window.hackflowyOffline = true; $('#header').append('
Running in offline mode, data may be lost
'); Tasks.fetch({ success: success, @@ -74,63 +71,8 @@ define( // Only show direct children filter: function (child, index, collection) { - return child.get('parentId') === 0; - }, - - // render: function () { - // var models = Tasks.filter({'parentId':0}); - // - // _.each(models, function(model, index) { - // var taskView = new this.childView({ - // model: model, - // collection: model.collection - // }); - // var a = taskView.render(); - // this.$el.append(a.el); - // }, this); - // - // this.triggerMethod('render', this); - // return this; - // }, - - renderTask: function (task) { - var taskView = new TaskView({ - model: task - }); - var a = taskView.render(); - this.$el.append(a.el); - // - // if (a.model.get('parentId') === 0) { - // // insert it at the end of the top-level items - // this.$el.append(a.el); - // } else { - // - // // TODO use dom nesting instead of data attributes - // var parent = $('*[data-id="' + a.model.get('parentId') + '"]').parents('li:first'); - // var siblings = parent.find('.children:first li'); - // var editingSibling = siblings.filter('.editing:first'); - // - // if (editingSibling.length > 0){ - // // insert it after open sibling - // a.$el.insertAfter(editingSibling); - // } - // else if (parent.length > 0) { - // // insert under parent - // a.$el.appendTo(parent.find('.children:first')); - // } else { - // // we have an orphan :( - // // insert at root for lack of a better option - // this.$el.append(a.el); - // - // // TODO deal with loading order - // console.error("Parent not rendered yet: ", { - // selector: parent.selector, - // task: task - // }); - // - // } - // } - } + return child.get('parentId') === 0; + }, }); diff --git a/public/javascripts/views/task.js b/public/javascripts/views/task.js index 9b6c2c6..9d1f66b 100644 --- a/public/javascripts/views/task.js +++ b/public/javascripts/views/task.js @@ -1,5 +1,3 @@ - - define( ['jquery', 'backbone', @@ -55,7 +53,7 @@ define( this.listenTo(this.model, 'destroy', this.remove); // updates from server - if (!window.hackflowyOffline){ + if (!window.hackflowyOffline) { this.socket = io.connect(); this.socket.on('task', function (data) { if (task.model.id == data.id) { @@ -64,16 +62,23 @@ define( 'isCompleted': data.isCompleted }); } else { - console.error("task.model.id != data.id",task.model.id , data.id); + console.error("task.model.id != data.id", task.model.id, data.id); } }); } }, - // Only show direct children + // override marionette filter to filter displayed children filter: function (child, index, collection) { - return child.get('parentId') === this.model.get('id'); + return child.get('parentId') === this.model.get('id'); + }, + + /** Get the parent view or root view **/ + getParentView: function(){ + var parentId = this.model.get('parentId'); + if (parentId>0) return Tasks.get(parentId).view; + else return listView; }, edit: function () { @@ -84,32 +89,58 @@ define( handleKey: function (e) { if (e.keyCode === constants.ENTER_KEY) this.addNote(e.currentTarget); - if (e.keyCode == constants.DOWN_ARROW) - this.$el.next('li').find('input').focus(); - else if (e.keyCode == constants.UP_ARROW) - this.$el.prev('li').find('input').focus(); - - // shift and tab - if (e.shiftKey && e.keyCode == constants.TAB) { + else if (e.ctrlKey && e.keyCode == constants.DOWN_ARROW){ e.preventDefault(); - var newParentId = this.$el.parents('ul:first').find('input:first').data('parent-id'); - if (newParentId === null) newParentId = 0; - this.model.set('parentId', newParentId); - this.model.save(); - Tasks.get(newParentId).view.render() + this.model.save({priority: this.model.get('priority')-1}); + this.getParentView().collection.sortBy(); + this.getParentView().resortView(); this.model.view.ui.input.focus(); + } else if (e.ctrlKey && e.keyCode == constants.UP_ARROW){ + e.preventDefault(); + this.model.save({priority: this.model.get('priority')+1}); + this.getParentView().collection.sortBy(); + this.getParentView().resortView(); + this.model.view.ui.input.focus(); + } else if (e.keyCode == constants.DOWN_ARROW){ + var all = listView.$el.find('ul:visible'); + var next = $(all[all.index(this.$el)+1]); + if (next) + next.find('input:first').focus(); + } else if (e.keyCode == constants.UP_ARROW){ + var all = listView.$el.find('ul:visible'); + var prev = $(all[all.index(this.$el)-1]); + if (prev) + prev.find('input:first').focus(); } + // indent one less, by changing parent + else if (e.shiftKey && e.keyCode == constants.TAB) { + e.preventDefault(); + var parent = this.$el.parents('ul:first'); + var grandparentId = parent.find('input:first').data('parent-id') || 0; + if (this.model.get('parentId') !== grandparentId) { + this.model.save({parentId: grandparentId}); + this.getParentView().render(); + this.model.view.ui.input.focus(); + } else { + console.warn("Can't untab any further"); + } + } + // indent one more, by changing parent else if (e.keyCode == constants.TAB) { e.preventDefault(); - var newParentId = this.$el.prev('ul').find('input:first').data('id'); - this.model.set('parentId', newParentId); - this.model.save(); - this.model.view.remove(); - Tasks.get(newParentId).view.render(); - this.model.view.ui.input.focus(); + var prevSibling = this.$el.prev('ul'); + if (prevSibling.length > 0) { + var siblingId = prevSibling.find('input:first').data('id'); + this.model.save({parentId: siblingId}); + this.model.view.remove(); + this.getParentView().render(); + this.model.view.ui.input.focus(); + } else { + console.warn("Can't tab any further"); + } } - if (!window.hackflowyOffline){ + if (!window.hackflowyOffline) { this.socket.emit('task', { id: this.model.id, parentId: this.model.parentId, @@ -126,9 +157,12 @@ define( var value = this.$('.edit:first').val().trim(); if (value === '') // remove empty items + // TODO let us tab and untab empty ones this.model.destroy(); else - this.model.save({content: value}); + this.model.save({ + content: value + }); this.$el.removeClass('editing'); }, @@ -162,6 +196,7 @@ define( /** Add a new blank note **/ addNote: function () { + // TODO add it after the current one var currentId = this.ui.input.data('id') || 0; parentId = this.ui.input.data('parent-id'); @@ -173,7 +208,7 @@ define( }, /** Fold children of the clicked element */ - foldChildren: function(){ + foldChildren: function () { this.$el.find('ul').toggle(); this.$el.find('li').toggleClass('folded'); },