From 72e3d6a09e0095d4350a10a96fca52b7e6f3e948 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Wed, 8 Apr 2026 14:51:49 +0000 Subject: [PATCH] feat(dees-table): add opt-in flash highlighting for updated table cells --- changelog.md | 8 + ts_web/00_commitinfo_data.ts | 2 +- .../dees-table/dees-table.demo.ts | 66 ++++ .../00group-dataview/dees-table/dees-table.ts | 331 +++++++++++++++++- .../00group-dataview/dees-table/styles.ts | 66 ++++ 5 files changed, 464 insertions(+), 9 deletions(-) diff --git a/changelog.md b/changelog.md index 1121b7c..ce7abe9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2026-04-08 - 3.70.0 - feat(dees-table) +add opt-in flash highlighting for updated table cells + +- introduces highlight-updates and highlight-duration properties for diff-based cell update highlighting +- adds a warning banner when flash highlighting is enabled without rowKey +- keeps selection stable across data refreshes and avoids flashing user-edited cells +- includes a live demo showcasing flashing updates and reduced-motion support + ## 2026-04-08 - 3.69.1 - fix(ui) refine heading emphasis and animate app dashboard subview expansion diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index abfeb60..8830dec 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.69.1', + version: '3.70.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.demo.ts b/ts_web/elements/00group-dataview/dees-table/dees-table.demo.ts index 38da7a9..ee33ffa 100644 --- a/ts_web/elements/00group-dataview/dees-table/dees-table.demo.ts +++ b/ts_web/elements/00group-dataview/dees-table/dees-table.demo.ts @@ -1,6 +1,7 @@ import { type ITableAction } from './dees-table.js'; import * as plugins from '../../00plugins.js'; import { html, css, cssManager } from '@design.estate/dees-element'; +import '@design.estate/dees-wcctools/demotools'; interface ITableDemoData { date: string; @@ -742,6 +743,71 @@ export const demoFunc = () => html` ] as ITableAction[]} > + + { + const tableEl = elementArg.querySelector('#demoLiveFlash') as any; + if (!tableEl) return; + // Guard against double-start if runAfterRender fires more than once + // (e.g. across hot-reload cycles). + if (tableEl.__liveFlashTimerId) { + window.clearInterval(tableEl.__liveFlashTimerId); + } + const tick = () => { + if (!Array.isArray(tableEl.data) || tableEl.data.length === 0) return; + const next = tableEl.data.map((r: any) => ({ ...r })); + const count = 1 + Math.floor(Math.random() * 3); + for (let i = 0; i < count; i++) { + const idx = Math.floor(Math.random() * next.length); + const delta = +((Math.random() * 2 - 1) * 3).toFixed(2); + const newPrice = Math.max(1, +(next[idx].price + delta).toFixed(2)); + next[idx] = { + ...next[idx], + price: newPrice, + change: delta, + updatedAt: new Date().toLocaleTimeString(), + }; + } + tableEl.data = next; + }; + tableEl.__liveFlashTimerId = window.setInterval(tick, 1500); + }}> +
+

Live Updates with Flash Highlighting

+

+ Opt-in cell-flash via highlight-updates="flash". The ticker below mutates + random rows every 1.5s and reassigns .data. Updated cells briefly flash + amber and fade out. Requires rowKey (here "symbol"). Honors + prefers-reduced-motion. Row selection persists across updates — click a + row, then watch it stay selected as the data churns. +

+ +
+
`; 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 ff9e116..ea12e52 100644 --- a/ts_web/elements/00group-dataview/dees-table/dees-table.ts +++ b/ts_web/elements/00group-dataview/dees-table/dees-table.ts @@ -214,6 +214,30 @@ export class DeesTable extends DeesElement { @property({ type: Number, attribute: 'virtual-overscan' }) accessor virtualOverscan: number = 8; + /** + * Opt-in visual indication of cell-value changes across data updates. + * + * - `'none'` (default): no diffing, zero overhead. + * - `'flash'`: when `data` is reassigned to a new array reference, diff the + * new rows against the previous snapshot and briefly flash any cells + * whose resolved value changed. Equality is strict `===`; object-valued + * cells are compared by reference. The currently-edited cell is never + * flashed. User-initiated cell edits do not flash. + * + * Requires `rowKey` to be set — without it, the feature silently no-ops + * and renders a visible dev warning banner. Honors `prefers-reduced-motion` + * (fades are replaced with a static background hint of the same duration). + */ + @property({ type: String, attribute: 'highlight-updates' }) + accessor highlightUpdates: 'none' | 'flash' = 'none'; + + /** + * Duration of the flash animation in milliseconds. Fed into the + * `--dees-table-flash-duration` CSS variable on the host. + */ + @property({ type: Number, attribute: 'highlight-duration' }) + accessor highlightDuration: number = 900; + /** * When set, the table renders inside a fixed-height scroll container * (`max-height: var(--table-max-height, 360px)`) and the header sticks @@ -268,6 +292,23 @@ export class DeesTable extends DeesElement { @state() private accessor __floatingActive: boolean = false; + // ─── Flash-on-update state (only populated when highlightUpdates === 'flash') ── + /** rowId → set of colKey strings currently flashing. */ + @state() + private accessor __flashingCells: Map> = new Map(); + + /** rowId → (colKey → last-seen resolved cell value). Populated per diff pass. */ + private __prevSnapshot?: Map>; + + /** Single shared timer that clears __flashingCells after highlightDuration ms. */ + private __flashClearTimer?: ReturnType; + + /** Monotonic counter bumped each flash batch so directives.keyed recreates the cell node and restarts the animation. */ + private __flashTick: number = 0; + + /** One-shot console.warn gate for missing rowKey in flash mode. */ + private __flashWarnedNoRowKey: boolean = false; + // ─── Render memoization ────────────────────────────────────────────── // These caches let render() short-circuit when the relevant inputs // (by reference) haven't changed. They are NOT @state — mutating them @@ -557,6 +598,15 @@ export class DeesTable extends DeesElement {
+ ${this.highlightUpdates === 'flash' && !this.rowKey + ? html`` + : html``}