Skip to content

Commit

Permalink
refactor(module:anchor): refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
hsuanxyz committed Mar 13, 2020
1 parent af4051e commit fd36dd4
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,22 @@ import {
OnInit,
Renderer2,
TemplateRef,
ViewChild,
ViewEncapsulation
} from '@angular/core';

import { NzAnchorComponent } from './nz-anchor.component';
import { NzAnchorComponent } from './anchor.component';

@Component({
selector: 'nz-link',
exportAs: 'nzLink',
preserveWhitespaces: false,
templateUrl: './nz-anchor-link.component.html',
template: `
<a #linkTitle (click)="goToClick($event)" href="{{ nzHref }}" class="ant-anchor-link-title" title="{{ titleStr }}">
<span *ngIf="titleStr; else titleTpl || nzTemplate">{{ titleStr }}</span>
</a>
<ng-content></ng-content>
`,
host: {
'[class.ant-anchor-link-active]': 'active'
},
Expand Down Expand Up @@ -60,6 +66,7 @@ export class NzAnchorLinkComponent implements OnInit, OnDestroy {
}

@ContentChild('nzTemplate', { static: false }) nzTemplate: TemplateRef<void>;
@ViewChild('linkTitle') linkTitle: ElementRef<HTMLAnchorElement>;

constructor(
public elementRef: ElementRef,
Expand All @@ -75,6 +82,10 @@ export class NzAnchorLinkComponent implements OnInit, OnDestroy {
this.anchorComp.registerLink(this);
}

getLinkTitleElement(): HTMLAnchorElement {
return this.linkTitle.nativeElement;
}

goToClick(e: Event): void {
e.preventDefault();
e.stopPropagation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,29 @@ import {
EventEmitter,
Inject,
Input,
NgZone,
OnChanges,
OnDestroy,
Output,
SimpleChanges,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';
import { distinctUntilChanged, throttleTime } from 'rxjs/operators';
import { fromEvent, Subject } from 'rxjs';
import { takeUntil, throttleTime } from 'rxjs/operators';

import { InputBoolean, InputNumber, NgStyleInterface, NzConfigService, NzScrollService, toNumber, WithConfig } from 'ng-zorro-antd/core';

import { NzAnchorLinkComponent } from './nz-anchor-link.component';
import {
InputBoolean,
InputNumber,
NgStyleInterface,
NzConfigService,
NzScrollService,
warnDeprecation,
WithConfig
} from 'ng-zorro-antd/core';

import { NzAnchorLinkComponent } from './anchor-link.component';
import { getOffsetTop } from './util';

interface Section {
comp: NzAnchorLinkComponent;
Expand All @@ -41,11 +53,25 @@ const sharpMatcherRegx = /#([^#]+)$/;
selector: 'nz-anchor',
exportAs: 'nzAnchor',
preserveWhitespaces: false,
templateUrl: './nz-anchor.component.html',
template: `
<nz-affix *ngIf="nzAffix; else content" [nzOffsetTop]="nzOffsetTop" [nzTarget]="container">
<ng-template [ngTemplateOutlet]="content"></ng-template>
</nz-affix>
<ng-template #content>
<div class="ant-anchor-wrapper" [ngStyle]="wrapperStyle">
<div class="ant-anchor" [ngClass]="{ fixed: !nzAffix && !nzShowInkInFixed }">
<div class="ant-anchor-ink">
<div class="ant-anchor-ink-ball" [class.visible]="visible" #ink></div>
</div>
<ng-content></ng-content>
</div>
</div>
</ng-template>
`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NzAnchorComponent implements OnDestroy, AfterViewInit {
export class NzAnchorComponent implements OnDestroy, AfterViewInit, OnChanges {
@ViewChild('ink', { static: false }) private ink: ElementRef;

@Input() @InputBoolean() nzAffix = true;
Expand All @@ -61,45 +87,34 @@ export class NzAnchorComponent implements OnDestroy, AfterViewInit {
nzBounds: number;

@Input()
@InputNumber()
@WithConfig<number>(NZ_CONFIG_COMPONENT_NAME)
set nzOffsetTop(value: number) {
this._offsetTop = toNumber(value, 0);
this.wrapperStyle = {
'max-height': `calc(100vh - ${this._offsetTop}px)`
};
}
nzOffsetTop: number;

get nzOffsetTop(): number {
return this._offsetTop;
}

private _offsetTop: number;

@Input()
set nzTarget(el: string | Element) {
this.target = typeof el === 'string' ? this.doc.querySelector(el) : el;
this.registerScrollEvent();
}
@Input() nzContainer: string | HTMLElement;
@Input() nzTarget: string | HTMLElement;

@Output() readonly nzClick = new EventEmitter<string>();
@Output() readonly nzScroll = new EventEmitter<NzAnchorLinkComponent>();

visible = false;
wrapperStyle: NgStyleInterface = { 'max-height': '100vh' };

container: HTMLElement | Window;

private links: NzAnchorLinkComponent[] = [];
private animating = false;
private target: Element | null = null;
private scroll$: Subscription | null = null;
private destroyed = false;
private destroy$ = new Subject();
private handleScrollTimeoutID = -1;

constructor(
public nzConfigService: NzConfigService,
private scrollSrv: NzScrollService,
/* tslint:disable-next-line:no-any */
@Inject(DOCUMENT) private doc: any,
public nzConfigService: NzConfigService,
private scrollSrv: NzScrollService,
private cdr: ChangeDetectorRef,
private platform: Platform
private platform: Platform,
private zone: NgZone
) {}

registerLink(link: NzAnchorLinkComponent): void {
Expand All @@ -110,54 +125,41 @@ export class NzAnchorComponent implements OnDestroy, AfterViewInit {
this.links.splice(this.links.indexOf(link), 1);
}

private getTarget(): Element | Window {
return this.target || window;
private getContainer(): HTMLElement | Window {
return this.container || window;
}

ngAfterViewInit(): void {
this.registerScrollEvent();
}

ngOnDestroy(): void {
this.destroyed = true;
this.removeListen();
clearTimeout(this.handleScrollTimeoutID);
this.destroy$.next();
this.destroy$.complete();
}

private registerScrollEvent(): void {
if (!this.platform.isBrowser) {
return;
}
this.removeListen();
this.scroll$ = fromEvent(this.getTarget(), 'scroll')
.pipe(throttleTime(50), distinctUntilChanged())
.subscribe(() => this.handleScroll());
this.destroy$.next();
this.zone.runOutsideAngular(() => {
fromEvent(this.getContainer(), 'scroll')
.pipe(throttleTime(50), takeUntil(this.destroy$))
.subscribe(() => {
this.zone.run(() => {
this.handleScroll();
});
});
});
// Browser would maintain the scrolling position when refreshing.
// So we have to delay calculation in avoid of getting a incorrect result.
setTimeout(() => this.handleScroll());
}

private removeListen(): void {
if (this.scroll$) {
this.scroll$.unsubscribe();
}
}

private getOffsetTop(element: HTMLElement): number {
if (!element || !element.getClientRects().length) {
return 0;
}
const rect = element.getBoundingClientRect();
if (rect.width || rect.height) {
if (this.getTarget() === window) {
return rect.top - element.ownerDocument!.documentElement!.clientTop;
}
return rect.top - (this.getTarget() as HTMLElement).getBoundingClientRect().top;
}
return rect.top;
this.handleScrollTimeoutID = setTimeout(() => this.handleScroll());
}

handleScroll(): void {
if (typeof document === 'undefined' || this.destroyed || this.animating) {
if (typeof document === 'undefined' || this.animating) {
return;
}

Expand All @@ -170,7 +172,7 @@ export class NzAnchorComponent implements OnDestroy, AfterViewInit {
}
const target = this.doc.getElementById(sharpLinkMatch[1]);
if (target) {
const top = this.getOffsetTop(target);
const top = getOffsetTop(target, this.getContainer());
if (top < scope) {
sections.push({
top,
Expand Down Expand Up @@ -203,7 +205,7 @@ export class NzAnchorComponent implements OnDestroy, AfterViewInit {
comp.active = true;
comp.markForCheck();

const linkNode = (comp.elementRef.nativeElement as HTMLDivElement).querySelector('.ant-anchor-link-title') as HTMLElement;
const linkNode = comp.getLinkTitleElement();
this.ink.nativeElement.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
this.visible = true;
this.cdr.detectChanges();
Expand All @@ -218,13 +220,30 @@ export class NzAnchorComponent implements OnDestroy, AfterViewInit {
}

this.animating = true;
const containerScrollTop = this.scrollSrv.getScroll(this.getTarget());
const elOffsetTop = this.getOffsetTop(el);
const containerScrollTop = this.scrollSrv.getScroll(this.getContainer());
const elOffsetTop = getOffsetTop(el, this.getContainer());
const targetScrollTop = containerScrollTop + elOffsetTop - (this.nzOffsetTop || 0);
this.scrollSrv.scrollTo(this.getTarget(), targetScrollTop, undefined, () => {
this.scrollSrv.scrollTo(this.getContainer(), targetScrollTop, undefined, () => {
this.animating = false;
this.handleActive(linkComp);
});
this.nzClick.emit(linkComp.nzHref);
}

ngOnChanges(changes: SimpleChanges): void {
const { nzOffsetTop, nzTarget, nzContainer } = changes;
if (nzOffsetTop) {
this.wrapperStyle = {
'max-height': `calc(100vh - ${this.nzOffsetTop}px)`
};
}
if (nzContainer || nzTarget) {
const container = this.nzContainer || this.nzTarget;
this.container = typeof container === 'string' ? this.doc.querySelector(container) : container;
this.registerScrollEvent();
if (nzTarget) {
warnDeprecation(`'nzTarget' of 'nz-anchor' is deprecated and will be removed in 10.0.0.Please use 'nzContainer' instead.`);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { NgModule } from '@angular/core';
import { NzAffixModule } from 'ng-zorro-antd/affix';
import { SCROLL_SERVICE_PROVIDER } from 'ng-zorro-antd/core';

import { NzAnchorLinkComponent } from './nz-anchor-link.component';
import { NzAnchorComponent } from './nz-anchor.component';
import { NzAnchorLinkComponent } from './anchor-link.component';
import { NzAnchorComponent } from './anchor.component';

@NgModule({
declarations: [NzAnchorComponent, NzAnchorLinkComponent],
Expand Down
8 changes: 4 additions & 4 deletions components/anchor/anchor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import { fakeAsync, tick, TestBed, ComponentFixture } from '@angular/core/testing';
import { Component, DebugElement, ViewChild } from '@angular/core';
import { By } from '@angular/platform-browser';
import { NzAnchorModule } from './nz-anchor.module';
import { NzAnchorComponent } from './nz-anchor.component';
import { NzAnchorModule } from './anchor.module';
import { NzAnchorComponent } from './anchor.component';
import { NzScrollService } from 'ng-zorro-antd/core';

const throttleTime = 51;
Expand Down Expand Up @@ -44,10 +44,10 @@ describe('anchor', () => {
});

it('should hava remove listen when the component is destroyed', () => {
expect(context.comp['scroll$']!.closed).toBeFalsy();
expect(context.comp['destroy$']!.isStopped).toBeFalsy();
context.comp.ngOnDestroy();
fixture.detectChanges();
expect(context.comp['scroll$']!.closed).toBeTruthy();
expect(context.comp['destroy$']!.isStopped).toBeTruthy();
});

it('should actived when scrolling to the anchor', (done: () => void) => {
Expand Down
2 changes: 1 addition & 1 deletion components/anchor/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { NzAnchorModule } from 'ng-zorro-antd/anchor';
| `[nzBounds]` | Bounding distance of anchor area, unit: px | `number` | `5` ||
| `[nzOffsetTop]` | Pixels to offset from top when calculating position of scroll | `number` | `0` ||
| `[nzShowInkInFixed]` | Whether show ink-balls in Fixed mode | `boolean` | `false` ||
| `[nzTarget]` | Scrolling container | `string \| HTMLElement` | `window` |
| `[nzContainer]` | Scrolling container | `string \| HTMLElement` | `window` |
| `(nzClick)` | Click of Anchor item | `EventEmitter<string>` | - |
| `(nzScroll)` | The scroll function that is triggered when scrolling to an anchor. | `EventEmitter<NzAnchorLinkComponent>` | - |

Expand Down
2 changes: 1 addition & 1 deletion components/anchor/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { NzAnchorModule } from 'ng-zorro-antd/anchor';
| `[nzBounds]` | 锚点区域边界,单位:px | `number` | `5` ||
| `[nzOffsetTop]` | 距离窗口顶部达到指定偏移量后触发 | `number` | - ||
| `[nzShowInkInFixed]` | 固定模式是否显示小圆点 | `boolean` | `false` ||
| `[nzTarget]` | 指定滚动的容器 | `string \| HTMLElement` | `window` |
| `[nzContainer]` | 指定滚动的容器 | `string \| HTMLElement` | `window` |
| `(nzClick)` | 点击项触发 | `EventEmitter<string>` | - |
| `(nzScroll)` | 滚动至某锚点时触发 | `EventEmitter<NzAnchorLinkComponent>` | - |

Expand Down
9 changes: 0 additions & 9 deletions components/anchor/nz-anchor-link.component.html

This file was deleted.

13 changes: 0 additions & 13 deletions components/anchor/nz-anchor.component.html

This file was deleted.

6 changes: 3 additions & 3 deletions components/anchor/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

export * from './nz-anchor-link.component';
export * from './nz-anchor.component';
export * from './nz-anchor.module';
export * from './anchor-link.component';
export * from './anchor.component';
export * from './anchor.module';
Loading

0 comments on commit fd36dd4

Please sign in to comment.