add internal sort panel.

This commit is contained in:
2024-01-26 17:27:24 +08:00
parent 984496e08e
commit ac605895c5
7 changed files with 520 additions and 121 deletions

View File

@ -6,6 +6,7 @@ import { createElement } from "../../functions";
import { createIcon } from "../icon";
import { createCheckbox } from "../checkbox";
import { setTooltip } from "../tooltip";
import { Popup, showAlert } from "../popup";
import { convertCssStyle } from "../extension";
import { GridColumn, GridInputColumn, GridTextColumn, GridDropdownColumn, GridCheckboxColumn, GridIconColumn, GridDateColumn } from "./column";
@ -89,6 +90,7 @@ export class Grid {
window = global;
sortIndex = -1;
sortDirection = 1;
sortArray = null;
willSelect;
cellClicked;
@ -119,7 +121,18 @@ export class Grid {
all: r('allItem', '( All )'),
ok: r('ok', 'OK'),
reset: r('reset', 'Reset'),
null: r('null', '( Null )')
cancel: r('cancel', 'Cancel'),
null: r('null', '( Null )'),
addLevel: r('', 'Add level'),
deleteLevel: r('', 'Delete level'),
copyLevel: r('', 'Copy level'),
asc: r('', 'Ascending'),
desc: r('', 'Descending'),
column: r('', 'Column'),
order: r('', 'Order'),
sort: r('', 'Sort'),
requirePrompt: r('', 'Column required.'),
duplicatePrompt: r('', 'Column duplicated: "{column}"')
};
}
@ -146,6 +159,8 @@ export class Grid {
this._refreshSource(list);
}
get sourceFiltered() { return this._var.currentSource?.map(s => s.values) ?? this.source }
setItem(index, item) {
if (this._var.source == null) {
throw new Error('no source');
@ -172,8 +187,9 @@ export class Grid {
if (this._var.source == null) {
throw new Error('no source');
}
this._var.source.splice(index, 1);
const item = this._var.source.splice(index, 1)[0];
this.reload();
return item;
}
_refreshSource(list) {
@ -202,6 +218,8 @@ export class Grid {
if (this.sortIndex >= 0) {
this.sortColumn();
} else if (this.sortArray?.length > 0) {
this.sort();
}
this.resize();
}
@ -351,8 +369,12 @@ export class Grid {
this._var.el = grid;
this._var.rendering = false;
if (this._var.source != null && this.sortIndex >= 0) {
this.sortColumn();
if (this._var.source != null) {
if (this.sortIndex >= 0) {
this.sortColumn();
} else if (this.sortArray?.length > 0) {
this.sort();
}
}
}
@ -452,31 +474,12 @@ export class Grid {
}
}
sortColumn(reload) {
const index = this.sortIndex;
const col = this.columns[index];
if (col == null) {
return;
}
const direction = this.sortDirection;
[...this._var.refs.header.children].forEach((th, i) => {
const arrow = th.querySelector('.arrow');
if (arrow == null) {
return;
}
if (i === index) {
arrow.className = `arrow ${(direction !== 1 ? 'desc' : 'asc')}`;
} else if (arrow.className !== 'arrow') {
arrow.className = 'arrow';
}
});
let comparer;
_getComparer(col, direction) {
if (typeof col.sortFilter !== 'function') {
let direction = this.sortDirection;
if (isNaN(direction)) {
direction = 1;
}
comparer = (a, b) => {
return (a, b) => {
a = this._getItemProp(a.values, true, col);
b = this._getItemProp(b.values, true, col);
if (a == null && typeof b === 'number') {
@ -491,9 +494,30 @@ export class Grid {
}
return a === b ? 0 : (a > b ? 1 : -1) * direction;
};
} else {
comparer = (a, b) => col.sortFilter(a.values, b.values) * direction;
}
return (a, b) => col.sortFilter(a.values, b.values) * direction;
}
sortColumn(reload) {
const index = this.sortIndex;
const col = this.columns[index];
if (col == null) {
return;
}
this.sortArray = null;
const direction = this.sortDirection;
[...this._var.refs.header.children].forEach((th, i) => {
const arrow = th.querySelector('.arrow');
if (arrow == null) {
return;
}
if (i === index) {
arrow.className = `arrow ${(direction !== 1 ? 'desc' : 'asc')}`;
} else if (arrow.className !== 'arrow') {
arrow.className = 'arrow';
}
});
const comparer = this._getComparer(col, direction);
this._var.source.sort(comparer);
if (this._var.colAttrs.__filtered === true) {
this._var.currentSource.sort(comparer);
@ -508,11 +532,257 @@ export class Grid {
}
}
sort(reload) {
const sortArray = this.sortArray;
if (sortArray == null || sortArray.length === 0) {
return;
}
this.sortIndex = -1;
const comparer = (a, b) => {
for (let i = 0; i < sortArray.length; ++i) {
const s = sortArray[i];
const col = this.columns.find(c => c.key === s.column && c.visible !== false);
if (col != null) {
const result = this._getComparer(col, s.order === 'desc' ? -1 : 1)(a, b);
if (result !== 0) {
return result;
}
}
}
return 0;
};
this._var.source.sort(comparer);
if (this._var.colAttrs.__filtered === true) {
this._var.currentSource.sort(comparer);
}
if (this._var.rowCount < 0) {
return;
}
if (reload) {
this.reload();
} else {
this.refresh();
}
// arrow icon
[...this._var.refs.header.children].forEach((th, i) => {
const arrow = th.querySelector('.arrow');
if (arrow == null) {
return;
}
const col = this.columns[i];
const s = sortArray.find(s => s.column === col.key && col.visible !== false);
if (s != null) {
arrow.className = `arrow ${s.order}`;
} else if (arrow.className !== 'arrow') {
arrow.className = 'arrow';
}
});
}
clearHeaderCheckbox() {
const boxes = this._var.refs.header.querySelectorAll('.ui-check-wrapper>input');
boxes.forEach(box => box.checked = false);
}
showSortPanel() {
const content = createElement('div', 'ui-sort-panel-content');
const buttonWrapper = createElement('div', 'ui-sort-panel-buttons');
const grid = new Grid(null, r);
grid.langs = this.langs;
const rowChanged = index => {
buttonWrapper.querySelector('.ui-button-delete').disabled = index < 0;
buttonWrapper.querySelector('.ui-button-copy').disabled = index < 0;
buttonWrapper.querySelector('.ui-button-move-up').disabled = index < 1;
buttonWrapper.querySelector('.ui-button-move-down').disabled = index >= grid.source.length - 1;
};
grid.onSelectedRowChanged = rowChanged;
const reload = index => {
grid.selectedIndexes = [index];
grid.scrollTop = index * grid.rowHeight;
rowChanged(index);
}
buttonWrapper.append(
createElement('button', null,
createIcon('fa-light', 'plus'),
createElement('span', span => {
span.innerText = this.langs.addLevel;
span.addEventListener('click', () => {
let index = grid.selectedIndex;
const n = { column: '', order: 'asc' };
if (index >= 0) {
index += 1;
grid.addItem(n, index);
} else {
grid.addItem(n);
index = grid.source.length - 1;
}
reload(index);
});
})
),
createElement('button', 'ui-button-delete',
createIcon('fa-light', 'times'),
createElement('span', span => {
span.innerText = this.langs.deleteLevel;
span.addEventListener('click', () => {
let index = grid.selectedIndex;
if (index < 0) {
return;
}
grid.removeItem(index);
const length = grid.source.length;
if (index >= length) {
index = length - 1;
}
reload(index);
});
})
),
createElement('button', 'ui-button-copy',
createIcon('fa-light', 'copy'),
createElement('span', span => {
span.innerText = this.langs.copyLevel;
span.addEventListener('click', () => {
const index = grid.selectedIndex;
if (index < 0) {
return;
}
const item = grid.source[index];
if (item == null) {
return;
}
grid.addItem(Object.assign({}, item), index + 1);
reload(index + 1);
});
})
),
createElement('button', button => {
button.className = 'ui-button-move-up';
const icon = createIcon('fa-light', 'chevron-up');
icon.addEventListener('click', () => {
const index = grid.selectedIndex;
if (index < 1) {
return;
}
const item = grid.source[index];
if (item == null) {
return;
}
const it = grid.removeItem(index);
grid.addItem(it, index - 1);
reload(index - 1);
});
button.appendChild(icon);
}),
createElement('button', button => {
button.className = 'ui-button-move-down';
const icon = createIcon('fa-light', 'chevron-down');
icon.addEventListener('click', () => {
const index = grid.selectedIndex;
if (index >= grid.source.length - 1) {
return;
}
const item = grid.source[index];
if (item == null) {
return;
}
const it = grid.removeItem(index);
grid.addItem(it, index + 1);
reload(index + 1);
});
button.appendChild(icon);
})
);
const gridWrapper = createElement('div', 'ui-sort-panel-grid');
content.append(buttonWrapper, gridWrapper);
const columnSource = this.columns.filter(c => c.sortable !== false && c.visible !== false);
grid.columns = [
{
key: 'column',
caption: this.langs.column,
width: 270,
type: Grid.ColumnTypes.Dropdown,
dropOptions: {
textKey: 'caption',
valueKey: 'key'
},
source: columnSource,
sortable: false,
orderable: false
},
{
key: 'order',
caption: this.langs.order,
width: 150,
type: Grid.ColumnTypes.Dropdown,
source: [
{ value: 'asc', text: this.langs.asc },
{ value: 'desc', text: this.langs.desc }
],
sortable: false,
orderable: false
}
];
const pop = new Popup({
title: this.langs.sort,
content,
resizable: true,
buttons: [
{
text: this.langs.ok,
trigger: () => {
const source = grid.source;
if (source == null || source.length === 0) {
this.sortArray = null;
} else {
const dict = {};
for (let i = 0; i < source.length; ++i) {
const it = source[i];
if (it.column == null || it.column === '') {
grid.selectedIndexes = [i];
grid.refresh();
showAlert(this.langs.sort, this.langs.requirePrompt, 'warn');
return false;
}
if (Object.prototype.hasOwnProperty.call(dict, it.column)) {
grid.selectedIndexes = [i];
grid.refresh();
let name = columnSource.find(c => c.key === it.column);
if (name == null) {
name = it.column;
} else {
name = name.caption;
}
showAlert(this.langs.sort, this.langs.duplicatePrompt.replace('{column}', name), 'warn');
return false;
}
dict[it.column] = true;
}
this.sortArray = source;
this.sortDirection = 1;
this.sort();
}
if (typeof this.onSorted === 'function') {
this.onSorted(this.sortArray);
}
return true;
}
},
{ text: this.langs.cancel }
],
onResizeEnded: () => grid.resize()
});
const source = this.sortArray || [{ column: '', order: 'asc' }];
pop.show(this._var.el).then(() => {
pop.container.style.cssText += 'width: 520px; height: 400px';
grid.init(gridWrapper);
grid.source = source.filter(s => s.column === '' || columnSource.find(c => c.key === s.column) != null);
grid.selectedIndexes = [0];
grid.refresh();
rowChanged(0);
});
}
_createHeader(table) {
const thead = createElement('thead');
if (this.headerVisible === false) {
@ -691,7 +961,7 @@ export class Grid {
const exists = content.children.length;
count -= exists;
if (count > 0) {
for (let i = 0; i < count; i += 1) {
for (let i = 0; i < count; ++i) {
const row = createElement('tr', 'ui-grid-row');
let left = 0;
cols.forEach((col, j) => {
@ -940,7 +1210,7 @@ export class Grid {
idx ??= 0;
} else {
const count = children.length;
for (let i = index; i < count - 1 && offset >= 0; i += 1) {
for (let i = index; i < count - 1 && offset >= 0; ++i) {
element = children[i];
if (element == null || !element.className || element.classList.contains('sticky')) {
idx = i;
@ -994,7 +1264,7 @@ export class Grid {
if (targetIndex > 1) {
targetIndex = orderIndex - 1;
// const current = columns[index];
// for (let i = index; i < targetIndex; i += 1) {
// for (let i = index; i < targetIndex; ++i) {
// columns[i] = columns[i + 1];
// }
// columns[targetIndex] = current;
@ -1018,16 +1288,18 @@ export class Grid {
row.insertBefore(row.children[index], row.children[targetIndex]);
}
}
// refresh sortIndex
[...children].forEach((th, i) => {
const arrow = th.querySelector('.arrow');
if (arrow == null) {
return;
}
if (arrow.className !== 'arrow') {
this.sortIndex = i;
}
});
if (this.sortArray == null || this.sortArray.length === 0) {
// refresh sortIndex
[...children].forEach((th, i) => {
const arrow = th.querySelector('.arrow');
if (arrow == null) {
return;
}
if (arrow.className !== 'arrow') {
this.sortIndex = i;
}
});
}
if (typeof this.onColumnChanged === 'function') {
this.onColumnChanged(ColumnChangedType.Reorder, index, targetIndex);
@ -1641,7 +1913,7 @@ export class Grid {
end = selectedIndex;
}
selectedIndexes.splice(0);
for (let i = start; i <= end; i += 1) {
for (let i = start; i <= end; ++i) {
selectedIndexes.push(i);
}
flag = true;
@ -1713,7 +1985,7 @@ export class Grid {
if (enabled !== false) {
const val = item[col.key];
let oldValue;
if (val != null && Object.prototype.hasOwnProperty.call(val, 'Value') != null) {
if (val != null && Object.prototype.hasOwnProperty.call(val, 'Value')) {
oldValue = val.Value;
val.Value = value;
} else {