diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 5ec26d8b33..43f74c5816 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -48,13 +48,6 @@ _fileActionTriggerTemplate: null, - /** - * File actions menu - * - * @type OCA.Files.FileActionsMenu - */ - _menu: null, - /** * @private */ @@ -333,8 +326,20 @@ * @param {OCA.Files.FileActionContext} context rendering context */ _showMenu: function(fileName, context) { - this._menu = new OCA.Files.FileActionsMenu(); - this._menu.showAt(context); + var menu; + var $trigger = context.$file.closest('tr').find('.fileactions .action-menu'); + $trigger.addClass('open'); + + menu = new OCA.Files.FileActionsMenu(); + menu.$el.on('afterHide', function() { + context.$file.removeClass('mouseOver'); + $trigger.removeClass('open'); + menu.remove(); + }); + + context.$file.addClass('mouseOver'); + context.$file.find('td.filename').append(menu.$el); + menu.show(context); }, /** @@ -378,14 +383,19 @@ a: null }, function(event) { + event.stopPropagation(); + event.preventDefault(); + + if ($actionEl.hasClass('open')) { + return; + } + var $file = $(event.target).closest('tr'); if ($file.hasClass('busy')) { return; } var currentFile = $file.find('td.filename'); var fileName = $file.attr('data-file'); - event.stopPropagation(); - event.preventDefault(); context.fileActions.currentFile = currentFile; // also set on global object for legacy apps diff --git a/apps/files/js/fileactionsmenu.js b/apps/files/js/fileactionsmenu.js index 0cc08a1ec9..623ebde544 100644 --- a/apps/files/js/fileactionsmenu.js +++ b/apps/files/js/fileactionsmenu.js @@ -24,13 +24,9 @@ * @constructs FileActionsMenu * @memberof OCA.Files */ - var FileActionsMenu = function() { - this.initialize(); - }; - - FileActionsMenu.prototype = { - $el: null, - _template: null, + var FileActionsMenu = OC.Backbone.View.extend({ + tagName: 'div', + className: 'fileActionsMenu bubble hidden open menu', /** * Current context @@ -39,19 +35,15 @@ */ _context: null, - /** - * @private - */ - initialize: function() { - this.$el = $(''); - this._template = Handlebars.compile(TEMPLATE_MENU); - - this.$el.on('click', 'a.action', _.bind(this._onClickAction, this)); - this.$el.on('afterHide', _.bind(this._onHide, this)); + events: { + 'click a.action': '_onClickAction' }, - destroy: function() { - this.$el.remove(); + template: function(data) { + if (!OCA.Files.FileActionsMenu._TEMPLATE) { + OCA.Files.FileActionsMenu._TEMPLATE = Handlebars.compile(TEMPLATE_MENU); + } + return OCA.Files.FileActionsMenu._TEMPLATE(data); }, /** @@ -113,8 +105,7 @@ return item; }); - this.$el.empty(); - this.$el.append(this._template({ + this.$el.html(this.template({ items: items })); }, @@ -123,27 +114,17 @@ * Displays the menu under the given element * * @param {OCA.Files.FileActionContext} context context + * @param {Object} $trigger trigger element */ - showAt: function(context) { + show: function(context) { this._context = context; this.render(); this.$el.removeClass('hidden'); - context.$file.find('td.filename').append(this.$el); - context.$file.addClass('mouseOver'); - OC.showMenu(null, this.$el); - }, - - /** - * Whenever the menu is hidden - */ - _onHide: function() { - this._context.$file.removeClass('mouseOver'); - this.destroy(); } - }; + }); OCA.Files.FileActionsMenu = FileActionsMenu; diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js index 8c43b917fa..236cff6caf 100644 --- a/apps/files/tests/js/fileactionsSpec.js +++ b/apps/files/tests/js/fileactionsSpec.js @@ -192,10 +192,26 @@ describe('OCA.Files.FileActions tests', function() { context = actionStub.getCall(0).args[1]; expect(context.dir).toEqual('/somepath'); }); - it('shows actions menu when clicking the menu trigger', function() { - expect($tr.find('.menu').length).toEqual(0); - $tr.find('.action-menu').click(); - expect($tr.find('.menu').length).toEqual(1); + describe('actions menu', function() { + it('shows actions menu inside row when clicking the menu trigger', function() { + expect($tr.find('td.filename .fileActionsMenu').length).toEqual(0); + $tr.find('.action-menu').click(); + expect($tr.find('td.filename .fileActionsMenu').length).toEqual(1); + }); + it('shows highlight on current row', function() { + $tr.find('.action-menu').click(); + expect($tr.hasClass('mouseOver')).toEqual(true); + }); + it('cleans up after hiding', function() { + var clock = sinon.useFakeTimers(); + $tr.find('.action-menu').click(); + expect($tr.find('.fileActionsMenu').length).toEqual(1); + OC.hideMenus(); + // sliding animation + clock.tick(500); + expect($tr.hasClass('mouseOver')).toEqual(false); + expect($tr.find('.fileActionsMenu').length).toEqual(0); + }); }); }); describe('custom rendering', function() { diff --git a/apps/files/tests/js/fileactionsmenuSpec.js b/apps/files/tests/js/fileactionsmenuSpec.js index 4343979497..0cfd12a2d0 100644 --- a/apps/files/tests/js/fileactionsmenuSpec.js +++ b/apps/files/tests/js/fileactionsmenuSpec.js @@ -87,24 +87,17 @@ describe('OCA.Files.FileActionsMenu tests', function() { dir: fileList.getCurrentDirectory() }; menu = new OCA.Files.FileActionsMenu(); - menu.showAt(menuContext); + menu.show(menuContext); }); afterEach(function() { fileActions = null; fileList.destroy(); fileList = undefined; - menu.destroy(); + menu.remove(); $('#dir, #permissions, #filestable').remove(); }); describe('rendering', function() { - it('displays menu in the row container', function() { - expect(menu.$el.closest('td.filename').length).toEqual(1); - expect($tr.find('.fileActionsMenu').length).toEqual(1); - }); - it('highlights the row in the file list', function() { - expect($tr.hasClass('mouseOver')).toEqual(true); - }); it('renders dropdown actions in menu', function() { var $action = menu.$el.find('a[data-action=Testdropdown]'); expect($action.length).toEqual(1); @@ -200,7 +193,7 @@ describe('OCA.Files.FileActionsMenu tests', function() { dir: fileList.getCurrentDirectory() }; menu = new OCA.Files.FileActionsMenu(); - menu.showAt(menuContext); + menu.show(menuContext); menu.$el.find('.action-download').click(); @@ -233,7 +226,7 @@ describe('OCA.Files.FileActionsMenu tests', function() { dir: '/anotherpath/there' }; menu = new OCA.Files.FileActionsMenu(); - menu.showAt(menuContext); + menu.show(menuContext); menu.$el.find('.action-download').click(); @@ -266,7 +259,7 @@ describe('OCA.Files.FileActionsMenu tests', function() { dir: '/somepath/dir' }; menu = new OCA.Files.FileActionsMenu(); - menu.showAt(menuContext); + menu.show(menuContext); menu.$el.find('.action-delete').click(); @@ -276,16 +269,5 @@ describe('OCA.Files.FileActionsMenu tests', function() { deleteStub.restore(); }); }); - describe('hiding', function() { - beforeEach(function() { - menu.$el.trigger(new $.Event('afterHide')); - }); - it('removes highlight on current row', function() { - expect($tr.hasClass('mouseOver')).toEqual(false); - }); - it('destroys its DOM element on hide', function() { - expect($tr.find('.fileActionsMenu').length).toEqual(0); - }); - }); }); diff --git a/core/js/js.js b/core/js/js.js index 25baafde08..89bb9a7143 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -611,9 +611,14 @@ var OC={ */ hideMenus: function(complete) { if (OC._currentMenu) { + var lastMenu = OC._currentMenu; OC._currentMenu.trigger(new $.Event('beforeHide')); - OC._currentMenu.slideUp(OC.menuSpeed, complete); - OC._currentMenu.trigger(new $.Event('afterHide')); + OC._currentMenu.slideUp(OC.menuSpeed, function() { + lastMenu.trigger(new $.Event('afterHide')); + if (complete) { + complete.apply(this, arguments); + } + }); } OC._currentMenu = null; OC._currentMenuToggle = null;