@rdkmaster/jigsaw-labs
Version:
Jigsaw, the next generation component set for RDK
453 lines (410 loc) • 16.2 kB
text/typescript
import {TemplateRef, Type} from "@angular/core";
import {
BigTableData,
LocalPageableTableData,
PageableTableData, RawTableData,
TableData
} from "../../core/data/table-data";
import {SortAs, SortOrder} from "../../core/data/component-data";
import {TableCellRendererBase} from "./table-renderer";
import {CommonUtils} from "../../core/utils/common-utils";
export type TableColumnTarget = number | string | (number | string)[];
export type ColumnDefineGenerator = (field: string, index: number) => ColumnDefine;
export type TableCellDataGenerator = (tableData: TableData,
row: number,
column: number,
additionalData: AdditionalTableData) => any;
export class TableValueGenerators {
public static rowIndexGenerator(tableData: TableData, row: number): any {
let index = 1;
if (tableData instanceof BigTableData) {
index += (tableData.cache.startPage - 1) * tableData.pagingInfo.pageSize + tableData.viewport.verticalTo;
} else if (tableData instanceof PageableTableData || tableData instanceof LocalPageableTableData) {
index += (tableData.pagingInfo.currentPage - 1) * tableData.pagingInfo.pageSize;
}
index += row;
return index;
}
public static originCellDataGenerator(tableData: TableData, row: number, column: number): any {
return tableData && tableData.data && tableData.data[row] ? tableData.data[row][column] : '';
}
}
export class ColumnDefine {
target?: TableColumnTarget;
visible?: boolean;
width?: string | number;
header?: TableHeader;
cell?: TableCell;
group?: boolean;
}
export class AdditionalColumnDefine {
pos?: number;
visible?: boolean;
width?: string | number;
header?: TableHeader;
cell?: TableCell;
group?: boolean;
}
export class TableDataChangeEvent {
field: string | number;
row: number | number[];
column: number;
cellData: string | number;
oldCellData: string | number;
}
export type TableAsyncRenderer = () => TemplateRef<any>;
export type TableSyncRenderer = Type<TableCellRendererBase> | TemplateRef<any>;
export type TableRenderer = TableSyncRenderer | TableAsyncRenderer | string;
export class TableHeader {
text?: string;
renderer?: TableRenderer;
clazz?: string;
sortable?: boolean;
sortAs?: SortAs;
defaultSortOrder?: SortOrder;
}
export class TableCell {
renderer?: TableRenderer;
rendererInitData?: any;
clazz?: string;
editable?: boolean;
editorRenderer?: TableRenderer;
editorRendererInitData?: any;
data?: any | TableCellDataGenerator;
tooltip?: any;
innerHtmlContext?: any;
}
export class TableHeadSetting {
cellData: string | number;
width: string | number;
visible: boolean;
renderer: Type<TableCellRendererBase> | TemplateRef<any>;
clazz: string;
sortable: boolean;
sortAs: SortAs;
defaultSortOrder: SortOrder;
field: string;
}
export class TableCellSetting {
cellData: any;
width: string | number;
visible: boolean;
renderer: Type<TableCellRendererBase> | TemplateRef<any>;
rendererInitData: any;
clazz: string;
editable: boolean;
editorRenderer: Type<TableCellRendererBase> | TemplateRef<any>;
editorRendererInitData: any;
group: boolean;
field: string;
rowSpan: number;
tooltip: any;
innerHtmlContext: any;
}
export class SortChangeEvent {
sortAs: SortAs;
order: SortOrder;
field: string;
}
/**
* @internal
*/
export function _getColumnIndex(data: TableData, additionalData: TableData, field: string): [number, TableData] {
let index = data.field.indexOf(field);
if (index != -1) {
return [index, data];
}
index = additionalData ? additionalData.field.indexOf(field) : -1;
if (index != -1) {
return [index, additionalData];
}
return [-1, undefined];
}
/**
* 用户在界面上,通过渲染器“摸”了表格的附加列之后,表格会将用户当时“摸过”列的相关信息,
* 以此类型保存在内存中备用。这些数据是渲染器在判定哪些列被“摸过”时的关键数据,
* 应用也可以通过`AdditionalTableData`的各个api更新这些数据。
*
* $since = v1.1.4
*/
export class TouchedValue {
key: any | any[];
value: any;
data: any[];
}
export class AdditionalTableData extends TableData {
/**
* 这个属性的值与`JigsawTable.trackRowBy`的值是相等的,关于这个属性的作用,
* 请访问[这个链接]($demo=table/checkbox-column#open-desc=true)。
*/
public trackRowBy: string;
public originData: RawTableData;
private _touchedValues: { [field: string]: TouchedValue[] } = {};
private _trackRowByFields: number[];
private _fixTrackRowFields() {
if (this._trackRowByFields) {
return;
}
this._trackRowByFields = [];
const fields = this.trackRowBy ? this.trackRowBy.split(/\s*,\s*/g) : this.originData.field;
fields.forEach(field => {
const col = this.originData.field.findIndex(f => f === field);
if (col == -1) {
return;
}
this._trackRowByFields.push(col);
});
}
private _toFieldString(field: string | number): string {
return typeof field === 'string' ? field : this.field[field];
}
/**
* $deprecatedFrom = v1.1.4
* $replacement = clearTouchedValues()
*
* @deprecated
*/
public clearCachedValues(): void {
console.warn("`clearCachedValues()` is deprecated from v1.1.4, use `clearTouchedValues()` instead");
this.clearTouchedValues();
}
/**
* 清空所有数据
*
* $since = v1.1.4
*/
public clearTouchedValues(): void {
this._touchedValues = {};
}
public reset() {
this.data.splice(0, this.data.length);
this.clearTouchedValues();
}
private _getKeysByRow(field: string, row: number): any[] {
const keys = [];
if (!this.originData) {
console.warn('set originData and trackRowBy property of table before caching a value');
return keys;
}
this._fixTrackRowFields();
const excludedColumn = this.originData.field.findIndex(f => f === field);
this._trackRowByFields.forEach(col => {
if (col == excludedColumn || CommonUtils.isUndefined(this.originData.data[row])) {
return;
}
keys.push(this.originData.data[row][col]);
});
return keys;
}
/**
* $deprecatedFrom = v1.1.4
* $replacement = touchValueByRow()
*
* @deprecated
*
* @param {string | number} field
* @param {number} row
* @param value
*/
public cacheValue(field: string | number, row: number, value: any): void {
console.warn("`cacheValue()` is deprecated from v1.1.4, use `touchValueByRow()` instead");
this.touchValueByRow(field, row, value);
}
/**
* 更新用户“摸过”的单元格的值。
*
* $since = v1.1.4
*
* @param {string | number} field 需要更新的列名或者列索引
* @param {number} row 当前页行索引,只支持更新表格的当前页数据,如果需要更新其他页的数据,请使用`touchValue()`方法
* @param value 将此变量替代缓存里的数据,如果缓存里不存在对应的数据,则会新建一个对应的数据
*/
public touchValueByRow(field: string | number, row: number, value: any): void {
const fieldString = this._toFieldString(field);
const keys = this._getKeysByRow(fieldString, row);
this.touchValue(field, keys, value, this.originData.data[row]);
}
/**
* 更新用户“摸过”的单元格的值。如果需要更新的是当前页,则通过`touchValueByRow()`方法更新会更方便些。
*
* $since = v1.1.4
*
* @param {string | number} field 需要更新的列名或者列索引
* @param {any | any[]} key 通过此值来确定更新缓存中的哪一行或者哪些行的数据,
* 一般需要配合[`trackRowBy`属性]($demo=table/checkbox-column#open-desc=true)一起使用。
* @param value 将此变量替代缓存里的数据,如果缓存里不存在对应的数据,则会新建一个对应的数据
* @param {any[]} data 可选,表格内置渲染器会自动将该行对应的数据存放在这个属性上,
* 方便应用在需要时使用该行的其他数据。
*/
public touchValue(field: string | number, key: any | any[], value: any, data?: any[]): void {
const fieldString = this._toFieldString(field);
if (!fieldString || CommonUtils.isUndefined(key)) {
return;
}
if (!this._touchedValues[fieldString]) {
this._touchedValues[fieldString] = [];
}
const touchedValues = this._touchedValues[fieldString];
const touchedValue = touchedValues.find(item => this._isKeyEquals(item.key, key));
if (touchedValue) {
touchedValue.value = value;
touchedValue.data = data ? data : touchedValue.data;
} else {
touchedValues.push({
// 长度只有1的时候,使用字符串简化应用对这个值的使用,毕竟单一key的情况应该是绝大多数
key: key,
value: value,
data: data
});
}
}
/**
* 获取用户“摸过”的单元格的数据
* - 如果需要获取缓存的详细信息,请使用`getTouchedInfoByRow()`
* - 如果需要获取其他页的信息,请使用`getTouchedValue()`或者`getTouchedInfo()`
*
* $since = v1.1.4
*
* @param {string | number} field 需要获取的列名或者列索引
* @param {number} row 当前页行索引,支持获取表格的当前页数据,如果需要获取其他页的数据,请使用`getTouchedValue()`方法
* @return {any} 用户“摸过”之后改单元格的新值。
*/
public getTouchedValueByRow(field: string | number, row: number): any {
const v = this.getTouchedInfoByRow(field, row);
return v ? v.value : undefined;
}
/**
* 获取用户“摸过”的单元格的详细信息,可以直接更新得到的值,调用`JigsawTable.update()`方法可以刷新界面。
*
* 只能返回当前页的数据,如果需要获取其他页的数据,请使用`getTouchedInfo()`。
*
* $since = v1.1.4
*
* @param {string | number} field 需要获取的列名或者列索引
* @param {number} row 当前页行索引,支持获取表格的当前页数据,如果需要获取其他页的数据,请使用`getTouchedValue()`方法
* @return {TouchedValue} 用户“摸过”之后该单元格对应缓存数据对象。
*/
public getTouchedInfoByRow(field: string | number, row: number): TouchedValue {
const fieldString = this._toFieldString(field);
const keys = this._getKeysByRow(fieldString, row);
return this.getTouchedInfo(field, keys);
}
/**
* 获取用户“摸过”的单元格的值,可获取任意页的缓存数据。
*
* $since = v1.1.4
*
* @param {string | number} field 需要获取的列名或者列索引
* @param {any | any[]} key
* @return {any}
*/
public getTouchedValue(field: string | number, key: any | any[]): any;
/**
* @deprecated
*
* @param {string | number} field
* @param {number} row 按行索引取值的重载**已经被废弃**,请使用`getTouchedValueByRow()`替代。
* @returns {any}
*/
public getTouchedValue(field: string | number, row: number): any;
/**
* @internal
*/
public getTouchedValue(field: string | number, key: number | any | any[]): any {
let v;
if (typeof key === 'number') {
console.warn("`getTouchedValue()` is deprecated from v1.1.4, use `getTouchedValueByRow()` instead");
v = this.getTouchedValueByRow(field, key);
} else {
v = this.getTouchedInfo(field, key);
}
return v ? v.value : undefined;
}
/**
* 获取用户“摸过”的单元格的详细信息,可以直接更新得到的值,调用`JigsawTable.update()`方法可以刷新界面。
*
* 通过此方法可获取任意页的缓存数据。
*
* $since = v1.1.4
*
* @param {string | number} field 需要获取的列名或者列索引
* @param {any | any[]} key 通过此值来确定更新缓存中的哪一行或者哪些行的数据,
* 一般需要配合[`trackRowBy`属性]($demo=table/checkbox-column#open-desc=true)一起使用。
* @return {TouchedValue}
*/
public getTouchedInfo(field: string | number, key: any | any[]): TouchedValue {
const fieldString = this._toFieldString(field);
if (!fieldString || CommonUtils.isUndefined(key)) {
return;
}
if (!this._touchedValues[fieldString]) {
return;
}
const touchedValues = this._touchedValues[fieldString];
return touchedValues.find(item => this._isKeyEquals(item.key, key));
}
/**
* 当表格单元格的值是对象而非简单类型的时候,对key的比较需要采用严格模式
*
* $since = v1.1.4
*
* @param key1
* @param key2
* @return {boolean}
* @private
*/
private _isKeyEquals(key1: any, key2: any): boolean {
if (key1 instanceof Array && key2 instanceof Array) {
if (key1.length != key2.length || key1.length == 0) {
return false;
}
// 注意,对数组的元素的比较,不再区分类型,一律采用值比较
return !key1.some((item, index) => (this._getObjectValue(item) !== this._getObjectValue(key2[index])));
} else {
return this._getObjectValue(key1) === this._getObjectValue(key2);
}
}
private _getObjectValue(obj: any): any {
if (typeof obj !== 'object') {
return obj;
}
if (obj && obj.hasOwnProperty('toString')) {
return obj.toString();
}
if (obj && obj.hasOwnProperty('valueOf')) {
return obj.valueOf();
}
return obj;
}
/**
* $deprecatedFrom = v1.1.4
* $replacement = getAllTouched()
*
* @deprecated
*
* @param {string | number} field
* @returns {TouchedValue[]}
*/
public getTouchedValues(field: string | number): TouchedValue[] {
console.warn("`getTouchedValues()` is deprecated from v1.1.4, use `getAllTouched()` instead");
return this.getAllTouched(field);
}
/**
* 获取用户在表格上“摸过”的所有行的详细信息,对这些信息做修改后调用`JigsawTable.update()`方法可以刷新界面
*
* $since = v1.1.4
*
* @param {string | number} field 需要获取的列名或者列索引
* @returns {TouchedValue[]} 返回所有行的详细信息
*/
public getAllTouched(field: string | number): TouchedValue[] {
const fieldString = this._toFieldString(field);
if (!fieldString) {
return [];
}
if (!this._touchedValues[fieldString]) {
this._touchedValues[fieldString] = [];
}
return this._touchedValues[fieldString];
}
}