nextcloud/apps/files/js/detailsview.js

316 lines
7.0 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;
})();