diff options
Diffstat (limited to 'apps/files/js')
-rw-r--r-- | apps/files/js/breadcrumb.js | 223 | ||||
-rw-r--r-- | apps/files/js/filelist.js | 41 | ||||
-rw-r--r-- | apps/files/js/files.js | 18 |
3 files changed, 174 insertions, 108 deletions
diff --git a/apps/files/js/breadcrumb.js b/apps/files/js/breadcrumb.js index 2a4c2bc8a52..35aeb8d357d 100644 --- a/apps/files/js/breadcrumb.js +++ b/apps/files/js/breadcrumb.js @@ -33,6 +33,9 @@ */ var BreadCrumb = function(options){ this.$el = $('<div class="breadcrumb"></div>'); + this.$menu = $('<div class="popovermenu menu-center"><ul></ul></div>'); + + this.crumbSelector = '.crumb:not(.hidden):not(.crumbhome):not(.crumbmenu)'; options = options || {}; if (options.onClick) { this.onClick = options.onClick; @@ -47,6 +50,7 @@ } this._detailViews = []; }; + /** * @memberof OCA.Files */ @@ -110,19 +114,32 @@ * Renders the breadcrumb elements */ render: function() { + // Menu is destroyed on every change, we need to init it + OC.unregisterMenu($('.crumbmenu'), $('.crumbmenu > .popovermenu')); + var parts = this._makeCrumbs(this.dir || '/'); var $crumb; + var $menuItem; this.$el.empty(); this.breadcrumbs = []; for (var i = 0; i < parts.length; i++) { var part = parts[i]; var $image; - var $link = $('<a></a>').attr('href', this.getCrumbUrl(part, i)); - $link.text(part.name); + var $link = $('<a></a>'); $crumb = $('<div class="crumb svg"></div>'); + if(part.dir) { + $link.attr('href', this.getCrumbUrl(part, i)); + } + if(part.name) { + $link.text(part.name); + } + $link.addClass(part.linkclass); $crumb.append($link); - $crumb.attr('data-dir', part.dir); + $crumb.data('dir', part.dir); + // Ignore menu button + $crumb.data('crumb-id', i - 1); + $crumb.addClass(part.class); if (part.img) { $image = $('<img class="svg"></img>'); @@ -132,12 +149,27 @@ } this.breadcrumbs.push($crumb); this.$el.append($crumb); - if (this.onClick) { - $crumb.on('click', this.onClick); + // Only add feedback if not menu + if (this.onClick && i !== 0) { + $link.on('click', this.onClick); } } - $crumb.addClass('last'); + // Menu creation + this._createMenu(); + for (var j = 0; j < parts.length; j++) { + var menuPart = parts[j]; + if(menuPart.dir) { + $menuItem = $('<li class="crumblist"><a><span class="icon-folder"></span><span></span></a></li>'); + $menuItem.data('dir', menuPart.dir); + $menuItem.find('a').attr('href', this.getCrumbUrl(part, j)); + $menuItem.find('span:eq(1)').text(menuPart.name); + this.$menu.children('ul').append($menuItem); + if (this.onClick) { + $menuItem.on('click', this.onClick); + } + } + } _.each(this._detailViews, function(view) { view.render({ dirInfo: this.dirInfo @@ -152,16 +184,20 @@ // setup drag and drop if (this.onDrop) { - this.$el.find('.crumb:not(.last)').droppable({ + this.$el.find('.crumb:not(:last-child):not(.crumbmenu), .crumblist:not(:last-child)').droppable({ drop: this.onDrop, over: this.onOver, out: this.onOut, tolerance: 'pointer', - hoverClass: 'canDrop' + hoverClass: 'canDrop', + greedy: true }); } - this._updateTotalWidth(); + // Menu is destroyed on every change, we need to init it + OC.registerMenu($('.crumbmenu'), $('.crumbmenu > .popovermenu')); + + this._resize(); }, /** @@ -179,12 +215,17 @@ if (dir === '') { parts = []; } + // menu part + crumbs.push({ + class: 'crumbmenu hidden', + linkclass: 'icon-more' + }); // root part crumbs.push({ + name: t('core', 'Home'), dir: '/', - name: '', - alt: t('files', 'Home'), - img: OC.imagePath('core', 'places/home.svg') + class: 'crumbhome', + linkclass: 'icon-home' }); for (var i = 0; i < parts.length; i++) { var part = parts[i]; @@ -198,22 +239,9 @@ }, /** - * Calculate the total breadcrumb width when - * all crumbs are expanded - */ - _updateTotalWidth: function () { - this.totalWidth = 0; - for (var i = 0; i < this.breadcrumbs.length; i++ ) { - var $crumb = $(this.breadcrumbs[i]); - $crumb.data('real-width', $crumb.width()); - this.totalWidth += $crumb.width(); - } - this._resize(); - }, - - /** * Show/hide breadcrumbs to fit the given width - * + * Mostly used by tests + * * @param {int} availableWidth available width */ setMaxWidth: function (availableWidth) { @@ -223,74 +251,107 @@ } }, - _resize: function() { - var i, $crumb, $ellipsisCrumb; - - if (!this.availableWidth) { - this.availableWidth = this.$el.width(); + /** + * Calculate real width based on individual crumbs + * More accurate and works with tests + * + * @param {boolean} ignoreHidden ignore hidden crumbs + */ + getTotalWidth: function(ignoreHidden) { + var totalWidth = 0; + for (var i = 0; i < this.breadcrumbs.length; i++ ) { + var $crumb = $(this.breadcrumbs[i]); + if(!$crumb.hasClass('hidden') || ignoreHidden === true) { + totalWidth += $crumb.outerWidth(); + } } + return totalWidth; + }, - if (this.breadcrumbs.length <= 1) { - return; + /** + * Hide the middle crumb + */ + _hideCrumb: function() { + var length = this.$el.find(this.crumbSelector).length; + // Get the middle one floored down + var elmt = Math.floor(length / 2 - 0.5); + this.$el.find(this.crumbSelector+':eq('+elmt+')').addClass('hidden'); + }, + + /** + * Get the crumb to show + */ + _getCrumbElement: function() { + var hidden = this.$el.find('.crumb.hidden').length; + var shown = this.$el.find(this.crumbSelector).length; + // Get the outer one with priority to the highest + var elmt = (1 - shown % 2) * (hidden - 1); + return this.$el.find('.crumb.hidden:eq('+elmt+')'); + }, + + /** + * Show the middle crumb + */ + _showCrumb: function() { + if(this.$el.find('.crumb.hidden').length === 1) { + this.$el.find('.crumb.hidden').removeClass('hidden'); } + this._getCrumbElement().removeClass('hidden'); + }, + + /** + * Create and append the popovermenu + */ + _createMenu: function() { + this.$el.find('.crumbmenu').append(this.$menu); + this.$menu.children('ul').empty(); + }, + + /** + * Update the popovermenu + */ + _updateMenu: function() { + var menuItems = this.$el.find('.crumb.hidden'); + // Hide the crumb menu if no elements + this.$el.find('.crumbmenu').toggleClass('hidden', menuItems.length === 0); - // reset crumbs - this.$el.find('.crumb.ellipsized').remove(); + this.$menu.find('li').addClass('in-breadcrumb'); + for (var i = 0; i < menuItems.length; i++) { + var crumbId = $(menuItems[i]).data('crumb-id'); + this.$menu.find('li:eq('+crumbId+')').removeClass('in-breadcrumb'); + } + }, - // unhide all - this.$el.find('.crumb.hidden').removeClass('hidden'); + _resize: function() { - if (this.totalWidth <= this.availableWidth) { - // no need to compute breadcrumbs, there is enough space + if (this.breadcrumbs.length <= 2) { + // home & menu return; } - // running width, considering the hidden crumbs - var currentTotalWidth = $(this.breadcrumbs[0]).data('real-width'); - var firstHidden = true; - - // insert ellipsis after root part (root part is always visible) - $ellipsisCrumb = $('<div class="crumb ellipsized svg"><span class="ellipsis">...</span></div>'); - $(this.breadcrumbs[0]).after($ellipsisCrumb); - currentTotalWidth += $ellipsisCrumb.width(); - - i = this.breadcrumbs.length - 1; - - // find the first section that would cause the overflow - // then hide everything in front of that - // - // this ensures that the last crumb section stays visible - // for most of the cases and is always the last one to be - // hidden when the screen becomes very narrow - while (i > 0) { - $crumb = $(this.breadcrumbs[i]); - // if the current breadcrumb would cause overflow - if (!firstHidden || currentTotalWidth + $crumb.data('real-width') > this.availableWidth) { - // hide it - $crumb.addClass('hidden'); - if (firstHidden) { - // set the path of this one as title for the ellipsis - this.$el.find('.crumb.ellipsized') - .attr('title', $crumb.attr('data-dir')) - .tooltip(); - this.$el.find('.ellipsis') - .wrap('<a class="ellipsislink" href="' + encodeURI(OC.generateUrl('apps/files/?dir=' + $crumb.attr('data-dir'))) + '"></a>'); - } - // and all the previous ones (going backwards) - firstHidden = false; - } else { - // add to total width - currentTotalWidth += $crumb.data('real-width'); - } - i--; + // Used for testing since this.$el.parent fails + if (!this.availableWidth) { + this.usedWidth = this.$el.parent().width() - (this.$el.parent().find('.button').length + 1) * 44; + } else { + this.usedWidth = this.availableWidth; } - if (!OC.Util.hasSVGSupport()) { - OC.Util.replaceSVG(this.$el); + // If container is smaller than content + // AND if there are crumbs left to hide + while (this.getTotalWidth() > this.usedWidth + && this.$el.find(this.crumbSelector).length > 0) { + this._hideCrumb(); } + // If container is bigger than content + element to be shown + // AND if there is at least one hidden crumb + while (this.$el.find('.crumb.hidden').length > 0 + && this.getTotalWidth() + this._getCrumbElement().width() < this.usedWidth) { + this._showCrumb(); + } + + this._updateMenu(); } }; OCA.Files.BreadCrumb = BreadCrumb; })(); - diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index af5e9c013f0..6e90f30fcf1 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -322,7 +322,7 @@ this.$el.find('thead th .columntitle').click(_.bind(this._onClickHeader, this)); - this._onResize = _.debounce(_.bind(this._onResize, this), 100); + this._onResize = _.debounce(_.bind(this._onResize, this), 250); $('#app-content').on('appresized', this._onResize); $(window).resize(this._onResize); @@ -556,7 +556,7 @@ // subtract app navigation toggle when visible containerWidth -= $('#app-navigation-toggle').width(); - this.breadcrumb.setMaxWidth(containerWidth - actionsWidth - 10); + this.breadcrumb._resize(); this.$table.find('>thead').width($('#app-content').width() - OC.Util.getScrollBarWidth()); }, @@ -837,7 +837,8 @@ * Event handler when clicking on a bread crumb */ _onClickBreadCrumb: function(e) { - var $el = $(e.target).closest('.crumb'), + // Select a crumb or a crumb in the menu + var $el = $(e.target).closest('.crumb, .crumblist'), $targetDir = $el.data('dir'); if ($targetDir !== undefined && e.which === 1) { @@ -863,8 +864,8 @@ _onDropOnBreadCrumb: function( event, ui ) { var self = this; var $target = $(event.target); - if (!$target.is('.crumb')) { - $target = $target.closest('.crumb'); + if (!$target.is('.crumb, .crumblist')) { + $target = $target.closest('.crumb, .crumblist'); } var targetPath = $(event.target).data('dir'); var dir = this.getCurrentDirectory(); @@ -1713,7 +1714,7 @@ if (status === 500) { // Go home this.changeDirectory('/'); - OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator'), + OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator'), {type: 'error'} ); return false; @@ -1724,7 +1725,7 @@ if (this.getCurrentDirectory() !== '/') { this.changeDirectory('/'); // TODO: read error message from exception - OC.Notification.show(t('files', 'Storage is temporarily not available'), + OC.Notification.show(t('files', 'Storage is temporarily not available'), {type: 'error'} ); } @@ -2040,11 +2041,11 @@ .fail(function(status) { if (status === 412) { // TODO: some day here we should invoke the conflict dialog - OC.Notification.show(t('files', 'Could not move "{file}", target exists', + OC.Notification.show(t('files', 'Could not move "{file}", target exists', {file: fileName}), {type: 'error'} ); } else { - OC.Notification.show(t('files', 'Could not move "{file}"', + OC.Notification.show(t('files', 'Could not move "{file}"', {file: fileName}), {type: 'error'} ); } @@ -2271,7 +2272,7 @@ // TODO: 409 means current folder does not exist, redirect ? if (status === 404) { // source not found, so remove it from the list - OC.Notification.show(t('files', 'Could not rename "{fileName}", it does not exist any more', + OC.Notification.show(t('files', 'Could not rename "{fileName}", it does not exist any more', {fileName: oldName}), {timeout: 7, type: 'error'} ); @@ -2291,7 +2292,7 @@ ); } else { // restore the item to its previous state - OC.Notification.show(t('files', 'Could not rename "{fileName}"', + OC.Notification.show(t('files', 'Could not rename "{fileName}"', {fileName: oldName}), {type: 'error'} ); } @@ -2376,18 +2377,18 @@ self.addAndFetchFileInfo(targetPath, '', {scrollTo: true}).then(function(status, data) { deferred.resolve(status, data); }, function() { - OC.Notification.show(t('files', 'Could not create file "{file}"', + OC.Notification.show(t('files', 'Could not create file "{file}"', {file: name}), {type: 'error'} ); }); }) .fail(function(status) { if (status === 412) { - OC.Notification.show(t('files', 'Could not create file "{file}" because it already exists', + OC.Notification.show(t('files', 'Could not create file "{file}" because it already exists', {file: name}), {type: 'error'} ); } else { - OC.Notification.show(t('files', 'Could not create file "{file}"', + OC.Notification.show(t('files', 'Could not create file "{file}"', {file: name}), {type: 'error'} ); } @@ -2426,7 +2427,7 @@ self.addAndFetchFileInfo(targetPath, '', {scrollTo:true}).then(function(status, data) { deferred.resolve(status, data); }, function() { - OC.Notification.show(t('files', 'Could not create folder "{dir}"', + OC.Notification.show(t('files', 'Could not create folder "{dir}"', {dir: name}), {type: 'error'} ); }); @@ -2437,20 +2438,20 @@ // add it to the list, for completeness self.addAndFetchFileInfo(targetPath, '', {scrollTo:true}) .done(function(status, data) { - OC.Notification.show(t('files', 'Could not create folder "{dir}" because it already exists', + OC.Notification.show(t('files', 'Could not create folder "{dir}" because it already exists', {dir: name}), {type: 'error'} ); // still consider a failure deferred.reject(createStatus, data); }) .fail(function() { - OC.Notification.show(t('files', 'Could not create folder "{dir}"', + OC.Notification.show(t('files', 'Could not create folder "{dir}"', {dir: name}), {type: 'error'} ); deferred.reject(status); }); } else { - OC.Notification.show(t('files', 'Could not create folder "{dir}"', + OC.Notification.show(t('files', 'Could not create folder "{dir}"', {dir: name}), {type: 'error'} ); deferred.reject(createStatus); @@ -2507,7 +2508,7 @@ deferred.resolve(status, data); }) .fail(function(status) { - OC.Notification.show(t('files', 'Could not create file "{file}"', + OC.Notification.show(t('files', 'Could not create file "{file}"', {file: name}), {type: 'error'} ); deferred.reject(status); @@ -2616,7 +2617,7 @@ removeFromList(file); } else { // only reset the spinner for that one file - OC.Notification.show(t('files', 'Error deleting file "{fileName}".', + OC.Notification.show(t('files', 'Error deleting file "{fileName}".', {fileName: file}), {type: 'error'} ); var deleteAction = self.findFileEl(file).find('.action.delete'); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index e34d7fe2550..017bf7ecf41 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -117,32 +117,32 @@ ownerDisplayName = $('#ownerDisplayName').val(); if (usedSpacePercent > 98) { if (owner !== oc_current_user) { - OC.Notification.show(t('files', 'Storage of {owner} is full, files can not be updated or synced anymore!', + OC.Notification.show(t('files', 'Storage of {owner} is full, files can not be updated or synced anymore!', {owner: ownerDisplayName}), {type: 'error'} ); return; } - OC.Notification.show(t('files', - 'Your storage is full, files can not be updated or synced anymore!'), + OC.Notification.show(t('files', + 'Your storage is full, files can not be updated or synced anymore!'), {type : 'error'} ); return; } if (usedSpacePercent > 90) { if (owner !== oc_current_user) { - OC.Notification.show(t('files', 'Storage of {owner} is almost full ({usedSpacePercent}%)', + OC.Notification.show(t('files', 'Storage of {owner} is almost full ({usedSpacePercent}%)', { - usedSpacePercent: usedSpacePercent, + usedSpacePercent: usedSpacePercent, owner: ownerDisplayName }), - { + { type: 'error' } ); return; } OC.Notification.show(t('files', 'Your storage is almost full ({usedSpacePercent}%)', - {usedSpacePercent: usedSpacePercent}), + {usedSpacePercent: usedSpacePercent}), {type : 'error'} ); } @@ -396,6 +396,8 @@ var dragOptions={ } $selectedFiles.closest('tr').addClass('animate-opacity dragging'); $selectedFiles.closest('tr').filter('.ui-droppable').droppable( 'disable' ); + // Show breadcrumbs menu + $('.crumbmenu').addClass('canDropChildren'); }, stop: function(event, ui) { @@ -411,6 +413,8 @@ var dragOptions={ setTimeout(function() { $tr.removeClass('animate-opacity'); }, 300); + // Hide breadcrumbs menu + $('.crumbmenu').removeClass('canDropChildren'); }, drag: function(event, ui) { var scrollingArea = FileList.$container; |