From 497d7465c5291e7f2fb18a0801b4a11fb12e35d7 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Mon, 27 Jul 2026 18:34:58 +0800 Subject: [PATCH 1/3] fix: remount input editor safely --- ...input-editor-remount_2026-07-27-20-25.json | 11 +++ packages/vtable-editors/src/input-editor.ts | 36 +++++---- ...issue-4810-edit-cell-double-click-blank.ts | 73 +++++++++++++++++++ packages/vtable/examples/menu.ts | 4 + 4 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 common/changes/@visactor/vtable-editors/fix-issue-4810-input-editor-remount_2026-07-27-20-25.json create mode 100644 packages/vtable/examples/debug/issue-4810-edit-cell-double-click-blank.ts diff --git a/common/changes/@visactor/vtable-editors/fix-issue-4810-input-editor-remount_2026-07-27-20-25.json b/common/changes/@visactor/vtable-editors/fix-issue-4810-input-editor-remount_2026-07-27-20-25.json new file mode 100644 index 0000000000..cb398d7203 --- /dev/null +++ b/common/changes/@visactor/vtable-editors/fix-issue-4810-input-editor-remount_2026-07-27-20-25.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable-editors", + "comment": "fix: safely remount and reposition input editor when editing restarts", + "type": "patch" + } + ], + "packageName": "@visactor/vtable-editors", + "email": "892739385@qq.com" +} diff --git a/packages/vtable-editors/src/input-editor.ts b/packages/vtable-editors/src/input-editor.ts index ece3c60398..b67cea442a 100644 --- a/packages/vtable-editors/src/input-editor.ts +++ b/packages/vtable-editors/src/input-editor.ts @@ -98,6 +98,18 @@ export class InputEditor implements IEditor { this.eventHandlers.push({ type: 'paste', handler: pasteHandler }); } + protected ensureElementMounted(container: HTMLElement) { + if (!this.element) { + this.createElement(); + return; + } + + if (!container.contains(this.element)) { + this.element.parentElement?.removeChild(this.element); + this.container.appendChild(this.element); + } + } + setValue(value: string) { this.element.value = typeof value !== 'undefined' ? value : ''; } @@ -118,14 +130,7 @@ export class InputEditor implements IEditor { if (selectCell.col !== this.col || selectCell.row !== this.row) { return; } - if (!this.element) { - this.createElement(); - } else { - if (!container.contains(this.element)) { - this.element.parentElement.removeChild(this.element); - this.container.appendChild(this.element); - } - } + this.ensureElementMounted(container); this.element.style.opacity = '0'; //这个pointerEvents = 'none'很重要,如果没有的话会引起vtable.getElement()元素和这里的element元素的focus和blur的切换, //也会引起mouseleave_table mouseleave_cell和mouseenter的切换 @@ -141,20 +146,13 @@ export class InputEditor implements IEditor { this.table = table; this.col = col; this.row = row; - if (!this.element) { - this.createElement(); - if (referencePosition?.rect) { - this.adjustPosition(referencePosition.rect); - } - } else { - if (!container.contains(this.element)) { - this.element.parentElement.removeChild(this.element); - this.container.appendChild(this.element); - } - } + this.ensureElementMounted(container); if (value !== undefined && value !== null) { this.setValue(value); } + if (referencePosition?.rect) { + this.adjustPosition(referencePosition.rect); + } //防止调用过prepareEdit 后,元素的显示和可操作性被影响 this.element.style.opacity = '1'; this.element.style.pointerEvents = 'auto'; diff --git a/packages/vtable/examples/debug/issue-4810-edit-cell-double-click-blank.ts b/packages/vtable/examples/debug/issue-4810-edit-cell-double-click-blank.ts new file mode 100644 index 0000000000..0ca7934ad5 --- /dev/null +++ b/packages/vtable/examples/debug/issue-4810-edit-cell-double-click-blank.ts @@ -0,0 +1,73 @@ +import * as VTable from '../../src'; +import { InputEditor } from '@visactor/vtable-editors'; + +const CONTAINER_ID = 'vTable'; +const inputEditor = new InputEditor({}); +VTable.register.editor('issue4810-input', inputEditor); + +const createStatusBar = () => { + const container = document.getElementById(CONTAINER_ID)!; + const status = document.createElement('div'); + status.id = 'issue4810Status'; + status.style.cssText = 'height: 32px; line-height: 32px; font-size: 13px; color: #333;'; + status.textContent = 'Click "Check double edit" to verify issue #4810.'; + + const button = document.createElement('button'); + button.textContent = 'Check double edit'; + button.style.cssText = 'margin: 0 0 8px 8px;'; + button.onclick = () => checkDoubleEdit(); + + container.parentElement?.insertBefore(status, container); + status.appendChild(button); +}; + +const checkDoubleEdit = () => { + const tableInstance = (window as any).tableInstance as VTable.ListTable; + const status = document.getElementById('issue4810Status')!; + + try { + tableInstance.startEditCell(0, tableInstance.columnHeaderLevelCount); + inputEditor.getInputElement()?.remove(); + (tableInstance.editorManager as any).editingEditor = null; + tableInstance.startEditCell(1, tableInstance.columnHeaderLevelCount); + } catch (err) { + status.textContent = `FAIL | ${(err as Error).message}`; + return status.textContent; + } + + const inputElement = inputEditor.getInputElement(); + const tableElement = tableInstance.getElement(); + const pass = + !!inputElement && + tableElement.contains(inputElement) && + inputElement.style.opacity === '1' && + inputElement.style.pointerEvents === 'auto' && + inputElement.style.left !== ''; + + status.textContent = `${pass ? 'PASS' : 'FAIL'} | mounted=${ + !!inputElement && tableElement.contains(inputElement) + }, left=${inputElement?.style.left}, top=${inputElement?.style.top}`; + return status.textContent; +}; + +export function createTable() { + const option: VTable.ListTableConstructorOptions = { + records: [ + { name: 'Alice', age: 20 }, + { name: 'Bob', age: 21 } + ], + columns: [ + { field: 'name', title: 'Name', width: 180, editor: 'issue4810-input' }, + { field: 'age', title: 'Age', width: 120, editor: 'issue4810-input' } + ], + editCellTrigger: 'doubleclick', + editor: 'issue4810-input', + widthMode: 'standard', + defaultRowHeight: 36 + }; + + createStatusBar(); + const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID)!, option); + (window as any).tableInstance = tableInstance; + (window as any).issue4810Run = checkDoubleEdit; +} diff --git a/packages/vtable/examples/menu.ts b/packages/vtable/examples/menu.ts index 2474d4e3ae..f51c4e2d48 100644 --- a/packages/vtable/examples/menu.ts +++ b/packages/vtable/examples/menu.ts @@ -54,6 +54,10 @@ export const menus = [ path: 'debug', name: 'issue-5213-row-series-number-aggregation' }, + { + path: 'debug', + name: 'issue-4810-edit-cell-double-click-blank' + }, { path: 'debug', name: 'header-frame-border-null-color' From 7317d702599924ec6ccdfdc1aada7e2d9f649781 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 28 Jul 2026 10:58:11 +0800 Subject: [PATCH 2/3] fix(editors): refresh reused textarea editor --- packages/vtable-editors/src/input-editor.ts | 2 +- .../vtable-editors/src/textArea-editor.ts | 27 ++++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/vtable-editors/src/input-editor.ts b/packages/vtable-editors/src/input-editor.ts index b67cea442a..805b3b31a1 100644 --- a/packages/vtable-editors/src/input-editor.ts +++ b/packages/vtable-editors/src/input-editor.ts @@ -106,7 +106,7 @@ export class InputEditor implements IEditor { if (!container.contains(this.element)) { this.element.parentElement?.removeChild(this.element); - this.container.appendChild(this.element); + container.appendChild(this.element); } } diff --git a/packages/vtable-editors/src/textArea-editor.ts b/packages/vtable-editors/src/textArea-editor.ts index 4eede430ca..5947e0c0f2 100644 --- a/packages/vtable-editors/src/textArea-editor.ts +++ b/packages/vtable-editors/src/textArea-editor.ts @@ -65,18 +65,27 @@ export class TextAreaEditor implements IEditor { return this.element?.value; } - onStart({ value, referencePosition, container, endEdit }: EditContext) { - this.container = container; - this.successCallback = endEdit; + protected ensureElementMounted(container: HTMLElement) { if (!this.element) { this.createElement(); + return; + } - if (value !== undefined && value !== null) { - this.setValue(value); - } - if (referencePosition?.rect) { - this.adjustPosition(referencePosition.rect); - } + if (!container.contains(this.element)) { + this.element.parentElement?.removeChild(this.element); + container.appendChild(this.element); + } + } + + onStart({ value, referencePosition, container, endEdit }: EditContext) { + this.container = container; + this.successCallback = endEdit; + this.ensureElementMounted(container); + if (value !== undefined && value !== null) { + this.setValue(value); + } + if (referencePosition?.rect) { + this.adjustPosition(referencePosition.rect); } this.element.focus(); // do nothing From 637a1d766428cb7a3a26bf9ed94c952b1223e5d4 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Tue, 28 Jul 2026 11:47:56 +0800 Subject: [PATCH 3/3] fix(editors): clear reused editor empty values --- packages/vtable-editors/src/input-editor.ts | 8 +++----- packages/vtable-editors/src/textArea-editor.ts | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/vtable-editors/src/input-editor.ts b/packages/vtable-editors/src/input-editor.ts index 805b3b31a1..9d190491bd 100644 --- a/packages/vtable-editors/src/input-editor.ts +++ b/packages/vtable-editors/src/input-editor.ts @@ -110,8 +110,8 @@ export class InputEditor implements IEditor { } } - setValue(value: string) { - this.element.value = typeof value !== 'undefined' ? value : ''; + setValue(value: string | null | undefined) { + this.element.value = value ?? ''; } getValue() { @@ -147,9 +147,7 @@ export class InputEditor implements IEditor { this.col = col; this.row = row; this.ensureElementMounted(container); - if (value !== undefined && value !== null) { - this.setValue(value); - } + this.setValue(value); if (referencePosition?.rect) { this.adjustPosition(referencePosition.rect); } diff --git a/packages/vtable-editors/src/textArea-editor.ts b/packages/vtable-editors/src/textArea-editor.ts index 5947e0c0f2..78612cc6ab 100644 --- a/packages/vtable-editors/src/textArea-editor.ts +++ b/packages/vtable-editors/src/textArea-editor.ts @@ -57,8 +57,8 @@ export class TextAreaEditor implements IEditor { }); } - setValue(value: string) { - this.element.value = typeof value !== 'undefined' ? value : ''; + setValue(value: string | null | undefined) { + this.element.value = value ?? ''; } getValue() { @@ -81,9 +81,7 @@ export class TextAreaEditor implements IEditor { this.container = container; this.successCallback = endEdit; this.ensureElementMounted(container); - if (value !== undefined && value !== null) { - this.setValue(value); - } + this.setValue(value); if (referencePosition?.rect) { this.adjustPosition(referencePosition.rect); }