diff --git a/packages/abc/st/demo/row-click.md b/packages/abc/st/demo/row-click.md index 882af536a..adc8fe9f2 100644 --- a/packages/abc/st/demo/row-click.md +++ b/packages/abc/st/demo/row-click.md @@ -19,15 +19,23 @@ Use `(change)` to implement click line callback, because DOM events can't distin ```ts import { Component } from '@angular/core'; -import { STChange, STColumn } from '@delon/abc/st'; + +import { STChange, STClickRowClassNameType, STColumn } from '@delon/abc/st'; @Component({ selector: 'app-demo', - template: ` `, + template: ` ` }) export class DemoComponent { url = `/users?results=3`; params = { a: 1, b: 2 }; + clickRowClassName: STClickRowClassNameType = { exclusive: true, fn: () => 'text-error' }; columns: STColumn[] = [ { title: '编号', index: 'id' }, { title: '邮箱', index: 'email' }, @@ -38,10 +46,10 @@ export class DemoComponent { { text: 'btn', type: 'link', - click: e => console.log('btn click', e), - }, - ], - }, + click: e => console.log('btn click', e) + } + ] + } ]; _click(e: STChange): void { diff --git a/packages/abc/st/index.en-US.md b/packages/abc/st/index.en-US.md index 488619480..791ee5cae 100644 --- a/packages/abc/st/index.en-US.md +++ b/packages/abc/st/index.en-US.md @@ -62,6 +62,7 @@ When an exception is thrown when parsing column data, *INVALID DATA* will be for | `[size]` | Size of table | `'small','middle','default'` | `'default'` | ✅ | | `[widthMode]` | Set the table width mode | `STWidthMode` | - | ✅ | | `[rowClassName]` | Row class name of table | `(record: STData, index: number) => string` | - | ✅ | +| `[clickRowClassName]` | Row class name of click the row | `string, STClickRowClassNameType` | - | ✅ | | `[loading]` | Loading status of table, when specifying `null` is controlled by st | `boolean | null` | `null` | - | | `[loadingIndicator]` | The spinning indicator | `TemplateRef` | - | ✅ | | `[loadingDelay]` | Specifies a delay in milliseconds for loading state (prevent flush) | `number` | `0` | ✅ | diff --git a/packages/abc/st/index.zh-CN.md b/packages/abc/st/index.zh-CN.md index b5df28bd2..9b0045518 100644 --- a/packages/abc/st/index.zh-CN.md +++ b/packages/abc/st/index.zh-CN.md @@ -62,6 +62,7 @@ module: import { STModule } from '@delon/abc/st'; | `[size]` | table大小 | `'small','middle','default'` | `'default'` | ✅ | | `[widthMode]` | 设置表格宽度模式 | `STWidthMode` | - | ✅ | | `[rowClassName]` | 表格行的类名 | `(record: STData, index: number) => string` | - | ✅ | +| `[clickRowClassName]` | 点击行切换类名 | `string, STClickRowClassNameType` | - | ✅ | | `[loading]` | 页面是否加载中,当指定 `null` 由 st 受控 | `boolean | null` | `null` | - | | `[loadingIndicator]` | 加载指示符 | `TemplateRef` | - | ✅ | | `[loadingDelay]` | 延迟显示加载效果的时间(防止闪烁) | `number` | `0` | ✅ | diff --git a/packages/abc/st/st.component.ts b/packages/abc/st/st.component.ts index 8b342b7ba..6981f46c4 100644 --- a/packages/abc/st/st.component.ts +++ b/packages/abc/st/st.component.ts @@ -50,6 +50,8 @@ import { ST_DEFAULT_CONFIG } from './st.config'; import { STChange, STChangeType, + STClickRowClassName, + STClickRowClassNameType, STColumn, STColumnButton, STColumnFilterMenu, @@ -191,6 +193,7 @@ export class STComponent implements AfterViewInit, OnChanges, OnDestroy { }; } @Input() rowClassName: STRowClassName; + @Input() clickRowClassName?: STClickRowClassName | null; @Input() set widthMode(value: STWidthMode) { this._widthMode = { ...this.cog.widthMode, ...value }; @@ -512,7 +515,8 @@ export class STComponent implements AfterViewInit, OnChanges, OnDestroy { this._data.filter(i => i !== item).forEach(i => (i.expand = false)); } _rowClick(e: Event, item: STData, index: number): void { - if ((e.target as HTMLElement).nodeName === 'INPUT') return; + const el = e.target as HTMLElement; + if (el.nodeName === 'INPUT') return; const { expand, expandRowByClick, rowClickTime } = this; if (!!expand && item.showExpand !== false && expandRowByClick) { item.expand = !item.expand; @@ -525,6 +529,7 @@ export class STComponent implements AfterViewInit, OnChanges, OnDestroy { setTimeout(() => { const data = { e, item, index }; if (this.rowClickCount === 1) { + this._clickRowClassName(el, item, index); this.changeEmit('click', data); } else { this.changeEmit('dblClick', data); @@ -533,6 +538,25 @@ export class STComponent implements AfterViewInit, OnChanges, OnDestroy { }, rowClickTime); } + private _clickRowClassName(el: HTMLElement, item: STData, index: number): void { + const cr = this.clickRowClassName; + if (cr == null) return; + const config = { + exclusive: false, + ...(typeof cr === 'string' ? { fn: () => cr } : cr) + } as STClickRowClassNameType; + const className = config.fn(item, index); + const trEl = el.closest('tr') as HTMLElement; + if (config.exclusive) { + trEl.parentElement!!.querySelectorAll('tr').forEach((a: HTMLElement) => a.classList.remove(className)); + } + if (trEl.classList.contains(className)) { + trEl.classList.remove(className); + } else { + trEl.classList.add(className); + } + } + _expandChange(item: STData, expand: boolean): void { item.expand = expand; this.closeOtherExpand(item); diff --git a/packages/abc/st/st.interfaces.ts b/packages/abc/st/st.interfaces.ts index 3e2c84e4d..d0d676868 100644 --- a/packages/abc/st/st.interfaces.ts +++ b/packages/abc/st/st.interfaces.ts @@ -1080,6 +1080,16 @@ export interface STError { } export type STRowClassName = (record: T, index: number) => string; +export type STClickRowClassName = string | STClickRowClassNameType; +export interface STClickRowClassNameType { + fn: (record: T, index: number) => string; + /** + * Whether mutually exclusive, default: `false` + * + * 是否互斥,默认:`false` + */ + exclusive?: boolean; +} export interface STColumnGroupType { column: STColumn; diff --git a/packages/abc/st/test/st.spec.ts b/packages/abc/st/test/st.spec.ts index 0fe6d5e9e..19c997874 100644 --- a/packages/abc/st/test/st.spec.ts +++ b/packages/abc/st/test/st.spec.ts @@ -35,6 +35,8 @@ import { STComponent } from '../st.component'; import { STChange, STChangeType, + STClickRowClassName, + STClickRowClassNameType, STColumn, STColumnBadge, STColumnFilter, @@ -1371,6 +1373,59 @@ describe('abc: st', () => { el.click(); page.cd().expectChangeType('click', false); })); + describe('clickRowClassName', () => { + it('should be null', fakeAsync(() => { + context.clickRowClassName = null; + page.cd(); + const trEl = (page.getCell() as HTMLElement).closest('tr') as HTMLElement; + const oldClassName = trEl.classList.value; + trEl.click(); + page.cd(100); + expect(trEl.classList.value).toBe(oldClassName); + })); + it('should be string', fakeAsync(() => { + context.clickRowClassName = 'aa'; + page.cd(); + const trEl = (page.getCell() as HTMLElement).closest('tr') as HTMLElement; + expect(trEl.classList).not.toContain('aa'); + trEl.click(); + page.cd(100); + expect(trEl.classList).toContain('aa'); + trEl.click(); + page.cd(100); + expect(trEl.classList).not.toContain('aa'); + })); + it('should be exclusive with false', fakeAsync(() => { + context.clickRowClassName = { exclusive: false, fn: () => 'bb' } as STClickRowClassNameType; + page.cd(); + [1, 2].forEach(idx => { + const trEl = (page.getCell(idx) as HTMLElement).closest('tr') as HTMLElement; + expect(trEl.classList).not.toContain('bb'); + trEl.click(); + page.cd(100); + expect(trEl.classList).toContain('bb'); + }); + const len = ((page.getCell() as HTMLElement).closest('tbody') as HTMLElement).querySelectorAll( + 'tr.bb' + ).length; + expect(len).toBe(2); + })); + it('should be exclusive with true', fakeAsync(() => { + context.clickRowClassName = { exclusive: true, fn: () => 'bb' } as STClickRowClassNameType; + page.cd(); + [1, 2].forEach(idx => { + const trEl = (page.getCell(idx) as HTMLElement).closest('tr') as HTMLElement; + expect(trEl.classList).not.toContain('bb'); + trEl.click(); + page.cd(100); + expect(trEl.classList).toContain('bb'); + }); + const len = ((page.getCell() as HTMLElement).closest('tbody') as HTMLElement).querySelectorAll( + 'tr.bb' + ).length; + expect(len).toBe(1); + })); + }); }); describe('[public method]', () => { describe('#load', () => { @@ -2178,6 +2233,7 @@ describe('abc: st', () => { [noResult]="noResult" [widthConfig]="widthConfig" [rowClickTime]="rowClickTime" + [clickRowClassName]="clickRowClassName" [showHeader]="showHeader" [contextmenu]="contextmenu" [customRequest]="customRequest" @@ -2207,6 +2263,7 @@ class TestComponent { noResult = 'noResult'; widthConfig: string[] = []; rowClickTime = 200; + clickRowClassName?: STClickRowClassName | null = 'text-error'; responsive = false; responsiveHideHeaderFooter = false; expandRowByClick = false;