add internal sort panel.
This commit is contained in:
@ -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 {
|
||||
|
Reference in New Issue
Block a user