Skip to content

Commit

Permalink
eat(core): enable window scrolling instead of tui-root (#80)
Browse files Browse the repository at this point in the history
* feat(core): enable `window` scrolling instead of `tui-root`

* feat(core): enable `window` scrolling instead of `tui-root`

* chore(comments): fix

* chore(comments): fix

Co-authored-by: Roman Sedov <darragon-nn@yandex.ru>
  • Loading branch information
waterplea and MarsiBarsi authored Jan 11, 2021
1 parent f061dcd commit 0190a8f
Show file tree
Hide file tree
Showing 18 changed files with 133 additions and 255 deletions.
1 change: 1 addition & 0 deletions projects/addon-doc/src/components/demo/demo.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const MIN_COMPONENT_WIDTH = 104;
styleUrls: ['./demo.style.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
TuiDestroyService,
{
provide: TuiModeDirective,
useExisting: forwardRef(() => TuiDocDemoComponent),
Expand Down
56 changes: 6 additions & 50 deletions projects/core/components/root/root.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,13 @@ import {
Optional,
} from '@angular/core';
import {EVENT_MANAGER_PLUGINS} from '@angular/platform-browser';
import {CSS, USER_AGENT} from '@ng-web-apis/common';
import {
isFirefox,
TUI_DIALOGS,
TUI_IS_MOBILE,
tuiAssert,
TuiDestroyService,
} from '@taiga-ui/cdk';
import {TUI_DIALOGS, TUI_IS_MOBILE, tuiAssert} from '@taiga-ui/cdk';
import {tuiFadeIn} from '@taiga-ui/core/animations';
import {VERSION} from '@taiga-ui/core/constants';
import {TuiNotificationsHostComponent} from '@taiga-ui/core/modules/notifications';
import {TuiRootScroller} from '@taiga-ui/core/services';
import {TUI_SCROLL_REF} from '@taiga-ui/core/tokens';
import {SilentEventPlugin} from '@tinkoff/ng-event-plugins';
import {merge, Observable} from 'rxjs';
import {map, takeUntil} from 'rxjs/operators';
import {merge, Observable, of} from 'rxjs';
import {map} from 'rxjs/operators';

// @dynamic
@Component({
Expand All @@ -32,63 +23,28 @@ import {map, takeUntil} from 'rxjs/operators';
host: {
'data-tui-version': VERSION,
},
providers: [
TuiDestroyService,
{
provide: TUI_SCROLL_REF,
useExisting: ElementRef,
},
],
animations: [tuiFadeIn],
})
export class TuiRootComponent {
private isDialogPresent = false;
private isLegacy =
!this.cssRef.supports('position', 'sticky') ||
(isFirefox(this.userAgent) && !this.cssRef.supports('scrollbar-width', 'none'));
readonly scrollbars$ = this.dialogs
? merge(...this.dialogs).pipe(map(({length}) => !length))
: of(!this.isMobile);

constructor(
/**
* TODO: remove "any" in new TS version; https://github.com/ng-web-apis/common/pull/6
*/
@Inject(CSS) private readonly cssRef: any,
@Inject(ElementRef) readonly elementRef: ElementRef<HTMLElement>,
@Inject(TuiDestroyService) destroy$: Observable<void>,
@Optional()
@Inject(TUI_DIALOGS)
readonly dialogs: ReadonlyArray<Observable<ReadonlyArray<unknown>>> | null,
@Optional()
@Inject(TuiNotificationsHostComponent)
readonly notificationsHost: TuiNotificationsHostComponent,
@Inject(TUI_IS_MOBILE) private readonly isMobile: boolean,
@Inject(TuiRootScroller) scroller: TuiRootScroller,
@Inject(EVENT_MANAGER_PLUGINS) plugins: ReadonlyArray<unknown>,
@Inject(USER_AGENT) private readonly userAgent: string,
) {
tuiAssert.bootstrapped = true;

scroller.register(this);

tuiAssert.assert(
!(plugins[0] instanceof SilentEventPlugin),
'PlatformBrowser or PlatformServer modules must come before TuiRootModule in your main module',
);

if (!dialogs) {
return;
}

merge(...dialogs)
.pipe(
map(({length}) => !!length),
takeUntil(destroy$),
)
.subscribe(isDialogPresent => {
this.isDialogPresent = isDialogPresent;
});
}

get showScrollbar(): boolean {
return !this.isDialogPresent && !this.isMobile && !this.isLegacy;
}
}
11 changes: 2 additions & 9 deletions projects/core/components/root/root.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {CommonModule, ViewportScroller} from '@angular/common';
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {TuiDialogHostModule, TuiDragModule, TuiPortalHostModule} from '@taiga-ui/cdk';
import {TuiHintsHostModule} from '@taiga-ui/core/components/hints-host';
import {TuiScrollControlsModule} from '@taiga-ui/core/components/scroll-controls';
import {TuiSvgDefsHostModule} from '@taiga-ui/core/internal/svg-defs-host';
import {TuiRootScroller} from '@taiga-ui/core/services';
import {NG_EVENT_PLUGINS} from '@tinkoff/ng-event-plugins';
import {TuiRootComponent} from './root.component';

Expand All @@ -20,12 +19,6 @@ import {TuiRootComponent} from './root.component';
],
declarations: [TuiRootComponent],
exports: [TuiRootComponent],
providers: [
{
provide: ViewportScroller,
useExisting: TuiRootScroller,
},
NG_EVENT_PLUGINS,
],
providers: [NG_EVENT_PLUGINS],
})
export class TuiRootModule {}
29 changes: 4 additions & 25 deletions projects/core/components/root/root.style.less
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,11 @@
position: relative;
display: block;
flex: 1;
height: 100vh;
overflow: auto;

