eru-grid 0.0.54 → 0.0.55

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.
@@ -194,9 +194,23 @@ const SELF_COLOURED_DATATYPES = new Set(['status', 'priority', 'tag', 'progress'
194
194
  */
195
195
  const ACTION_COLUMN_MIN_WIDTH = 56;
196
196
  const SEEDED_FIELD_KEYS = [
197
- 'dynamic_number', 'display_number_as', 'symbol_field', 'date_format', 'label',
197
+ 'dynamic_number', 'display_number_as', 'symbol_field', 'date_format',
198
198
  'storage_name', 'folder_name', 'default_upload',
199
199
  ];
200
+ /**
201
+ * Keys the model seeds only until the design panel overrides them EXPLICITLY,
202
+ * tracked by a companion flag rather than by "does the column hold a value".
203
+ *
204
+ * `label` is the case that forced this: it is in SEEDED_FIELD_KEYS' spirit —
205
+ * the model's label is the default and a grid may shorten it — but a column
206
+ * never arrives without one, so the seeded test ("column already has a value,
207
+ * leave it alone") was true from the first render and the model's label never
208
+ * reached a mapped column. A query-derived column kept showing its raw result
209
+ * key as the heading.
210
+ */
211
+ const EXPLICITLY_OVERRIDDEN_FIELD_KEYS = {
212
+ label: 'label_overridden',
213
+ };
200
214
  /**
201
215
  * Datatype aliases the data model emits that mean an existing DataTypes value.
202
216
  *
@@ -4077,33 +4091,34 @@ class EruGridStore {
4077
4091
  }
4078
4092
  return this.rows();
4079
4093
  }, ...(ngDevMode ? [{ debugName: "displayData" }] : []));
4094
+ // The frozen grand total is the row the transform appended (or prepended) to
4095
+ // the pivot data, so it is only there when the transform actually produced
4096
+ // one. It skips totalling an empty result, so `data` can be [] with the
4097
+ // grand-total config still on; indexing it then yields undefined and the
4098
+ // frozen-row template renders a row that has no `_rowKey`. Read the row
4099
+ // positionally but keep it only when it really is the grand total.
4100
+ frozenGrandTotalRow = computed(() => {
4101
+ if (!this.isPivotMode() || !this.pivotResult())
4102
+ return null;
4103
+ const config = this.configuration().config;
4104
+ if (!config?.enableGrandTotal || !config?.freezeGrandTotal)
4105
+ return null;
4106
+ const data = this.pivotResult().data;
4107
+ const row = config?.grandTotalPosition === 'before' ? data[0] : data[data.length - 1];
4108
+ return row?._isGrandTotal ? row : null;
4109
+ }, ...(ngDevMode ? [{ debugName: "frozenGrandTotalRow" }] : []));
4080
4110
  // Type-safe accessors for specific modes
4081
4111
  pivotGrandTotalData = computed(() => {
4082
- let pd = [];
4083
- if (this.isPivotMode() && this.pivotResult()) {
4084
- if (this.configuration().config?.enableGrandTotal) {
4085
- if (this.configuration().config?.freezeGrandTotal) {
4086
- if (this.configuration().config?.grandTotalPosition === 'before') {
4087
- pd = [this.pivotResult().data[0]];
4088
- }
4089
- else {
4090
- pd = [this.pivotResult().data[this.pivotResult().data.length - 1]];
4091
- }
4092
- }
4093
- }
4094
- }
4095
- return pd;
4112
+ const row = this.frozenGrandTotalRow();
4113
+ return row ? [row] : [];
4096
4114
  }, ...(ngDevMode ? [{ debugName: "pivotGrandTotalData" }] : []));
4097
4115
  // Type-safe accessors for specific modes
4098
4116
  pivotDisplayData = computed(() => {
4099
4117
  if (this.isPivotMode() && this.pivotResult()) {
4100
- if (this.configuration().config?.enableGrandTotal && this.configuration().config?.freezeGrandTotal) {
4101
- if (this.configuration().config?.grandTotalPosition === 'before') {
4102
- return this.pivotResult().data.slice(1);
4103
- }
4104
- else {
4105
- return this.pivotResult().data;
4106
- }
4118
+ // Only drop the leading row when it is the frozen grand total that the
4119
+ // header renders separately, never a real data row.
4120
+ if (this.frozenGrandTotalRow() && this.configuration().config?.grandTotalPosition === 'before') {
4121
+ return this.pivotResult().data.slice(1);
4107
4122
  }
4108
4123
  return this.pivotResult().data;
4109
4124
  }
@@ -4502,6 +4517,56 @@ class EruGridStore {
4502
4517
  const rows = this.rows().map(row => row.entity_id === rowId ? { ...row, ...updates } : row);
4503
4518
  this._rows.set(rows);
4504
4519
  }
4520
+ /**
4521
+ * Merge field values into the record with this id, wherever the grid is
4522
+ * holding it — the flat row list AND the per-group buckets a board fills.
4523
+ *
4524
+ * For a consumer that edits a record somewhere else on the page and needs the
4525
+ * grid to agree without refetching. `updateRow` is not enough on two counts:
4526
+ * it only looks at `_rows`, so a grouped/board grid keeps the stale copy, and
4527
+ * it replaces top-level row keys rather than merging into `entity_data`,
4528
+ * where an entity-backed row keeps its fields.
4529
+ *
4530
+ * Unknown ids are ignored, so a caller can announce a change blindly.
4531
+ */
4532
+ patchRecord(entityId, fields) {
4533
+ if (!entityId || !fields)
4534
+ return;
4535
+ const entries = Object.entries(fields);
4536
+ if (entries.length === 0)
4537
+ return;
4538
+ const patch = (row) => {
4539
+ if (!row || row.entity_id !== entityId)
4540
+ return row;
4541
+ const next = { ...row };
4542
+ if (next.entity_data && typeof next.entity_data === 'object') {
4543
+ next.entity_data = { ...next.entity_data, ...fields };
4544
+ }
4545
+ // An array-sourced row carries its fields at the top level instead. Only
4546
+ // keys the row already has are touched, so this never invents columns.
4547
+ for (const [key, value] of entries) {
4548
+ if (key !== 'entity_data' && Object.prototype.hasOwnProperty.call(next, key)) {
4549
+ next[key] = value;
4550
+ }
4551
+ }
4552
+ return next;
4553
+ };
4554
+ const holdsRecord = (rows) => rows.some(row => row?.entity_id === entityId);
4555
+ if (holdsRecord(this._rows())) {
4556
+ this._rows.set(this._rows().map(patch));
4557
+ }
4558
+ this._groupRows.update(current => {
4559
+ let touched = false;
4560
+ const next = new Map(current);
4561
+ current.forEach((rows, groupId) => {
4562
+ if (!holdsRecord(rows))
4563
+ return;
4564
+ next.set(groupId, rows.map(patch));
4565
+ touched = true;
4566
+ });
4567
+ return touched ? next : current;
4568
+ });
4569
+ }
4505
4570
  // Selection methods
4506
4571
  selectRow(rowId) {
4507
4572
  const selectedRowIds = new Set(this.selectedRowIds());
@@ -13756,9 +13821,13 @@ class ColumnDesignPanelComponent {
13756
13821
  * drift apart: a key that is inherited is always locked, and vice versa.
13757
13822
  *
13758
13823
  * SEEDED_FIELD_KEYS are the exception the resolver makes too: the model seeds
13759
- * them, this grid may then override them, so they stay editable here.
13824
+ * them, this grid may then override them, so they stay editable here. So are
13825
+ * EXPLICITLY_OVERRIDDEN_FIELD_KEYS, seeded the same way but tracked by a flag
13826
+ * — `label` among them, which must stay typeable for the flag to ever be set.
13760
13827
  */
13761
- static INHERITED_KEYS = new Set(INHERITED_FIELD_KEYS.filter(k => !SEEDED_FIELD_KEYS.includes(k) && k !== 'datatype'));
13828
+ static INHERITED_KEYS = new Set(INHERITED_FIELD_KEYS.filter(k => !SEEDED_FIELD_KEYS.includes(k) &&
13829
+ !EXPLICITLY_OVERRIDDEN_FIELD_KEYS[k] &&
13830
+ k !== 'datatype'));
13762
13831
  isInherited(key) {
13763
13832
  return this.isEntityMapped() && ColumnDesignPanelComponent.INHERITED_KEYS.has(key);
13764
13833
  }
@@ -14045,6 +14114,20 @@ class ColumnDesignPanelComponent {
14045
14114
  this.gridStore.updateColumnMeta(name, { [key]: value });
14046
14115
  }
14047
14116
  onText(key, value) {
14117
+ // A typed heading is the author's, and stops the mapped field's label from
14118
+ // seeding it on the next resolve; clearing the box hands the column back to
14119
+ // the data model rather than pinning it to an empty header.
14120
+ const flag = EXPLICITLY_OVERRIDDEN_FIELD_KEYS[key];
14121
+ if (flag) {
14122
+ const name = this.gridStore.selectedDesignColumn();
14123
+ if (!name)
14124
+ return;
14125
+ const typed = String(value ?? '').trim();
14126
+ this.gridStore.updateColumnMeta(name, typed
14127
+ ? { [key]: value, [flag]: true }
14128
+ : { [key]: null, [flag]: null });
14129
+ return;
14130
+ }
14048
14131
  this.patch(key, value);
14049
14132
  }
14050
14133
  onNumber(key, value) {
@@ -15789,7 +15872,7 @@ class EruGridComponent {
15789
15872
  }
15790
15873
  }
15791
15874
  trackByPivotRowFn(index, pivotRow) {
15792
- return pivotRow._rowKey || `pivot-row-${index}`;
15875
+ return pivotRow?._rowKey || `pivot-row-${index}`;
15793
15876
  }
15794
15877
  trackByRowFn(index, row) {
15795
15878
  // Use both entity_id and index to ensure uniqueness even with duplicate entity_ids
@@ -16121,6 +16204,11 @@ class EruGridComponent {
16121
16204
  * including an explicit `false` on a checkbox — wins from then on. Without the
16122
16205
  * column in hand there is no way to tell "not set" from "set to the opposite",
16123
16206
  * so an override was overwritten on every resolve.
16207
+ *
16208
+ * EXPLICITLY_OVERRIDDEN_FIELD_KEYS work the same way but ask a companion flag
16209
+ * instead, for keys a column always holds a value for — `label`, whose
16210
+ * "already set" was true from the first render, so the model's label never
16211
+ * won and a mapped column kept its raw result key as the heading.
16124
16212
  */
16125
16213
  buildMappedColumnPatch(meta, col) {
16126
16214
  const patch = {};
@@ -16132,6 +16220,9 @@ class EruGridComponent {
16132
16220
  continue;
16133
16221
  if (seeded.has(k) && !isUnset(col?.[k]))
16134
16222
  continue;
16223
+ const overrideFlag = EXPLICITLY_OVERRIDDEN_FIELD_KEYS[k];
16224
+ if (overrideFlag && col?.[overrideFlag])
16225
+ continue;
16135
16226
  patch[k] = v;
16136
16227
  }
16137
16228
  // setColumns() stores the canonical datatype ('r_status' -> 'status'), so a
@@ -17033,5 +17124,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
17033
17124
  * Generated bundle index. Do not edit.
17034
17125
  */
17035
17126
 
17036
- export { ACTION_COLUMN_MIN_WIDTH, AttachmentComponent, CELL_RULE_OPERATORS, CheckboxComponent, ChipListComponent, ColumnConstraintsService, CompositeComponent, CurrencyComponent, CustomVirtualScrollStrategy, DATA_TYPES, DATETIME_FORMATS, DATE_FORMATS, DateComponent, DatetimeComponent, DurationComponent, EmailComponent, EruGridComponent, EruGridService, EruGridStore, GRID_COLOR_TOKENS, INHERITED_FIELD_KEYS, LocationComponent, MATERIAL_MODULES, MATERIAL_PROVIDERS, MONTH_SHORT_NAMES, NumberComponent, NumericInputDirective, PRESENTATION_DATATYPES, PRESET_CONFIG_DEFAULTS, PRESET_MANAGED_FIELDS, PeopleComponent, PhoneComponent, PriorityComponent, ProgressComponent, RatingComponent, SEEDED_FIELD_KEYS, SELF_COLOURED_DATATYPES, SelectComponent, StatusComponent, TagComponent, TextareaComponent, TextboxComponent, ThemeService, ThemeToggleComponent, WebsiteComponent, abbreviateNumber, abbreviationScaleFor, cellRuleBarPercent, cellRuleMatches, cellRuleToCss, cellStatAlias, cellTextStyleToCss, collectColumnStatRequests, columnCellRules, composeColorValue, effectiveValueDatatype, evaluateRowCondition, formatCellValue, formatDateTimeWithPattern, formatDateWithPattern, formatNumberValue, hasOwnCellRules, matchCellRule, matchColorRange, normalizeCellRender, normalizeDatatype, normalizeDateFormat, normalizeDateTimeFormat, parseCellDate, parseCellDateTime, parseColorValue, parseDateWithPattern, readRowValue, resolveColumnRules, resolveRowValue, resolveStatValue, rowSelectionKey, statusPillColors, tagPillColors, toDateOnlyString, toDateTimeString };
17127
+ export { ACTION_COLUMN_MIN_WIDTH, AttachmentComponent, CELL_RULE_OPERATORS, CheckboxComponent, ChipListComponent, ColumnConstraintsService, CompositeComponent, CurrencyComponent, CustomVirtualScrollStrategy, DATA_TYPES, DATETIME_FORMATS, DATE_FORMATS, DateComponent, DatetimeComponent, DurationComponent, EXPLICITLY_OVERRIDDEN_FIELD_KEYS, EmailComponent, EruGridComponent, EruGridService, EruGridStore, GRID_COLOR_TOKENS, INHERITED_FIELD_KEYS, LocationComponent, MATERIAL_MODULES, MATERIAL_PROVIDERS, MONTH_SHORT_NAMES, NumberComponent, NumericInputDirective, PRESENTATION_DATATYPES, PRESET_CONFIG_DEFAULTS, PRESET_MANAGED_FIELDS, PeopleComponent, PhoneComponent, PriorityComponent, ProgressComponent, RatingComponent, SEEDED_FIELD_KEYS, SELF_COLOURED_DATATYPES, SelectComponent, StatusComponent, TagComponent, TextareaComponent, TextboxComponent, ThemeService, ThemeToggleComponent, WebsiteComponent, abbreviateNumber, abbreviationScaleFor, cellRuleBarPercent, cellRuleMatches, cellRuleToCss, cellStatAlias, cellTextStyleToCss, collectColumnStatRequests, columnCellRules, composeColorValue, effectiveValueDatatype, evaluateRowCondition, formatCellValue, formatDateTimeWithPattern, formatDateWithPattern, formatNumberValue, hasOwnCellRules, matchCellRule, matchColorRange, normalizeCellRender, normalizeDatatype, normalizeDateFormat, normalizeDateTimeFormat, parseCellDate, parseCellDateTime, parseColorValue, parseDateWithPattern, readRowValue, resolveColumnRules, resolveRowValue, resolveStatValue, rowSelectionKey, statusPillColors, tagPillColors, toDateOnlyString, toDateTimeString };
17037
17128
  //# sourceMappingURL=eru-grid.mjs.map