Add basic filtering
All checks were successful
build / build (push) Successful in 1m37s

This commit is contained in:
2025-05-02 19:46:40 +09:30
parent f388efe667
commit 95855b9e99
3 changed files with 183 additions and 49 deletions

View File

@@ -7,16 +7,20 @@ import {
signal,
} from '@angular/core';
import { Skeleton } from 'primeng/skeleton';
import { TableLazyLoadEvent, TableModule } from 'primeng/table';
import { Column, DuckdbService } from '../duckdb.service';
import { Table, TableLazyLoadEvent, TableModule } from 'primeng/table';
import { Column, DuckdbService, Filter, FilterValue } from '../duckdb.service';
import { ButtonModule } from 'primeng/button';
import { IconField } from 'primeng/iconfield';
import { InputIconModule } from 'primeng/inputicon';
@Component({
selector: 'app-file-viewer',
standalone: true,
imports: [TableModule, Skeleton],
imports: [TableModule, Skeleton, ButtonModule, IconField, InputIconModule],
template: `
@if (file() && columns().length > 0) {
<p-table
#table
sortMode="multiple"
[reorderableColumns]="true"
[resizableColumns]="true"
@@ -34,6 +38,22 @@ import { Column, DuckdbService } from '../duckdb.service';
[multiSortMeta]="[{ field: columns()[0].name, order: 1 }]"
stripedRows
>
<ng-template #caption>
<div class="flex">
<p-button
label="Reset"
[outlined]="true"
icon="pi pi-filter-slash"
(click)="clear(table)"
/>
<p-iconfield iconPosition="left" class="ml-auto">
<p-inputicon>
<i class="pi pi-search"></i>
</p-inputicon>
<input pInputText type="text" placeholder="Search keyword" />
</p-iconfield>
</div>
</ng-template>
<ng-template #header let-columns>
<tr>
@for (col of columns; track $index) {
@@ -86,25 +106,16 @@ export class FileViewerComponent {
constructor() {
effect(async () => {
const file = this.file();
if (file) {
await this.duckdbService.addFile(file);
this.columns.set(await this.duckdbService.getColumns(file));
const rows = await this.duckdbService.getRows(
file,
0,
this.numRows,
this.columns(),
[],
[],
[],
);
const newValue = Array.from({ length: Number(rows.totalRows) });
this.currentValue.set(newValue);
}
this.loadEmpty();
});
}
protected clear(table: Table) {
table.clear();
table.multiSortMeta = [{ field: this.columns()[0].name, order: 1 }];
table.sortMultiple();
}
protected async onLazyLoad(event: TableLazyLoadEvent) {
const file = this.file();
if (file) {
@@ -117,17 +128,43 @@ export class FileViewerComponent {
name: meta.field,
sortType: meta.order < 0 ? 'desc' : 'asc',
})) ?? [],
Array.isArray(event.filters)
? Object.values(event.filters).map((filter) => ({
value: Array.isArray(filter.value)
? filter.value
: [filter.value],
matchType: filter.matchMode,
operator: filter.operator,
}))
event.filters
? Object.entries(event.filters).flatMap(([column, filter]) => {
if (Array.isArray(filter)) {
const firstFilter = filter[0];
return Filter.parse({
column,
value: filter
.filter((f) => f.value != null)
.map((f) =>
FilterValue.parse({
value: f.value,
matchType: f.matchMode,
}),
),
operator: firstFilter.operator,
});
} else if (filter) {
return Filter.parse({
column,
value: [
FilterValue.parse({
value: filter.value,
matchType: filter.matchMode,
}),
],
operator: filter.operator,
});
}
return [];
})
: [],
[],
);
if (this.currentValue().length !== Number(rows.totalRows)) {
this.currentValue.set(Array.from({ length: Number(rows.totalRows) }));
return;
}
// First clear out existing data, don't want to risk loading entire file into memory
// Keep data in previous/next page so when user scrolls between 2 pages they don't see missing data
for (let i = 0; i < this.currentValue().length; i++) {
@@ -138,9 +175,29 @@ export class FileViewerComponent {
this.currentValue()[i] = undefined;
}
}
// Can't update the current value, otherwise we get an infinite loop
this.currentValue().splice(event.first!, event.rows!, ...rows.rows);
event.forceUpdate!();
}
}
private async loadEmpty() {
const file = this.file();
if (file) {
await this.duckdbService.addFile(file);
this.columns.set(await this.duckdbService.getColumns(file));
const rows = await this.duckdbService.getRows(
file,
0,
this.numRows,
this.columns(),
[],
[],
[],
);
const newValue = Array.from({ length: Number(rows.totalRows) });
this.currentValue.set(newValue);
}
}
}