&:not(._legacy) {
/* stylelint-disable*/
scrollbar-width: none;
-ms-overflow-style: none;
/* stylelint-enable*/

&::-webkit-scrollbar,
&::-webkit-scrollbar-thumb {
background: transparent;
width: 0;
height: 0;
}
}

&._legacy {
.customize-scroll();
overflow: overlay;
}
}

@media print {
height: auto;
overflow: visible;
}
.scrollbar {
.fullsize(fixed, inset);
margin: 0;
}

.content {
Expand Down
5 changes: 4 additions & 1 deletion projects/core/components/root/root.template.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<tui-scroll-controls *ngIf="showScrollbar"></tui-scroll-controls>
<tui-scroll-controls
*ngIf="scrollbars$ | async"
class="scrollbar"
></tui-scroll-controls>
<tui-svg-defs-host></tui-svg-defs-host>
<tui-portal-host>
<div class="content">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {DOCUMENT} from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
Expand Down Expand Up @@ -32,7 +33,10 @@ export class TuiScrollControlsComponent {

constructor(
@Inject(NgZone) private readonly ngZone: NgZone,
@Inject(TUI_SCROLL_REF) private readonly scrollRef: ElementRef<HTMLElement>,
@Inject(DOCUMENT) private readonly documentRef: Document,
@Optional()
@Inject(TUI_SCROLL_REF)
private readonly scrollRef: ElementRef<HTMLElement> | null,
@Optional()
@Inject(TuiModeDirective)
private readonly modeDirective: TuiModeDirective | null,
Expand All @@ -43,12 +47,9 @@ export class TuiScrollControlsComponent {
}

private get scrollbars(): [boolean, boolean] {
const {
clientHeight,
scrollHeight,
clientWidth,
scrollWidth,
} = this.scrollRef.nativeElement;
const {clientHeight, scrollHeight, clientWidth, scrollWidth} = this.scrollRef
? this.scrollRef.nativeElement
: this.documentRef.documentElement;

return [
Math.ceil((clientHeight / scrollHeight) * 100) < 100,
Expand Down
96 changes: 60 additions & 36 deletions projects/core/components/scroll-controls/scrollbar.directive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import {DOCUMENT} from '@angular/common';
import {Directive, ElementRef, Inject, Input, NgZone, Renderer2} from '@angular/core';
import {DOCUMENT, ViewportScroller} from '@angular/common';
import {
Directive,
ElementRef,
Inject,
Input,
NgZone,
Optional,
Renderer2,
} from '@angular/core';
import {WINDOW} from '@ng-web-apis/common';
import {
POLLING_TIME,
preventDefault,
Expand Down Expand Up @@ -28,11 +37,16 @@ export class TuiScrollbarDirective {
@Inject(NgZone) ngZone: NgZone,
@Inject(Renderer2) renderer: Renderer2,
@Inject(TuiDestroyService) destroy$: Observable<void>,
@Inject(TUI_SCROLL_REF) private readonly container: ElementRef<HTMLElement>,
@Optional()
@Inject(TUI_SCROLL_REF)
private readonly container: ElementRef<HTMLElement> | null,
@Inject(DOCUMENT) private readonly documentRef: Document,
@Inject(WINDOW) private readonly windowRef: Window,
@Inject(ElementRef) private readonly elementRef: ElementRef<HTMLElement>,
@Inject(ViewportScroller) private readonly viewportScroller: ViewportScroller,
) {
const mousedown$ = typedFromEvent(this.elementRef.nativeElement, 'mousedown');
const {nativeElement} = this.elementRef;
const mousedown$ = typedFromEvent(nativeElement, 'mousedown');
const mousemove$ = typedFromEvent(this.documentRef, 'mousemove');
const mouseup$ = typedFromEvent(this.documentRef, 'mouseup');

Expand All @@ -53,6 +67,17 @@ export class TuiScrollbarDirective {
tuiZonefree(ngZone),
)
.subscribe(([scrollTop, scrollLeft]) => {
const [x, y] = this.viewportScroller.getScrollPosition();

if (!this.container) {
this.viewportScroller.scrollToPosition([
this.tuiScrollbar === TuiOrientation.Vertical ? x : scrollLeft,
this.tuiScrollbar === TuiOrientation.Vertical ? scrollTop : y,
]);

return;
}

if (this.tuiScrollbar === TuiOrientation.Vertical) {
renderer.setProperty(
this.container.nativeElement,
Expand All @@ -68,57 +93,47 @@ export class TuiScrollbarDirective {
}
});

merge(fromEvent(this.container.nativeElement, 'scroll'), interval(POLLING_TIME))
merge(
fromEvent(
this.container ? this.container.nativeElement : this.windowRef,
'scroll',
),
interval(POLLING_TIME),
)
.pipe(takeUntil(destroy$), tuiZonefree(ngZone))
.subscribe(() => {
if (this.tuiScrollbar === TuiOrientation.Vertical) {
renderer.setStyle(
this.elementRef.nativeElement,
'top',
`${this.thumb * 100}%`,
);
renderer.setStyle(
this.elementRef.nativeElement,
'height',
`${this.view * 100}%`,
);
renderer.setStyle(nativeElement, 'top', `${this.thumb * 100}%`);
renderer.setStyle(nativeElement, 'height', `${this.view * 100}%`);
} else {
renderer.setStyle(
this.elementRef.nativeElement,
'left',
`${this.thumb * 100}%`,
);
renderer.setStyle(
this.elementRef.nativeElement,
'width',
`${this.view * 100}%`,
);
renderer.setStyle(nativeElement, 'left', `${this.thumb * 100}%`);
renderer.setStyle(nativeElement, 'width', `${this.view * 100}%`);
}
});
}

get scrolled(): number {
private get scrolled(): number {
const {
scrollTop,
scrollHeight,
clientHeight,
scrollLeft,
scrollWidth,
clientWidth,
} = this.container.nativeElement;
} = this.computedContainer;

return this.tuiScrollbar === TuiOrientation.Vertical
? scrollTop / (scrollHeight - clientHeight)
: scrollLeft / (scrollWidth - clientWidth);
}

get compensation(): number {
private get compensation(): number {
const {
clientHeight,
scrollHeight,
clientWidth,
scrollWidth,
} = this.container.nativeElement;
} = this.computedContainer;

if (
((clientHeight * clientHeight) / scrollHeight > MIN_WIDTH &&
Expand All @@ -134,36 +149,45 @@ export class TuiScrollbarDirective {
: MIN_WIDTH / clientWidth;
}

get thumb(): number {
private get thumb(): number {
const compensation = this.compensation || this.view;

return this.scrolled * (1 - compensation);
}

get view(): number {
private get view(): number {
const {
clientHeight,
scrollHeight,
clientWidth,
scrollWidth,
} = this.container.nativeElement;
} = this.computedContainer;

return this.tuiScrollbar === TuiOrientation.Vertical
? Math.ceil((clientHeight / scrollHeight) * 100) / 100
: Math.ceil((clientWidth / scrollWidth) * 100) / 100;
}

private get computedContainer(): HTMLElement {
return this.container
? this.container.nativeElement
: this.documentRef.documentElement;
}

private getScrolled(
{clientY, clientX}: MouseEvent,
offsetVertical: number,
offsetHorizontal: number,
): [number, number] {
const {innerWidth, innerHeight} = this.windowRef;
const {offsetHeight, offsetWidth} = this.elementRef.nativeElement;
const {nativeElement} = this.container;
const {top, left, width, height} = nativeElement.getBoundingClientRect();
const {top = 0, left = 0, width = innerWidth, height = innerHeight} = this
.container
? this.container.nativeElement.getBoundingClientRect()
: {};

const maxTop = nativeElement.scrollHeight - height;
const maxLeft = nativeElement.scrollWidth - width;
const maxTop = this.computedContainer.scrollHeight - height;
const maxLeft = this.computedContainer.scrollWidth - width;
const scrolledTop =
(clientY - top - offsetHeight * offsetVertical) / (height - offsetHeight);
const scrolledLeft =
Expand Down
1 change: 0 additions & 1 deletion projects/core/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './hint.service';
export * from './root-scroller.service';
export * from './router-link-active.service';
export * from './svg.service';
Loading

0 comments on commit 0190a8f

Please sign in to comment.