@rdkmaster/jigsaw-labs
Version:
Jigsaw, the next generation component set for RDK
362 lines (307 loc) • 10.6 kB
text/typescript
import {
AfterViewInit, ChangeDetectorRef, Component, EventEmitter, Input, NgModule, OnDestroy, OnInit, Output, Renderer2,
ViewChild
} from "@angular/core";
import {CommonModule} from "@angular/common";
import {Observable} from "rxjs/Observable";
import {JigsawInput, JigsawInputModule} from "../input/input";
import {JigsawCheckBoxModule} from "../checkbox/index";
import {CheckBoxStatus} from "../checkbox/typings";
import {TableData} from "../../core/data/table-data";
import {_getColumnIndex, AdditionalTableData} from "./table-typings";
import {CommonUtils} from "../../core/utils/common-utils";
import {JigsawSwitchModule} from "../switch/index";
import {Subscription} from "rxjs/Subscription";
import {JigsawSelectModule} from "../select/select";
import {ArrayCollection} from "../../core/data/array-collection";
export class TableCellRendererBase implements OnInit, OnDestroy {
public cellData: any;
public row: number;
public field: string;
public initData: any;
public cellDataChange = new EventEmitter<any>();
protected targetData: TableData;
private _removeTableDataRefresh: Function;
private _removeAdditionalDataRefresh: Function;
private _column: number = -1;
public get column(): number {
return this._column;
}
protected onDataRefresh(): void {
}
public dispatchChangeEvent(value: any): void {
this.cellDataChange.emit(value)
}
private _tableData: TableData;
public get tableData(): TableData {
return this._tableData;
}
public set tableData(value: TableData) {
this._tableData = value;
this._initTargetData();
if (this._removeTableDataRefresh) {
this._removeTableDataRefresh();
}
this._removeTableDataRefresh = this._tableData.onRefresh(this.onDataRefresh, this);
}
protected _additionalData: AdditionalTableData;
public get additionalData(): TableData {
return this._additionalData;
}
public set additionalData(value: TableData) {
this._additionalData = <AdditionalTableData>value;
if (this._removeAdditionalDataRefresh) {
this._removeAdditionalDataRefresh();
}
this._removeAdditionalDataRefresh = this._additionalData.onRefresh(this.onDataRefresh, this);
this._initTargetData();
}
private _initTargetData(): void {
if (!this.tableData || !this.additionalData) {
return;
}
[this._column, this.targetData] = _getColumnIndex(this._tableData, this._additionalData, this.field);
}
ngOnDestroy() {
if (this._removeTableDataRefresh) {
this._removeTableDataRefresh();
}
if (this._removeAdditionalDataRefresh) {
this._removeAdditionalDataRefresh();
}
}
ngOnInit() {
this.onDataRefresh();
}
}
/*
* 默认表格渲染组件
* */
export class DefaultCellRenderer extends TableCellRendererBase {
}
/*
* 编辑单元格渲染器
* */
export class TableCellTextEditorRenderer extends TableCellRendererBase implements AfterViewInit {
protected input: JigsawInput;
ngAfterViewInit() {
this.input.focus();
}
}
/*
* head checkbox renderer
* */
export class TableHeadCheckboxRenderer extends TableCellRendererBase {
private _checked: CheckBoxStatus = CheckBoxStatus.unchecked;
public get checked(): CheckBoxStatus {
return this._checked;
}
public set checked(value: CheckBoxStatus) {
this._checked = value;
this.targetData.data.forEach((row, index) => {
row[this.column] = value;
this._additionalData.touchValueByRow(this.field, index, value);
});
this.targetData.refresh();
}
protected onDataRefresh(): void {
let type = 0;
this.targetData.data.forEach((row, index) => {
let value = this._additionalData.getTouchedValueByRow(this.field, index);
value = CommonUtils.isDefined(value) ? value : !!row[this.column];
type |= value ? 2 : 1;
});
switch (type) {
case 3:
this._checked = CheckBoxStatus.indeterminate;
break;
case 2:
this._checked = CheckBoxStatus.checked;
break;
case 1:
case 0:
default:
this._checked = CheckBoxStatus.unchecked;
}
}
}
/*
* cell checkbox renderer
* */
export class TableCellCheckboxRenderer extends TableCellRendererBase {
protected onDataRefresh() {
this._updateChecked();
this._updateTargetData();
}
public checked: boolean;
private _cellData: any;
public get cellData(): any {
return this._cellData;
}
public set cellData(value: any) {
this._cellData = value;
this._updateChecked();
this._updateTargetData();
}
private _updateChecked(): void {
let checked = this._additionalData.getTouchedValueByRow(this.field, this.row);
checked = CommonUtils.isDefined(checked) ? checked : this.cellData;
this.checked = checked;
}
private _updateTargetData() {
if (CommonUtils.isDefined(this.targetData.data[this.row])) {
this.targetData.data[this.row][this.column] = this.checked;
}
}
onChange(value) {
this.checked = value;
this._additionalData.touchValueByRow(this.field, this.row, value);
this._updateTargetData();
this.dispatchChangeEvent(value);
}
ngOnInit() {
super.ngOnInit();
if (!this._additionalData.trackRowBy && this.row == 0) { // this.row == 0 ensures print once
console.warn('You may need to add a [trackRowBy="field-name"] attribute to ' +
'the table if using a renderer which has status.');
}
}
}
/*
* switch renderer
* */
export class TableCellSwitchRenderer extends TableCellRendererBase {
}
export type InitDataGenerator = (td: TableData, row: number, column: number) =>
ArrayCollection<any> | any[] | Observable<ArrayCollection<any> | any[]>;
/**
* Select renderer
*/
export class TableCellSelectRenderer extends TableCellRendererBase implements OnInit, OnDestroy {
public selected: any;
public initData: InitDataGenerator | ArrayCollection<any> | any[];
public data: ArrayCollection<any> | any[];
constructor(private _changeDetector: ChangeDetectorRef, renderer: Renderer2) {
super();
this._removeKeyDownHandler = renderer.listen('document', 'keydown.esc', this._onKeyDown.bind(this));
}
private readonly _removeKeyDownHandler;
private _onKeyDown() {
this.dispatchChangeEvent(this.selected ? this.selected.label : '');
}
protected onDataRefresh() {
if (this.initData instanceof Function) {
const data = this.initData(this.tableData, this.row, this.column);
if (data instanceof Observable) {
this.data = [];
const subscription = data.subscribe(
value => {
subscription.unsubscribe();
if (!this.hasDestroyed) {
this.data = value;
this._changeDetector.detectChanges();
}
},
error => {
subscription.unsubscribe();
console.log('select renderer: read async data error', error);
if (!this.hasDestroyed) {
this.data = [];
this._changeDetector.detectChanges();
}
});
} else {
this.data = data;
}
} else {
this.data = this.initData;
}
}
public static defaultInitDataGenerator(tableData, row, col) {
// 找出当前列所有可选项
return tableData.data.reduce(
(items, row) => {
if (items.findIndex(office => office.label == row[col]) == -1) {
items.push({label: row[col]})
}
return items;
}, []);
}
private _cellData: any;
get cellData(): any {
return this._cellData;
}
set cellData(value: any) {
this._cellData = value;
this.selected = {label: value};
}
subscriber: Subscription;
public get hasDestroyed(): boolean {
return !this.subscriber;
}
ngOnInit() {
super.ngOnInit();
// 使用此方法使其他单元格退出编辑状态
this.tableData.emit(this);
this.subscriber = this.tableData.subscribe(renderer => {
if (this != renderer) {
this.dispatchChangeEvent(this.cellData);
}
});
}
ngOnDestroy() {
super.ngOnDestroy();
this._removeKeyDownHandler();
// 随手清理垃圾是一个好习惯
if (this.subscriber) {
this.subscriber.unsubscribe();
this.subscriber = null;
}
}
}
export class JigsawTableRendererModule {
}