зеркало из https://github.com/nextcloud/spreed.git
Merge pull request #507 from nextcloud/make-possible-to-remove-tabs-from-the-right-sidebar
Make possible to remove tabs from the right sidebar
This commit is contained in:
Коммит
d3cdf41b97
|
@ -46,23 +46,24 @@
|
|||
* The right sidebar is an area that can be shown or hidden from the right
|
||||
* border of the document. It contains a view intended to provide details of
|
||||
* the current call at the top and a TabView to which different sections can
|
||||
* be added as needed. The call details view can be set through
|
||||
* "setCallInfoView()" while new tabs can be added through "addTab()".
|
||||
* be added and removed as needed. The call details view can be set through
|
||||
* "setCallInfoView()" while new tabs can be added through "addTab()" and
|
||||
* removed through "removeTab()".
|
||||
*
|
||||
* The SidebarView can be shown or hidden programatically using "show()" and
|
||||
* "hide()". It will delegate on "OC.Apps.showAppSidebar()" and
|
||||
* The SidebarView can be opened or closed programatically using "open()"
|
||||
* and "close()". It will delegate on "OC.Apps.showAppSidebar()" and
|
||||
* "OC.Apps.hideAppSidebar()", so it must be used along an "#app-content"
|
||||
* that takes into account the "with-app-sidebar" CSS class.
|
||||
*
|
||||
* In order for the user to be able to show the sidebar when it is hidden,
|
||||
* In order for the user to be able to open the sidebar when it is closed,
|
||||
* the SidebarView shows a small icon ("#app-sidebar-trigger") on the right
|
||||
* border of the document that shows the sidebar when clicked. When the
|
||||
* sidebar is shown the icon is hidden.
|
||||
* border of the document that opens the sidebar when clicked. When the
|
||||
* sidebar is open the icon is hidden.
|
||||
*
|
||||
* By default the sidebar is disabled, that is, it is hidden and can not be
|
||||
* shown, neither by the user nor programatically. Calling "enable()" will
|
||||
* make possible for the sidebar to be shown, and calling "disable()" will
|
||||
* prevent it again (also hidden it if it was shown).
|
||||
* By default the sidebar is disabled, that is, it is closed and can not be
|
||||
* opened, neither by the user nor programatically. Calling "enable()" will
|
||||
* make possible for the sidebar to be opened, and calling "disable()" will
|
||||
* prevent it again (also closing it if it was open).
|
||||
*/
|
||||
var SidebarView = Marionette.View.extend({
|
||||
|
||||
|
@ -142,7 +143,7 @@
|
|||
/**
|
||||
* Sets a new call info view.
|
||||
*
|
||||
* Once set, the Sidebar takes ownership of the view, and it will
|
||||
* Once set, the SidebarView takes ownership of the view, and it will
|
||||
* destroy it if a new one is set.
|
||||
*
|
||||
* @param Marionette.View callInfoView the view to set.
|
||||
|
@ -167,8 +168,9 @@
|
|||
* can provide an 'onRender' function to extend the default rendering of
|
||||
* the header).
|
||||
*
|
||||
* The Sidebar takes ownership of the given content view, and it will
|
||||
* destroy it when the Sidebar is destroyed.
|
||||
* The SidebarView takes ownership of the given content view, and it
|
||||
* will destroy it when the SidebarView is destroyed, except if the
|
||||
* content view is removed first.
|
||||
*
|
||||
* @param string tabId the ID of the tab.
|
||||
* @param Object tabHeaderOptions the options for the constructor of the
|
||||
|
@ -180,6 +182,27 @@
|
|||
this._tabView.addTab(tabId, tabHeaderOptions, tabContentView);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the tab for the given tabId.
|
||||
*
|
||||
* If the tab to be removed is the one currently selected and there are
|
||||
* other tabs the next one (in priority and then insertion order) is
|
||||
* automatically selected; if the tab to be removed is the last one,
|
||||
* then the previous one is selected instead. If there are no other tabs
|
||||
* then the TabView is simply emptied.
|
||||
*
|
||||
* In any case the content view given when the tab was added is
|
||||
* returned; this SidebarView will no longer have ownership of the
|
||||
* content view, and thus the content view must be explicitly destroyed
|
||||
* when no longer needed.
|
||||
*
|
||||
* @param string tabId the ID of the tab to remove.
|
||||
* @return Marionette.View the content view of the removed tab.
|
||||
*/
|
||||
removeTab: function(tabId) {
|
||||
return this._tabView.removeTab(tabId);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
OCA.SpreedMe.Views.SidebarView = SidebarView;
|
||||
|
|
|
@ -76,9 +76,13 @@
|
|||
// nothing to be rendered with a template.
|
||||
template: _.noop,
|
||||
|
||||
childViewTriggers: {
|
||||
// Propagate the event to the parent view.
|
||||
'click:tabHeader': 'click:tabHeader'
|
||||
childViewEvents: {
|
||||
'click:tabHeader': 'selectTabHeader'
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
// The tabIds in priority and then insertion order.
|
||||
this._tabIds = [];
|
||||
},
|
||||
|
||||
addTabHeader: function(tabId, tabHeaderOptions) {
|
||||
|
@ -95,6 +99,8 @@
|
|||
|
||||
var tabHeaderIndex = this._getIndexForTabHeaderPriority(tabHeaderOptions.priority);
|
||||
|
||||
this._tabIds.splice(tabHeaderIndex, 0, tabId);
|
||||
|
||||
// When adding a region and showing a view on it the target element
|
||||
// of the region must exist in the parent view. Therefore, a dummy
|
||||
// target element, which will be replaced with the tab header
|
||||
|
@ -123,20 +129,11 @@
|
|||
* @return int the insertion index.
|
||||
*/
|
||||
_getIndexForTabHeaderPriority: function(priority) {
|
||||
// this.getRegions() returns an object that acts as a map, but it
|
||||
// has no "length" property; _.map creates an array, thus ensuring
|
||||
// that there is a "length" property to know the current number of
|
||||
// tab headers.
|
||||
var currentPriorities = _.map(this.getRegions(), function(region) {
|
||||
return region.currentView.getOption('priority');
|
||||
});
|
||||
|
||||
// By default sort() converts the values to strings and sorts them
|
||||
// in ascending order using their Unicode value; a custom function
|
||||
// must be used to sort them by their numerical value instead.
|
||||
currentPriorities.sort(function(a, b) {
|
||||
return a - b;
|
||||
}).reverse();
|
||||
// _.map creates an array, so "currentPriorities" will contain a
|
||||
// "length" property.
|
||||
var currentPriorities = _.map(this._tabIds, _.bind(function(tabId) {
|
||||
return this.getRegion(tabId).currentView.getOption('priority');
|
||||
}, this));
|
||||
|
||||
var index = _.findIndex(currentPriorities, function(currentPriority) {
|
||||
return priority > currentPriority;
|
||||
|
@ -149,6 +146,41 @@
|
|||
return index;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the tab header for the given tabId.
|
||||
*
|
||||
* If the tab header to be removed is the one currently selected and
|
||||
* there are other tab headers the next one (in priority and then
|
||||
* insertion order) is automatically selected; if the tab header to be
|
||||
* removed is the last one, then the previous one is selected instead.
|
||||
*
|
||||
* @param string tabId the ID of the tab.
|
||||
*/
|
||||
removeTabHeader: function(tabId) {
|
||||
var tabIdIndex = _.indexOf(this._tabIds, tabId);
|
||||
|
||||
// If the tab header to be removed is the one currently selected
|
||||
// then select the next tab header or, if it is the last tab header,
|
||||
// the previous one (or none if there are no other tab headers).
|
||||
if (this._currentTabId === tabId) {
|
||||
if (this._tabIds.length <= 1) {
|
||||
delete this._currentTabId;
|
||||
} else if (tabIdIndex === (this._tabIds.length - 1)) {
|
||||
this.selectTabHeader(this._tabIds[tabIdIndex - 1]);
|
||||
} else {
|
||||
this.selectTabHeader(this._tabIds[tabIdIndex + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
this._tabIds.splice(tabIdIndex, 1);
|
||||
|
||||
var removedRegion = this.removeRegion(tabId);
|
||||
// Remove the dummy target element that was replaced by the view
|
||||
// when it was shown and that is restored back when the region is
|
||||
// removed.
|
||||
removedRegion.el.remove();
|
||||
},
|
||||
|
||||
selectTabHeader: function(tabId) {
|
||||
if (this._currentTabId !== undefined) {
|
||||
this.getChildView(this._currentTabId).setSelected(false);
|
||||
|
@ -157,6 +189,8 @@
|
|||
this._currentTabId = tabId;
|
||||
|
||||
this.getChildView(this._currentTabId).setSelected(true);
|
||||
|
||||
this.triggerMethod('select:tabHeader', tabId);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -208,7 +242,8 @@
|
|||
* the header).
|
||||
*
|
||||
* The TabView takes ownership of the given content view, and it will
|
||||
* destroy it when the TabView is destroyed.
|
||||
* destroy it when the TabView is destroyed, except if the content view
|
||||
* is removed first.
|
||||
*
|
||||
* @param string tabId the ID of the tab.
|
||||
* @param Object tabHeaderOptions the options for the constructor of the
|
||||
|
@ -231,8 +266,43 @@
|
|||
}
|
||||
},
|
||||
|
||||
onChildviewClickTabHeader: function(tabId) {
|
||||
this.selectTab(tabId);
|
||||
/**
|
||||
* Removes the tab for the given tabId.
|
||||
*
|
||||
* If the tab to be removed is the one currently selected and there are
|
||||
* other tabs the next one (in priority and then insertion order) is
|
||||
* automatically selected; if the tab to be removed is the last one,
|
||||
* then the previous one is selected instead. If there are no other tabs
|
||||
* then this TabView is simply emptied.
|
||||
*
|
||||
* In any case the content view given when the tab was added is
|
||||
* returned; this TabView will no longer have ownership of the content
|
||||
* view, and thus the content view must be explicitly destroyed when no
|
||||
* longer needed.
|
||||
*
|
||||
* @param string tabId the ID of the tab to remove.
|
||||
* @return Marionette.View the content view of the removed tab.
|
||||
*/
|
||||
removeTab: function(tabId) {
|
||||
if (!this._tabContentViews.hasOwnProperty(tabId)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var removedTabContentView = this._tabContentViews[tabId];
|
||||
|
||||
this._tabHeadersView.removeTabHeader(tabId);
|
||||
|
||||
delete this._tabContentViews[tabId];
|
||||
|
||||
// Removing the tab header selects a new tab header, which in turn
|
||||
// changes the content view, except when there are no other tabs. In
|
||||
// that case the content view would be being shown in the region and
|
||||
// thus would have to be removed from there.
|
||||
if (Object.keys(this._tabContentViews).length === 0) {
|
||||
this.getRegion('tabContent').empty({preventDestroy: true});
|
||||
}
|
||||
|
||||
return removedTabContentView;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -246,7 +316,16 @@
|
|||
}
|
||||
|
||||
this._tabHeadersView.selectTabHeader(tabId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the content view associated to the selected tab header.
|
||||
*
|
||||
* Only for internal use as an event handler.
|
||||
*
|
||||
* @param string tabId the ID of the selected tab.
|
||||
*/
|
||||
onChildviewSelectTabHeader: function(tabId) {
|
||||
// With Marionette 3.1 "this.detachChildView('tabContent')" would be
|
||||
// used instead of the "preventDestroy" option.
|
||||
this.showChildView('tabContent', this._tabContentViews[tabId], { preventDestroy: true } );
|
||||
|
|
Загрузка…
Ссылка в новой задаче