From 2e06880c956c87908a0342cbe649ea4166c6bea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Sat, 11 Nov 2017 12:47:44 +0100 Subject: Make calling "selectTabHeader" to implicitly show the content view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "selectTabHeader" now triggers a "select:tabHeader" event, which the TabView handles by showing the appropriate content view. Thus, the click events on tab headers can be directly handled with "selectTabHeader" and "selectTab" no longer needs to explicitly show the content view. Showing the appropriate content view when a tab header is selected through "selectTabHeader" is a preparatory step to make possible to remove tabs. Signed-off-by: Daniel Calviño Sánchez --- js/views/tabview.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'js') diff --git a/js/views/tabview.js b/js/views/tabview.js index c3b4597bf..4efeb31ad 100644 --- a/js/views/tabview.js +++ b/js/views/tabview.js @@ -76,9 +76,8 @@ // 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' }, addTabHeader: function(tabId, tabHeaderOptions) { @@ -157,6 +156,8 @@ this._currentTabId = tabId; this.getChildView(this._currentTabId).setSelected(true); + + this.triggerMethod('select:tabHeader', tabId); } }); @@ -231,10 +232,6 @@ } }, - onChildviewClickTabHeader: function(tabId) { - this.selectTab(tabId); - }, - /** * Select the tab associated to the given tabId. * @@ -246,7 +243,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 } ); -- cgit v1.2.3 From dda1349ebda32507ee70059da6170b150cd7b68d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Sat, 11 Nov 2017 13:59:13 +0100 Subject: Fix SidebarView documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- js/views/sidebarview.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'js') diff --git a/js/views/sidebarview.js b/js/views/sidebarview.js index 614230ec8..9ffd74282 100644 --- a/js/views/sidebarview.js +++ b/js/views/sidebarview.js @@ -49,20 +49,20 @@ * be added as needed. The call details view can be set through * "setCallInfoView()" while new tabs can be added through "addTab()". * - * 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 +142,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 +167,8 @@ * 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. * * @param string tabId the ID of the tab. * @param Object tabHeaderOptions the options for the constructor of the -- cgit v1.2.3 From 43e3054c50d4ce2f0bd5eecf962572dacf532d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Sat, 11 Nov 2017 14:03:31 +0100 Subject: Make possible to remove tabs from the right sidebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing a tab from the right sidebar returns its content view, which would be needed to move a view from the main area to the right sidebar and back to the main area again (for example, the chat view). Signed-off-by: Daniel Calviño Sánchez --- js/views/sidebarview.js | 29 +++++++++++++++-- js/views/tabview.js | 84 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 4 deletions(-) (limited to 'js') diff --git a/js/views/sidebarview.js b/js/views/sidebarview.js index 9ffd74282..b6203e08f 100644 --- a/js/views/sidebarview.js +++ b/js/views/sidebarview.js @@ -46,8 +46,9 @@ * 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 opened or closed programatically using "open()" * and "close()". It will delegate on "OC.Apps.showAppSidebar()" and @@ -168,7 +169,8 @@ * the header). * * The SidebarView takes ownership of the given content view, and it - * will destroy it when the SidebarView is destroyed. + * 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; diff --git a/js/views/tabview.js b/js/views/tabview.js index 4efeb31ad..d0f81d2d7 100644 --- a/js/views/tabview.js +++ b/js/views/tabview.js @@ -80,6 +80,11 @@ 'click:tabHeader': 'selectTabHeader' }, + initialize: function() { + // The tabIds in priority and then insertion order. + this._tabIds = []; + }, + addTabHeader: function(tabId, tabHeaderOptions) { var tabHeaderId = 'tabHeader' + tabId.charAt(0).toUpperCase() + tabId.substr(1); @@ -94,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 @@ -148,6 +155,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); @@ -209,7 +251,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 @@ -232,6 +275,45 @@ } }, + /** + * 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; + }, + /** * Select the tab associated to the given tabId. * -- cgit v1.2.3 From c8b5d2798f102e03c4aa1ab5204932ab9f30a6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Sat, 11 Nov 2017 14:08:41 +0100 Subject: Get the current priorities using the tabIds list instead of the regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tabIds list is already sorted by priority and insertion order, while the regions order is undefined and has to be explicitly sorted. Signed-off-by: Daniel Calviño Sánchez --- js/views/tabview.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'js') diff --git a/js/views/tabview.js b/js/views/tabview.js index d0f81d2d7..ad5d40dea 100644 --- a/js/views/tabview.js +++ b/js/views/tabview.js @@ -129,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; -- cgit v1.2.3