Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(abc:st): add onCell, support colSpan and rowSpan merging #1613

Merged
merged 3 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions packages/abc/st/demo/colspan-rowspan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
order: 8
title:
en-US: colSpan and rowSpan
zh-CN: 表格行/列合并
---

## zh-CN

表格支持行/列合并,使用 `onCell` 进行设置,若返回 `colSpan` 或者 `rowSpan` 设值为 0 时,设置的表格不会渲染。

## en-US

The table supports row/column merging, use `onCell` to set, if return `colSpan` or `rowSpan` is set to 0, the set table will not be rendered.

```ts
import { Component } from '@angular/core';

import { STColumn, STData, STOnCellResult } from '@delon/abc/st';

// In the fifth row, other columns are merged into first column
// by setting it's colSpan to be 0
const sharedOnCell = (_: STData, index: number): STOnCellResult => {
if (index === 1) {
return { colSpan: 0 };
}

return {};
};

@Component({
selector: 'app-demo',
template: ` <st #st [data]="url" [ps]="5" [req]="{ params: params }" [columns]="columns" bordered size="middle">
</st>`
})
export class DemoComponent {
url = `/users?total=2&field=list`;
params = { a: 1, b: 2 };

columns: STColumn[] = [
{ title: '编号', index: 'id', sort: true, width: 100 },
{ title: '头像', type: 'img', index: 'picture.thumbnail', width: 60 },
{
title: '邮箱',
index: 'email',
onCell: (_, index) => ({
colSpan: index === 1 ? 5 : 1
})
},
{
title: 'first',
index: 'name.first',
sort: true,
className: 'text-center',
onCell: (_, index) => {
if (index === 3) {
return { rowSpan: 2 };
}
// These two are merged into above cell
if (index === 4) {
return { rowSpan: 0 };
}
if (index === 1) {
return { colSpan: 0 };
}

return {};
}
},
{ title: 'last', index: 'name.last', onCell: sharedOnCell }
];
}
```
1 change: 1 addition & 0 deletions packages/abc/st/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ class TestComponent {
| `[format]` | Format value of this column | `(item: STData, col: STColumn, index: number) => string` | - |
| `[className]` | Class name of this column, e.g: `text-center`, `text-right`, `text-error`, pls refer to [Style Tools](/theme/tools) | `string` | - |
| `[colSpan]` | Span of this column's title | `number` | - |
| `[onCell]` | Set props on per cell | `(item: T, index: number) => STOnCellResult;` | - |
| `[sort]` | Sort config of this column, Remote Data Configuration**Priority** Rule: <br>`true` allow sorting, should be auto generate compose `compare: (a, b) => a[index] - b[index]` method when data is local<br>`string` corresponding `key` value | `true,string,STColumnSort` | - |
| `[filter]` | Filter config of this column | `STColumnFilter` | - |
| `[selections]` | Config of type is checkbox | `STColumnSelection[]` | - |
Expand Down
1 change: 1 addition & 0 deletions packages/abc/st/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ class TestComponent {
| `[format]` | 格式化列值 | `(item: STData, col: STColumn, index: number) => string` | - |
| `[className]` | 列 `class` 属性值,例如:`text-center` 居中; `text-right` 居右; `text-error` 异常色,更多参考[样式工具类](/theme/tools) | `string` | - |
| `[colSpan]` | 合并列 | `number` | - |
| `[onCell]` | 设置单元格属性 | `(item: T, index: number) => STOnCellResult;` | - |
| `[sort]` | 排序配置项,远程数据配置**优先**规则:<br>`true` 表示允许排序,且若数据源为本地数据时会自动生成 `compare: (a, b) => a[index] - b[index]` 方法<br>`string` 表示远程数据排序相对应 `key` 值 | `true,string,STColumnSort` | - |
| `[filter]` | 过滤配置项 | `STColumnFilter` | - |
| `[selections]` | 选择功能配置 | `STColumnSelection[]` | - |
Expand Down
17 changes: 15 additions & 2 deletions packages/abc/st/st-data-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
STData,
STMultiSort,
STMultiSortResultType,
STOnCellResult,
STPage,
STReq,
STReqReNameType,
Expand Down Expand Up @@ -324,15 +325,27 @@ export class STDataSource {
return this.http.request(method, url, reqOptions);
}

getCell(c: STColumn, item: STData, idx: number): STOnCellResult {
const onCellResult = typeof c.onCell === 'function' ? c.onCell(item, idx) : null;
const mergedColSpan = onCellResult?.colSpan ?? 1;
const mergedRowSpan = onCellResult?.rowSpan ?? 1;
return {
colSpan: mergedColSpan <= 0 ? null : mergedColSpan,
rowSpan: mergedRowSpan <= 0 ? null : mergedRowSpan
} as STOnCellResult;
}

optimizeData(options: { columns: _STColumn[]; result: STData[]; rowClassName?: STRowClassName | null }): STData[] {
const { result, columns, rowClassName } = options;
for (let i = 0, len = result.length; i < len; i++) {
result[i]._values = columns.map(c => {
const props = this.getCell(c, result[i], i);

if (Array.isArray(c.buttons) && c.buttons.length > 0) {
return { buttons: this.genButtons(c.buttons, result[i], c), _text: '' };
return { buttons: this.genButtons(c.buttons, result[i], c), _text: '', props };
}

return this.get(result[i], c, i);
return { ...this.get(result[i], c, i), props };
});
result[i]._rowClassName = [rowClassName ? rowClassName(result[i], i) : null, result[i].className]
.filter(w => !!w)
Expand Down
32 changes: 19 additions & 13 deletions packages/abc/st/st.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -167,19 +167,25 @@
(click)="_stopPropagation($event)"
nzWidth="50px"
></td>
<td
*ngFor="let c of _columns; let cIdx = index"
[nzLeft]="!!c._left"
[nzRight]="!!c._right"
[attr.data-col-index]="cIdx"
[ngClass]="c._className"
[attr.colspan]="c.colSpan"
>
<span *ngIf="responsive" class="ant-table-rep__title">
<ng-template [ngTemplateOutlet]="titleTpl" [ngTemplateOutletContext]="{ $implicit: c.title }"></ng-template>
</span>
<st-td [data]="_data" [i]="i" [index]="index" [c]="c" [cIdx]="cIdx" (n)="_handleTd($event)"></st-td>
</td>
<ng-container *ngFor="let c of _columns; let cIdx = index">
<td
*ngIf="i._values[cIdx].props?.colSpan > 0 && i._values[cIdx].props?.rowSpan > 0"
[nzLeft]="!!c._left"
[nzRight]="!!c._right"
[attr.data-col-index]="cIdx"
[ngClass]="c._className"
[attr.colspan]="i._values[cIdx].props?.colSpan === 1 ? null : i._values[cIdx].props?.colSpan"
[attr.rowspan]="i._values[cIdx].props?.rowSpan === 1 ? null : i._values[cIdx].props?.rowSpan"
>
<span *ngIf="responsive" class="ant-table-rep__title">
<ng-template
[ngTemplateOutlet]="titleTpl"
[ngTemplateOutletContext]="{ $implicit: c.title }"
></ng-template>
</span>
<st-td [data]="_data" [i]="i" [index]="index" [c]="c" [cIdx]="cIdx" (n)="_handleTd($event)"></st-td>
</td>
</ng-container>
</tr>
<tr [nzExpand]="i.expand">
<ng-template
Expand Down
21 changes: 14 additions & 7 deletions packages/abc/st/st.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,14 +559,21 @@ export class STComponent implements AfterViewInit, OnChanges, OnDestroy {
}

private _refColAndData(): this {
this._columns
.filter(w => w.type === 'no')
.forEach(c =>
this._data.forEach((i, idx) => {
this._columns.forEach(c => {
this._data.forEach((i, idx) => {
const values = i._values as _STDataValue[];
if (c.type === 'no') {
const text = `${this.dataSource.getNoIndex(i, c, idx)}`;
i._values![c.__point!] = { text, _text: text, org: idx, safeType: 'text' } as _STDataValue;
})
);
values[c.__point!] = {
text,
_text: text,
org: idx,
safeType: 'text'
} as _STDataValue;
}
values[c.__point!].props = this.dataSource.getCell(c, i, idx);
});
});

return this.refreshData();
}
Expand Down
13 changes: 10 additions & 3 deletions packages/abc/st/st.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,11 @@ export interface STColumn<T extends STData = any> {
*/
className?: NgClassType;
/**
* 合并列
* Table cell supports `colSpan` and `rowSpan`. When each of them is set to 0, the cell will not be rendered.
*
* 表格支持行/列合并,若返回的 `colSpan` 或者 `rowSpan` 设值为 0 时表示不会渲染
*/
colSpan?: number;
onCell?: (item: T, index: number) => STOnCellResult;
/**
* 数字格式,`type=number` 有效
*/
Expand Down Expand Up @@ -432,7 +434,7 @@ export interface STColumn<T extends STData = any> {
* 分组表头
*/
children?: Array<STColumn<T>>;

colSpan?: number;
rowSpan?: number;

/**
Expand Down Expand Up @@ -1280,3 +1282,8 @@ export interface STCustomRequestOptions {
url: string;
options: STRequestOptions;
}

export interface STOnCellResult {
rowSpan?: number | null;
colSpan?: number | null;
}
3 changes: 2 additions & 1 deletion packages/abc/st/st.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { TemplateRef } from '@angular/core';
import { SafeHtml } from '@angular/platform-browser';

import { STColumn, STColumnButton, STColumnSafeType, STData, STSortMap } from './st.interfaces';
import { STColumn, STColumnButton, STColumnSafeType, STData, STOnCellResult, STSortMap } from './st.interfaces';

/**
* @inner
Expand Down Expand Up @@ -74,4 +74,5 @@ export interface _STDataValue {
color?: string;
safeType: STColumnSafeType;
buttons?: _STColumnButton[];
props?: STOnCellResult | null;
}
38 changes: 37 additions & 1 deletion packages/abc/st/test/st-data-source.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { STDataSource, STDataSourceOptions } from '../st-data-source';
import { ST_DEFAULT_CONFIG } from '../st.config';
import { STColumnButton, STColumnFilterMenu, STData } from '../st.interfaces';
import { _STColumn } from '../st.types';
import { _STColumn, _STDataValue } from '../st.types';

const DEFAULT = {
pi: 1,
Expand Down Expand Up @@ -908,6 +908,42 @@ describe('abc: table: data-souce', () => {
done();
});
});
describe('#onCell', () => {
it('should be working', done => {
const index = 1;
options.data = genData();
options.columns = [
{ index: 'a', onCell: (_, idx) => ({ colSpan: idx === index ? 2 : 1 }) },
{ index: 'b', onCell: (_, idx) => ({ rowSpan: idx === index ? 2 : 1 }) }
] as _STColumn[];
srv.process(options).subscribe(res => {
const values = res.list[index]._values as _STDataValue[];
expect(values[0].props?.colSpan).toBe(2);
expect(values[0].props?.rowSpan).toBe(1);

expect(values[1].props?.colSpan).toBe(1);
expect(values[1].props?.rowSpan).toBe(2);
done();
});
});
it('should be ignore when set 0', done => {
const index = 1;
options.data = genData();
options.columns = [
{ index: 'a', onCell: (_, idx) => ({ colSpan: idx === index ? 0 : 1 }) },
{ index: 'b', onCell: (_, idx) => ({ rowSpan: idx === index ? 0 : 1 }) }
] as _STColumn[];
srv.process(options).subscribe(res => {
const values = res.list[index]._values as _STDataValue[];
expect(values[0].props?.colSpan).toBeNull();
expect(values[0].props?.rowSpan).toBe(1);

expect(values[1].props?.colSpan).toBe(1);
expect(values[1].props?.rowSpan).toBeNull();
done();
});
});
});
});

describe('[buttons]', () => {
Expand Down