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