diff --git a/src/components/context-menu/context-menu.ts b/src/components/context-menu/context-menu.ts index 6ef697cc4..022305118 100644 --- a/src/components/context-menu/context-menu.ts +++ b/src/components/context-menu/context-menu.ts @@ -34,6 +34,7 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { hideMenu: boolean; ariaLabel: string; protected items: CoreContextMenuItemComponent[] = []; + protected itemsMovedToParent: CoreContextMenuItemComponent[] = []; protected itemsChangedStream: Subject; // Stream to update the hideMenu boolean when items change. protected instanceId: string; protected parentContextMenu: CoreContextMenuComponent; @@ -74,7 +75,11 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { if (this.parentContextMenu) { // All items were moved to the "parent" menu. Add the item in there. this.parentContextMenu.addItem(item); - } else { + + if (this.itemsMovedToParent.indexOf(item) == -1) { + this.itemsMovedToParent.push(item); + } + } else if (this.items.indexOf(item) == -1) { this.items.push(item); this.itemsChanged(); } @@ -103,7 +108,9 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { // Add all the items to the other menu. for (let i = 0; i < this.items.length; i++) { - contextMenu.addItem(this.items[i]); + const item = this.items[i]; + contextMenu.addItem(item); + this.itemsMovedToParent.push(item); } // Remove all items from the current menu. @@ -120,6 +127,11 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { if (this.parentContextMenu) { // All items were moved to the "parent" menu. Remove the item from there. this.parentContextMenu.removeItem(item); + + const index = this.itemsMovedToParent.indexOf(item); + if (index >= 0) { + this.itemsMovedToParent.splice(index, 1); + } } else { const index = this.items.indexOf(item); if (index >= 0) { @@ -129,6 +141,28 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { } } + /** + * Remove the items that were merged to a parent context menu. + */ + removeMergedItems(): void { + if (this.parentContextMenu) { + for (let i = 0; i < this.itemsMovedToParent.length; i++) { + this.parentContextMenu.removeItem(this.itemsMovedToParent[i]); + } + } + } + + /** + * Restore the items that were merged to a parent context menu. + */ + restoreMergedItems(): void { + if (this.parentContextMenu) { + for (let i = 0; i < this.itemsMovedToParent.length; i++) { + this.parentContextMenu.addItem(this.itemsMovedToParent[i]); + } + } + } + /** * Show the context menu. * @@ -146,5 +180,6 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { */ ngOnDestroy(): void { this.domUtils.removeInstanceById(this.instanceId); + this.removeMergedItems(); } } diff --git a/src/components/navbar-buttons/navbar-buttons.ts b/src/components/navbar-buttons/navbar-buttons.ts index c9e1a8d30..6b969dfbb 100644 --- a/src/components/navbar-buttons/navbar-buttons.ts +++ b/src/components/navbar-buttons/navbar-buttons.ts @@ -16,6 +16,7 @@ import { Component, Input, OnInit, OnDestroy, ContentChildren, ElementRef, Query import { Button } from 'ionic-angular'; import { CoreLoggerProvider } from '../../providers/logger'; import { CoreDomUtilsProvider } from '../../providers/utils/dom'; +import { CoreContextMenuComponent } from '../context-menu/context-menu'; /** * Component to add buttons to the app's header without having to place them inside the header itself. This is meant for @@ -59,12 +60,16 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { protected element: HTMLElement; protected _hidden: boolean; + protected forceHidden = false; protected logger: any; protected movedChildren: Node[]; + protected instanceId: string; + protected mergedContextMenu: CoreContextMenuComponent; constructor(element: ElementRef, logger: CoreLoggerProvider, private domUtils: CoreDomUtilsProvider) { this.element = element.nativeElement; this.logger = logger.getInstance('CoreNavBarButtonsComponent'); + this.instanceId = this.domUtils.storeInstanceByElement(this.element, this); } /** @@ -100,6 +105,17 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { }); } + /** + * Force or unforce hiding all buttons. If this is true, it will override the "hidden" input. + * + * @param {boolean} value The value to set. + */ + forceHide(value: boolean): void { + this.forceHidden = value; + + this.showHideAllElements(); + } + /** * If both button containers have a context menu, merge them into a single one. * @@ -122,7 +138,9 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { secondaryContextMenuInstance = this.domUtils.getInstanceByElement(secondaryContextMenu); if (mainContextMenuInstance && secondaryContextMenuInstance) { - secondaryContextMenuInstance.mergeContextMenus(mainContextMenuInstance); + this.mergedContextMenu = secondaryContextMenuInstance; + + this.mergedContextMenu.mergeContextMenus(mainContextMenuInstance); // Remove the empty context menu from the DOM. secondaryContextMenu.parentElement.removeChild(secondaryContextMenu); @@ -189,11 +207,21 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { * Show or hide all the elements. */ protected showHideAllElements(): void { + // Show or hide all moved children. if (this.movedChildren) { this.movedChildren.forEach((child: Node) => { this.showHideElement(child); }); } + + // Show or hide all the context menu items that were merged to another context menu. + if (this.mergedContextMenu) { + if (this.forceHidden || this._hidden) { + this.mergedContextMenu.removeMergedItems(); + } else { + this.mergedContextMenu.restoreMergedItems(); + } + } } /** @@ -204,7 +232,7 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { protected showHideElement(element: Node): void { // Check if it's an HTML Element if (element instanceof Element) { - if (this._hidden) { + if (this.forceHidden || this._hidden) { element.classList.add(this.BUTTON_HIDDEN_CLASS); } else { element.classList.remove(this.BUTTON_HIDDEN_CLASS); @@ -216,6 +244,8 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { * Component destroyed. */ ngOnDestroy(): void { + this.domUtils.removeInstanceById(this.instanceId); + // This component was destroyed, remove all the buttons that were moved. // The buttons can be moved outside of the current page, that's why we need to manually destroy them. // There's no need to destroy context menu items that were merged because they weren't moved from their DOM position. @@ -226,5 +256,9 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { } }); } + + if (this.mergedContextMenu) { + this.mergedContextMenu.removeMergedItems(); + } } } diff --git a/src/components/tabs/tab.ts b/src/components/tabs/tab.ts index 46467c2e6..6a36878a1 100644 --- a/src/components/tabs/tab.ts +++ b/src/components/tabs/tab.ts @@ -15,6 +15,8 @@ import { Component, Input, Output, OnInit, OnDestroy, ElementRef, EventEmitter, ContentChild, TemplateRef } from '@angular/core'; import { CoreTabsComponent } from './tabs'; import { Content } from 'ionic-angular'; +import { CoreDomUtilsProvider } from '../../providers/utils/dom'; +import { CoreNavBarButtonsComponent } from '../navbar-buttons/navbar-buttons'; /** * A tab to use inside core-tabs. The content of this tab will be displayed when the tab is selected. @@ -55,7 +57,7 @@ export class CoreTabComponent implements OnInit, OnDestroy { element: HTMLElement; // The core-tab element. loaded = false; - constructor(private tabs: CoreTabsComponent, element: ElementRef) { + constructor(protected tabs: CoreTabsComponent, element: ElementRef, protected domUtils: CoreDomUtilsProvider) { this.element = element.nativeElement; } @@ -81,6 +83,7 @@ export class CoreTabComponent implements OnInit, OnDestroy { this.loaded = true; this.ionSelect.emit(this); + this.showHideNavBarButtons(true); // Setup tab scrolling. setTimeout(() => { @@ -97,5 +100,39 @@ export class CoreTabComponent implements OnInit, OnDestroy { */ unselectTab(): void { this.element.classList.remove('selected'); + this.showHideNavBarButtons(false); + } + + /** + * Get all child core-navbar-buttons. We need to use querySelectorAll because ContentChildren doesn't work with ng-template. + * https://github.com/angular/angular/issues/14842 + * + * @return {CoreNavBarButtonsComponent[]} List of component instances. + */ + protected getChildrenNavBarButtons(): CoreNavBarButtonsComponent[] { + const elements = this.element.querySelectorAll('core-navbar-buttons'), + instances: CoreNavBarButtonsComponent[] = []; + + for (let i = 0; i < elements.length; i++) { + const instance = this.domUtils.getInstanceByElement(elements[i]); + if (instance) { + instances.push(instance); + } + } + + return instances; + } + + /** + * Show all hide all children navbar buttons. + * + * @param {boolean} show Whether to show or hide the buttons. + */ + protected showHideNavBarButtons(show: boolean): void { + const instances = this.getChildrenNavBarButtons(); + + for (const i in instances) { + instances[i].forceHide(!show); + } } } diff --git a/src/core/course/pages/section/section.html b/src/core/course/pages/section/section.html index 8420d3fb3..83bf61d17 100644 --- a/src/core/course/pages/section/section.html +++ b/src/core/course/pages/section/section.html @@ -2,12 +2,7 @@ - - - - - - + @@ -15,6 +10,12 @@ + + + + + + diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts index 233f98841..292cf4315 100644 --- a/src/providers/utils/dom.ts +++ b/src/providers/utils/dom.ts @@ -152,7 +152,7 @@ export class CoreDomUtilsProvider { this.element.innerHTML = html; elements = this.element.querySelectorAll('a, img, audio, video, source, track'); - for (const i in elements) { + for (let i = 0; i < elements.length; i++) { const element = elements[i]; let url = element.tagName === 'A' ? element.href : element.src; @@ -525,7 +525,7 @@ export class CoreDomUtilsProvider { if (removeAll) { selected = this.element.querySelectorAll(selector); - for (const i in selected) { + for (let i = 0; i < selected.length; i++) { selected[i].remove(); } } else { @@ -568,7 +568,7 @@ export class CoreDomUtilsProvider { for (const key in map) { const foundElements = element.querySelectorAll('.' + key); - for (const i in foundElements) { + for (let i = 0; i < foundElements.length; i++) { const foundElement = foundElements[i]; foundElement.className = foundElement.className.replace(key, map[key]); }