Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0375508f0 | |||
| 3e86ba034b |
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-04-14 - 3.78.3 - fix(dees-table)
|
||||
stabilize live updates by reusing row DOM and avoiding redundant layout recalculations
|
||||
|
||||
- reuse keyed table rows across live-sorted updates so existing row elements persist while cells reorder
|
||||
- limit flash animation restarts to changed cells by tracking per-cell flash tokens
|
||||
- avoid repeated column width measurements unless table layout inputs actually change
|
||||
- replace async header and footer action rendering with direct mapped output to prevent comment node growth during updates
|
||||
- add Chromium live update tests covering width measurement stability, comment growth, and row DOM reuse
|
||||
|
||||
## 2026-04-12 - 3.78.2 - fix(deps)
|
||||
bump @design.estate/dees-wcctools to ^3.9.0
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@design.estate/dees-catalog",
|
||||
"version": "3.78.2",
|
||||
"version": "3.78.3",
|
||||
"private": false,
|
||||
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
|
||||
"main": "dist_ts_web/index.js",
|
||||
|
||||
167
test/test.dees-table-liveupdates.chromium.ts
Normal file
167
test/test.dees-table-liveupdates.chromium.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as deesCatalog from '../ts_web/index.js';
|
||||
import type {
|
||||
Column,
|
||||
ISortDescriptor,
|
||||
} from '../ts_web/elements/00group-dataview/dees-table/index.js';
|
||||
|
||||
interface ITestRow {
|
||||
id: string;
|
||||
score: number;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const testColumns: Column<ITestRow>[] = [
|
||||
{ key: 'id', header: 'ID' },
|
||||
{ key: 'score', header: 'Score' },
|
||||
{ key: 'label', header: 'Label' },
|
||||
];
|
||||
|
||||
const scoreSort: ISortDescriptor[] = [{ key: 'score', dir: 'desc' }];
|
||||
|
||||
const waitForNextFrame = async () => {
|
||||
await new Promise<void>((resolve) => {
|
||||
requestAnimationFrame(() => resolve());
|
||||
});
|
||||
};
|
||||
|
||||
const waitForMacrotask = async () => {
|
||||
await new Promise<void>((resolve) => {
|
||||
window.setTimeout(() => resolve(), 0);
|
||||
});
|
||||
};
|
||||
|
||||
const settleTable = async (table: deesCatalog.DeesTable<ITestRow>) => {
|
||||
await table.updateComplete;
|
||||
await waitForNextFrame();
|
||||
await waitForMacrotask();
|
||||
await table.updateComplete;
|
||||
};
|
||||
|
||||
const createRows = (iteration: number): ITestRow[] => {
|
||||
const cycle = iteration % 3;
|
||||
|
||||
if (cycle === 0) {
|
||||
return [
|
||||
{ id: 'alpha', score: 60, label: `Alpha ${iteration}` },
|
||||
{ id: 'beta', score: 20, label: `Beta ${iteration}` },
|
||||
{ id: 'gamma', score: 40, label: `Gamma ${iteration}` },
|
||||
];
|
||||
}
|
||||
|
||||
if (cycle === 1) {
|
||||
return [
|
||||
{ id: 'alpha', score: 30, label: `Alpha ${iteration}` },
|
||||
{ id: 'beta', score: 70, label: `Beta ${iteration}` },
|
||||
{ id: 'gamma', score: 50, label: `Gamma ${iteration}` },
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
{ id: 'alpha', score: 55, label: `Alpha ${iteration}` },
|
||||
{ id: 'beta', score: 35, label: `Beta ${iteration}` },
|
||||
{ id: 'gamma', score: 75, label: `Gamma ${iteration}` },
|
||||
];
|
||||
};
|
||||
|
||||
const createTable = (
|
||||
rows: ITestRow[],
|
||||
highlightUpdates: 'none' | 'flash'
|
||||
): deesCatalog.DeesTable<ITestRow> => {
|
||||
const table = new deesCatalog.DeesTable<ITestRow>();
|
||||
table.searchable = false;
|
||||
table.columns = testColumns;
|
||||
table.rowKey = 'id';
|
||||
table.sortBy = scoreSort;
|
||||
table.highlightUpdates = highlightUpdates;
|
||||
table.data = rows;
|
||||
document.body.appendChild(table);
|
||||
return table;
|
||||
};
|
||||
|
||||
const countComments = (root: Node): number => {
|
||||
const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
|
||||
let count = 0;
|
||||
while (walker.nextNode()) count++;
|
||||
return count;
|
||||
};
|
||||
|
||||
const getBodyRows = (table: deesCatalog.DeesTable<ITestRow>): HTMLTableRowElement[] =>
|
||||
Array.from(
|
||||
table.shadowRoot?.querySelectorAll('tbody tr[data-row-idx]') ?? []
|
||||
) as HTMLTableRowElement[];
|
||||
|
||||
const getRenderedRowIds = (table: deesCatalog.DeesTable<ITestRow>): string[] =>
|
||||
getBodyRows(table).map((row) => row.cells[0]?.textContent?.trim() ?? '');
|
||||
|
||||
const getRenderedRowMap = (
|
||||
table: deesCatalog.DeesTable<ITestRow>
|
||||
): Map<string, HTMLTableRowElement> => {
|
||||
const rowMap = new Map<string, HTMLTableRowElement>();
|
||||
for (const row of getBodyRows(table)) {
|
||||
const rowId = row.cells[0]?.textContent?.trim() ?? '';
|
||||
if (rowId) rowMap.set(rowId, row);
|
||||
}
|
||||
return rowMap;
|
||||
};
|
||||
|
||||
tap.test('dees-table avoids repeated width measurement and comment growth on live updates', async () => {
|
||||
const table = new deesCatalog.DeesTable<ITestRow>();
|
||||
let widthMeasureCalls = 0;
|
||||
const originalDetermineColumnWidths = table.determineColumnWidths.bind(table);
|
||||
table.determineColumnWidths = (async () => {
|
||||
widthMeasureCalls++;
|
||||
await originalDetermineColumnWidths();
|
||||
}) as typeof table.determineColumnWidths;
|
||||
|
||||
table.searchable = false;
|
||||
table.columns = testColumns;
|
||||
table.rowKey = 'id';
|
||||
table.sortBy = scoreSort;
|
||||
table.highlightUpdates = 'none';
|
||||
table.data = createRows(0);
|
||||
document.body.appendChild(table);
|
||||
|
||||
try {
|
||||
await settleTable(table);
|
||||
|
||||
const initialWidthMeasureCalls = widthMeasureCalls;
|
||||
const initialCommentCount = countComments(table.shadowRoot!);
|
||||
|
||||
expect(initialWidthMeasureCalls).toBeGreaterThan(0);
|
||||
|
||||
for (let iteration = 1; iteration <= 10; iteration++) {
|
||||
table.data = createRows(iteration);
|
||||
await settleTable(table);
|
||||
}
|
||||
|
||||
expect(widthMeasureCalls).toEqual(initialWidthMeasureCalls);
|
||||
expect(countComments(table.shadowRoot!)).toEqual(initialCommentCount);
|
||||
} finally {
|
||||
table.remove();
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('dees-table reuses row DOM while flashing live-sorted updates', async () => {
|
||||
const table = createTable(createRows(0), 'flash');
|
||||
|
||||
try {
|
||||
await settleTable(table);
|
||||
|
||||
const initialRowMap = getRenderedRowMap(table);
|
||||
|
||||
table.data = createRows(1);
|
||||
await settleTable(table);
|
||||
|
||||
const updatedRowMap = getRenderedRowMap(table);
|
||||
|
||||
expect(getRenderedRowIds(table)).toEqual(['beta', 'gamma', 'alpha']);
|
||||
expect(updatedRowMap.get('alpha')).toEqual(initialRowMap.get('alpha'));
|
||||
expect(updatedRowMap.get('beta')).toEqual(initialRowMap.get('beta'));
|
||||
expect(updatedRowMap.get('gamma')).toEqual(initialRowMap.get('gamma'));
|
||||
} finally {
|
||||
table.remove();
|
||||
}
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-catalog',
|
||||
version: '3.78.2',
|
||||
version: '3.78.3',
|
||||
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
||||
}
|
||||
|
||||
@@ -293,9 +293,9 @@ export class DeesTable<T> extends DeesElement {
|
||||
private accessor __floatingActive: boolean = false;
|
||||
|
||||
// ─── Flash-on-update state (only populated when highlightUpdates === 'flash') ──
|
||||
/** rowId → set of colKey strings currently flashing. */
|
||||
/** rowId → (colKey → flash token) for cells currently flashing. */
|
||||
@state()
|
||||
private accessor __flashingCells: Map<string, Set<string>> = new Map();
|
||||
private accessor __flashingCells: Map<string, Map<string, number>> = new Map();
|
||||
|
||||
/** rowId → (colKey → last-seen resolved cell value). Populated per diff pass. */
|
||||
private __prevSnapshot?: Map<string, Map<string, unknown>>;
|
||||
@@ -303,7 +303,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
/** Single shared timer that clears __flashingCells after highlightDuration ms. */
|
||||
private __flashClearTimer?: ReturnType<typeof setTimeout>;
|
||||
|
||||
/** Monotonic counter bumped each flash batch so directives.keyed recreates the cell node and restarts the animation. */
|
||||
/** Monotonic counter bumped per flash batch so only changed cells restart their animation. */
|
||||
private __flashTick: number = 0;
|
||||
|
||||
/** One-shot console.warn gate for missing rowKey in flash mode. */
|
||||
@@ -317,7 +317,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
columns: any;
|
||||
augment: boolean;
|
||||
displayFunction: any;
|
||||
data: any;
|
||||
displayShapeKey: string;
|
||||
out: Column<T>[];
|
||||
};
|
||||
private __memoViewData?: {
|
||||
@@ -329,8 +329,13 @@ export class DeesTable<T> extends DeesElement {
|
||||
effectiveColumns: Column<T>[];
|
||||
out: T[];
|
||||
};
|
||||
/** Tracks the (data, columns) pair that `determineColumnWidths()` last sized for. */
|
||||
private __columnsSizedFor?: { data: any; columns: any };
|
||||
/** Tracks the layout inputs that `determineColumnWidths()` last sized for. */
|
||||
private __columnsSizedFor?: {
|
||||
effectiveColumns: Column<T>[];
|
||||
showSelectionCheckbox: boolean;
|
||||
inRowActionCount: number;
|
||||
table: HTMLTableElement;
|
||||
};
|
||||
|
||||
// ─── Virtualization state ────────────────────────────────────────────
|
||||
/** Estimated row height (px). Measured once from the first rendered row. */
|
||||
@@ -409,15 +414,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
const view: T[] = (this as any)._lastViewData ?? [];
|
||||
const item = view.find((r) => this.getRowId(r) === this.__focusedCell!.rowId);
|
||||
if (!item) return;
|
||||
const allCols: Column<T>[] =
|
||||
Array.isArray(this.columns) && this.columns.length > 0
|
||||
? computeEffectiveColumnsFn(
|
||||
this.columns,
|
||||
this.augmentFromDisplayFunction,
|
||||
this.displayFunction,
|
||||
this.data
|
||||
)
|
||||
: computeColumnsFromDisplayFunctionFn(this.displayFunction, this.data);
|
||||
const allCols = this.__getEffectiveColumns();
|
||||
const col = allCols.find((c) => String(c.key) === this.__focusedCell!.colKey);
|
||||
if (!col || !this.__isColumnEditable(col)) return;
|
||||
eventArg.preventDefault();
|
||||
@@ -469,15 +466,24 @@ export class DeesTable<T> extends DeesElement {
|
||||
* that affect it. Avoids re-running `computeEffectiveColumnsFn` /
|
||||
* `computeColumnsFromDisplayFunctionFn` on every Lit update.
|
||||
*/
|
||||
private __getDisplayFunctionShapeKey(): string {
|
||||
if (!this.data || this.data.length === 0) return '';
|
||||
const firstTransformedItem = this.displayFunction(this.data[0]) ?? {};
|
||||
return Object.keys(firstTransformedItem).join('\u0000');
|
||||
}
|
||||
|
||||
private __getEffectiveColumns(): Column<T>[] {
|
||||
const usingColumns = Array.isArray(this.columns) && this.columns.length > 0;
|
||||
const displayShapeKey = !usingColumns || this.augmentFromDisplayFunction
|
||||
? this.__getDisplayFunctionShapeKey()
|
||||
: '';
|
||||
const cache = this.__memoEffectiveCols;
|
||||
if (
|
||||
cache &&
|
||||
cache.columns === this.columns &&
|
||||
cache.augment === this.augmentFromDisplayFunction &&
|
||||
cache.displayFunction === this.displayFunction &&
|
||||
cache.data === this.data
|
||||
cache.displayShapeKey === displayShapeKey
|
||||
) {
|
||||
return cache.out;
|
||||
}
|
||||
@@ -493,7 +499,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
columns: this.columns,
|
||||
augment: this.augmentFromDisplayFunction,
|
||||
displayFunction: this.displayFunction,
|
||||
data: this.data,
|
||||
displayShapeKey,
|
||||
out,
|
||||
};
|
||||
return out;
|
||||
@@ -543,6 +549,9 @@ export class DeesTable<T> extends DeesElement {
|
||||
public render(): TemplateResult {
|
||||
const effectiveColumns = this.__getEffectiveColumns();
|
||||
const viewData = this.__getViewData(effectiveColumns);
|
||||
const headerActions = this.getActionsForType('header');
|
||||
const footerActions = this.getActionsForType('footer');
|
||||
const inRowActions = this.getActionsForType('inRow');
|
||||
(this as any)._lastViewData = viewData;
|
||||
|
||||
// Virtualization slice — only the rows in `__virtualRange` actually
|
||||
@@ -572,29 +581,22 @@ export class DeesTable<T> extends DeesElement {
|
||||
<div class="heading heading2">${this.heading2}</div>
|
||||
</div>
|
||||
<div class="headerActions">
|
||||
${directives.resolveExec(async () => {
|
||||
const resultArray: TemplateResult[] = [];
|
||||
for (const action of this.dataActions) {
|
||||
if (!action.type?.includes('header')) continue;
|
||||
resultArray.push(
|
||||
html`<div
|
||||
class="headerAction"
|
||||
@click=${() => {
|
||||
action.actionFunc({
|
||||
item: this.selectedDataRow,
|
||||
table: this,
|
||||
});
|
||||
}}
|
||||
>
|
||||
${action.iconName
|
||||
? html`<dees-icon .iconSize=${14} .icon=${action.iconName}></dees-icon>
|
||||
${action.name}`
|
||||
: action.name}
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
return resultArray;
|
||||
})}
|
||||
${headerActions.map(
|
||||
(action) => html`<div
|
||||
class="headerAction"
|
||||
@click=${() => {
|
||||
action.actionFunc({
|
||||
item: this.selectedDataRow,
|
||||
table: this,
|
||||
});
|
||||
}}
|
||||
>
|
||||
${action.iconName
|
||||
? html`<dees-icon .iconSize=${14} .icon=${action.iconName}></dees-icon>
|
||||
${action.name}`
|
||||
: action.name}
|
||||
</div>`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="headingSeparation"></div>
|
||||
@@ -658,11 +660,11 @@ export class DeesTable<T> extends DeesElement {
|
||||
: html``}
|
||||
${directives.repeat(
|
||||
renderRows,
|
||||
(itemArg, sliceIdx) => `${this.getRowId(itemArg)}::${renderStart + sliceIdx}`,
|
||||
(itemArg) => this.getRowId(itemArg),
|
||||
(itemArg, sliceIdx) => {
|
||||
const rowIndex = renderStart + sliceIdx;
|
||||
const rowId = this.getRowId(itemArg);
|
||||
const flashSet = this.__flashingCells.get(rowId);
|
||||
const flashTokens = this.__flashingCells.get(rowId);
|
||||
return html`
|
||||
<tr
|
||||
data-row-idx=${rowIndex}
|
||||
@@ -694,7 +696,8 @@ export class DeesTable<T> extends DeesElement {
|
||||
const isEditing =
|
||||
this.__editingCell?.rowId === rowId &&
|
||||
this.__editingCell?.colKey === editKey;
|
||||
const isFlashing = !!flashSet?.has(editKey);
|
||||
const flashToken = flashTokens?.get(editKey);
|
||||
const isFlashing = flashToken !== undefined;
|
||||
const useFlashBorder = isFlashing && !!col.flashBorder;
|
||||
const cellClasses = [
|
||||
isEditable ? 'editable' : '',
|
||||
@@ -720,7 +723,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
>
|
||||
${isFlashing
|
||||
? directives.keyed(
|
||||
`${rowId}:${editKey}:${this.__flashTick}`,
|
||||
`${rowId}:${editKey}:${flashToken}`,
|
||||
innerHtml
|
||||
)
|
||||
: innerHtml}
|
||||
@@ -728,11 +731,11 @@ export class DeesTable<T> extends DeesElement {
|
||||
`;
|
||||
})}
|
||||
${(() => {
|
||||
if (this.dataActions && this.dataActions.length > 0) {
|
||||
if (inRowActions.length > 0) {
|
||||
return html`
|
||||
<td class="actionsCol">
|
||||
<div class="actionsContainer">
|
||||
${this.getActionsForType('inRow').map(
|
||||
${inRowActions.map(
|
||||
(actionArg) => html`
|
||||
<div
|
||||
class="action"
|
||||
@@ -780,29 +783,22 @@ export class DeesTable<T> extends DeesElement {
|
||||
selected
|
||||
</div>
|
||||
<div class="footerActions">
|
||||
${directives.resolveExec(async () => {
|
||||
const resultArray: TemplateResult[] = [];
|
||||
for (const action of this.dataActions) {
|
||||
if (!action.type?.includes('footer')) continue;
|
||||
resultArray.push(
|
||||
html`<div
|
||||
class="footerAction"
|
||||
@click=${() => {
|
||||
action.actionFunc({
|
||||
item: this.selectedDataRow,
|
||||
table: this,
|
||||
});
|
||||
}}
|
||||
>
|
||||
${action.iconName
|
||||
? html`<dees-icon .iconSize=${14} .icon=${action.iconName}></dees-icon>
|
||||
${action.name}`
|
||||
: action.name}
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
return resultArray;
|
||||
})}
|
||||
${footerActions.map(
|
||||
(action) => html`<div
|
||||
class="footerAction"
|
||||
@click=${() => {
|
||||
action.actionFunc({
|
||||
item: this.selectedDataRow,
|
||||
table: this,
|
||||
});
|
||||
}}
|
||||
>
|
||||
${action.iconName
|
||||
? html`<dees-icon .iconSize=${14} .icon=${action.iconName}></dees-icon>
|
||||
${action.name}`
|
||||
: action.name}
|
||||
</div>`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</dees-tile>
|
||||
@@ -1160,7 +1156,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
/**
|
||||
* Measures the height of the first rendered body row and stores it for
|
||||
* subsequent virtualization math. Idempotent — only measures once per
|
||||
* `data`/`columns` pair (cleared in `updated()` when those change).
|
||||
* rendered table layout (cleared in `updated()` when that layout changes).
|
||||
*/
|
||||
private __measureRowHeight() {
|
||||
if (!this.virtualized || this.__rowHeightMeasured) return;
|
||||
@@ -1426,20 +1422,16 @@ export class DeesTable<T> extends DeesElement {
|
||||
if (newlyFlashing.size === 0) return;
|
||||
|
||||
// Merge with any in-flight flashes from a rapid second update so a cell
|
||||
// that changes twice before its animation ends gets a single clean
|
||||
// restart (via __flashTick / directives.keyed) instead of stacking.
|
||||
// that changes twice before its animation ends gets a clean restart,
|
||||
// while unrelated cells keep their existing DOM subtree.
|
||||
const flashToken = ++this.__flashTick;
|
||||
const nextFlashingCells = new Map(this.__flashingCells);
|
||||
for (const [rowId, cols] of newlyFlashing) {
|
||||
const existing = this.__flashingCells.get(rowId);
|
||||
if (existing) {
|
||||
for (const c of cols) existing.add(c);
|
||||
} else {
|
||||
this.__flashingCells.set(rowId, cols);
|
||||
}
|
||||
const existing = new Map(nextFlashingCells.get(rowId) ?? []);
|
||||
for (const colKey of cols) existing.set(colKey, flashToken);
|
||||
nextFlashingCells.set(rowId, existing);
|
||||
}
|
||||
this.__flashTick++;
|
||||
// Reactivity nudge: we've mutated the Map in place, so give Lit a fresh
|
||||
// reference so the @state change fires for render.
|
||||
this.__flashingCells = new Map(this.__flashingCells);
|
||||
this.__flashingCells = nextFlashingCells;
|
||||
if (this.__flashClearTimer) clearTimeout(this.__flashClearTimer);
|
||||
this.__flashClearTimer = setTimeout(() => {
|
||||
this.__flashingCells = new Map();
|
||||
@@ -1449,6 +1441,9 @@ export class DeesTable<T> extends DeesElement {
|
||||
|
||||
public async updated(changedProperties: Map<string | number | symbol, unknown>): Promise<void> {
|
||||
super.updated(changedProperties);
|
||||
const effectiveColumns = this.__getEffectiveColumns();
|
||||
const currentTable = this.shadowRoot?.querySelector('table') ?? null;
|
||||
const inRowActionCount = this.getActionsForType('inRow').length;
|
||||
|
||||
// Feed highlightDuration into the CSS variable so JS and CSS stay in
|
||||
// sync via a single source of truth.
|
||||
@@ -1456,15 +1451,23 @@ export class DeesTable<T> extends DeesElement {
|
||||
this.style.setProperty('--dees-table-flash-duration', `${this.highlightDuration}ms`);
|
||||
}
|
||||
|
||||
// Only re-measure column widths when the data or schema actually changed
|
||||
// (or on first paint). `determineColumnWidths` is the single biggest
|
||||
// first-paint cost — it forces multiple layout flushes per row.
|
||||
const dataOrColsChanged =
|
||||
!this.__columnsSizedFor ||
|
||||
this.__columnsSizedFor.data !== this.data ||
|
||||
this.__columnsSizedFor.columns !== this.columns;
|
||||
if (dataOrColsChanged) {
|
||||
this.__columnsSizedFor = { data: this.data, columns: this.columns };
|
||||
// Only re-measure column widths when layout-affecting inputs changed or
|
||||
// when a new <table> element was rendered after previously having none.
|
||||
const columnLayoutChanged =
|
||||
!!currentTable && (
|
||||
!this.__columnsSizedFor ||
|
||||
this.__columnsSizedFor.effectiveColumns !== effectiveColumns ||
|
||||
this.__columnsSizedFor.showSelectionCheckbox !== this.showSelectionCheckbox ||
|
||||
this.__columnsSizedFor.inRowActionCount !== inRowActionCount ||
|
||||
this.__columnsSizedFor.table !== currentTable
|
||||
);
|
||||
if (currentTable && columnLayoutChanged) {
|
||||
this.__columnsSizedFor = {
|
||||
effectiveColumns,
|
||||
showSelectionCheckbox: this.showSelectionCheckbox,
|
||||
inRowActionCount,
|
||||
table: currentTable,
|
||||
};
|
||||
this.determineColumnWidths();
|
||||
// Force re-measure of row height; structure may have changed.
|
||||
this.__rowHeightMeasured = false;
|
||||
@@ -1502,7 +1505,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
if (
|
||||
!this.fixedHeight &&
|
||||
this.data.length > 0 &&
|
||||
(this.__floatingActive || dataOrColsChanged)
|
||||
(this.__floatingActive || columnLayoutChanged)
|
||||
) {
|
||||
this.__syncFloatingHeader();
|
||||
}
|
||||
@@ -1804,10 +1807,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
* Used by the modal helper to render human-friendly labels.
|
||||
*/
|
||||
private _lookupColumnByKey(key: string): Column<T> | undefined {
|
||||
const usingColumns = Array.isArray(this.columns) && this.columns.length > 0;
|
||||
const effective = usingColumns
|
||||
? computeEffectiveColumnsFn(this.columns, this.augmentFromDisplayFunction, this.displayFunction, this.data)
|
||||
: computeColumnsFromDisplayFunctionFn(this.displayFunction, this.data);
|
||||
const effective = this.__getEffectiveColumns();
|
||||
return effective.find((c) => String(c.key) === key);
|
||||
}
|
||||
|
||||
@@ -2543,9 +2543,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
const view: T[] = (this as any)._lastViewData ?? [];
|
||||
if (view.length === 0) return;
|
||||
// Recompute editable columns from the latest effective set.
|
||||
const allCols: Column<T>[] = Array.isArray(this.columns) && this.columns.length > 0
|
||||
? computeEffectiveColumnsFn(this.columns, this.augmentFromDisplayFunction, this.displayFunction, this.data)
|
||||
: computeColumnsFromDisplayFunctionFn(this.displayFunction, this.data);
|
||||
const allCols = this.__getEffectiveColumns();
|
||||
const editableCols = this.__editableColumns(allCols);
|
||||
if (editableCols.length === 0) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user