mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-17 22:36:01 +00:00
SYNC: filterable table control
This commit is contained in:
committed by
Diflow
parent
fc43b35628
commit
13d057e4f7
@@ -1,6 +1,9 @@
|
||||
import { arrayToHexString, isTypeDateTime } from 'dbgate-tools';
|
||||
import { arrayToHexString, evalFilterBehaviour, isTypeDateTime } from 'dbgate-tools';
|
||||
import { format, toDate } from 'date-fns';
|
||||
import _isString from 'lodash/isString';
|
||||
import _cloneDeepWith from 'lodash/cloneDeepWith';
|
||||
import { Condition, Expression } from 'dbgate-sqltree';
|
||||
import { parseFilter } from './parseFilter';
|
||||
|
||||
export type FilterMultipleValuesMode = 'is' | 'is_not' | 'contains' | 'begins' | 'ends';
|
||||
|
||||
@@ -61,3 +64,29 @@ export function createMultiLineFilter(mode: FilterMultipleValuesMode, text: stri
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export function compileCompoudEvalCondition(filters: { [column: string]: string }): Condition {
|
||||
if (!filters) return null;
|
||||
const conditions = [];
|
||||
for (const name in filters) {
|
||||
try {
|
||||
const condition = parseFilter(filters[name], evalFilterBehaviour);
|
||||
const replaced = _cloneDeepWith(condition, (expr: Expression) => {
|
||||
if (expr.exprType == 'placeholder')
|
||||
return {
|
||||
exprType: 'column',
|
||||
columnName: name,
|
||||
};
|
||||
});
|
||||
conditions.push(replaced);
|
||||
} catch (err) {
|
||||
// filter parse error - ignore filter
|
||||
}
|
||||
}
|
||||
|
||||
if (conditions.length == 0) return null;
|
||||
return {
|
||||
conditionType: 'and',
|
||||
conditions,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import Link from './Link.svelte';
|
||||
import TableControl from './TableControl.svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export let title;
|
||||
export let collection;
|
||||
@@ -12,6 +13,9 @@
|
||||
export let hideDisplayName = false;
|
||||
export let clickable = false;
|
||||
export let onAddNew = null;
|
||||
export let displayNameFieldName = null;
|
||||
|
||||
export let filters = writable({});
|
||||
|
||||
let collapsed = false;
|
||||
</script>
|
||||
@@ -43,14 +47,16 @@
|
||||
rows={collection || []}
|
||||
columns={_.compact([
|
||||
!hideDisplayName && {
|
||||
fieldName: 'displayName',
|
||||
fieldName: displayNameFieldName || 'displayName',
|
||||
header: 'Name',
|
||||
slot: -1,
|
||||
sortable: true,
|
||||
filterable: !!displayNameFieldName,
|
||||
},
|
||||
...columns,
|
||||
])}
|
||||
{clickable}
|
||||
{filters}
|
||||
on:clickrow
|
||||
>
|
||||
<svelte:fragment slot="-1" let:row let:col>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
slot?: number;
|
||||
isHighlighted?: Function;
|
||||
sortable?: boolean;
|
||||
filterable?: boolean;
|
||||
filteredExpression?: (row: any) => string;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -19,6 +21,10 @@
|
||||
import keycodes from '../utility/keycodes';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import DataFilterControl from '../datagrid/DataFilterControl.svelte';
|
||||
import { evalFilterBehaviour } from 'dbgate-tools';
|
||||
import { evaluateCondition } from 'dbgate-sqltree';
|
||||
import { compileCompoudEvalCondition } from 'dbgate-filterparser';
|
||||
|
||||
export let columns: (TableControlColumn | false)[];
|
||||
export let rows;
|
||||
@@ -36,6 +42,7 @@
|
||||
export let checkedKeys = null;
|
||||
export let onSetCheckedKeys = null;
|
||||
export let extractCheckedKey = x => x.id;
|
||||
export let filters = null;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@@ -54,10 +61,28 @@
|
||||
}
|
||||
};
|
||||
|
||||
function filterRows(rows, filters) {
|
||||
const condition = compileCompoudEvalCondition(filters);
|
||||
|
||||
if (!condition) return rows;
|
||||
|
||||
return rows.filter(row => {
|
||||
const newrow = { ...row };
|
||||
for (const col of columnList) {
|
||||
if (col.filteredExpression) {
|
||||
newrow[col.fieldName] = col.filteredExpression(row);
|
||||
}
|
||||
}
|
||||
return evaluateCondition(condition, newrow);
|
||||
});
|
||||
}
|
||||
|
||||
let sortedByField = null;
|
||||
let sortOrderIsDesc = false;
|
||||
|
||||
$: sortedRowsTmp = sortedByField ? _.sortBy(rows || [], sortedByField) : rows;
|
||||
$: filteredRows = filters ? filterRows(rows, $filters) : rows;
|
||||
|
||||
$: sortedRowsTmp = sortedByField ? _.sortBy(filteredRows || [], sortedByField) : filteredRows;
|
||||
$: sortedRows = sortOrderIsDesc ? [...sortedRowsTmp].reverse() : sortedRowsTmp;
|
||||
</script>
|
||||
|
||||
@@ -101,6 +126,25 @@
|
||||
</th>
|
||||
{/each}
|
||||
</tr>
|
||||
{#if filters}
|
||||
<tr>
|
||||
{#if checkedKeys}
|
||||
<td></td>
|
||||
{/if}
|
||||
{#each columnList as col}
|
||||
<td class="filter-cell" class:empty-cell={!col.filterable}>
|
||||
{#if col.filterable}
|
||||
<DataFilterControl
|
||||
filterBehaviour={evalFilterBehaviour}
|
||||
filter={$filters[col.fieldName]}
|
||||
setFilter={value => filters.update(f => ({ ...f, [col.fieldName]: value }))}
|
||||
placeholder="Data filter"
|
||||
/>
|
||||
{/if}
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{/if}
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each sortedRows as row, index}
|
||||
@@ -232,4 +276,15 @@
|
||||
border-collapse: separate;
|
||||
border-left: 1px solid var(--theme-border);
|
||||
}
|
||||
|
||||
.filter-cell {
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.empty-cell {
|
||||
background-color: var(--theme-bg-1);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -192,6 +192,7 @@
|
||||
clickable
|
||||
on:clickrow={e => showModal(ColumnEditorModal, { columnInfo: e.detail, tableInfo, setTableInfo, driver })}
|
||||
onAddNew={isWritable ? addColumn : null}
|
||||
displayNameFieldName="columnName"
|
||||
columns={[
|
||||
!driver?.dialect?.specificNullabilityImplementation && {
|
||||
fieldName: 'notNull',
|
||||
@@ -203,11 +204,13 @@
|
||||
fieldName: 'dataType',
|
||||
header: 'Data Type',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
},
|
||||
{
|
||||
fieldName: 'defaultValue',
|
||||
header: 'Default value',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
},
|
||||
driver?.dialect?.columnProperties?.isSparse && {
|
||||
fieldName: 'isSparse',
|
||||
@@ -219,6 +222,7 @@
|
||||
fieldName: 'computedExpression',
|
||||
header: 'Computed Expression',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
},
|
||||
driver?.dialect?.columnProperties?.isPersisted && {
|
||||
fieldName: 'isPersisted',
|
||||
@@ -242,10 +246,12 @@
|
||||
fieldName: 'columnComment',
|
||||
header: 'Comment',
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
},
|
||||
isWritable
|
||||
? {
|
||||
fieldName: 'actions',
|
||||
filterable: false,
|
||||
slot: 3,
|
||||
}
|
||||
: null,
|
||||
|
||||
Reference in New Issue
Block a user