MOBILE-3833 style: Shadow on collapsible footer only if not at bottom

main
Pau Ferrer Ocaña 2022-03-24 12:58:55 +01:00
parent 042619dd61
commit 602331d2c6
5 changed files with 68 additions and 38 deletions

View File

@ -445,13 +445,9 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
return;
}
// Don't use domUtils.getScrollHeight because it gives an outdated value after receiving a new message.
const scrollHeight = this.scrollElement ? this.scrollElement.scrollHeight : 0;
// Check if we are at the bottom to scroll it after render.
// Use a 5px error margin because in iOS there is 1px difference for some reason.
this.scrollBottom = Math.abs(scrollHeight - (this.scrollElement?.scrollTop || 0) -
(this.scrollElement?.clientHeight || 0)) < 5;
this.scrollBottom = CoreDom.scrollIsBottom(this.scrollElement, 5);
if (this.messagesBeingSent > 0) {
// Ignore polling due to a race condition.
@ -510,39 +506,39 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
* The scroll was moved. Update new messages count.
*/
scrollFunction(): void {
if (this.newMessages > 0) {
const scrollBottom = (this.scrollElement?.scrollTop || 0) + (this.scrollElement?.clientHeight || 0);
const scrollHeight = (this.scrollElement?.scrollHeight || 0);
if (scrollBottom > scrollHeight - 40) {
// At the bottom, reset.
this.setNewMessagesBadge(0);
if (this.newMessages == 0) {
return;
}
return;
if (CoreDom.scrollIsBottom(this.scrollElement, 40)) {
// At the bottom, reset.
this.setNewMessagesBadge(0);
return;
}
const scrollElRect = this.scrollElement?.getBoundingClientRect();
const scrollBottomPos = (scrollElRect && scrollElRect.bottom) || 0;
if (scrollBottomPos == 0) {
return;
}
const messages = Array.from(this.hostElement.querySelectorAll('.addon-message-not-mine'))
.slice(-this.newMessages)
.reverse();
const newMessagesUnread = messages.findIndex((message) => {
const elementRect = message.getBoundingClientRect();
if (!elementRect) {
return false;
}
const scrollElRect = this.scrollElement?.getBoundingClientRect();
const scrollBottomPos = (scrollElRect && scrollElRect.bottom) || 0;
return elementRect.bottom <= scrollBottomPos;
});
if (scrollBottomPos == 0) {
return;
}
const messages = Array.from(this.hostElement.querySelectorAll('.addon-message-not-mine'))
.slice(-this.newMessages)
.reverse();
const newMessagesUnread = messages.findIndex((message) => {
const elementRect = message.getBoundingClientRect();
if (!elementRect) {
return false;
}
return elementRect.bottom <= scrollBottomPos;
});
if (newMessagesUnread > 0 && newMessagesUnread < this.newMessages) {
this.setNewMessagesBadge(newMessagesUnread);
}
if (newMessagesUnread > 0 && newMessagesUnread < this.newMessages) {
this.setNewMessagesBadge(newMessagesUnread);
}
}

View File

@ -31,6 +31,7 @@ import { CoreUrlUtils } from '@services/utils/url';
import { CoreConstants } from '@/core/constants';
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreDom } from '@singletons/dom';
const MOODLE_VERSION_PREFIX = 'version-';
const MOODLEAPP_VERSION_PREFIX = 'moodleapp-';
@ -90,9 +91,21 @@ export class AppComponent implements OnInit, AfterViewInit {
});
// Listen to scroll to add style when scroll is not 0.
win.addEventListener('ionScroll', ({ detail, target }: CustomEvent<ScrollDetail>) => {
const header = (target as HTMLElement).closest('.ion-page')?.querySelector('ion-header');
header?.classList.toggle('core-header-shadow', detail.scrollTop > 0);
win.addEventListener('ionScroll', async ({ detail, target }: CustomEvent<ScrollDetail>) => {
if ((target as HTMLElement).tagName != 'ION-CONTENT') {
return;
}
const content = (target as HTMLIonContentElement);
const page = content.closest('.ion-page');
if (!page) {
return;
}
page.querySelector<HTMLIonHeaderElement>('ion-header')?.classList.toggle('core-header-shadow', detail.scrollTop > 0);
const scrollElement = await content.getScrollElement();
content.classList.toggle('core-footer-shadow', !CoreDom.scrollIsBottom(scrollElement));
});
// Listen for session expired events.

View File

@ -126,6 +126,9 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
const scroll = await this.content.getScrollElement();
this.content.scrollEvents = true;
// Init shadow.
this.content.classList.toggle('core-footer-shadow', !CoreDom.scrollIsBottom(scroll));
this.content.addEventListener('ionScroll', this.contentScrollListener = (e: CustomEvent<ScrollDetail>): void => {
if (!this.content) {
return;

View File

@ -232,6 +232,21 @@ export class CoreDom {
return CoreDom.scrollToElement(container, '.core-input-error');
}
/**
* Has the scroll reached bottom?
*
* @param scrollElement Scroll Element.
* @param marginError Error margin when calculating.
* @return Wether the scroll reached the bottom.
*/
static scrollIsBottom(scrollElement?: HTMLElement, marginError = 0): boolean {
if (!scrollElement) {
return true;
}
return scrollElement.scrollTop + scrollElement.clientHeight >= scrollElement.scrollHeight - marginError;
}
/**
* Move element to content so it can be slotted.
*

View File

@ -1434,8 +1434,11 @@ ion-grid.core-no-grid > ion-row {
right: 0;
}
[collapsible-footer] {
.core-footer-shadow [collapsible-footer] {
box-shadow: var(--drop-shadow-top, none);
}
[collapsible-footer] {
transition: box-shadow 0.5s;
width: 100%;
bottom: 0;
z-index: 3;