mirror of
https://github.com/akveo/ngx-admin.git
synced 2025-12-16 23:40:14 +01:00
fix(layout): prevent layout scroll disappearing when overlay open
This commit is contained in:
parent
47979ac17b
commit
2144c7a3d9
4 changed files with 157 additions and 2 deletions
|
|
@ -1,4 +1,8 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { AfterViewInit, Component, Inject, PLATFORM_ID, ViewChild } from '@angular/core';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
import { NbLayoutComponent } from '@nebular/theme';
|
||||
|
||||
import { WindowModeBlockScrollService } from '../../services/window-mode-block-scroll.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-one-column-layout',
|
||||
|
|
@ -23,5 +27,18 @@ import { Component } from '@angular/core';
|
|||
</nb-layout>
|
||||
`,
|
||||
})
|
||||
export class OneColumnLayoutComponent {
|
||||
export class OneColumnLayoutComponent implements AfterViewInit {
|
||||
|
||||
@ViewChild(NbLayoutComponent, { static: false }) layout: NbLayoutComponent;
|
||||
|
||||
constructor(
|
||||
@Inject(PLATFORM_ID) private platformId,
|
||||
private windowModeBlockScrollService: WindowModeBlockScrollService,
|
||||
) {}
|
||||
|
||||
ngAfterViewInit() {
|
||||
if (isPlatformBrowser(this.platformId)) {
|
||||
this.windowModeBlockScrollService.register(this.layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
132
src/app/@theme/services/window-mode-block-scroll.service.ts
Normal file
132
src/app/@theme/services/window-mode-block-scroll.service.ts
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import { Inject, Injectable, OnDestroy } from '@angular/core';
|
||||
import { coerceCssPixelValue } from '@angular/cdk/coercion';
|
||||
import {
|
||||
NB_WINDOW,
|
||||
NbLayoutComponent,
|
||||
NbLayoutDimensions,
|
||||
NbLayoutRulerService,
|
||||
NbLayoutScrollService,
|
||||
NbViewportRulerAdapter,
|
||||
} from '@nebular/theme';
|
||||
import { filter, map, take, takeUntil } from 'rxjs/operators';
|
||||
import { fromEvent as observableFromEvent, merge, Subject } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class WindowModeBlockScrollService implements OnDestroy {
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
private blockEnabled = false;
|
||||
private unblock$ = new Subject<void>();
|
||||
|
||||
private container: HTMLElement;
|
||||
private content: HTMLElement;
|
||||
|
||||
private previousScrollPosition: { top: number, left: number };
|
||||
private previousContainerStyles: { overflowY: string };
|
||||
private previousContentStyles: { left: string, top: string, width: string, overflow: string, position: string };
|
||||
|
||||
constructor(
|
||||
private scrollService: NbLayoutScrollService,
|
||||
private viewportRuler: NbViewportRulerAdapter,
|
||||
private layout: NbLayoutRulerService,
|
||||
@Inject(NB_WINDOW) private window,
|
||||
) {}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
this.unblock$.complete();
|
||||
}
|
||||
|
||||
register(layout: NbLayoutComponent) {
|
||||
this.container = layout.scrollableContainerRef.nativeElement;
|
||||
this.content = this.container.children[0] as HTMLElement;
|
||||
|
||||
this.scrollService.onScrollableChange()
|
||||
.pipe(
|
||||
filter(() => layout.windowModeValue),
|
||||
map((scrollable: boolean) => !scrollable),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe((shouldBlock: boolean) => {
|
||||
if (shouldBlock) {
|
||||
this.blockScroll();
|
||||
} else {
|
||||
this.unblockScroll();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
blockScroll() {
|
||||
if (!this.canBeBlocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.previousScrollPosition = this.viewportRuler.getViewportScrollPosition();
|
||||
this.backupStyles();
|
||||
|
||||
this.container.style.overflowY = 'scroll';
|
||||
this.content.style.overflow = 'hidden';
|
||||
this.content.style.position = 'fixed';
|
||||
this.updateContentSizeAndPosition();
|
||||
|
||||
observableFromEvent(this.window, 'resize')
|
||||
.pipe(
|
||||
takeUntil(merge(this.destroy$, this.unblock$).pipe(take(1))),
|
||||
)
|
||||
.subscribe(() => this.updateContentSizeAndPosition());
|
||||
|
||||
this.blockEnabled = true;
|
||||
}
|
||||
|
||||
unblockScroll() {
|
||||
if (this.blockEnabled) {
|
||||
this.restoreStyles();
|
||||
this.scrollService.scrollTo(this.previousScrollPosition.left, this.previousScrollPosition.top);
|
||||
this.unblock$.next();
|
||||
this.blockEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private canBeBlocked(): boolean {
|
||||
if (this.blockEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { height: containerHeight } = this.viewportRuler.getViewportSize();
|
||||
return this.content.scrollHeight > containerHeight;
|
||||
}
|
||||
|
||||
private updateContentSizeAndPosition() {
|
||||
const { top, left } = this.container.getBoundingClientRect();
|
||||
this.content.style.left = coerceCssPixelValue(-this.previousScrollPosition.left + left);
|
||||
this.content.style.top = coerceCssPixelValue(-this.previousScrollPosition.top + top);
|
||||
this.layout.getDimensions()
|
||||
.pipe(
|
||||
map(({ clientWidth }: NbLayoutDimensions) => coerceCssPixelValue(clientWidth)),
|
||||
take(1),
|
||||
)
|
||||
.subscribe((clientWidth: string) => this.content.style.width = clientWidth);
|
||||
}
|
||||
|
||||
private backupStyles() {
|
||||
this.previousContainerStyles = { overflowY: this.container.style.overflowY };
|
||||
this.previousContentStyles = {
|
||||
overflow: this.content.style.overflow,
|
||||
position: this.content.style.position,
|
||||
left: this.content.style.left,
|
||||
top: this.content.style.top,
|
||||
width: this.content.style.width,
|
||||
};
|
||||
}
|
||||
|
||||
private restoreStyles() {
|
||||
this.container.style.overflowY = this.previousContainerStyles.overflowY;
|
||||
this.content.style.overflow = this.previousContentStyles.overflow;
|
||||
this.content.style.position = this.previousContentStyles.position;
|
||||
this.content.style.left = this.previousContentStyles.left;
|
||||
this.content.style.top = this.previousContentStyles.top;
|
||||
this.content.style.width = this.previousContentStyles.width;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
$nb-themes: nb-register-theme((
|
||||
font-family-secondary: font-family-primary,
|
||||
layout-padding-top: 2.25rem,
|
||||
layout-window-mode-padding-top: 0,
|
||||
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
|
||||
|
|
@ -28,6 +29,7 @@ $nb-themes: nb-register-theme((
|
|||
$nb-themes: nb-register-theme((
|
||||
font-family-secondary: font-family-primary,
|
||||
layout-padding-top: 2.25rem,
|
||||
layout-window-mode-padding-top: 0,
|
||||
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
|
||||
|
|
@ -50,6 +52,7 @@ $nb-themes: nb-register-theme((
|
|||
$nb-themes: nb-register-theme((
|
||||
font-family-secondary: font-family-primary,
|
||||
layout-padding-top: 2.25rem,
|
||||
layout-window-mode-padding-top: 0,
|
||||
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
|
||||
|
|
@ -72,6 +75,7 @@ $nb-themes: nb-register-theme((
|
|||
$nb-themes: nb-register-theme((
|
||||
font-family-secondary: font-family-primary,
|
||||
layout-padding-top: 2.25rem,
|
||||
layout-window-mode-padding-top: 0,
|
||||
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import {
|
|||
ThreeColumnsLayoutComponent,
|
||||
TwoColumnsLayoutComponent,
|
||||
} from './layouts';
|
||||
import { WindowModeBlockScrollService } from './services/window-mode-block-scroll.service';
|
||||
import { DEFAULT_THEME } from './styles/theme.default';
|
||||
import { COSMIC_THEME } from './styles/theme.cosmic';
|
||||
import { CORPORATE_THEME } from './styles/theme.corporate';
|
||||
|
|
@ -86,6 +87,7 @@ export class ThemeModule {
|
|||
},
|
||||
[ DEFAULT_THEME, COSMIC_THEME, CORPORATE_THEME, DARK_THEME ],
|
||||
).providers,
|
||||
WindowModeBlockScrollService,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue