UNPKG

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.

596 lines 20.5 kB
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'; import TextSelector from './TextSelector'; import { normalizeBorderStyle, shouldDrawOuterBorder } from './BorderStyle'; export default class EVirtTable { static useLocale(langConfig) { EVirtTable.locale = langConfig; } 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, "textSelector", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "animationFrameId", { 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.textSelector = new TextSelector(this.ctx); 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('loadData', (data) => { this.loadData(data); }); 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, }; } doDraw(ignoreOverlayer = false) { this.updateStageBorder(); this.header.update(); this.body.update(); this.footer.update(); this.ctx.paint.clear(); this.textSelector.clearLayouts(); this.body.draw(); this.footer.draw(); this.header.draw(); // 在 body/footer/header 之后绘制,保证文字选中高亮覆盖在三类单元格之上 this.textSelector.draw(); this.body.drawDragRowTip(); this.scroller.draw(); this.empty.draw(); this.loading.draw(); // 忽略重绘覆盖层,解决按下事件时,重绘覆盖层导致事件无法触发,目前只在Selector中按下事件使用 if (!ignoreOverlayer) { this.overlayer.draw(); } } updateStageBorder() { const { BORDER, BORDER_COLOR, BORDER_RADIUS } = this.ctx.config; this.ctx.stageElement.dataset.borderStyle = normalizeBorderStyle(BORDER); this.ctx.stageElement.style.borderColor = shouldDrawOuterBorder(BORDER) ? BORDER_COLOR : 'transparent'; this.ctx.stageElement.style.borderRadius = `${BORDER_RADIUS}px`; } draw(ignoreOverlayer = false) { if (this.animationFrameId) { cancelAnimationFrame(this.animationFrameId); } this.animationFrameId = requestAnimationFrame(() => { this.doDraw(ignoreOverlayer); const needReDraw = this.body.updateAutoHeight(); if (needReDraw) { this.doDraw(ignoreOverlayer); } }); } 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(); } showColumns(keys, show = true) { this.ctx.database.setCustomHeaderHideData(keys, !show); this.header.init(); this.ctx.emit('draw'); } fixedColumns(keys, fixed) { this.ctx.database.setCustomHeaderFixedData(keys, fixed); this.header.init(); this.ctx.emit('draw'); } 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'); } /** * 设置内容缩放比例(容器大小不变,内容变大/变小,基于原生分辨率重绘不失清晰度) * @param zoom 缩放比例,范围 MIN_ZOOM ~ MAX_ZOOM */ setZoom(zoom) { this.ctx.setZoom(zoom); } getZoom() { return this.ctx.getZoom(); } 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() { // 禁用不验证 if (this.ctx.config.DISABLED) { return new Promise((resolve) => { resolve([]); }); } 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(); } 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'); } getReadonly(rowKey, key) { return this.ctx.database.getReadonly(rowKey, key); } useLocale(langConfig) { this.ctx.locale.use(langConfig); } /** * 销毁 */ 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.textSelector.destroy(); this.ctx.destroy(); this.ctx.containerElement.remove(); } } //# sourceMappingURL=EVirtTable.js.map