fixed: column drag issue.

optimized: documentation.
This commit is contained in:
Chen Lily 2024-01-26 14:09:52 +08:00
parent 0b9b322b80
commit 984496e08e
5 changed files with 313 additions and 78 deletions

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

@ -32,7 +32,7 @@ export interface DropdownOptions {
search?: boolean; search?: boolean;
/** 搜索的关键字数组 */ /** 搜索的关键字数组 */
searchKeys?: Array<string>; searchKeys?: Array<string>;
/** 搜索提示文本,默认值取语言资源 <code>searchHolder</code> "Search..." */ /** 搜索提示文本,默认值取语言资源 `searchHolder` "Search..." */
searchPlaceholder?: string; searchPlaceholder?: string;
/** 焦点索引 */ /** 焦点索引 */
tabIndex?: Number; tabIndex?: Number;
@ -47,7 +47,7 @@ export interface DropdownOptions {
/** 下拉框类 */ /** 下拉框类 */
export class Dropdown { export class Dropdown {
/** /**
* <code>select</code> * `select`
* @param dom * @param dom
* @returns * @returns
*/ */
@ -64,16 +64,24 @@ export class Dropdown {
/** /**
* *
* @param item * @param item
* @eventProperty
*/ */
onSelected: (item: DropdownItem) => void; onSelected: (item: DropdownItem) => void;
/** /**
* *
* @param list * @param list
* @eventProperty
*/ */
onSelectedList: (list: Array<DropdownItem>) => void; onSelectedList: (list: Array<DropdownItem>) => void;
/** 下拉框展开时触发 */ /**
*
* @eventProperty
*/
onExpanded: () => void; onExpanded: () => void;
/** 下拉框收缩时触发 */ /**
*
* @eventProperty
*/
onCollapsed: () => void; onCollapsed: () => void;
/** 获取下拉框是否禁用 */ /** 获取下拉框是否禁用 */

View File

@ -35,13 +35,18 @@ export interface GridColumnDefinition {
align?: "left" | "center" | "right"; align?: "left" | "center" | "right";
/** /**
* <br/><br/> * <br/><br/>
* <code>boolean</code> 使<br/><br/> * `boolean` 使<br/><br/>
* <code>string</code> <br/><br/> * `string` <br/><br/>
* <code>(item: GridItem) => boolean</code> <br/><br/> * `(item: GridItem) => boolean` <br/><br/>
*/ */
enabled?: boolean | string | ((item: GridItem) => boolean); enabled?: boolean | string | ((item: GridItem) => boolean);
/** 单元格取值采用该方法返回的值 */ /**
filter?: (item: GridItem) => any; *
* @param item
* @param editing
* @param body Grid `&lt;tbody&gt;`
*/
filter?: (item: GridItem, editing: boolean, body?: HTMLElement) => any;
/** 单元格以该值填充内容忽略filter与关键字属性 */ /** 单元格以该值填充内容忽略filter与关键字属性 */
text?: string; text?: string;
/** 列是否可见 */ /** 列是否可见 */
@ -76,8 +81,8 @@ export interface GridColumnDefinition {
source?: Array<GridSourceItem> | ((item: GridItem) => Array<GridSourceItem> | Promise<Array<GridSourceItem>>); source?: Array<GridSourceItem> | ((item: GridItem) => Array<GridSourceItem> | Promise<Array<GridSourceItem>>);
/** 下拉列表数据源是否缓存结果即行数据未发生变化时仅从source属性获取一次值 */ /** 下拉列表数据源是否缓存结果即行数据未发生变化时仅从source属性获取一次值 */
sourceCache?: boolean; sourceCache?: boolean;
/** 列为图标类型时以该值设置图标样式(函数上下文为列定义对象),允许值为 <code>fa-light</code>、<code>fa-regular</code>、<code>fa-solid</code> */ /** 列为图标类型时以该值设置图标样式(函数上下文为列定义对象),默认值 `fa-light` */
iconType?: string; iconType?: "fa-light" | "fa-regular" | "fa-solid";
/** 列为图标类型时以该值作为单元格元素的额外样式类型(函数上下文为列定义对象) */ /** 列为图标类型时以该值作为单元格元素的额外样式类型(函数上下文为列定义对象) */
iconClassName?: string | ((item: GridItem) => string); iconClassName?: string | ((item: GridItem) => string);
/** 列为日期类型时以该值作为最小可选日期值 */ /** 列为日期类型时以该值作为最小可选日期值 */
@ -92,6 +97,7 @@ export interface GridColumnDefinition {
* @param this Grid * @param this Grid
* @param col * @param col
* @param flag * @param flag
* @eventProperty
*/ */
onAllChecked?: (this: Grid, col: GridColumnDefinition, flag: boolean) => void; onAllChecked?: (this: Grid, col: GridColumnDefinition, flag: boolean) => void;
/** /**
@ -101,6 +107,7 @@ export interface GridColumnDefinition {
* @param value * @param value
* @param oldValue * @param oldValue
* @param e * @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;
/** /**
@ -108,6 +115,7 @@ export interface GridColumnDefinition {
* @param this Grid * @param this Grid
* @param item * @param item
* @param value * @param value
* @eventProperty
*/ */
onInputEnded?: (this: Grid, item: GridItem, value: string) => void; onInputEnded?: (this: Grid, item: GridItem, value: string) => void;
/** /**
@ -115,12 +123,14 @@ export interface GridColumnDefinition {
* @param this Grid * @param this Grid
* @param col * @param col
* @param selected * @param selected
* @eventProperty
*/ */
onFilterOk?: (this: Grid, col: GridColumnDefinition, selected: Array<GridItem>) => void; onFilterOk?: (this: Grid, col: GridColumnDefinition, selected: Array<GridItem>) => void;
/** /**
* *
* @param this Grid * @param this Grid
* @param col * @param col
* @eventProperty
*/ */
onFiltered?: (this: Grid, col: GridColumnDefinition) => void; onFiltered?: (this: Grid, col: GridColumnDefinition) => void;
/** /**
@ -128,6 +138,7 @@ export interface GridColumnDefinition {
* @param this * @param this
* @param item * @param item
* @param drop * @param drop
* @eventProperty
*/ */
onDropExpanded?: (this: GridColumnDefinition, item: GridItem, drop: Dropdown) => void; onDropExpanded?: (this: GridColumnDefinition, item: GridItem, drop: Dropdown) => void;
} }
@ -138,19 +149,28 @@ export class GridColumn {
* *
* @param col * @param col
* @returns * @returns
* @virtual
*/ */
static create(col: GridColumnDefinition): HTMLElement; static create(col: GridColumnDefinition): HTMLElement;
/** /**
* <br/><br/> * <br/><br/>
* <code>__editing</code> {@linkcode leaveEdit} <br/> * `__editing` {@linkcode leaveEdit} <br/>
* {@linkcode GridDropdownColumn} * {@linkcode GridDropdownColumn}
* @param trigger e {@linkcode getValue} * @param trigger e {@linkcode getValue}
* @param col * @param col
* @param container * @param container
* @param vals <code>values</code> * @param vals `values`
* @returns * @returns
* @virtual
*/ */
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement; static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
/**
*
* @param col
* @returns
* @virtual
*/
static createCaption?(col: GridColumnDefinition): HTMLElement;
/** /**
* *
* @param element * @param element
@ -158,59 +178,178 @@ export class GridColumn {
* @param vals * @param vals
* @param col * @param col
* @param grid {@linkcode Grid} * @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} <code>trigger</code> * @param e {@linkcode createEdit} `trigger`
* @param col * @param col
* @returns * @returns
* @virtual
*/ */
static getValue(e: any, col: GridColumnDefinition): string | boolean | Number; static getValue(e: any, col: GridColumnDefinition): string | boolean | Number;
/** /**
* *
* @param element * @param element
* @param style * @param style
* @virtual
*/ */
static setStyle(element: HTMLElement, style: { [key: string]: string }): void; static setStyle(element: HTMLElement, style: { [key: string]: string }): void;
/** /**
* *
* @param element * @param element
* @param enabled false时代表禁用 * @param enabled false时代表禁用
* @virtual
*/ */
static setEnabled(element: HTMLElement, enabled?: boolean): void; static setEnabled(element: HTMLElement, enabled?: boolean): void;
/** /**
* <code>__editing</code> * `__editing`
* @param element * @param element
* @param container * @param container
* @virtual
*/ */
static leaveEdit(element: HTMLElement, container: HTMLElement): void; static leaveEdit?(element: HTMLElement, container: HTMLElement): void;
} }
/** 单行文本列 */ /** 单行文本列 */
export class GridInputColumn extends GridColumn { export class GridInputColumn extends GridColumn {
/** /**
* {@linkcode GridColumnDefinition.onInputEnded} <br/> * {@linkcode GridColumnDefinition.onInputEnded} <br/>
* <code>true</code> <code>__editing</code> {@linkcode GridColumnDefinition.onInputEnded} <code>onchange</code> <br/> * `true` `__editing` {@linkcode GridColumnDefinition.onInputEnded} `onchange` <br/>
* {@linkcode GridInputColumn} * {@linkcode GridInputColumn}
*/ */
static get editing(): boolean; static get editing(): boolean;
/**
* @inheritdoc GridColumn.createEdit
* @override
*/
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
/**
* @inheritdoc GridColumn.setValue
* @override
*/
static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void;
/**
* @inheritdoc GridColumn.getValue
* @override
*/
static getValue(e: any): string;
/**
* @inheritdoc GridColumn.setEnabled
* @override
*/
static setEnabled(element: HTMLElement, enabled?: boolean): void;
} }
/** 多行文本列 */ /** 多行文本列 */
export class GridTextColumn extends GridInputColumn { } export class GridTextColumn extends GridInputColumn {
/**
* @inheritdoc GridInputColumn.createEdit
* @override
*/
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
/**
* @inheritdoc GridInputColumn.setValue
* @override
*/
static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition, grid: Grid): void;
}
/** 下拉选择列 */ /** 下拉选择列 */
export class GridDropdownColumn extends GridColumn { } export class GridDropdownColumn extends GridColumn {
/**
* @inheritdoc GridColumn.createEdit
* @override
*/
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
/**
* @inheritdoc GridColumn.setValue
* @override
*/
static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition): void;
/**
* @inheritdoc GridColumn.getValue
* @override
*/
static getValue(e: any, col: GridColumnDefinition): string;
/**
* @inheritdoc GridColumn.setEnabled
* @override
*/
static setEnabled(element: HTMLElement, enabled?: boolean): void;
/**
* @inheritdoc GridColumn.leaveEdit
* @override
*/
static leaveEdit?(element: HTMLElement, container: HTMLElement): void;
}
/** 复选框列 */ /** 复选框列 */
export class GridCheckboxColumn extends GridColumn { } export class GridCheckboxColumn extends GridColumn {
/**
* @inheritdoc GridColumn.createEdit
* @override
*/
static createEdit(trigger: (e: any) => void): HTMLElement;
/**
* @inheritdoc GridColumn.setValue
* @override
*/
static setValue(element: HTMLElement, val: boolean): void;
/**
* @inheritdoc GridColumn.getValue
* @override
*/
static getValue(e: any): boolean;
/**
* @inheritdoc GridColumn.setEnabled
* @override
*/
static setEnabled(element: HTMLElement, enabled?: boolean): void;
}
/** 图标列 */ /** 图标列 */
export class GridIconColumn extends GridColumn { } export class GridIconColumn extends GridColumn {
/**
* @inheritdoc GridColumn.create
* @override
*/
static create(): HTMLElement;
/**
* @inheritdoc GridColumn.setValue
* @override
*/
static setValue(element: HTMLElement, val: string, vals: GridItemWrapper, col: GridColumnDefinition): void;
/**
* @inheritdoc GridColumn.setEnabled
* @override
*/
static setEnabled(element: HTMLElement, enabled?: boolean): void;
}
/** 日期选择列 */ /** 日期选择列 */
export class GridDateColumn extends GridColumn { export class GridDateColumn extends GridColumn {
/**
* @inheritdoc GridColumn.createEdit
* @override
*/
static createEdit(trigger: (e: any) => void, col: GridColumnDefinition, container: HTMLElement, vals: GridItemWrapper): HTMLElement;
/**
* @inheritdoc GridColumn.setValue
* @override
*/
static setValue(element: HTMLElement, val: string | Number): void;
/**
* @inheritdoc GridColumn.getValue
* @override
*/
static getValue(e: any): string | Number;
/**
* @inheritdoc GridColumn.setEnabled
* @override
*/
static setEnabled(element: HTMLElement, enabled?: boolean): void;
/** /**
* M/d/yyyy * M/d/yyyy
* @param date * @param date

View File

@ -250,7 +250,7 @@ export class GridCheckboxColumn extends GridColumn {
export class GridIconColumn extends GridColumn { export class GridIconColumn extends GridColumn {
static create() { return createElement('span', 'col-icon') } static create() { return createElement('span', 'col-icon') }
static setValue(element, val, item, col, _grid) { static setValue(element, val, item, col) {
let className = col.iconClassName; let className = col.iconClassName;
if (typeof className === 'function') { if (typeof className === 'function') {
className = className.call(col, item.values); className = className.call(col, item.values);
@ -264,7 +264,7 @@ export class GridIconColumn extends GridColumn {
if (typeof type === 'function') { if (typeof type === 'function') {
type = type.call(col, item.values); type = type.call(col, item.values);
} }
type ??= 'fa-regular'; type ??= 'fa-light';
if (element.dataset.type !== type || element.dataset.icon !== val) { if (element.dataset.type !== type || element.dataset.icon !== val) {
const icon = createIcon(type, val); const icon = createIcon(type, val);
// const layer = element.children[0]; // const layer = element.children[0];

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

@ -24,6 +24,22 @@ interface GridSourceItem {
text: string; text: string;
} }
/** Grid 语言资源接口 */
interface GridLanguages {
/**
*
*
* @default `( All )`
*/
all: string;
/** “确定”文本,默认值 OK */
ok: string;
/** “重置”文本,默认值 Reset */
reset: string;
/** “空”文本,默认值 ( Null ) */
null: string
}
/** 列排序枚举 */ /** 列排序枚举 */
declare enum GridColumnDirection { declare enum GridColumnDirection {
/** 倒序 */ /** 倒序 */
@ -70,7 +86,7 @@ export class Grid {
/** 列定义的数组 */ /** 列定义的数组 */
columns: Array<GridColumnDefinition>; columns: Array<GridColumnDefinition>;
/** 多语言资源对象 */ /** 多语言资源对象 */
langs?: { all: string, ok: string, reset: string }; langs?: GridLanguages;
/** 行数大于等于该值则启用虚模式,默认值 100 */ /** 行数大于等于该值则启用虚模式,默认值 100 */
virtualCount?: Number; virtualCount?: Number;
/** 表格行高,默认值 36 */ /** 表格行高,默认值 36 */
@ -112,29 +128,34 @@ export class Grid {
* falsenullundefined0 * falsenullundefined0
* @param index * @param index
* @param colIndex * @param colIndex
* @eventProperty
*/ */
willSelect?: (index: Number, colIndex: Number) => boolean; willSelect?: (index: Number, colIndex: Number) => boolean;
/** /**
* colIndex -1 false * colIndex -1 false
* @param index * @param index
* @param colIndex * @param colIndex
* @eventProperty
*/ */
cellClicked?: (index: Number, colIndex: Number) => boolean; cellClicked?: (index: Number, colIndex: Number) => boolean;
/** /**
* *
* @param index * @param index
* @eventProperty
*/ */
onSelectedRowChanged?: (index?: Number) => void; onSelectedRowChanged?: (index?: Number) => void;
/** /**
* colIndex -1 * colIndex -1
* @param index * @param index
* @param colIndex * @param colIndex
* @eventProperty
*/ */
onCellDblClicked?: (index: Number, colIndex: Number) => void; onCellDblClicked?: (index: Number, colIndex: Number) => void;
/** /**
* *
* @param index * @param index
* @eventProperty
*/ */
onRowDblClicked?: (index: Number) => void; onRowDblClicked?: (index: Number) => void;
/** /**
@ -145,11 +166,13 @@ export class Grid {
* "sort" value 1 -1 * "sort" value 1 -1
* @param colIndex * @param colIndex
* @param value * @param value
* @eventProperty
*/ */
onColumnChanged?: (type: GridColumnColumnEvent, colIndex: Number, value: Number | GridColumnDirection) => void; onColumnChanged?: (type: GridColumnColumnEvent, colIndex: Number, value: Number | GridColumnDirection) => void;
/** /**
* *
* @param e * @param e
* @eventProperty
*/ */
onBodyScrolled?: (e: Event) => void; onBodyScrolled?: (e: Event) => void;

View File

@ -17,7 +17,7 @@ const ColumnChangedType = {
const RefreshInterval = isMobile() ? 32 : 0; const RefreshInterval = isMobile() ? 32 : 0;
const HoverInternal = 200; const HoverInternal = 200;
const RedumCount = 4; const RedumCount = 4;
const MiniDragOffset = 4; const MiniDragOffset = 10;
const MiniColumnWidth = 50; const MiniColumnWidth = 50;
const FilterPanelWidth = 200; const FilterPanelWidth = 200;
@ -29,15 +29,6 @@ function getClientX(e) {
return cx ?? e.clientX; return cx ?? e.clientX;
} }
function getOffsetLeftFromWindow(element) {
let left = 0;
while (element != null) {
left += element.offsetLeft;
element = element.offsetParent;
}
return left;
}
function indexOfParent(target) { function indexOfParent(target) {
// return [...target.parentElement.children].indexOf(target); // return [...target.parentElement.children].indexOf(target);
return Array.prototype.indexOf.call(target.parentElement.children, target); return Array.prototype.indexOf.call(target.parentElement.children, target);
@ -127,7 +118,8 @@ export class Grid {
this.langs = { this.langs = {
all: r('allItem', '( All )'), all: r('allItem', '( All )'),
ok: r('ok', 'OK'), ok: r('ok', 'OK'),
reset: r('reset', 'Reset') reset: r('reset', 'Reset'),
null: r('null', '( Null )')
}; };
} }
@ -190,7 +182,7 @@ export class Grid {
this._var.currentSource = list.filter(it => { this._var.currentSource = list.filter(it => {
for (let col of this.columns) { for (let col of this.columns) {
if (Array.isArray(col.filterValues)) { if (Array.isArray(col.filterValues)) {
const v = this._getItemValue(it.values, col.key, col.filter); const v = this._getItemProp(it.values, false, col);
if (col.filterValues.indexOf(v) < 0) { if (col.filterValues.indexOf(v) < 0) {
return false; return false;
} }
@ -485,8 +477,8 @@ export class Grid {
direction = 1; direction = 1;
} }
comparer = (a, b) => { comparer = (a, b) => {
a = this._getItemValue(a.values, col.key, col.filter); a = this._getItemProp(a.values, true, col);
b = this._getItemValue(b.values, col.key, col.filter); b = this._getItemProp(b.values, true, col);
if (a == null && typeof b === 'number') { if (a == null && typeof b === 'number') {
a = 0; a = 0;
} else if (typeof a === 'number' && b == null) { } else if (typeof a === 'number' && b == null) {
@ -533,7 +525,7 @@ export class Grid {
let left = 0; let left = 0;
for (let col of this.columns) { for (let col of this.columns) {
if (col.visible === false) { if (col.visible === false) {
const hidden = createElement('th'); const hidden = createElement('th', 'column');
hidden.style.display = 'none'; hidden.style.display = 'none';
if (col.sortable !== false) { if (col.sortable !== false) {
hidden.dataset.key = col.key; hidden.dataset.key = col.key;
@ -544,7 +536,17 @@ export class Grid {
} }
// style // style
const isCheckbox = Grid.ColumnTypes.isCheckbox(col.type); const isCheckbox = Grid.ColumnTypes.isCheckbox(col.type);
if (col.width > 0) { let type = this._var.colTypes[col.key];
if (type == null) {
if (isNaN(col.type)) {
type = col.type;
} else {
type = ColumnTypes[col.type];
}
type ??= GridColumn;
this._var.colTypes[col.key] = type;
}
if (col.width > 0 || typeof type.createCaption === 'function') {
// col.autoResize = false; // col.autoResize = false;
} else { } else {
this._set(col.key, 'autoResize', true); this._set(col.key, 'autoResize', true);
@ -600,12 +602,19 @@ export class Grid {
}); });
wrapper.appendChild(check); wrapper.appendChild(check);
} }
const caption = createElement('span'); let caption;
if (typeof type.createCaption === 'function') {
caption = type.createCaption(col);
} else {
caption = createElement('span');
caption.innerText = col.caption ?? '';
}
if (caption instanceof HTMLElement) {
if (col.captionStyle != null) { if (col.captionStyle != null) {
caption.style.cssText = convertCssStyle(col.captionStyle); caption.style.cssText = convertCssStyle(col.captionStyle);
} }
caption.innerText = col.caption ?? '';
wrapper.appendChild(caption); wrapper.appendChild(caption);
}
// order arrow // order arrow
if (col.sortable) { if (col.sortable) {
th.appendChild(createElement('layer', 'arrow')); th.appendChild(createElement('layer', 'arrow'));
@ -906,13 +915,15 @@ export class Grid {
} }
} }
_changingColumnOrder(index, offset, x, offsetLeft, scrollLeft) { _changingColumnOrder(index, offset, mouse, draggerCellLeft) {
const children = this._var.refs.header.children; const children = this._var.refs.header.children;
let element = children[index]; let element = children[index];
this._var.refs.dragger.style.cssText = `left: ${element.offsetLeft - offsetLeft + offset}px; width: ${element.style.width}; display: block`; this._var.refs.dragger.style.cssText = `left: ${element.offsetLeft - draggerCellLeft + offset}px; width: ${element.style.width}; display: block`;
offset = x + scrollLeft - element.offsetLeft; // getOffsetLeftFromWindow(element); // offset = x + gridScrollLeft - element.offsetLeft; // getOffsetLeftFromWindow(element);
offset += mouse;
let idx; let idx;
if (offset < 0) { const toLeft = offset < 0;
if (toLeft) {
offset = -offset; offset = -offset;
for (let i = index - 1; i >= 0 && offset >= 0; i -= 1) { for (let i = index - 1; i >= 0 && offset >= 0; i -= 1) {
element = children[i]; element = children[i];
@ -943,13 +954,27 @@ export class Grid {
} }
idx ??= count - 1; idx ??= count - 1;
} }
if (idx !== this._var.colAttrs.__orderIndex) { if (idx !== this._var.colAttrs.__orderIndex || this._var.refs.draggerCursor.style.display !== 'block') {
this._var.colAttrs.__orderIndex = idx;
element = children[idx]; element = children[idx];
if (element == null) { if (element == null) {
return; return;
} }
this._var.refs.draggerCursor.style.cssText = `left: ${element.offsetLeft - offsetLeft}px; display: block`; this._var.colAttrs.__orderIndex = idx;
// avoid `offsetLeft` of hidden header to be 0
let left;
if (element.style.display === 'none') {
left = 0;
while (left === 0 && (element = children[++idx]) != null) {
left = element.offsetLeft;
}
if (!toLeft && left === 0) {
left = draggerCellLeft;
}
} else {
left = element.offsetLeft;
}
// set position of dragger cursor
this._var.refs.draggerCursor.style.cssText = `left: ${left - draggerCellLeft}px; display: block`;
} }
} }
@ -1057,14 +1082,21 @@ export class Grid {
} }
} }
_getItemValue(item, key, filter) { _getItemProp(item, editing, col) {
let value; let value;
if (typeof filter === 'function') { if (typeof col?.filter === 'function') {
value = filter(item, false, this._var.refs.body); value = col.filter(item, editing, this._var.refs.body);
} else { } else {
value = item[key]; value = item[col.key];
} }
return value?.Value ?? value; if (value == null) {
return value;
}
const prop = editing ? 'Value' : 'DisplayValue';
if (Object.prototype.hasOwnProperty.call(value, prop)) {
return value[prop];
}
return value;
} }
_getRowTarget(target) { _getRowTarget(target) {
@ -1174,19 +1206,35 @@ export class Grid {
} else { } else {
const dict = Object.create(null); const dict = Object.create(null);
for (let item of this._var.source) { for (let item of this._var.source) {
const val = this._getItemValue(item.values, col.key, col.filter); let displayValue = this._getItemProp(item.values, false, col);
if (!Object.hasOwnProperty.call(dict, val)) { if (displayValue == null) {
const v = item.values[col.key]; displayValue = this.langs.null;
dict[val] = { }
if (!Object.hasOwnProperty.call(dict, displayValue)) {
const val = this._getItemProp(item.values, true, col);
dict[displayValue] = {
Value: val, Value: val,
DisplayValue: typeof col.filter === 'function' ? col.filter(item.values) : v?.DisplayValue ?? v DisplayValue: displayValue
}; };
} }
} }
array = Object.values(dict) array = Object.values(dict)
.sort((a, b) => { .sort((a, b) => {
a = a?.Value ?? a; if (a == null && b == null) {
b = b?.Value ?? b; return 0;
}
if (a == null && b != null) {
return -1;
}
if (a != null && b == null) {
return 1;
}
if (Object.prototype.hasOwnProperty.call(a, 'Value')) {
a = a.Value;
}
if (Object.prototype.hasOwnProperty.call(b, 'Value')) {
b = b.Value;
}
return a > b ? 1 : a < b ? -1 : 0; return a > b ? 1 : a < b ? -1 : 0;
}); });
} }
@ -1197,7 +1245,7 @@ export class Grid {
} }
return { return {
Value: i, Value: i,
DisplayValue: i == null ? '' : i DisplayValue: i == null ? this.langs.null : i
}; };
}); });
this._fillFilterList(col, itemlist, array, itemall); this._fillFilterList(col, itemlist, array, itemall);
@ -1207,8 +1255,16 @@ export class Grid {
searchbox.addEventListener('input', e => { searchbox.addEventListener('input', e => {
const key = e.currentTarget.value.toLowerCase(); const key = e.currentTarget.value.toLowerCase();
const items = key.length === 0 ? array : array.filter(i => { const items = key.length === 0 ? array : array.filter(i => {
const displayValue = i?.DisplayValue ?? i; let displayValue;
return String(displayValue ?? '').toLowerCase().includes(key); if (i != null && Object.prototype.hasOwnProperty.call(i, 'DisplayValue')) {
displayValue = i.DisplayValue;
} else {
displayValue = i;
}
if (displayValue == null) {
displayValue = this.langs.null;
}
return String(displayValue).toLowerCase().includes(key);
}); });
this._fillFilterList(col, itemlist, items, itemall); this._fillFilterList(col, itemlist, items, itemall);
}); });
@ -1223,7 +1279,7 @@ export class Grid {
if (typeof col.onFilterOk === 'function') { if (typeof col.onFilterOk === 'function') {
col.onFilterOk.call(this, col, array); col.onFilterOk.call(this, col, array);
} else { } else {
col.filterValues = array.map(a => a.Value); col.filterValues = array.map(a => a.DisplayValue);
} }
this._var.colAttrs.__filtered = true; this._var.colAttrs.__filtered = true;
this._refreshSource(); this._refreshSource();
@ -1268,7 +1324,8 @@ export class Grid {
content.style.top = `${rowHeight}px`; content.style.top = `${rowHeight}px`;
this._set(col.key, 'filterSource', array); this._set(col.key, 'filterSource', array);
for (let item of array) { for (let item of array) {
item.__checked = !Array.isArray(col.filterValues) || col.filterValues.includes(item.Value ?? item); const v = Object.prototype.hasOwnProperty.call(item, 'DisplayValue') ? item.DisplayValue : item;
item.__checked = !Array.isArray(col.filterValues) || col.filterValues.includes(v);
} }
if (array.length > 12) { if (array.length > 12) {
array = array.slice(0, 12); array = array.slice(0, 12);
@ -1282,7 +1339,7 @@ export class Grid {
const div = createElement('div', 'filter-item'); const div = createElement('div', 'filter-item');
div.appendChild(createCheckbox({ div.appendChild(createCheckbox({
checked: item.__checked, checked: item.__checked,
label: item?.DisplayValue ?? item, label: Object.prototype.hasOwnProperty.call(item, 'DisplayValue') ? item.DisplayValue : item,
onchange: e => { onchange: e => {
item.__checked = e.target.checked; item.__checked = e.target.checked;
all.querySelector('input').checked = ![...content.querySelectorAll('input')].some(i => !i.checked); all.querySelector('input').checked = ![...content.querySelectorAll('input')].some(i => !i.checked);
@ -1333,7 +1390,7 @@ export class Grid {
const cx = getClientX(e); const cx = getClientX(e);
const clearEvents = attr => { const clearEvents = attr => {
for (let event of ['mousemove', 'mouseup']) { for (let event of ['mousemove', 'mouseup']) {
if (attr.hasOwnProperty(event)) { if (Object.prototype.hasOwnProperty.call(attr, event)) {
window.removeEventListener(event, attr[event]); window.removeEventListener(event, attr[event]);
delete attr[event]; delete attr[event];
} }
@ -1346,20 +1403,28 @@ export class Grid {
clearEvents(attr); clearEvents(attr);
} }
attr.dragging = true; attr.dragging = true;
const offsetLeft = this._var.refs.header.querySelector('th:last-child').offsetLeft; const draggerCellLeft = this._var.refs.header.querySelector('th:last-child').offsetLeft;
const scrollLeft = this._var.el.scrollLeft; // const gridScrollLeft = this._var.el.scrollLeft;
let p = this._var.el;
let gridLeftFromWindow = p.offsetLeft;
while ((p = p.offsetParent) != null) {
gridLeftFromWindow += p.offsetLeft + p.clientLeft;
}
const mouse = cx - e.currentTarget.offsetLeft + this._var.el.scrollLeft - gridLeftFromWindow;
const dragmove = e => { const dragmove = e => {
const cx2 = getClientX(e); const cx2 = getClientX(e);
const offset = cx2 - cx; const offset = cx2 - cx;
let pos = attr.offset; let pos = attr.offset;
let dragging; let dragging;
if (pos == null && (offset > MiniDragOffset || offset < -MiniDragOffset)) { if (pos == null) {
if (offset > MiniDragOffset || offset < -MiniDragOffset) {
dragging = true; dragging = true;
}
} else if (pos !== offset) { } else if (pos !== offset) {
dragging = true; dragging = true;
} }
if (dragging) { if (dragging) {
this._changingColumnOrder(index, offset, cx2, offsetLeft, scrollLeft); this._changingColumnOrder(index, offset, mouse, draggerCellLeft);
attr.offset = offset; attr.offset = offset;
} }
}; };
@ -1386,7 +1451,7 @@ export class Grid {
const window = this.window ?? global; const window = this.window ?? global;
const clearEvents = attr => { const clearEvents = attr => {
for (let event of ['mousemove', 'mouseup']) { for (let event of ['mousemove', 'mouseup']) {
if (attr.hasOwnProperty(event)) { if (Object.prototype.hasOwnProperty.call(attr, event)) {
window.removeEventListener(event, attr[event]); window.removeEventListener(event, attr[event]);
delete attr[event]; delete attr[event];
} }
@ -1648,7 +1713,7 @@ export class Grid {
if (enabled !== false) { if (enabled !== false) {
const val = item[col.key]; const val = item[col.key];
let oldValue; let oldValue;
if (val?.Value != null) { if (val != null && Object.prototype.hasOwnProperty.call(val, 'Value') != null) {
oldValue = val.Value; oldValue = val.Value;
val.Value = value; val.Value = value;
} else { } else {