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
JavaScript
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