MOBILE-4202 split-view: Scroll to current element when swipe
parent
77605b87f4
commit
c6b2ea058e
|
@ -103,6 +103,10 @@ export abstract class CoreItemsManager<
|
|||
* @param item Item, null if none.
|
||||
*/
|
||||
setSelectedItem(item: Item | null): void {
|
||||
if (item === this.selectedItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedItem = item;
|
||||
|
||||
this.listeners.forEach(listener => listener.onSelectedItemUpdated?.call(listener, item));
|
||||
|
|
|
@ -22,6 +22,7 @@ import { CoreUtils } from '@services/utils/utils';
|
|||
|
||||
import { CoreRoutedItemsManagerSource } from './routed-items-manager-source';
|
||||
import { CoreRoutedItemsManager } from './routed-items-manager';
|
||||
import { CoreDom } from '@singletons/dom';
|
||||
|
||||
/**
|
||||
* Helper class to manage the state and routing of a list of items in a page.
|
||||
|
@ -129,6 +130,7 @@ export class CoreListItemsManager<
|
|||
}
|
||||
|
||||
await this.navigateToItem(item, { reset: this.resetNavigation() });
|
||||
setTimeout(async () => await this.scrollToCurrentElement(), 100);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,10 +198,33 @@ export class CoreListItemsManager<
|
|||
super.updateSelectedItem(route);
|
||||
|
||||
const selectDefault = CoreScreen.isTablet && this.selectedItem === null && this.splitView && !this.splitView.isNested;
|
||||
|
||||
this.select(selectDefault ? this.getDefaultItem() : this.selectedItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to current element in split-view list.
|
||||
*/
|
||||
protected async scrollToCurrentElement(): Promise<void> {
|
||||
if (CoreScreen.isMobile) {
|
||||
return;
|
||||
}
|
||||
|
||||
const element = this.splitView?.nativeElement ?? document;
|
||||
const currentItem = element.querySelector<HTMLElement>('[aria-current="page"]');
|
||||
|
||||
if (!currentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isElementInViewport = CoreDom.isElementInViewport(currentItem, 1, this.splitView?.nativeElement);
|
||||
|
||||
if (isElementInViewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentItem.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item that should be selected by default.
|
||||
*
|
||||
|
|
|
@ -152,6 +152,11 @@ export abstract class CoreRoutedItemsManager<
|
|||
*/
|
||||
protected onSourceItemsUpdated(items: Item[]): void {
|
||||
super.onSourceItemsUpdated(items);
|
||||
const selectedItem = this.selectedItem;
|
||||
|
||||
if (selectedItem !== null && items.some(item => item === selectedItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateSelectedItem();
|
||||
}
|
||||
|
|
|
@ -81,6 +81,8 @@ describe('CoreSwipeNavigationItemsManager', () => {
|
|||
|
||||
await source.load();
|
||||
|
||||
instance.setSelectedItem(items[0]);
|
||||
|
||||
// Act.
|
||||
await instance.navigateToNextItem();
|
||||
|
||||
|
@ -96,6 +98,8 @@ describe('CoreSwipeNavigationItemsManager', () => {
|
|||
|
||||
await source.load();
|
||||
|
||||
instance.setSelectedItem(items[1]);
|
||||
|
||||
// Act.
|
||||
await instance.navigateToPreviousItem();
|
||||
|
||||
|
@ -112,6 +116,8 @@ describe('CoreSwipeNavigationItemsManager', () => {
|
|||
|
||||
await source.load();
|
||||
|
||||
instance.setSelectedItem(items[0]);
|
||||
|
||||
// Act.
|
||||
await instance.navigateToNextItem();
|
||||
|
||||
|
@ -127,6 +133,8 @@ describe('CoreSwipeNavigationItemsManager', () => {
|
|||
|
||||
await source.load();
|
||||
|
||||
instance.setSelectedItem(items[0]);
|
||||
|
||||
// Assert.
|
||||
await expect(instance.hasNextItem()).resolves.toBe(true);
|
||||
await expect(instance.hasPreviousItem()).resolves.toBe(false);
|
||||
|
|
|
@ -57,6 +57,10 @@ export class CoreSplitViewComponent implements AfterViewInit, OnDestroy {
|
|||
return this.outletRouteSubject.asObservable();
|
||||
}
|
||||
|
||||
get nativeElement(): HTMLElement {
|
||||
return this.element.nativeElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
|
|
@ -88,21 +88,23 @@ export class CoreDom {
|
|||
*
|
||||
* @param element Element to check.
|
||||
* @param intersectionRatio Intersection ratio (From 0 to 1).
|
||||
* @param container Container where element is located
|
||||
* @returns True if in viewport.
|
||||
*/
|
||||
static isElementInViewport(element: HTMLElement, intersectionRatio = 1): boolean {
|
||||
static isElementInViewport(element: HTMLElement, intersectionRatio = 1, container: HTMLElement | null = null): boolean {
|
||||
const elementRectangle = element.getBoundingClientRect();
|
||||
|
||||
const containerRectangle = container?.getBoundingClientRect();
|
||||
const elementArea = elementRectangle.width * elementRectangle.height;
|
||||
|
||||
if (elementArea == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const intersectionRectangle = {
|
||||
top: Math.max(0, elementRectangle.top),
|
||||
left: Math.max(0, elementRectangle.left),
|
||||
bottom: Math.min(window.innerHeight, elementRectangle.bottom),
|
||||
right: Math.min(window.innerWidth, elementRectangle.right),
|
||||
top: Math.max(containerRectangle?.top ?? 0, elementRectangle.top),
|
||||
left: Math.max(containerRectangle?.left ?? 0, elementRectangle.left),
|
||||
bottom: Math.min(containerRectangle?.bottom ?? window.innerHeight, elementRectangle.bottom),
|
||||
right: Math.min(containerRectangle?.right ?? window.innerWidth, elementRectangle.right),
|
||||
};
|
||||
|
||||
const intersectionArea = (intersectionRectangle.right - intersectionRectangle.left) *
|
||||
|
|
Loading…
Reference in New Issue