Fix: Resolve editing bugs including stale editor, scope errors, and scroll sync
This commit is contained in:
parent
3e0dcc7df5
commit
cc6d2363b0
80
src/index.js
80
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)'">
|
||||
<span $text="item[f.id] ?? ''" class="text-truncate"></span>
|
||||
<div $if="f.type === 'switch'" class="form-check form-switch d-flex justify-content-center w-100 m-0 p-0">
|
||||
<input class="form-check-input m-0" type="checkbox" $checked="item[f.id]" disabled>
|
||||
</div>
|
||||
<span $if="f.type !== 'switch'" $text="item[f.id] ?? ''" class="text-truncate"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div $if="(this.state?.postHeight || 0) > 0" $style="'height:' + this.state.postHeight + 'px;'" class="flex-shrink-0"></div>
|
||||
|
||||
<div $if="this.state.editing" class="dt-editor-container" $style="this.state.editing.style" $onclick="event.stopPropagation()" $onmousedown="event.stopPropagation()">
|
||||
<AutoForm inline class="h-100 w-100" $onkeydown="(event.key === 'Enter' && thisNode.closest('DataTable').state.editing.field.type !== 'textarea') && thisNode.closest('DataTable').finishEdit(); event.key === 'Escape' && thisNode.closest('DataTable').cancelEdit()"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dt-footer border-top bg-light d-flex align-items-center px-2 py-1 small text-muted" style="height:32px">
|
||||
@ -142,6 +205,19 @@ Component.register('DataTable', container => {
|
||||
.dt-cell-selected {
|
||||
background-color: var(--bs-primary-bg-subtle) !important;
|
||||
}
|
||||
.dt-editor-container {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
background: var(--bs-body-bg);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
||||
border: 1px solid var(--bs-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.dt-editor-container .auto-form-root, .dt-editor-container form {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.btn-xs {
|
||||
padding: 1px 5px;
|
||||
line-height: 1.5;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user