MOBILE-3814 dom: Fix scroll to element with selector

main
Pau Ferrer Ocaña 2022-03-22 10:09:54 +01:00
parent be28e1c2e4
commit ac5e4b1d79
3 changed files with 71 additions and 12 deletions

View File

@ -514,6 +514,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
CoreDomUtils.scrollViewToElement( CoreDomUtils.scrollViewToElement(
this.elementRef.nativeElement, this.elementRef.nativeElement,
'#core-course-module-' + moduleId, '#core-course-module-' + moduleId,
{ addYAxis: -10 },
); );
} }

View File

@ -112,6 +112,7 @@ export class CoreCourseCourseIndexComponent implements OnInit {
CoreDomUtils.scrollViewToElement( CoreDomUtils.scrollViewToElement(
this.elementRef.nativeElement, this.elementRef.nativeElement,
'.item.item-current', '.item.item-current',
{ addYAxis: -10 },
); );
} }

View File

@ -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. * @param element Element to wait.
* @return Cancellable promise. * @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<CoreCancellablePromise<HTMLElement>> {
await CoreDomUtils.waitToBeInDOM(container);
let element = container.querySelector<HTMLElement>(selector);
if (element) {
// Already in DOM.
return CoreCancellablePromise.resolve(element);
}
let observer: MutationObserver;
return new CoreCancellablePromise<HTMLElement>(
(resolve) => {
observer = new MutationObserver(() => {
element = container.querySelector<HTMLElement>(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. * 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. * @param findFunction The function used to find the element.
* @return Resolved if found, rejected if too many tries. * @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<HTMLElement> { waitElementToExist(findFunction: () => HTMLElement | null): Promise<HTMLElement> {
const promiseInterval = CoreUtils.promiseDefer<HTMLElement>(); const promiseInterval = CoreUtils.promiseDefer<HTMLElement>();
@ -1320,14 +1358,12 @@ export class CoreDomUtilsProvider {
* *
* @param element The element to scroll to. * @param element The element to scroll to.
* @param selector Selector to find the element to scroll to inside the defined element. * @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. * @return Wether the scroll suceeded.
*/ */
async scrollViewToElement(element: HTMLElement, selector?: string, duration = 0): Promise<boolean> { async scrollViewToElement(element: HTMLElement, selector?: string, scrollOptions: CoreScrollOptions = {}): Promise<boolean> {
await CoreDomUtils.waitToBeInDOM(element);
if (selector) { if (selector) {
const foundElement = element.querySelector<HTMLElement>(selector); const foundElement = await CoreDomUtils.waitToBeInsideElement(element, selector);
if (!foundElement) { if (!foundElement) {
// Element not found. // Element not found.
return false; return false;
@ -1336,16 +1372,28 @@ export class CoreDomUtilsProvider {
element = foundElement; element = foundElement;
} }
await CoreDomUtils.waitToBeVisible(element);
const content = element.closest<HTMLIonContentElement>('ion-content') ?? undefined; const content = element.closest<HTMLIonContentElement>('ion-content') ?? undefined;
if (!content) { if (!content) {
// Content to scroll, not found. // Content to scroll, not found.
return false; return false;
} }
try { try {
const position = CoreDomUtils.getRelativeElementPosition(element, content); 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; return true;
} catch { } catch {
@ -1373,8 +1421,8 @@ export class CoreDomUtilsProvider {
* @return True if the element is found, false otherwise. * @return True if the element is found, false otherwise.
* @deprecated since app 4.0 Use scrollViewToElement instead. * @deprecated since app 4.0 Use scrollViewToElement instead.
*/ */
scrollToElement(content: IonContent, element: HTMLElement, scrollParentClass?: string, duration = 0): boolean { scrollToElement(content: IonContent, element: HTMLElement, scrollParentClass?: string, duration?: number): boolean {
CoreDomUtils.scrollViewToElement(element, undefined, duration); CoreDomUtils.scrollViewToElement(element, undefined, { duration });
return true; return true;
} }
@ -1395,13 +1443,13 @@ export class CoreDomUtilsProvider {
content: unknown | null, content: unknown | null,
selector: string, selector: string,
scrollParentClass?: string, scrollParentClass?: string,
duration = 0, duration?: number,
): boolean { ): boolean {
if (!container || !content) { if (!container || !content) {
return false; return false;
} }
CoreDomUtils.scrollViewToElement(container, selector, duration); CoreDomUtils.scrollViewToElement(container, selector, { duration });
return true; return true;
@ -2435,3 +2483,12 @@ export type CoreCoordinates = {
x: number; // X axis coordinates. x: number; // X axis coordinates.
y: number; // Y axis coordinates. y: number; // Y axis coordinates.
}; };
/**
* Scroll options.
*/
export type CoreScrollOptions = {
duration?: number;
addYAxis?: number;
addXAxis?: number;
};