start grid
This commit is contained in:
parent
4e230ed7e7
commit
c44aaf5177
@ -3,6 +3,7 @@ import { createIcon, resolveIcon } from "./ui/icon";
|
||||
import { createCheckbox, resolveCheckbox } from "./ui/checkbox";
|
||||
import { setTooltip, resolveTooltip } from "./ui/tooltip";
|
||||
import Dropdown from "./ui/dropdown";
|
||||
import Grid from "./ui/grid";
|
||||
|
||||
export {
|
||||
// icon
|
||||
@ -15,5 +16,7 @@ export {
|
||||
setTooltip,
|
||||
resolveTooltip,
|
||||
// dropdown
|
||||
Dropdown
|
||||
Dropdown,
|
||||
// grid
|
||||
Grid
|
||||
}
|
||||
|
@ -14,8 +14,7 @@ function fillCheckbox(container, type, label) {
|
||||
}
|
||||
}
|
||||
|
||||
function createCheckbox(opts) {
|
||||
opts ??= {};
|
||||
function createCheckbox(opts = {}) {
|
||||
const container = document.createElement('label');
|
||||
container.className = 'checkbox-wrapper';
|
||||
const input = document.createElement('input');
|
||||
@ -56,8 +55,7 @@ function createCheckbox(opts) {
|
||||
return container;
|
||||
}
|
||||
|
||||
function resolveCheckbox(container, legacy) {
|
||||
container ??= document.body;
|
||||
function resolveCheckbox(container = document.body, legacy) {
|
||||
if (legacy) {
|
||||
const checks = container.querySelectorAll('input[type="checkbox"]');
|
||||
for (let chk of checks) {
|
||||
|
@ -97,8 +97,7 @@ class Dropdown {
|
||||
onselected;
|
||||
onexpanded;
|
||||
|
||||
constructor(options) {
|
||||
options ??= {};
|
||||
constructor(options = {}) {
|
||||
options.searchplaceholder ??= r('searchHolder', 'Search...');
|
||||
options.textkey ??= 'text';
|
||||
options.valuekey ??= 'value';
|
||||
@ -287,8 +286,7 @@ class Dropdown {
|
||||
|
||||
get #expanded() { return this.#container?.style.visibility === 'visible' }
|
||||
|
||||
#dropdown(flag) {
|
||||
flag ??= true;
|
||||
#dropdown(flag = true) {
|
||||
const options = this.#options;
|
||||
const textkey = options.textkey;
|
||||
let panel = this.#container;
|
||||
@ -472,8 +470,7 @@ class Dropdown {
|
||||
}
|
||||
}
|
||||
|
||||
static resolve(dom) {
|
||||
dom ??= document.body;
|
||||
static resolve(dom = document.body) {
|
||||
const selects = dom.querySelectorAll('select');
|
||||
for (let sel of selects) {
|
||||
const source = [...sel.children].map(it => {
|
||||
|
607
lib/ui/grid.js
Normal file
607
lib/ui/grid.js
Normal file
@ -0,0 +1,607 @@
|
||||
import { isMobile, global, nullOrEmpty, throttle, truncate, isPositive } from "../utility";
|
||||
import { r } from "../utility/lgres";
|
||||
import { createIcon } from "../ui/icon";
|
||||
import { createCheckbox } from "../ui/checkbox";
|
||||
|
||||
const ColumnChangedType = {
|
||||
Reorder: 'reorder',
|
||||
Resize: 'resize',
|
||||
Sort: 'sort'
|
||||
};
|
||||
const RefreshInterval = isMobile() ? 32 : 0;
|
||||
const MaxColumnBit = 10;
|
||||
const MaxColumnMask = 0x3ff;
|
||||
const RedumCount = 4;
|
||||
const MiniDragOffset = 4;
|
||||
const MiniColumnWidth = 50;
|
||||
|
||||
class GridColumn {
|
||||
static create() { return document.createElement('span') }
|
||||
|
||||
static setValue(element, val) { element.innerText = val }
|
||||
|
||||
static getValue(element) { return element.innerText }
|
||||
}
|
||||
|
||||
class GridInputColumn extends GridColumn {
|
||||
static createEdit(trigger) {
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'input');
|
||||
if (typeof trigger === 'function') {
|
||||
input.addEventListener('change', trigger);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
static setValue(element, val) { element.value = val }
|
||||
|
||||
static getValue(element) { return element.value }
|
||||
|
||||
static setEnabled(element, enabled) { element.disabled = enabled !== false }
|
||||
}
|
||||
|
||||
class GridDropdownColumn extends GridColumn {
|
||||
}
|
||||
|
||||
class GridCheckboxColumn extends GridColumn {
|
||||
static createEdit(trigger) {
|
||||
const check = createCheckbox({
|
||||
onchange: typeof trigger === 'function' ? trigger : null
|
||||
});
|
||||
return check;
|
||||
}
|
||||
|
||||
static setValue(element, val) { element.querySelector('input').checked = val }
|
||||
|
||||
static getValue(element) { return element.querySelector('input').checked }
|
||||
|
||||
static setEnabled(element, enabled) { element.querySelector('input').disabled = enabled !== false }
|
||||
}
|
||||
|
||||
const ColumnTypes = {
|
||||
0: GridColumn,
|
||||
1: GridInputColumn,
|
||||
2: GridDropdownColumn,
|
||||
3: GridCheckboxColumn
|
||||
};
|
||||
|
||||
class Grid {
|
||||
#source;
|
||||
#currentSource;
|
||||
#parent;
|
||||
#el;
|
||||
#refs;
|
||||
#rendering;
|
||||
#selectedColumnIndex = -1;
|
||||
#selectedIndexes;
|
||||
#startIndex = 0;
|
||||
#needResize;
|
||||
#containerHeight;
|
||||
#bodyClientWidth;
|
||||
#rowCount = -1;
|
||||
#overflows;
|
||||
#scrollTop;
|
||||
|
||||
columns = [];
|
||||
langs = {
|
||||
all: r('allItem', '( All )'),
|
||||
ok: r('ok', 'OK'),
|
||||
reset: r('reset', 'Reset')
|
||||
};
|
||||
virtualCount = 100;
|
||||
rowHeight = 27;
|
||||
filterRowHeight = 26;
|
||||
height;
|
||||
multiSelect = false;
|
||||
allowHtml = false;
|
||||
holderDisabled = false;
|
||||
window = global;
|
||||
sortIndex = -1;
|
||||
sortDirection = 'asc';
|
||||
|
||||
willSelect;
|
||||
selectedRowChanged;
|
||||
cellDblClicked;
|
||||
cellClicked;
|
||||
rowDblClicked;
|
||||
columnChanged;
|
||||
|
||||
static ColumnTypes = {
|
||||
Common: 0,
|
||||
Input: 1,
|
||||
Dropdown: 2,
|
||||
Checkbox: 3
|
||||
};
|
||||
|
||||
constructor(container) {
|
||||
this.#parent = container;
|
||||
}
|
||||
|
||||
get source() { return this.#source; }
|
||||
set source(list) {
|
||||
if (this.#el == null) {
|
||||
throw new Error('grid has not been initialized.')
|
||||
}
|
||||
this.#source = list;
|
||||
// TODO: filter to currentSource;
|
||||
this.#currentSource = list;
|
||||
this.#containerHeight = list.length * this.rowHeight;
|
||||
this.#overflows = {};
|
||||
this.#selectedColumnIndex = -1;
|
||||
this.#selectedIndexes = [];
|
||||
this.#startIndex = 0;
|
||||
this.#scrollTop = 0;
|
||||
this.#refs.body.scrollTop = 0;
|
||||
this.#refs.bodyContent.style.top = '0px';
|
||||
this.#refs.bodyContainer.style.height = `${this.#containerHeight}px`;
|
||||
this.#rowCount = -1;
|
||||
|
||||
if (this.sortIndex >= 0) {
|
||||
this.sortColumn(true);
|
||||
} else {
|
||||
this.resize();
|
||||
}
|
||||
}
|
||||
get virtual() { return this.#currentSource?.length > this.virtualCount }
|
||||
get sortKey() { }
|
||||
get selectedIndexes() { return this.#selectedIndexes }
|
||||
set selectedIndexes(indexes) { }
|
||||
get selectedIndex() { }
|
||||
get loading() { return this.#refs.loading?.style.visibility === 'visible' }
|
||||
set loading(flag) {
|
||||
if (this.#refs.loading == null) {
|
||||
return;
|
||||
}
|
||||
if (flag === false) {
|
||||
this.#refs.loading.style.visibility = 'hidden';
|
||||
this.#refs.loading.style.opacity = 0;
|
||||
} else {
|
||||
this.#refs.loading.style.visibility = 'visible';
|
||||
this.#refs.loading.style.opacity = 1;
|
||||
}
|
||||
}
|
||||
get scrollTop() { return this.#refs.body?.scrollTop; }
|
||||
set scrollTop(top) {
|
||||
if (this.#refs.body == null) {
|
||||
return;
|
||||
}
|
||||
this.#refs.body.scrollTop = top;
|
||||
this.reload();
|
||||
}
|
||||
|
||||
init(container = this.#parent) {
|
||||
this.#el = null;
|
||||
this.#refs = {};
|
||||
this.#rendering = true;
|
||||
this.#currentSource = this.source;
|
||||
if (!(container instanceof HTMLElement)) {
|
||||
throw new Error('no specified parent.');
|
||||
}
|
||||
this.#parent = container;
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'grid';
|
||||
grid.setAttribute('tabindex', 0);
|
||||
grid.addEventListener('keydown', e => {
|
||||
let index = this.selectedIndex;
|
||||
let flag = false;
|
||||
if (e.key === 'ArrowUp') {
|
||||
// up
|
||||
flag = true;
|
||||
if (index > 1) {
|
||||
delete this.#currentSource[index].__selected;
|
||||
index -= 1;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
// down
|
||||
flag = true;
|
||||
const count = this.#currentSource?.length ?? 0;
|
||||
if (index < count - 1) {
|
||||
delete this.#currentSource[index].__selected;
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
this.selectedIndexes = [index];
|
||||
this.scrollToIndex(index);
|
||||
this.#currentSource[index].__selected = true;
|
||||
this.refresh();
|
||||
if (typeof this.selectedRowChanged === 'function') {
|
||||
this.selectedRowChanged(index);
|
||||
}
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
container.replaceChildren(grid);
|
||||
const sizer = document.createElement('span');
|
||||
sizer.className = 'grid-sizer';
|
||||
grid.appendChild(sizer);
|
||||
this.#refs.sizer = sizer;
|
||||
|
||||
// header & body
|
||||
const header = this.#createHeader();
|
||||
grid.appendChild(header);
|
||||
const body = this.#createBody();
|
||||
grid.appendChild(body);
|
||||
|
||||
// loading
|
||||
const loading = document.createElement('div');
|
||||
loading.className = 'grid-loading';
|
||||
loading.appendChild(createIcon('fa-regular', 'spinner-third'));
|
||||
this.#refs.loading = loading;
|
||||
grid.appendChild(loading);
|
||||
this.#el = grid;
|
||||
|
||||
this.#rendering = false;
|
||||
if (this.sortIndex >= 0) {
|
||||
this.sortColumn(true);
|
||||
} else {
|
||||
this.resize();
|
||||
}
|
||||
}
|
||||
|
||||
scrollToIndex(index) {
|
||||
this.#scrollToTop(index * this.rowHeight, true);
|
||||
}
|
||||
|
||||
resize(force) {
|
||||
if (this.#rendering || this.#el == null) {
|
||||
return;
|
||||
}
|
||||
const body = this.#refs.body;
|
||||
let height = this.#refs.header.offsetHeight + 2;
|
||||
let top = body.offsetTop;
|
||||
if (top !== height) {
|
||||
body.style.top = `${height}px`;
|
||||
top = height;
|
||||
}
|
||||
|
||||
height = this.height;
|
||||
if (isNaN(height)) {
|
||||
height = this.#el.offsetHeight - top;
|
||||
} else if (height === 0) {
|
||||
height = this.#refs.bodyContent.offsetHeight;
|
||||
this.#el.style.height = `${top + height}px`;
|
||||
}
|
||||
body.style.height = `${height}px`;
|
||||
const count = truncate((height - 1) / this.rowHeight) * (RedumCount * 2) + 1;
|
||||
if (force || count !== this.#rowCount) {
|
||||
this.#rowCount = count;
|
||||
this.reload();
|
||||
}
|
||||
this.#bodyClientWidth = body.clientWidth;
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.#containerHeight = this.#currentSource.length * this.rowHeight;
|
||||
this.#adjustRows(this.#refs.bodyContent);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
if (this.#refs.bodyContent == null) {
|
||||
throw new Error('body has not been created.');
|
||||
}
|
||||
const rows = this.#refs.bodyContent.children;
|
||||
const widths = {};
|
||||
this.#fillRows(rows, this.columns, widths);
|
||||
if (this.#needResize && widths.flag) {
|
||||
this.#needResize = false;
|
||||
this.columns.forEach((col, i) => {
|
||||
if (!col.autoResize) {
|
||||
return;
|
||||
}
|
||||
const width = widths[i];
|
||||
if (width > 0) {
|
||||
this.#changeColumnWidth(i, width);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sortColumn(auto, reload) { }
|
||||
|
||||
#createHeader() {
|
||||
const thead = document.createElement('table');
|
||||
thead.className = 'grid-header';
|
||||
const header = document.createElement('tr');
|
||||
thead.appendChild(header);
|
||||
const sizer = this.#refs.sizer;
|
||||
for (let col of this.columns) {
|
||||
if (col.visible === false) {
|
||||
const hidden = document.createElement('th');
|
||||
hidden.style.display = 'none';
|
||||
if (col.sortable === true) {
|
||||
hidden.dataset.key = col.key;
|
||||
hidden.addEventListener('mouseup', e => this.#onHeaderClicked(col, e, true));
|
||||
}
|
||||
header.appendChild(hidden);
|
||||
continue;
|
||||
}
|
||||
// style
|
||||
if (col.width > 0 || col.shrink) {
|
||||
col.autoResize = false;
|
||||
} else {
|
||||
col.autoResize = true;
|
||||
this.#needResize = true;
|
||||
sizer.innerText = col.caption;
|
||||
let width = sizer.offsetWidth + 20;
|
||||
if (width < MiniColumnWidth) {
|
||||
width = MiniColumnWidth;
|
||||
}
|
||||
col.width = width;
|
||||
}
|
||||
col.align ??= 'left';
|
||||
if (col.sortable !== false) {
|
||||
col.sortable = true;
|
||||
}
|
||||
if (col.shrink) {
|
||||
col.style = { 'text-align': col.align };
|
||||
} else {
|
||||
col.style = {
|
||||
'width': col.width,
|
||||
'max-width': col.width,
|
||||
'min-width': col.width,
|
||||
'text-align': col.align
|
||||
};
|
||||
}
|
||||
// element
|
||||
const th = document.createElement('th');
|
||||
th.dataset.key = col.key;
|
||||
for (let css of Object.entries(col.style)) {
|
||||
th.style.setProperty(css[0], css[1]);
|
||||
}
|
||||
th.style.cursor = col.sortable ? 'pointer' : 'auto';
|
||||
th.addEventListener('mouseup', e => this.#onHeaderClicked(col, e));
|
||||
th.addEventListener('mousedown', e => this.#onDragStart(col, e));
|
||||
const wrapper = document.createElement('div');
|
||||
th.appendChild(wrapper);
|
||||
if (col.enabled !== false && col.allcheck && col.type === Grid.ColumnTypes.Checkbox) {
|
||||
const check = createCheckbox({
|
||||
onchange: e => this.#onColumnAllChecked(col, e.target.checked)
|
||||
});
|
||||
wrapper.appendChild(check);
|
||||
}
|
||||
const caption = document.createElement('span');
|
||||
caption = col.caption;
|
||||
wrapper.appendChild(caption);
|
||||
// order arrow
|
||||
if (col.sortable) {
|
||||
const arrow = document.createElement('layer');
|
||||
arrow.className = 'arrow';
|
||||
th.appendChild(arrow);
|
||||
}
|
||||
// filter
|
||||
if (col.allowFilter) {
|
||||
// TODO: filter
|
||||
}
|
||||
// resize spliter
|
||||
if (col.resizable !== false) {
|
||||
const spliter = document.createElement('layer');
|
||||
spliter.className = 'spliter';
|
||||
spliter.addEventListener('mousedown', e => this.#onResizeStart(col, e));
|
||||
th.appendChild(spliter);
|
||||
}
|
||||
// tooltip
|
||||
!nullOrEmpty(col.tooltip) && th.setAttribute('title', col.tooltip);
|
||||
header.appendChild(th);
|
||||
}
|
||||
const placeholder = document.createElement('th');
|
||||
const dragger = document.createElement('div');
|
||||
dragger.className = 'dragger';
|
||||
const draggerCursor = document.createElement('layer');
|
||||
draggerCursor.className = 'dragger-cursor';
|
||||
placeholder.append(dragger, draggerCursor);
|
||||
header.appendChild(placeholder);
|
||||
|
||||
sizer.replaceChildren();
|
||||
this.#refs.header = header;
|
||||
this.#refs.dragger = dragger;
|
||||
this.#refs.draggerCursor = draggerCursor;
|
||||
return thead;
|
||||
}
|
||||
|
||||
#createBody() {
|
||||
const body = document.createElement('div');
|
||||
body.className = 'grid-body';
|
||||
body.addEventListener('scroll', e => throttle(this.#onScroll, RefreshInterval, this, e), { passive: true });
|
||||
const cols = this.columns;
|
||||
let height = this.#currentSource.length * this.rowHeight;
|
||||
let width;
|
||||
if (height === 0) {
|
||||
height = 1;
|
||||
width = 0;
|
||||
for (let col of cols) {
|
||||
if (col.visible !== false && !isNaN(col.width)) {
|
||||
width += col.width + 1;
|
||||
}
|
||||
}
|
||||
width += 1;
|
||||
}
|
||||
this.#containerHeight = height;
|
||||
// body container
|
||||
const bodyContainer = document.createElement('div');
|
||||
bodyContainer.style.position = 'relative';
|
||||
bodyContainer.style.minWidth = '100%';
|
||||
bodyContainer.style.minHeight = '1px';
|
||||
bodyContainer.style.height = `${height}px`;
|
||||
if (width > 0) {
|
||||
bodyContainer.style.width = `${width}px`;
|
||||
}
|
||||
body.appendChild(bodyContainer);
|
||||
// body content
|
||||
const bodyContent = document.createElement('table');
|
||||
bodyContent.className = 'grid-body-content';
|
||||
bodyContainer.appendChild(bodyContent);
|
||||
this.#adjustRows();
|
||||
// events
|
||||
if (!this.holderDisabled) {
|
||||
const holder = document.createElement('div');
|
||||
holder.className = 'grid-hover-holder';
|
||||
holder.style.display = 'none';
|
||||
bodyContainer.appendChild(holder);
|
||||
body.addEventListener('mousemove', e => throttle(this.#onBodyMouseMove, RefreshInterval, this, e, holder));
|
||||
}
|
||||
this.#refs.body = body;
|
||||
this.#refs.bodyContainer = bodyContainer;
|
||||
this.#refs.bodyContent = bodyContent;
|
||||
|
||||
// this.refresh();
|
||||
return body;
|
||||
}
|
||||
|
||||
#adjustRows() {
|
||||
let count = this.#rowCount;
|
||||
if (isNaN(count) || count < 0 || !this.virtual) {
|
||||
count = this.#currentSource.length;
|
||||
}
|
||||
const cols = this.columns;
|
||||
const content = this.#refs.bodyContent;
|
||||
const exists = content.children.length;
|
||||
count -= exists;
|
||||
if (count > 0) {
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
const row = document.createElement('tr');
|
||||
row.className = 'grid-row';
|
||||
row.addEventListener('mousedown', e => this.#onRowClicked(e, exists + 1));
|
||||
row.addEventListener('dblclick', e => this.#onRowDblClicked(e));
|
||||
cols.forEach((col, j) => {
|
||||
const cell = document.createElement('td');
|
||||
if (col.visible !== false) {
|
||||
cell.keyid = ((exists + i) << MaxColumnBit) | j;
|
||||
if (col.style != null) {
|
||||
for (let css of Object.entries(col.style)) {
|
||||
cell.style.setProperty(css[0], css[1]);
|
||||
}
|
||||
}
|
||||
if (col.css != null) {
|
||||
for (let css of Object.entries(col.css)) {
|
||||
cell.style.setProperty(css[0], css[1]);
|
||||
}
|
||||
}
|
||||
if (col.type === Grid.ColumnTypes.Checkbox) {
|
||||
cell.appendChild(GridCheckboxColumn.createEdit(e => this.#onRowChanged(e, exists + i, col, e.target.checked)));
|
||||
} else if (this.allowHtml && col.type != null && isNaN(col.type)) {
|
||||
cell.appendChild(col.type.create());
|
||||
} else {
|
||||
cell.appendChild(GridColumn.create());
|
||||
}
|
||||
}
|
||||
row.appendChild(cell);
|
||||
});
|
||||
row.appendChild(document.createElement('td'));
|
||||
content.appendChild(row);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
for (let i = -1; i >= count; i -= 1) {
|
||||
// content.removeChild(content.children[exists + i]);
|
||||
content.children[exists + i].remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#fillRows(rows, cols, widths) {
|
||||
const startIndex = this.startIndex;
|
||||
const selected = this.#selectedIndexes;
|
||||
const allowHtml = this.allowHtml;
|
||||
rows.forEach((row, i) => {
|
||||
const vals = this.#currentSource[startIndex + i];
|
||||
if (vals == null) {
|
||||
return;
|
||||
}
|
||||
if (!isPositive(row.children.length)) {
|
||||
return;
|
||||
}
|
||||
const item = vals.values;
|
||||
if (selected.indexOf(startIndex + i) < 0) {
|
||||
row.classList.remove('selected');
|
||||
} else {
|
||||
row.classList.add('selected');
|
||||
}
|
||||
// data
|
||||
const selected = row.dataset.selected === '1';
|
||||
cols.forEach((col, j) => {
|
||||
if (col.visible === false) {
|
||||
return;
|
||||
}
|
||||
let val;
|
||||
if (col.text != null) {
|
||||
val = col.text;
|
||||
} else if (typeof col.filter === 'function') {
|
||||
val = col.filter(item);
|
||||
} else {
|
||||
val = item[col.key];
|
||||
if (val?.displayValue != null) {
|
||||
val = val.displayValue;
|
||||
}
|
||||
}
|
||||
val ??= '';
|
||||
// fill
|
||||
const cell = row.children[j];
|
||||
const custom = allowHtml && col.type != null && isNaN(col.type);
|
||||
let element;
|
||||
if (vals.__selected ^ selected) {
|
||||
if (custom) {
|
||||
element = selected ?
|
||||
col.type.createEdit(e => this.#onRowChanged(e, startIndex + i, col, col.type.getValue(element))) :
|
||||
col.type.create();
|
||||
cell.replaceChildren(element);
|
||||
// } else if (col.type !== Grid.ColumnTypes.Checkbox) {
|
||||
// // TODO:
|
||||
} else {
|
||||
element = cell.children[0];
|
||||
}
|
||||
} else {
|
||||
element = cell.children[0];
|
||||
}
|
||||
let enabled = col.enabled;
|
||||
if (typeof enabled === 'string') {
|
||||
enabled = item[enabled];
|
||||
}
|
||||
if (custom) {
|
||||
col.type.setValue(element, item);
|
||||
col.type.setEnabled(element, enabled);
|
||||
} else if (col.type === Grid.ColumnTypes.Checkbox) {
|
||||
GridCheckboxColumn.setValue(element, val);
|
||||
GridCheckboxColumn.setEnabled(element, enabled);
|
||||
} else {
|
||||
// TODO: input, dropdown, etc...
|
||||
GridColumn.setValue(element, val);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#changeColumnWidth(i, width) { }
|
||||
|
||||
#scrollToTop(top, reload) { }
|
||||
|
||||
#onHeaderClicked(col, e, force) { }
|
||||
#onDragStart(col, e) { }
|
||||
#onResizeStart(col, e) { }
|
||||
#onColumnAllChecked(col, flag) { }
|
||||
#onScroll(e) { }
|
||||
#onBodyMouseMove(e, holder) { }
|
||||
#onRowClicked(e, index, colIndex) { }
|
||||
#onRowDblClicked(e) { }
|
||||
#onRowChanged(_e, index, col, value) {
|
||||
if (this.#currentSource == null) {
|
||||
return;
|
||||
}
|
||||
const item = this.#currentSource[this.#startIndex + index].values;
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
const enabled = typeof col.enabled === 'string' ? item[col.enabled] : col.enabled;
|
||||
if (enabled !== false) {
|
||||
item[col.key] = value;
|
||||
item.__changed = true;
|
||||
if (typeof col.onchanged === 'function') {
|
||||
col.onchanged.call(this, item, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Grid;
|
@ -48,8 +48,7 @@ function setTooltip(container, content) {
|
||||
});
|
||||
}
|
||||
|
||||
function resolveTooltip(container) {
|
||||
container ??= document.body;
|
||||
function resolveTooltip(container = document.body) {
|
||||
const tips = container.querySelectorAll('[title]');
|
||||
for (let tip of tips) {
|
||||
const title = tip.getAttribute('title');
|
||||
|
@ -9,6 +9,28 @@ function isPositive(n) {
|
||||
return !isNaN(n) && n > 0;
|
||||
}
|
||||
|
||||
function isMobile() {
|
||||
return /mobile/i.test(navigator.userAgent);
|
||||
}
|
||||
|
||||
function throttle(method, delay = 100, context = g, ...args) {
|
||||
if (method == null) {
|
||||
return;
|
||||
}
|
||||
method.tiid && clearTimeout(method.tiid);
|
||||
const current = new Date();
|
||||
if (method.tdate == null || current - method.tdate > delay) {
|
||||
method.apply(context, args);
|
||||
method.tdate = current;
|
||||
} else {
|
||||
method.tiid = setTimeout(() => method.apply(context, args), delay);
|
||||
}
|
||||
}
|
||||
|
||||
function truncate(v) {
|
||||
return (v > 0 ? Math.floor : Math.ceil)(v);
|
||||
}
|
||||
|
||||
export {
|
||||
// cookie
|
||||
getCookie,
|
||||
@ -29,5 +51,9 @@ export {
|
||||
padStart,
|
||||
// variables
|
||||
g as global,
|
||||
isPositive
|
||||
isPositive,
|
||||
isMobile,
|
||||
// functions
|
||||
throttle,
|
||||
truncate
|
||||
}
|
@ -45,8 +45,7 @@ function getStorageKey(lgid) {
|
||||
return `res_${lgid}`;
|
||||
}
|
||||
|
||||
async function doRefreshLgres(template) {
|
||||
template ??= '';
|
||||
async function doRefreshLgres(template = '') {
|
||||
const lgid = getCurrentLgId();
|
||||
const r = await get(`language/${lgid}${template}`);
|
||||
const dict = await r.json();
|
||||
@ -81,7 +80,6 @@ function getLanguage(lgres, key, defaultValue) {
|
||||
}
|
||||
|
||||
function applyLanguage(dom, result) {
|
||||
dom ??= document.body;
|
||||
for (let text of dom.querySelectorAll('[data-lgid]')) {
|
||||
const key = text.dataset.lgid;
|
||||
if (text.tagName === 'INPUT') {
|
||||
@ -100,8 +98,7 @@ function applyLanguage(dom, result) {
|
||||
}
|
||||
}
|
||||
|
||||
async function init(dom, options) {
|
||||
options ??= {};
|
||||
async function init(dom = document.body, options = {}) {
|
||||
const lgid = getCurrentLgId();
|
||||
let lgres = localStorage.getItem(getStorageKey(lgid));
|
||||
let result;
|
||||
|
@ -8,8 +8,7 @@ function combineUrl(url) {
|
||||
return (consts.path || '') + url;
|
||||
}
|
||||
|
||||
function get(url, options) {
|
||||
options ??= {};
|
||||
function get(url, options = {}) {
|
||||
return fetch(combineUrl(url), {
|
||||
method: options.method || 'GET',
|
||||
headers: {
|
||||
@ -21,8 +20,7 @@ function get(url, options) {
|
||||
});
|
||||
}
|
||||
|
||||
function post(url, data, options) {
|
||||
options ??= {};
|
||||
function post(url, data, options = {}) {
|
||||
// let contentType;
|
||||
if (data instanceof FormData) {
|
||||
// contentType = 'multipart/form-data';
|
||||
@ -47,7 +45,7 @@ function post(url, data, options) {
|
||||
});
|
||||
}
|
||||
|
||||
function upload(url, data, options) {
|
||||
function upload(url, data, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function () {
|
||||
@ -59,7 +57,6 @@ function upload(url, data, options) {
|
||||
}
|
||||
}
|
||||
};
|
||||
options ??= {};
|
||||
if (typeof options.progress === 'function') {
|
||||
request.upload.addEventListener('progress', function (ev) {
|
||||
if (ev.lengthComputable) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user