Skip to content

Commit

Permalink
feat(module:transfer): add transfer component (#578)
Browse files Browse the repository at this point in the history
export transfer component.

fix code review issues.

related #132
  • Loading branch information
cipchk authored and vthinkxie committed Nov 17, 2017
1 parent 5bc4327 commit 84895cb
Show file tree
Hide file tree
Showing 21 changed files with 1,262 additions and 1 deletion.
8 changes: 8 additions & 0 deletions src/components/locale/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,12 @@ export const enUS: NzLocale = {
Select: {
notFoundContent: 'Not Found',
},

Transfer: {
titles: ',',
notFoundContent: 'Not Found',
searchPlaceholder: 'Search here',
itemUnit: 'item',
itemsUnit: 'items',
}
};
8 changes: 8 additions & 0 deletions src/components/locale/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,12 @@ export const zhCN: NzLocale = {
Select: {
notFoundContent: '无法找到',
},

Transfer: {
titles: ',',
notFoundContent: '无匹配结果',
searchPlaceholder: '请输入',
itemUnit: '项目',
itemsUnit: '项目',
}
};
8 changes: 8 additions & 0 deletions src/components/locale/nz-locale.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,12 @@ export class NzLocale {
Select: {
notFoundContent: string;
};

Transfer: {
titles: string,
notFoundContent: string,
searchPlaceholder: string,
itemUnit: string,
itemsUnit: string,
};
}
6 changes: 5 additions & 1 deletion src/components/ng-zorro-antd.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { NzBackTopModule } from './back-top/nz-back-top.module';
import { NzAffixModule } from './affix/nz-affix.module';
import { NzAnchorModule } from './anchor/nz-anchor.module';
import { NzAvatarModule } from './avatar/nz-avatar.module';
import { NzTransferModule } from './transfer/nz-transfer.module';

// Services
import { NzNotificationService } from './notification/nz-notification.service';
Expand Down Expand Up @@ -118,6 +119,7 @@ export { NzBackTopModule } from './back-top/nz-back-top.module';
export { NzAffixModule } from './affix/nz-affix.module';
export { NzAnchorModule } from './anchor/nz-anchor.module';
export { NzAvatarModule } from './avatar/nz-avatar.module';
export { NzTransferModule } from './transfer/nz-transfer.module';

// Components
export { NzRowComponent } from './grid/nz-row.component';
Expand Down Expand Up @@ -222,6 +224,7 @@ export { NzAffixComponent } from './affix/nz-affix.component';
export { NzAnchorLinkComponent } from './anchor/nz-anchor-link.component';
export { NzAnchorComponent } from './anchor/nz-anchor.component';
export { NzAvatarComponent } from './avatar/nz-avatar.component';
export { NzTransferComponent } from './transfer/nz-transfer.component';

// Services
export { NzNotificationService } from './notification/nz-notification.service';
Expand Down Expand Up @@ -286,7 +289,8 @@ export { NZ_ROOT_CONFIG, NzRootConfig } from './root/nz-root-config';
NzBackTopModule,
NzAffixModule,
NzAnchorModule,
NzAvatarModule
NzAvatarModule,
NzTransferModule
]
})
export class NgZorroAntdModule {
Expand Down
8 changes: 8 additions & 0 deletions src/components/transfer/item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface TransferItem {
title: string;
direction?: 'left' | 'right';
disabled?: boolean;
checked?: boolean;
_hiden?: boolean;
[key: string]: any;
}
178 changes: 178 additions & 0 deletions src/components/transfer/nz-transfer-list.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// tslint:disable:member-ordering
import { Component, Input, ContentChild, TemplateRef, Renderer2, ElementRef, OnChanges, SimpleChanges, Output, EventEmitter, OnInit, DoCheck, IterableDiffers, IterableDiffer } from '@angular/core';
import { TransferItem } from './item';

@Component({
selector: 'nz-transfer-list',
template: `
<div class="ant-transfer-list-header">
<label nz-checkbox [(ngModel)]="stat.checkAll" (ngModelChange)="onHandleSelectAll()"
[nzIndeterminate]="stat.checkHalf"></label><span class="ant-transfer-list-header-selected">
<span>{{ (stat.checkCount > 0 ? stat.checkCount + '/' : '') + stat.shownCount}} {{_list.length > 1 ? itemsUnit : itemUnit}}</span>
<span *ngIf="titleText" class="ant-transfer-list-header-title">{{titleText}}</span>
</span>
</div>
<div class="{{showSearch ? 'ant-transfer-list-body ant-transfer-list-body-with-search' : 'ant-transfer-list-body'}}"
[ngClass]="{'ant-transfer__nodata': stat.shownCount === 0}">
<div *ngIf="showSearch" class="ant-transfer-list-body-search-wrapper">
<nz-transfer-search class="ant-transfer-list-search"
(valueChanged)="handleFilter($event)"
(valueClear)="handleClear()"
[placeholder]="searchPlaceholder"
[value]="filter"></nz-transfer-search>
</div>
<ul class="ant-transfer-list-content">
<ng-container *ngFor="let item of _list">
<li *ngIf="!item._hiden" (click)="_handleSelect(item)" class="ant-transfer-list-content-item">
<label nz-checkbox [ngModel]="item.checked" [nzDisabled]="item.disabled">
<span>
<ng-container *ngIf="!render; else renderContainer">{{item.title}}</ng-container>
<ng-template #renderContainer [ngTemplateOutlet]="render" [ngTemplateOutletContext]="{ $implicit: item }"></ng-template>
</span>
</label>
</li>
</ng-container>
</ul>
<div class="ant-transfer-list-body-not-found">{{notFoundContent}}</div>
</div>
<div *ngIf="footer" class="ant-transfer-list-footer">
<ng-template [ngTemplateOutlet]="footer" [ngTemplateOutletContext]="{ $implicit: direction }"></ng-template>
</div>
`
})
export class NzTransferListComponent implements OnChanges, OnInit, DoCheck {

// private
_list: TransferItem[] = [];

// region: fields

@Input() direction = '';
@Input() titleText = '';

@Input()
set dataSource(list: TransferItem[]) {
this._list = list;
this.updateCheckStatus();
}

@Input() itemUnit = '';
@Input() itemsUnit = '';
@Input() filter = '';
// search
@Input() showSearch: boolean;
@Input() searchPlaceholder: string;
@Input() notFoundContent: string;
@Input() filterOption: (inputValue: any, item: any) => boolean;

@Input() render: TemplateRef<any>;
@Input() footer: TemplateRef<any>;

// events
@Output() handleSelectAll: EventEmitter<boolean> = new EventEmitter<boolean>();
@Output() handleSelect: EventEmitter<any> = new EventEmitter<any>();
@Output() filterChange: EventEmitter<any> = new EventEmitter<any>();

// endregion

// region: styles

_prefixCls = 'ant-transfer-list';
_classList: string[] = [];

_setClassMap() {
this._classList.forEach(cls => this._renderer.removeClass(this._el.nativeElement, cls));

this._classList = [
this._prefixCls,
!!this.footer && `${this._prefixCls}-with-footer`
].filter(item => !!item);

this._classList.forEach(cls => this._renderer.addClass(this._el.nativeElement, cls));
}

// endregion

// region: select all
stat = {
checkAll: false,
checkHalf: false,
checkCount: 0,
shownCount: 0
};

onHandleSelectAll() {
this._list.forEach(item => {
if (!item.disabled) {
item.checked = this.stat.checkAll;
}
});
this.updateCheckStatus();

this.handleSelectAll.emit(this.stat.checkAll);
}

private updateCheckStatus() {
const validCount = this._list.filter(w => !w.disabled).length;
this.stat.checkCount = this._list.filter(w => w.checked && !w.disabled).length;
this.stat.shownCount = this._list.filter(w => !w._hiden).length;
this.stat.checkAll = validCount > 0 && validCount === this.stat.checkCount;
this.stat.checkHalf = this.stat.checkCount > 0 && !this.stat.checkAll;
}

// endregion

// region: search

handleFilter(value: string) {
this._list.forEach(item => {
item._hiden = value.length > 0 && !this.matchFilter(value, item);
});
this.stat.shownCount = this._list.filter(w => !w._hiden).length;
this.filterChange.emit({ direction: this.direction, value });
}

handleClear() {
this.handleFilter('');
}

private matchFilter(text: string, item: TransferItem) {
if (this.filterOption) {
return this.filterOption(text, item);
}
return item.title.includes(text);
}

// endregion

_listDiffer: IterableDiffer<{}>;
constructor(private _el: ElementRef, private _renderer: Renderer2, differs: IterableDiffers) {
this._listDiffer = differs.find([]).create(null);
}

ngOnChanges(changes: SimpleChanges): void {
if ('footer' in changes) {
this._setClassMap();
}
}

ngOnInit() {
this._setClassMap();
}

ngDoCheck(): void {
const change = this._listDiffer.diff(this._list);
if (change) {
this.updateCheckStatus();
}
}

_handleSelect(item: TransferItem) {
if (item.disabled) {
return;
}
item.checked = !item.checked;
this.updateCheckStatus();
this.handleSelect.emit(item);
}
}
35 changes: 35 additions & 0 deletions src/components/transfer/nz-transfer-search.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
selector: 'nz-transfer-search',
template: `
<input nz-input [(ngModel)]="value" (ngModelChange)="_handle()"
[placeholder]="placeholder" class="ant-transfer-list-search">
<a *ngIf="value && value.length > 0; else def" class="ant-transfer-list-search-action" (click)="_clear()">
<i class="anticon anticon-cross-circle"></i>
</a>
<ng-template #def><span class="ant-transfer-list-search-action"><i class="anticon anticon-search"></i></span></ng-template>
`
})
export class NzTransferSearchComponent {

// region: fields

@Input() placeholder: string;
@Input() value: string;

@Output() valueChanged = new EventEmitter<string>();
@Output() valueClear = new EventEmitter();

// endregion

_handle() {
this.valueChanged.emit(this.value);
}

_clear() {
this.value = '';
this.valueClear.emit();
}

}
Loading

0 comments on commit 84895cb

Please sign in to comment.