diff --git a/apps/files/css/files.scss b/apps/files/css/files.scss index 15255fdc47f5e..3541ba22f40bf 100644 --- a/apps/files/css/files.scss +++ b/apps/files/css/files.scss @@ -243,6 +243,7 @@ table th .columntitle.name { table.multiselect th .columntitle.name { margin-left: 0; + padding-left: 15px; } table th .sort-indicator { @@ -311,9 +312,9 @@ table th.column-last, table td.column-last { top: 94px; } -#app-content-recent, -#app-content-favorites, -#app-content-shareoverview, +#app-content-recent, +#app-content-favorites, +#app-content-shareoverview, #app-content-sharingout, #app-content-sharingin, #app-content-sharinglinks, @@ -333,7 +334,7 @@ table.multiselect thead th { table.multiselect #headerName { position: relative; - width: 9999px; /* when we use 100%, the styling breaks on mobile … table styling */ + width: auto; } table.multiselect #modified { display: none; @@ -721,7 +722,7 @@ a.action > img { .summary { color: var(--color-text-maxcontrast); /* add whitespace to bottom of files list to correctly show dropdowns */ - $action-menu-items-count: 7; // list view has currently max 7 items in its action menu + $action-menu-items-count: 7; // list view has currently max 7 items in its action menu height: 44px * ($action-menu-items-count + 0.5); // 0.5 is added to show some whitespace below } #filestable .filesummary { @@ -859,7 +860,7 @@ table.dragshadow td.size { overflow: auto; min-width: 160px; height: 54px; - + &:not(.hidden) { display: flex; } @@ -911,11 +912,9 @@ table.dragshadow td.size { /* HEADER and MULTISELECT */ thead { tr { - display: block; border-bottom: 1px solid var(--color-border); background-color: var(--color-main-background-translucent); th { - width: auto; border: none; } } @@ -1238,3 +1237,115 @@ table.dragshadow td.size { } } +/* Actions for selected files */ +#headerSize .hidden { + display: none; +} + +#headerDate .hidden { + display: none; +} + +#headerSizeOpen .hidden { + display: none; +} + +table.multiselect { + thead { + #selectedMenu { + width: 100% !important; + } + } +} + +.multiselect { + .sort-indicator { + display: none !important; + } +} + +#selectedActionLabel { + padding-right: 16px; + display: none; + a { + display: inline; + line-height: 50px; + padding: 16px 5px; + img { + position: relative; + vertical-align: text-bottom; + margin-bottom: -1px; + } + } + a.hidden { + display: none; + } + .actions-selected { + .icon-more { + display: inline-block; + vertical-align: middle; + } + } +} + +#headerSizeCount .hidden { + display: none !important; +} + +.selectedActions { + .filesSelectionMenu { + display: block !important; + ul { + li { + display: inline; + &.hidden { + display: none !important; + } + } + } + a { + .label { + padding-left: 8px; + padding-right: 19px; + } + &:hover { + .label { + text-decoration: underline; + } + } + } + .item-delete { + a { + &:hover { + color: #ff0000; + filter: invert(18%) sepia(99%) saturate(7440%) hue-rotate(1deg) brightness(110%) contrast(115%); + } + } + } + } +} + +#selectedActionsList { + width: 100% !important; + text-align: right !important; + opacity: unset; + .popovermenu { + right: -70px; + top: 45px; + } +} + +#allLabel { + padding-top: 5px; +} + +.item-cancel { + a { + .label { + color: #ff0000; + } + } + .icon-close { + filter: invert(18%) sepia(99%) saturate(7440%) hue-rotate(1deg) brightness(110%) contrast(115%); + } +} diff --git a/apps/files/css/mobile.scss b/apps/files/css/mobile.scss index da6fdd25f285b..03aff83d5342a 100644 --- a/apps/files/css/mobile.scss +++ b/apps/files/css/mobile.scss @@ -85,4 +85,33 @@ table.dragshadow { table.multiselect th .columntitle.name { margin-left: 0; } + + /* Actions for selected files */ + table th #selectedActionLabel a { + padding: 17px 14px; + } + + #allLabel { + display: none; + } + + #selectedActionsList .filesSelectionMenu { + display: none !important; + } + + #selectedActionsList .popovermenu { + right: -70px; + top: 25px; + } + + #selectedActionLabel { + padding-right: 0px; + } +} + +@media only screen and (max-width: 440px) { + #selectedActionsList .popovermenu { + right: -85px; + top: 20px; + } } diff --git a/apps/files/js/app.js b/apps/files/js/app.js index 5efa20887e605..eb50c38c9a2c4 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -119,6 +119,12 @@ iconClass: 'icon-tag', order: 100, }, + { + name: 'cancel', + displayName: 'Cancel', + iconClass: 'icon-close', + order: 101, + }, ], sorting: { mode: $('#defaultFileSorting').val(), diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 4f2f3f104d2ed..ae0c162b3b0e0 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -379,6 +379,9 @@ this.$el.on('show', this._onResize); + this.resizeFileActionMenu = _.debounce(_.bind(this.resizeFileActionMenu, this), 250); + $(window).resize(this.resizeFileActionMenu); + // reload files list on share accept $('body').on('OCA.Notification.Action', function(eventObject) { if (eventObject.notification.app === 'files_sharing' && eventObject.action.type === 'POST') { @@ -558,6 +561,9 @@ case 'tags': this._onClickTagSelected(ev); break; + case 'cancel': + this._onClickCancelSelected(ev); + break; } }, /** @@ -1191,6 +1197,17 @@ }); }, + /** + * Event handler for when deselecting all selected files + */ + _onClickCancelSelected: function(ev) { + this._selectedFiles = {}; + this._selectionSummary.clear(); + $('#filestable input').prop('checked', false); + this.$fileList.find('td.selection > .selectCheckBox:visible').closest('tr').toggleClass('selected', false); + this.updateSelectionSummary(); + }, + _onClickDocument: function(ev) { if(!$(ev.target).closest('#editor_container').length) { this._inputView.setValues([]); @@ -1511,6 +1528,12 @@ this.fileMultiSelectMenu.render(); this.$el.find('.selectedActions .filesSelectMenu').remove(); this.$el.find('.selectedActions').append(this.fileMultiSelectMenu.$el); + this.fileMultipleSelectionMenu = new OCA.Files.FileMultipleSelectionMenu(this.multiSelectMenuItems.sort(function(a, b) { + return a.order - b.order + })); + this.fileMultipleSelectionMenu.render(); + this.$el.find('.selectedActions .filesSelectionMenu').remove(); + this.$el.find('.selectedActions').append(this.fileMultipleSelectionMenu.$el); }, /** @@ -3431,10 +3454,22 @@ this.$el.find('#modified a>span:first').text(t('files','Modified')); this.$el.find('table').removeClass('multiselect'); this.$el.find('.selectedActions').addClass('hidden'); + this.$el.find('#headerSize').removeClass('hidden'); + this.$el.find('#headerDate').removeClass('hidden'); + this.$el.find('#headerSizeCount').addClass('hidden'); + this.$el.find('.headerSizeOpen').addClass('hidden'); + $('#selectedActionLabel').css('display','none'); } else { this.$el.find('.selectedActions').removeClass('hidden'); - this.$el.find('#headerSize a>span:first').text(OC.Util.humanFileSize(summary.totalSize)); + this.$el.find('#headerSize').addClass('hidden'); + this.$el.find('#headerDate').addClass('hidden'); + this.$el.find('#headerSizeCount').removeClass('hidden'); + this.$el.find('.headerSizeOpen').removeClass('hidden'); + this.$el.find('#selectedActionsList').removeClass('menu-center'); + this.$el.find('#headerSizeCount').text(OC.Util.humanFileSize(summary.totalSize)); + this.fileMultipleSelectionMenu.show(this); + this.resizeFileActionMenu(); var directoryInfo = n('files', '%n folder', '%n folders', summary.totalDirs); var fileInfo = n('files', '%n file', '%n files', summary.totalFiles); @@ -3477,6 +3512,63 @@ } }, + /** + * Show or hide file action menu based on the current selection + */ + resizeFileActionMenu: function() { + const appList = $('.filesSelectionMenu ul li'); + const headerWidth = $('#filestable thead').outerWidth(); + const checkWidth = $('#headerSelection').outerWidth(); + const headerNameWidth = $('#headerName').outerWidth(); + const actionWidth = $('#selectedActionLabel').outerWidth(); + const allLabelWidth = $('#allLabel').not('#allLabel:hidden').outerWidth(); + var availableWidth; + if(!allLabelWidth){ + availableWidth = headerWidth - (checkWidth + headerNameWidth); + } + else{ + availableWidth = headerWidth - (checkWidth + allLabelWidth+ headerNameWidth); + } + + let appCount = Math.floor((availableWidth / $(appList).width())); + + if(appCount < appList.length) { + if(!allLabelWidth){ + availableWidth = headerWidth - (checkWidth + headerNameWidth + actionWidth); + } + else{ + availableWidth = headerWidth - (checkWidth + allLabelWidth+ headerNameWidth + actionWidth); + } + appCount = Math.floor((availableWidth / $(appList).width())); + } + + var summary = this._selectionSummary.summary; + if (summary.totalFiles === 0 && summary.totalDirs === 0) { + $('#selectedActionLabel').css('display','none'); + } + else{ + if(appCount < appList.length) { + $('#selectedActionLabel').css('display','block'); + } + else if(appCount == appList.length){ + $('#selectedActionLabel').css('display','none'); + } + else if (!isFinite(appCount)) + { + $('#selectedActionLabel').css('display','block'); + } + } + + for (let k = 0; k < appList.length; k++) { + if (k < appCount) { + $(appList[k]).removeClass('hidden'); + } else { + $(appList[k]).addClass('hidden'); + } + } + }, + + /** * Check whether all selected files are copiable */ diff --git a/apps/files/js/filemultipleselectionmenu.js b/apps/files/js/filemultipleselectionmenu.js new file mode 100644 index 0000000000000..f696bf8e9af8c --- /dev/null +++ b/apps/files/js/filemultipleselectionmenu.js @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + var FileMultipleSelectionMenu = OC.Backbone.View.extend({ + tagName: 'div', + className: 'filesSelectionMenu', + _scopes: null, + initialize: function(menuItems) { + this._scopes = menuItems; + }, + events: { + 'click a.action': '_onClickAction' + }, + + /** + * Renders the menu with the currently set items + */ + render: function() { + this.$el.html(OCA.Files.Templates['filemultiselectmenu']({ + 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; + 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 = $(''); + $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.FileMultipleSelectionMenu = FileMultipleSelectionMenu; +})(OC, OCA); diff --git a/apps/files/js/filemultiselectmenu.js b/apps/files/js/filemultiselectmenu.js index d50fe28eaceb9..5474fa8887323 100644 --- a/apps/files/js/filemultiselectmenu.js +++ b/apps/files/js/filemultiselectmenu.js @@ -11,7 +11,7 @@ (function() { var FileMultiSelectMenu = OC.Backbone.View.extend({ tagName: 'div', - className: 'filesSelectMenu popovermenu bubble menu-center', + className: 'filesSelectMenu popovermenu bubble menu-right', _scopes: null, initialize: function(menuItems) { this._scopes = menuItems; @@ -37,11 +37,6 @@ 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; }, diff --git a/apps/files/js/merged-index.json b/apps/files/js/merged-index.json index 478db35f6fb4c..df1c480c236be 100644 --- a/apps/files/js/merged-index.json +++ b/apps/files/js/merged-index.json @@ -12,6 +12,7 @@ "fileinfomodel.js", "filelist.js", "filemultiselectmenu.js", + "filemultipleselectionmenu.js", "files.js", "filesummary.js", "gotoplugin.js", diff --git a/apps/files/templates/list.php b/apps/files/templates/list.php index a88a9550beb29..5e0f685203487 100644 --- a/apps/files/templates/list.php +++ b/apps/files/templates/list.php @@ -42,19 +42,18 @@ t('Select all'))?> +