From ca34921cdf8db4075906b3531390aa1b1ae9216c Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 16 Jul 2015 15:28:45 +0200 Subject: Implement file actions dropdown File actions now have two types "inline" and "dropdown". The default is "dropdown". The file actions will now be shown in a dropdown menu. --- core/js/js.js | 56 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 12 deletions(-) (limited to 'core/js') diff --git a/core/js/js.js b/core/js/js.js index 72d4edd28dd..25baafde08f 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -571,21 +571,20 @@ var OC={ * @todo Write documentation */ registerMenu: function($toggle, $menuEl) { + var self = this; $menuEl.addClass('menu'); $toggle.on('click.menu', function(event) { // prevent the link event (append anchor to URL) event.preventDefault(); if ($menuEl.is(OC._currentMenu)) { - $menuEl.slideUp(OC.menuSpeed); - OC._currentMenu = null; - OC._currentMenuToggle = null; + self.hideMenus(); return; } // another menu was open? else if (OC._currentMenu) { // close it - OC._currentMenu.hide(); + self.hideMenus(); } $menuEl.slideToggle(OC.menuSpeed); OC._currentMenu = $menuEl; @@ -599,14 +598,50 @@ var OC={ unregisterMenu: function($toggle, $menuEl) { // close menu if opened if ($menuEl.is(OC._currentMenu)) { - $menuEl.slideUp(OC.menuSpeed); - OC._currentMenu = null; - OC._currentMenuToggle = null; + this.hideMenus(); } $toggle.off('click.menu').removeClass('menutoggle'); $menuEl.removeClass('menu'); }, + /** + * Hides any open menus + * + * @param {Function} complete callback when the hiding animation is done + */ + hideMenus: function(complete) { + if (OC._currentMenu) { + OC._currentMenu.trigger(new $.Event('beforeHide')); + OC._currentMenu.slideUp(OC.menuSpeed, complete); + OC._currentMenu.trigger(new $.Event('afterHide')); + } + OC._currentMenu = null; + OC._currentMenuToggle = null; + }, + + /** + * Shows a given element as menu + * + * @param {Object} [$toggle=null] menu toggle + * @param {Object} $menuEl menu element + * @param {Function} complete callback when the showing animation is done + */ + showMenu: function($toggle, $menuEl, complete) { + if ($menuEl.is(OC._currentMenu)) { + return; + } + this.hideMenus(); + OC._currentMenu = $menuEl; + OC._currentMenuToggle = $toggle; + $menuEl.trigger(new $.Event('beforeShow')); + $menuEl.show(); + $menuEl.trigger(new $.Event('afterShow')); + // no animation + if (_.isFunction()) { + complete(); + } + }, + /** * Wrapper for matchMedia * @@ -1256,11 +1291,8 @@ function initCore() { // don't close when clicking on the menu directly or a menu toggle return false; } - if (OC._currentMenu) { - OC._currentMenu.slideUp(OC.menuSpeed); - } - OC._currentMenu = null; - OC._currentMenuToggle = null; + + OC.hideMenus(); }); -- cgit v1.2.3 From 86e1eaf370ff5c290805bac130f3c3bbd1d1c774 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 10 Aug 2015 15:57:21 +0200 Subject: Inline actions using default renderer are now always permanent Default renderer like the favorite icon can decide whether to use the permanent class or not. Fixed sharing code to properly update the icon according to sharing state modifications. --- apps/files/js/fileactions.js | 1 + apps/files_sharing/tests/js/shareSpec.js | 6 +++--- core/js/share.js | 4 +--- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'core/js') diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 0d9161c6eb4..5ec26d8b334 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -308,6 +308,7 @@ var $actionLink = this._makeActionLink(params, context); context.$file.find('a.name>span.fileactions').append($actionLink); + $actionLink.addClass('permanent'); return $actionLink; } }, diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js index aa409285ca4..581e15caf93 100644 --- a/apps/files_sharing/tests/js/shareSpec.js +++ b/apps/files_sharing/tests/js/shareSpec.js @@ -97,7 +97,7 @@ describe('OCA.Sharing.Util tests', function() { }]); $tr = fileList.$el.find('tbody tr:first'); $action = $tr.find('.action-share'); - expect($action.hasClass('permanent')).toEqual(false); + expect($action.hasClass('permanent')).toEqual(true); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder.svg'); expect($action.find('img').length).toEqual(1); @@ -257,7 +257,7 @@ describe('OCA.Sharing.Util tests', function() { $action = fileList.$el.find('tbody tr:first .action-share'); $tr = fileList.$el.find('tr:first'); - expect($action.hasClass('permanent')).toEqual(false); + expect($action.hasClass('permanent')).toEqual(true); $tr.find('.action-share').click(); @@ -344,7 +344,7 @@ describe('OCA.Sharing.Util tests', function() { expect($tr.attr('data-share-recipients')).not.toBeDefined(); OC.Share.updateIcon('file', 1); - expect($action.hasClass('permanent')).toEqual(false); + expect($action.hasClass('permanent')).toEqual(true); }); it('keep share text after updating reshare', function() { var $action, $tr; diff --git a/core/js/share.js b/core/js/share.js index 99fd08c6411..57dd0dd6553 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -266,7 +266,6 @@ OC.Share={ if (hasShares || owner) { recipients = $tr.attr('data-share-recipients'); - action.addClass('permanent'); message = t('core', 'Shared'); // even if reshared, only show "Shared by" if (owner) { @@ -281,8 +280,7 @@ OC.Share={ } } else { - action.removeClass('permanent'); - action.html(' '+ escapeHTML(t('core', 'Share'))+'').prepend(img); + action.html('').prepend(img); } if (hasLink) { image = OC.imagePath('core', 'actions/public'); -- cgit v1.2.3 From a5aa03a1a6c0f659c0528253d28c63f759d1ed50 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 11 Aug 2015 11:35:21 +0200 Subject: Load backbone when running unit tests --- core/js/core.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'core/js') diff --git a/core/js/core.json b/core/js/core.json index 1053debaa99..a67491c4a35 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -7,7 +7,8 @@ "moment/min/moment-with-locales.js", "handlebars/handlebars.js", "blueimp-md5/js/md5.js", - "bootstrap/js/tooltip.js" + "bootstrap/js/tooltip.js", + "backbone/backbone.js" ], "libraries": [ "jquery-showpassword.js", @@ -19,6 +20,7 @@ "jquery.ocdialog.js", "oc-dialogs.js", "js.js", + "oc-backbone.js", "l10n.js", "apps.js", "share.js", -- cgit v1.2.3 From 984ae8140d986e93a2fcea5951436e95c8e2c603 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 11 Aug 2015 11:35:46 +0200 Subject: Fixed file actions menu to close when reclicking trigger FileActionsMenu is now a backbone view. The trigger and highlight handling is now done in the FileActions.showMenu() method using events. --- apps/files/js/fileactions.js | 32 +++++++++++++------- apps/files/js/fileactionsmenu.js | 47 +++++++++--------------------- apps/files/tests/js/fileactionsSpec.js | 24 ++++++++++++--- apps/files/tests/js/fileactionsmenuSpec.js | 28 ++++-------------- core/js/js.js | 9 ++++-- 5 files changed, 67 insertions(+), 73 deletions(-) (limited to 'core/js') diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 5ec26d8b334..43f74c5816d 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 0cc08a1ec90..623ebde5442 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 8c43b917fa9..236cff6cafd 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 43439794975..0cfd12a2d04 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 25baafde08f..89bb9a71430 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; -- cgit v1.2.3