add internal sort panel.

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

View File

@ -297,7 +297,7 @@ export function getMessageStatus(comm, r, _var) {
let statusTips;
if (statusUpdatable !== false || ls.length > 1) {
statusTips = createElement('div', tip => {
for (let i = 0; i < msgs.length; i++) {
for (let i = 0; i < msgs.length; ++i) {
tip.appendChild(createElement('div', t => {
const p = msgs[i];
if (statusUpdatable !== false && p.StatusChanged) {

View File

@ -546,6 +546,61 @@
}
}
}
.ui-sort-panel-content {
height: 100%;
display: flex;
flex-direction: column;
>.ui-sort-panel-buttons {
flex: 0 0 auto;
white-space: nowrap;
overflow: hidden;
>button {
margin-right: 6px;
border: none;
line-height: 28px;
color: var(--title-color);
border-radius: var(--corner-radius);
padding: 0 10px;
box-sizing: border-box;
height: 28px;
cursor: pointer;
user-select: none;
background-color: var(--title-bg-color);
transition: opacity .12s ease;
display: inline-flex;
align-items: center;
&:hover {
opacity: .8;
}
&:disabled {
opacity: .6;
cursor: default;
}
>svg {
flex: 0 0 auto;
width: 16px;
height: 16px;
fill: var(--title-color);
}
>span {
flex: 1 1 auto;
margin-left: 4px;
}
}
}
>.ui-sort-panel-grid {
flex: 1 1 auto;
position: relative;
}
}
}
/*@media (prefers-color-scheme: dark) {

14
lib/ui/dropdown.d.ts vendored
View File

@ -5,19 +5,19 @@ export interface DropdownItem {
/** 显示文本 */
text: string;
/** 源码显示内容 */
html?: HTMLElement
html?: HTMLElement | string;
}
/** 下拉框选项接口 */
export interface DropdownOptions {
/** 文本关键字,默认值 text */
/** 文本关键字,默认值 `text` */
textKey?: string;
/** 值关键字,默认值 value */
/** 值关键字,默认值 `value` */
valueKey?: string;
/** 源码显示的关键字,默认值 html */
/** 源码显示的关键字,默认值 `html` */
htmlKey?: string;
/** 最大输入长度,默认值 500 */
maxLength?: Number;
/** 最大输入长度,默认值 `500` */
maxLength?: number;
/** 是否允许多选 */
multiSelect?: boolean;
/** 选中值 */
@ -35,7 +35,7 @@ export interface DropdownOptions {
/** 搜索提示文本,默认值取语言资源 `searchHolder` "Search..." */
searchPlaceholder?: string;
/** 焦点索引 */
tabIndex?: Number;
tabIndex?: number;
/** 输入框的提示文本 */
placeholder?: string;
/** 是否固定为向下展开 */

View File

@ -30,7 +30,7 @@ export interface GridColumnDefinition {
/** 列标题的元素样式 */
captionStyle?: { [key: string]: string };
/** 大于 0 则设置为该宽度,否则根据列内容自动调整列宽 */
width?: Number;
width?: number;
/** 列对齐方式 */
align?: "left" | "center" | "right";
/**
@ -89,6 +89,8 @@ export interface GridColumnDefinition {
dateMin?: string;
/** 列为日期类型时以该值作为最大可选日期值 */
dateMax?: string;
/** 列为日期类型时自定义日期转字符串函数 */
dateValueFormatter?: (date: Date) => string;
/** 以返回值额外设置单元格的tooltip函数上下文为列定义对象 */
tooltip?: string | ((item: GridItem) => string);
@ -109,7 +111,7 @@ export interface GridColumnDefinition {
* @param e
* @eventProperty
*/
onChanged?: (this: Grid, item: GridItem, value: boolean | string | Number, oldValue: boolean | string | Number, e?: any) => void;
onChanged?: (this: Grid, item: GridItem, value: boolean | string | number, oldValue: boolean | string | number, e?: any) => void;
/**
*
* @param this Grid
@ -145,6 +147,8 @@ export interface GridColumnDefinition {
/** 列定义基类 */
export class GridColumn {
/** @ignore */
constructor();
/**
*
* @param col
@ -180,7 +184,7 @@ export class GridColumn {
* @param grid {@linkcode Grid}
* @virtual
*/
static setValue(element: HTMLElement, val: string | boolean | Number, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void;
static setValue(element: HTMLElement, val: string | boolean | number, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void;
/**
*
* @param e {@linkcode createEdit} `trigger`
@ -188,7 +192,7 @@ export class GridColumn {
* @returns
* @virtual
*/
static getValue(e: any, col: GridColumnDefinition): string | boolean | Number;
static getValue(e: any, col: GridColumnDefinition): string | boolean | number;
/**
*
* @param element
@ -336,15 +340,22 @@ export class GridDateColumn extends GridColumn {
*/
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
/**
* @inheritdoc GridColumn.setValue
* <br/><br/>
* <br/><br/>
* `"2024-01-26"`<br/>
* `"1/26/2024"`<br/>
* `"638418240000000000"`<br/>
* `new Date('2024-01-26')`<br/>
* @param element
* @param val
* @override
*/
static setValue(element: HTMLElement, val: string | Number): void;
static setValue(element: HTMLElement, val: string | number): void;
/**
* @inheritdoc GridColumn.getValue
* @override
*/
static getValue(e: any): string | Number;
static getValue(e: any): string | number;
/**
* @inheritdoc GridColumn.setEnabled
* @override

View File

@ -111,7 +111,7 @@ export class GridDropdownColumn extends GridColumn {
wrapper: container.parentElement
});
drop.onSelected = trigger;
drop.onExpanded = function () {
drop.onExpanded = () => {
if (it.__editing == null) {
it.__editing = {
[col.key]: true
@ -161,10 +161,10 @@ export class GridDropdownColumn extends GridColumn {
return source;
}
static _setValue(source, element, val) {
const data = source?.find(v => v.value === val);
static _setValue(source, element, val, opts) {
const data = source?.find(v => v[opts?.valueKey ?? 'value'] === val);
if (data != null) {
val = data.text;
val = data[opts?.textKey ?? 'text'];
}
super.setValue(element, val);
}
@ -173,9 +173,9 @@ export class GridDropdownColumn extends GridColumn {
if (element.tagName !== 'DIV') {
let source = this._getSource(item, col);
if (source instanceof Promise) {
source.then(s => this._setValue(s, element, val));
source.then(s => this._setValue(s, element, val, col.dropOptions));
} else {
this._setValue(source, element, val);
this._setValue(source, element, val, col.dropOptions);
}
return;
}
@ -312,26 +312,35 @@ export class GridDateColumn extends GridColumn {
static setValue(element, val) {
if (element.tagName === 'INPUT') {
if (isNaN(val) || /^\d{4}-\d{2}-\d{2}$/.test(val)) {
element.value = val;
if (isNaN(val)) {
if (/^\d{4}-\d{2}-\d{2}$/.test(val)) {
element.value = val;
} else if (/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(val)) {
element.value = this._toDateValue(new Date(val));
} else {
element.value = '';
}
} else {
val = new Date((val - 621355968e9) / 1e4);
const month = String(val.getMonth() + 1).padStart(2, '0');
const date = String(val.getDate()).padStart(2, '0');
element.value = `${val.getFullYear()}-${month}-${date}`;
if (!(val instanceof Date)) {
val = new Date((val - 621355968e9) / 1e4);
}
element.value = this._toDateValue(val);
}
} else {
element.innerText = this.formatDate(val);
}
}
static getValue(e) {
static getValue(e, col) {
const date = e.target?.valueAsDate;
if (date instanceof Date && !isNaN(date)) {
const year = date.getFullYear();
if (year < 1900 || year > 9999) {
return '';
}
if (typeof col.dateValueFormatter === 'function') {
return col.dateValueFormatter(date);
}
return String(date.getTime() * 1e4 + 621355968e9);
}
return '';
@ -341,6 +350,15 @@ export class GridDateColumn extends GridColumn {
element.disabled = enabled === false;
}
static _toDateValue(dt) {
if (isNaN(dt)) {
return '';
}
const month = String(dt.getMonth() + 1).padStart(2, '0');
const date = String(dt.getDate()).padStart(2, '0');
return `${dt.getFullYear()}-${month}-${date}`;
}
static _resolveDate(s) {
if (s instanceof Date) {
return s;

145
lib/ui/grid/grid.d.ts vendored
View File

@ -1,4 +1,12 @@
import { GridColumnDefinition } from "./column"
/**
*
*
* @callback cellClickedCallback
* @param {number} index -
* @param {number} colIndex -
* @returns {boolean} `false`
*/
/** 列数据接口 */
interface GridItem {
@ -27,17 +35,33 @@ interface GridSourceItem {
/** Grid 语言资源接口 */
interface GridLanguages {
/**
*
*
* @default `( All )`
*/
* `( All )` */
all: string;
/** “确定”文本,默认值 OK */
/** “确定”文本,默认值 `OK` */
ok: string;
/** “重置”文本,默认值 Reset */
/** “重置”文本,默认值 `Reset` */
reset: string;
/** “空”文本,默认值 ( Null ) */
null: string
cancel: string;
/** “空”文本,默认值 `( Null )` */
null: string;
addLevel: string;
deleteLevel: string;
copyLevel: string;
asc: string;
desc: string;
column: string;
order: string;
sort: string;
requirePrompt: string;
duplicatePrompt: string;
}
/** Grid 列排序定义接口 */
interface GridColumnSortDefinition {
/** 排序列的关键字 */
column: string;
/** 升序或降序 */
order: "asc" | "desc";
}
/** 列排序枚举 */
@ -49,7 +73,7 @@ declare enum GridColumnDirection {
}
/** 列事件枚举 */
declare enum GridColumnColumnEvent {
declare enum GridColumnEvent {
/** 重排事件 */
Reorder = "reorder",
/** 宽调整事件 */
@ -80,41 +104,43 @@ export class Grid {
*
* @param type
*/
isCheckbox(type: Number): boolean;
isCheckbox(type: number): boolean;
};
/** 列定义的数组 */
columns: Array<GridColumnDefinition>;
/** 多语言资源对象 */
langs?: GridLanguages;
/** 行数大于等于该值则启用虚模式,默认值 100 */
virtualCount?: Number;
/** 表格行高,默认值 36 */
rowHeight?: Number;
/** 文本行高,默认值 24 */
lineHeight?: Number;
/** 列表底部留出额外行的空白,默认值 0 */
extraRows?: Number;
/** 过滤条件列表的行高,默认值 30 */
filterRowHeight?: Number;
/** 列表高度值,为 0 时列表始终显示全部内容(自增高),为非数字或者小于 0 则根据容器高度来确定虚模式的渲染行数,默认值 null */
height?: Number;
/** 列表是否为只读,默认值 false */
/** 行数大于等于该值则启用虚模式,默认值 `100` */
virtualCount?: number;
/** 表格行高,默认值 `36` */
rowHeight?: number;
/** 文本行高,默认值 `24` */
lineHeight?: number;
/** 列表底部留出额外行的空白,默认值 `0` */
extraRows?: number;
/** 过滤条件列表的行高,默认值 `30` */
filterRowHeight?: number;
/** 列表高度值,为 0 时列表始终显示全部内容(自增高),为非数字或者小于 0 则根据容器高度来确定虚模式的渲染行数,默认值 `null` */
height?: number;
/** 列表是否为只读,默认值 `false` */
readonly?: boolean;
/** 是否允许多选,默认值 false */
/** 是否允许多选,默认值 `false` */
multiSelect?: boolean;
/** 为 false 时只有点击在单元格内才会选中行,默认值 true */
/** 为 false 时只有点击在单元格内才会选中行,默认值 `true` */
fullrowClick?: boolean;
/** 单元格 tooltip 是否禁用,默认值 false */
/** 单元格 tooltip 是否禁用,默认值 `false` */
tooltipDisabled?: boolean;
/** 列头是否显示,默认值 true */
/** 列头是否显示,默认值 `true` */
headerVisible?: boolean;
/** 监听事件的窗口载体,默认值 window */
/** 监听事件的窗口载体,默认值 `window` */
window?: Window
/** 排序列的索引,默认值 -1 */
sortIndex?: Number;
/** 排序方式,正数升序,负数倒序,默认值 1 */
/** 排序列的索引,默认值 `-1` */
sortIndex?: number;
/** 排序方式,正数升序,负数倒序,默认值 `1` */
sortDirection?: GridColumnDirection;
/** 排序列 */
sortArray?: Array<GridColumnSortDefinition>;
/**
* Grid
@ -130,34 +156,33 @@ export class Grid {
* @param colIndex
* @eventProperty
*/
willSelect?: (index: Number, colIndex: Number) => boolean;
willSelect?: (index: number, colIndex: number) => boolean;
/**
* colIndex -1 false
* @param index
* @param colIndex
* @property {cellClickedCallback}
* @eventProperty
*/
cellClicked?: (index: Number, colIndex: Number) => boolean;
cellClicked?: (index: number, colIndex: number) => boolean;
/**
*
* @param index
* @eventProperty
*/
onSelectedRowChanged?: (index?: Number) => void;
onSelectedRowChanged?: (index?: number) => void;
/**
* colIndex -1
* @param index
* @param colIndex
* @eventProperty
*/
onCellDblClicked?: (index: Number, colIndex: Number) => void;
onCellDblClicked?: (index: number, colIndex: number) => void;
/**
*
* @param index
* @eventProperty
*/
onRowDblClicked?: (index: Number) => void;
onRowDblClicked?: (index: number) => void;
/**
*
* @param type <br/><br/>
@ -168,31 +193,39 @@ export class Grid {
* @param value
* @eventProperty
*/
onColumnChanged?: (type: GridColumnColumnEvent, colIndex: Number, value: Number | GridColumnDirection) => void;
onColumnChanged?: (type: GridColumnEvent, colIndex: number, value: number | GridColumnDirection) => void;
/**
*
* @param e
* @eventProperty
*/
onBodyScrolled?: (e: Event) => void;
/**
*
* @param array
* @eventProperty
*/
onSorted?: (array?: Array<GridColumnSortDefinition>) => void;
/** 获取数据数组 */
get source(): GridItem;
get source(): Array<GridItem>;
/** 设置数据,并刷新列表 */
set source(list: Array<GridItem>);
/** 获取当前选中的行索引的数组 */
get selectedIndexes(): Array<Number>;
get selectedIndexes(): Array<number>;
/** 设置当前选中的行索引的数组,并刷新列表 */
set selectedIndexes(indexes: Array<Number>);
set selectedIndexes(indexes: Array<number>);
/** 获取 Grid 当前是否处于加载状态 */
get loading(): boolean;
/** 使 Grid 进入加载状态 */
set loading(flag: boolean);
/** 获取 Grid 当前滚动的偏移量 */
get scrollTop(): Number;
get scrollTop(): number;
/** 设置 Grid 滚动偏移量 */
set scrollTop(top: Number);
set scrollTop(top: number);
/** 获取已过滤后的当前列表中的数据数组 */
get sourceFiltered(): Array<GridItem>;
/** 获取 Grid 的页面元素 */
get element(): HTMLElement;
/** 获取当前 Grid 是否已发生改变 */
@ -202,7 +235,7 @@ export class Grid {
/** 获取当前排序的列关键字,为 null 则当前无排序列 */
get sortKey(): string | undefined;
/** 获取当前选中行的索引,为 -1 则当前没有选中行 */
get selectedIndex(): Number | -1;
get selectedIndex(): number | -1;
/**
* Grid控件
@ -219,26 +252,27 @@ export class Grid {
* @param index
* @param item
*/
setItem(index: Number, item: GridItem): void;
setItem(index: number, item: GridItem): void;
/**
*
* @param item
* @param index
*/
addItem(item: GridItem, index?: Number): void;
addItem(item: GridItem, index?: number): void;
/**
*
* @param index
* @returns
*/
removeItem(index: Number): void;
removeItem(index: number): GridItem;
/**
*
* @param index
*/
scrollToIndex(index: Number): void;
scrollToIndex(index: number): void;
/**
* Grid
* @param force reloadreload
* @param force {@linkcode reload}
* @param keep
*/
resize(force?: boolean, keep?: boolean): void;
@ -257,11 +291,20 @@ export class Grid {
resetChange(): void;
/**
*
* @param reload true reload
* @param reload true {@linkcode Grid.reload}
*/
sortColumn(reload?: boolean): void;
/**
*
* @param reload true {@linkcode Grid.reload}
*/
sort(reload?: boolean): void;
/**
*
*/
clearHeaderCheckbox(): void;
/**
*
*/
showSortPanel(): void;
}

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 {