eru-grid 0.0.53 → 0.0.54

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.
@@ -1288,7 +1288,10 @@ class PivotTransformService {
1288
1288
  const enableColumnSubtotals = gridConfiguration?.config?.enableColumnSubtotals ?? false;
1289
1289
  const enableGrandTotal = gridConfiguration?.config?.enableGrandTotal ?? false;
1290
1290
  const enableColumnGrandTotal = gridConfiguration?.config?.enableColumnGrandTotal ?? false;
1291
- if (enableRowSubtotals || enableColumnSubtotals || enableGrandTotal || enableColumnGrandTotal) {
1291
+ // Nothing to total when there are no rows — a grand total of an empty set
1292
+ // would render a row of zeros under a result that has no data.
1293
+ if (pivotRows.length > 0 &&
1294
+ (enableRowSubtotals || enableColumnSubtotals || enableGrandTotal || enableColumnGrandTotal)) {
1292
1295
  pivotRows = this.addSubtotals(pivotRows, configuration, pivotColumns, gridConfiguration, sourceData);
1293
1296
  }
1294
1297
  // Step 5: Generate column definitions (including subtotal columns)
@@ -3896,6 +3899,11 @@ class EruGridStore {
3896
3899
  // Pivot-specific signals
3897
3900
  _pivotConfiguration = signal(null, ...(ngDevMode ? [{ debugName: "_pivotConfiguration" }] : []));
3898
3901
  _pivotResult = signal(null, ...(ngDevMode ? [{ debugName: "_pivotResult" }] : []));
3902
+ /**
3903
+ * Widths for generated pivot leaves that have no field to hold them.
3904
+ * Keyed by leaf name — see updateColumnWidth.
3905
+ */
3906
+ _pivotLeafWidths = signal(new Map(), ...(ngDevMode ? [{ debugName: "_pivotLeafWidths" }] : []));
3899
3907
  _drilldown = signal(null, ...(ngDevMode ? [{ debugName: "_drilldown" }] : []));
3900
3908
  _actionClick = signal(null, ...(ngDevMode ? [{ debugName: "_actionClick" }] : []));
3901
3909
  _columnResize = signal(null, ...(ngDevMode ? [{ debugName: "_columnResize" }] : []));
@@ -3985,6 +3993,7 @@ class EruGridStore {
3985
3993
  // Pivot-specific readonly signals
3986
3994
  pivotConfiguration = this._pivotConfiguration.asReadonly();
3987
3995
  pivotResult = this._pivotResult.asReadonly();
3996
+ pivotLeafWidths = this._pivotLeafWidths.asReadonly();
3988
3997
  drilldown = this._drilldown.asReadonly();
3989
3998
  actionClick = this._actionClick.asReadonly();
3990
3999
  columnResize = this._columnResize.asReadonly();
@@ -4401,8 +4410,20 @@ class EruGridStore {
4401
4410
  });
4402
4411
  return;
4403
4412
  }
4404
- const columns = this.columns().map(col => col.name === owner ? { ...col, field_size: field_size } : col);
4405
- this._columns.set(columns);
4413
+ const columns = this.columns();
4414
+ if (!columns.some(col => col.name === owner)) {
4415
+ // A pivot dimension the grid has no column for — the author put a field
4416
+ // in the pivot's rows that is not in the column set. There is no owner to
4417
+ // hold the width, so it is held against the leaf name. Without this the
4418
+ // resize reached the cells (the directive styles them directly) but
4419
+ // nothing else: the next change detection re-asserted the old width, and
4420
+ // the table total, which is summed from the leaves, never moved.
4421
+ const next = new Map(this._pivotLeafWidths());
4422
+ next.set(owner, field_size);
4423
+ this._pivotLeafWidths.set(next);
4424
+ return;
4425
+ }
4426
+ this._columns.set(columns.map(col => (col.name === owner ? { ...col, field_size: field_size } : col)));
4406
4427
  }
4407
4428
  setCellValueChange(change) {
4408
4429
  this._cellValueChange.set(change);
@@ -4721,16 +4742,16 @@ class EruGridStore {
4721
4742
  }
4722
4743
  if (!pivotConfig)
4723
4744
  return;
4724
- // No source rows: publish an EMPTY pivot rather than returning and leaving
4725
- // the previous result on screen. Bailing here is what kept the last
4726
- // filter's rows — and their subtotals — rendered after a query came back
4727
- // with nothing, since the pivot view reads _pivotResult and not the row
4728
- // stores that had already been cleared.
4729
- if (sourceData.length === 0) {
4730
- if (this._pivotResult() !== null)
4731
- this._pivotResult.set(null);
4732
- return;
4733
- }
4745
+ // No source rows still goes through the transform, producing a pivot with
4746
+ // the row-dimension columns and no data rows.
4747
+ //
4748
+ // Two things this must NOT do. Bailing out leaves the previous filter's
4749
+ // rows and subtotals on screen, since the pivot view reads _pivotResult and
4750
+ // not the row stores that were already cleared. Publishing null empties it
4751
+ // but drops the pivot's own column definitions, so displayColumns() falls
4752
+ // back to the raw source columns and an empty pivot reads as a table. The
4753
+ // transform on an empty set yields neither: column values and measures
4754
+ // cannot be derived from nothing, so only the row dimensions remain.
4734
4755
  this.setLoading(true);
4735
4756
  this.setError(null);
4736
4757
  try {
@@ -14584,7 +14605,6 @@ class EruGridComponent {
14584
14605
  return !!row && this.gridStore.activeBoardRow() === row;
14585
14606
  }
14586
14607
  initialMinHeight = 400;
14587
- initialTotalWidth = 0;
14588
14608
  viewport;
14589
14609
  groupsViewport;
14590
14610
  groupsScrollContainerEl;
@@ -14889,10 +14909,21 @@ class EruGridComponent {
14889
14909
  this.viewport?.getRenderedRange();
14890
14910
  this.firstTr = this.viewport?.getRenderedRange()?.start || 0;
14891
14911
  }
14912
+ /**
14913
+ * The sum of the rendered column widths — what every table in the grid is
14914
+ * forced to via `--table-total-width`.
14915
+ *
14916
+ * A signal, not a value captured at init. A resize writes the new width to
14917
+ * the column (or, in pivot mode, to the measure the leaf came from), and the
14918
+ * cells and `<col>`s follow it; a cached total does not, so the table stayed
14919
+ * at its old width while its columns changed underneath — leaving the header
14920
+ * and body tables wider or narrower than the columns they contain, and the
14921
+ * horizontal scroll range wrong. Pivot mode also changes its leaf SET as the
14922
+ * data changes, so even without a resize the total goes stale.
14923
+ */
14924
+ totalTableWidth = computed(() => this.getLeafColumns().reduce((total, column) => total + (column?.field_size || 0), 0), ...(ngDevMode ? [{ debugName: "totalTableWidth" }] : []));
14892
14925
  getTotalTableWidth() {
14893
- const columns = this.getLeafColumns();
14894
- let totalWidth = columns.reduce((total, column) => total + column.field_size, 0);
14895
- return totalWidth;
14926
+ return this.totalTableWidth();
14896
14927
  }
14897
14928
  /* getContainerHeight(): number {
14898
14929
  const element = this.rowContainer?.nativeElement;
@@ -15120,7 +15151,6 @@ class EruGridComponent {
15120
15151
  this.initializeColumnWidths();
15121
15152
  this.firstDataRowIndex.set(this.maxDepth() + 1);
15122
15153
  this.initialMinHeight = this.getMinHeightPx();
15123
- this.initialTotalWidth = this.getTotalTableWidth();
15124
15154
  this.columnsInitialized.set(true);
15125
15155
  }
15126
15156
  });
@@ -15322,7 +15352,6 @@ class EruGridComponent {
15322
15352
  // initializeGroups() moved to effect in constructor to handle timing
15323
15353
  this.firstDataRowIndex.set(this.maxDepth() + 1);
15324
15354
  this.initialMinHeight = this.getMinHeightPx();
15325
- this.initialTotalWidth = this.getTotalTableWidth();
15326
15355
  }
15327
15356
  ngAfterViewInit() {
15328
15357
  this.applyTokens();
@@ -15419,12 +15448,12 @@ class EruGridComponent {
15419
15448
  return this.initialMinHeight;
15420
15449
  }
15421
15450
  getInitialTotalWidth() {
15422
- return this.initialTotalWidth;
15451
+ return this.totalTableWidth();
15423
15452
  }
15424
15453
  applyCdkWidth() {
15425
15454
  const element = this.rowContainer?.nativeElement;
15426
15455
  if (element) {
15427
- return element.clientWidth > this.initialTotalWidth;
15456
+ return element.clientWidth > this.totalTableWidth();
15428
15457
  }
15429
15458
  return false;
15430
15459
  }
@@ -15828,6 +15857,7 @@ class EruGridComponent {
15828
15857
  const leaves = pivotResult.headerStructure.leafColumns;
15829
15858
  const aggregations = this.gridStore.configuration()?.pivot?.aggregations || [];
15830
15859
  const sourceColumns = this.gridStore.columns() || [];
15860
+ const leafWidths = this.gridStore.pivotLeafWidths();
15831
15861
  // Carry configuration from the field a leaf came from onto the leaf itself.
15832
15862
  // The pivot transform builds leaves with a handful of hard-coded keys — it
15833
15863
  // types every aggregated column as plain 'number' whatever the measure says,
@@ -15841,15 +15871,20 @@ class EruGridComponent {
15841
15871
  const owner = leaf.aggregationFunction
15842
15872
  ? aggregations.find(a => a.name === this.designTargetFor(leaf))
15843
15873
  : sourceColumns.find(c => c.name === leaf.name);
15844
- if (!owner)
15845
- return leaf;
15846
15874
  const patch = {};
15847
- for (const key of EruGridComponent.PIVOT_INHERITED_KEYS) {
15848
- const value = owner[key];
15849
- // `false` is meaningful (enableDrilldown), blank/absent is not.
15850
- if (value !== undefined && value !== null && value !== '')
15851
- patch[key] = value;
15875
+ if (owner) {
15876
+ for (const key of EruGridComponent.PIVOT_INHERITED_KEYS) {
15877
+ const value = owner[key];
15878
+ // `false` is meaningful (enableDrilldown), blank/absent is not.
15879
+ if (value !== undefined && value !== null && value !== '')
15880
+ patch[key] = value;
15881
+ }
15852
15882
  }
15883
+ // A resize of a leaf with no owning field is held against the leaf name
15884
+ // instead — see EruGridStore.updateColumnWidth.
15885
+ const width = leafWidths.get(leaf.name);
15886
+ if (typeof width === 'number' && width > 0)
15887
+ patch.field_size = width;
15853
15888
  return Object.keys(patch).length ? { ...leaf, ...patch } : leaf;
15854
15889
  });
15855
15890
  }