From 9854e71d2c83bd5f74a4798be1547e75112d5a41 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 15 Jul 2015 12:06:13 +0200 Subject: [PATCH 01/25] Basic work for right sidebar Adds right sidebar with registrable panels (still WIP) --- apps/files/css/detailsView.css | 22 +++ apps/files/index.php | 6 + apps/files/js/detailfileinfoview.js | 87 ++++++++++++ apps/files/js/detailsview.js | 177 ++++++++++++++++++++++++ apps/files/js/detailtabview.js | 108 +++++++++++++++ apps/files/js/filelist.js | 33 +++++ apps/files/js/mainfileinfodetailview.js | 75 ++++++++++ 7 files changed, 508 insertions(+) create mode 100644 apps/files/css/detailsView.css create mode 100644 apps/files/js/detailfileinfoview.js create mode 100644 apps/files/js/detailsview.js create mode 100644 apps/files/js/detailtabview.js create mode 100644 apps/files/js/mainfileinfodetailview.js diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css new file mode 100644 index 00000000000..783a8c9b0c3 --- /dev/null +++ b/apps/files/css/detailsView.css @@ -0,0 +1,22 @@ + + +#app-content-files .detailsView { + position: absolute; + width: 300px; + top: 44px; + bottom: 0; + right: 0; + background-color: white; + border: 1px solid black; +} + +#app-content-files .detailsView .thumbnail { + width: 32px; + height: 32px; + float: left; +} + +#app-content-files .detailsView .detailList { + float: left; +} + diff --git a/apps/files/index.php b/apps/files/index.php index 4f103f975cb..1cb7c16ce6b 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -50,6 +50,12 @@ OCP\Util::addscript('files', 'search'); \OCP\Util::addScript('files', 'tagsplugin'); \OCP\Util::addScript('files', 'favoritesplugin'); +\OCP\Util::addScript('files', 'detailfileinfoview'); +\OCP\Util::addScript('files', 'detailtabview'); +\OCP\Util::addScript('files', 'detailsview'); +\OCP\Util::addScript('files', 'mainfileinfodetailview'); +\OCP\Util::addStyle('files', 'detailsView'); + \OC_Util::addVendorScript('core', 'handlebars/handlebars'); OCP\App::setActiveNavigationEntry('files_index'); diff --git a/apps/files/js/detailfileinfoview.js b/apps/files/js/detailfileinfoview.js new file mode 100644 index 00000000000..9585f57f1ef --- /dev/null +++ b/apps/files/js/detailfileinfoview.js @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + /** + * @class OCA.Files.DetailFileInfoView + * @classdesc + * + * Displays a block of details about the file info. + * + */ + var DetailFileInfoView = function() { + this.initialize(); + }; + /** + * @memberof OCA.Files + */ + DetailFileInfoView.prototype = { + /** + * jQuery element + */ + $el: null, + + _template: null, + + /** + * Currently displayed file info + * + * @type OCA.Files.FileInfo + */ + _fileInfo: null, + + /** + * Initialize the details view + */ + initialize: function() { + this.$el = $('
'); + }, + + /** + * Destroy / uninitialize this instance. + */ + destroy: function() { + if (this.$el) { + this.$el.remove(); + } + }, + + /** + * Renders this details view + * + * @abstract + */ + render: function() { + // to be implemented in subclass + }, + + /** + * Sets the file info to be displayed in the view + * + * @param {OCA.Files.FileInfo} fileInfo file info to set + */ + setFileInfo: function(fileInfo) { + this._fileInfo = fileInfo; + this.render(); + }, + + /** + * Returns the file info. + * + * @return {OCA.Files.FileInfo} file info + */ + getFileInfo: function() { + return this._fileInfo; + } + }; + + OCA.Files.DetailFileInfoView = DetailFileInfoView; +})(); + diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js new file mode 100644 index 00000000000..a8abac5c68f --- /dev/null +++ b/apps/files/js/detailsview.js @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + + var TEMPLATE = + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
'; + + var TEMPLATE_TAB_HEADER = + '
{{label}}
'; + + /** + * @class OCA.Files.DetailsView + * @classdesc + * + * The details view show details about a selected file. + * + */ + var DetailsView = function() { + this.initialize(); + }; + /** + * @memberof OCA.Files + */ + DetailsView.prototype = { + + /** + * jQuery element + */ + $el: null, + + _template: null, + _templateTabHeader: null, + + /** + * Currently displayed file info + * + * @type OCA.Files.FileInfo + */ + _fileInfo: null, + + /** + * List of detail tab views + * + * @type Array + */ + _tabViews: [], + + /** + * List of detail file info views + * + * @type Array + */ + _detailFileInfoViews: [], + + /** + * Initialize the details view + */ + initialize: function() { + this.$el = $('
'); + this.fileInfo = null; + this._tabViews = []; + this._detailFileInfoViews = []; + }, + + /** + * Destroy / uninitialize this instance. + */ + destroy: function() { + if (this.$el) { + this.$el.remove(); + } + }, + + /** + * Renders this details view + */ + render: function() { + this.$el.empty(); + + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + + if (!this._templateTabHeader) { + this._templateTabHeader = Handlebars.compile(TEMPLATE_TAB_HEADER); + } + + var $el = $(this._template()); + var $tabHeadsContainer = $el.find('.tabHeadsContainer'); + var $tabsContainer = $el.find('.tabContentsContainer'); + var $detailsContainer = $el.find('.detailFileInfoContainer'); + + // render tabs + _.each(this._tabs, function(tabView) { + tabView.render(); + // hidden by default + tabView.$el.addClass('hidden'); + $tabsContainer.append(tabView.$el); + + $tabHeadsContainer.append(this._templateTabHeader({label: tabView.getLabel()})); + }); + + // render details + _.each(this._detailFileInfoViews, function(detailView) { + detailView.render(); + $detailsContainer.append(detailView.$el); + }); + + // select first tab + $el.find('.tabContentsContainer:first').removeClass('hidden'); + + this.$el.append($el); + }, + + /** + * Sets the file info to be displayed in the view + * + * @param {OCA.Files.FileInfo} fileInfo file info to set + */ + setFileInfo: function(fileInfo) { + this._fileInfo = fileInfo; + + // notify all panels + _.each(this._tabs, function(tabView) { + tabView.setFileInfo(fileInfo); + }); + _.each(this._detailFileInfoViews, function(detailView) { + detailView.setFileInfo(fileInfo); + }); + }, + + /** + * Returns the file info. + * + * @return {OCA.Files.FileInfo} file info + */ + getFileInfo: function() { + return this._fileInfo; + }, + + /** + * Adds a tab in the tab view + * + * @param {OCA.Files.DetailTabView} tab view + */ + addTabView: function(tabView) { + this._tabViews.push(tabView); + }, + + /** + * Adds a detail view for file info. + * + * @param {OCA.Files.DetailFileInfoView} detail view + */ + addDetailView: function(detailView) { + this._detailFileInfoViews.push(detailView); + } + }; + + OCA.Files.DetailsView = DetailsView; +})(); + diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js new file mode 100644 index 00000000000..f630099111d --- /dev/null +++ b/apps/files/js/detailtabview.js @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + + /** + * @class OCA.Files.DetailTabView + * @classdesc + * + * Base class for tab views to display file information. + * + */ + var DetailTabView = function() { + this.initialize(); + }; + + /** + * @memberof OCA.Files + */ + DetailTabView.prototype = { + /** + * jQuery element + */ + $el: null, + + /** + * Tab id + */ + _id: null, + + /** + * Tab label + */ + _label: null, + + _template: null, + + /** + * Currently displayed file info + * + * @type OCA.Files.FileInfo + */ + _fileInfo: null, + + /** + * Initialize the details view + */ + initialize: function() { + this.$el = $('
'); + }, + + /** + * Destroy / uninitialize this instance. + */ + destroy: function() { + if (this.$el) { + this.$el.remove(); + } + }, + + /** + * Returns the tab label + * + * @return {String} label + */ + getLabel: function() { + return 'Tab ' + this._id; + }, + + /** + * Renders this details view + * + * @abstract + */ + render: function() { + // to be implemented in subclass + }, + + /** + * Sets the file info to be displayed in the view + * + * @param {OCA.Files.FileInfo} fileInfo file info to set + */ + setFileInfo: function(fileInfo) { + this._fileInfo = fileInfo; + this.render(); + }, + + /** + * Returns the file info. + * + * @return {OCA.Files.FileInfo} file info + */ + getFileInfo: function() { + return this._fileInfo; + } + }; + + OCA.Files.DetailTabView = DetailTabView; +})(); + diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index a7d4e41d0e0..cbf946974eb 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -64,6 +64,11 @@ */ fileSummary: null, + /** + * @type OCA.Files.DetailsView + */ + _detailsView: null, + /** * Whether the file list was initialized already. * @type boolean @@ -262,6 +267,25 @@ this.fileActions.on('setDefault', this._onFileActionsUpdated); }, + /** + * Update the details view to display the given file + * + * @param {OCA.Files.FileInfo} fileInfo file info to display + */ + _updateDetailsView: function(fileInfo) { + if (!this._detailsView) { + this._detailsView = new OCA.Files.DetailsView(); + this.$el.append(this._detailsView.$el); + + this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView()); + + this._detailsView.render(); + } + this._detailsView.setFileInfo(_.extend({ + path: this.getCurrentDirectory() + }, fileInfo)); + }, + /** * Event handler for when the window size changed */ @@ -350,6 +374,15 @@ this._selectFileEl($tr, !$checkbox.prop('checked')); this.updateSelectionSummary(); } else { + var currentIndex = $tr.index(); + var fileInfo = this.files[currentIndex]; + + this._updateDetailsView(fileInfo); + event.preventDefault(); + return; + + // FIXME: disabled for testing details view + var filename = $tr.attr('data-file'); var renaming = $tr.data('renaming'); if (!renaming) { diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js new file mode 100644 index 00000000000..3f3705c2586 --- /dev/null +++ b/apps/files/js/mainfileinfodetailview.js @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + var TEMPLATE = + '
' + + '
    ' + + '
  • Name: {{name}}
  • ' + + '
  • Path: {{path}}
  • ' + + '
'; + + /** + * @class OCA.Files.MainFileInfoDetailView + * @classdesc + * + * Displays main details about a file + * + */ + var MainFileInfoDetailView = function() { + this.initialize(); + }; + /** + * @memberof OCA.Files + */ + MainFileInfoDetailView.prototype = _.extend({}, OCA.Files.DetailFileInfoView.prototype, + /** @lends OCA.Files.MainFileInfoDetailView.prototype */ { + _template: null, + + /** + * Initialize the details view + */ + initialize: function() { + this.$el = $('
'); + }, + + /** + * Renders this details view + * + * @abstract + */ + render: function() { + this.$el.empty(); + + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + + if (this._fileInfo) { + this.$el.append(this._template(this._fileInfo)); + var $iconDiv = this.$el.find('.thumbnail'); + // FIXME: use proper way, this is only for demo purposes + FileList.lazyLoadPreview({ + path: this._fileInfo.path + '/' + this._fileInfo.name, + mime: this._fileInfo.mimetype, + etag: this._fileInfo.etag, + callback: function(url) { + $iconDiv.css('background-image', 'url("' + url + '")'); + } + }); + } else { + // TODO: render placeholder text? + } + } + }); + + OCA.Files.MainFileInfoDetailView = MainFileInfoDetailView; +})(); + From 12e5f310ddb784c0ed1248020b770d93040918da Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 15 Jul 2015 16:09:00 +0200 Subject: [PATCH 02/25] Improved right sidebar Added owner info. Added animation, but causes scrollbal. Default file action now when clicking on name directly. Fixed icon. Added empty share tab. --- apps/files/css/detailsView.css | 30 +++++- apps/files/index.php | 2 +- apps/files/js/detailsview.js | 45 +++++---- apps/files/js/detailtabview.js | 25 ++++- apps/files/js/filelist.js | 118 ++++++++++++++++------- apps/files/js/mainfileinfodetailview.js | 48 +++++---- apps/files_sharing/js/share.js | 7 ++ apps/files_sharing/js/sharedetailview.js | 63 ++++++++++++ apps/files_sharing/js/sharetabview.js | 64 ++++++++++++ 9 files changed, 328 insertions(+), 74 deletions(-) create mode 100644 apps/files_sharing/js/sharedetailview.js create mode 100644 apps/files_sharing/js/sharetabview.js diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css index 783a8c9b0c3..0bbdfe94903 100644 --- a/apps/files/css/detailsView.css +++ b/apps/files/css/detailsView.css @@ -1,19 +1,45 @@ +#app-content-files .detailsView.disappear { + margin-right: -300px; +} + #app-content-files .detailsView { position: absolute; width: 300px; top: 44px; bottom: 0; right: 0; + left: auto; background-color: white; border: 1px solid black; + -webkit-transition: margin-right 300ms; + -moz-transition: margin-right 300ms; + -o-transition: margin-right 300ms; + transition: margin-right 300ms; +} + +#app-content-files .detailsView .detailFileInfoContainer { + min-height: 200px; + padding: 10px; +} + +#app-content-files .detailsView .detailFileInfoContainer > div{ + clear: both; + margin-left: 5px; } #app-content-files .detailsView .thumbnail { - width: 32px; - height: 32px; + width: 50px; + height: 50px; float: left; + margin: 5px; + background-size: 50px; +} + +#app-content-files .detailsView .fileName { + font-weight: bold; + font-size: 17px; } #app-content-files .detailsView .detailList { diff --git a/apps/files/index.php b/apps/files/index.php index 1cb7c16ce6b..dca3e5ae74d 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -52,8 +52,8 @@ OCP\Util::addscript('files', 'search'); \OCP\Util::addScript('files', 'detailfileinfoview'); \OCP\Util::addScript('files', 'detailtabview'); -\OCP\Util::addScript('files', 'detailsview'); \OCP\Util::addScript('files', 'mainfileinfodetailview'); +\OCP\Util::addScript('files', 'detailsview'); \OCP\Util::addStyle('files', 'detailsView'); \OC_Util::addVendorScript('core', 'handlebars/handlebars'); diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index a8abac5c68f..719299485a6 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -14,14 +14,14 @@ '
' + '
' + '
' + - '
' + - '
' + - '
' + + '
' + + '
    ' + + '
