MOBILE-3814 collapsible: Listen to resize to recalculate size
parent
275ef0ac21
commit
bcb5d937ee
|
@ -14,6 +14,7 @@
|
|||
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { AddonModQuizDdImageOrTextQuestionData } from '../component/ddimageortext';
|
||||
|
||||
|
@ -28,7 +29,7 @@ export class AddonQtypeDdImageOrTextQuestion {
|
|||
protected afterImageLoadDone = false;
|
||||
protected proportion = 1;
|
||||
protected selected?: HTMLElement | null; // Selected element (being "dragged").
|
||||
protected resizeFunction?: (ev?: Event) => void;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
|
||||
/**
|
||||
* Create the this.
|
||||
|
@ -174,9 +175,7 @@ export class AddonQtypeDdImageOrTextQuestion {
|
|||
destroy(): void {
|
||||
this.stopPolling();
|
||||
|
||||
if (this.resizeFunction) {
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
this.resizeListener?.off();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -360,8 +359,9 @@ export class AddonQtypeDdImageOrTextQuestion {
|
|||
this.pollForImageLoad();
|
||||
});
|
||||
|
||||
this.resizeFunction = this.windowResized.bind(this);
|
||||
window.addEventListener('resize', this.resizeFunction!);
|
||||
this.resizeListener = CoreDomUtils.onWindowResize(() => {
|
||||
this.windowResized();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { AddonQtypeDdMarkerQuestionData } from '../component/ddmarker';
|
||||
import { AddonQtypeDdMarkerGraphicsApi } from './graphics_api';
|
||||
|
@ -41,7 +42,7 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
protected proportion = 1;
|
||||
protected selected?: HTMLElement; // Selected element (being "dragged").
|
||||
protected graphics: AddonQtypeDdMarkerGraphicsApi;
|
||||
protected resizeFunction?: () => void;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
|
||||
doc!: AddonQtypeDdMarkerQuestionDocStructure;
|
||||
shapes: SVGElement[] = [];
|
||||
|
@ -160,9 +161,7 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
* Function to call when the instance is no longer needed.
|
||||
*/
|
||||
destroy(): void {
|
||||
if (this.resizeFunction) {
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
this.resizeListener?.off();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -601,8 +600,9 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
this.pollForImageLoad();
|
||||
});
|
||||
|
||||
this.resizeFunction = this.windowResized.bind(this);
|
||||
window.addEventListener('resize', this.resizeFunction!);
|
||||
this.resizeListener = CoreDomUtils.onWindowResize(() => {
|
||||
this.windowResized();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { AddonModQuizDdwtosQuestionData } from '../component/ddwtos';
|
||||
|
||||
|
@ -28,13 +29,11 @@ export class AddonQtypeDdwtosQuestion {
|
|||
protected selectors!: AddonQtypeDdwtosQuestionCSSSelectors; // Result of cssSelectors.
|
||||
protected placed: {[no: number]: number} = {}; // Map that relates drag elements numbers with drop zones numbers.
|
||||
protected selected?: HTMLElement; // Selected element (being "dragged").
|
||||
protected resizeFunction?: () => void;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
|
||||
/**
|
||||
* Create the instance.
|
||||
*
|
||||
* @param logger Logger provider.
|
||||
* @param domUtils Dom Utils provider.
|
||||
* @param container The container HTMLElement of the question.
|
||||
* @param question The question instance.
|
||||
* @param readOnly Whether it's read only.
|
||||
|
@ -122,9 +121,7 @@ export class AddonQtypeDdwtosQuestion {
|
|||
* Function to call when the instance is no longer needed.
|
||||
*/
|
||||
destroy(): void {
|
||||
if (this.resizeFunction) {
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
this.resizeListener?.off();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,8 +211,9 @@ export class AddonQtypeDdwtosQuestion {
|
|||
|
||||
this.positionDragItems();
|
||||
|
||||
this.resizeFunction = this.windowResized.bind(this);
|
||||
window.addEventListener('resize', this.resizeFunction!);
|
||||
this.resizeListener = CoreDomUtils.onWindowResize(() => {
|
||||
this.windowResized();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,6 +32,8 @@ import { Subscription } from 'rxjs';
|
|||
import { Platform, Translate } from '@singletons';
|
||||
import { CoreSettingsHelper } from '@features/settings/services/settings-helper';
|
||||
import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from './aria-role-tab';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
|
||||
/**
|
||||
* Class to abstract some common code for tabs.
|
||||
|
@ -75,7 +77,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
|||
protected tabsElement?: HTMLElement; // The tabs parent element. It's the element that will be "scrolled" to hide tabs.
|
||||
protected tabBarElement?: HTMLIonTabBarElement; // The top tab bar element.
|
||||
protected tabsShown = true;
|
||||
protected resizeFunction: EventListenerOrEventListenerObject;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
protected isDestroyed = false;
|
||||
protected isCurrentView = true;
|
||||
protected shouldSlideToInitial = false; // Whether we need to slide to the initial slide because it's out of view.
|
||||
|
@ -99,7 +101,6 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
|||
protected element: ElementRef,
|
||||
) {
|
||||
this.backButtonFunction = this.backButtonClicked.bind(this);
|
||||
this.resizeFunction = this.windowResized.bind(this);
|
||||
|
||||
this.tabAction = new CoreTabsRoleTab(this);
|
||||
}
|
||||
|
@ -134,7 +135,9 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
|||
await this.initializeTabs();
|
||||
}
|
||||
|
||||
window.addEventListener('resize', this.resizeFunction);
|
||||
this.resizeListener = CoreDomUtils.onWindowResize(() => {
|
||||
this.windowResized();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -419,24 +422,24 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
|||
*/
|
||||
async slideNext(): Promise<void> {
|
||||
// Stop if slides are in transition.
|
||||
if (!this.showNextButton || this.isInTransition) {
|
||||
if (!this.showNextButton || this.isInTransition || !this.slides) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await this.slides!.isBeginning()) {
|
||||
if (await this.slides.isBeginning()) {
|
||||
// Slide to the second page.
|
||||
this.slides!.slideTo(this.maxSlides);
|
||||
this.slides.slideTo(this.maxSlides);
|
||||
} else {
|
||||
const currentIndex = await this.slides!.getActiveIndex();
|
||||
const currentIndex = await this.slides.getActiveIndex();
|
||||
if (currentIndex !== undefined) {
|
||||
const nextSlideIndex = currentIndex + this.maxSlides;
|
||||
this.isInTransition = true;
|
||||
if (nextSlideIndex < this.numTabsShown) {
|
||||
// Slide to the next page.
|
||||
await this.slides!.slideTo(nextSlideIndex);
|
||||
await this.slides.slideTo(nextSlideIndex);
|
||||
} else {
|
||||
// Slide to the latest slide.
|
||||
await this.slides!.slideTo(this.numTabsShown - 1);
|
||||
await this.slides.slideTo(this.numTabsShown - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,24 +451,24 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
|||
*/
|
||||
async slidePrev(): Promise<void> {
|
||||
// Stop if slides are in transition.
|
||||
if (!this.showPrevButton || this.isInTransition) {
|
||||
if (!this.showPrevButton || this.isInTransition || !this.slides) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await this.slides!.isEnd()) {
|
||||
this.slides!.slideTo(this.numTabsShown - this.maxSlides * 2);
|
||||
if (await this.slides.isEnd()) {
|
||||
this.slides.slideTo(this.numTabsShown - this.maxSlides * 2);
|
||||
// Slide to the previous of the latest page.
|
||||
} else {
|
||||
const currentIndex = await this.slides!.getActiveIndex();
|
||||
const currentIndex = await this.slides.getActiveIndex();
|
||||
if (currentIndex !== undefined) {
|
||||
const prevSlideIndex = currentIndex - this.maxSlides;
|
||||
this.isInTransition = true;
|
||||
if (prevSlideIndex >= 0) {
|
||||
// Slide to the previous page.
|
||||
await this.slides!.slideTo(prevSlideIndex);
|
||||
await this.slides.slideTo(prevSlideIndex);
|
||||
} else {
|
||||
// Slide to the first page.
|
||||
await this.slides!.slideTo(0);
|
||||
await this.slides.slideTo(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -646,9 +649,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
|||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
|
||||
if (this.resizeFunction) {
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
}
|
||||
this.resizeListener?.off();
|
||||
this.languageChangedSubscription?.unsubscribe();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import { CoreComponentsRegistry } from '@singletons/components-registry';
|
|||
import { CoreFormatTextDirective } from './format-text';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreLoadingComponent } from '@components/loading/loading';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
|
||||
/**
|
||||
* Directive to make an element fixed at the bottom collapsible when scrolling.
|
||||
|
@ -37,7 +38,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
@Input() appearOnBottom = false;
|
||||
|
||||
protected element: HTMLElement;
|
||||
protected initialHeight = 0;
|
||||
protected initialHeight = 48;
|
||||
protected finalHeight = 0;
|
||||
protected initialPaddingBottom = '0px';
|
||||
protected previousTop = 0;
|
||||
|
@ -46,22 +47,38 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
protected loadingChangedListener?: CoreEventObserver;
|
||||
protected contentScrollListener?: EventListener;
|
||||
protected endContentScrollListener?: EventListener;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
|
||||
constructor(el: ElementRef, protected ionContent: IonContent) {
|
||||
this.element = el.nativeElement;
|
||||
this.element.setAttribute('slot', 'fixed'); // Just in case somebody forgets to add it.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
// Only if not present or explicitly falsy it will be false.
|
||||
this.appearOnBottom = !CoreUtils.isFalseOrZero(this.appearOnBottom);
|
||||
|
||||
await CoreDomUtils.waitToBeInDOM(this.element);
|
||||
await this.waitLoadingsDone();
|
||||
await this.waitFormatTextsRendered(this.element);
|
||||
|
||||
await this.calculateHeight();
|
||||
|
||||
this.listenScrollEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the height of the footer.
|
||||
*/
|
||||
protected async calculateHeight(): Promise<void> {
|
||||
await this.waitFormatTextsRendered(this.element);
|
||||
|
||||
this.element.classList.remove('is-active');
|
||||
await CoreUtils.nextTick();
|
||||
|
||||
// Set a minimum height value.
|
||||
this.initialHeight = this.element.getBoundingClientRect().height || 48;
|
||||
this.initialHeight = this.element.getBoundingClientRect().height || this.initialHeight;
|
||||
const moduleNav = this.element.querySelector('core-course-module-navigation');
|
||||
if (moduleNav) {
|
||||
this.element.classList.add('has-module-nav');
|
||||
|
@ -71,6 +88,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
this.previousHeight = this.initialHeight;
|
||||
|
||||
this.content?.style.setProperty('--core-collapsible-footer-max-height', this.initialHeight + 'px');
|
||||
this.element.classList.add('is-active');
|
||||
|
||||
this.setBarHeight(this.initialHeight);
|
||||
}
|
||||
|
@ -131,6 +149,10 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
|
||||
this.setBarHeight(newHeight); }
|
||||
});
|
||||
|
||||
this.resizeListener = CoreDomUtils.onWindowResize(() => {
|
||||
this.calculateHeight();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,20 +202,6 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
this.previousHeight = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
// Only if not present or explicitly falsy it will be false.
|
||||
this.appearOnBottom = !CoreUtils.isFalseOrZero(this.appearOnBottom);
|
||||
|
||||
await this.waitLoadingsDone();
|
||||
|
||||
await this.calculateHeight();
|
||||
|
||||
this.listenScrollEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until all <core-loading> children inside the page.
|
||||
*
|
||||
|
@ -225,6 +233,8 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
if (this.content && this.endContentScrollListener) {
|
||||
this.content.removeEventListener('ionScrollEnd', this.endContentScrollListener);
|
||||
}
|
||||
|
||||
this.resizeListener?.off();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,8 +17,10 @@ import { CorePromisedValue } from '@classes/promised-value';
|
|||
import { CoreLoadingComponent } from '@components/loading/loading';
|
||||
import { CoreTabsOutletComponent } from '@components/tabs-outlet/tabs-outlet';
|
||||
import { ScrollDetail } from '@ionic/core';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreMath } from '@singletons/math';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CoreFormatTextDirective } from './format-text';
|
||||
|
@ -66,6 +68,7 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
|
|||
protected contentScrollListener?: EventListener;
|
||||
protected endContentScrollListener?: EventListener;
|
||||
protected pageDidEnterListener?: EventListener;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
protected floatingTitle?: HTMLHeadingElement;
|
||||
protected scrollingHeight?: number;
|
||||
protected subscriptions: Subscription[] = [];
|
||||
|
@ -120,6 +123,8 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
|
|||
if (this.page && this.pageDidEnterListener) {
|
||||
this.page.removeEventListener('ionViewDidEnter', this.pageDidEnterListener);
|
||||
}
|
||||
|
||||
this.resizeListener?.off();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -147,6 +152,10 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
|
|||
const timeout = window.setTimeout(() => {
|
||||
this.enteredPromise.reject(new Error('[collapsible-header] Waiting for ionViewDidEnter timeout reached'));
|
||||
}, 5000);
|
||||
|
||||
this.resizeListener = CoreDomUtils.onWindowResize(() => {
|
||||
this.initializeFloatingTitle();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -217,16 +226,26 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
|
|||
throw new Error('[collapsible-header] Couldn\'t create floating title');
|
||||
}
|
||||
|
||||
this.page.classList.remove('is-active');
|
||||
CoreUtils.nextTick();
|
||||
|
||||
// Add floating title and measure initial position.
|
||||
const collapsedHeaderTitle = this.collapsedHeader.querySelector('h1') as HTMLHeadingElement;
|
||||
const originalTitle = this.expandedHeader.querySelector('h1') as HTMLHeadingElement;
|
||||
const floatingTitleWrapper = originalTitle.parentElement as HTMLElement;
|
||||
const floatingTitle = originalTitle.cloneNode(true) as HTMLHeadingElement;
|
||||
const originalTitle = this.expandedHeader.querySelector('h1.collapsible-header-original-title') ||
|
||||
this.expandedHeader.querySelector('h1') as HTMLHeadingElement;
|
||||
|
||||
originalTitle.classList.add('collapsible-header-original-title');
|
||||
floatingTitle.classList.add('collapsible-header-floating-title');
|
||||
floatingTitleWrapper.classList.add('collapsible-header-floating-title-wrapper');
|
||||
floatingTitleWrapper.insertBefore(floatingTitle, originalTitle);
|
||||
const floatingTitleWrapper = originalTitle.parentElement as HTMLElement;
|
||||
let floatingTitle = floatingTitleWrapper.querySelector('.collapsible-header-floating-title') as HTMLHeadingElement;
|
||||
if (!floatingTitle) {
|
||||
// First time, create it.
|
||||
floatingTitle = originalTitle.cloneNode(true) as HTMLHeadingElement;
|
||||
floatingTitle.classList.add('collapsible-header-floating-title');
|
||||
|
||||
floatingTitleWrapper.classList.add('collapsible-header-floating-title-wrapper');
|
||||
floatingTitleWrapper.insertBefore(floatingTitle, originalTitle);
|
||||
|
||||
originalTitle.classList.add('collapsible-header-original-title');
|
||||
}
|
||||
|
||||
const floatingTitleBoundingBox = floatingTitle.getBoundingClientRect();
|
||||
|
||||
|
|
|
@ -12,12 +12,13 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Directive, ElementRef, Input, OnInit } from '@angular/core';
|
||||
import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { CoreLoadingComponent } from '@components/loading/loading';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { Translate } from '@singletons';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreFormatTextDirective } from './format-text';
|
||||
|
||||
const defaultMaxHeight = 80;
|
||||
|
@ -33,7 +34,7 @@ const minMaxHeight = 56;
|
|||
@Directive({
|
||||
selector: '[collapsible-item]',
|
||||
})
|
||||
export class CoreCollapsibleItemDirective implements OnInit {
|
||||
export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
|
||||
|
||||
/**
|
||||
* Max height in pixels to render the content box. It should be 56 at least to make sense.
|
||||
|
@ -47,6 +48,7 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
protected expanded = false;
|
||||
protected maxHeight = defaultMaxHeight;
|
||||
protected expandedHeight = 0;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
|
||||
constructor(el: ElementRef<HTMLElement>) {
|
||||
this.element = el.nativeElement;
|
||||
|
@ -81,6 +83,10 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
await this.waitLoadingsDone();
|
||||
|
||||
await this.calculateHeight();
|
||||
|
||||
this.resizeListener = CoreDomUtils.onWindowResize(() => {
|
||||
this.calculateHeight();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,7 +132,7 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
|
||||
await this.waitFormatTextsRendered(this.element);
|
||||
|
||||
this.expandedHeight = CoreDomUtils.getElementHeight(this.element) || 0;
|
||||
this.expandedHeight = this.element.getBoundingClientRect().height;
|
||||
|
||||
// Restore the max height now.
|
||||
this.element.classList.remove('collapsible-loading-height');
|
||||
|
@ -229,4 +235,11 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
this.toggleExpand();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.resizeListener?.off();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import { filter } from 'rxjs/operators';
|
|||
import { NavigationEnd } from '@angular/router';
|
||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
|
||||
/**
|
||||
* Page that displays the main menu of the app.
|
||||
|
@ -73,7 +74,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
|
|||
protected navSubscription?: Subscription;
|
||||
protected keyboardObserver?: CoreEventObserver;
|
||||
protected badgeUpdateObserver?: CoreEventObserver;
|
||||
protected resizeFunction: () => void;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
protected backButtonFunction: (event: BackButtonEvent) => void;
|
||||
protected selectHistory: string[] = [];
|
||||
protected firstSelectedTab?: string;
|
||||
|
@ -86,7 +87,6 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
|
|||
tabAction: CoreMainMenuRoleTab;
|
||||
|
||||
constructor() {
|
||||
this.resizeFunction = this.initHandlers.bind(this);
|
||||
this.backButtonFunction = this.backButtonClicked.bind(this);
|
||||
this.tabAction = new CoreMainMenuRoleTab(this);
|
||||
|
||||
|
@ -122,7 +122,9 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
|
|||
}
|
||||
});
|
||||
|
||||
window.addEventListener('resize', this.resizeFunction);
|
||||
this.resizeListener = CoreDomUtils.onWindowResize(() => {
|
||||
this.initHandlers();
|
||||
});
|
||||
document.addEventListener('ionBackButton', this.backButtonFunction);
|
||||
|
||||
if (CoreApp.isIOS()) {
|
||||
|
@ -221,10 +223,10 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
|
|||
ngOnDestroy(): void {
|
||||
this.subscription?.unsubscribe();
|
||||
this.navSubscription?.unsubscribe();
|
||||
window.removeEventListener('resize', this.resizeFunction);
|
||||
document.removeEventListener('ionBackButton', this.backButtonFunction);
|
||||
this.keyboardObserver?.off();
|
||||
this.badgeUpdateObserver?.off();
|
||||
this.resizeListener?.off();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ import { CoreContentLinksHelper } from '@features/contentlinks/services/contentl
|
|||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { Translate } from '@singletons';
|
||||
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
|
||||
/**
|
||||
* Page that displays the more page of the app.
|
||||
|
@ -46,6 +47,7 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy {
|
|||
protected subscription!: Subscription;
|
||||
protected langObserver: CoreEventObserver;
|
||||
protected updateSiteObserver: CoreEventObserver;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
|
||||
constructor() {
|
||||
this.langObserver = CoreEvents.on(CoreEvents.LANGUAGE_CHANGED, this.loadCustomMenuItems.bind(this));
|
||||
|
@ -71,7 +73,9 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy {
|
|||
this.initHandlers();
|
||||
});
|
||||
|
||||
window.addEventListener('resize', this.initHandlers.bind(this));
|
||||
this.resizeListener = CoreDomUtils.onWindowResize(() => {
|
||||
this.initHandlers();
|
||||
});
|
||||
|
||||
const deepLinkManager = new CoreMainMenuDeepLinkManager();
|
||||
deepLinkManager.treatLink();
|
||||
|
@ -81,10 +85,10 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy {
|
|||
* @inheritdoc
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
window.removeEventListener('resize', this.initHandlers.bind(this));
|
||||
this.langObserver?.off();
|
||||
this.updateSiteObserver?.off();
|
||||
this.subscription?.unsubscribe();
|
||||
this.resizeListener?.off();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -53,6 +53,7 @@ import { NavigationStart } from '@angular/router';
|
|||
import { filter } from 'rxjs/operators';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
|
||||
/*
|
||||
* "Utils" service with helper functions for UI, DOM elements and HTML code.
|
||||
|
@ -127,6 +128,28 @@ export class CoreDomUtilsProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Window resize is widely checked and may have many performance issues, debouce usage is needed to avoid calling it too much.
|
||||
* This function helps setting up the debounce feature and remove listener easily.
|
||||
*
|
||||
* @param resizeFunction Function to execute on resize.
|
||||
* @param debounceDelay Debounce time in ms.
|
||||
* @return Event observer to call off when finished.
|
||||
*/
|
||||
onWindowResize(resizeFunction: (ev?: Event) => void, debounceDelay = 20): CoreEventObserver {
|
||||
const resizeListener = CoreUtils.debounce((ev?: Event) => {
|
||||
resizeFunction(ev);
|
||||
}, debounceDelay);
|
||||
|
||||
window.addEventListener('resize', resizeListener);
|
||||
|
||||
return {
|
||||
off: (): void => {
|
||||
window.removeEventListener('resize', resizeListener);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to element.closest(). If the browser doesn't support element.closest, it will
|
||||
* traverse the parents to achieve the same functionality.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
--collapsible-header-floating-title-width: 0px;
|
||||
--collapsible-header-floating-title-x-delta: 0px;
|
||||
--collapsible-header-floating-title-width-delta: 0px;
|
||||
|
||||
ion-header.core-header-shadow {
|
||||
--core-header-shadow: none;
|
||||
}
|
||||
|
@ -19,9 +20,8 @@
|
|||
|
||||
.collapsible-header-floating-title {
|
||||
position: absolute;
|
||||
top: var(--collapsible-header-floating-title-top);
|
||||
left: var(--collapsible-header-floating-title-left);
|
||||
transform: translateX(calc(var(--collapsible-header-floating-title-x-delta) * var(--collapsible-header-progress)));
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,9 @@
|
|||
|
||||
.collapsible-header-floating-title {
|
||||
opacity: 1;
|
||||
top: var(--collapsible-header-floating-title-top);
|
||||
left: var(--collapsible-header-floating-title-left);
|
||||
transform: translateX(calc(var(--collapsible-header-floating-title-x-delta) * var(--collapsible-header-progress)));
|
||||
width: calc(var(--collapsible-header-floating-title-width) + var(--collapsible-header-progress) * var(--collapsible-header-floating-title-width-delta));
|
||||
|
||||
@include core-transition(width transform, 200ms, linear);
|
||||
|
|
|
@ -1421,37 +1421,42 @@ ion-grid.core-no-grid > ion-row {
|
|||
}
|
||||
|
||||
[collapsible-footer] {
|
||||
&.footer-collapsed {
|
||||
--core-collapsible-footer-height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
&.has-module-nav.footer-collapsed {
|
||||
--core-collapsible-footer-height: auto;
|
||||
opacity: 1;
|
||||
core-course-module-navigation {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
@include core-transition(all, 200ms);
|
||||
}
|
||||
|
||||
}
|
||||
&.footer-expanded {
|
||||
--core-collapsible-footer-height: auto;
|
||||
}
|
||||
box-shadow: var(--drop-shadow-top, none);
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
display: block;
|
||||
background-color: var(--core-collapsible-footer-background);
|
||||
|
||||
.ion-margin {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
box-shadow: var(--drop-shadow-top, none);
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
height: var(--core-collapsible-footer-height, auto);
|
||||
background-color: var(--core-collapsible-footer-background);
|
||||
display: block;
|
||||
@include core-transition(all, 200ms);
|
||||
&.is-active {
|
||||
height: var(--core-collapsible-footer-height, auto);
|
||||
@include core-transition(all, 200ms);
|
||||
|
||||
&.footer-collapsed {
|
||||
--core-collapsible-footer-height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
&.has-module-nav.footer-collapsed {
|
||||
--core-collapsible-footer-height: auto;
|
||||
opacity: 1;
|
||||
core-course-module-navigation {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
@include core-transition(all, 200ms);
|
||||
}
|
||||
|
||||
}
|
||||
&.footer-expanded {
|
||||
--core-collapsible-footer-height: auto;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.core-iframe-fullscreen [collapsible-footer] {
|
||||
|
|
|
@ -329,7 +329,6 @@
|
|||
--core-courseimage-on-course-size: 72px;
|
||||
--core-courseimage-radius: var(--medium-radius);
|
||||
|
||||
--core-collapsible-footer-height: 48px;
|
||||
--core-navigation-background: var(--contrast-background);
|
||||
|
||||
--core-collapsible-footer-background: var(--contrast-background);
|
||||
|
|
Loading…
Reference in New Issue