feat: add per-column filtering and sticky header support to DeesTable component
This commit is contained in:
@@ -3,7 +3,6 @@ import { demoFunc } from './dees-table.demo.js';
|
||||
import { customElement, html, DeesElement, property, type TemplateResult, directives } from '@design.estate/dees-element';
|
||||
|
||||
import { DeesContextmenu } from '../dees-contextmenu.js';
|
||||
import * as plugins from '../00plugins.js';
|
||||
import * as domtools from '@design.estate/dees-domtools';
|
||||
import { type TIconKey } from '../dees-icon.js';
|
||||
import { tableStyles } from './styles.js';
|
||||
@@ -167,6 +166,13 @@ export class DeesTable<T> extends DeesElement {
|
||||
// simple client-side filtering (Phase 1)
|
||||
@property({ type: String })
|
||||
public filterText: string = '';
|
||||
// per-column quick filters
|
||||
@property({ attribute: false })
|
||||
public columnFilters: Record<string, string> = {};
|
||||
@property({ type: Boolean, attribute: 'show-column-filters' })
|
||||
public showColumnFilters: boolean = false;
|
||||
@property({ type: Boolean, reflect: true, attribute: 'sticky-header' })
|
||||
public stickyHeader: boolean = false;
|
||||
|
||||
// selection (Phase 1)
|
||||
@property({ type: String })
|
||||
@@ -254,6 +260,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
<style></style>
|
||||
${this.data.length > 0
|
||||
? html`
|
||||
<div class="tableScroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -261,12 +268,15 @@ export class DeesTable<T> extends DeesElement {
|
||||
? html`
|
||||
<th style="width:42px; text-align:center;">
|
||||
${this.selectionMode === 'multi'
|
||||
? html`<input type="checkbox"
|
||||
.checked=${this.areAllSelected()}
|
||||
@click=${(e: Event) => {
|
||||
e.stopPropagation();
|
||||
this.toggleSelectAll();
|
||||
}} />`
|
||||
? html`
|
||||
<dees-input-checkbox
|
||||
.value=${this.areAllSelected()}
|
||||
@newValue=${(e: CustomEvent<boolean>) => {
|
||||
e.stopPropagation();
|
||||
this.setSelectAll(e.detail === true);
|
||||
}}
|
||||
></dees-input-checkbox>
|
||||
`
|
||||
: html``}
|
||||
</th>
|
||||
`
|
||||
@@ -293,9 +303,31 @@ export class DeesTable<T> extends DeesElement {
|
||||
}
|
||||
})()}
|
||||
</tr>
|
||||
${this.showColumnFilters
|
||||
? html`<tr class="filtersRow">
|
||||
${this.selectionMode !== 'none'
|
||||
? html`<th style="width:42px;"></th>`
|
||||
: html``}
|
||||
${effectiveColumns
|
||||
.filter((c) => !c.hidden)
|
||||
.map((col) => {
|
||||
const key = String(col.key);
|
||||
if (col.filterable === false) return html`<th></th>`;
|
||||
return html`<th>
|
||||
<input type="text" placeholder="Filter..." .value=${this.columnFilters[key] || ''}
|
||||
@input=${(e: Event) => this.setColumnFilter(key, (e.target as HTMLInputElement).value)} />
|
||||
</th>`;
|
||||
})}
|
||||
${(() => {
|
||||
if (this.dataActions && this.dataActions.length > 0) {
|
||||
return html` <th></th> `;
|
||||
}
|
||||
})()}
|
||||
</tr>`
|
||||
: html``}
|
||||
</thead>
|
||||
<tbody>
|
||||
${getViewDataFn(this.data, effectiveColumns, this.sortKey, this.sortDir, this.filterText).map((itemArg, rowIndex) => {
|
||||
${getViewDataFn(this.data, effectiveColumns, this.sortKey, this.sortDir, this.filterText, this.columnFilters).map((itemArg, rowIndex) => {
|
||||
const getTr = (elementArg: HTMLElement): HTMLElement => {
|
||||
if (elementArg.tagName === 'TR') {
|
||||
return elementArg;
|
||||
@@ -370,14 +402,13 @@ export class DeesTable<T> extends DeesElement {
|
||||
>
|
||||
${this.selectionMode !== 'none'
|
||||
? html`<td style="width:42px; text-align:center;">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${this.isRowSelected(itemArg)}
|
||||
@click=${(e: Event) => {
|
||||
<dees-input-checkbox
|
||||
.value=${this.isRowSelected(itemArg)}
|
||||
@newValue=${(e: CustomEvent<boolean>) => {
|
||||
e.stopPropagation();
|
||||
this.toggleRowSelected(itemArg);
|
||||
this.setRowSelected(itemArg, e.detail === true);
|
||||
}}
|
||||
/>
|
||||
></dees-input-checkbox>
|
||||
</td>`
|
||||
: html``}
|
||||
${effectiveColumns
|
||||
@@ -435,6 +466,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`
|
||||
: html` <div class="noDataSet">No data set!</div> `}
|
||||
<div class="footer">
|
||||
@@ -583,7 +615,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
if (prev !== this.filterText) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('filterChange', {
|
||||
detail: { text: this.filterText },
|
||||
detail: { text: this.filterText, columns: { ...this.columnFilters } },
|
||||
bubbles: true,
|
||||
})
|
||||
);
|
||||
@@ -591,6 +623,17 @@ export class DeesTable<T> extends DeesElement {
|
||||
}
|
||||
}
|
||||
|
||||
public setColumnFilter(key: string, value: string) {
|
||||
this.columnFilters = { ...this.columnFilters, [key]: value };
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('filterChange', {
|
||||
detail: { text: this.filterText, columns: { ...this.columnFilters } },
|
||||
bubbles: true,
|
||||
})
|
||||
);
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
// selection helpers
|
||||
private getRowId(row: T): string {
|
||||
if (this.rowKey) {
|
||||
@@ -621,6 +664,19 @@ export class DeesTable<T> extends DeesElement {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private setRowSelected(row: T, checked: boolean) {
|
||||
const id = this.getRowId(row);
|
||||
if (this.selectionMode === 'single') {
|
||||
this.selectedIds.clear();
|
||||
if (checked) this.selectedIds.add(id);
|
||||
} else if (this.selectionMode === 'multi') {
|
||||
if (checked) this.selectedIds.add(id);
|
||||
else this.selectedIds.delete(id);
|
||||
}
|
||||
this.emitSelectionChange();
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private areAllSelected(): boolean {
|
||||
return this.data.length > 0 && this.selectedIds.size === this.data.length;
|
||||
}
|
||||
@@ -635,6 +691,16 @@ export class DeesTable<T> extends DeesElement {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private setSelectAll(checked: boolean) {
|
||||
if (checked) {
|
||||
this.selectedIds = new Set(this.data.map((r) => this.getRowId(r)));
|
||||
} else {
|
||||
this.selectedIds.clear();
|
||||
}
|
||||
this.emitSelectionChange();
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private emitSelectionChange() {
|
||||
const selectedIds = Array.from(this.selectedIds);
|
||||
const selectedRows = this.data.filter((r) => this.selectedIds.has(this.getRowId(r)));
|
||||
|
Reference in New Issue
Block a user