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.

488 lines 18.3 kB
import { getLayoutScrollerTrackSize } from './ScrollbarMode'; 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 }); Object.defineProperty(this, "cancel", { enumerable: true, configurable: true, writable: true, value: false }); this.ctx = ctx; this.initTextEditor(); this.init(); } init() { // 容器不聚焦,清除选择器 this.ctx.on('outsideMousedown', () => { if (!this.cellTarget) { return; } if (this.cellTarget.editorType === 'text') { this.clearEditor(); } }); this.ctx.on('moveFocus', (cell) => { this.cellTarget = cell; this.resetEditorStyle(); const { xArr, yArr } = this.ctx.selector; this.selectorArrStr = JSON.stringify(xArr) + JSON.stringify(yArr); this.focusInput(); }); // 滚动时,结束编辑 this.ctx.on('onScroll', () => { if (this.enable) { this.doneEdit(); } }); this.ctx.on('hoverIconClick', (cell) => { this.editCell(cell.rowIndex, cell.colIndex); }); this.ctx.on('keydown', (e) => { if (!this.ctx.isTarget(e)) { return; } if (!this.ctx.focusCell) { return; } if (this.ctx.finding) { return; } // 如果是多选不能输入任何字符, 如果输入框聚焦,则进入编辑模式 const { focusCell } = this.ctx; if (e.code === 'Escape' && this.ctx.editing) { this.cancel = true; const { focusCell } = this.ctx; if (focusCell) { this.ctx.emit('setSelectorCell', focusCell); this.cellTarget = focusCell; } this.doneEdit(); return; } if ((e.altKey || e.metaKey) && e.key === 'Enter' && this.ctx.editing && this.inputEl) { 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 === 'Tab' && this.ctx.editing) { e.preventDefault(); this.doneEdit(); if (e.shiftKey) { this.ctx.emit('setMoveFocus', 'LEFT'); return; } this.ctx.emit('setMoveFocus', 'RIGHT'); return; } if (e.key === 'Enter' && this.ctx.editing) { e.preventDefault(); this.doneEdit(); if (e.shiftKey) { this.ctx.emit('setMoveFocus', 'TOP'); return; } this.ctx.emit('setMoveFocus', 'BOTTOM'); return; } if (e.key === 'Enter' && !this.ctx.editing) { e.preventDefault(); this.startEdit(); return; } 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', 'CapsLock', '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; } const isFocus = document.activeElement === this.inputEl; if (!isFocus && focusCell.editorType === 'text') { e.preventDefault(); return; } this.startEdit(true); }); // 重绘可能会导致cellClick事件不能触发,调整用按下cellMouseup按下延时赋值cellTarget this.ctx.on('cellClick', (cell) => { // 如果是选择器,不进入编辑模式 if (this.ctx.stageElement.style.cursor === 'pointer') { return; } // 不在区域内 if (!this.isInSelectorRange(cell.rowIndex, cell.colIndex)) { return; } const { xArr, yArr } = this.ctx.selector; const selectorArrStr = JSON.stringify(xArr) + JSON.stringify(yArr); this.focusInput(); 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; this.resetEditorStyle(); // 单击单元格进入编辑模式 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(); } } }); } isTextSelectionActive() { return (this.ctx.config.ENABLE_TEXT_SELECTION && (this.ctx.textSelecting || !!this.ctx.textSelectionStr)); } 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.id = 'e-virt-table-editor-textarea'; this.inputEl.setAttribute('rows', '1'); this.inputEl.setAttribute('tabindex', '-1'); // 监听输入事件,自动调整高度 this.inputEl.addEventListener('input', this.autoSize.bind(this)); 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() { if (!this.ctx.editing) { return; } // 针对数字类型提示错误信息 const value = this.inputEl.value; if (this.cellTarget && this.cellTarget.type === 'number' && value !== '') { this.ctx.emit('cellHideTooltip'); if (!/^-?\d+(\.\d+)?$/.test(value)) { const numberErrorTip = this.ctx.locale.getText('numberErrorTip'); this.ctx.emit('cellShowTooltip', this.cellTarget, numberErrorTip); } } this.inputEl.style.height = 'auto'; // 重置高度 let scrollHeight = this.inputEl.scrollHeight; // 物理高度换算为逻辑高度做比较 const scrollHeightLogical = this.ctx.toLogical(scrollHeight); let maxHeight = this.ctx.body.visibleHeight; if (scrollHeightLogical > maxHeight) { scrollHeight = this.ctx.toVisual(maxHeight); } const { stageHeight, footer, header, } = this.ctx; const bottomY = stageHeight - footer.height - getLayoutScrollerTrackSize(this.ctx.config); this.editorEl.style.bottom = `auto`; if (this.drawY < header.height) { this.editorEl.style.top = this.ctx.toVisualPx(header.height - 1); } if (this.drawY + this.ctx.toLogical(scrollHeight) > bottomY) { this.editorEl.style.left = this.ctx.toVisualPx(this.drawX - 1); this.editorEl.style.top = `auto`; this.editorEl.style.bottom = this.ctx.toVisualPx(stageHeight - bottomY); } this.inputEl.style.height = `${scrollHeight}px`; // 设置为内容的高度 } startEditByInput(cell, ignoreValue = false) { const value = ignoreValue ? null : cell.getValue(); const { editorType } = cell; // 没有编辑器的情况下不进入编辑模式 if (editorType === 'none') { return; } cell.update(); // 更新单元格信息 if (this.ctx.config.ENABLE_MERGE_CELL_LINK) { cell.updateSpanInfo(); // 更新合并单元格信息 } let { height, width, drawY, drawX } = 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; } const visualMaxHeight = this.ctx.toVisual(maxHeight); // 显示编辑器(drawX/drawY/width/height 为逻辑值,DOM 定位需*zoom) this.editorEl.style.zIndex = '100'; this.editorEl.style.left = this.ctx.toVisualPx(this.drawX - 1); this.editorEl.style.top = this.ctx.toVisualPx(this.drawY); this.editorEl.style.bottom = `auto`; this.editorEl.style.maxWidth = 'none'; // 恢复border样式 this.editorEl.style.border = ''; this.editorEl.style.maxHeight = `${visualMaxHeight}px`; if (['text'].includes(editorType)) { this.inputEl.style.opacity = '1'; this.inputEl.style.position = 'relative'; this.inputEl.style.minWidth = this.ctx.toVisualPx(width - 1); this.inputEl.style.minHeight = this.ctx.toVisualPx(height - 1); this.inputEl.style.maxHeight = `${visualMaxHeight}px`; this.inputEl.style.width = this.ctx.toVisualPx(width); this.inputEl.style.height = `auto`; this.inputEl.style.padding = this.ctx.toVisualPx(CELL_PADDING); this.inputEl.value = ''; // 清空 if (value !== null) { this.inputEl.value = value; } if (this.ctx.toLogical(this.inputEl.scrollHeight) > height || this.drawY < header.height) { this.autoSize(); } } else { this.inputEl.style.opacity = '0'; this.inputEl.style.width = '1px'; this.inputEl.style.height = '1px'; this.inputEl.style.position = 'absolute'; this.inputEl.style.left = '0px'; this.inputEl.style.top = '0px'; } } doneEditByInput() { if (!this.cellTarget) { return; } // 如果是文本编辑器 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.cancel) { this.ctx.setItemValueByEditor(rowKey, key, textContent, true); // this.cellTarget.setValue(textContent); } } } focusInput() { if (document.activeElement !== this.inputEl) { this.inputEl.focus({ preventScroll: true }); } } startEdit(ignoreValue = false) { if (this.isTextSelectionActive()) { return; } this.cancel = false; // 如果不启用点击选择器编辑 const { ENABLE_EDIT_CLICK_SELECTOR } = this.ctx.config; if (!ENABLE_EDIT_CLICK_SELECTOR) { return; } const focusCell = this.ctx.focusCell; if (!focusCell) { return; } // 可视区可见 const isVisible = focusCell.isVerticalVisible() && focusCell.isHorizontalVisible(); if (!isVisible) { // 滚动到焦点单元格 this.ctx.emit('scrollToIndex', focusCell.rowIndex, focusCell.colIndex); } this.editCell(focusCell.rowIndex, focusCell.colIndex, ignoreValue); } editCell(rowIndex, colIndex, ignoreValue = false) { // 直接从renderRows中获取cell,防止focusCell不是最新的 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; } // 可视区可见 const isVisible = cell.isVerticalVisible() && cell.isHorizontalVisible(); if (!isVisible) { 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, ignoreValue); this.ctx.emit('startEdit', this.cellTarget); // 触发绘制,刷新 this.ctx.emit('draw'); } } doneEdit() { if (!this.enable) { return; } this.doneEditByInput(); this.ctx.emit('cellHideTooltip'); this.ctx.emit('doneEdit', this.cellTarget); this.enable = false; this.ctx.editing = false; this.resetEditorStyle(); // 聚焦输入框,防止非文本类型无法聚焦 setTimeout(() => { this.focusInput(); }, 0); this.ctx.emit('draw'); } resetEditorStyle() { const cell = this.cellTarget; if (!cell) { return; } this.editorEl.style.left = this.ctx.toVisualPx(cell.drawX); this.editorEl.style.top = this.ctx.toVisualPx(cell.drawY); this.editorEl.style.maxWidth = `1px`; this.editorEl.style.maxHeight = `1px`; this.editorEl.style.zIndex = '-1'; this.editorEl.style.border = 'none'; } clearEditor() { this.doneEdit(); this.cellTarget = null; this.selectorArrStr = ''; this.ctx.clearSelector(); this.ctx.focusCell = undefined; this.inputEl.blur(); this.ctx.emit('draw'); } destroy() { this.editorEl?.remove(); } } //# sourceMappingURL=Editor.js.map