diff options
-rw-r--r-- | apps/files/css/files.scss | 28 | ||||
-rw-r--r-- | apps/files/css/mobile.scss | 11 | ||||
-rw-r--r-- | apps/files/js/app.js | 19 | ||||
-rw-r--r-- | apps/files/js/fileactions.js | 12 | ||||
-rw-r--r-- | apps/files/js/filelist.js | 89 | ||||
-rw-r--r-- | apps/files/js/filemultiselectmenu.js | 112 | ||||
-rw-r--r-- | apps/files/js/merged-index.json | 1 | ||||
-rw-r--r-- | apps/files/templates/list.php | 28 | ||||
-rw-r--r-- | apps/files/templates/recentlist.php | 10 | ||||
-rw-r--r-- | apps/files/templates/simplelist.php | 13 | ||||
-rw-r--r-- | apps/files/tests/js/filelistSpec.js | 62 | ||||
-rw-r--r-- | apps/files_trashbin/js/app.js | 14 | ||||
-rw-r--r-- | apps/files_trashbin/js/filelist.js | 20 | ||||
-rw-r--r-- | apps/files_trashbin/templates/index.php | 16 | ||||
-rw-r--r-- | apps/files_trashbin/tests/js/filelistSpec.js | 49 | ||||
-rw-r--r-- | core/css/mobile.scss | 2 |
16 files changed, 338 insertions, 148 deletions
diff --git a/apps/files/css/files.scss b/apps/files/css/files.scss index d2d810c1e6c..38c74d242bc 100644 --- a/apps/files/css/files.scss +++ b/apps/files/css/files.scss @@ -176,9 +176,11 @@ table th .columntitle { -moz-box-sizing: border-box; vertical-align: middle; } +table.multiselect th .columntitle { + display: inline-block; +} table th .columntitle.name { padding-left: 5px; - padding-right: 80px; margin-left: 50px; } @@ -246,7 +248,7 @@ table th.column-last, table td.column-last { } table.multiselect thead { position: fixed; - top: 89px; + top: 94px; z-index: 55; -moz-box-sizing: border-box; box-sizing: border-box; @@ -474,19 +476,19 @@ a.action > img { /* Actions for selected files */ .selectedActions { - position: absolute; - top: 0; - right: 0; + position: relative; + display: inline-block; + vertical-align: middle; +} +.selectedActions.hidden { + display: none; } .selectedActions a { display: inline; - font-size: 11px; line-height: 50px; - padding: 18px 5px; -} -.selectedActions a.delete-selected { - padding-right: 15px; + padding: 16px 5px; } + .selectedActions a.hidden { display: none; } @@ -495,9 +497,9 @@ a.action > img { vertical-align: text-bottom; margin-bottom: -1px; } -/* hide the delete icon in name column normal resolutions */ -table th#headerName .selectedActions .delete-selected { - display: none; + +.selectedActions .actions-selected .icon-more { + margin-top: -3px; } #fileList td a { diff --git a/apps/files/css/mobile.scss b/apps/files/css/mobile.scss index c5bb8193924..a53066c4452 100644 --- a/apps/files/css/mobile.scss +++ b/apps/files/css/mobile.scss @@ -28,7 +28,7 @@ table td { table.multiselect thead { padding-left: 0; } - + #fileList a.action.action-menu img { padding-left: 0; } @@ -41,10 +41,6 @@ table.multiselect thead { display: none !important; } -/* show the delete icon in name column in lower resolutions */ -table th#headerName .selectedActions .delete-selected { - display: inline; -} /* proper notification area for multi line messages */ #notification-container { @@ -71,7 +67,10 @@ table.dragshadow { } @media only screen and (max-width: 480px) { /* Only show icons */ - table th .selectedActions a span:not(.icon) { + table th .selectedActions { + float: right; + } + table th .selectedActions > a span:not(.icon) { display: none; } diff --git a/apps/files/js/app.js b/apps/files/js/app.js index 6e4e8c1b136..6a21bce975b 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -88,6 +88,23 @@ allowLegacyActions: true, scrollTo: urlParams.scrollto, filesClient: OC.Files.getClient(), + multiSelectMenu: [ + { + name: 'copyMove', + displayName: t('files', 'Move or copy'), + iconClass: 'icon-external', + }, + { + name: 'download', + displayName: t('files', 'Download'), + iconClass: 'icon-download', + }, + { + name: 'delete', + displayName: t('files', 'Delete'), + iconClass: 'icon-delete', + } + ], sorting: { mode: $('#defaultFileSorting').val(), direction: $('#defaultFileSortingDirection').val() @@ -130,7 +147,7 @@ window.FileActions.off('registerAction.app-files', this._onActionsUpdated); }, - _onActionsUpdated: function(ev, newAction) { + _onActionsUpdated: function(ev) { // forward new action to the file list if (ev.action) { this.fileList.fileActions.registerAction(ev.action); diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 4c0ccaf6451..3623663ed6c 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -692,21 +692,21 @@ OCA.Files.FileActions = FileActions; /** - * Replaces the download icon with a loading spinner and vice versa + * Replaces the button icon with a loading spinner and vice versa * - also adds the class disabled to the passed in element * - * @param {jQuery} $downloadButtonElement download fileaction + * @param {jQuery} $buttonElement The button element * @param {boolean} showIt whether to show the spinner(true) or to hide it(false) */ - OCA.Files.FileActions.updateFileActionSpinner = function($downloadButtonElement, showIt) { - var $icon = $downloadButtonElement.find('.icon'); + OCA.Files.FileActions.updateFileActionSpinner = function($buttonElement, showIt) { + var $icon = $buttonElement.find('.icon'); if (showIt) { var $loadingIcon = $('<span class="icon icon-loading-small"></span>'); $icon.after($loadingIcon); $icon.addClass('hidden'); } else { - $downloadButtonElement.find('.icon-loading-small').remove(); - $downloadButtonElement.find('.icon').removeClass('hidden'); + $buttonElement.find('.icon-loading-small').remove(); + $buttonElement.find('.icon').removeClass('hidden'); } }; diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 307147076b2..c420e7212a0 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -126,7 +126,11 @@ * @type OCA.Files.FileActions */ fileActions: null, - + /** + * File selection menu, defaults to OCA.Files.FileSelectionMenu + * @type OCA.Files.FileSelectionMenu + */ + fileMultiSelectMenu: null, /** * Whether selection is allowed, checkboxes and selection overlay will * be rendered @@ -288,6 +292,12 @@ this.fileSummary = this._createSummary(); + if (options.multiSelectMenu) { + this.fileMultiSelectMenu = new OCA.Files.FileMultiSelectMenu(options.multiSelectMenu); + this.fileMultiSelectMenu.render(); + this.$el.find('.selectedActions').append(this.fileMultiSelectMenu.$el); + } + if (options.sorting) { this.setSort(options.sorting.mode, options.sorting.direction, false, false); } else { @@ -336,11 +346,10 @@ this.$el.on('show', _.bind(this._onShow, this)); this.$el.on('urlChanged', _.bind(this._onUrlChanged, this)); this.$el.find('.select-all').click(_.bind(this._onClickSelectAll, this)); - this.$el.find('.download').click(_.bind(this._onClickDownloadSelected, this)); - this.$el.find('.copy-move').click(_.bind(this._onClickCopyMoveSelected, this)); - this.$el.find('.delete-selected').click(_.bind(this._onClickDeleteSelected, this)); - - this.$el.find('.selectedActions a').tooltip({placement:'top'}); + this.$el.find('.actions-selected').click(function () { + self.fileMultiSelectMenu.show(self); + return false; + }); this.$container.on('scroll', _.bind(this._onScroll, this)); @@ -365,6 +374,7 @@ } } + OC.Plugins.attach('OCA.Files.FileList', this); }, @@ -388,6 +398,22 @@ $('#app-content').off('appresized', this._onResize); }, + multiSelectMenuClick: function (ev, action) { + switch (action) { + case 'delete': + this._onClickDeleteSelected(ev) + break; + case 'download': + this._onClickDownloadSelected(ev); + break; + case 'copyMove': + this._onClickCopyMoveSelected(ev); + break; + case 'restore': + this._onClickRestoreSelected(ev); + break; + } + }, /** * Initializes the file actions, set up listeners. * @@ -745,7 +771,9 @@ */ _onClickDownloadSelected: function(event) { var files; + var self = this; var dir = this.getCurrentDirectory(); + if (this.isAllSelected() && this.getSelectedFiles().length > 1) { files = OC.basename(dir); dir = OC.dirname(dir) || '/'; @@ -754,19 +782,16 @@ files = _.pluck(this.getSelectedFiles(), 'name'); } - var downloadFileaction = $('#selectedActionsList').find('.download'); - // don't allow a second click on the download action - if(downloadFileaction.hasClass('disabled')) { - event.preventDefault(); - return; + if(this.fileMultiSelectMenu.isDisabled('download')) { + return false; } + this.fileMultiSelectMenu.toggleLoading('download', true); var disableLoadingState = function(){ - OCA.Files.FileActions.updateFileActionSpinner(downloadFileaction, false); + self.fileMultiSelectMenu.toggleLoading('download', false); }; - OCA.Files.FileActions.updateFileActionSpinner(downloadFileaction, true); if(this.getSelectedFiles().length > 1) { OCA.Files.Files.handleDownload(this.getDownloadUrl(files, dir, true), disableLoadingState); } @@ -774,7 +799,7 @@ var first = this.getSelectedFiles()[0]; OCA.Files.Files.handleDownload(this.getDownloadUrl(first.name, dir, true), disableLoadingState); } - return false; + event.preventDefault(); }, /** @@ -786,20 +811,18 @@ files = _.pluck(this.getSelectedFiles(), 'name'); - var moveFileAction = $('#selectedActionsList').find('.move'); - // don't allow a second click on the download action - if(moveFileAction.hasClass('disabled')) { - event.preventDefault(); - return; + if(this.fileMultiSelectMenu.isDisabled('copyMove')) { + return false; } var disableLoadingState = function(){ - OCA.Files.FileActions.updateFileActionSpinner(moveFileAction, false); + self.fileMultiSelectMenu.toggleLoading('copyMove', false); }; var actions = this.isSelectedMovable() ? OC.dialogs.FILEPICKER_TYPE_COPY_MOVE : OC.dialogs.FILEPICKER_TYPE_COPY; OC.dialogs.filepicker(t('files', 'Target folder'), function(targetPath, type) { + self.fileMultiSelectMenu.toggleLoading('copyMove', true); if (type === OC.dialogs.FILEPICKER_TYPE_COPY) { self.copy(files, targetPath, disableLoadingState); } @@ -807,7 +830,7 @@ self.move(files, targetPath, disableLoadingState); } }, false, "httpd/unix-directory", true, actions); - return false; + event.preventDefault(); }, /** @@ -820,7 +843,6 @@ } this.do_delete(files); event.preventDefault(); - return false; }, /** @@ -2885,19 +2907,20 @@ this.$el.find('#headerName a.name>span:first').text(selection); this.$el.find('#modified a>span:first').text(''); this.$el.find('table').addClass('multiselect'); - this.$el.find('.selectedActions .download').toggleClass('hidden', !this.isSelectedDownloadable()); - this.$el.find('.delete-selected').toggleClass('hidden', !this.isSelectedDeletable()); - - var $copyMove = this.$el.find('.selectedActions .copy-move'); - if (this.isSelectedCopiable()) { - $copyMove.toggleClass('hidden', false); - if (this.isSelectedMovable()) { - $copyMove.find('.label').text(t('files', 'Move or copy')); + + if (this.fileMultiSelectMenu) { + this.fileMultiSelectMenu.toggleItemVisibility('download', this.isSelectedDownloadable()); + this.fileMultiSelectMenu.toggleItemVisibility('delete', this.isSelectedDeletable()); + this.fileMultiSelectMenu.toggleItemVisibility('copyMove', this.isSelectedCopiable()); + if (this.isSelectedCopiable()) { + if (this.isSelectedMovable()) { + this.fileMultiSelectMenu.updateItemText('copyMove', t('files', 'Move or copy')); + } else { + this.fileMultiSelectMenu.updateItemText('copyMove', t('files', 'Copy')); + } } else { - $copyMove.find('.label').text(t('files', 'Copy')); + this.fileMultiSelectMenu.toggleItemVisibility('copyMove', false); } - } else { - $copyMove.toggleClass('hidden', true); } } }, diff --git a/apps/files/js/filemultiselectmenu.js b/apps/files/js/filemultiselectmenu.js new file mode 100644 index 00000000000..d587d1fbdb2 --- /dev/null +++ b/apps/files/js/filemultiselectmenu.js @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + var TEMPLATE_MENU = + '<ul>' + + '{{#each items}}' + + '<li class="item-{{name}}">' + + '<a href="#" class="menuitem action {{name}} permanent" data-action="{{name}}">' + + '{{#if iconClass}}' + + '<span class="icon {{iconClass}}"></span>' + + '{{else}}' + + '<span class="no-icon"></span>' + + '{{/if}}' + + '<span class="label">{{displayName}}</span>' + + '</a></li>' + + '{{/each}}' + + '</ul>'; + + var FileMultiSelectMenu = OC.Backbone.View.extend({ + tagName: 'div', + className: 'filesSelectMenu popovermenu bubble menu-center', + _scopes: null, + initialize: function(menuItems) { + this._scopes = menuItems; + }, + events: { + 'click a.action': '_onClickAction' + }, + template: Handlebars.compile(TEMPLATE_MENU), + /** + * Renders the menu with the currently set items + */ + render: function() { + this.$el.html(this.template({ + items: this._scopes + })); + }, + /** + * Displays the menu under the given element + * + * @param {OCA.Files.FileActionContext} context context + * @param {Object} $trigger trigger element + */ + show: function(context) { + this._context = context; + this.$el.removeClass('hidden'); + if (window.innerWidth < 480) { + this.$el.removeClass('menu-center').addClass('menu-right'); + } else { + this.$el.removeClass('menu-right').addClass('menu-center'); + } + OC.showMenu(null, this.$el); + return false; + }, + toggleItemVisibility: function (itemName, show) { + if (show) { + this.$el.find('.item-' + itemName).removeClass('hidden'); + } else { + this.$el.find('.item-' + itemName).addClass('hidden'); + } + }, + updateItemText: function (itemName, translation) { + this.$el.find('.item-' + itemName).find('.label').text(translation); + }, + toggleLoading: function (itemName, showLoading) { + var $actionElement = this.$el.find('.item-' + itemName); + if ($actionElement.length === 0) { + return; + } + var $icon = $actionElement.find('.icon'); + if (showLoading) { + var $loadingIcon = $('<span class="icon icon-loading-small"></span>'); + $icon.after($loadingIcon); + $icon.addClass('hidden'); + $actionElement.addClass('disabled'); + } else { + $actionElement.find('.icon-loading-small').remove(); + $actionElement.find('.icon').removeClass('hidden'); + $actionElement.removeClass('disabled'); + } + }, + isDisabled: function (itemName) { + var $actionElement = this.$el.find('.item-' + itemName); + return $actionElement.hasClass('disabled'); + }, + /** + * Event handler whenever an action has been clicked within the menu + * + * @param {Object} event event object + */ + _onClickAction: function (event) { + var $target = $(event.currentTarget); + if (!$target.hasClass('menuitem')) { + $target = $target.closest('.menuitem'); + } + + OC.hideMenus(); + this._context.multiSelectMenuClick(event, $target.data('action')); + return false; + } + }); + + OCA.Files.FileMultiSelectMenu = FileMultiSelectMenu; +})(OC, OCA); diff --git a/apps/files/js/merged-index.json b/apps/files/js/merged-index.json index 127bbb46b29..cd7e72e1a51 100644 --- a/apps/files/js/merged-index.json +++ b/apps/files/js/merged-index.json @@ -6,6 +6,7 @@ "jquery-visibility.js", "fileinfomodel.js", "filesummary.js", + "filemultiselectmenu.js", "breadcrumb.js", "filelist.js", "search.js", diff --git a/apps/files/templates/list.php b/apps/files/templates/list.php index e6b1e54d389..9cae72a176f 100644 --- a/apps/files/templates/list.php +++ b/apps/files/templates/list.php @@ -49,20 +49,16 @@ </th> <th id='headerName' class="hidden column-name"> <div id="headerName-container"> - <a class="name sort columntitle" data-sort="name"><span><?php p($l->t( 'Name' )); ?></span><span class="sort-indicator"></span></a> - <span id="selectedActionsList" class="selectedActions"> - <a href="" class="copy-move"> - <span class="icon icon-external"></span> - <span class="label"><?php p($l->t('Move or copy'))?></span> - </a> - <a href="" class="download"> - <span class="icon icon-download"></span> - <span><?php p($l->t('Download'))?></span> - </a> - <a href="" class="delete-selected"> - <span class="icon icon-delete"></span> - <span><?php p($l->t('Delete'))?></span> - </a> + <a class="name sort columntitle" data-sort="name"> + <span><?php p($l->t( 'Name' )); ?></span> + <span class="sort-indicator"></span> + + </a> + <span id="selectedActionsList" class="selectedActions"> + <a href="" class="actions-selected"> + <span class="icon icon-more"></span> + <span><?php p($l->t('Actions'))?></span> + </a> </span> </div> </th> @@ -71,10 +67,6 @@ </th> <th id="headerDate" class="hidden column-mtime"> <a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Modified' )); ?></span><span class="sort-indicator"></span></a> - <span class="selectedActions"><a href="" class="delete-selected"> - <span><?php p($l->t('Delete'))?></span> - <span class="icon icon-delete"></span> - </a></span> </th> </tr> </thead> diff --git a/apps/files/templates/recentlist.php b/apps/files/templates/recentlist.php index 6c271a07f5f..cfdb95c80a0 100644 --- a/apps/files/templates/recentlist.php +++ b/apps/files/templates/recentlist.php @@ -28,10 +28,12 @@ <a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t('Modified')); ?></span><span class="sort-indicator"></span></a> - <span class="selectedActions"><a href="" class="delete-selected"> - <span><?php p($l->t('Delete')) ?></span> - <span class="icon icon-delete"></span> - </a></span> + <span class="selectedActions"> + <a href="" class="delete-selected"> + <span class="icon icon-delete"></span> + <span><?php p($l->t('Delete')) ?></span> + </a> + </span> </th> </tr> </thead> diff --git a/apps/files/templates/simplelist.php b/apps/files/templates/simplelist.php index 1dc927c9b59..78adb21922f 100644 --- a/apps/files/templates/simplelist.php +++ b/apps/files/templates/simplelist.php @@ -13,7 +13,6 @@ <h2><?php p($l->t('No entries found in this folder')); ?></h2> <p></p> </div> - <table id="filestable"> <thead> <tr> @@ -27,11 +26,13 @@ </th> <th id="headerDate" class="hidden column-mtime"> <a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Modified' )); ?></span><span class="sort-indicator"></span></a> - <span class="selectedActions"><a href="" class="delete-selected"> - <?php p($l->t('Delete'))?> - <img class="svg" alt="" - src="<?php print_unescaped(OCP\Template::image_path("core", "actions/delete.svg")); ?>" /> - </a></span> + <span class="selectedActions"> + <a href="" class="delete-selected"> + <img class="svg" alt="" + src="<?php print_unescaped(OCP\Template::image_path("core", "actions/delete.svg")); ?>" /> + <?php p($l->t('Delete'))?> + </a> + </span> </th> </tr> </thead> diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index 1b26a468172..efaf9968def 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -94,9 +94,7 @@ describe('OCA.Files.FileList tests', function() { '<input type="checkbox" id="select_all_files" class="select-all checkbox">' + '<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' + '<span id="selectedActionsList" class="selectedActions hidden">' + - '<a href class="copy-move"><span class="label">Move or copy</span></a>' + - '<a href class="download"><img src="actions/download.svg">Download</a>' + - '<a href class="delete-selected">Delete</a></span>' + + '<a href class="actions-selected"><span class="icon icon-more"></span><span>Actions</span></a>' + '</th>' + '<th class="hidden column-size"><a class="columntitle" data-sort="size"><span class="sort-indicator"></span></a></th>' + '<th class="hidden column-mtime"><a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a></th>' + @@ -161,7 +159,22 @@ describe('OCA.Files.FileList tests', function() { fileList = new OCA.Files.FileList($('#app-content-files'), { filesClient: filesClient, config: filesConfig, - enableUpload: true + enableUpload: true, + multiSelectMenu: [{ + name: 'copyMove', + displayName: t('files', 'Move or copy'), + iconClass: 'icon-external', + }, + { + name: 'download', + displayName: t('files', 'Download'), + iconClass: 'icon-download', + }, + { + name: 'delete', + displayName: t('files', 'Delete'), + iconClass: 'icon-delete', + }] }); }); afterEach(function() { @@ -2100,41 +2113,41 @@ describe('OCA.Files.FileList tests', function() { fileList.setFiles(testFiles); $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_UPDATE); $('.select-all').click(); - expect(fileList.$el.find('.selectedActions .copy-move').hasClass('hidden')).toEqual(false); - expect(fileList.$el.find('.selectedActions .copy-move .label').text()).toEqual('Move or copy'); + expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(false); + expect(fileList.$el.find('.selectedActions .item-copyMove .label').text()).toEqual('Move or copy'); testFiles[0].permissions = OC.PERMISSION_READ; $('.select-all').click(); fileList.setFiles(testFiles); $('.select-all').click(); - expect(fileList.$el.find('.selectedActions .copy-move').hasClass('hidden')).toEqual(false); - expect(fileList.$el.find('.selectedActions .copy-move .label').text()).toEqual('Copy'); + expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(false); + expect(fileList.$el.find('.selectedActions .item-copyMove .label').text()).toEqual('Copy'); testFiles[0].permissions = OC.PERMISSION_NONE; $('.select-all').click(); fileList.setFiles(testFiles); $('.select-all').click(); - expect(fileList.$el.find('.selectedActions .copy-move').hasClass('hidden')).toEqual(true); + expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(true); }); it('show doesnt show the download action if one or more files are not downloadable', function () { fileList.setFiles(testFiles); $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_UPDATE); $('.select-all').click(); - expect(fileList.$el.find('.selectedActions .download').hasClass('hidden')).toEqual(false); + expect(fileList.$el.find('.selectedActions .item-download').hasClass('hidden')).toEqual(false); testFiles[0].permissions = OC.PERMISSION_UPDATE; $('.select-all').click(); fileList.setFiles(testFiles); $('.select-all').click(); - expect(fileList.$el.find('.selectedActions .download').hasClass('hidden')).toEqual(true); + expect(fileList.$el.find('.selectedActions .item-download').hasClass('hidden')).toEqual(true); }); it('show doesnt show the delete action if one or more files are not deletable', function () { fileList.setFiles(testFiles); $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_DELETE); $('.select-all').click(); - expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(false); + expect(fileList.$el.find('.selectedActions .item-delete').hasClass('hidden')).toEqual(false); testFiles[0].permissions = OC.PERMISSION_READ; $('.select-all').click(); fileList.setFiles(testFiles); $('.select-all').click(); - expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(true); + expect(fileList.$el.find('.selectedActions .item-delete').hasClass('hidden')).toEqual(true); }); }); describe('Actions', function() { @@ -2219,8 +2232,12 @@ describe('OCA.Files.FileList tests', function() { }); }); describe('Download', function() { + beforeEach(function() { + fileList.$el.find('.actions-selected').click(); + }); + it('Opens download URL when clicking "Download"', function() { - $('.selectedActions .download').click(); + $('.selectedActions .filesSelectMenu .download').click(); expect(redirectStub.calledOnce).toEqual(true); expect(redirectStub.getCall(0).args[0]).toContain(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22One.txt%22%2C%22Three.pdf%22%2C%22somedir%22%5D'); redirectStub.restore(); @@ -2228,28 +2245,37 @@ describe('OCA.Files.FileList tests', function() { it('Downloads root folder when all selected in root folder', function() { $('#dir').val('/'); $('.select-all').click(); - $('.selectedActions .download').click(); + $('.selectedActions .filesSelectMenu .download').click(); expect(redirectStub.calledOnce).toEqual(true); expect(redirectStub.getCall(0).args[0]).toContain(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files='); }); it('Downloads parent folder when all selected in subfolder', function() { $('.select-all').click(); - $('.selectedActions .download').click(); + $('.selectedActions .filesSelectMenu .download').click(); expect(redirectStub.calledOnce).toEqual(true); expect(redirectStub.getCall(0).args[0]).toContain(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=subdir'); }); + + afterEach(function() { + fileList.$el.find('.actions-selected').click(); + }); }); + describe('Delete', function() { var deleteStub, deferredDelete; beforeEach(function() { deferredDelete = $.Deferred(); deleteStub = sinon.stub(filesClient, 'remove').returns(deferredDelete.promise()); + fileList.$el.find('.actions-selected').click(); }); + afterEach(function() { + fileList.$el.find('.actions-selected').click(); deleteStub.restore(); }); + it('Deletes selected files when "Delete" clicked', function() { - $('.selectedActions .delete-selected').click(); + $('.selectedActions .filesSelectMenu .delete').click(); expect(deleteStub.callCount).toEqual(3); expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt'); @@ -2265,7 +2291,7 @@ describe('OCA.Files.FileList tests', function() { }); it('Deletes all files when all selected when "Delete" clicked', function() { $('.select-all').click(); - $('.selectedActions .delete-selected').click(); + $('.selectedActions .filesSelectMenu .delete').click(); expect(deleteStub.callCount).toEqual(4); expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt'); diff --git a/apps/files_trashbin/js/app.js b/apps/files_trashbin/js/app.js index fd3d5db32ff..002c01bf967 100644 --- a/apps/files_trashbin/js/app.js +++ b/apps/files_trashbin/js/app.js @@ -30,7 +30,19 @@ OCA.Trashbin.App = { fileActions: this._createFileActions(), detailsViewEnabled: false, scrollTo: urlParams.scrollto, - config: OCA.Files.App.getFilesConfig() + config: OCA.Files.App.getFilesConfig(), + multiSelectMenu: [ + { + name: 'restore', + displayName: t('files', 'Restore'), + iconClass: 'icon-history', + }, + { + name: 'delete', + displayName: t('files', 'Delete'), + iconClass: 'icon-delete', + } + ] } ); }, diff --git a/apps/files_trashbin/js/filelist.js b/apps/files_trashbin/js/filelist.js index 4846c2361fe..324e4d8a7e0 100644 --- a/apps/files_trashbin/js/filelist.js +++ b/apps/files_trashbin/js/filelist.js @@ -153,7 +153,6 @@ } this.fileSummary.update(); this.updateEmptyContent(); - this.enableActions(); }, _onClickRestoreSelected: function(event) { @@ -162,7 +161,7 @@ var allFiles = this.$el.find('.select-all').is(':checked'); var files = []; var params = {}; - this.disableActions(); + this.fileMultiSelectMenu.toggleLoading('restore', true); if (allFiles) { this.showMask(); params = { @@ -192,13 +191,14 @@ self.hideMask(); // simply remove all files self.setFiles([]); - self.enableActions(); } else { self._removeCallback(result); } + self.fileMultiSelectMenu.toggleLoading('restore', false); } ); + event.preventDefault(); }, _onClickDeleteSelected: function(event) { @@ -221,7 +221,7 @@ }; } - this.disableActions(); + this.fileMultiSelectMenu.toggleLoading('delete', true); if (allFiles) { this.showMask(); } @@ -242,11 +242,11 @@ self.hideMask(); // simply remove all files self.setFiles([]); - self.enableActions(); } else { self._removeCallback(result); } + self.fileMultiSelectMenu.toggleLoading('delete', false); } ); }, @@ -268,16 +268,6 @@ return '#'; }, - enableActions: function() { - this.$el.find('.action').css('display', 'inline'); - this.$el.find('input:checkbox').removeClass('u-hidden'); - }, - - disableActions: function() { - this.$el.find('.action').css('display', 'none'); - this.$el.find('input:checkbox').addClass('u-hidden'); - }, - updateStorageStatistics: function() { // no op because the trashbin doesn't have // storage info like free space / used space diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php index a4459947d09..dd24abb5de1 100644 --- a/apps/files_trashbin/templates/index.php +++ b/apps/files_trashbin/templates/index.php @@ -31,25 +31,15 @@ <div id="headerName-container"> <a class="name sort columntitle" data-sort="name"><span><?php p($l->t( 'Name' )); ?></span><span class="sort-indicator"></span></a> <span id="selectedActionsList" class='selectedActions'> - <a href="" class="undelete"> - <span class="icon icon-history"></span> - <span><?php p($l->t('Restore'))?></span> - </a> - <a href="" class="delete-selected"> - <span class="icon icon-delete"></span> - <span><?php p($l->t('Delete'))?></span> + <a href="" class="actions-selected"> + <span class="icon icon-more"></span> + <span><?php p($l->t('Actions'))?></span> </a> </span> </div> </th> <th id="headerDate" class="hidden column-mtime"> <a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Deleted' )); ?></span><span class="sort-indicator"></span></a> - <span class="selectedActions"> - <a href="" class="delete-selected"> - <span><?php p($l->t('Delete'))?></span> - <span class="icon icon-delete"></span> - </a> - </span> </th> </tr> </thead> diff --git a/apps/files_trashbin/tests/js/filelistSpec.js b/apps/files_trashbin/tests/js/filelistSpec.js index 04ff243d07b..c5b1018856b 100644 --- a/apps/files_trashbin/tests/js/filelistSpec.js +++ b/apps/files_trashbin/tests/js/filelistSpec.js @@ -46,8 +46,8 @@ describe('OCA.Trashbin.FileList tests', function() { '<input type="checkbox" id="select_all_trash" class="select-all">' + '<span class="name">Name</span>' + '<span class="selectedActions hidden">' + - '<a href class="undelete">Restore</a>' + - '<a href class="delete-selected">Delete</a></span>' + + '<a href="" class="actions-selected"><span class="icon icon-more"></span><span>Actions</span>' + + '</span>' + '</th></tr></thead>' + '<tbody id="fileList"></tbody>' + '<tfoot></tfoot>' + @@ -90,7 +90,18 @@ describe('OCA.Trashbin.FileList tests', function() { var fileActions = OCA.Trashbin.App._createFileActions(fileList); fileList = new OCA.Trashbin.FileList( $('#app-content-trashbin'), { - fileActions: fileActions + fileActions: fileActions, + multiSelectMenu: [{ + name: 'restore', + displayName: t('files', 'Restore'), + iconClass: 'icon-history', + }, + { + name: 'delete', + displayName: t('files', 'Delete'), + iconClass: 'icon-delete', + } + ] } ); }); @@ -260,33 +271,41 @@ describe('OCA.Trashbin.FileList tests', function() { fileList.findFileEl('One.txt.d11111').find('input:checkbox').click(); fileList.findFileEl('Three.pdf.d33333').find('input:checkbox').click(); fileList.findFileEl('somedir.d99999').find('input:checkbox').click(); + fileList.$el.find('.actions-selected').click(); + }); + + afterEach(function() { + fileList.$el.find('.actions-selected').click(); }); + describe('Delete', function() { it('Shows trashbin actions', function() { // visible because a few files were selected expect($('.selectedActions').is(':visible')).toEqual(true); - expect($('.selectedActions .delete-selected').is(':visible')).toEqual(true); - expect($('.selectedActions .undelete').is(':visible')).toEqual(true); + expect($('.selectedActions .item-delete').is(':visible')).toEqual(true); + expect($('.selectedActions .item-restore').is(':visible')).toEqual(true); // check fileList.$el.find('.select-all').click(); // stays visible expect($('.selectedActions').is(':visible')).toEqual(true); - expect($('.selectedActions .delete-selected').is(':visible')).toEqual(true); - expect($('.selectedActions .undelete').is(':visible')).toEqual(true); + expect($('.selectedActions .item-delete').is(':visible')).toEqual(true); + expect($('.selectedActions .item-restore').is(':visible')).toEqual(true); // uncheck fileList.$el.find('.select-all').click(); // becomes hidden now expect($('.selectedActions').is(':visible')).toEqual(false); - expect($('.selectedActions .delete-selected').is(':visible')).toEqual(false); - expect($('.selectedActions .undelete').is(':visible')).toEqual(false); + expect($('.selectedActions .item-delete').is(':visible')).toEqual(false); + expect($('.selectedActions .item-restore').is(':visible')).toEqual(false); }); it('Deletes selected files when "Delete" clicked', function() { var request; - $('.selectedActions .delete-selected').click(); + var $deleteLink = $('.selectedActions .filesSelectMenu .delete'); + $deleteLink.click(); + expect($deleteLink.find('.icon-loading-small').length).toEqual(1); expect(fakeServer.requests.length).toEqual(1); request = fakeServer.requests[0]; expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/delete.php'); @@ -306,6 +325,7 @@ describe('OCA.Trashbin.FileList tests', function() { } }) ); + expect($deleteLink.find('.icon-loading-small').length).toEqual(0); expect(fileList.findFileEl('One.txt.d11111').length).toEqual(0); expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0); expect(fileList.findFileEl('somedir.d99999').length).toEqual(0); @@ -314,7 +334,7 @@ describe('OCA.Trashbin.FileList tests', function() { it('Deletes all files when all selected when "Delete" clicked', function() { var request; $('.select-all').click(); - $('.selectedActions .delete-selected').click(); + $('.selectedActions .filesSelectMenu .delete').click(); expect(fakeServer.requests.length).toEqual(1); request = fakeServer.requests[0]; expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/delete.php'); @@ -331,7 +351,9 @@ describe('OCA.Trashbin.FileList tests', function() { describe('Restore', function() { it('Restores selected files when "Restore" clicked', function() { var request; - $('.selectedActions .undelete').click(); + var $restoreLink = $('.selectedActions .filesSelectMenu .restore'); + $restoreLink.click(); + expect($restoreLink.find('.icon-loading-small').length).toEqual(1); expect(fakeServer.requests.length).toEqual(1); request = fakeServer.requests[0]; expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/undelete.php'); @@ -351,6 +373,7 @@ describe('OCA.Trashbin.FileList tests', function() { } }) ); + expect($restoreLink.find('.icon-loading-small').length).toEqual(0); expect(fileList.findFileEl('One.txt.d11111').length).toEqual(0); expect(fileList.findFileEl('Three.pdf.d33333').length).toEqual(0); expect(fileList.findFileEl('somedir.d99999').length).toEqual(0); @@ -359,7 +382,7 @@ describe('OCA.Trashbin.FileList tests', function() { it('Restores all files when all selected when "Restore" clicked', function() { var request; $('.select-all').click(); - $('.selectedActions .undelete').click(); + $('.selectedActions .filesSelectMenu .restore').click(); expect(fakeServer.requests.length).toEqual(1); request = fakeServer.requests[0]; expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_trashbin/ajax/undelete.php'); diff --git a/core/css/mobile.scss b/core/css/mobile.scss index cfc8c002e17..239876223d5 100644 --- a/core/css/mobile.scss +++ b/core/css/mobile.scss @@ -67,7 +67,7 @@ #app-navigation-toggle { position: fixed; display: inline-block !important; - top: 45px; + top: 50px; left: 0; width: 44px; height: 44px; |