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.
453 lines • 16.4 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
});
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;
const { xArr, yArr } = this.ctx.selector;
this.selectorArrStr = JSON.stringify(xArr) + JSON.stringify(yArr);
});
// 滚动时,结束编辑
this.ctx.on('onScroll', () => {
if (this.enable) {
this.doneEdit();
}
});
this.ctx.on('cellHeaderMousedown', () => {
if (this.enable) {
this.doneEdit();
}
this.cellTarget = null;
});
this.ctx.on('hoverIconClick', (cell) => {
if (this.ctx.disableHoverIconClick) {
return;
}
this.editCell(cell.rowIndex, cell.colIndex);
});
this.ctx.on('cellMousedown', () => {
this.inputEl.focus({ preventScroll: true });
});
this.ctx.on('keydown', (e) => {
if (!this.ctx.isTarget(e)) {
return;
}
if (!this.ctx.focusCell) {
return;
}
if (this.ctx.finding) {
return;
}
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.code === '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.code === '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.code === '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',
'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;
}
this.startEdit(true);
});
// 重绘可能会导致cellClick事件不能触发,调整用按下cellMouseup按下延时赋值cellTarget
this.ctx.on('cellMouseup', (cell) => {
// 如果是选择器,不进入编辑模式
if (this.ctx.isPointer) {
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.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() {
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, 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;
}
// 显示编辑器
this.editorEl.style.display = 'inline-block';
this.editorEl.style.zIndex = '100';
this.editorEl.style.left = `${this.drawX - 1}px`;
this.editorEl.style.top = `${this.drawY - 1}px`;
this.editorEl.style.bottom = `auto`;
this.editorEl.style.maxHeight = `${maxHeight}px`;
if (editorType === 'text') {
this.inputEl.style.display = 'inline-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`;
this.inputEl.value = ''; // 清空
if (value !== null) {
this.inputEl.value = value;
this.inputEl.focus({ preventScroll: true });
}
}
else {
this.inputEl.style.display = 'none';
}
if (this.inputEl.scrollHeight > height || this.drawY < header.height) {
this.autoSize();
}
}
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);
}
}
}
startEdit(ignoreValue = false) {
this.cancel = false;
// 如果不启用点击选择器编辑
const { ENABLE_EDIT_CLICK_SELECTOR } = this.ctx.config;
if (!ENABLE_EDIT_CLICK_SELECTOR) {
return;
}
// 如果是调整边界位置,不进入编辑模式
if (this.ctx.adjustPositioning) {
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 isVisible = focusCell.isVerticalVisible() && focusCell.isHorizontalVisible();
if (!isVisible) {
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');
}
}
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);
// 触发绘制,刷新
this.ctx.emit('draw');
}
}
doneEdit() {
if (!this.enable) {
return;
}
this.doneEditByInput();
this.ctx.emit('doneEdit', this.cellTarget);
this.enable = false;
this.ctx.editing = false;
this.inputEl.style.display = 'inline-block';
this.editorEl.style.zIndex = '-1';
setTimeout(() => {
this.inputEl.focus({ preventScroll: true });
}, 0);
this.ctx.emit('draw');
}
clearEditor() {
this.doneEdit();
this.cellTarget = null;
this.selectorArrStr = '';
this.ctx.clearSelector();
this.ctx.focusCell = undefined;
this.ctx.emit('draw');
}
destroy() {
this.editorEl?.remove();
}
}
//# sourceMappingURL=Editor.js.map