bookmarks/js/views/Folder.js

326 lines
8.0 KiB
JavaScript

import _ from 'underscore';
import Backbone from 'backbone';
import interact from 'interactjs';
import isTouchDevice from '../utils/IsTouchscreen';
import templateString from '../templates/Folder.html';
import FoldersView from './Folders';
import Folder from '../models/Folder';
const Marionette = Backbone.Marionette;
const Radio = Backbone.Radio;
export default Marionette.View.extend({
className: 'folders-item',
tagName: 'li',
template: _.template(templateString),
regions: {
children: {
el: '.children',
replaceElement: true
}
},
ui: {
actionsMenu: '.app-navigation-entry-menu',
actionsToggle: '.app-navigation-entry-utils-menu-button'
},
events: {
'click > a': 'select',
'click > .collapse': 'toggleChildren',
'click @ui.actionsToggle': 'toggleActions',
'click > .app-navigation-entry-menu .menu-delete': 'actionDelete',
'click > .app-navigation-entry-menu .menu-edit': 'actionEdit',
'click > .app-navigation-entry-menu .menu-addsub': 'actionAddSubFolder',
'click .action.submit': 'actionSubmit',
'click .action.cancel': 'actionCancel',
'keyup input.title': 'onKeyup',
mouseover: 'onMouseOver',
mouseout: 'onMouseOut'
},
initialize: function(options) {
this.app = options.app;
this.selectedFolder = options.selectedFolder;
this.listenTo(Radio.channel('nav'), 'navigate', this.onNavigate);
this.listenTo(Radio.channel('documentClicked'), 'click', this.closeActions);
this.listenTo(this.model, 'dropFolder', this.onDropFolder);
this.listenTo(this.model, 'dropped', this.onDropped);
this.initInteractable();
},
initInteractable: function() {
this.interactable = interact(this.el)
.dropzone({
overlap: 'pointer',
ignoreFrom: '.folders, input',
ondrop: this.onDrop.bind(this),
ondropactivate: this.onDropActivate.bind(this),
ondropdeactivate: this.onDropDeactivate.bind(this)
})
.draggable({
onstart: this.onDragStart.bind(this),
onend: this.onDragEnd.bind(this),
onmove: this.onDragMove.bind(this),
hold: isTouchDevice() ? 500 : 0
});
this.interactable.model = this.model;
},
onRender: function() {
if (this.model.get('children') && this.model.get('children').length) {
this.$el.addClass('collapsible');
} else {
this.$el.removeClass('collapsible');
this.$('> .collapse').hide();
}
this.showChildView(
'children',
new FoldersView({
collection: this.model.get('children'),
parentFolder: this.model,
selectedFolder: this.selectedFolder,
app: this.app
})
);
this.$el.removeClass('active');
if (this.selectedFolder == this.model.get('id')) {
this.$el.addClass('active');
}
if (this.model.contains(this.selectedFolder)) {
this.showChildren();
}
this.$el.removeClass('editing');
if (this.editing) {
this.$el.addClass('editing');
this.$('input').focus();
}
},
select: function(e) {
e.preventDefault();
e.stopPropagation();
if (this.editing) return;
this.triggerRoute();
},
toggleChildren: function(e) {
if (e) {
e.stopPropagation();
e.preventDefault();
}
if (this.editing) return;
this.$el.toggleClass('open');
},
showChildren: function(e) {
if (e) {
e.stopPropagation();
e.preventDefault();
}
this.$el.addClass('open');
},
onNavigate: function(category, folderId) {
if (category !== 'folder') {
return;
}
this.selectedFolder = folderId;
this.render();
},
triggerRoute: function() {
Backbone.history.navigate('folder/' + this.model.get('id'), {
trigger: true
});
},
toggleActions: function(e) {
if (e) {
e.stopPropagation();
e.preventDefault();
}
this.getUI('actionsMenu').toggleClass('open');
},
closeActions: function(e) {
if (this.editing || $.contains(this.getUI('actionsToggle')[0], e.target))
return;
this.getUI('actionsMenu').removeClass('open');
},
actionDelete: function(e) {
if (e) {
e.stopPropagation();
e.preventDefault();
}
this.model.destroy();
},
actionEdit: function(e) {
if (e) {
e.stopPropagation();
e.preventDefault();
}
this.editing = true;
this.render();
},
onKeyup: function(e) {
if (e.which === 13) {
this.actionSubmit();
}
},
actionSubmit: function(e) {
if (e) {
e.stopPropagation();
e.preventDefault();
}
this.model.set('title', this.$('input.title').val());
this.model.save();
this.actionCancel();
},
actionCancel: function(e) {
if (e) {
e.stopPropagation();
e.preventDefault();
}
this.editing = false;
this.render();
},
actionAddSubFolder: function(e) {
if (e) {
e.stopPropagation();
e.preventDefault();
}
this.model.trigger('addSubFolder'); // communicate to AddFolderView
this.$el.addClass('collapsible');
this.toggleActions();
this.showChildren();
},
onDropActivate: function(e) {
if (e.draggable.model instanceof Folder) {
this.folderBeingDragged = e.draggable.model;
this.$el.addClass('droptarget-folder');
return;
}
if (this.$el.hasClass('active')) return;
this.$el.addClass('droptarget-bookmark');
},
onMouseOver: function(e) {
if (
this.$el.hasClass('droptarget-bookmark') &&
this.model.get('children').length
) {
this.showChildren();
}
if (this.folderBeingDragged) {
this.showChildren();
}
},
onMouseOut: function(e) {
if (
this.$el.hasClass('droptarget-bookmark') &&
this.model.get('children').length
) {
this.toggleChildren();
}
// Only hide children if this is a folder AND the folder being dragged is not within this one
if (
this.folderBeingDragged &&
!this.model.contains(this.folderBeingDragged.get('id'))
) {
this.toggleChildren();
}
},
onDropDeactivate: function(e) {
// TODO: Wait for 'sync' til we remove this
this.folderBeingDragged = false;
this.$el.removeClass('droptarget-bookmark');
this.$el.removeClass('droptarget-folder');
},
onDrop: function(e) {
if (!(e.draggable.model instanceof Folder)) {
this.onDropBookmark(e);
} else {
this.onDropFolder(e);
}
},
onDropBookmark: function(e) {
var that = this;
e.draggable.model.trigger('dropped');
if (this.app.selectedBookmarks.length) {
this.app.selectedBookmarks.models.slice().forEach(function(bm, i) {
bm.trigger('unselect');
that.moveBookmark(bm);
// quiver only once
if (i === that.app.selectedBookmarks.length - 1) {
bm.once('sync', function() {
that.quiver();
});
}
});
this.app.selectedBookmarks.reset();
return;
}
this.moveBookmark(e.draggable.model);
e.draggable.model.once('sync', function() {
that.quiver();
});
},
onDropFolder: function(e) {
var that = this;
e.draggable.model.trigger('dropped');
e.draggable.model.once('sync', function() {
that.quiver(function() {
that.app.folders.fetch({ reset: true });
});
});
this.moveFolder(e.draggable.model);
},
quiver: function(cb) {
var that = this;
that.$('> a').addClass('quiver-vertically');
setTimeout(function() {
that.$('> a').removeClass('quiver-vertically');
cb && cb();
}, 600);
},
moveFolder: function(folder) {
folder.set('parent_folder', this.model.get('id'));
folder.save();
},
moveBookmark: function(bm) {
var that = this;
var folders = bm.get('folders'),
isInsideFolder =
'undefined' !==
typeof this.app.bookmarks.loadingState.get('query').folder;
if (isInsideFolder) {
if (
this.app.bookmarks.loadingState.get('query').folder ===
this.model.get('id')
) {
return;
}
folders = _.without(
folders,
this.app.bookmarks.loadingState.get('query').folder
);
}
folders.push(this.model.get('id'));
bm.set('folders', folders);
if (isInsideFolder) {
bm.once('sync', function() {
that.app.bookmarks.remove(bm);
});
}
bm.save();
},
onDragStart: function(e) {
this.$el.addClass('dragging');
},
onDragMove: function(e) {
this.$el.offset({ top: e.pageY + 5, left: e.pageX });
},
onDragEnd: function(e) {
this.$el.removeClass('dragging');
this.$el.css({ position: 'relative', top: 0, left: 0 });
},
onDropped: function() {
var that = this;
this.$el.hide();
},
onDestroy: function() {
this.interactable.unset();
}
});