diff --git a/src/core/features/course/components/course-format/course-format.ts b/src/core/features/course/components/course-format/course-format.ts index 4f100845e..83ff3280f 100644 --- a/src/core/features/course/components/course-format/course-format.ts +++ b/src/core/features/course/components/course-format/course-format.ts @@ -514,6 +514,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { CoreDomUtils.scrollViewToElement( this.elementRef.nativeElement, '#core-course-module-' + moduleId, + { addYAxis: -10 }, ); } diff --git a/src/core/features/course/components/course-index/course-index.ts b/src/core/features/course/components/course-index/course-index.ts index 6643c02f8..2d2141df6 100644 --- a/src/core/features/course/components/course-index/course-index.ts +++ b/src/core/features/course/components/course-index/course-index.ts @@ -112,6 +112,7 @@ export class CoreCourseCourseIndexComponent implements OnInit { CoreDomUtils.scrollViewToElement( this.elementRef.nativeElement, '.item.item-current', + { addYAxis: -10 }, ); } diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index 797faa9fb..ccb08d6f6 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -94,7 +94,7 @@ export class CoreDomUtilsProvider { } /** - * Wait an element to be in dom of another element. + * Wait an element to be added to the root DOM. * * @param element Element to wait. * @return Cancellable promise. @@ -130,6 +130,44 @@ export class CoreDomUtilsProvider { ); } + /** + * Wait an element to be in dom of another element using a selector + * + * @param container Element to wait. + * @return Cancellable promise. + */ + async waitToBeInsideElement(container: HTMLElement, selector: string): Promise> { + await CoreDomUtils.waitToBeInDOM(container); + + let element = container.querySelector(selector); + if (element) { + // Already in DOM. + return CoreCancellablePromise.resolve(element); + } + + let observer: MutationObserver; + + return new CoreCancellablePromise( + (resolve) => { + observer = new MutationObserver(() => { + element = container.querySelector(selector); + + if (!element) { + return; + } + + observer?.disconnect(); + resolve(element); + }); + + observer.observe(container, { subtree: true, childList: true }); + }, + () => { + observer?.disconnect(); + }, + ); + } + /** * Wait an element to be in dom and visible. * @@ -889,7 +927,7 @@ export class CoreDomUtilsProvider { * * @param findFunction The function used to find the element. * @return Resolved if found, rejected if too many tries. - * @deprecated since app 4.0 Use waitToBeInDOM instead. + * @deprecated since app 4.0 Use waitToBeInsideElement instead. */ waitElementToExist(findFunction: () => HTMLElement | null): Promise { const promiseInterval = CoreUtils.promiseDefer(); @@ -1320,14 +1358,12 @@ export class CoreDomUtilsProvider { * * @param element The element to scroll to. * @param selector Selector to find the element to scroll to inside the defined element. - * @param duration Duration of the scroll animation in milliseconds. + * @param scrollOptions Scroll Options. * @return Wether the scroll suceeded. */ - async scrollViewToElement(element: HTMLElement, selector?: string, duration = 0): Promise { - await CoreDomUtils.waitToBeInDOM(element); - + async scrollViewToElement(element: HTMLElement, selector?: string, scrollOptions: CoreScrollOptions = {}): Promise { if (selector) { - const foundElement = element.querySelector(selector); + const foundElement = await CoreDomUtils.waitToBeInsideElement(element, selector); if (!foundElement) { // Element not found. return false; @@ -1336,16 +1372,28 @@ export class CoreDomUtilsProvider { element = foundElement; } + await CoreDomUtils.waitToBeVisible(element); + const content = element.closest('ion-content') ?? undefined; if (!content) { + // Content to scroll, not found. return false; } try { const position = CoreDomUtils.getRelativeElementPosition(element, content); + const scrollElement = await content.getScrollElement(); - await content.scrollToPoint(position.x, position.y, duration); + scrollOptions.duration = scrollOptions.duration ?? 200; + scrollOptions.addXAxis = scrollOptions.addXAxis ?? 0; + scrollOptions.addYAxis = scrollOptions.addYAxis ?? 0; + + await content.scrollToPoint( + position.x + scrollElement.scrollLeft + scrollOptions.addXAxis, + position.y + scrollElement.scrollTop + scrollOptions.addYAxis, + scrollOptions.duration, + ); return true; } catch { @@ -1373,8 +1421,8 @@ export class CoreDomUtilsProvider { * @return True if the element is found, false otherwise. * @deprecated since app 4.0 Use scrollViewToElement instead. */ - scrollToElement(content: IonContent, element: HTMLElement, scrollParentClass?: string, duration = 0): boolean { - CoreDomUtils.scrollViewToElement(element, undefined, duration); + scrollToElement(content: IonContent, element: HTMLElement, scrollParentClass?: string, duration?: number): boolean { + CoreDomUtils.scrollViewToElement(element, undefined, { duration }); return true; } @@ -1395,13 +1443,13 @@ export class CoreDomUtilsProvider { content: unknown | null, selector: string, scrollParentClass?: string, - duration = 0, + duration?: number, ): boolean { if (!container || !content) { return false; } - CoreDomUtils.scrollViewToElement(container, selector, duration); + CoreDomUtils.scrollViewToElement(container, selector, { duration }); return true; @@ -2435,3 +2483,12 @@ export type CoreCoordinates = { x: number; // X axis coordinates. y: number; // Y axis coordinates. }; + +/** + * Scroll options. + */ +export type CoreScrollOptions = { + duration?: number; + addYAxis?: number; + addXAxis?: number; +};