diff --git a/src/index.js b/src/index.js index 987f208..76a1543 100644 --- a/src/index.js +++ b/src/index.js @@ -10,7 +10,9 @@ Component.register('DataTable', container => { Object.assign(state, { list: [], fields: [], _renderedList: [], prevHeight: 0, postHeight: 0, _listStartIndex: 0, - selectedRowCount: 0 + selectedRowCount: 0, + // --- Editing State --- + editing: null // { row, field, node, style } }) const perf = createPerfMonitor(); @@ -19,6 +21,10 @@ Component.register('DataTable', container => { const selection = createSelectionManager(container, state); const scroll = createScrollManager(container, state, (renderedCount) => { + if (state.editing) { + // Close editor on scroll to prevent floating away + container.cancelEdit(); + } selection.applySelectionUI(); }); @@ -49,6 +55,55 @@ Component.register('DataTable', container => { container.updateSelect = selection.updateSelect; container.deleteSelected = selection.deleteSelected; + // --- Editing Logic --- + container.editCell = (row, field, cellNode) => { + const body = container.querySelector('.dt-body'); + const rect = cellNode.getBoundingClientRect(); + const bodyRect = body.getBoundingClientRect(); + + // Ensure row is a State object for reliable binding + const listIdx = state.list.indexOf(row); + let targetRow = row; + if (listIdx !== -1 && !row.__watch) { + targetRow = NewState(row); + state.list[listIdx] = targetRow; + } + + state.editing = { + row: targetRow, + field, + node: cellNode, + style: `left:${rect.left - bodyRect.left}px; top:${rect.top - bodyRect.top}px; width:${rect.width}px; height:${rect.height}px;` + }; + + // Optimization: $. attributes are not reactive. We must manually update the editor + // if it's already in the DOM, or the next frame after $if renders it. + const syncEditor = () => { + const editor = container.querySelector('.dt-editor-container AutoForm'); + if (editor) { + editor.state.schema = [{ ...field, name: field.id, label: '' }]; + editor.data = targetRow; + RefreshState(editor); + const el = editor.querySelector('.form-control, .form-select, .form-check-input'); + if (el) el.focus(); + } else { + requestAnimationFrame(syncEditor); + } + }; + syncEditor(); + }; + + container.finishEdit = () => { + const node = state.editing?.node; + state.editing = null; + if (node) RefreshState(node); + container.focus(); // Return focus to table + }; + + container.cancelEdit = () => { + state.editing = null; + }; + // Copy & Paste (simplified) const escapeTSV = val => { const str = String(val ?? '') @@ -100,11 +155,19 @@ Component.register('DataTable', container => { class="dt-cell border-end px-2 d-flex align-items-center" $onmousedown="this.startSelect(rIdx + this.state._listStartIndex, fIdx, event)" $onmouseenter="this.updateSelect(rIdx + this.state._listStartIndex, fIdx)" + $ondblclick="this.editCell(item, f, thisNode)" $style="'width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px); min-width: var(--w-' + f.id + ', ' + (f.width || 150) + 'px)'"> - +