feat(dees-table): add configurable cell flash comparison and border highlight mode

This commit is contained in:
2026-04-12 20:18:05 +00:00
parent 8ecaffe165
commit 940eebe29f
5 changed files with 88 additions and 3 deletions

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@design.estate/dees-catalog',
version: '3.76.1',
version: '3.77.0',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
}

View File

@@ -695,6 +695,7 @@ export class DeesTable<T> extends DeesElement {
this.__editingCell?.rowId === rowId &&
this.__editingCell?.colKey === editKey;
const isFlashing = !!flashSet?.has(editKey);
const useFlashBorder = isFlashing && !!col.flashBorder;
const cellClasses = [
isEditable ? 'editable' : '',
isFocused && !isEditing ? 'focused' : '',
@@ -702,8 +703,13 @@ export class DeesTable<T> extends DeesElement {
]
.filter(Boolean)
.join(' ');
const flashClass = isFlashing
? useFlashBorder
? 'innerCellContainer flashing-border'
: 'innerCellContainer flashing'
: 'innerCellContainer';
const innerHtml = html`<div
class=${isFlashing ? 'innerCellContainer flashing' : 'innerCellContainer'}
class=${flashClass}
>
${isEditing ? this.renderCellEditor(itemArg, col) : content}
</div>`;
@@ -1362,6 +1368,7 @@ export class DeesTable<T> extends DeesElement {
const effectiveColumns = this.__getEffectiveColumns();
const visibleCols = effectiveColumns.filter((c) => !c.hidden);
const colByKey = new Map<string, Column<T>>(visibleCols.map((c) => [String(c.key), c]));
const nextSnapshot = new Map<string, Map<string, unknown>>();
const newlyFlashing = new Map<string, Set<string>>();
@@ -1376,7 +1383,26 @@ export class DeesTable<T> extends DeesElement {
const prevCells = this.__prevSnapshot?.get(rowId);
if (!prevCells) continue; // new row — not an "update"
for (const [colKey, nextVal] of cellMap) {
if (prevCells.get(colKey) !== nextVal) {
const prevVal = prevCells.get(colKey);
// Look up the column definition for flash options.
const colDef = colByKey.get(colKey);
// Determine whether the cell changed.
let changed: boolean;
if (colDef?.flashCompare) {
// Explicit custom comparator — caller decides.
changed = colDef.flashCompare(prevVal, nextVal);
} else if (nextVal !== null && nextVal !== undefined && typeof nextVal === 'object') {
// Non-primitive (TemplateResult, object, array, etc.) — skip by
// default. Custom renderings don't benefit from the text-color
// flash and reference inequality causes false positives.
changed = false;
} else {
changed = prevVal !== nextVal;
}
if (changed) {
// Don't flash the cell the user is actively editing.
if (
this.__editingCell &&

View File

@@ -404,11 +404,44 @@ export const tableStyles: CSSResult[] = [
100% { color: var(--dees-color-text-primary); }
}
/* Border/background flash variant for cells with styled content
(badges, icons, custom components) where a text-color animation
would be invisible. Activated via flashBorder on Column. */
.innerCellContainer.flashing-border {
animation: dees-table-cell-flash-border
var(--dees-table-flash-duration, 900ms)
var(--dees-table-flash-easing);
border-radius: 3px;
}
@keyframes dees-table-cell-flash-border {
0%,
35% {
box-shadow: inset 0 0 0 1.5px var(--dees-table-flash-color);
background: ${cssManager.bdTheme(
'hsl(45 93% 62% / 0.10)',
'hsl(45 93% 62% / 0.08)'
)};
}
100% {
box-shadow: inset 0 0 0 1.5px transparent;
background: transparent;
}
}
@media (prefers-reduced-motion: reduce) {
.innerCellContainer.flashing {
animation: none;
color: var(--dees-table-flash-color);
}
.innerCellContainer.flashing-border {
animation: none;
box-shadow: inset 0 0 0 1.5px var(--dees-table-flash-color);
background: ${cssManager.bdTheme(
'hsl(45 93% 62% / 0.10)',
'hsl(45 93% 62% / 0.08)'
)};
}
}
/* Dev-time warning banner shown when highlight-updates="flash" but

View File

@@ -65,6 +65,25 @@ export interface Column<T = any> {
parse?: (editorValue: any, row: T) => any;
/** Validate the parsed value before commit. Return string for error, true/void for ok. */
validate?: (value: any, row: T) => true | string | void;
// ─── Flash highlight options ───
/**
* Custom comparison for flash-on-update diffing.
* Return `true` if the cell should flash (i.e. the values differ).
* When absent, non-primitive cell values are skipped entirely
* (only strings, numbers, booleans, null, and undefined are diffed).
*/
flashCompare?: (prevVal: any, nextVal: any) => boolean;
/**
* When `true`, flash this cell with a border/background pulse instead of
* the default text-color animation. Useful for cells containing styled
* badges, icons, or custom web-component renderings where a text-color
* change would be invisible.
* @default false
*/
flashBorder?: boolean;
}
/**