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.

385 lines 14 kB
export default class Editor { constructor(ctx) { Object.defineProperty(this, "editorEl", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "inputEl", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "enable", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "cellTarget", { enumerable: true, configurable: true, writable: true, value: null }); Object.defineProperty(this, "selectorArrStr", { enumerable: true, configurable: true, writable: true, value: '' }); Object.defineProperty(this, "ctx", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "drawY", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "drawX", { enumerable: true, configurable: true, writable: true, value: 0 }); this.ctx = ctx; this.initTextEditor(); this.init(); } init() { // 滚动时,结束编辑 this.ctx.on('onScroll', () => { if (this.enable) { this.doneEdit(); } }); this.ctx.on('cellHeaderMousedown', () => { if (this.enable) { this.doneEdit(); } this.cellTarget = null; }); this.ctx.on('keydown', (e) => { const key = e.key; const isCtrl = e.ctrlKey; const isAlt = e.altKey; const isShift = e.shiftKey; const isMeta = e.metaKey; // 检测是否按下了组合键(Ctrl、Alt、Shift、Meta) if (isCtrl || isAlt || isShift || isMeta) { return; } // 检测功能键(比如 F1, Escape,Tab 等) const functionKeys = [ // "Enter", 'Escape', 'Tab', 'Backspace', 'Delete', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'PageUp', 'PageDown', 'Insert', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', ]; if (functionKeys.includes(key)) { return; } // 编辑模式按下按Enter进入编辑模式 if (e.code === 'Enter' && !this.enable) { e.preventDefault(); this.startEdit(); return; } // 除了上面的建其他都开始编辑 this.startEdit(); }); this.ctx.on('adjustBoundaryPosition', (cell) => { // 调整位置会触发重绘可能会导致cellClick事件不能触发,调整位置时需要赋值cellTarget this.cellTarget = cell; const { xArr, yArr } = this.ctx.selector; this.selectorArrStr = JSON.stringify(xArr) + JSON.stringify(yArr); }); this.ctx.on('cellClick', (cell) => { // 没有编辑器的情况下不进入编辑模式 if (cell.editorType === 'none') { return; } // 如果是调整边界位置,不进入编辑模式 if (this.ctx.adjustPositioning) { return; } // 不在区域内 if (!this.isInSelectorRange(cell.rowIndex, cell.colIndex)) { return; } const { xArr, yArr } = this.ctx.selector; const selectorArrStr = JSON.stringify(xArr) + JSON.stringify(yArr); if (this.selectorArrStr === selectorArrStr && this.cellTarget) { // 启用合并单元格关联&&只有合并单元格时才进入编辑模式 if (this.ctx.config.ENABLE_MERGE_CELL_LINK && this.ctx.onlyMergeCell) { this.startEdit(); return; } // 只有一个的情况下才进入编辑模式 if (this.ctx.selectOnlyOne && cell.rowKey === this.cellTarget.rowKey && cell.key === this.cellTarget.key) { this.startEdit(); return; } } this.selectorArrStr = selectorArrStr; this.doneEdit(); this.cellTarget = cell; // 单击单元格进入编辑模式 if (this.ctx.config.ENABLE_EDIT_SINGLE_CLICK) { // 启用合并单元格关联&&只有合并单元格时才进入编辑模式 if (this.ctx.config.ENABLE_MERGE_CELL_LINK && this.ctx.onlyMergeCell) { this.startEdit(); return; } // 只有一个的情况下才进入编辑模式 if (this.ctx.selectOnlyOne) { this.startEdit(); } } }); } isInSelectorRange(rowIndex, colIndex) { const { xArr, yArr } = this.ctx.selector; const [minX, maxX] = xArr; const [minY, maxY] = yArr; if (colIndex < minX) { return false; } if (colIndex > maxX) { return false; } if (rowIndex < minY) { return false; } if (rowIndex > maxY) { return false; } return true; } initTextEditor() { // 初始化文本编辑器 this.inputEl = document.createElement('textarea'); this.inputEl.setAttribute('rows', '1'); // 监听键盘事件 this.inputEl.addEventListener('keydown', (e) => { // 如果是在输入中文过程中,不触发 if (e.isComposing) { return; } if (!this.enable) { return; } e.stopPropagation(); // 模拟excel altKey enter换行 if ((e.altKey || e.metaKey) && e.code === 'Enter') { e.preventDefault(); // 插入换行符 const cursorPos = this.inputEl.selectionStart; // 获取光标位置 const textBefore = this.inputEl.value.substring(0, cursorPos); // 光标前的文本 const textAfter = this.inputEl.value.substring(cursorPos); // 光标后的文本 // 更新 textarea 的内容,在光标位置插入换行符 this.inputEl.value = textBefore + '\n' + textAfter; // 设置光标位置到新行 this.inputEl.selectionStart = this.inputEl.selectionEnd = cursorPos + 1; this.autoSize(); return; } if (e.code === 'Escape' || e.code === 'Enter') { e.preventDefault(); this.inputEl.blur(); } }); // 监听输入事件,自动调整高度 this.inputEl.addEventListener('input', this.autoSize.bind(this)); this.inputEl.addEventListener('blur', () => { this.doneEdit(); }); this.editorEl = this.ctx.editorElement; this.inputEl.className = 'e-virt-table-editor-textarea'; this.editorEl.appendChild(this.inputEl); this.ctx.containerElement.appendChild(this.editorEl); } autoSize() { this.inputEl.style.height = 'auto'; // 重置高度 let scrollHeight = this.inputEl.scrollHeight; let maxHeight = this.ctx.body.visibleHeight; if (scrollHeight > maxHeight) { scrollHeight = maxHeight; } const { stageHeight, footer, header, config: { SCROLLER_TRACK_SIZE }, } = this.ctx; const bottomY = stageHeight - footer.height - SCROLLER_TRACK_SIZE; this.editorEl.style.bottom = `auto`; if (this.drawY < header.height) { this.editorEl.style.top = `${header.height - 1}px`; } if (this.drawY + scrollHeight > bottomY) { this.editorEl.style.left = `${this.drawX - 1}px`; this.editorEl.style.top = `auto`; this.editorEl.style.bottom = `${stageHeight - bottomY}px`; } this.inputEl.style.height = `${scrollHeight}px`; // 设置为内容的高度 } startEditByInput(cell) { const value = cell.getValue(); const { editorType } = cell; if (this.ctx.config.ENABLE_MERGE_CELL_LINK) { cell.updateSpanInfo(); // 更新合并单元格信息 } let { drawX, drawY, height, width } = cell; this.drawX = drawX; this.drawY = drawY; const { config: { CELL_PADDING }, header, } = this.ctx; let maxHeight = this.ctx.body.visibleHeight; if (height > maxHeight) { height = maxHeight; } // 显示编辑器 this.editorEl.style.display = 'inline-block'; this.editorEl.style.left = `${drawX - 1}px`; this.editorEl.style.top = `${drawY - 1}px`; this.editorEl.style.bottom = `auto`; this.editorEl.style.maxHeight = `${maxHeight}px`; if (editorType === 'text') { this.inputEl.style.display = 'block'; this.inputEl.style.minWidth = `${width - 1}px`; this.inputEl.style.minHeight = `${height - 1}px`; this.inputEl.style.maxHeight = `${maxHeight}px`; this.inputEl.style.width = `${width - 1}px`; this.inputEl.style.height = `auto`; this.inputEl.style.padding = `${CELL_PADDING}px`; if (value !== null) { this.inputEl.value = value; } this.inputEl.focus(); const length = this.inputEl.value.length; this.inputEl.setSelectionRange(length, length); } else { this.inputEl.style.display = 'none'; } if (this.inputEl.scrollHeight > height || drawY < header.height) { this.autoSize(); } } doneEditByInput() { if (!this.cellTarget) { return; } // 隐藏编辑器 this.editorEl.style.display = 'none'; // 如果是文本编辑器 if (this.cellTarget.editorType === 'text') { const { rowKey, key } = this.cellTarget; const value = this.cellTarget.getValue(); const textContent = this.inputEl.value; // !(text.textContent === '' && value === null)剔除点击编辑后未修改会把null变为''的情况 if (textContent !== value && !(textContent === '' && value === null)) { this.ctx.setItemValueByEditor(rowKey, key, textContent, true); // this.cellTarget.setValue(textContent); } this.inputEl.value = ''; } } startEdit() { // 如果不启用点击选择器编辑 const { ENABLE_EDIT_CLICK_SELECTOR } = this.ctx.config; if (!ENABLE_EDIT_CLICK_SELECTOR) { return; } const focusCell = this.ctx.focusCell; if (!focusCell) { return; } // 如果是index或者index-selection,selection类型的单元格,不允许编辑 if (['index', 'index-selection', 'selection'].includes(focusCell.type)) { return; } if (this.enable) { return; } const { rowKey, key } = focusCell; const readonly = this.ctx.database.getReadonly(rowKey, key); if (focusCell && !readonly) { this.enable = true; this.ctx.editing = true; this.cellTarget = focusCell; this.startEditByInput(this.cellTarget); this.ctx.emit('startEdit', this.cellTarget); } } editCell(rowIndex, colIndex) { const row = this.ctx.body.renderRows.find((row) => row.rowIndex === rowIndex); if (!row) { return; } const cell = row.cells.find((cell) => cell.colIndex === colIndex); if (!cell) { return; } this.ctx.emit('setSelectorCell', cell); const focusCell = this.ctx.focusCell; if (!focusCell) { return; } // 如果是index或者index-selection,selection类型的单元格,不允许编辑 if (['index', 'index-selection', 'selection'].includes(focusCell.type)) { return; } if (this.enable) { return; } const { rowKey, key } = focusCell; const readonly = this.ctx.database.getReadonly(rowKey, key); if (focusCell && !readonly) { this.enable = true; this.ctx.editing = true; this.cellTarget = focusCell; this.startEditByInput(this.cellTarget); this.ctx.emit('startEdit', this.cellTarget); } } doneEdit() { if (!this.enable) { return; } this.doneEditByInput(); this.ctx.emit('doneEdit', this.cellTarget); this.enable = false; this.ctx.editing = false; this.ctx.emit('draw'); } destroy() { this.editorEl?.remove(); } } //# sourceMappingURL=Editor.js.map