@design.estate/dees-catalog 3.78.1 → 3.78.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@design.estate/dees-catalog",
3
- "version": "3.78.1",
3
+ "version": "3.78.3",
4
4
  "private": false,
5
5
  "description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
6
6
  "main": "dist_ts_web/index.js",
@@ -18,7 +18,7 @@
18
18
  "dependencies": {
19
19
  "@design.estate/dees-domtools": "^2.5.4",
20
20
  "@design.estate/dees-element": "^2.2.4",
21
- "@design.estate/dees-wcctools": "^3.8.4",
21
+ "@design.estate/dees-wcctools": "^3.9.0",
22
22
  "@fortawesome/fontawesome-svg-core": "^7.2.0",
23
23
  "@fortawesome/free-brands-svg-icons": "^7.2.0",
24
24
  "@fortawesome/free-regular-svg-icons": "^7.2.0",
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@design.estate/dees-catalog',
6
- version: '3.78.1',
6
+ version: '3.78.3',
7
7
  description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
8
8
  }
@@ -293,9 +293,9 @@ export class DeesTable<T> extends DeesElement {
293
293
  private accessor __floatingActive: boolean = false;
294
294
 
295
295
  // ─── Flash-on-update state (only populated when highlightUpdates === 'flash') ──
296
- /** rowId → set of colKey strings currently flashing. */
296
+ /** rowId → (colKey flash token) for cells currently flashing. */
297
297
  @state()
298
- private accessor __flashingCells: Map<string, Set<string>> = new Map();
298
+ private accessor __flashingCells: Map<string, Map<string, number>> = new Map();
299
299
 
300
300
  /** rowId → (colKey → last-seen resolved cell value). Populated per diff pass. */
301
301
  private __prevSnapshot?: Map<string, Map<string, unknown>>;
@@ -303,7 +303,7 @@ export class DeesTable<T> extends DeesElement {
303
303
  /** Single shared timer that clears __flashingCells after highlightDuration ms. */
304
304
  private __flashClearTimer?: ReturnType<typeof setTimeout>;
305
305
 
306
- /** Monotonic counter bumped each flash batch so directives.keyed recreates the cell node and restarts the animation. */
306
+ /** Monotonic counter bumped per flash batch so only changed cells restart their animation. */
307
307
  private __flashTick: number = 0;
308
308
 
309
309
  /** One-shot console.warn gate for missing rowKey in flash mode. */
@@ -317,7 +317,7 @@ export class DeesTable<T> extends DeesElement {
317
317
  columns: any;
318
318
  augment: boolean;
319
319
  displayFunction: any;
320
- data: any;
320
+ displayShapeKey: string;
321
321
  out: Column<T>[];
322
322
  };
323
323
  private __memoViewData?: {
@@ -329,8 +329,13 @@ export class DeesTable<T> extends DeesElement {
329
329
  effectiveColumns: Column<T>[];
330
330
  out: T[];
331
331
  };
332
- /** Tracks the (data, columns) pair that `determineColumnWidths()` last sized for. */
333
- private __columnsSizedFor?: { data: any; columns: any };
332
+ /** Tracks the layout inputs that `determineColumnWidths()` last sized for. */
333
+ private __columnsSizedFor?: {
334
+ effectiveColumns: Column<T>[];
335
+ showSelectionCheckbox: boolean;
336
+ inRowActionCount: number;
337
+ table: HTMLTableElement;
338
+ };
334
339
 
335
340
  // ─── Virtualization state ────────────────────────────────────────────
336
341
  /** Estimated row height (px). Measured once from the first rendered row. */
@@ -409,15 +414,7 @@ export class DeesTable<T> extends DeesElement {
409
414
  const view: T[] = (this as any)._lastViewData ?? [];
410
415
  const item = view.find((r) => this.getRowId(r) === this.__focusedCell!.rowId);
411
416
  if (!item) return;
412
- const allCols: Column<T>[] =
413
- Array.isArray(this.columns) && this.columns.length > 0
414
- ? computeEffectiveColumnsFn(
415
- this.columns,
416
- this.augmentFromDisplayFunction,
417
- this.displayFunction,
418
- this.data
419
- )
420
- : computeColumnsFromDisplayFunctionFn(this.displayFunction, this.data);
417
+ const allCols = this.__getEffectiveColumns();
421
418
  const col = allCols.find((c) => String(c.key) === this.__focusedCell!.colKey);
422
419
  if (!col || !this.__isColumnEditable(col)) return;
423
420
  eventArg.preventDefault();
@@ -469,15 +466,24 @@ export class DeesTable<T> extends DeesElement {
469
466
  * that affect it. Avoids re-running `computeEffectiveColumnsFn` /
470
467
  * `computeColumnsFromDisplayFunctionFn` on every Lit update.
471
468
  */
469
+ private __getDisplayFunctionShapeKey(): string {
470
+ if (!this.data || this.data.length === 0) return '';
471
+ const firstTransformedItem = this.displayFunction(this.data[0]) ?? {};
472
+ return Object.keys(firstTransformedItem).join('\u0000');
473
+ }
474
+
472
475
  private __getEffectiveColumns(): Column<T>[] {
473
476
  const usingColumns = Array.isArray(this.columns) && this.columns.length > 0;
477
+ const displayShapeKey = !usingColumns || this.augmentFromDisplayFunction
478
+ ? this.__getDisplayFunctionShapeKey()
479
+ : '';
474
480
  const cache = this.__memoEffectiveCols;
475
481
  if (
476
482
  cache &&
477
483
  cache.columns === this.columns &&
478
484
  cache.augment === this.augmentFromDisplayFunction &&
479
485
  cache.displayFunction === this.displayFunction &&
480
- cache.data === this.data
486
+ cache.displayShapeKey === displayShapeKey
481
487
  ) {
482
488
  return cache.out;
483
489
  }
@@ -493,7 +499,7 @@ export class DeesTable<T> extends DeesElement {
493
499
  columns: this.columns,
494
500
  augment: this.augmentFromDisplayFunction,
495
501
  displayFunction: this.displayFunction,
496
- data: this.data,
502
+ displayShapeKey,
497
503
  out,
498
504
  };
499
505
  return out;
@@ -543,6 +549,9 @@ export class DeesTable<T> extends DeesElement {
543
549
  public render(): TemplateResult {
544
550
  const effectiveColumns = this.__getEffectiveColumns();
545
551
  const viewData = this.__getViewData(effectiveColumns);
552
+ const headerActions = this.getActionsForType('header');
553
+ const footerActions = this.getActionsForType('footer');
554
+ const inRowActions = this.getActionsForType('inRow');
546
555
  (this as any)._lastViewData = viewData;
547
556
 
548
557
  // Virtualization slice — only the rows in `__virtualRange` actually
@@ -572,29 +581,22 @@ export class DeesTable<T> extends DeesElement {
572
581
  <div class="heading heading2">${this.heading2}</div>
573
582
  </div>
574
583
  <div class="headerActions">
575
- ${directives.resolveExec(async () => {
576
- const resultArray: TemplateResult[] = [];
577
- for (const action of this.dataActions) {
578
- if (!action.type?.includes('header')) continue;
579
- resultArray.push(
580
- html`<div
581
- class="headerAction"
582
- @click=${() => {
583
- action.actionFunc({
584
- item: this.selectedDataRow,
585
- table: this,
586
- });
587
- }}
588
- >
589
- ${action.iconName
590
- ? html`<dees-icon .iconSize=${14} .icon=${action.iconName}></dees-icon>
591
- ${action.name}`
592
- : action.name}
593
- </div>`
594
- );
595
- }
596
- return resultArray;
597
- })}
584
+ ${headerActions.map(
585
+ (action) => html`<div
586
+ class="headerAction"
587
+ @click=${() => {
588
+ action.actionFunc({
589
+ item: this.selectedDataRow,
590
+ table: this,
591
+ });
592
+ }}
593
+ >
594
+ ${action.iconName
595
+ ? html`<dees-icon .iconSize=${14} .icon=${action.iconName}></dees-icon>
596
+ ${action.name}`
597
+ : action.name}
598
+ </div>`
599
+ )}
598
600
  </div>
599
601
  </div>
600
602
  <div class="headingSeparation"></div>
@@ -658,11 +660,11 @@ export class DeesTable<T> extends DeesElement {
658
660
  : html``}
659
661
  ${directives.repeat(
660
662
  renderRows,
661
- (itemArg, sliceIdx) => `${this.getRowId(itemArg)}::${renderStart + sliceIdx}`,
663
+ (itemArg) => this.getRowId(itemArg),
662
664
  (itemArg, sliceIdx) => {
663
665
  const rowIndex = renderStart + sliceIdx;
664
666
  const rowId = this.getRowId(itemArg);
665
- const flashSet = this.__flashingCells.get(rowId);
667
+ const flashTokens = this.__flashingCells.get(rowId);
666
668
  return html`
667
669
  <tr
668
670
  data-row-idx=${rowIndex}
@@ -694,7 +696,8 @@ export class DeesTable<T> extends DeesElement {
694
696
  const isEditing =
695
697
  this.__editingCell?.rowId === rowId &&
696
698
  this.__editingCell?.colKey === editKey;
697
- const isFlashing = !!flashSet?.has(editKey);
699
+ const flashToken = flashTokens?.get(editKey);
700
+ const isFlashing = flashToken !== undefined;
698
701
  const useFlashBorder = isFlashing && !!col.flashBorder;
699
702
  const cellClasses = [
700
703
  isEditable ? 'editable' : '',
@@ -720,7 +723,7 @@ export class DeesTable<T> extends DeesElement {
720
723
  >
721
724
  ${isFlashing
722
725
  ? directives.keyed(
723
- `${rowId}:${editKey}:${this.__flashTick}`,
726
+ `${rowId}:${editKey}:${flashToken}`,
724
727
  innerHtml
725
728
  )
726
729
  : innerHtml}
@@ -728,11 +731,11 @@ export class DeesTable<T> extends DeesElement {
728
731
  `;
729
732
  })}
730
733
  ${(() => {
731
- if (this.dataActions && this.dataActions.length > 0) {
734
+ if (inRowActions.length > 0) {
732
735
  return html`
733
736
  <td class="actionsCol">
734
737
  <div class="actionsContainer">
735
- ${this.getActionsForType('inRow').map(
738
+ ${inRowActions.map(
736
739
  (actionArg) => html`
737
740
  <div
738
741
  class="action"
@@ -780,29 +783,22 @@ export class DeesTable<T> extends DeesElement {
780
783
  selected
781
784
  </div>
782
785
  <div class="footerActions">
783
- ${directives.resolveExec(async () => {
784
- const resultArray: TemplateResult[] = [];
785
- for (const action of this.dataActions) {
786
- if (!action.type?.includes('footer')) continue;
787
- resultArray.push(
788
- html`<div
789
- class="footerAction"
790
- @click=${() => {
791
- action.actionFunc({
792
- item: this.selectedDataRow,
793
- table: this,
794
- });
795
- }}
796
- >
797
- ${action.iconName
798
- ? html`<dees-icon .iconSize=${14} .icon=${action.iconName}></dees-icon>
799
- ${action.name}`
800
- : action.name}
801
- </div>`
802
- );
803
- }
804
- return resultArray;
805
- })}
786
+ ${footerActions.map(
787
+ (action) => html`<div
788
+ class="footerAction"
789
+ @click=${() => {
790
+ action.actionFunc({
791
+ item: this.selectedDataRow,
792
+ table: this,
793
+ });
794
+ }}
795
+ >
796
+ ${action.iconName
797
+ ? html`<dees-icon .iconSize=${14} .icon=${action.iconName}></dees-icon>
798
+ ${action.name}`
799
+ : action.name}
800
+ </div>`
801
+ )}
806
802
  </div>
807
803
  </div>
808
804
  </dees-tile>
@@ -1160,7 +1156,7 @@ export class DeesTable<T> extends DeesElement {
1160
1156
  /**
1161
1157
  * Measures the height of the first rendered body row and stores it for
1162
1158
  * subsequent virtualization math. Idempotent — only measures once per
1163
- * `data`/`columns` pair (cleared in `updated()` when those change).
1159
+ * rendered table layout (cleared in `updated()` when that layout changes).
1164
1160
  */
1165
1161
  private __measureRowHeight() {
1166
1162
  if (!this.virtualized || this.__rowHeightMeasured) return;
@@ -1426,20 +1422,16 @@ export class DeesTable<T> extends DeesElement {
1426
1422
  if (newlyFlashing.size === 0) return;
1427
1423
 
1428
1424
  // Merge with any in-flight flashes from a rapid second update so a cell
1429
- // that changes twice before its animation ends gets a single clean
1430
- // restart (via __flashTick / directives.keyed) instead of stacking.
1425
+ // that changes twice before its animation ends gets a clean restart,
1426
+ // while unrelated cells keep their existing DOM subtree.
1427
+ const flashToken = ++this.__flashTick;
1428
+ const nextFlashingCells = new Map(this.__flashingCells);
1431
1429
  for (const [rowId, cols] of newlyFlashing) {
1432
- const existing = this.__flashingCells.get(rowId);
1433
- if (existing) {
1434
- for (const c of cols) existing.add(c);
1435
- } else {
1436
- this.__flashingCells.set(rowId, cols);
1437
- }
1430
+ const existing = new Map(nextFlashingCells.get(rowId) ?? []);
1431
+ for (const colKey of cols) existing.set(colKey, flashToken);
1432
+ nextFlashingCells.set(rowId, existing);
1438
1433
  }
1439
- this.__flashTick++;
1440
- // Reactivity nudge: we've mutated the Map in place, so give Lit a fresh
1441
- // reference so the @state change fires for render.
1442
- this.__flashingCells = new Map(this.__flashingCells);
1434
+ this.__flashingCells = nextFlashingCells;
1443
1435
  if (this.__flashClearTimer) clearTimeout(this.__flashClearTimer);
1444
1436
  this.__flashClearTimer = setTimeout(() => {
1445
1437
  this.__flashingCells = new Map();
@@ -1449,6 +1441,9 @@ export class DeesTable<T> extends DeesElement {
1449
1441
 
1450
1442
  public async updated(changedProperties: Map<string | number | symbol, unknown>): Promise<void> {
1451
1443
  super.updated(changedProperties);
1444
+ const effectiveColumns = this.__getEffectiveColumns();
1445
+ const currentTable = this.shadowRoot?.querySelector('table') ?? null;
1446
+ const inRowActionCount = this.getActionsForType('inRow').length;
1452
1447
 
1453
1448
  // Feed highlightDuration into the CSS variable so JS and CSS stay in
1454
1449
  // sync via a single source of truth.
@@ -1456,15 +1451,23 @@ export class DeesTable<T> extends DeesElement {
1456
1451
  this.style.setProperty('--dees-table-flash-duration', `${this.highlightDuration}ms`);
1457
1452
  }
1458
1453
 
1459
- // Only re-measure column widths when the data or schema actually changed
1460
- // (or on first paint). `determineColumnWidths` is the single biggest
1461
- // first-paint cost — it forces multiple layout flushes per row.
1462
- const dataOrColsChanged =
1463
- !this.__columnsSizedFor ||
1464
- this.__columnsSizedFor.data !== this.data ||
1465
- this.__columnsSizedFor.columns !== this.columns;
1466
- if (dataOrColsChanged) {
1467
- this.__columnsSizedFor = { data: this.data, columns: this.columns };
1454
+ // Only re-measure column widths when layout-affecting inputs changed or
1455
+ // when a new <table> element was rendered after previously having none.
1456
+ const columnLayoutChanged =
1457
+ !!currentTable && (
1458
+ !this.__columnsSizedFor ||
1459
+ this.__columnsSizedFor.effectiveColumns !== effectiveColumns ||
1460
+ this.__columnsSizedFor.showSelectionCheckbox !== this.showSelectionCheckbox ||
1461
+ this.__columnsSizedFor.inRowActionCount !== inRowActionCount ||
1462
+ this.__columnsSizedFor.table !== currentTable
1463
+ );
1464
+ if (currentTable && columnLayoutChanged) {
1465
+ this.__columnsSizedFor = {
1466
+ effectiveColumns,
1467
+ showSelectionCheckbox: this.showSelectionCheckbox,
1468
+ inRowActionCount,
1469
+ table: currentTable,
1470
+ };
1468
1471
  this.determineColumnWidths();
1469
1472
  // Force re-measure of row height; structure may have changed.
1470
1473
  this.__rowHeightMeasured = false;
@@ -1502,7 +1505,7 @@ export class DeesTable<T> extends DeesElement {
1502
1505
  if (
1503
1506
  !this.fixedHeight &&
1504
1507
  this.data.length > 0 &&
1505
- (this.__floatingActive || dataOrColsChanged)
1508
+ (this.__floatingActive || columnLayoutChanged)
1506
1509
  ) {
1507
1510
  this.__syncFloatingHeader();
1508
1511
  }
@@ -1804,10 +1807,7 @@ export class DeesTable<T> extends DeesElement {
1804
1807
  * Used by the modal helper to render human-friendly labels.
1805
1808
  */
1806
1809
  private _lookupColumnByKey(key: string): Column<T> | undefined {
1807
- const usingColumns = Array.isArray(this.columns) && this.columns.length > 0;
1808
- const effective = usingColumns
1809
- ? computeEffectiveColumnsFn(this.columns, this.augmentFromDisplayFunction, this.displayFunction, this.data)
1810
- : computeColumnsFromDisplayFunctionFn(this.displayFunction, this.data);
1810
+ const effective = this.__getEffectiveColumns();
1811
1811
  return effective.find((c) => String(c.key) === key);
1812
1812
  }
1813
1813
 
@@ -2543,9 +2543,7 @@ export class DeesTable<T> extends DeesElement {
2543
2543
  const view: T[] = (this as any)._lastViewData ?? [];
2544
2544
  if (view.length === 0) return;
2545
2545
  // Recompute editable columns from the latest effective set.
2546
- const allCols: Column<T>[] = Array.isArray(this.columns) && this.columns.length > 0
2547
- ? computeEffectiveColumnsFn(this.columns, this.augmentFromDisplayFunction, this.displayFunction, this.data)
2548
- : computeColumnsFromDisplayFunctionFn(this.displayFunction, this.data);
2546
+ const allCols = this.__getEffectiveColumns();
2549
2547
  const editableCols = this.__editableColumns(allCols);
2550
2548
  if (editableCols.length === 0) return;
2551
2549