diff options
-rw-r--r-- | apps/files/css/detailsView.css | 30 | ||||
-rw-r--r-- | apps/files/index.php | 2 | ||||
-rw-r--r-- | apps/files/js/detailsview.js | 45 | ||||
-rw-r--r-- | apps/files/js/detailtabview.js | 25 | ||||
-rw-r--r-- | apps/files/js/filelist.js | 118 | ||||
-rw-r--r-- | apps/files/js/mainfileinfodetailview.js | 48 | ||||
-rw-r--r-- | apps/files_sharing/js/share.js | 7 | ||||
-rw-r--r-- | apps/files_sharing/js/sharedetailview.js | 63 | ||||
-rw-r--r-- | apps/files_sharing/js/sharetabview.js | 64 |
9 files changed, 328 insertions, 74 deletions
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 @@ '<div>' + ' <div class="detailFileInfoContainer">' + ' </div>' + - ' <div class="tabHeadsContainer">' + - ' </div>' + - ' <div class="tabContentsContainer">' + + ' <div class="tabsContainer">' + + ' <ul class="tabHeadsContainer">' + + ' </ul>' + ' </div>' + '</div>'; var TEMPLATE_TAB_HEADER = - '<div class="tabHeaders">{{label}}</div>'; + '<li class="tabHeaders"><a href="#{{tabId}}">{{label}}</a></li>'; /** * @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 = $('<div class="detailTabView"></div>'); + this.$el.attr('id', id); }, /** @@ -66,6 +73,15 @@ }, /** + * Returns the tab element id + * + * @return {string} tab id + */ + getId: function() { + return this._id; + }, + + /** * Returns the tab label * * @return {String} label @@ -81,6 +97,9 @@ */ render: function() { // to be implemented in subclass + // FIXME: code is only for testing + this.$el.empty(); + this.$el.append('<div>Hello ' + this._id + '</div>'); }, /** 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 { @@ -2261,6 +2285,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. * * @todo make this a real class in the future 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 = - '<div class="thumbnail"></div>' + - '<ul class="detailList">' + - ' <li>Name: {{name}}</li>' + - ' <li>Path: {{path}}</li>' + - '</ul>'; + '<div class="thumbnail"></div><div class="fileName">{{name}}</div>' + + '<div><span title="{{altSize}}">{{size}}</span>, <span title="{{altDate}}">{{date}}</span></div>'; /** * @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 = + '<ul class="shareDetailList">' + + ' <li>Owner: {{owner}}</li>' + + '</ul>'; + + /** + * @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 = $('<div class="shareDetailView"></div>'); + }, + + /** + * 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 = + '<div>TODO: here comes the share dialog</div>'; + + /** + * @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; +})(); + |