e-virt-table
Version:
A powerful data table based on canvas. You can use it as data grid、Microsoft Excel or Google sheets. It supports virtual scroll、cell edit etc.
544 lines • 18.7 kB
JavaScript
import Context from './Context';
import Scroller from './Scroller';
import Header from './Header';
import Body from './Body';
import Footer from './Footer';
import Selector from './Selector';
import Autofill from './Autofill';
import Tooltip from './Tooltip';
import Editor from './Editor';
import Empty from './Empty';
import Overlayer from './Overlayer';
import ContextMenu from './ContextMenu';
import { mergeColCell, mergeRowCell, getSpanArrByRow, getSpanObjByColumn } from './util';
import './style.css';
import Loading from './Loading';
import { FinderBar } from './FinderBar';
export default class EVirtTable {
constructor(target, options) {
Object.defineProperty(this, "options", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "scroller", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "header", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "body", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "footer", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "selector", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "autofill", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "tooltip", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "editor", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "empty", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "overlayer", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "contextMenu", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "loading", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "finderBar", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "animationFrameId", {
enumerable: true,
configurable: true,
writable: true,
value: undefined
});
Object.defineProperty(this, "animationFrameId2", {
enumerable: true,
configurable: true,
writable: true,
value: undefined
});
Object.defineProperty(this, "ctx", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.options = options;
const { overlayerElement, editorElement, emptyElement, contextMenuElement } = this.options;
const containerElement = this.createContainer(target, overlayerElement, editorElement, emptyElement, contextMenuElement);
this.ctx = new Context(containerElement, this.options);
this.header = new Header(this.ctx);
this.footer = new Footer(this.ctx);
this.body = new Body(this.ctx);
this.scroller = new Scroller(this.ctx);
this.selector = new Selector(this.ctx);
this.autofill = new Autofill(this.ctx);
this.tooltip = new Tooltip(this.ctx);
this.empty = new Empty(this.ctx);
this.editor = new Editor(this.ctx);
this.overlayer = new Overlayer(this.ctx);
this.contextMenu = new ContextMenu(this.ctx);
this.loading = new Loading(this.ctx);
this.finderBar = new FinderBar(this.ctx);
this.ctx.on('draw', () => {
this.draw();
});
this.ctx.on('drawView', () => {
this.draw(true);
});
this.draw();
}
createContainer(containerElement, _overlayerElement, _editorElement, _emptyElement, _contextMenuElement) {
containerElement.className = 'e-virt-table-container';
const stageElement = document.createElement('div');
const canvasElement = document.createElement('canvas');
const overlayerElement = _overlayerElement || document.createElement('div');
stageElement.className = 'e-virt-table-stage';
containerElement.tabIndex = 0;
canvasElement.className = 'e-virt-table-canvas';
overlayerElement.className = 'e-virt-table-overlayer';
overlayerElement.setAttribute('data-overlayer', _overlayerElement ? 'custom' : 'default');
const editorElement = _editorElement || document.createElement('div');
editorElement.className = 'e-virt-table-editor';
const emptyElement = _emptyElement;
const contextMenuElement = _contextMenuElement;
stageElement.appendChild(canvasElement);
stageElement.appendChild(overlayerElement);
containerElement.appendChild(stageElement);
return {
containerElement,
stageElement,
canvasElement,
overlayerElement,
editorElement,
emptyElement,
contextMenuElement,
};
}
draw(ignoreOverlayer = false) {
if (this.animationFrameId) {
cancelAnimationFrame(this.animationFrameId);
}
this.animationFrameId = requestAnimationFrame(() => {
this.header.update();
this.footer.update();
this.body.update();
this.ctx.paint.clear();
this.body.draw();
this.footer.draw();
this.header.draw();
this.scroller.draw();
// 忽略重绘覆盖层,解决按下事件时,重绘覆盖层导致事件无法触发,目前只在Selector中按下事件使用
if (!ignoreOverlayer) {
this.overlayer.draw();
}
// 更新自动高度放在第二帧,避免影响绘制性能
if (this.animationFrameId2) {
cancelAnimationFrame(this.animationFrameId2);
}
this.animationFrameId2 = requestAnimationFrame(() => {
this.body.updateAutoHeight();
});
});
}
loadConfig(_config) {
this.ctx.config.init(_config);
//重新加载config,初始化表格,但是默认不清除用户操作
this.ctx.database.init(false);
this.header.init();
this.ctx.emit('draw');
}
loadColumns(columns) {
// 先关闭编辑
this.editor.doneEdit();
this.ctx.database.setColumns(columns);
this.header.init();
this.ctx.emit('draw');
}
loadData(data) {
// 先关闭编辑
this.editor.doneEdit();
this.ctx.database.setData(data);
this.header.init();
this.tooltip.hide();
this.ctx.emit('draw');
}
loadFooterData(data) {
this.ctx.database.setFooterData(data);
this.ctx.emit('draw');
}
setCustomHeader(customHeader, ignoreEmit = true) {
this.ctx.database.setCustomHeader(customHeader, ignoreEmit);
this.header.init();
this.ctx.emit('draw');
}
getCustomHeader() {
return this.header.getCustomHeader();
}
setLoading(loading) {
this.ctx.loading = loading;
if (loading) {
this.loading.show();
}
else {
this.loading.hide();
}
}
on(event, callback) {
this.ctx.on(event, callback);
}
emit(event, ...args) {
this.ctx.emit(event, ...args);
}
off(event, callback) {
this.ctx.off(event, callback);
}
filterMethod(func) {
this.ctx.database.setFilterMethod(func);
//重新初始化表格,但是默认不清除用户操作
this.ctx.database.init(false);
this.header.init();
this.ctx.emit('draw');
}
editCell(rowIndex, colIndex) {
this.editor.editCell(rowIndex, colIndex);
}
setItemValue(rowKey, key, value, history = true, reDraw = true, isEditor = false) {
this.ctx.database.setItemValue(rowKey, key, value, history, reDraw, isEditor);
}
batchSetItemValue(list, history = true) {
this.ctx.database.batchSetItemValue(list, history);
}
setItemValueByEditor(rowKey, key, value, history = true, reDraw = true) {
this.ctx.setItemValueByEditor(rowKey, key, value, history, reDraw);
this.editor.doneEdit();
}
clearEditableData(value = null) {
const xArr = [0, this.ctx.maxColIndex];
const yArr = [0, this.ctx.maxRowIndex];
return this.selector.clearSelectedData(xArr, yArr, false, value);
}
clearEditor() {
this.editor.clearEditor();
}
doLayout() {
this.ctx.emit('draw');
}
getChangedData() {
return this.ctx.database.getChangedData();
}
getChangedRows() {
return this.ctx.database.getChangedRows();
}
setCurrentRowByRowIndex(rowIndex) {
this.ctx.currentCell = this.ctx.database.getVirtualBodyCell(rowIndex, 0);
this.ctx.emit('draw');
}
setCurrentRow(rowKey) {
const column = this.ctx.database.getColumnByColIndex(0);
if (!column) {
return;
}
this.ctx.currentCell = this.ctx.database.getVirtualBodyCellByKey(rowKey, column.key);
this.ctx.emit('draw');
}
getCurrentRow() {
const cell = this.ctx.currentCell;
if (!cell) {
return;
}
const rowData = {
row: cell.row,
rowIndex: cell.rowIndex,
rowKey: cell.rowKey,
};
return rowData;
}
clearValidate() {
this.ctx.database.clearValidate();
this.ctx.emit('draw');
}
validateFields(ValidateFields, scrollError = true) {
return new Promise(async (resolve, reject) => {
let errors = [];
for (let i = 0; i < ValidateFields.length; i++) {
const { rowKey, key } = ValidateFields[i];
const _errors = await this.ctx.database.getValidator(rowKey, key);
if (Array.isArray(_errors) && _errors.length) {
errors.push(_errors);
}
}
if (errors.length) {
reject(errors);
if (scrollError) {
const [err] = errors;
if (Array.isArray(err) && err.length) {
const [_err] = err;
const { rowKey, key } = _err;
this.scrollToRowkey(rowKey);
this.scrollToColkey(key);
}
}
this.ctx.emit('draw');
}
else {
resolve([]);
this.ctx.emit('draw');
}
});
}
async validate(scrollError = true) {
// 先关闭编辑
return new Promise(async (resolve, reject) => {
try {
const res = await this.getValidations();
resolve(res);
}
catch (errors) {
// 滚动到错误位置,取第一个错误
if (scrollError && Array.isArray(errors) && errors.length) {
const [err] = errors;
if (Array.isArray(err) && err.length) {
const [_err] = err;
const { rowKey, key } = _err;
const targetRow = this.ctx.database.getRowForRowKey(rowKey);
if (targetRow) {
const { parentRowKeys = [] } = targetRow;
if (parentRowKeys && parentRowKeys.length) {
this.setExpandRowKeys(parentRowKeys, true);
}
}
this.scrollToRowkey(rowKey);
this.scrollToColkey(key);
}
}
reject(errors);
}
});
}
setValidations(errors) {
errors.forEach((item) => {
const { rowIndex, key, message, rowKey } = item;
if (rowIndex !== undefined && rowKey === undefined) {
const _rowKey = this.ctx.database.getRowKeyForRowIndex(rowIndex);
this.ctx.database.setValidationErrorByRowKey(_rowKey, key, message);
}
if (rowKey) {
this.ctx.database.setValidationErrorByRowKey(rowKey, key, message);
}
});
// 滚动到错误位置,取第一个错误
if (errors && Array.isArray(errors) && errors.length) {
const [err] = errors;
if (err && err.rowKey) {
this.scrollToRowkey(err.rowKey);
this.scrollToColkey(err.key);
}
else if (err && err.rowIndex !== undefined && err.rowIndex >= 0 && err.key) {
const { rowIndex, key } = err;
this.scrollToRowIndex(rowIndex);
this.scrollToColkey(key);
}
}
}
getValidations() {
return new Promise(async (resolve, reject) => {
const data = this.ctx.database.getAllRowsData();
const leafCellHeaders = this.ctx.header.leafCellHeaders;
let errors = [];
for (let i = 0; i < data.length; i++) {
for (let y = 0; y < leafCellHeaders.length; y++) {
const rowKey = this.ctx.database.getRowKeyByItem(data[i]);
const leafCellHeader = leafCellHeaders[y];
const key = leafCellHeader.key;
const _errors = await this.ctx.database.getValidator(rowKey, key);
if (Array.isArray(_errors) && _errors.length) {
errors.push(_errors);
}
}
}
if (errors.length) {
reject(errors);
this.ctx.emit('draw');
}
else {
resolve([]);
this.ctx.emit('draw');
}
});
}
hasValidationError() {
return this.ctx.database.hasValidationError();
}
scrollTo(x, y) {
this.scroller.setScroll(x, y);
}
scrollXTo(x) {
this.scroller.setScrollX(x);
}
scrollToColkey(key) {
this.scroller.scrollToColkey(key);
}
scrollToRowkey(key) {
this.scroller.scrollToRowKey(key);
}
scrollToColIndex(colIndex) {
this.scroller.scrollToColIndex(colIndex);
}
scrollToRowIndex(rowIndex) {
this.scroller.scrollToRowIndex(rowIndex);
}
scrollYTo(y) {
this.scroller.setScrollY(y);
}
setExpandRowKeys(keys, expand = true) {
this.ctx.database.setExpandRowKeys(keys, expand);
}
getExpandRowKeys() {
return this.ctx.database.getExpandRowKeys();
}
clearSelection() {
this.ctx.database.clearSelection();
this.ctx.emit('draw');
}
toggleRowSelection(row) {
const rowKey = this.ctx.database.getRowKeyByItem(row);
this.ctx.database.toggleRowSelection(rowKey);
this.ctx.emit('draw');
}
setSelectionByRows(rows, selected = true) {
rows.forEach((row) => {
const rowKey = this.ctx.database.getRowKeyByItem(row);
this.ctx.database.setRowSelection(rowKey, selected, false);
});
this.ctx.emit('selectionChange', this.getSelectionRows());
this.ctx.emit('draw');
}
setSelectionByRowKeys(keys, selected = true) {
keys.forEach((key) => {
this.ctx.database.setRowSelection(key, selected, false);
});
this.ctx.emit('selectionChange', this.getSelectionRows());
this.ctx.emit('draw');
}
toggleAllSelection() {
this.ctx.database.toggleAllSelection();
this.ctx.emit('draw');
}
toggleRowExpand(rowKey, expand) {
this.ctx.database.expandItem(rowKey, expand);
this.ctx.emit('draw');
}
toggleExpandAll(expand) {
this.ctx.database.expandAll(expand);
this.ctx.emit('draw');
}
getSelectionRows() {
return this.ctx.database.getSelectionRows();
}
getPositionForRowIndex(rowIndex) {
return this.ctx.database.getPositionForRowIndex(rowIndex);
}
getCellValue(rowKey, key) {
return this.ctx.database.getItemValue(rowKey, key);
}
getCellValueByIndex(rowIndex, colIndex) {
return this.ctx.database.getItemValueForRowIndexAndColIndex(rowIndex, colIndex);
}
clearSort() {
this.ctx.database.clearSort();
}
clearMaxRowHeight() {
this.ctx.database.clearMaxRowHeight();
}
contextMenuHide() {
this.contextMenu.hide();
}
getUtils() {
return {
mergeColCell,
mergeRowCell,
getSpanArrByRow,
getSpanObjByColumn,
};
}
getColumnByKey(key) {
return this.ctx.database.getColumnByKey(key)?.column;
}
clearChangeData() {
this.ctx.database.clearChangeData();
this.ctx.emit('draw');
}
/**
* 销毁
*/
destroy() {
this.overlayer.destroy();
this.empty.destroy();
this.editor.destroy();
this.tooltip.destroy();
this.selector.destroy();
this.autofill.destroy();
this.contextMenu.destroy();
this.loading.destroy();
this.finderBar.destroy();
this.ctx.destroy();
this.ctx.containerElement.remove();
}
}
//# sourceMappingURL=EVirtTable.js.map