mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-05-01 09:44:00 +00:00
perspective sorting
This commit is contained in:
@@ -6,6 +6,10 @@ export interface PerspectiveConfigColumns {
|
|||||||
|
|
||||||
export interface PerspectiveConfig extends PerspectiveConfigColumns {
|
export interface PerspectiveConfig extends PerspectiveConfigColumns {
|
||||||
filters: { [uniqueName: string]: string };
|
filters: { [uniqueName: string]: string };
|
||||||
|
sort: {
|
||||||
|
uniqueName: string;
|
||||||
|
order: 'ASC' | 'DESC';
|
||||||
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPerspectiveConfig(): PerspectiveConfig {
|
export function createPerspectiveConfig(): PerspectiveConfig {
|
||||||
@@ -14,6 +18,7 @@ export function createPerspectiveConfig(): PerspectiveConfig {
|
|||||||
checkedColumns: [],
|
checkedColumns: [],
|
||||||
uncheckedColumns: [],
|
uncheckedColumns: [],
|
||||||
filters: {},
|
filters: {},
|
||||||
|
sort: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,10 +109,10 @@ export class PerspectiveDataLoader {
|
|||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
selectAll: !dataColumns,
|
selectAll: !dataColumns,
|
||||||
orderBy: orderBy?.map(columnName => ({
|
orderBy: orderBy?.map(({ columnName, order }) => ({
|
||||||
exprType: 'column',
|
exprType: 'column',
|
||||||
columnName,
|
columnName,
|
||||||
direction: 'ASC',
|
direction: order,
|
||||||
source: {
|
source: {
|
||||||
name: { schemaName, pureName },
|
name: { schemaName, pureName },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ export interface PerspectiveDataLoadProps {
|
|||||||
schemaName: string;
|
schemaName: string;
|
||||||
pureName: string;
|
pureName: string;
|
||||||
dataColumns: string[];
|
dataColumns: string[];
|
||||||
orderBy: string[];
|
orderBy: {
|
||||||
|
columnName: string;
|
||||||
|
order: 'ASC' | 'DESC';
|
||||||
|
}[];
|
||||||
bindingColumns?: string[];
|
bindingColumns?: string[];
|
||||||
bindingValues?: any[][];
|
bindingValues?: any[][];
|
||||||
range?: RangeDefinition;
|
range?: RangeDefinition;
|
||||||
|
|||||||
@@ -95,6 +95,9 @@ export abstract class PerspectiveTreeNode {
|
|||||||
get filterType(): FilterType {
|
get filterType(): FilterType {
|
||||||
return 'string';
|
return 'string';
|
||||||
}
|
}
|
||||||
|
get columnName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
getChildMatchColumns() {
|
getChildMatchColumns() {
|
||||||
return [];
|
return [];
|
||||||
@@ -181,6 +184,25 @@ export abstract class PerspectiveTreeNode {
|
|||||||
conditions,
|
conditions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOrderBy(table: TableInfo): PerspectiveDataLoadProps['orderBy'] {
|
||||||
|
const res = _compact(
|
||||||
|
this.childNodes.map(node => {
|
||||||
|
const sort = this.config?.sort?.find(x => x.uniqueName == node.uniqueName);
|
||||||
|
if (sort) {
|
||||||
|
return {
|
||||||
|
columnName: node.columnName,
|
||||||
|
order: sort.order,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return res.length > 0
|
||||||
|
? res
|
||||||
|
: table?.primaryKey?.columns.map(x => ({ columnName: x.columnName, order: 'ASC' })) || [
|
||||||
|
{ columnName: table?.columns[0].columnName, order: 'ASC' },
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
|
export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
|
||||||
@@ -237,7 +259,7 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
|
|||||||
),
|
),
|
||||||
dataColumns: this.getDataLoadColumns(),
|
dataColumns: this.getDataLoadColumns(),
|
||||||
databaseConfig: this.databaseConfig,
|
databaseConfig: this.databaseConfig,
|
||||||
orderBy: this.refTable?.primaryKey?.columns.map(x => x.columnName) || [this.refTable.columns[0].columnName],
|
orderBy: this.getOrderBy(this.refTable),
|
||||||
condition: this.getChildrenCondition(),
|
condition: this.getChildrenCondition(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -252,6 +274,10 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
|
|||||||
return this.column.columnName;
|
return this.column.columnName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get columnName() {
|
||||||
|
return this.column.columnName;
|
||||||
|
}
|
||||||
|
|
||||||
get fieldName() {
|
get fieldName() {
|
||||||
return this.codeName + 'Ref';
|
return this.codeName + 'Ref';
|
||||||
}
|
}
|
||||||
@@ -322,7 +348,7 @@ export class PerspectiveTableNode extends PerspectiveTreeNode {
|
|||||||
pureName: this.table.pureName,
|
pureName: this.table.pureName,
|
||||||
dataColumns: this.getDataLoadColumns(),
|
dataColumns: this.getDataLoadColumns(),
|
||||||
databaseConfig: this.databaseConfig,
|
databaseConfig: this.databaseConfig,
|
||||||
orderBy: this.table.primaryKey?.columns.map(x => x.columnName) || [this.table.columns[0].columnName],
|
orderBy: this.getOrderBy(this.table),
|
||||||
condition: this.getChildrenCondition(),
|
condition: this.getChildrenCondition(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -397,7 +423,7 @@ export class PerspectiveTableReferenceNode extends PerspectiveTableNode {
|
|||||||
),
|
),
|
||||||
dataColumns: this.getDataLoadColumns(),
|
dataColumns: this.getDataLoadColumns(),
|
||||||
databaseConfig: this.databaseConfig,
|
databaseConfig: this.databaseConfig,
|
||||||
orderBy: this.table.primaryKey?.columns.map(x => x.columnName) || [this.table.columns[0].columnName],
|
orderBy: this.getOrderBy(this.table),
|
||||||
condition: this.getChildrenCondition(),
|
condition: this.getChildrenCondition(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
184
packages/web/src/perspectives/PerspectiveHeaderControl.svelte
Normal file
184
packages/web/src/perspectives/PerspectiveHeaderControl.svelte
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveDisplayColumn } from 'dbgate-datalib';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import DropDownButton from '../buttons/DropDownButton.svelte';
|
||||||
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
export let label;
|
||||||
|
export let column: PerspectiveDisplayColumn;
|
||||||
|
export let columnLevel;
|
||||||
|
export let config: PerspectiveConfig;
|
||||||
|
export let setConfig: ChangePerspectiveConfigFunc;
|
||||||
|
|
||||||
|
// export let setSort = undefined;
|
||||||
|
// export let addToSort = undefined;
|
||||||
|
// export let clearSort = undefined;
|
||||||
|
// export let grouping = undefined;
|
||||||
|
// export let order = undefined;
|
||||||
|
// export let orderIndex = undefined;
|
||||||
|
// export let isSortDefined = false;
|
||||||
|
let mouseIn;
|
||||||
|
|
||||||
|
$: uniqueName = column.dataNode.uniqueName;
|
||||||
|
// $: console.log('CFG', config);
|
||||||
|
$: order = config.sort?.find(x => x.uniqueName == uniqueName)?.order;
|
||||||
|
$: orderIndex = config.sort?.length > 1 ? _.findIndex(config.sort, x => x.uniqueName == uniqueName) : -1;
|
||||||
|
$: isSortDefined = config.sort?.length > 0;
|
||||||
|
|
||||||
|
const setSort = order => {
|
||||||
|
setConfig(
|
||||||
|
cfg => ({
|
||||||
|
...cfg,
|
||||||
|
sort: [{ uniqueName, order }],
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addToSort = order => {
|
||||||
|
setConfig(
|
||||||
|
cfg => ({
|
||||||
|
...cfg,
|
||||||
|
sort: [...(cfg.sort || []), { uniqueName, order }],
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearSort = () => {
|
||||||
|
setConfig(
|
||||||
|
cfg => ({
|
||||||
|
...cfg,
|
||||||
|
sort: [],
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// display.setSort(col.uniqueName, order)
|
||||||
|
// addToSort={display.sortable ? order => display.addToSort(col.uniqueName, order) : null}
|
||||||
|
// order={display.sortable ? display.getSortOrder(col.uniqueName) : null}
|
||||||
|
// orderIndex={display.sortable ? display.getSortOrderIndex(col.uniqueName) : -1}
|
||||||
|
// isSortDefined={display.sortable ? display.isSortDefined() : false}
|
||||||
|
// clearSort={display.sortable ? () => display.clearSort() : null}
|
||||||
|
|
||||||
|
function getMenu() {
|
||||||
|
return [
|
||||||
|
{ onClick: () => setSort('ASC'), text: 'Sort ascending' },
|
||||||
|
{ onClick: () => setSort('DESC'), text: 'Sort descending' },
|
||||||
|
isSortDefined && !order && { onClick: () => addToSort('ASC'), text: 'Add to sort - ascending' },
|
||||||
|
isSortDefined && !order && { onClick: () => addToSort('DESC'), text: 'Add to sort - descending' },
|
||||||
|
order && { onClick: () => clearSort(), text: 'Clear sort criteria' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if column.isVisible(columnLevel)}
|
||||||
|
<th
|
||||||
|
rowspan={column.rowSpan}
|
||||||
|
class="columnHeader"
|
||||||
|
on:mouseenter={() => (mouseIn = true)}
|
||||||
|
on:mouseleave={() => (mouseIn = false)}
|
||||||
|
>
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="label">
|
||||||
|
{column.title}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if order == 'ASC'}
|
||||||
|
<span class="icon">
|
||||||
|
<FontIcon icon="img sort-asc" />
|
||||||
|
{#if orderIndex >= 0}
|
||||||
|
<span class="color-icon-green order-index">{orderIndex + 1}</span>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{#if order == 'DESC'}
|
||||||
|
<span class="icon">
|
||||||
|
<FontIcon icon="img sort-desc" />
|
||||||
|
{#if orderIndex >= 0}
|
||||||
|
<span class="color-icon-green order-index">{orderIndex + 1}</span>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if mouseIn}
|
||||||
|
<div class="menuButton">
|
||||||
|
<DropDownButton menu={getMenu} narrow />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</th>
|
||||||
|
{/if}
|
||||||
|
{#if column.showParent(columnLevel)}
|
||||||
|
<th colspan={column.getColSpan(columnLevel)} class="tableHeader">{column.getParentName(columnLevel)}</th>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrap {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.menuButton {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
.order-index {
|
||||||
|
font-size: 10pt;
|
||||||
|
margin-left: -3px;
|
||||||
|
margin-right: 2px;
|
||||||
|
top: -1px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 10px;
|
||||||
|
padding: 2px;
|
||||||
|
margin: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
margin-left: 3px;
|
||||||
|
align-self: center;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
/* .resizer {
|
||||||
|
background-color: var(--theme-border);
|
||||||
|
width: 2px;
|
||||||
|
cursor: col-resize;
|
||||||
|
z-index: 1;
|
||||||
|
} */
|
||||||
|
.grouping {
|
||||||
|
color: var(--theme-font-alt);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.data-type {
|
||||||
|
color: var(--theme-font-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
/* border: 1px solid var(--theme-border); */
|
||||||
|
text-align: left;
|
||||||
|
padding: 2px;
|
||||||
|
margin: 0;
|
||||||
|
background-color: var(--theme-bg-1);
|
||||||
|
overflow: hidden;
|
||||||
|
vertical-align: center;
|
||||||
|
z-index: 100;
|
||||||
|
font-weight: normal;
|
||||||
|
|
||||||
|
border-bottom: 1px solid var(--theme-border);
|
||||||
|
border-right: 1px solid var(--theme-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
th.tableHeader {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
th.columnHeader {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -28,12 +28,14 @@
|
|||||||
import PerspectiveCell from './PerspectiveCell.svelte';
|
import PerspectiveCell from './PerspectiveCell.svelte';
|
||||||
import DataGridCell from '../datagrid/DataGridCell.svelte';
|
import DataGridCell from '../datagrid/DataGridCell.svelte';
|
||||||
import PerspectiveLoadingIndicator from './PerspectiveLoadingIndicator.svelte';
|
import PerspectiveLoadingIndicator from './PerspectiveLoadingIndicator.svelte';
|
||||||
|
import PerspectiveHeaderControl from './PerspectiveHeaderControl.svelte';
|
||||||
|
|
||||||
const dbg = debug('dbgate:PerspectivaTable');
|
const dbg = debug('dbgate:PerspectivaTable');
|
||||||
export const activator = createActivator('PerspectiveTable', true);
|
export const activator = createActivator('PerspectiveTable', true);
|
||||||
|
|
||||||
export let root: PerspectiveTreeNode;
|
export let root: PerspectiveTreeNode;
|
||||||
export let loadedCounts;
|
export let loadedCounts;
|
||||||
|
export let config;
|
||||||
export let setConfig;
|
export let setConfig;
|
||||||
|
|
||||||
let dataRows;
|
let dataRows;
|
||||||
@@ -159,12 +161,7 @@
|
|||||||
{#each _.range(display.columnLevelCount) as columnLevel}
|
{#each _.range(display.columnLevelCount) as columnLevel}
|
||||||
<tr>
|
<tr>
|
||||||
{#each display.columns as column}
|
{#each display.columns as column}
|
||||||
{#if column.isVisible(columnLevel)}
|
<PerspectiveHeaderControl label={column.title} {column} {columnLevel} {setConfig} {config} />
|
||||||
<th rowspan={column.rowSpan}>{column.title}</th>
|
|
||||||
{/if}
|
|
||||||
{#if column.showParent(columnLevel)}
|
|
||||||
<th colspan={column.getColSpan(columnLevel)} class="tableName">{column.getParentName(columnLevel)}</th>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -266,25 +263,6 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
|
||||||
/* border: 1px solid var(--theme-border); */
|
|
||||||
text-align: left;
|
|
||||||
padding: 2px;
|
|
||||||
margin: 0;
|
|
||||||
background-color: var(--theme-bg-1);
|
|
||||||
overflow: hidden;
|
|
||||||
vertical-align: center;
|
|
||||||
z-index: 100;
|
|
||||||
font-weight: normal;
|
|
||||||
|
|
||||||
border-bottom: 1px solid var(--theme-border);
|
|
||||||
border-right: 1px solid var(--theme-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
th.tableName {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
th.filter {
|
th.filter {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<svelte:fragment slot="2">
|
<svelte:fragment slot="2">
|
||||||
<PerspectiveTable {root} {loadedCounts} {setConfig} />
|
<PerspectiveTable {root} {loadedCounts} {config} {setConfig} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</HorizontalSplitter>
|
</HorizontalSplitter>
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
testEnabled: () => getCurrentEditor() != null,
|
testEnabled: () => getCurrentEditor() != null,
|
||||||
onClick: () => getCurrentEditor().refresh(),
|
onClick: () => getCurrentEditor().refresh(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const allowAddToFavorites = props => true;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
Reference in New Issue
Block a user