Skip to content

Commit

Permalink
refactor(module:affix): refactor affix (#2544)
Browse files Browse the repository at this point in the history
* refactor(module:affix): refactor affix

- the nzTarget support string value

* fix: release

* style: removed no-any in first line

* fix: fix style

* chore: fix test

* chore: fix display style in host

* chore: remove :host
  • Loading branch information
cipchk authored and vthinkxie committed Nov 30, 2018
1 parent 484a793 commit 9f48b8f
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 93 deletions.
36 changes: 23 additions & 13 deletions components/affix/affix.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ describe('affix', () => {
new Event('pageshow'),
new Event('load')
];
const height = 100;
const width = 100;

beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
Expand Down Expand Up @@ -109,13 +111,13 @@ describe('affix', () => {
describe('when element gets shifted horizontally', () => {
it('adjusts left position accordingly to maintain natural position', fakeAsync(() => {
setupInitialState();
componentObject.offsetTo(componentObject.elementRef(), { top: startOffset, left: 10, width: 100, height: 100 });
componentObject.offsetTo(componentObject.elementRef(), { top: startOffset, left: 10, width, height });
emitScroll(window, defaultOffsetTop + startOffset + 1);

expect(componentObject.wrap().offsetLeft).toBe(10);

emitScroll(window, defaultOffsetTop + startOffset - 1);
componentObject.offsetTo(componentObject.elementRef(), { top: startOffset, left: 100, width: 100, height: 100 });
componentObject.offsetTo(componentObject.elementRef(), { top: startOffset, left: 100, width, height });
emitScroll(window, defaultOffsetTop + startOffset + 1);

expect(componentObject.wrap().offsetLeft).toBe(100);
Expand Down Expand Up @@ -288,6 +290,15 @@ describe('affix', () => {
fixture.detectChanges();
expect(component.updatePosition).toHaveBeenCalled();
});

it('should be a string value', () => {
spyOn(component, 'updatePosition');
expect(component.updatePosition).not.toHaveBeenCalled();
fixture.detectChanges();
context.fakeTarget = '#target';
fixture.detectChanges();
expect(component.updatePosition).toHaveBeenCalled();
});
});

describe('(nzChange)', () => {
Expand Down Expand Up @@ -318,19 +329,20 @@ describe('affix', () => {
}));
});

it('should adjust the width when resize', fakeAsync(() => {
it('should adjust placeholder width when resize', fakeAsync(() => {
const offsetTop = 150;
context.newOffset = offsetTop;
setupInitialState({ offsetTop: offsetTop + 1 });
emitScroll(window, 2);
expect(componentObject.elementRef().style.width).toBe(`${width}px`);
componentObject.offsetYTo(componentObject.elementRef(), offsetTop + 2);
tick(20);
fixture.detectChanges();
componentObject.emitEvent(window, new Event('resize'));
tick(20);
fixture.detectChanges();

expect(componentObject.wrap().offsetTop).toBe(offsetTop);
expect(componentObject.elementRef().style.width).toBe(``);

discardPeriodicTasks();
}));
Expand All @@ -352,7 +364,7 @@ describe('affix', () => {
}

getOffset(el: Element): Offset {
return this.offsets[el.id] || { top: 10, left: 0, height: 100, width: 100 };
return this.offsets[el.id] || { top: 10, left: 0, height, width };
}

emitEvent(el: Element | Window, event: Event): void {
Expand All @@ -368,8 +380,8 @@ describe('affix', () => {
this.offsets[this.getKey(el)] = {
top: offset.top,
left: offset.left,
height: 100,
width: 100
height,
width
};
}

Expand All @@ -379,8 +391,8 @@ describe('affix', () => {
{
top: offsetTop,
left: 0,
height: 100,
width: 100
height,
width
}
);
}
Expand Down Expand Up @@ -486,14 +498,12 @@ describe('affix-extra', () => {
<button id="content">Affix Button</button>
</nz-affix>
<div id="target"></div>
`,
styleUrls: [ './style/index.less' ],
encapsulation: ViewEncapsulation.None
`
})
class TestAffixComponent {
@ViewChild(NzAffixComponent)
nzAffixComponent: NzAffixComponent;
fakeTarget: Element | Window = null;
fakeTarget: string | Element | Window = null;
newOffset: {};
newOffsetBottom: {};
}
2 changes: 1 addition & 1 deletion components/affix/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Please note that Affix should not cover other content on the page, especially wh
| -------- | ----------- | ---- | ------- |
| `[nzOffsetBottom]` | Pixels to offset from bottom when calculating position of scroll | number | - |
| `[nzOffsetTop]` | Pixels to offset from top when calculating position of scroll | number | 0 |
| `[nzTarget]` | specifies the scrollable area dom node | `HTMLElement` | `window` |
| `[nzTarget]` | specifies the scrollable area dom node | `string, HTMLElement` | `window` |
| `(nzChange)` | Callback for when affix state is changed | `EventEmitter<boolean>` | - |

**Note:** Children of `nz-affix` can not be `position: absolute`, but you can set `nz-affix` as `position: absolute`:
Expand Down
2 changes: 1 addition & 1 deletion components/affix/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ title: Affix
| --- | --- | --- | --- |
| `[nzOffsetBottom]` | 距离窗口底部达到指定偏移量后触发 | number | |
| `[nzOffsetTop]` | 距离窗口顶部达到指定偏移量后触发 | number | |
| `[nzTarget]` | 设置 `nz-affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | `HTMLElement` | `window` |
| `[nzTarget]` | 设置 `nz-affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | `string, HTMLElement` | `window` |
| `(nzChange)` | 固定状态改变时触发的回调函数 | `EventEmitter<boolean>` ||

**注意:**`nz-affix` 内的元素不要使用绝对定位,如需要绝对定位的效果,可以直接设置 `nz-affix` 为绝对定位:
Expand Down
161 changes: 83 additions & 78 deletions components/affix/nz-affix.component.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
// tslint:disable:no-any
import { DOCUMENT } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
Inject,
Input,
OnDestroy,
OnInit,
Output,
ViewChild
ViewChild,
ViewEncapsulation
} from '@angular/core';

import { NzScrollService } from '../core/scroll/nz-scroll.service';
Expand All @@ -21,18 +23,19 @@ import { throttleByAnimationFrameDecorator } from '../core/util/throttleByAnimat
selector : 'nz-affix',
templateUrl : './nz-affix.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
styles : [
`:host {
styles : [ `
nz-affix {
display: block;
}`
]
}
` ],
encapsulation : ViewEncapsulation.None
})
export class NzAffixComponent implements OnInit, OnDestroy {

@Input()
set nzTarget(value: Element | Window) {
set nzTarget(value: string | Element | Window) {
this.clearEventListeners();
this._target = value || window;
this._target = typeof value === 'string' ? this.doc.querySelector(value) : value || window;
this.setTargetEventListeners();
this.updatePosition({});
}
Expand Down Expand Up @@ -60,10 +63,11 @@ export class NzAffixComponent implements OnInit, OnDestroy {
@Output()
readonly nzChange: EventEmitter<boolean> = new EventEmitter();

constructor(private scrollSrv: NzScrollService, private _el: ElementRef, private cd: ChangeDetectorRef) {
// tslint:disable-next-line:no-any
constructor(private scrollSrv: NzScrollService, private _el: ElementRef, @Inject(DOCUMENT) private doc: any, private cd: ChangeDetectorRef) {
}

private timeout: any;
private timeout;
private events = [
'resize',
'scroll',
Expand All @@ -73,8 +77,8 @@ export class NzAffixComponent implements OnInit, OnDestroy {
'pageshow',
'load'
];
private affixStyle: any;
private placeholderStyle: any;
private affixStyle;
private placeholderStyle;

@ViewChild('wrap') private wrap: ElementRef;

Expand All @@ -94,6 +98,7 @@ export class NzAffixComponent implements OnInit, OnDestroy {
ngOnDestroy(): void {
this.clearEventListeners();
clearTimeout(this.timeout);
// tslint:disable-next-line:no-any
(this.updatePosition as any).cancel();
}

Expand All @@ -109,7 +114,7 @@ export class NzAffixComponent implements OnInit, OnDestroy {
const scrollTop = this.scrollSrv.getScroll(target, true);
const scrollLeft = this.scrollSrv.getScroll(target, false);

const docElem = window.document.body;
const docElem = this.doc.body;
const clientTop = docElem.clientTop || 0;
const clientLeft = docElem.clientLeft || 0;

Expand All @@ -121,7 +126,72 @@ export class NzAffixComponent implements OnInit, OnDestroy {
};
}

private setTargetEventListeners(): void {
this.clearEventListeners();
this.events.forEach((eventName: string) => {
this._target.addEventListener(eventName, this.updatePosition, false);
});
}

private clearEventListeners(): void {
this.events.forEach(eventName => {
this._target.removeEventListener(eventName, this.updatePosition, false);
});
}

private getTargetRect(target: Element | Window | null): ClientRect {
return target !== window ?
(target as HTMLElement).getBoundingClientRect() :
{ top: 0, left: 0, bottom: 0 } as ClientRect;
}

private genStyle(affixStyle: {}): string {
if (affixStyle == null) {
return '';
}
return Object.keys(affixStyle).map(key => {
const val = affixStyle[ key ];
return `${key}:${typeof val === 'string' ? val : val + 'px'}`;
}).join(';');
}

private setAffixStyle(e: Event, affixStyle: {}): void {
const originalAffixStyle = this.affixStyle;
const isWindow = this._target === window;
if (e.type === 'scroll' && originalAffixStyle && affixStyle && isWindow) {
return;
}
if (shallowEqual(originalAffixStyle, affixStyle)) {
return;
}

const fixed = !!affixStyle;
const wrapEl = this.wrap.nativeElement as HTMLElement;
wrapEl.style.cssText = this.genStyle(affixStyle);
this.affixStyle = affixStyle;
const cls = 'ant-affix';
if (fixed) {
wrapEl.classList.add(cls);
} else {
wrapEl.classList.remove(cls);
}

if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) {
this.nzChange.emit(fixed);
}
}

private setPlaceholderStyle(placeholderStyle: {}): void {
const originalPlaceholderStyle = this.placeholderStyle;
if (shallowEqual(placeholderStyle, originalPlaceholderStyle)) {
return;
}
(this._el.nativeElement as HTMLElement).style.cssText = this.genStyle(placeholderStyle);
this.placeholderStyle = placeholderStyle;
}

@throttleByAnimationFrameDecorator()
// tslint:disable-next-line:no-any
updatePosition(e: any): void {
const targetNode = this._target;
// Backwards support
Expand Down Expand Up @@ -187,69 +257,4 @@ export class NzAffixComponent implements OnInit, OnDestroy {
this.setPlaceholderStyle(null);
}
}

private setTargetEventListeners(): void {
this.clearEventListeners();
this.events.forEach((eventName: string) => {
this._target.addEventListener(eventName, this.updatePosition, false);
});
}

private clearEventListeners(): void {
this.events.forEach(eventName => {
this._target.removeEventListener(eventName, this.updatePosition, false);
});
}

private getTargetRect(target: Element | Window | null): ClientRect {
return target !== window ?
(target as HTMLElement).getBoundingClientRect() :
{ top: 0, left: 0, bottom: 0 } as ClientRect;
}

private genStyle(affixStyle: {}): string {
if (affixStyle == null) {
return '';
}
return Object.keys(affixStyle).map(key => {
const val = affixStyle[ key ];
return `${key}:${typeof val === 'string' ? val : val + 'px'}`;
}).join(';');
}

private setAffixStyle(e: any, affixStyle: {}): void {
const originalAffixStyle = this.affixStyle;
const isWindow = this._target === window;
if (e.type === 'scroll' && originalAffixStyle && affixStyle && isWindow) {
return;
}
if (shallowEqual(originalAffixStyle, affixStyle)) {
return;
}

const fixed = !!affixStyle;
const wrapEl = this.wrap.nativeElement as HTMLElement;
wrapEl.style.cssText = this.genStyle(affixStyle);
this.affixStyle = affixStyle;
const cls = 'ant-affix';
if (fixed) {
wrapEl.classList.add(cls);
} else {
wrapEl.classList.remove(cls);
}

if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) {
this.nzChange.emit(fixed);
}
}

private setPlaceholderStyle(placeholderStyle: {}): void {
const originalPlaceholderStyle = this.placeholderStyle;
if (shallowEqual(placeholderStyle, originalPlaceholderStyle)) {
return;
}
(this._el.nativeElement as HTMLElement).style.cssText = this.genStyle(placeholderStyle);
this.placeholderStyle = placeholderStyle;
}

}

0 comments on commit 9f48b8f

Please sign in to comment.