diff --git a/changelog.md b/changelog.md index 5eaa6d3..561c293 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2026-04-12 - 3.77.0 - feat(dees-table) +add configurable cell flash comparison and border highlight mode + +- adds column-level flashCompare support so update highlighting can detect meaningful changes for custom cell values +- adds flashBorder styling for cells with badges, icons, or custom rendered content where text-color flashing is not visible +- avoids false-positive flash animations for non-primitive cell values unless a custom comparator is provided + ## 2026-04-12 - 3.76.1 - fix(demo-inputs) wrap input component demos in dees-form containers for consistent form integration diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index deb3820..0277571 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -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.' } diff --git a/ts_web/elements/00group-dataview/dees-table/dees-table.ts b/ts_web/elements/00group-dataview/dees-table/dees-table.ts index 453bdb2..036cc93 100644 --- a/ts_web/elements/00group-dataview/dees-table/dees-table.ts +++ b/ts_web/elements/00group-dataview/dees-table/dees-table.ts @@ -695,6 +695,7 @@ export class DeesTable 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 extends DeesElement { ] .filter(Boolean) .join(' '); + const flashClass = isFlashing + ? useFlashBorder + ? 'innerCellContainer flashing-border' + : 'innerCellContainer flashing' + : 'innerCellContainer'; const innerHtml = html`
${isEditing ? this.renderCellEditor(itemArg, col) : content}
`; @@ -1362,6 +1368,7 @@ export class DeesTable extends DeesElement { const effectiveColumns = this.__getEffectiveColumns(); const visibleCols = effectiveColumns.filter((c) => !c.hidden); + const colByKey = new Map>(visibleCols.map((c) => [String(c.key), c])); const nextSnapshot = new Map>(); const newlyFlashing = new Map>(); @@ -1376,7 +1383,26 @@ export class DeesTable 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 && diff --git a/ts_web/elements/00group-dataview/dees-table/styles.ts b/ts_web/elements/00group-dataview/dees-table/styles.ts index a92020c..22ada0f 100644 --- a/ts_web/elements/00group-dataview/dees-table/styles.ts +++ b/ts_web/elements/00group-dataview/dees-table/styles.ts @@ -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 diff --git a/ts_web/elements/00group-dataview/dees-table/types.ts b/ts_web/elements/00group-dataview/dees-table/types.ts index b8a433c..21df31f 100644 --- a/ts_web/elements/00group-dataview/dees-table/types.ts +++ b/ts_web/elements/00group-dataview/dees-table/types.ts @@ -65,6 +65,25 @@ export interface Column { 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; } /**