Compare commits

...

3 Commits

Author SHA1 Message Date
7ef3613e91 v3.77.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 20:18:05 +00:00
940eebe29f feat(dees-table): add configurable cell flash comparison and border highlight mode 2026-04-12 20:18:05 +00:00
8ecaffe165 refactor(demos): remove redundant input group styles from demo components 2026-04-12 19:58:01 +00:00
14 changed files with 108 additions and 58 deletions

View File

@@ -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

View File

@@ -1,6 +1,6 @@
{
"name": "@design.estate/dees-catalog",
"version": "3.76.1",
"version": "3.77.0",
"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",

View File

@@ -59,8 +59,8 @@ For developers working on this library, please refer to the [UI Components Playb
| **Forms** | [`DeesForm`](#deesform), [`DeesInputText`](#deesinputtext), [`DeesInputCheckbox`](#deesinputcheckbox), [`DeesInputDropdown`](#deesinputdropdown), [`DeesInputRadiogroup`](#deesinputradiogroup), [`DeesInputFileupload`](#deesinputfileupload), [`DeesInputIban`](#deesinputiban), [`DeesInputPhone`](#deesinputphone), [`DeesInputQuantitySelector`](#deesinputquantityselector), [`DeesInputMultitoggle`](#deesinputmultitoggle), [`DeesInputToggle`](#deesinputtoggle), [`DeesInputTags`](#deesinputtags), [`DeesInputTypelist`](#deesinputtypelist), [`DeesInputList`](#deesinputlist), [`DeesInputProfilepicture`](#deesinputprofilepicture), [`DeesInputRichtext`](#deesinputrichtext), [`DeesInputWysiwyg`](#deesinputwysiwyg), [`DeesInputDatepicker`](#deesinputdatepicker), [`DeesInputSearchselect`](#deesinputsearchselect), [`DeesInputCode`](#deesinputcode), [`DeesFormSubmit`](#deesformsubmit) |
| **App Shell (Layout)** | [`DeesAppui`](#deesappui-), [`DeesAppuiMainmenu`](#deesappuimainmenu), [`DeesAppuiSecondarymenu`](#deesappuisecondarymenu), [`DeesAppuiMaincontent`](#deesappuimaincontent), [`DeesAppuiAppbar`](#deesappuiappbar), [`DeesAppuiActivitylog`](#deesappuiactivitylog), [`DeesAppuiBottombar`](#deesappuibottombar), [`DeesAppuiProfiledropdown`](#deesappuiprofiledropdown), [`DeesAppuiTabs`](#deesappuitabs), [`DeesMobileNavigation`](#deesmobilenavigation), [`DeesDashboardGrid`](#deesdashboardgrid) |
| **Data Display** | [`DeesTable`](#deestable), [`DeesDataviewCodebox`](#deesdataviewcodebox), [`DeesDataviewStatusobject`](#deesdataviewstatusobject), [`DeesPdf`](#deespdf), [`DeesStatsGrid`](#deesstatsgrid), [`DeesPagination`](#deespagination), [`DeesStorageBrowser`](#deesstorgebrowser) |
| **Media & Tiles** | [`DeesTilePdf`](#deestilepdf), [`DeesTileImage`](#deestileimage), [`DeesTileAudio`](#deestileaudio), [`DeesTileVideo`](#deestilevideo), [`DeesTileNote`](#deestilenote), [`DeesTileFolder`](#deestilefolder), [`DeesPreview`](#deespreview), [`DeesPdfViewer`](#deespdfviewer), [`DeesPdfPreview`](#deespdfpreview), [`DeesImageViewer`](#deesimageviewer), [`DeesAudioViewer`](#deesaudioviewer), [`DeesVideoViewer`](#deesvideoviewer) |
| **Visualization** | [`DeesChartArea`](#deeschartarea), [`DeesChartLog`](#deeschartlog) |
| **Media & Thumbnails** | [`DeesThumbnailPdf`](#deesthumbnailpdf), [`DeesThumbnailImage`](#deesthumbnailimage), [`DeesThumbnailAudio`](#deesthumbnailaudio), [`DeesThumbnailVideo`](#deesthumbnalvideo), [`DeesThumbnailNote`](#deesthumbnailnote), [`DeesThumbnailFolder`](#deesthumbnailfolder), [`DeesPreview`](#deespreview), [`DeesPdfViewer`](#deespdfviewer), [`DeesImageViewer`](#deesimageviewer), [`DeesAudioViewer`](#deesaudioviewer), [`DeesVideoViewer`](#deesvideoviewer) |
| **Visualization** | [`DeesChartArea`](#deeschartarea), [`DeesChartBar`](#deeschartbar), [`DeesChartDonut`](#deeschartdonut), [`DeesChartGauge`](#deeschartgauge), [`DeesChartRadar`](#deeschartradar), [`DeesChartLog`](#deeschartlog) |
| **Dialogs & Overlays** | [`DeesModal`](#deesmodal), [`DeesContextmenu`](#deescontextmenu), [`DeesSpeechbubble`](#deesspeechbubble), [`DeesWindowlayer`](#deeswindowlayer) |
| **Navigation** | [`DeesStepper`](#deesstepper), [`DeesProgressbar`](#deesprogressbar) |
| **Workspace / IDE** | [`DeesWorkspace`](#deesworkspace), [`DeesWorkspaceMonaco`](#deesworkspacemonaco), [`DeesWorkspaceDiffEditor`](#deesworkspacediffeditor), [`DeesWorkspaceFiletree`](#deesworkspacefiletree), [`DeesWorkspaceTerminal`](#deesworkspaceterminal), [`DeesWorkspaceTerminalPreview`](#deesworkspaceterminalpreview), [`DeesWorkspaceMarkdown`](#deesworkspacemarkdown), [`DeesWorkspaceMarkdownoutlet`](#deesworkspacemarkdownoutlet), [`DeesWorkspaceBottombar`](#deesworkspacebottombar) |
@@ -143,14 +143,13 @@ Display icons from FontAwesome and Lucide icon libraries with library prefixes.
```
#### `DeesLabel`
Text label component with optional icon and status indicators.
Text label component with optional required indicator and info tooltip. Used internally by all input components.
```typescript
<dees-label
text="Status" // Label text
icon="info-circle" // Optional: icon name
type="info" // Options: default, info, success, warning, error
size="medium" // Options: small, medium, large
.label=${'Email Address'} // Label text
.required=${true} // Optional: shows red asterisk
.infoText=${'We will never share your email'} // Optional: shows hover info icon with tooltip
></dees-label>
```
@@ -321,7 +320,7 @@ Container component for form elements with built-in validation and data handling
```
#### `DeesInputText`
Text input field with validation and formatting options.
Text input field with validation, info tooltips, description text, and context menu (Cut/Copy/Paste/Select All).
```typescript
<dees-input-text
@@ -330,10 +329,20 @@ Text input field with validation and formatting options.
value="initial@value.com" // Initial value
required // Makes the field required
disabled // Disables the input
placeholder="Enter your email"
.infoText=${'Hover icon tooltip text'} // Shows ⓘ icon on label with hover tooltip
.description=${'Permanent help text below the input'} // Small text below the input
.validationFunction=${(value) => { // Auto-validates on every keystroke
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (emailRegex.test(value)) {
return { valid: true, message: 'Email is valid' };
}
return { valid: false, message: 'Please enter a valid email' };
}}
></dees-input-text>
```
> 💡 **All input components** share these common properties from `DeesInputBase`: `key`, `label`, `required`, `disabled`, `infoText`, `description`, `layoutMode`, `labelPosition`.
#### `DeesInputCheckbox`
Checkbox input component for boolean values.
@@ -1780,7 +1789,7 @@ interface ITileFolderItem {
## License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./license) file.
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
@@ -1798,5 +1807,3 @@ Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

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;
}
/**

View File

@@ -31,12 +31,6 @@ export const demoFunc = () => html`
flex-wrap: wrap;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.spacer {
height: 200px;
display: flex;

View File

@@ -13,12 +13,6 @@ export const demoFunc = () => html`
margin: 0 auto;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.payment-group {
display: flex;
align-items: center;

View File

@@ -13,12 +13,6 @@ export const demoFunc = () => html`
margin: 0 auto;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.horizontal-group {
display: flex;
align-items: center;

View File

@@ -14,12 +14,6 @@ export const demoFunc = () => html`
margin: 0 auto;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.shopping-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));

View File

@@ -23,12 +23,6 @@ export const demoFunc = () => html`
margin-bottom: 0;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.demo-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));

View File

@@ -30,12 +30,6 @@ export const demoFunc = () => html`
flex-wrap: wrap;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.grid-layout {
display: grid;
grid-template-columns: 1fr 1fr;

View File

@@ -13,12 +13,6 @@ export const demoFunc = () => html`
margin: 0 auto;
}
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.horizontal-group {
display: flex;
gap: 24px;