feat: enhance DeesTable with server-side search and Lucene filtering capabilities
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
getCellValue as getCellValueFn,
|
||||
getViewData as getViewDataFn,
|
||||
} from './data.js';
|
||||
import { compileLucenePredicate } from './lucene.js';
|
||||
|
||||
export type { Column, ITableAction, ITableActionDataArg, TDisplayFunction } from './types.js';
|
||||
|
||||
@@ -173,6 +174,12 @@ export class DeesTable<T> extends DeesElement {
|
||||
public showColumnFilters: boolean = false;
|
||||
@property({ type: Boolean, reflect: true, attribute: 'sticky-header' })
|
||||
public stickyHeader: boolean = false;
|
||||
|
||||
// search row state
|
||||
@property({ type: String })
|
||||
public searchMode: 'table' | 'data' | 'server' = 'table';
|
||||
private __searchTextSub?: { unsubscribe?: () => void };
|
||||
private __searchModeSub?: { unsubscribe?: () => void };
|
||||
|
||||
// selection (Phase 1)
|
||||
@property({ type: String })
|
||||
@@ -194,6 +201,23 @@ export class DeesTable<T> extends DeesElement {
|
||||
? computeEffectiveColumnsFn(this.columns, this.augmentFromDisplayFunction, this.displayFunction, this.data)
|
||||
: computeColumnsFromDisplayFunctionFn(this.displayFunction, this.data);
|
||||
|
||||
const lucenePred = compileLucenePredicate<T>(
|
||||
this.filterText,
|
||||
this.searchMode === 'data' ? 'data' : 'table',
|
||||
effectiveColumns
|
||||
);
|
||||
|
||||
const viewData = getViewDataFn(
|
||||
this.data,
|
||||
effectiveColumns,
|
||||
this.sortKey,
|
||||
this.sortDir,
|
||||
this.filterText,
|
||||
this.columnFilters,
|
||||
this.searchMode === 'data' ? 'data' : 'table',
|
||||
lucenePred || undefined
|
||||
);
|
||||
(this as any)._lastViewData = viewData;
|
||||
return html`
|
||||
<div class="mainbox">
|
||||
<!-- the heading part -->
|
||||
@@ -270,10 +294,11 @@ export class DeesTable<T> extends DeesElement {
|
||||
${this.selectionMode === 'multi'
|
||||
? html`
|
||||
<dees-input-checkbox
|
||||
.value=${this.areAllSelected()}
|
||||
.value=${this.areAllVisibleSelected()}
|
||||
.indeterminate=${this.isVisibleSelectionIndeterminate()}
|
||||
@newValue=${(e: CustomEvent<boolean>) => {
|
||||
e.stopPropagation();
|
||||
this.setSelectAll(e.detail === true);
|
||||
this.setSelectVisible(e.detail === true);
|
||||
}}
|
||||
></dees-input-checkbox>
|
||||
`
|
||||
@@ -327,7 +352,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
: html``}
|
||||
</thead>
|
||||
<tbody>
|
||||
${getViewDataFn(this.data, effectiveColumns, this.sortKey, this.sortDir, this.filterText, this.columnFilters).map((itemArg, rowIndex) => {
|
||||
${viewData.map((itemArg, rowIndex) => {
|
||||
const getTr = (elementArg: HTMLElement): HTMLElement => {
|
||||
if (elementArg.tagName === 'TR') {
|
||||
return elementArg;
|
||||
@@ -528,6 +553,53 @@ export class DeesTable<T> extends DeesElement {
|
||||
console.log(this.dataActions);
|
||||
this.requestUpdate();
|
||||
};
|
||||
// wire search inputs
|
||||
this.wireSearchInputs();
|
||||
}
|
||||
}
|
||||
|
||||
private __debounceTimer?: any;
|
||||
private debounceRun(fn: () => void, ms = 200) {
|
||||
if (this.__debounceTimer) clearTimeout(this.__debounceTimer);
|
||||
this.__debounceTimer = setTimeout(fn, ms);
|
||||
}
|
||||
|
||||
private wireSearchInputs() {
|
||||
const searchTextEl: any = this.shadowRoot?.querySelector('.searchGrid dees-input-text');
|
||||
const searchModeEl: any = this.shadowRoot?.querySelector('.searchGrid dees-input-multitoggle');
|
||||
if (searchTextEl && !this.__searchTextSub) {
|
||||
this.__searchTextSub = searchTextEl.changeSubject.subscribe((el: any) => {
|
||||
const val: string = el?.value ?? '';
|
||||
this.debounceRun(() => {
|
||||
if (this.searchMode === 'server') {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('searchRequest', {
|
||||
detail: { query: val, mode: 'server' },
|
||||
bubbles: true,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this.setFilterText(val);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
if (searchModeEl && !this.__searchModeSub) {
|
||||
this.__searchModeSub = searchModeEl.changeSubject.subscribe((el: any) => {
|
||||
const mode: string = el?.selectedOption || el?.value || 'table';
|
||||
if (mode === 'table' || mode === 'data' || mode === 'server') {
|
||||
this.searchMode = mode as any;
|
||||
// When switching modes, re-apply current text input
|
||||
const val: string = searchTextEl?.value ?? '';
|
||||
this.debounceRun(() => {
|
||||
if (this.searchMode === 'server') {
|
||||
this.dispatchEvent(new CustomEvent('searchRequest', { detail: { query: val, mode: 'server' }, bubbles: true }));
|
||||
} else {
|
||||
this.setFilterText(val);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,25 +749,31 @@ export class DeesTable<T> extends DeesElement {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private areAllSelected(): boolean {
|
||||
return this.data.length > 0 && this.selectedIds.size === this.data.length;
|
||||
}
|
||||
|
||||
private toggleSelectAll() {
|
||||
if (this.areAllSelected()) {
|
||||
this.selectedIds.clear();
|
||||
} else {
|
||||
this.selectedIds = new Set(this.data.map((r) => this.getRowId(r)));
|
||||
private areAllVisibleSelected(): boolean {
|
||||
const view: T[] = (this as any)._lastViewData || [];
|
||||
if (view.length === 0) return false;
|
||||
for (const r of view) {
|
||||
if (!this.selectedIds.has(this.getRowId(r))) return false;
|
||||
}
|
||||
this.emitSelectionChange();
|
||||
this.requestUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
private setSelectAll(checked: boolean) {
|
||||
private isVisibleSelectionIndeterminate(): boolean {
|
||||
const view: T[] = (this as any)._lastViewData || [];
|
||||
if (view.length === 0) return false;
|
||||
let count = 0;
|
||||
for (const r of view) {
|
||||
if (this.selectedIds.has(this.getRowId(r))) count++;
|
||||
}
|
||||
return count > 0 && count < view.length;
|
||||
}
|
||||
|
||||
private setSelectVisible(checked: boolean) {
|
||||
const view: T[] = (this as any)._lastViewData || [];
|
||||
if (checked) {
|
||||
this.selectedIds = new Set(this.data.map((r) => this.getRowId(r)));
|
||||
for (const r of view) this.selectedIds.add(this.getRowId(r));
|
||||
} else {
|
||||
this.selectedIds.clear();
|
||||
for (const r of view) this.selectedIds.delete(this.getRowId(r));
|
||||
}
|
||||
this.emitSelectionChange();
|
||||
this.requestUpdate();
|
||||
|
Reference in New Issue
Block a user