Skip to content

Commit

Permalink
feat(module:affix&anchor&back-top&avatar): add components to library (#…
Browse files Browse the repository at this point in the history
…88)

* feat(module:back-top): add `nz-back-top` component

* feat(module:affix): add `nz-affix` component

* feat(module:anchor): add `nz-anchor` component

* feat(module:avatar): add `nz-avatar` component
  • Loading branch information
cipchk authored and wilsoncook committed Aug 26, 2017
1 parent 2bf24e0 commit 468e80b
Show file tree
Hide file tree
Showing 50 changed files with 1,956 additions and 1 deletion.
59 changes: 59 additions & 0 deletions src/components/affix/nz-affix.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed, ComponentFixtureAutoDetect, fakeAsync, tick } from '@angular/core/testing';
import { Component, DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';

import { NzAffixModule } from './nz-affix.module';
import { NzScrollService } from "../core/scroll/nz-scroll.service";
import { NzAffixComponent } from "./nz-affix.component";

describe('Component:nz-affix', () => {

let scrollSrv: MockNzScrollService;
let fixture: ComponentFixture<TestAffixComponent>;
let context: TestAffixComponent;
let el: HTMLDivElement;
let comp: NzAffixComponent;

beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
imports: [NzAffixModule],
declarations: [TestAffixComponent],
providers: [
{ provide: NzScrollService, useClass: MockNzScrollService }
]
}).compileComponents();

fixture = TestBed.createComponent(TestAffixComponent);
context = fixture.componentInstance;
spyOn(context, 'onChange');
fixture.detectChanges();
el = fixture.nativeElement;
comp = fixture.debugElement.query(By.css('nz-affix')).componentInstance as NzAffixComponent;
tick();
}));

it('should correctly initialize and attach to DOM', () => {
expect(el.querySelectorAll('.ant-affix').length).toBe(1);
});

});

@Component({ template: `<nz-affix [nzOffsetTop]="10" (nzChange)="onChange($event)"><button>Affix Button</button></nz-affix>` })
class TestAffixComponent {
onChange(status: boolean) {
return status;
}
}

class MockNzScrollService {

getOffset(el: Element): { top: number, left: number } {
return { top: 0, left: 0 };
}

getScroll(el?: Element | Window, top: boolean = true): number {
return 0;
}

}
121 changes: 121 additions & 0 deletions src/components/affix/nz-affix.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {
Component,
ViewEncapsulation,
OnInit,
Input,
EventEmitter,
Output,
Renderer2,
OnDestroy,
ViewChild,
ElementRef,
HostBinding
} from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';

import { NzScrollService } from "../core/scroll/nz-scroll.service";

@Component({
selector: 'nz-affix',
encapsulation: ViewEncapsulation.None,
template: `<div #wrap><ng-content></ng-content></div>`,
styleUrls: [
'./style/index.less',
'./style/patch.less'
]
})
export class NzAffixComponent implements OnInit, OnDestroy {

private scroll$: Subscription = null;
private scrollWinInTarget$: Subscription = null;
private target: Element = null;
@ViewChild('wrap') private wrap: ElementRef;
// 缓存固定状态
private fixed: boolean = false;
// 原始位置
private orgOffset: { top: number, left: number };

@Input()
set nzTarget(el: Element) {
this.target = el;
this.registerScrollEvent();
}

@Input() nzOffsetTop: number = 0;

@Input() nzOffsetBottom: number = 0;

@Output() nzChange: EventEmitter<boolean> = new EventEmitter();

constructor(private scrollSrv: NzScrollService, private _el: ElementRef, private _renderer: Renderer2) { }

ngOnInit(): void {
if (!this.scroll$) this.registerScrollEvent();
}

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

private reCalculate() {
let elOffset = this.scrollSrv.getOffset(this._el.nativeElement);
this.orgOffset = {
top: elOffset.top + this.scrollSrv.getScroll(this.getTarget()),
left: elOffset.left + this.scrollSrv.getScroll(this.getTarget(), false)
};

return this;
}

private process() {
if (!this.orgOffset) this.reCalculate();
const containerScrollTop = this.scrollSrv.getScroll(this.getTarget());
let fixTop = this.getTarget() === window ? 0 : this.scrollSrv.getOffset(this.getTarget() as Element).top;
let hasFixed = this.orgOffset.top - fixTop - containerScrollTop - this.nzOffsetTop <= 0;
if (this.fixed === hasFixed) return;

const wrapEl = this.wrap.nativeElement;
wrapEl.classList[hasFixed ? 'add' : 'remove']('ant-affix');
if (hasFixed) {
wrapEl.style.cssText = `top:${this.nzOffsetTop + fixTop}px;left:${this.orgOffset.left}px`;
} else {
wrapEl.style.cssText = ``;
}

this.fixed = hasFixed;
this.nzChange.emit(hasFixed);
}

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

private registerScrollEvent() {
this.removeListen();
this.reCalculate().process();
this.scroll$ = Observable.fromEvent(this.getTarget(), 'scroll')
.throttleTime(50)
.distinctUntilChanged()
.subscribe(e => {
this.process();
});

if (this.getTarget() !== window) {
// 当 window 滚动位发生变动时,需要重新计算滚动容器
this.scrollWinInTarget$ = Observable.fromEvent(window, 'scroll')
.throttleTime(50)
.distinctUntilChanged()
.subscribe(e => {
this.orgOffset = null;
this.fixed = false;
});
}
}

ngOnDestroy(): void {
this.removeListen();
}

}
15 changes: 15 additions & 0 deletions src/components/affix/nz-affix.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { NzAffixComponent } from './nz-affix.component';
import { SCROLL_SERVICE_PROVIDER } from "../core/scroll/nz-scroll.service";

@NgModule({
declarations: [ NzAffixComponent ],
exports : [ NzAffixComponent ],
imports : [ CommonModule ],
providers : [ SCROLL_SERVICE_PROVIDER ]
})
export class NzAffixModule {
}

6 changes: 6 additions & 0 deletions src/components/affix/style/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@import "../../style/themes/default";

.@{ant-prefix}-affix {
position: fixed;
z-index: @zindex-affix;
}
3 changes: 3 additions & 0 deletions src/components/affix/style/patch.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
nz-affix {
display: block;
}
52 changes: 52 additions & 0 deletions src/components/anchor/nz-anchor-link.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
Component,
ViewEncapsulation,
Input,
TemplateRef,
ContentChild,
HostBinding,
HostListener,
ElementRef
} from '@angular/core';

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

@Component({
selector: 'nz-link',
encapsulation: ViewEncapsulation.None,
template: `
<a (click)="goToClick($event)" href="{{nzHref}}" class="ant-anchor-link-title">
<span *ngIf="!nzTemplate">{{nzTitle}}</span>
<ng-template *ngIf="nzTemplate" [ngTemplateOutlet]="nzTemplate"></ng-template>
</a>
<ng-content></ng-content>
`
})
export class NzAnchorLinkComponent {

@Input() nzHref: string;

@Input() nzTitle: string;

@ContentChild('nzTemplate') nzTemplate: TemplateRef<any>;

@HostBinding('class.ant-anchor-link') _nzAnchorLink = true;

@HostBinding('class.ant-anchor-link-active') active: boolean = false;

@HostListener('click')
_onClick() {
this._anchorComp.scrollTo(this);
}

constructor(public el: ElementRef, private _anchorComp: NzAnchorComponent) {
this._anchorComp.add(this);
}

goToClick(e: Event) {
e.preventDefault();
e.stopPropagation();
this._anchorComp.scrollTo(this);
return false;
}
}

0 comments on commit 468e80b

Please sign in to comment.