' + '
' + '
'; var TEMPLATE_TAB_HEADER = - '
{{label}}
'; + '
  • {{label}}
  • '; /** * @class OCA.Files.DetailsView @@ -33,6 +33,7 @@ var DetailsView = function() { this.initialize(); }; + /** * @memberof OCA.Files */ @@ -90,6 +91,7 @@ * Renders this details view */ render: function() { + var self = this; this.$el.empty(); if (!this._template) { @@ -101,30 +103,37 @@ } var $el = $(this._template()); + var $tabsContainer = $el.find('.tabsContainer'); var $tabHeadsContainer = $el.find('.tabHeadsContainer'); - var $tabsContainer = $el.find('.tabContentsContainer'); var $detailsContainer = $el.find('.detailFileInfoContainer'); - // render tabs - _.each(this._tabs, function(tabView) { - tabView.render(); - // hidden by default - tabView.$el.addClass('hidden'); - $tabsContainer.append(tabView.$el); - - $tabHeadsContainer.append(this._templateTabHeader({label: tabView.getLabel()})); - }); - // render details _.each(this._detailFileInfoViews, function(detailView) { detailView.render(); $detailsContainer.append(detailView.$el); }); - // select first tab - $el.find('.tabContentsContainer:first').removeClass('hidden'); + if (this._tabViews.length > 0) { + // render tabs + _.each(this._tabViews, function(tabView) { + tabView.render(); + // hidden by default + $tabsContainer.append(tabView.$el); + + $tabHeadsContainer.append(self._templateTabHeader({ + tabId: tabView.getId(), + label: tabView.getLabel() + })); + }); + } + + // TODO: select current tab this.$el.append($el); + + if (this._tabViews.length > 0) { + $tabsContainer.tabs({}); + } }, /** @@ -136,7 +145,7 @@ this._fileInfo = fileInfo; // notify all panels - _.each(this._tabs, function(tabView) { + _.each(this._tabViews, function(tabView) { tabView.setFileInfo(fileInfo); }); _.each(this._detailFileInfoViews, function(detailView) { diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js index f630099111d..767ece2297c 100644 --- a/apps/files/js/detailtabview.js +++ b/apps/files/js/detailtabview.js @@ -17,8 +17,8 @@ * Base class for tab views to display file information. * */ - var DetailTabView = function() { - this.initialize(); + var DetailTabView = function(id) { + this.initialize(id); }; /** @@ -51,9 +51,16 @@ /** * Initialize the details view + * + * @param {string} id tab id */ - initialize: function() { + initialize: function(id) { + if (!id) { + throw 'Argument "id" is required'; + } + this._id = id; this.$el = $('
    '); + this.$el.attr('id', id); }, /** @@ -65,6 +72,15 @@ } }, + /** + * Returns the tab element id + * + * @return {string} tab id + */ + getId: function() { + return this._id; + }, + /** * Returns the tab label * @@ -81,6 +97,9 @@ */ render: function() { // to be implemented in subclass + // FIXME: code is only for testing + this.$el.empty(); + this.$el.append('
    Hello ' + this._id + '
    '); }, /** diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index cbf946974eb..355c76f9c64 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -221,6 +221,13 @@ this.updateSearch(); + this.$el.on('click', function(event) { + var $target = $(event.target); + // click outside file row ? + if (!$target.closest('tbody').length) { + self._updateDetailsView(null); + } + }); this.$fileList.on('click','td.filename>a.name', _.bind(this._onClickFile, this)); this.$fileList.on('change', 'td.filename>.selectCheckBox', _.bind(this._onClickFileCheckbox, this)); this.$el.on('urlChanged', _.bind(this._onUrlChanged, this)); @@ -273,17 +280,35 @@ * @param {OCA.Files.FileInfo} fileInfo file info to display */ _updateDetailsView: function(fileInfo) { + var self = this; + if (!fileInfo) { + if (this._detailsView) { + // hide it + this._detailsView.$el.addClass('disappear'); + this._detailsView.setFileInfo(null); + } + return; + } + if (!this._detailsView) { this._detailsView = new OCA.Files.DetailsView(); - this.$el.append(this._detailsView.$el); - this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView()); - + _.each(this._detailFileInfoViews, function(view) { + self._detailsView.addDetailView(view); + }); + _.each(this._tabViews, function(view) { + self._detailsView.addTabView(view); + }); + this.$el.append(this._detailsView.$el); + this._detailsView.$el.addClass('disappear'); this._detailsView.render(); } this._detailsView.setFileInfo(_.extend({ path: this.getCurrentDirectory() }, fileInfo)); + _.defer(function() { + self._detailsView.$el.removeClass('disappear'); + }); }, /** @@ -374,36 +399,34 @@ this._selectFileEl($tr, !$checkbox.prop('checked')); this.updateSelectionSummary(); } else { - var currentIndex = $tr.index(); - var fileInfo = this.files[currentIndex]; - - this._updateDetailsView(fileInfo); - event.preventDefault(); - return; - - // FIXME: disabled for testing details view - - var filename = $tr.attr('data-file'); - var renaming = $tr.data('renaming'); - if (!renaming) { - this.fileActions.currentFile = $tr.find('td'); - var mime = this.fileActions.getCurrentMimeType(); - var type = this.fileActions.getCurrentType(); - var permissions = this.fileActions.getCurrentPermissions(); - var action = this.fileActions.getDefault(mime,type, permissions); - if (action) { - event.preventDefault(); - // also set on global object for legacy apps - window.FileActions.currentFile = this.fileActions.currentFile; - action(filename, { - $file: $tr, - fileList: this, - fileActions: this.fileActions, - dir: $tr.attr('data-path') || this.getCurrentDirectory() - }); + // clicked directly on the name + if ($(event.target).is('.nametext') || $(event.target).closest('.nametext').length) { + var filename = $tr.attr('data-file'); + var renaming = $tr.data('renaming'); + if (!renaming) { + this.fileActions.currentFile = $tr.find('td'); + var mime = this.fileActions.getCurrentMimeType(); + var type = this.fileActions.getCurrentType(); + var permissions = this.fileActions.getCurrentPermissions(); + var action = this.fileActions.getDefault(mime,type, permissions); + if (action) { + event.preventDefault(); + // also set on global object for legacy apps + window.FileActions.currentFile = this.fileActions.currentFile; + action(filename, { + $file: $tr, + fileList: this, + fileActions: this.fileActions, + dir: $tr.attr('data-path') || this.getCurrentDirectory() + }); + } + // deselect row + $(event.target).closest('a').blur(); } - // deselect row - $(event.target).closest('a').blur(); + } else { + var fileInfo = this.files[$tr.index()]; + this._updateDetailsView(fileInfo); + event.preventDefault(); } } }, @@ -858,7 +881,7 @@ var formatted; var text; if (mtime > 0) { - formatted = formatDate(mtime); + formatted = OC.Util.formatDate(mtime); text = OC.Util.relativeModifiedDate(mtime); } else { formatted = t('files', 'Unable to determine date'); @@ -1554,6 +1577,7 @@ tr.remove(); tr = self.add(fileInfo, {updateSummary: false, silent: true}); self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)})); + self._updateDetailsView(fileInfo); } }); } else { @@ -2260,6 +2284,34 @@ } }; + /** + * Globally registered tab views + * + * @type OCA.Files.DetailTabView + */ + FileList.prototype._tabViews = []; + + /** + * Globally registered detail views + * + * @type OCA.Files.DetailFileInfoView + */ + FileList.prototype._detailFileInfoViews = []; + + /** + * Register a tab view to be added to all views + */ + FileList.prototype.registerTabView = function(tabView) { + this._tabViews.push(tabView); + }; + + /** + * Register a detail view to be added to all views + */ + FileList.prototype.registerDetailView = function(detailView) { + this._detailFileInfoViews.push(detailView); + }; + /** * File info attributes. * diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js index 3f3705c2586..5eb74145f12 100644 --- a/apps/files/js/mainfileinfodetailview.js +++ b/apps/files/js/mainfileinfodetailview.js @@ -10,11 +10,8 @@ (function() { var TEMPLATE = - '
    ' + - '
      ' + - '
    • Name: {{name}}
    • ' + - '
    • Path: {{path}}
    • ' + - '
    '; + '
    {{name}}
    ' + + '
    {{size}}, {{date}}
    '; /** * @class OCA.Files.MainFileInfoDetailView @@ -42,8 +39,6 @@ /** * Renders this details view - * - * @abstract */ render: function() { this.$el.empty(); @@ -53,17 +48,36 @@ } if (this._fileInfo) { - this.$el.append(this._template(this._fileInfo)); + this.$el.append(this._template({ + nameLabel: t('files', 'Name'), + name: this._fileInfo.name, + pathLabel: t('files', 'Path'), + path: this._fileInfo.path, + sizeLabel: t('files', 'Size'), + // TODO: refactor and use size formatter + size: OC.Util.humanFileSize(this._fileInfo.size, true), + altSize: this._fileInfo.size, + dateLabel: t('files', 'Modified'), + altDate: OC.Util.formatDate(this._fileInfo.mtime), + date: OC.Util.relativeModifiedDate(this._fileInfo.mtime) + })); + var $iconDiv = this.$el.find('.thumbnail'); - // FIXME: use proper way, this is only for demo purposes - FileList.lazyLoadPreview({ - path: this._fileInfo.path + '/' + this._fileInfo.name, - mime: this._fileInfo.mimetype, - etag: this._fileInfo.etag, - callback: function(url) { - $iconDiv.css('background-image', 'url("' + url + '")'); - } - }); + // TODO: we really need OC.Previews + if (this._fileInfo.mimetype !== 'httpd/unix-directory') { + // FIXME: use proper way, this is only for demo purposes + var previewUrl = FileList.generatePreviewUrl({ + file: this._fileInfo.path + '/' + this._fileInfo.name, + c: this._fileInfo.etag, + x: 50, + y: 50 + }); + previewUrl = previewUrl.replace('(', '%28').replace(')', '%29'); + $iconDiv.css('background-image', 'url("' + previewUrl + '")'); + } else { + // TODO: special icons / shared / external + $iconDiv.css('background-image', 'url("' + OC.MimeType.getIconUrl('dir') + '")'); + } } else { // TODO: render placeholder text? } diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index e7823454c53..227eec79578 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -140,6 +140,13 @@ } }); }, t('files_sharing', 'Share')); + + OC.addScript('files_sharing', 'sharetabview').done(function() { + fileList.registerTabView(new OCA.Sharing.ShareTabView('shareTabView')); + }); + OC.addScript('files_sharing', 'sharedetailview').done(function() { + fileList.registerDetailView(new OCA.Sharing.ShareDetailView()); + }); }, /** diff --git a/apps/files_sharing/js/sharedetailview.js b/apps/files_sharing/js/sharedetailview.js new file mode 100644 index 00000000000..a18a6d19d15 --- /dev/null +++ b/apps/files_sharing/js/sharedetailview.js @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + var TEMPLATE = + '
      ' + + '
    • Owner: {{owner}}
    • ' + + '
    '; + + /** + * @class OCA.Files.MainFileInfoDetailView + * @classdesc + * + * Displays main details about a file + * + */ + var ShareDetailView = function() { + this.initialize(); + }; + /** + * @memberof OCA.Sharing + */ + ShareDetailView.prototype = _.extend({}, OCA.Files.DetailFileInfoView.prototype, + /** @lends OCA.Sharing.ShareDetailView.prototype */ { + _template: null, + + /** + * Initialize the details view + */ + initialize: function() { + this.$el = $('
    '); + }, + + /** + * Renders this details view + */ + render: function() { + this.$el.empty(); + + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + + if (this._fileInfo) { + this.$el.append(this._template({ + owner: this._fileInfo.shareOwner || OC.currentUser + })); + } else { + // TODO: render placeholder text? + } + } + }); + + OCA.Sharing.ShareDetailView = ShareDetailView; +})(); + diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js new file mode 100644 index 00000000000..48dfcae16d8 --- /dev/null +++ b/apps/files_sharing/js/sharetabview.js @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + var TEMPLATE = + '
    TODO: here comes the share dialog
    '; + + /** + * @class OCA.Sharing.ShareTabView + * @classdesc + * + * Displays sharing information + * + */ + var ShareTabView = function(id) { + this.initialize(id); + }; + /** + * @memberof OCA.Sharing + */ + ShareTabView.prototype = _.extend({}, OCA.Files.DetailTabView.prototype, + /** @lends OCA.Sharing.ShareTabView.prototype */ { + _template: null, + + /** + * Initialize the details view + */ + initialize: function() { + OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments); + this.$el.addClass('shareTabView'); + }, + + getLabel: function() { + return t('files_sharing', 'Sharing'); + }, + + /** + * Renders this details view + */ + render: function() { + this.$el.empty(); + + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + + if (this._fileInfo) { + this.$el.append(this._template()); + } else { + // TODO: render placeholder text? + } + } + }); + + OCA.Sharing.ShareTabView = ShareTabView; +})(); + From ff614a7dbde66a9cd795a674d0e0635b6f32e3a5 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 15 Jul 2015 17:05:25 +0200 Subject: [PATCH 03/25] More fixes to the right sidebar Sidebar now works in all file list views. Moved owner to share tab. --- apps/files/css/detailsView.css | 36 +++++++---- apps/files/js/detailsview.js | 13 +++- apps/files/js/filelist.js | 77 +++++++++--------------- apps/files/js/mainfileinfodetailview.js | 20 ++++-- apps/files_sharing/js/share.js | 3 - apps/files_sharing/js/sharedetailview.js | 63 ------------------- apps/files_sharing/js/sharetabview.js | 6 +- 7 files changed, 85 insertions(+), 133 deletions(-) delete mode 100644 apps/files_sharing/js/sharedetailview.js diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css index 0bbdfe94903..a62bcd8c968 100644 --- a/apps/files/css/detailsView.css +++ b/apps/files/css/detailsView.css @@ -1,35 +1,43 @@ -#app-content-files .detailsView.disappear { - margin-right: -300px; +.app-files .detailsView.disappear { + margin-right: -350px; } -#app-content-files .detailsView { +.app-files .detailsView { position: absolute; - width: 300px; - top: 44px; + width: 350px; + top: 0; bottom: 0; right: 0; left: auto; background-color: white; - border: 1px solid black; -webkit-transition: margin-right 300ms; -moz-transition: margin-right 300ms; -o-transition: margin-right 300ms; transition: margin-right 300ms; } -#app-content-files .detailsView .detailFileInfoContainer { +.app-files .detailsView { + background: #eee; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + box-shadow: 0 2px 3px rgba(50, 50, 50, .4); + display: block; + z-index: 500; +} + +.app-files .detailsView .detailFileInfoContainer { min-height: 200px; padding: 10px; } -#app-content-files .detailsView .detailFileInfoContainer > div{ +.app-files .detailsView .detailFileInfoContainer > div{ clear: both; margin-left: 5px; } -#app-content-files .detailsView .thumbnail { +.app-files .detailsView .thumbnail { width: 50px; height: 50px; float: left; @@ -37,12 +45,18 @@ background-size: 50px; } -#app-content-files .detailsView .fileName { +.app-files .detailsView .fileName { font-weight: bold; font-size: 17px; } -#app-content-files .detailsView .detailList { +.app-files .detailsView .detailList { float: left; } +.app-files .detailsView .close { + position: absolute; + top: 0; + right: 0; + margin: 10px; +} diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index 719299485a6..d69d8c09c16 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -18,6 +18,7 @@ '
      ' + '
    ' + '
    ' + + ' ' + ''; var TEMPLATE_TAB_HEADER = @@ -72,10 +73,16 @@ * Initialize the details view */ initialize: function() { + var self = this; this.$el = $('
    '); this.fileInfo = null; this._tabViews = []; this._detailFileInfoViews = []; + + this.$el.on('click', 'a.close', function(event) { + self.$el.addClass('disappear'); + event.preventDefault(); + }); }, /** @@ -102,7 +109,9 @@ this._templateTabHeader = Handlebars.compile(TEMPLATE_TAB_HEADER); } - var $el = $(this._template()); + var $el = $(this._template({ + closeLabel: t('files', 'Close') + })); var $tabsContainer = $el.find('.tabsContainer'); var $tabHeadsContainer = $el.find('.tabHeadsContainer'); var $detailsContainer = $el.find('.detailFileInfoContainer'); @@ -144,6 +153,8 @@ setFileInfo: function(fileInfo) { this._fileInfo = fileInfo; + this.render(); + // notify all panels _.each(this._tabViews, function(tabView) { tabView.setFileInfo(fileInfo); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 355c76f9c64..9f0d48ff011 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -210,6 +210,11 @@ } this.breadcrumb = new OCA.Files.BreadCrumb(breadcrumbOptions); + this._detailsView = new OCA.Files.DetailsView(); + this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView()); + this.$el.append(this._detailsView.$el); + this._detailsView.$el.addClass('disappear'); + this.$el.find('#controls').prepend(this.breadcrumb.$el); this.$el.find('thead th .columntitle').click(_.bind(this._onClickHeader, this)); @@ -224,7 +229,7 @@ this.$el.on('click', function(event) { var $target = $(event.target); // click outside file row ? - if (!$target.closest('tbody').length) { + if (!$target.closest('tbody').length && !$target.closest('.detailsView').length) { self._updateDetailsView(null); } }); @@ -282,32 +287,16 @@ _updateDetailsView: function(fileInfo) { var self = this; if (!fileInfo) { - if (this._detailsView) { - // hide it - this._detailsView.$el.addClass('disappear'); - this._detailsView.setFileInfo(null); - } + this._detailsView.$el.addClass('disappear'); + this._detailsView.setFileInfo(null); return; } - if (!this._detailsView) { - this._detailsView = new OCA.Files.DetailsView(); - this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView()); - _.each(this._detailFileInfoViews, function(view) { - self._detailsView.addDetailView(view); - }); - _.each(this._tabViews, function(view) { - self._detailsView.addTabView(view); - }); - this.$el.append(this._detailsView.$el); - this._detailsView.$el.addClass('disappear'); - this._detailsView.render(); - } this._detailsView.setFileInfo(_.extend({ path: this.getCurrentDirectory() }, fileInfo)); _.defer(function() { - self._detailsView.$el.removeClass('disappear'); + self._detailsView.$el.removeClass('disappear hidden'); }); }, @@ -364,6 +353,12 @@ delete this._selectedFiles[$tr.data('id')]; this._selectionSummary.remove(data); } + if (this._selectionSummary.getTotal() === 1) { + this._updateDetailsView(_.values(this._selectedFiles)[0]); + } else { + // show nothing when multiple files are selected + this._updateDetailsView(null); + } this.$el.find('.select-all').prop('checked', this._selectionSummary.getTotal() === this.files.length); }, @@ -2233,6 +2228,20 @@ } }); + }, + + /** + * Register a tab view to be added to all views + */ + registerTabView: function(tabView) { + this._detailsView.addTabView(tabView); + }, + + /** + * Register a detail view to be added to all views + */ + registerDetailView: function(detailView) { + this._detailsView.addDetailView(detailView); } }; @@ -2284,34 +2293,6 @@ } }; - /** - * Globally registered tab views - * - * @type OCA.Files.DetailTabView - */ - FileList.prototype._tabViews = []; - - /** - * Globally registered detail views - * - * @type OCA.Files.DetailFileInfoView - */ - FileList.prototype._detailFileInfoViews = []; - - /** - * Register a tab view to be added to all views - */ - FileList.prototype.registerTabView = function(tabView) { - this._tabViews.push(tabView); - }; - - /** - * Register a detail view to be added to all views - */ - FileList.prototype.registerDetailView = function(detailView) { - this._detailFileInfoViews.push(detailView); - }; - /** * File info attributes. * diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js index 5eb74145f12..88463bda9e6 100644 --- a/apps/files/js/mainfileinfodetailview.js +++ b/apps/files/js/mainfileinfodetailview.js @@ -11,7 +11,14 @@ (function() { var TEMPLATE = '
    {{name}}
    ' + - '
    {{size}}, {{date}}
    '; + '
    ' + + ' ' + + ' ' + + ' ' + + ' {{size}}, {{date}}' + + '
    '; /** * @class OCA.Files.MainFileInfoDetailView @@ -48,6 +55,7 @@ } if (this._fileInfo) { + var isFavorite = (this._fileInfo.tags || []).indexOf(OC.TAG_FAVORITE) >= 0 this.$el.append(this._template({ nameLabel: t('files', 'Name'), name: this._fileInfo.name, @@ -56,10 +64,13 @@ sizeLabel: t('files', 'Size'), // TODO: refactor and use size formatter size: OC.Util.humanFileSize(this._fileInfo.size, true), - altSize: this._fileInfo.size, + altSize: n('files', '%n byte', '%n bytes', this._fileInfo.size), dateLabel: t('files', 'Modified'), altDate: OC.Util.formatDate(this._fileInfo.mtime), - date: OC.Util.relativeModifiedDate(this._fileInfo.mtime) + date: OC.Util.relativeModifiedDate(this._fileInfo.mtime), + isFavorite: isFavorite, + starAltText: isFavorite ? t('files', 'Favorited') : t('files', 'Favorite'), + starIcon: OC.imagePath('core', isFavorite ? 'actions/starred' : 'actions/star') })); var $iconDiv = this.$el.find('.thumbnail'); @@ -78,8 +89,7 @@ // TODO: special icons / shared / external $iconDiv.css('background-image', 'url("' + OC.MimeType.getIconUrl('dir') + '")'); } - } else { - // TODO: render placeholder text? + this.$el.find('[title]').tipsy(); } } }); diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 227eec79578..12bec0e8c9a 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -144,9 +144,6 @@ OC.addScript('files_sharing', 'sharetabview').done(function() { fileList.registerTabView(new OCA.Sharing.ShareTabView('shareTabView')); }); - OC.addScript('files_sharing', 'sharedetailview').done(function() { - fileList.registerDetailView(new OCA.Sharing.ShareDetailView()); - }); }, /** diff --git a/apps/files_sharing/js/sharedetailview.js b/apps/files_sharing/js/sharedetailview.js deleted file mode 100644 index a18a6d19d15..00000000000 --- a/apps/files_sharing/js/sharedetailview.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2015 - * - * This file is licensed under the Affero General Public License version 3 - * or later. - * - * See the COPYING-README file. - * - */ - -(function() { - var TEMPLATE = - '
      ' + - '
    • Owner: {{owner}}
    • ' + - '
    '; - - /** - * @class OCA.Files.MainFileInfoDetailView - * @classdesc - * - * Displays main details about a file - * - */ - var ShareDetailView = function() { - this.initialize(); - }; - /** - * @memberof OCA.Sharing - */ - ShareDetailView.prototype = _.extend({}, OCA.Files.DetailFileInfoView.prototype, - /** @lends OCA.Sharing.ShareDetailView.prototype */ { - _template: null, - - /** - * Initialize the details view - */ - initialize: function() { - this.$el = $('
    '); - }, - - /** - * Renders this details view - */ - render: function() { - this.$el.empty(); - - if (!this._template) { - this._template = Handlebars.compile(TEMPLATE); - } - - if (this._fileInfo) { - this.$el.append(this._template({ - owner: this._fileInfo.shareOwner || OC.currentUser - })); - } else { - // TODO: render placeholder text? - } - } - }); - - OCA.Sharing.ShareDetailView = ShareDetailView; -})(); - diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js index 48dfcae16d8..42bddc20b3c 100644 --- a/apps/files_sharing/js/sharetabview.js +++ b/apps/files_sharing/js/sharetabview.js @@ -10,7 +10,7 @@ (function() { var TEMPLATE = - '
    TODO: here comes the share dialog
    '; + '
    Owner: {{owner}}'; /** * @class OCA.Sharing.ShareTabView @@ -52,7 +52,9 @@ } if (this._fileInfo) { - this.$el.append(this._template()); + this.$el.append(this._template({ + owner: this._fileInfo.shareOwner || OC.currentUser + })); } else { // TODO: render placeholder text? } From ad90ba27cdcdcd60e04ee0b781c6698124e900fa Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 15 Jul 2015 17:35:35 +0200 Subject: [PATCH 04/25] Styling fixes + hightlight in files sidebar Sidebar scrolls with contents Highlight currently show file --- apps/files/css/detailsView.css | 6 +++--- apps/files/css/files.css | 1 + apps/files/js/detailsview.js | 1 + apps/files/js/filelist.js | 7 +++++++ apps/files_sharing/appinfo/app.php | 1 + apps/files_sharing/css/sharetabview.css | 3 +++ apps/files_sharing/js/sharetabview.js | 1 + 7 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 apps/files_sharing/css/sharetabview.css diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css index a62bcd8c968..5b9b51a16d3 100644 --- a/apps/files/css/detailsView.css +++ b/apps/files/css/detailsView.css @@ -8,9 +8,9 @@ position: absolute; width: 350px; top: 0; - bottom: 0; right: 0; left: auto; + min-height: 100%; background-color: white; -webkit-transition: margin-right 300ms; -moz-transition: margin-right 300ms; @@ -28,11 +28,11 @@ } .app-files .detailsView .detailFileInfoContainer { - min-height: 200px; + min-height: 80px; padding: 10px; } -.app-files .detailsView .detailFileInfoContainer > div{ +.app-files .detailsView .detailFileInfoContainer > div { clear: both; margin-left: 5px; } diff --git a/apps/files/css/files.css b/apps/files/css/files.css index f2f2c5ac3bc..7375baeac30 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -143,6 +143,7 @@ #filestable tbody tr:active { background-color: rgb(240,240,240); } +#filestable tbody tr.highlighted, #filestable tbody tr.selected { background-color: rgb(230,230,230); } diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index d69d8c09c16..feced7e34dd 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -153,6 +153,7 @@ setFileInfo: function(fileInfo) { this._fileInfo = fileInfo; + // FIXME: this will render panels twice this.render(); // notify all panels diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 9f0d48ff011..5c7fcb6b998 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -286,12 +286,19 @@ */ _updateDetailsView: function(fileInfo) { var self = this; + var oldFileInfo = this._detailsView.getFileInfo(); + if (oldFileInfo) { + // TODO: use more efficient way, maybe track the highlight + this.$fileList.children().filterAttr('data-id', '' + oldFileInfo.id).removeClass('highlighted'); + } + if (!fileInfo) { this._detailsView.$el.addClass('disappear'); this._detailsView.setFileInfo(null); return; } + this.$fileList.children().filterAttr('data-id', '' + fileInfo.id).addClass('highlighted'); this._detailsView.setFileInfo(_.extend({ path: this.getCurrentDirectory() }, fileInfo)); diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index f72f5024622..9000fafd8dd 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -56,6 +56,7 @@ $application->setupPropagation(); \OCP\Util::addScript('files_sharing', 'share'); \OCP\Util::addScript('files_sharing', 'external'); +\OCP\Util::addStyle('files_sharing', 'sharetabview'); // FIXME: registering a job here will cause additional useless SQL queries // when the route is not cron.php, needs a better way diff --git a/apps/files_sharing/css/sharetabview.css b/apps/files_sharing/css/sharetabview.css new file mode 100644 index 00000000000..a8ad1ceaba7 --- /dev/null +++ b/apps/files_sharing/css/sharetabview.css @@ -0,0 +1,3 @@ +.app-files .shareTabView { + min-height: 200px; +} diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js index 42bddc20b3c..e02de923751 100644 --- a/apps/files_sharing/js/sharetabview.js +++ b/apps/files_sharing/js/sharetabview.js @@ -55,6 +55,7 @@ this.$el.append(this._template({ owner: this._fileInfo.shareOwner || OC.currentUser })); + } else { // TODO: render placeholder text? } From d0f73d78f3de2546de80246c8343f6b64e37e5d7 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 16 Jul 2015 12:21:24 +0200 Subject: [PATCH 05/25] Remove horizontal scroll caused by right sidebar --- apps/files/css/files.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 7375baeac30..7e3318a962b 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -103,6 +103,10 @@ min-height: 100%; } +.app-files #app-content { + overflow-x: hidden; +} + /* icons for sidebar */ .nav-icon-files { background-image: url('../img/folder.svg'); From deb9d4d7788948db627c4b18cd646e39e7190827 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 16 Jul 2015 12:21:40 +0200 Subject: [PATCH 06/25] Right sidebar now has its own scrollbar --- apps/files/css/detailsView.css | 12 +++++++----- apps/files/js/detailsview.js | 17 +++++++++++++++++ apps/files/js/filelist.js | 1 + 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css index 5b9b51a16d3..8d412a83667 100644 --- a/apps/files/css/detailsView.css +++ b/apps/files/css/detailsView.css @@ -1,21 +1,23 @@ .app-files .detailsView.disappear { - margin-right: -350px; + margin-right: -359px; } .app-files .detailsView { - position: absolute; + position: fixed; width: 350px; - top: 0; - right: 0; + top: 44px; /* main header's height */ + right: 9px; left: auto; - min-height: 100%; + bottom: 0; background-color: white; -webkit-transition: margin-right 300ms; -moz-transition: margin-right 300ms; -o-transition: margin-right 300ms; transition: margin-right 300ms; + overflow-x: hidden; + overflow-y: auto; } .app-files .detailsView { diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index feced7e34dd..c28de71ab7d 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -83,6 +83,8 @@ self.$el.addClass('disappear'); event.preventDefault(); }); + + this._addTestTabs(); }, /** @@ -94,6 +96,21 @@ } }, + _addTestTabs: function() { + for (var j = 0; j < 2; j++) { + var testView = new OCA.Files.DetailTabView('testtab' + j); + testView.index = j; + testView.getLabel = function() { return 'Test tab ' + this.index; }; + testView.render = function() { + this.$el.empty(); + for (var i = 0; i < 100; i++) { + this.$el.append('
    Test tab ' + this.index + ' row ' + i + '
    '); + } + }; + this._tabViews.push(testView); + } + }, + /** * Renders this details view */ diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 5c7fcb6b998..796b14199d8 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -302,6 +302,7 @@ this._detailsView.setFileInfo(_.extend({ path: this.getCurrentDirectory() }, fileInfo)); + this._detailsView.$el.scrollTop(0); _.defer(function() { self._detailsView.$el.removeClass('disappear hidden'); }); From b6192db61076b04005388b889d6ec666f3e87687 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 16 Jul 2015 12:28:19 +0200 Subject: [PATCH 07/25] Disable right sidebar in public file list page --- apps/files/js/filelist.js | 17 ++++++++++++----- apps/files_sharing/js/public.js | 3 ++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 796b14199d8..e228b8e852b 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -23,6 +23,7 @@ * @param [options.scrollContainer] scrollable container, defaults to $(window) * @param [options.dragOptions] drag options, disabled by default * @param [options.folderDropOptions] folder drop options, disabled by default + * @param [options.detailsViewEnabled=true] whether to enable details view */ var FileList = function($el, options) { this.initialize($el, options); @@ -210,10 +211,12 @@ } this.breadcrumb = new OCA.Files.BreadCrumb(breadcrumbOptions); - this._detailsView = new OCA.Files.DetailsView(); - this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView()); - this.$el.append(this._detailsView.$el); - this._detailsView.$el.addClass('disappear'); + if (_.isUndefined(options.detailsViewEnabled) || options.detailsViewEnabled) { + this._detailsView = new OCA.Files.DetailsView(); + this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView()); + this.$el.append(this._detailsView.$el); + this._detailsView.$el.addClass('disappear'); + } this.$el.find('#controls').prepend(this.breadcrumb.$el); @@ -285,6 +288,10 @@ * @param {OCA.Files.FileInfo} fileInfo file info to display */ _updateDetailsView: function(fileInfo) { + if (!this._detailsView) { + return; + } + var self = this; var oldFileInfo = this._detailsView.getFileInfo(); if (oldFileInfo) { @@ -403,7 +410,7 @@ this.updateSelectionSummary(); } else { // clicked directly on the name - if ($(event.target).is('.nametext') || $(event.target).closest('.nametext').length) { + if (!this._detailsView || $(event.target).is('.nametext') || $(event.target).closest('.nametext').length) { var filename = $tr.attr('data-file'); var renaming = $tr.data('renaming'); if (!renaming) { diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 5923e426f05..1993efe7d73 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -57,7 +57,8 @@ OCA.Sharing.PublicApp = { scrollContainer: $(window), dragOptions: dragOptions, folderDropOptions: folderDropOptions, - fileActions: fileActions + fileActions: fileActions, + detailsViewEnabled: false } ); this.files = OCA.Files.Files; From f11946c8c0c41e87e17c50b5e3691ece11a09ae4 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 16 Jul 2015 12:49:34 +0200 Subject: [PATCH 08/25] Improve layout, fix mime icon --- apps/files/css/detailsView.css | 20 +++++++++++++++----- apps/files/js/detailsview.js | 1 + apps/files/js/filelist.js | 6 ++++++ apps/files/js/mainfileinfodetailview.js | 25 +++++++++++++------------ 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css index 8d412a83667..51aaf393751 100644 --- a/apps/files/css/detailsView.css +++ b/apps/files/css/detailsView.css @@ -30,26 +30,36 @@ } .app-files .detailsView .detailFileInfoContainer { - min-height: 80px; - padding: 10px; + min-height: 60px; + padding: 15px; } .app-files .detailsView .detailFileInfoContainer > div { clear: both; - margin-left: 5px; +} + +.app-files .detailsView .mainFileInfoView { + margin-right: 20px; /* accomodate for close icon */ } .app-files .detailsView .thumbnail { width: 50px; height: 50px; float: left; - margin: 5px; + margin-right: 10px; background-size: 50px; } +.app-files .detailsView .ellipsis { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + .app-files .detailsView .fileName { font-weight: bold; font-size: 17px; + overflow: hidden; } .app-files .detailsView .detailList { @@ -60,5 +70,5 @@ position: absolute; top: 0; right: 0; - margin: 10px; + margin: 15px; } diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index c28de71ab7d..2142c9824f9 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -84,6 +84,7 @@ event.preventDefault(); }); + // uncomment to add some dummy tabs for testing this._addTestTabs(); }, diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index e228b8e852b..a03c4e1754f 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -1305,6 +1305,12 @@ ready(iconURL); // set mimeicon URL urlSpec.file = OCA.Files.Files.fixPath(path); + if (options.x) { + urlSpec.x = options.x; + } + if (options.y) { + urlSpec.y = options.y; + } if (etag){ // use etag as cache buster diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js index 88463bda9e6..4707e3e9f0d 100644 --- a/apps/files/js/mainfileinfodetailview.js +++ b/apps/files/js/mainfileinfodetailview.js @@ -10,8 +10,8 @@ (function() { var TEMPLATE = - '
    {{name}}
    ' + - '
    ' + + '
    {{name}}
    ' + + '
    ' + ' ' + @@ -55,14 +55,13 @@ } if (this._fileInfo) { - var isFavorite = (this._fileInfo.tags || []).indexOf(OC.TAG_FAVORITE) >= 0 + var isFavorite = (this._fileInfo.tags || []).indexOf(OC.TAG_FAVORITE) >= 0; this.$el.append(this._template({ nameLabel: t('files', 'Name'), name: this._fileInfo.name, pathLabel: t('files', 'Path'), path: this._fileInfo.path, sizeLabel: t('files', 'Size'), - // TODO: refactor and use size formatter size: OC.Util.humanFileSize(this._fileInfo.size, true), altSize: n('files', '%n byte', '%n bytes', this._fileInfo.size), dateLabel: t('files', 'Modified'), @@ -73,18 +72,20 @@ starIcon: OC.imagePath('core', isFavorite ? 'actions/starred' : 'actions/star') })); - var $iconDiv = this.$el.find('.thumbnail'); // TODO: we really need OC.Previews + var $iconDiv = this.$el.find('.thumbnail'); if (this._fileInfo.mimetype !== 'httpd/unix-directory') { - // FIXME: use proper way, this is only for demo purposes - var previewUrl = FileList.generatePreviewUrl({ - file: this._fileInfo.path + '/' + this._fileInfo.name, - c: this._fileInfo.etag, + // TODO: inject utility class? + FileList.lazyLoadPreview({ + path: this._fileInfo.path + '/' + this._fileInfo.name, + mime: this._fileInfo.mimetype, + etag: this._fileInfo.etag, x: 50, - y: 50 + y: 50, + callback: function(previewUrl) { + $iconDiv.css('background-image', 'url("' + previewUrl + '")'); + } }); - previewUrl = previewUrl.replace('(', '%28').replace(')', '%29'); - $iconDiv.css('background-image', 'url("' + previewUrl + '")'); } else { // TODO: special icons / shared / external $iconDiv.css('background-image', 'url("' + OC.MimeType.getIconUrl('dir') + '")'); From 3e44ca6dc24d5ffc4cb678532a5afa3b026780ce Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 17 Jul 2015 14:09:00 +0200 Subject: [PATCH 09/25] Added unit tests for right sidebar --- apps/files/js/detailsview.js | 2 +- apps/files/js/mainfileinfodetailview.js | 5 +- apps/files/tests/js/detailsviewSpec.js | 95 +++++++++++++++++++ apps/files/tests/js/favoritespluginspec.js | 2 +- apps/files/tests/js/filelistSpec.js | 46 ++++++++- .../tests/js/mainfileinfodetailviewSpec.js | 77 +++++++++++++++ apps/files_sharing/tests/js/appSpec.js | 2 +- 7 files changed, 222 insertions(+), 7 deletions(-) create mode 100644 apps/files/tests/js/detailsviewSpec.js create mode 100644 apps/files/tests/js/mainfileinfodetailviewSpec.js diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index 2142c9824f9..18cefef45d9 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -85,7 +85,7 @@ }); // uncomment to add some dummy tabs for testing - this._addTestTabs(); + // this._addTestTabs(); }, /** diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js index 4707e3e9f0d..245653d8e1e 100644 --- a/apps/files/js/mainfileinfodetailview.js +++ b/apps/files/js/mainfileinfodetailview.js @@ -14,10 +14,10 @@ '
    ' + ' ' + + ' class="action action-favorite favorite">' + ' ' + ' ' + - ' {{size}}, {{date}}' + + ' {{size}}, {{date}}' + '
    '; /** @@ -67,7 +67,6 @@ dateLabel: t('files', 'Modified'), altDate: OC.Util.formatDate(this._fileInfo.mtime), date: OC.Util.relativeModifiedDate(this._fileInfo.mtime), - isFavorite: isFavorite, starAltText: isFavorite ? t('files', 'Favorited') : t('files', 'Favorite'), starIcon: OC.imagePath('core', isFavorite ? 'actions/starred' : 'actions/star') })); diff --git a/apps/files/tests/js/detailsviewSpec.js b/apps/files/tests/js/detailsviewSpec.js new file mode 100644 index 00000000000..b00c15032b0 --- /dev/null +++ b/apps/files/tests/js/detailsviewSpec.js @@ -0,0 +1,95 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2015 Vincent Petry +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see . +* +*/ + +describe('OCA.Files.DetailsView tests', function() { + var detailsView; + + beforeEach(function() { + detailsView = new OCA.Files.DetailsView(); + }); + afterEach(function() { + detailsView.destroy(); + detailsView = undefined; + }); + it('renders itself empty when nothing registered', function() { + detailsView.render(); + expect(detailsView.$el.find('.detailFileInfoContainer').length).toEqual(1); + expect(detailsView.$el.find('.tabsContainer').length).toEqual(1); + }); + describe('file info detail view', function() { + it('renders registered view', function() { + var testView = new OCA.Files.DetailFileInfoView(); + var testView2 = new OCA.Files.DetailFileInfoView(); + detailsView.addDetailView(testView); + detailsView.addDetailView(testView2); + detailsView.render(); + + expect(detailsView.$el.find('.detailFileInfoContainer .detailFileInfoView').length).toEqual(2); + }); + it('updates registered tabs when fileinfo is updated', function() { + var viewRenderStub = sinon.stub(OCA.Files.DetailFileInfoView.prototype, 'render'); + var testView = new OCA.Files.DetailFileInfoView(); + var testView2 = new OCA.Files.DetailFileInfoView(); + detailsView.addDetailView(testView); + detailsView.addDetailView(testView2); + detailsView.render(); + + var fileInfo = {id: 5, name: 'test.txt'}; + viewRenderStub.reset(); + detailsView.setFileInfo(fileInfo); + + expect(testView.getFileInfo()).toEqual(fileInfo); + expect(testView2.getFileInfo()).toEqual(fileInfo); + + expect(viewRenderStub.callCount).toEqual(2); + viewRenderStub.restore(); + }); + }); + describe('tabs', function() { + it('renders registered tabs', function() { + var testView = new OCA.Files.DetailTabView('test1'); + var testView2 = new OCA.Files.DetailTabView('test2'); + detailsView.addTabView(testView); + detailsView.addTabView(testView2); + detailsView.render(); + + expect(detailsView.$el.find('.tabsContainer .detailTabView').length).toEqual(2); + }); + it('updates registered tabs when fileinfo is updated', function() { + var tabRenderStub = sinon.stub(OCA.Files.DetailTabView.prototype, 'render'); + var testView = new OCA.Files.DetailTabView('test1'); + var testView2 = new OCA.Files.DetailTabView('test2'); + detailsView.addTabView(testView); + detailsView.addTabView(testView2); + detailsView.render(); + + var fileInfo = {id: 5, name: 'test.txt'}; + tabRenderStub.reset(); + detailsView.setFileInfo(fileInfo); + + expect(testView.getFileInfo()).toEqual(fileInfo); + expect(testView2.getFileInfo()).toEqual(fileInfo); + + expect(tabRenderStub.callCount).toEqual(2); + tabRenderStub.restore(); + }); + }); +}); diff --git a/apps/files/tests/js/favoritespluginspec.js b/apps/files/tests/js/favoritespluginspec.js index 90b40ede74b..1b144c28707 100644 --- a/apps/files/tests/js/favoritespluginspec.js +++ b/apps/files/tests/js/favoritespluginspec.js @@ -113,7 +113,7 @@ describe('OCA.Files.FavoritesPlugin tests', function() { shareOwner: 'user2' }]); - fileList.findFileEl('testdir').find('td a.name').click(); + fileList.findFileEl('testdir').find('td .nametext').click(); expect(OCA.Files.App.fileList.getCurrentDirectory()).toEqual('/somewhere/inside/subdir/testdir'); diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index 09d698088ae..5c0c8c96bc5 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -1870,6 +1870,50 @@ describe('OCA.Files.FileList tests', function() { }); }) }); + describe('Details sidebar', function() { + beforeEach(function() { + fileList.setFiles(testFiles); + }); + it('Clicking on a file row will trigger file action if no details view configured', function() { + fileList._detailsView = null; + var updateDetailsViewStub = sinon.stub(fileList, '_updateDetailsView'); + var actionStub = sinon.stub(); + fileList.setFiles(testFiles); + fileList.fileActions.register( + 'text/plain', + 'Test', + OC.PERMISSION_ALL, + function() { + // Specify icon for hitory button + return OC.imagePath('core','actions/history'); + }, + actionStub + ); + fileList.fileActions.setDefault('text/plain', 'Test'); + var $tr = fileList.findFileEl('One.txt'); + $tr.find('td.filename>a.name').click(); + expect(actionStub.calledOnce).toEqual(true); + expect(updateDetailsViewStub.notCalled).toEqual(true); + updateDetailsViewStub.restore(); + }); + it('Clicking on a file row will trigger details sidebar', function() { + fileList.fileActions.setDefault('text/plain', 'Test'); + var $tr = fileList.findFileEl('One.txt'); + $tr.find('td.filename>a.name').click(); + expect($tr.hasClass('highlighted')).toEqual(true); + + expect(fileList._detailsView.getFileInfo().id).toEqual(1); + }); + it('Clicking outside to deselect a file row will trigger details sidebar', function() { + var $tr = fileList.findFileEl('One.txt'); + $tr.find('td.filename>a.name').click(); + + fileList.$el.find('tfoot').click(); + + expect($tr.hasClass('highlighted')).toEqual(false); + expect(fileList._detailsView.getFileInfo()).toEqual(null); + }); + }); describe('File actions', function() { it('Clicking on a file name will trigger default action', function() { var actionStub = sinon.stub(); @@ -1886,7 +1930,7 @@ describe('OCA.Files.FileList tests', function() { ); fileList.fileActions.setDefault('text/plain', 'Test'); var $tr = fileList.findFileEl('One.txt'); - $tr.find('td.filename>a.name').click(); + $tr.find('td.filename .nametext').click(); expect(actionStub.calledOnce).toEqual(true); expect(actionStub.getCall(0).args[0]).toEqual('One.txt'); var context = actionStub.getCall(0).args[1]; diff --git a/apps/files/tests/js/mainfileinfodetailviewSpec.js b/apps/files/tests/js/mainfileinfodetailviewSpec.js new file mode 100644 index 00000000000..e4b3adc578e --- /dev/null +++ b/apps/files/tests/js/mainfileinfodetailviewSpec.js @@ -0,0 +1,77 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2015 Vincent Petry +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see . +* +*/ + +describe('OCA.Files.MainFileInfoDetailView tests', function() { + var view, tipsyStub; + + beforeEach(function() { + tipsyStub = sinon.stub($.fn, 'tipsy'); + view = new OCA.Files.MainFileInfoDetailView(); + }); + afterEach(function() { + view.destroy(); + view = undefined; + tipsyStub.restore(); + }); + describe('rendering', function() { + var testFileInfo; + beforeEach(function() { + view = new OCA.Files.MainFileInfoDetailView(); + testFileInfo = { + id: 5, + name: 'One.txt', + path: '/subdir', + size: 123456789, + dateLabel: new Date(Date.UTC(2015, 6, 17, 1, 2, 3, 4)) + }; + }); + it('displays basic info', function() { + var clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 0)); + view.setFileInfo(testFileInfo); + expect(view.$el.find('.fileName').text()).toEqual('One.txt'); + expect(view.$el.find('.fileName').attr('title')).toEqual('One.txt'); + expect(view.$el.find('.size').text()).toEqual('117.7 MB'); + expect(view.$el.find('.size').attr('title')).toEqual('123456789 bytes'); + expect(view.$el.find('.date').text()).toEqual('a few seconds ago'); + expect(view.$el.find('.date').attr('title')).toEqual('July 17, 2015 3:02 AM'); + clock.restore(); + }); + it('displays favorite icon', function() { + view.setFileInfo(_.extend(testFileInfo, { + tags: [OC.FAVORITE] + })); + expect(OC.TestUtil.getImageUrl(view.$el.find('.favorite img'))) + .toEqual(OC.imagePath('core', 'actions/starred')); + + view.setFileInfo(_.extend(testFileInfo, { + tags: [] + })); + expect(OC.TestUtil.getImageUrl(view.$el.find('.favorite img'))) + .toEqual(OC.imagePath('core', 'actions/star')); + }); + it('displays mime icon', function() { + // TODO + }); + it('displays thumbnail', function() { + // TODO + }); + }); +}); diff --git a/apps/files_sharing/tests/js/appSpec.js b/apps/files_sharing/tests/js/appSpec.js index 49bca568001..133bd44f750 100644 --- a/apps/files_sharing/tests/js/appSpec.js +++ b/apps/files_sharing/tests/js/appSpec.js @@ -132,7 +132,7 @@ describe('OCA.Sharing.App tests', function() { shareOwner: 'user2' }]); - fileListIn.findFileEl('testdir').find('td a.name').click(); + fileListIn.findFileEl('testdir').find('td .nametext').click(); expect(OCA.Files.App.fileList.getCurrentDirectory()).toEqual('/somewhere/inside/subdir/testdir'); From e0bcd56402b3e3ac43ebbce62c18b34faef0970e Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 17 Jul 2015 15:19:11 +0200 Subject: [PATCH 10/25] More work on right sidebar unit tests --- apps/files/tests/js/mainfileinfodetailviewSpec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/files/tests/js/mainfileinfodetailviewSpec.js b/apps/files/tests/js/mainfileinfodetailviewSpec.js index e4b3adc578e..ed7c6401a77 100644 --- a/apps/files/tests/js/mainfileinfodetailviewSpec.js +++ b/apps/files/tests/js/mainfileinfodetailviewSpec.js @@ -56,15 +56,15 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() { }); it('displays favorite icon', function() { view.setFileInfo(_.extend(testFileInfo, { - tags: [OC.FAVORITE] + tags: [OC.TAG_FAVORITE] })); - expect(OC.TestUtil.getImageUrl(view.$el.find('.favorite img'))) + expect(view.$el.find('.favorite img').attr('src')) .toEqual(OC.imagePath('core', 'actions/starred')); view.setFileInfo(_.extend(testFileInfo, { tags: [] })); - expect(OC.TestUtil.getImageUrl(view.$el.find('.favorite img'))) + expect(view.$el.find('.favorite img').attr('src')) .toEqual(OC.imagePath('core', 'actions/star')); }); it('displays mime icon', function() { From 7a982872e7310ffff979a2842ada88db1f832680 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Mon, 20 Jul 2015 14:42:10 +0200 Subject: [PATCH 11/25] Main view does not commands detail and tab views to render. Fixes JS tests. For a predictive behaviour we need to determine who is allowed to call render methods on the views. Either, the main view is solely allowed to call render and views do not do anything about output until then. Or, the main view relies on the concrete views to be ready when things are about to be shown. The latter approach has the advantage that concrete views know when they have to update themselves (e.g. new data arrives or information change), but the main view has now idea of the inner workings. --- apps/files/js/detailfileinfoview.js | 9 +++++++++ apps/files/js/detailsview.js | 7 ++----- apps/files/js/detailtabview.js | 9 +++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/apps/files/js/detailfileinfoview.js b/apps/files/js/detailfileinfoview.js index 9585f57f1ef..9a88b5e2d8a 100644 --- a/apps/files/js/detailfileinfoview.js +++ b/apps/files/js/detailfileinfoview.js @@ -44,6 +44,15 @@ this.$el = $('
    '); }, + /** + * returns the jQuery object for HTML output + * + * @returns {jQuery} + */ + get$: function() { + return this.$el; + }, + /** * Destroy / uninitialize this instance. */ diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index 18cefef45d9..d4495cd8dab 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -136,16 +136,14 @@ // render details _.each(this._detailFileInfoViews, function(detailView) { - detailView.render(); - $detailsContainer.append(detailView.$el); + $detailsContainer.append(detailView.get$()); }); if (this._tabViews.length > 0) { // render tabs _.each(this._tabViews, function(tabView) { - tabView.render(); // hidden by default - $tabsContainer.append(tabView.$el); + $tabsContainer.append(tabView.get$()); $tabHeadsContainer.append(self._templateTabHeader({ tabId: tabView.getId(), @@ -171,7 +169,6 @@ setFileInfo: function(fileInfo) { this._fileInfo = fileInfo; - // FIXME: this will render panels twice this.render(); // notify all panels diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js index 767ece2297c..7e0f9eff16f 100644 --- a/apps/files/js/detailtabview.js +++ b/apps/files/js/detailtabview.js @@ -90,6 +90,15 @@ return 'Tab ' + this._id; }, + /** + * returns the jQuery object for HTML output + * + * @returns {jQuery} + */ + get$: function() { + return this.$el; + }, + /** * Renders this details view * From 734c6b0b6f10cb8cb39773e1c93088c24c9f50af Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 22 Jul 2015 20:15:12 +0200 Subject: [PATCH 12/25] extend MainFileInfoDetailView JS tests --- .../tests/js/mainfileinfodetailviewSpec.js | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/files/tests/js/mainfileinfodetailviewSpec.js b/apps/files/tests/js/mainfileinfodetailviewSpec.js index ed7c6401a77..2dec4ea8a1a 100644 --- a/apps/files/tests/js/mainfileinfodetailviewSpec.js +++ b/apps/files/tests/js/mainfileinfodetailviewSpec.js @@ -20,16 +20,19 @@ */ describe('OCA.Files.MainFileInfoDetailView tests', function() { - var view, tipsyStub; + var view, tipsyStub, previewStub, fncLazyLoadPreview, fileListMock; beforeEach(function() { tipsyStub = sinon.stub($.fn, 'tipsy'); + fileListMock = sinon.mock(OCA.Files.FileList.prototype); view = new OCA.Files.MainFileInfoDetailView(); }); afterEach(function() { view.destroy(); view = undefined; tipsyStub.restore(); + fileListMock.restore(); + }); describe('rendering', function() { var testFileInfo; @@ -68,10 +71,33 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() { .toEqual(OC.imagePath('core', 'actions/star')); }); it('displays mime icon', function() { - // TODO + // File + view.setFileInfo(_.extend(testFileInfo, { + mimetype: 'text/calendar' + })); + + expect(view.$el.find('.thumbnail').css('background-image')) + .toContain('filetypes/text-calendar.svg'); + + // Folder + view.setFileInfo(_.extend(testFileInfo, { + mimetype: 'httpd/unix-directory' + })); + + expect(view.$el.find('.thumbnail').css('background-image')) + .toContain('filetypes/folder.svg'); }); it('displays thumbnail', function() { - // TODO + view.setFileInfo(_.extend(testFileInfo, { + mimetype: 'text/plain' + })); + + var expectation = fileListMock.expects('lazyLoadPreview'); + expectation.once(); + + view.setFileInfo(testFileInfo); + + fileListMock.verify(); }); }); }); From f508cf40da93eba05a17b4ffa834a125dbdb2bcb Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 23 Jul 2015 18:29:45 +0200 Subject: [PATCH 13/25] sidebar shall not overlap but shrink the contnet --- apps/files/css/detailsView.css | 13 +++++++++---- apps/files/js/detailsview.js | 4 +++- apps/files/js/filelist.js | 4 +++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css index 51aaf393751..ec0d5ac2110 100644 --- a/apps/files/css/detailsView.css +++ b/apps/files/css/detailsView.css @@ -1,14 +1,18 @@ .app-files .detailsView.disappear { - margin-right: -359px; + visibility: hidden; +} + +.with-sidebar { + width: 80%; } .app-files .detailsView { - position: fixed; - width: 350px; + position: absolute; + width: 20%; top: 44px; /* main header's height */ - right: 9px; + right: 0; left: auto; bottom: 0; background-color: white; @@ -18,6 +22,7 @@ transition: margin-right 300ms; overflow-x: hidden; overflow-y: auto; + visibility: visible; } .app-files .detailsView { diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index d4495cd8dab..0cf4ade0767 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -80,7 +80,9 @@ this._detailFileInfoViews = []; this.$el.on('click', 'a.close', function(event) { - self.$el.addClass('disappear'); + self.$el + .addClass('disappear') + .siblings('.with-sidebar').removeClass('with-sidebar'); event.preventDefault(); }); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index a03c4e1754f..7e94d5c3d4e 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -214,7 +214,7 @@ if (_.isUndefined(options.detailsViewEnabled) || options.detailsViewEnabled) { this._detailsView = new OCA.Files.DetailsView(); this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView()); - this.$el.append(this._detailsView.$el); + this._detailsView.$el.insertAfter(this.$el); this._detailsView.$el.addClass('disappear'); } @@ -301,6 +301,7 @@ if (!fileInfo) { this._detailsView.$el.addClass('disappear'); + this.$el.removeClass('with-sidebar'); this._detailsView.setFileInfo(null); return; } @@ -312,6 +313,7 @@ this._detailsView.$el.scrollTop(0); _.defer(function() { self._detailsView.$el.removeClass('disappear hidden'); + self.$el.addClass('with-sidebar'); }); }, From 5af59c68712032ef387ccd4410eb4b8b04c27df9 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 24 Jul 2015 23:07:09 +0200 Subject: [PATCH 14/25] rename sidebar element and use css and js utility functions to core --- apps/files/css/detailsView.css | 52 ++++++---------------------------- apps/files/js/detailsview.js | 6 ++-- apps/files/js/filelist.js | 12 +++----- core/css/apps.css | 36 +++++++++++++++++++++++ core/css/mobile.css | 8 ++++++ core/js/apps.js | 21 ++++++++++++++ core/js/core.json | 1 + 7 files changed, 80 insertions(+), 56 deletions(-) diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css index ec0d5ac2110..e89a6a36f4c 100644 --- a/apps/files/css/detailsView.css +++ b/apps/files/css/detailsView.css @@ -1,53 +1,17 @@ - - -.app-files .detailsView.disappear { - visibility: hidden; -} - -.with-sidebar { - width: 80%; -} - -.app-files .detailsView { - position: absolute; - width: 20%; - top: 44px; /* main header's height */ - right: 0; - left: auto; - bottom: 0; - background-color: white; - -webkit-transition: margin-right 300ms; - -moz-transition: margin-right 300ms; - -o-transition: margin-right 300ms; - transition: margin-right 300ms; - overflow-x: hidden; - overflow-y: auto; - visibility: visible; -} - -.app-files .detailsView { - background: #eee; - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - box-shadow: 0 2px 3px rgba(50, 50, 50, .4); - display: block; - z-index: 500; -} - -.app-files .detailsView .detailFileInfoContainer { +#app-sidebar .detailFileInfoContainer { min-height: 60px; padding: 15px; } -.app-files .detailsView .detailFileInfoContainer > div { +#app-sidebar .detailFileInfoContainer > div { clear: both; } -.app-files .detailsView .mainFileInfoView { +#app-sidebar .mainFileInfoView { margin-right: 20px; /* accomodate for close icon */ } -.app-files .detailsView .thumbnail { +#app-sidebar .thumbnail { width: 50px; height: 50px; float: left; @@ -55,23 +19,23 @@ background-size: 50px; } -.app-files .detailsView .ellipsis { +#app-sidebar .ellipsis { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } -.app-files .detailsView .fileName { +#app-sidebar .fileName { font-weight: bold; font-size: 17px; overflow: hidden; } -.app-files .detailsView .detailList { +#app-sidebar .detailList { float: left; } -.app-files .detailsView .close { +#app-sidebar .close { position: absolute; top: 0; right: 0; diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index 0cf4ade0767..03fe736856e 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -74,15 +74,13 @@ */ initialize: function() { var self = this; - this.$el = $('
    '); + this.$el = $('
    '); this.fileInfo = null; this._tabViews = []; this._detailFileInfoViews = []; this.$el.on('click', 'a.close', function(event) { - self.$el - .addClass('disappear') - .siblings('.with-sidebar').removeClass('with-sidebar'); + OC.Apps.hideAppSidebar(); event.preventDefault(); }); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 7e94d5c3d4e..8ec7e626162 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -214,7 +214,7 @@ if (_.isUndefined(options.detailsViewEnabled) || options.detailsViewEnabled) { this._detailsView = new OCA.Files.DetailsView(); this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView()); - this._detailsView.$el.insertAfter(this.$el); + this._detailsView.$el.insertBefore(this.$el); this._detailsView.$el.addClass('disappear'); } @@ -232,7 +232,7 @@ this.$el.on('click', function(event) { var $target = $(event.target); // click outside file row ? - if (!$target.closest('tbody').length && !$target.closest('.detailsView').length) { + if (!$target.closest('tbody').length && !$target.closest('#app-sidebar').length) { self._updateDetailsView(null); } }); @@ -300,8 +300,7 @@ } if (!fileInfo) { - this._detailsView.$el.addClass('disappear'); - this.$el.removeClass('with-sidebar'); + OC.Apps.hideAppSidebar(); this._detailsView.setFileInfo(null); return; } @@ -311,10 +310,7 @@ path: this.getCurrentDirectory() }, fileInfo)); this._detailsView.$el.scrollTop(0); - _.defer(function() { - self._detailsView.$el.removeClass('disappear hidden'); - self.$el.addClass('with-sidebar'); - }); + _.defer(OC.Apps.showAppSidebar); }, /** diff --git a/core/css/apps.css b/core/css/apps.css index 57133729f15..d3b9c0e7095 100644 --- a/core/css/apps.css +++ b/core/css/apps.css @@ -417,7 +417,43 @@ min-height: 100%; } +/* APP-SIDEBAR ----------------------------------------------------------------*/ +/* + Sidebar: a sidebar to be used within #app-content + have it as first element within app-content in order to shrink other + sibling containers properly. Compare Files app for example. +*/ +#app-sidebar { + position: absolute; + top: 0; + right: 0; + left: auto; + bottom: 0; + width: 30%; + min-width: 300px; + display: block; + background: #eee; + -webkit-transition: margin-right 300ms; + -moz-transition: margin-right 300ms; + -o-transition: margin-right 300ms; + transition: margin-right 300ms; + overflow-x: hidden; + overflow-y: auto; + visibility: visible; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + box-shadow: 0 2px 3px rgba(50, 50, 50, .4); + z-index: 500; +} + +#app-sidebar ~ .with-app-sidebar { + width: 70%; +} + +#app-sidebar.disappear { + visibility: hidden; +} /* APP-SETTINGS ---------------------------------------------------------------*/ diff --git a/core/css/mobile.css b/core/css/mobile.css index 80217d7069c..e38d9756d95 100644 --- a/core/css/mobile.css +++ b/core/css/mobile.css @@ -103,6 +103,14 @@ z-index: 1000; } +#app-sidebar { + width: 250px; +} + +#app-sidebar ~ .with-app-sidebar { + width: 100%; +} + /* allow horizontal scrollbar in settings otherwise user management is not usable on mobile */ #body-settings #app-content { diff --git a/core/js/apps.js b/core/js/apps.js index 71170bbc23a..1606abf5971 100644 --- a/core/js/apps.js +++ b/core/js/apps.js @@ -20,6 +20,27 @@ } }; + /** + * Shows the #app-sidebar and add .with-app-sidebar to subsequent siblings + */ + exports.Apps.showAppSidebar = function() { + var $appSidebar = $('#app-sidebar'); + $appSidebar + .removeClass('disappear') + .find('~ .with-app-sidebar').removeClass('.with-app-sidebar'); + }; + + /** + * Shows the #app-sidebar and removes .with-app-sidebar from subsequent + * siblings + */ + exports.Apps.hideAppSidebar = function() { + var $appSidebar = $('#app-sidebar'); + $appSidebar + .addClass('disappear') + .find('~').addClass('.with-app-sidebar'); + }; + /** * Provides a way to slide down a target area through a button and slide it * up if the user clicks somewhere else. Used for the news app settings and diff --git a/core/js/core.json b/core/js/core.json index 0f052b798a9..1053debaa99 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -20,6 +20,7 @@ "oc-dialogs.js", "js.js", "l10n.js", + "apps.js", "share.js", "octemplate.js", "eventsource.js", From a81251454d81a5ea2532b66af42c835bc5566f00 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 28 Jul 2015 14:46:10 +0200 Subject: [PATCH 15/25] fix show/hide app sidebar functions --- core/js/apps.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/js/apps.js b/core/js/apps.js index 1606abf5971..5d9bbeb870a 100644 --- a/core/js/apps.js +++ b/core/js/apps.js @@ -27,7 +27,8 @@ var $appSidebar = $('#app-sidebar'); $appSidebar .removeClass('disappear') - .find('~ .with-app-sidebar').removeClass('.with-app-sidebar'); + .find('~').addClass('with-app-sidebar'); + }; /** @@ -38,7 +39,7 @@ var $appSidebar = $('#app-sidebar'); $appSidebar .addClass('disappear') - .find('~').addClass('.with-app-sidebar'); + .find('~ .with-app-sidebar').removeClass('with-app-sidebar'); }; /** From 781ba420c95576ead4e8275b05fb72ea72b0d9c6 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 29 Jul 2015 17:36:07 +0200 Subject: [PATCH 16/25] Use own tabs impl in files sidebar Also change style. --- apps/files/css/detailsView.css | 1 + apps/files/js/detailsview.js | 64 ++++++++++++++++++++------ apps/files/js/detailtabview.js | 4 +- apps/files/tests/js/detailsviewSpec.js | 32 ++++++++----- core/css/apps.css | 48 +++++++++++++++++++ 5 files changed, 123 insertions(+), 26 deletions(-) diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css index e89a6a36f4c..78d3357fbd0 100644 --- a/apps/files/css/detailsView.css +++ b/apps/files/css/detailsView.css @@ -41,3 +41,4 @@ right: 0; margin: 15px; } + diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index 03fe736856e..7b7bd013f9e 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -14,15 +14,17 @@ '
    ' + '
    ' + '
    ' + - '
    ' + - '
      ' + + '
      ' + + '
        ' + '
      ' + + '
      ' + + '
      ' + '
      ' + ' ' + '
    '; var TEMPLATE_TAB_HEADER = - '
  • {{label}}
  • '; + '
  • {{label}}
  • '; /** * @class OCA.Files.DetailsView @@ -69,11 +71,17 @@ */ _detailFileInfoViews: [], + /** + * Id of the currently selected tab + * + * @type string + */ + _currentTabId: null, + /** * Initialize the details view */ initialize: function() { - var self = this; this.$el = $('
    '); this.fileInfo = null; this._tabViews = []; @@ -84,8 +92,10 @@ event.preventDefault(); }); + this.$el.on('click', '.tabHeaders .tabHeader', _.bind(this._onClickTab, this)); + // uncomment to add some dummy tabs for testing - // this._addTestTabs(); + //this._addTestTabs(); }, /** @@ -97,6 +107,27 @@ } }, + _onClickTab: function(e) { + var $target = $(e.target); + if (!$target.hasClass('tabHeader')) { + $target = $target.closest('.tabHeader'); + } + var tabIndex = $target.attr('data-tabindex'); + var targetTab; + if (_.isUndefined(tabIndex)) { + return; + } + + this.$el.find('.tabsContainer .tab').addClass('hidden'); + targetTab = this._tabViews[tabIndex]; + targetTab.$el.removeClass('hidden'); + + this.$el.find('.tabHeaders li').removeClass('selected'); + $target.addClass('selected'); + + e.preventDefault(); + }, + _addTestTabs: function() { for (var j = 0; j < 2; j++) { var testView = new OCA.Files.DetailTabView('testtab' + j); @@ -131,7 +162,7 @@ closeLabel: t('files', 'Close') })); var $tabsContainer = $el.find('.tabsContainer'); - var $tabHeadsContainer = $el.find('.tabHeadsContainer'); + var $tabHeadsContainer = $el.find('.tabHeaders'); var $detailsContainer = $el.find('.detailFileInfoContainer'); // render details @@ -140,14 +171,25 @@ }); if (this._tabViews.length > 0) { + if (!this._currentTab) { + this._currentTab = this._tabViews[0].getId(); + } + // render tabs - _.each(this._tabViews, function(tabView) { + _.each(this._tabViews, function(tabView, i) { // hidden by default - $tabsContainer.append(tabView.get$()); + var $el = tabView.get$(); + var isCurrent = (tabView.getId() === self._currentTab); + if (!isCurrent) { + $el.addClass('hidden'); + } + $tabsContainer.append($el); $tabHeadsContainer.append(self._templateTabHeader({ tabId: tabView.getId(), - label: tabView.getLabel() + tabIndex: i, + label: tabView.getLabel(), + selected: isCurrent })); }); } @@ -155,10 +197,6 @@ // TODO: select current tab this.$el.append($el); - - if (this._tabViews.length > 0) { - $tabsContainer.tabs({}); - } }, /** diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js index 7e0f9eff16f..b9b1dda2ccc 100644 --- a/apps/files/js/detailtabview.js +++ b/apps/files/js/detailtabview.js @@ -59,8 +59,8 @@ throw 'Argument "id" is required'; } this._id = id; - this.$el = $('
    '); - this.$el.attr('id', id); + this.$el = $('
    '); + this.$el.attr('data-tabid', id); }, /** diff --git a/apps/files/tests/js/detailsviewSpec.js b/apps/files/tests/js/detailsviewSpec.js index b00c15032b0..db1e24fd68e 100644 --- a/apps/files/tests/js/detailsviewSpec.js +++ b/apps/files/tests/js/detailsviewSpec.js @@ -64,23 +64,20 @@ describe('OCA.Files.DetailsView tests', function() { }); }); describe('tabs', function() { - it('renders registered tabs', function() { - var testView = new OCA.Files.DetailTabView('test1'); - var testView2 = new OCA.Files.DetailTabView('test2'); + var testView, testView2; + + beforeEach(function() { + testView = new OCA.Files.DetailTabView('test1'); + testView2 = new OCA.Files.DetailTabView('test2'); detailsView.addTabView(testView); detailsView.addTabView(testView2); detailsView.render(); - - expect(detailsView.$el.find('.tabsContainer .detailTabView').length).toEqual(2); + }); + it('renders registered tabs', function() { + expect(detailsView.$el.find('.tab').length).toEqual(2); }); it('updates registered tabs when fileinfo is updated', function() { var tabRenderStub = sinon.stub(OCA.Files.DetailTabView.prototype, 'render'); - var testView = new OCA.Files.DetailTabView('test1'); - var testView2 = new OCA.Files.DetailTabView('test2'); - detailsView.addTabView(testView); - detailsView.addTabView(testView2); - detailsView.render(); - var fileInfo = {id: 5, name: 'test.txt'}; tabRenderStub.reset(); detailsView.setFileInfo(fileInfo); @@ -91,5 +88,18 @@ describe('OCA.Files.DetailsView tests', function() { expect(tabRenderStub.callCount).toEqual(2); tabRenderStub.restore(); }); + it('selects the first tab by default', function() { + expect(detailsView.$el.find('.tabHeader').eq(0).hasClass('selected')).toEqual(true); + expect(detailsView.$el.find('.tabHeader').eq(1).hasClass('selected')).toEqual(false); + expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(false); + expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(true); + }); + it('switches the current tab when clicking on tab header', function() { + detailsView.$el.find('.tabHeader').eq(1).click(); + expect(detailsView.$el.find('.tabHeader').eq(0).hasClass('selected')).toEqual(false); + expect(detailsView.$el.find('.tabHeader').eq(1).hasClass('selected')).toEqual(true); + expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(true); + expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(false); + }); }); }); diff --git a/core/css/apps.css b/core/css/apps.css index d3b9c0e7095..bbeb4444991 100644 --- a/core/css/apps.css +++ b/core/css/apps.css @@ -592,3 +592,51 @@ em { padding:16px; } +/* generic tab styles */ +.tabHeaders { + margin: 15px; + background-color: #1D2D44; +} + +.tabHeaders .tabHeader { + float: left; + border: 1px solid #ddd; + padding: 5px; + cursor: pointer; + background-color: #f8f8f8; + font-weight: bold; +} +.tabHeaders .tabHeader, .tabHeaders .tabHeader a { + color: #888; +} + +.tabHeaders .tabHeader:first-child { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} + +.tabHeaders .tabHeader:last-child { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.tabHeaders .tabHeader.selected, +.tabHeaders .tabHeader:hover { + background-color: #e8e8e8; +} + +.tabHeaders .tabHeader.selected, +.tabHeaders .tabHeader.selected a, +.tabHeaders .tabHeader:hover, +.tabHeaders .tabHeader:hover a { + color: #000; +} + +.tabsContainer { + clear: left; +} + +.tabsContainer .tab { + padding: 15px; +} + From 3c36813ea70a82eadf77e9b86ae261320364d489 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 4 Aug 2015 15:47:43 +0200 Subject: [PATCH 17/25] fix right sidebar position and first style --- apps/files_sharing/css/sharetabview.css | 2 +- core/css/apps.css | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/files_sharing/css/sharetabview.css b/apps/files_sharing/css/sharetabview.css index a8ad1ceaba7..42c9bee7173 100644 --- a/apps/files_sharing/css/sharetabview.css +++ b/apps/files_sharing/css/sharetabview.css @@ -1,3 +1,3 @@ .app-files .shareTabView { - min-height: 200px; + min-height: 100px; } diff --git a/core/css/apps.css b/core/css/apps.css index bbeb4444991..091bb2c22fc 100644 --- a/core/css/apps.css +++ b/core/css/apps.css @@ -425,13 +425,12 @@ sibling containers properly. Compare Files app for example. */ #app-sidebar { - position: absolute; - top: 0; + position: fixed; + top: 45px; right: 0; left: auto; bottom: 0; - width: 30%; - min-width: 300px; + width: 300px; display: block; background: #eee; -webkit-transition: margin-right 300ms; @@ -441,14 +440,11 @@ overflow-x: hidden; overflow-y: auto; visibility: visible; - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - box-shadow: 0 2px 3px rgba(50, 50, 50, .4); z-index: 500; } #app-sidebar ~ .with-app-sidebar { - width: 70%; + width: 73%; } #app-sidebar.disappear { @@ -639,4 +635,3 @@ em { .tabsContainer .tab { padding: 15px; } - From eb323fe8dee06a3685793f7e08748f303add7766 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 4 Aug 2015 16:02:18 +0200 Subject: [PATCH 18/25] fix position of elements in right sidebar --- apps/files/css/detailsView.css | 23 +++++++++++++++++------ apps/files/js/mainfileinfodetailview.js | 3 +-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css index 78d3357fbd0..76629cb790f 100644 --- a/apps/files/css/detailsView.css +++ b/apps/files/css/detailsView.css @@ -1,5 +1,5 @@ #app-sidebar .detailFileInfoContainer { - min-height: 60px; + min-height: 50px; padding: 15px; } @@ -26,9 +26,19 @@ } #app-sidebar .fileName { - font-weight: bold; - font-size: 17px; - overflow: hidden; + font-size: 16px; + padding-top: 3px; +} + +#app-sidebar .file-details { + margin-top: 3px; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + opacity: .5; +} +#app-sidebar .action-favorite { + vertical-align: text-bottom; + padding: 10px; + margin: -10px; } #app-sidebar .detailList { @@ -39,6 +49,7 @@ position: absolute; top: 0; right: 0; - margin: 15px; + padding: 15px; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + opacity: .5; } - diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js index 245653d8e1e..5beb9430d1e 100644 --- a/apps/files/js/mainfileinfodetailview.js +++ b/apps/files/js/mainfileinfodetailview.js @@ -11,7 +11,7 @@ (function() { var TEMPLATE = '
    {{name}}
    ' + - '
    ' + + '
    ' + ' ' + @@ -96,4 +96,3 @@ OCA.Files.MainFileInfoDetailView = MainFileInfoDetailView; })(); - From ae27f90efe554d3a8a069165fe14cfd50445855d Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 6 Aug 2015 11:45:12 +0200 Subject: [PATCH 19/25] Fix app-content scrollbar when app-sidebar is open --- core/css/apps.css | 6 +++--- core/css/mobile.css | 6 +----- core/js/apps.js | 10 ++++------ core/js/js.js | 4 ++-- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/core/css/apps.css b/core/css/apps.css index 091bb2c22fc..5769120c5ed 100644 --- a/core/css/apps.css +++ b/core/css/apps.css @@ -430,7 +430,7 @@ right: 0; left: auto; bottom: 0; - width: 300px; + width: 27%; display: block; background: #eee; -webkit-transition: margin-right 300ms; @@ -443,8 +443,8 @@ z-index: 500; } -#app-sidebar ~ .with-app-sidebar { - width: 73%; +#app-content.with-app-sidebar { + margin-right: 27%; } #app-sidebar.disappear { diff --git a/core/css/mobile.css b/core/css/mobile.css index e38d9756d95..2256d821d73 100644 --- a/core/css/mobile.css +++ b/core/css/mobile.css @@ -103,11 +103,7 @@ z-index: 1000; } -#app-sidebar { - width: 250px; -} - -#app-sidebar ~ .with-app-sidebar { +#app-sidebar{ width: 100%; } diff --git a/core/js/apps.js b/core/js/apps.js index 5d9bbeb870a..d0d351f5147 100644 --- a/core/js/apps.js +++ b/core/js/apps.js @@ -25,9 +25,8 @@ */ exports.Apps.showAppSidebar = function() { var $appSidebar = $('#app-sidebar'); - $appSidebar - .removeClass('disappear') - .find('~').addClass('with-app-sidebar'); + $appSidebar.removeClass('disappear') + $('#app-content').addClass('with-app-sidebar'); }; @@ -37,9 +36,8 @@ */ exports.Apps.hideAppSidebar = function() { var $appSidebar = $('#app-sidebar'); - $appSidebar - .addClass('disappear') - .find('~ .with-app-sidebar').removeClass('with-app-sidebar'); + $appSidebar.addClass('disappear'); + $('#app-content').removeClass('with-app-sidebar'); }; /** diff --git a/core/js/js.js b/core/js/js.js index 45c9c90362f..72d4edd28dd 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -1366,13 +1366,13 @@ function initCore() { // if there is a scrollbar … if($('#app-content').get(0).scrollHeight > $('#app-content').height()) { if($(window).width() > 768) { - controlsWidth = $('#content').width() - $('#app-navigation').width() - getScrollBarWidth(); + controlsWidth = $('#content').width() - $('#app-navigation').width() - $('#app-sidebar').width() - getScrollBarWidth(); } else { controlsWidth = $('#content').width() - getScrollBarWidth(); } } else { // if there is none if($(window).width() > 768) { - controlsWidth = $('#content').width() - $('#app-navigation').width(); + controlsWidth = $('#content').width() - $('#app-navigation').width() - $('#app-sidebar').width(); } else { controlsWidth = $('#content').width(); } From 604694630bddff28f0e8daf3868bb35abf910eab Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 6 Aug 2015 15:45:41 +0200 Subject: [PATCH 20/25] Set momentJS locale to en during unit test runs --- core/js/tests/specHelper.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/js/tests/specHelper.js b/core/js/tests/specHelper.js index 29293e89bcb..dbe005ba2e9 100644 --- a/core/js/tests/specHelper.js +++ b/core/js/tests/specHelper.js @@ -121,6 +121,8 @@ window.isPhantom = /phantom/i.test(navigator.userAgent); OC.TestUtil = TestUtil; } + moment.locale('en'); + // reset plugins OC.Plugins._plugins = []; From bdfab8108ee54c544dcf777f9cb7bf867ee6888c Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 6 Aug 2015 17:24:45 +0200 Subject: [PATCH 21/25] Fix sidebar mtime display unit test --- apps/files/tests/js/mainfileinfodetailviewSpec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/files/tests/js/mainfileinfodetailviewSpec.js b/apps/files/tests/js/mainfileinfodetailviewSpec.js index 2dec4ea8a1a..3ea5ab66337 100644 --- a/apps/files/tests/js/mainfileinfodetailviewSpec.js +++ b/apps/files/tests/js/mainfileinfodetailviewSpec.js @@ -43,11 +43,11 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() { name: 'One.txt', path: '/subdir', size: 123456789, - dateLabel: new Date(Date.UTC(2015, 6, 17, 1, 2, 3, 4)) + mtime: Date.UTC(2015, 6, 17, 1, 2, 0, 0) }; }); it('displays basic info', function() { - var clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 0)); + var clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 3)); view.setFileInfo(testFileInfo); expect(view.$el.find('.fileName').text()).toEqual('One.txt'); expect(view.$el.find('.fileName').attr('title')).toEqual('One.txt'); From 9c402c761f1c8272f23f653609acb2e557f34b74 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 7 Aug 2015 01:22:39 +0200 Subject: [PATCH 22/25] fix timezone issue with unittest --- apps/files/tests/js/mainfileinfodetailviewSpec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/files/tests/js/mainfileinfodetailviewSpec.js b/apps/files/tests/js/mainfileinfodetailviewSpec.js index 3ea5ab66337..9f1de8752de 100644 --- a/apps/files/tests/js/mainfileinfodetailviewSpec.js +++ b/apps/files/tests/js/mainfileinfodetailviewSpec.js @@ -48,13 +48,14 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() { }); it('displays basic info', function() { var clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 3)); + var dateExpected = OC.Util.formatDate(Date(Date.UTC(2015, 6, 17, 1, 2, 0, 0))); view.setFileInfo(testFileInfo); expect(view.$el.find('.fileName').text()).toEqual('One.txt'); expect(view.$el.find('.fileName').attr('title')).toEqual('One.txt'); expect(view.$el.find('.size').text()).toEqual('117.7 MB'); expect(view.$el.find('.size').attr('title')).toEqual('123456789 bytes'); expect(view.$el.find('.date').text()).toEqual('a few seconds ago'); - expect(view.$el.find('.date').attr('title')).toEqual('July 17, 2015 3:02 AM'); + expect(view.$el.find('.date').attr('title')).toEqual(dateExpected); clock.restore(); }); it('displays favorite icon', function() { From d1439ec1048841e6f0dbcef7ea666d5c15559a49 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 7 Aug 2015 01:23:50 +0200 Subject: [PATCH 23/25] fix tipsy gravity after rebase --- apps/files/js/mainfileinfodetailview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js index 5beb9430d1e..68fb6b50058 100644 --- a/apps/files/js/mainfileinfodetailview.js +++ b/apps/files/js/mainfileinfodetailview.js @@ -89,7 +89,7 @@ // TODO: special icons / shared / external $iconDiv.css('background-image', 'url("' + OC.MimeType.getIconUrl('dir') + '")'); } - this.$el.find('[title]').tipsy(); + this.$el.find('[title]').tipsy({gravity: 'n'}); } } }); From 7760300fca4dbfbcc05407f58fdcd61a94031c9c Mon Sep 17 00:00:00 2001 From: Hendrik Leppelsack Date: Fri, 7 Aug 2015 12:33:50 +0200 Subject: [PATCH 24/25] replace tipsy with tooltip --- apps/files/js/mainfileinfodetailview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js index 68fb6b50058..a00d907d0d6 100644 --- a/apps/files/js/mainfileinfodetailview.js +++ b/apps/files/js/mainfileinfodetailview.js @@ -89,7 +89,7 @@ // TODO: special icons / shared / external $iconDiv.css('background-image', 'url("' + OC.MimeType.getIconUrl('dir') + '")'); } - this.$el.find('[title]').tipsy({gravity: 'n'}); + this.$el.find('[title]').tooltip({placement: 'bottom'}); } } }); From 038d29b8def77ad906a722f72a1501b369f9c1ee Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 10 Aug 2015 11:23:39 +0200 Subject: [PATCH 25/25] Fix unit test for tipsy->tooltip change --- apps/files/tests/js/mainfileinfodetailviewSpec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/files/tests/js/mainfileinfodetailviewSpec.js b/apps/files/tests/js/mainfileinfodetailviewSpec.js index 9f1de8752de..10ad38097c6 100644 --- a/apps/files/tests/js/mainfileinfodetailviewSpec.js +++ b/apps/files/tests/js/mainfileinfodetailviewSpec.js @@ -20,17 +20,17 @@ */ describe('OCA.Files.MainFileInfoDetailView tests', function() { - var view, tipsyStub, previewStub, fncLazyLoadPreview, fileListMock; + var view, tooltipStub, previewStub, fncLazyLoadPreview, fileListMock; beforeEach(function() { - tipsyStub = sinon.stub($.fn, 'tipsy'); + tooltipStub = sinon.stub($.fn, 'tooltip'); fileListMock = sinon.mock(OCA.Files.FileList.prototype); view = new OCA.Files.MainFileInfoDetailView(); }); afterEach(function() { view.destroy(); view = undefined; - tipsyStub.restore(); + tooltipStub.restore(); fileListMock.restore(); });