MOBILE-3814 dom: Improve scroll handling
parent
dbc91004e4
commit
a76914f25a
|
@ -1106,10 +1106,10 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
|
|||
* Scroll to the first new unread message.
|
||||
*/
|
||||
scrollToFirstUnreadMessage(): void {
|
||||
if (this.newMessages > 0 && this.content) {
|
||||
const messages = Array.from(this.hostElement.querySelectorAll('.addon-message-not-mine'));
|
||||
if (this.newMessages > 0) {
|
||||
const messages = Array.from(this.hostElement.querySelectorAll<HTMLElement>('.addon-message-not-mine'));
|
||||
|
||||
CoreDomUtils.scrollToElement(this.content, <HTMLElement> messages[messages.length - this.newMessages]);
|
||||
CoreDomUtils.scrollViewToElement(messages[messages.length - this.newMessages]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave {
|
|||
this.messages[this.messages.length - 1].showTail = true;
|
||||
|
||||
// New messages or beeps, scroll to bottom.
|
||||
setTimeout(() => this.scrollToBottom());
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
protected async loadMessageBeepWho(message: AddonModChatFormattedMessage): Promise<void> {
|
||||
|
@ -341,13 +341,12 @@ export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave {
|
|||
/**
|
||||
* Scroll bottom when render has finished.
|
||||
*/
|
||||
scrollToBottom(): void {
|
||||
async scrollToBottom(): Promise<void> {
|
||||
// Need a timeout to leave time to the view to be rendered.
|
||||
setTimeout(() => {
|
||||
if (!this.viewDestroyed) {
|
||||
this.content?.scrollToBottom();
|
||||
}
|
||||
});
|
||||
await CoreUtils.nextTick();
|
||||
if (!this.viewDestroyed) {
|
||||
this.content?.scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -352,9 +352,7 @@ export class AddonModDataEditPage implements OnInit {
|
|||
}
|
||||
this.jsData!.errors = this.errors;
|
||||
|
||||
setTimeout(() => {
|
||||
this.scrollToFirstError();
|
||||
});
|
||||
this.scrollToFirstError();
|
||||
}
|
||||
} finally {
|
||||
modal.dismiss();
|
||||
|
@ -449,8 +447,9 @@ export class AddonModDataEditPage implements OnInit {
|
|||
/**
|
||||
* Scroll to first error or to the top if not found.
|
||||
*/
|
||||
protected scrollToFirstError(): void {
|
||||
if (!CoreDomUtils.scrollToElementBySelector(this.formElement.nativeElement, this.content, '.addon-data-error')) {
|
||||
protected async scrollToFirstError(): Promise<void> {
|
||||
const scrolled = await CoreDomUtils.scrollViewToElement(this.formElement.nativeElement, '.addon-data-error');
|
||||
if (!scrolled) {
|
||||
this.content?.scrollToTop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import {
|
|||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Optional,
|
||||
Output,
|
||||
SimpleChange,
|
||||
ViewChild,
|
||||
|
@ -41,7 +40,6 @@ import {
|
|||
import { CoreTag } from '@features/tag/services/tag';
|
||||
import { Translate } from '@singletons';
|
||||
import { CoreFileUploader } from '@features/fileuploader/services/fileuploader';
|
||||
import { IonContent } from '@ionic/angular';
|
||||
import { AddonModForumSync } from '../../services/forum-sync';
|
||||
import { CoreSync } from '@services/sync';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
|
@ -94,7 +92,6 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
|||
|
||||
constructor(
|
||||
protected elementRef: ElementRef,
|
||||
@Optional() protected content?: IonContent,
|
||||
) {}
|
||||
|
||||
get showForm(): boolean {
|
||||
|
@ -308,8 +305,8 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
|||
this.post.id > 0 ? this.post.id : undefined,
|
||||
);
|
||||
|
||||
this.scrollToForm(5);
|
||||
} catch (error) {
|
||||
this.scrollToForm();
|
||||
} catch {
|
||||
// Cancelled.
|
||||
}
|
||||
}
|
||||
|
@ -540,19 +537,11 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
|
|||
/**
|
||||
* Scroll to reply/edit form.
|
||||
*
|
||||
* @param ticksToWait Number of ticks to wait before scrolling.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async scrollToForm(ticksToWait = 1): Promise<void> {
|
||||
if (!this.content) {
|
||||
return;
|
||||
}
|
||||
|
||||
await CoreUtils.nextTicks(ticksToWait);
|
||||
|
||||
CoreDomUtils.scrollToElementBySelector(
|
||||
protected async scrollToForm(): Promise<void> {
|
||||
await CoreDomUtils.scrollViewToElement(
|
||||
this.elementRef.nativeElement,
|
||||
this.content,
|
||||
'#addon-forum-reply-edit-form-' + this.uniqueId,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -187,13 +187,10 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
|
|||
const scrollTo = this.postId || this.parent;
|
||||
if (scrollTo) {
|
||||
// Scroll to the post.
|
||||
setTimeout(() => {
|
||||
CoreDomUtils.scrollToElementBySelector(
|
||||
this.elementRef.nativeElement,
|
||||
this.content,
|
||||
'#addon-mod_forum-post-' + scrollTo,
|
||||
);
|
||||
});
|
||||
CoreDomUtils.scrollViewToElement(
|
||||
this.elementRef.nativeElement,
|
||||
'#addon-mod_forum-post-' + scrollTo,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
import { Component, OnInit, ViewChild, ElementRef, Input, Type } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { IonContent } from '@ionic/angular';
|
||||
import { CoreSites } from '@services/sites';
|
||||
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
|
@ -32,7 +31,6 @@ import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '../../service
|
|||
})
|
||||
export class AddonModQuizPreflightModalComponent implements OnInit {
|
||||
|
||||
@ViewChild(IonContent) content?: IonContent;
|
||||
@ViewChild('preflightFormEl') formElement?: ElementRef;
|
||||
|
||||
@Input() title!: string;
|
||||
|
@ -111,15 +109,14 @@ export class AddonModQuizPreflightModalComponent implements OnInit {
|
|||
*
|
||||
* @param e Event.
|
||||
*/
|
||||
sendData(e: Event): void {
|
||||
async sendData(e: Event): Promise<void> {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (!this.preflightForm.valid) {
|
||||
// Form not valid. Scroll to the first element with errors.
|
||||
const hasScrolled = CoreDomUtils.scrollToInputError(
|
||||
const hasScrolled = await CoreDomUtils.scrollViewToInputError(
|
||||
this.elementRef.nativeElement,
|
||||
this.content,
|
||||
);
|
||||
|
||||
if (!hasScrolled) {
|
||||
|
|
|
@ -318,10 +318,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
|||
this.loaded = true;
|
||||
|
||||
if (slot !== undefined) {
|
||||
// Scroll to the question. Give some time to the questions to render.
|
||||
setTimeout(() => {
|
||||
this.scrollToQuestion(slot);
|
||||
}, 2000);
|
||||
// Scroll to the question.
|
||||
this.scrollToQuestion(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -689,9 +687,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
|||
* @param slot Slot of the question to scroll to.
|
||||
*/
|
||||
protected scrollToQuestion(slot: number): void {
|
||||
CoreDomUtils.scrollToElementBySelector(
|
||||
CoreDomUtils.scrollViewToElement(
|
||||
this.elementRef.nativeElement,
|
||||
this.content,
|
||||
'#addon-mod_quiz-question-' + slot,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -133,10 +133,8 @@ export class AddonModQuizReviewPage implements OnInit {
|
|||
this.loaded = true;
|
||||
|
||||
if (slot !== undefined) {
|
||||
// Scroll to the question. Give some time to the questions to render.
|
||||
setTimeout(() => {
|
||||
this.scrollToQuestion(slot);
|
||||
}, 2000);
|
||||
// Scroll to the question.
|
||||
this.scrollToQuestion(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -249,9 +247,8 @@ export class AddonModQuizReviewPage implements OnInit {
|
|||
* @param slot Slot of the question to scroll to.
|
||||
*/
|
||||
protected scrollToQuestion(slot: number): void {
|
||||
CoreDomUtils.scrollToElementBySelector(
|
||||
CoreDomUtils.scrollViewToElement(
|
||||
this.elementRef.nativeElement,
|
||||
this.content,
|
||||
`#addon-mod_quiz-question-${slot}`,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -142,11 +142,13 @@ export class CoreLinkDirective implements OnInit {
|
|||
if (href.charAt(0) == '#') {
|
||||
// Look for id or name.
|
||||
href = href.substring(1);
|
||||
CoreDomUtils.scrollToElementBySelector(
|
||||
this.element.closest('ion-content'),
|
||||
this.content,
|
||||
`#${href}, [name='${href}']`,
|
||||
);
|
||||
const container = this.element.closest('ion-content');
|
||||
if (container) {
|
||||
CoreDomUtils.scrollViewToElement(
|
||||
container,
|
||||
`#${href}, [name='${href}']`,
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -492,9 +492,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
// Scroll to module if needed. Give more priority to the input.
|
||||
const moduleIdToScroll = this.moduleId && previousValue === undefined ? this.moduleId : moduleId;
|
||||
if (moduleIdToScroll) {
|
||||
setTimeout(() => {
|
||||
this.scrollToModule(moduleIdToScroll);
|
||||
}, 200);
|
||||
this.scrollToModule(moduleIdToScroll);
|
||||
} else {
|
||||
this.content.scrollToTop(0);
|
||||
}
|
||||
|
@ -513,9 +511,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
* @param moduleId Module ID.
|
||||
*/
|
||||
protected scrollToModule(moduleId: number): void {
|
||||
CoreDomUtils.scrollToElementBySelector(
|
||||
CoreDomUtils.scrollViewToElement(
|
||||
this.elementRef.nativeElement,
|
||||
this.content,
|
||||
'#core-course-module-' + moduleId,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { Component, ElementRef, Input, OnInit } from '@angular/core';
|
||||
import {
|
||||
CoreCourseModuleCompletionStatus,
|
||||
CoreCourseModuleCompletionTracking,
|
||||
|
@ -21,7 +21,6 @@ import {
|
|||
import { CoreCourseHelper, CoreCourseSection } from '@features/course/services/course-helper';
|
||||
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
|
||||
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
||||
import { IonContent } from '@ionic/angular';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { ModalController } from '@singletons';
|
||||
|
||||
|
@ -35,8 +34,6 @@ import { ModalController } from '@singletons';
|
|||
})
|
||||
export class CoreCourseCourseIndexComponent implements OnInit {
|
||||
|
||||
@ViewChild(IonContent) content?: IonContent;
|
||||
|
||||
@Input() sections: CoreCourseSection[] = [];
|
||||
@Input() selectedId?: number;
|
||||
@Input() course?: CoreCourseAnyCourseData;
|
||||
|
@ -112,13 +109,10 @@ export class CoreCourseCourseIndexComponent implements OnInit {
|
|||
|
||||
this.highlighted = CoreCourseFormatDelegate.getSectionHightlightedName(this.course);
|
||||
|
||||
setTimeout(() => {
|
||||
CoreDomUtils.scrollToElementBySelector(
|
||||
this.elementRef.nativeElement,
|
||||
this.content,
|
||||
'.item.item-current',
|
||||
);
|
||||
}, 300);
|
||||
CoreDomUtils.scrollViewToElement(
|
||||
this.elementRef.nativeElement,
|
||||
'.item.item-current',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AfterViewInit, Component, ElementRef, OnDestroy, Optional } from '@angular/core';
|
||||
import { IonContent, IonRefresher } from '@ionic/angular';
|
||||
import { AfterViewInit, Component, ElementRef, OnDestroy } from '@angular/core';
|
||||
import { IonRefresher } from '@ionic/angular';
|
||||
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreGrades } from '@features/grades/services/grades';
|
||||
|
@ -59,7 +59,6 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
|
|||
constructor(
|
||||
protected route: ActivatedRoute,
|
||||
protected element: ElementRef<HTMLElement>,
|
||||
@Optional() protected content?: IonContent,
|
||||
) {
|
||||
try {
|
||||
this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId', { route });
|
||||
|
@ -170,11 +169,9 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
|
|||
|
||||
if (row) {
|
||||
this.toggleRow(row, true);
|
||||
await CoreUtils.nextTick();
|
||||
|
||||
CoreDomUtils.scrollToElementBySelector(
|
||||
CoreDomUtils.scrollViewToElement(
|
||||
this.element.nativeElement,
|
||||
this.content,
|
||||
'#grade-' + row.id,
|
||||
);
|
||||
this.gradeId = undefined;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
import { Component, ViewChild, ElementRef, OnInit, ChangeDetectorRef } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
|
||||
import { IonContent, IonRefresher } from '@ionic/angular';
|
||||
import { IonRefresher } from '@ionic/angular';
|
||||
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
|
@ -46,7 +46,6 @@ import { CoreText } from '@singletons/text';
|
|||
})
|
||||
export class CoreLoginEmailSignupPage implements OnInit {
|
||||
|
||||
@ViewChild(IonContent) content?: IonContent;
|
||||
@ViewChild(CoreRecaptchaComponent) recaptchaComponent?: CoreRecaptchaComponent;
|
||||
@ViewChild('ageForm') ageFormElement?: ElementRef;
|
||||
@ViewChild('signupFormEl') signupFormElement?: ElementRef;
|
||||
|
@ -285,9 +284,8 @@ export class CoreLoginEmailSignupPage implements OnInit {
|
|||
this.changeDetector.detectChanges();
|
||||
|
||||
// Scroll to the first element with errors.
|
||||
const errorFound = CoreDomUtils.scrollToInputError(
|
||||
const errorFound = await CoreDomUtils.scrollViewToInputError(
|
||||
this.elementRef.nativeElement,
|
||||
this.content,
|
||||
);
|
||||
|
||||
if (!errorFound) {
|
||||
|
|
|
@ -725,10 +725,14 @@ export class CoreDomUtilsProvider {
|
|||
* @param positionParentClass Parent Class where to stop calculating the position. Default inner-scroll.
|
||||
* @return positionLeft, positionTop of the element relative to.
|
||||
*/
|
||||
getElementXY(container: HTMLElement, selector: undefined, positionParentClass?: string): number[];
|
||||
getElementXY(container: HTMLElement, selector?: undefined, positionParentClass?: string): number[];
|
||||
getElementXY(container: HTMLElement, selector: string, positionParentClass?: string): number[] | null;
|
||||
getElementXY(container: HTMLElement, selector?: string, positionParentClass?: string): number[] | null {
|
||||
let element: HTMLElement | null = <HTMLElement> (selector ? container.querySelector(selector) : container);
|
||||
getElementXY(container: HTMLElement, selector?: string, positionParentClass = 'inner-scroll'): number[] | null {
|
||||
let element = (selector ? container.querySelector<HTMLElement>(selector) : container);
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let positionTop = 0;
|
||||
let positionLeft = 0;
|
||||
|
||||
|
@ -736,10 +740,6 @@ export class CoreDomUtilsProvider {
|
|||
positionParentClass = 'inner-scroll';
|
||||
}
|
||||
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (element) {
|
||||
positionLeft += (element.offsetLeft - element.scrollLeft + element.clientLeft);
|
||||
positionTop += (element.offsetTop - element.scrollTop + element.clientTop);
|
||||
|
@ -766,6 +766,25 @@ export class CoreDomUtilsProvider {
|
|||
return [positionLeft, positionTop];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the position of a element relative to another element.
|
||||
*
|
||||
* @param element Element to get the position.
|
||||
* @param parent Parent element to get relative position.
|
||||
* @return X and Y position.
|
||||
*/
|
||||
getRelativeElementPosition(element: HTMLElement, parent: HTMLElement): { x: number; y: number} {
|
||||
// Get the top, left coordinates of two elements
|
||||
const elementRectangle = element.getBoundingClientRect();
|
||||
const parentRectangle = parent.getBoundingClientRect();
|
||||
|
||||
// Calculate the top and left positions
|
||||
return {
|
||||
x: elementRectangle.x - parentRectangle.x,
|
||||
y: elementRectangle.y - parentRectangle.y,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a message, it deduce if it's a network error.
|
||||
*
|
||||
|
@ -1096,11 +1115,9 @@ export class CoreDomUtilsProvider {
|
|||
* @param selector Selector to search.
|
||||
*/
|
||||
removeElement(element: HTMLElement, selector: string): void {
|
||||
if (element) {
|
||||
const selected = element.querySelector(selector);
|
||||
if (selected) {
|
||||
selected.remove();
|
||||
}
|
||||
const selected = element.querySelector(selector);
|
||||
if (selected) {
|
||||
selected.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1198,9 +1215,9 @@ export class CoreDomUtilsProvider {
|
|||
}
|
||||
|
||||
// Treat video posters.
|
||||
if (media.tagName == 'VIDEO' && media.getAttribute('poster')) {
|
||||
const currentPoster = media.getAttribute('poster');
|
||||
const newPoster = paths[CoreTextUtils.decodeURIComponent(currentPoster!)];
|
||||
const currentPoster = media.getAttribute('poster');
|
||||
if (media.tagName == 'VIDEO' && currentPoster) {
|
||||
const newPoster = paths[CoreTextUtils.decodeURIComponent(currentPoster)];
|
||||
if (newPoster !== undefined) {
|
||||
media.setAttribute('poster', newPoster);
|
||||
}
|
||||
|
@ -1237,8 +1254,8 @@ export class CoreDomUtilsProvider {
|
|||
* @return Returns a promise which is resolved when the scroll has completed.
|
||||
* @deprecated since 3.9.5. Use directly the IonContent class.
|
||||
*/
|
||||
scrollTo(content: IonContent, x: number, y: number, duration?: number): Promise<void> {
|
||||
return content.scrollToPoint(x, y, duration || 0);
|
||||
scrollTo(content: IonContent, x: number, y: number, duration = 0): Promise<void> {
|
||||
return content.scrollToPoint(x, y, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1261,7 +1278,7 @@ export class CoreDomUtilsProvider {
|
|||
* @return Returns a promise which is resolved when the scroll has completed.
|
||||
* @deprecated since 3.9.5. Use directly the IonContent class.
|
||||
*/
|
||||
scrollToTop(content: IonContent, duration?: number): Promise<void> {
|
||||
scrollToTop(content: IonContent, duration = 0): Promise<void> {
|
||||
return content.scrollToTop(duration);
|
||||
}
|
||||
|
||||
|
@ -1308,7 +1325,7 @@ export class CoreDomUtilsProvider {
|
|||
const scrollElement = await content.getScrollElement();
|
||||
|
||||
return scrollElement.scrollTop || 0;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -1316,51 +1333,34 @@ export class CoreDomUtilsProvider {
|
|||
/**
|
||||
* Scroll to a certain element.
|
||||
*
|
||||
* @param content The content that must be scrolled.
|
||||
* @param element The element to scroll to.
|
||||
* @param scrollParentClass Parent class where to stop calculating the position. Default inner-scroll.
|
||||
* @param selector Selector to find the element to scroll to inside the defined element.
|
||||
* @param duration Duration of the scroll animation in milliseconds.
|
||||
* @return True if the element is found, false otherwise.
|
||||
* @return Wether the scroll suceeded.
|
||||
*/
|
||||
scrollToElement(content: IonContent, element: HTMLElement, scrollParentClass?: string, duration?: number): boolean {
|
||||
const position = this.getElementXY(element, undefined, scrollParentClass);
|
||||
if (!position) {
|
||||
return false;
|
||||
async scrollViewToElement(element: HTMLElement, selector?: string, duration = 0): Promise<boolean> {
|
||||
await CoreDomUtils.waitToBeInDOM(element);
|
||||
|
||||
if (selector) {
|
||||
const foundElement = element.querySelector<HTMLElement>(selector);
|
||||
if (!foundElement) {
|
||||
// Element not found.
|
||||
return false;
|
||||
}
|
||||
|
||||
element = foundElement;
|
||||
}
|
||||
|
||||
content.scrollToPoint(position[0], position[1], duration || 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to a certain element using a selector to find it.
|
||||
*
|
||||
* @param container The element that contains the element that must be scrolled.
|
||||
* @param content The content that must be scrolled.
|
||||
* @param selector Selector to find the element to scroll to.
|
||||
* @param scrollParentClass Parent class where to stop calculating the position. Default inner-scroll.
|
||||
* @param duration Duration of the scroll animation in milliseconds.
|
||||
* @return True if the element is found, false otherwise.
|
||||
*/
|
||||
scrollToElementBySelector(
|
||||
container: HTMLElement | null,
|
||||
content: IonContent | undefined,
|
||||
selector: string,
|
||||
scrollParentClass?: string,
|
||||
duration?: number,
|
||||
): boolean {
|
||||
if (!container || !content) {
|
||||
const content = element.closest<HTMLIonContentElement>('ion-content') ?? undefined;
|
||||
if (!content) {
|
||||
// Content to scroll, not found.
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const position = this.getElementXY(container, selector, scrollParentClass);
|
||||
if (!position) {
|
||||
return false;
|
||||
}
|
||||
const position = CoreDomUtils.getRelativeElementPosition(element, content);
|
||||
|
||||
content.scrollToPoint(position[0], position[1], duration || 0);
|
||||
await content.scrollToPoint(position.x, position.y, duration);
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
|
@ -1372,12 +1372,71 @@ export class CoreDomUtilsProvider {
|
|||
* Search for an input with error (core-input-error directive) and scrolls to it if found.
|
||||
*
|
||||
* @param container The element that contains the element that must be scrolled.
|
||||
* @param content The content that must be scrolled.
|
||||
* @param scrollParentClass Parent class where to stop calculating the position. Default inner-scroll.
|
||||
* @return True if the element is found, false otherwise.
|
||||
*/
|
||||
scrollToInputError(container: HTMLElement | null, content?: IonContent, scrollParentClass?: string): boolean {
|
||||
return this.scrollToElementBySelector(container, content, '.core-input-error', scrollParentClass);
|
||||
async scrollViewToInputError(container: HTMLElement): Promise<boolean> {
|
||||
return this.scrollViewToElement(container, '.core-input-error');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to a certain element.
|
||||
*
|
||||
* @param content Not used anymore.
|
||||
* @param element The element to scroll to.
|
||||
* @param scrollParentClass Not used anymore.
|
||||
* @param duration Duration of the scroll animation in milliseconds.
|
||||
* @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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to a certain element using a selector to find it.
|
||||
*
|
||||
* @param container The element that contains the element that must be scrolled.
|
||||
* @param content Not used anymore.
|
||||
* @param selector Selector to find the element to scroll to.
|
||||
* @param scrollParentClass Not used anymore.
|
||||
* @param duration Duration of the scroll animation in milliseconds.
|
||||
* @return True if the element is found, false otherwise.
|
||||
* @deprecated since app 4.0 Use scrollViewToElement instead.
|
||||
*/
|
||||
scrollToElementBySelector(
|
||||
container: HTMLElement | null,
|
||||
content: unknown | null,
|
||||
selector: string,
|
||||
scrollParentClass?: string,
|
||||
duration = 0,
|
||||
): boolean {
|
||||
if (!container || !content) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CoreDomUtils.scrollViewToElement(container, selector, duration);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for an input with error (core-input-error directive) and scrolls to it if found.
|
||||
*
|
||||
* @param container The element that contains the element that must be scrolled.
|
||||
* @return True if the element is found, false otherwise.
|
||||
* @deprecated since app 4.0 Use scrollViewToInputError instead.
|
||||
*/
|
||||
scrollToInputError(container: HTMLElement | null): boolean {
|
||||
if (!container) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.scrollViewToInputError(container);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
&:before {
|
||||
content: '';
|
||||
height: 60px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
@include position(null, 0, 0, 0);
|
||||
background: linear-gradient(to bottom, rgba(var(--background-gradient-rgb), 0) calc(100% - var(--gradient-size)), rgba(var(--background-gradient-rgb), 1) calc(100% - 4px));
|
||||
|
|
Loading…
Reference in New Issue