16f6c07173
Signed-off-by: Julius Härtl <jus@bitgrid.net>
315 lines
7 KiB
JavaScript
315 lines
7 KiB
JavaScript
/*
|
|
* 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 class="detailFileInfoContainer">' +
|
|
' </div>' +
|
|
' {{#if tabHeaders}}' +
|
|
' <ul class="tabHeaders">' +
|
|
' {{#each tabHeaders}}' +
|
|
' <li class="tabHeader" data-tabid="{{tabId}}" tabindex="0">' +
|
|
' <a href="#" tabindex="-1">{{label}}</a>' +
|
|
' </li>' +
|
|
' {{/each}}' +
|
|
' </ul>' +
|
|
' {{/if}}' +
|
|
' <div class="tabsContainer">' +
|
|
' </div>' +
|
|
' <a class="close icon-close" href="#"><span class="hidden-visually">{{closeLabel}}</span></a>';
|
|
|
|
/**
|
|
* @class OCA.Files.DetailsView
|
|
* @classdesc
|
|
*
|
|
* The details view show details about a selected file.
|
|
*
|
|
*/
|
|
var DetailsView = OC.Backbone.View.extend({
|
|
id: 'app-sidebar',
|
|
tabName: 'div',
|
|
className: 'detailsView scroll-container',
|
|
|
|
_template: null,
|
|
|
|
/**
|
|
* List of detail tab views
|
|
*
|
|
* @type Array<OCA.Files.DetailTabView>
|
|
*/
|
|
_tabViews: [],
|
|
|
|
/**
|
|
* List of detail file info views
|
|
*
|
|
* @type Array<OCA.Files.DetailFileInfoView>
|
|
*/
|
|
_detailFileInfoViews: [],
|
|
|
|
/**
|
|
* Id of the currently selected tab
|
|
*
|
|
* @type string
|
|
*/
|
|
_currentTabId: null,
|
|
|
|
/**
|
|
* Dirty flag, whether the view needs to be rerendered
|
|
*/
|
|
_dirty: false,
|
|
|
|
events: {
|
|
'click a.close': '_onClose',
|
|
'click .tabHeaders .tabHeader': '_onClickTab',
|
|
'keyup .tabHeaders .tabHeader': '_onKeyboardActivateTab'
|
|
},
|
|
|
|
/**
|
|
* Initialize the details view
|
|
*/
|
|
initialize: function() {
|
|
this._tabViews = [];
|
|
this._detailFileInfoViews = [];
|
|
|
|
this._dirty = true;
|
|
},
|
|
|
|
_onClose: function(event) {
|
|
OC.Apps.hideAppSidebar(this.$el);
|
|
event.preventDefault();
|
|
},
|
|
|
|
_onClickTab: function(e) {
|
|
var $target = $(e.target);
|
|
e.preventDefault();
|
|
if (!$target.hasClass('tabHeader')) {
|
|
$target = $target.closest('.tabHeader');
|
|
}
|
|
var tabId = $target.attr('data-tabid');
|
|
if (_.isUndefined(tabId)) {
|
|
return;
|
|
}
|
|
|
|
this.selectTab(tabId);
|
|
},
|
|
|
|
_onKeyboardActivateTab: function (event) {
|
|
if (event.key === " " || event.key === "Enter") {
|
|
this._onClickTab(event);
|
|
}
|
|
},
|
|
|
|
template: function(vars) {
|
|
if (!this._template) {
|
|
this._template = Handlebars.compile(TEMPLATE);
|
|
}
|
|
return this._template(vars);
|
|
},
|
|
|
|
/**
|
|
* Renders this details view
|
|
*/
|
|
render: function() {
|
|
// remove old instances
|
|
var $appSidebar = $('#app-sidebar');
|
|
if ($appSidebar.length === 0) {
|
|
this.$el.insertAfter($('#app-content'));
|
|
} else {
|
|
if ($appSidebar[0] !== this.el) {
|
|
$appSidebar.replaceWith(this.$el)
|
|
}
|
|
}
|
|
|
|
var templateVars = {
|
|
closeLabel: t('files', 'Close')
|
|
};
|
|
|
|
this._tabViews = this._tabViews.sort(function(tabA, tabB) {
|
|
var orderA = tabA.order || 0;
|
|
var orderB = tabB.order || 0;
|
|
if (orderA === orderB) {
|
|
return OC.Util.naturalSortCompare(tabA.getLabel(), tabB.getLabel());
|
|
}
|
|
return orderA - orderB;
|
|
});
|
|
|
|
templateVars.tabHeaders = _.map(this._tabViews, function(tabView, i) {
|
|
return {
|
|
tabId: tabView.id,
|
|
label: tabView.getLabel()
|
|
};
|
|
});
|
|
|
|
this.$el.html(this.template(templateVars));
|
|
|
|
var $detailsContainer = this.$el.find('.detailFileInfoContainer');
|
|
|
|
// render details
|
|
_.each(this._detailFileInfoViews, function(detailView) {
|
|
$detailsContainer.append(detailView.get$());
|
|
});
|
|
|
|
if (!this._currentTabId && this._tabViews.length > 0) {
|
|
this._currentTabId = this._tabViews[0].id;
|
|
}
|
|
|
|
this.selectTab(this._currentTabId);
|
|
|
|
this._updateTabVisibilities();
|
|
|
|
this._dirty = false;
|
|
},
|
|
|
|
/**
|
|
* Selects the given tab by id
|
|
*
|
|
* @param {string} tabId tab id
|
|
*/
|
|
selectTab: function(tabId) {
|
|
if (!tabId) {
|
|
return;
|
|
}
|
|
|
|
var tabView = _.find(this._tabViews, function(tab) {
|
|
return tab.id === tabId;
|
|
});
|
|
|
|
if (!tabView) {
|
|
console.warn('Details view tab with id "' + tabId + '" not found');
|
|
return;
|
|
}
|
|
|
|
this._currentTabId = tabId;
|
|
|
|
var $tabsContainer = this.$el.find('.tabsContainer');
|
|
var $tabEl = $tabsContainer.find('#' + tabId);
|
|
|
|
// hide other tabs
|
|
$tabsContainer.find('.tab').addClass('hidden');
|
|
|
|
// tab already rendered ?
|
|
if (!$tabEl.length) {
|
|
// render tab
|
|
$tabsContainer.append(tabView.$el);
|
|
$tabEl = tabView.$el;
|
|
}
|
|
|
|
// this should trigger tab rendering
|
|
tabView.setFileInfo(this.model);
|
|
|
|
$tabEl.removeClass('hidden');
|
|
|
|
// update tab headers
|
|
var $tabHeaders = this.$el.find('.tabHeaders li');
|
|
$tabHeaders.removeClass('selected');
|
|
$tabHeaders.filterAttr('data-tabid', tabView.id).addClass('selected');
|
|
},
|
|
|
|
/**
|
|
* Sets the file info to be displayed in the view
|
|
*
|
|
* @param {OCA.Files.FileInfoModel} fileInfo file info to set
|
|
*/
|
|
setFileInfo: function(fileInfo) {
|
|
this.model = fileInfo;
|
|
|
|
if (this._dirty) {
|
|
this.render();
|
|
} else {
|
|
this._updateTabVisibilities();
|
|
}
|
|
|
|
if (this._currentTabId) {
|
|
// only update current tab, others will be updated on-demand
|
|
var tabId = this._currentTabId;
|
|
var tabView = _.find(this._tabViews, function(tab) {
|
|
return tab.id === tabId;
|
|
});
|
|
tabView.setFileInfo(fileInfo);
|
|
}
|
|
|
|
_.each(this._detailFileInfoViews, function(detailView) {
|
|
detailView.setFileInfo(fileInfo);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Update tab headers based on the current model
|
|
*/
|
|
_updateTabVisibilities: function() {
|
|
// update tab header visibilities
|
|
var self = this;
|
|
var deselect = false;
|
|
var countVisible = 0;
|
|
var $tabHeaders = this.$el.find('.tabHeaders li');
|
|
_.each(this._tabViews, function(tabView) {
|
|
var isVisible = tabView.canDisplay(self.model);
|
|
if (isVisible) {
|
|
countVisible += 1;
|
|
}
|
|
if (!isVisible && self._currentTabId === tabView.id) {
|
|
deselect = true;
|
|
}
|
|
$tabHeaders.filterAttr('data-tabid', tabView.id).toggleClass('hidden', !isVisible);
|
|
});
|
|
|
|
// hide the whole container if there is only one tab
|
|
this.$el.find('.tabHeaders').toggleClass('hidden', countVisible <= 1);
|
|
|
|
if (deselect) {
|
|
// select the first visible tab instead
|
|
var visibleTabId = this.$el.find('.tabHeader:not(.hidden):first').attr('data-tabid');
|
|
this.selectTab(visibleTabId);
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Returns the file info.
|
|
*
|
|
* @return {OCA.Files.FileInfoModel} file info
|
|
*/
|
|
getFileInfo: function() {
|
|
return this.model;
|
|
},
|
|
|
|
/**
|
|
* Adds a tab in the tab view
|
|
*
|
|
* @param {OCA.Files.DetailTabView} tab view
|
|
*/
|
|
addTabView: function(tabView) {
|
|
this._tabViews.push(tabView);
|
|
this._dirty = true;
|
|
},
|
|
|
|
/**
|
|
* Adds a detail view for file info.
|
|
*
|
|
* @param {OCA.Files.DetailFileInfoView} detail view
|
|
*/
|
|
addDetailView: function(detailView) {
|
|
this._detailFileInfoViews.push(detailView);
|
|
this._dirty = true;
|
|
},
|
|
|
|
/**
|
|
* Returns an array with the added DetailFileInfoViews.
|
|
*
|
|
* @return Array<OCA.Files.DetailFileInfoView> an array with the added
|
|
* DetailFileInfoViews.
|
|
*/
|
|
getDetailViews: function() {
|
|
return [].concat(this._detailFileInfoViews);
|
|
}
|
|
});
|
|
|
|
OCA.Files.DetailsView = DetailsView;
|
|
})();
|