eru-grid 0.0.39 → 0.0.41

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": "eru-grid",
3
- "version": "0.0.39",
3
+ "version": "0.0.41",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.1.2",
6
6
  "@angular/core": "^21.1.2",
@@ -3,6 +3,7 @@ import * as _angular_core from '@angular/core';
3
3
  import { OnInit, AfterViewInit, OnDestroy, QueryList, ElementRef, TemplateRef, EventEmitter, ChangeDetectorRef, Provider } from '@angular/core';
4
4
  import { FixedSizeVirtualScrollStrategy, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
5
5
  import { MatDatepicker } from '@angular/material/datepicker';
6
+ import { ConnectedPosition } from '@angular/cdk/overlay';
6
7
 
7
8
  interface Row {
8
9
  entity_id: string;
@@ -157,6 +158,22 @@ interface ExcelDownloadRequest extends ExcelDownloadPayload {
157
158
  id: number;
158
159
  timestamp: number;
159
160
  }
161
+ /**
162
+ * A message from a cell's server-side validation check, queued for the consumer
163
+ * to display. eru-grid does not render a snackbar — it delegates chrome to the
164
+ * consumer, the same way it delegates data fetching — so this follows the
165
+ * enqueue/drain pattern of ExcelDownloadRequest: the consumer watches
166
+ * `EruGridStore.serverValidationNotices()`, shows its own snackbar (red when
167
+ * status is 'error'), and calls removeServerValidationNotice(id).
168
+ */
169
+ interface ServerValidationNotice {
170
+ id: string;
171
+ timestamp: number;
172
+ rowId: string;
173
+ fieldName: string;
174
+ status: 'success' | 'error';
175
+ msg: string;
176
+ }
160
177
  interface GridStyles {
161
178
  subTotalStyle?: 'bold' | 'italic' | 'highlighted';
162
179
  grandTotalStyle?: 'bold' | 'italic' | 'highlighted';
@@ -200,6 +217,7 @@ interface DynamicDataRequest {
200
217
  apiName?: string;
201
218
  apiField?: string;
202
219
  search?: string;
220
+ filter?: Record<string, any>;
203
221
  }
204
222
  interface Field {
205
223
  name: string;
@@ -207,8 +225,8 @@ interface Field {
207
225
  datatype: DataTypes;
208
226
  field_size: number;
209
227
  grid_index: number;
210
- required?: boolean;
211
228
  mandatory?: boolean;
229
+ required?: boolean;
212
230
  minWidth?: number;
213
231
  maxWidth?: number;
214
232
  symbol?: string;
@@ -238,31 +256,166 @@ interface Field {
238
256
  open_status?: any[];
239
257
  close_status?: any[];
240
258
  color_ranges?: {
241
- from: number;
242
- to: number;
243
- color: string;
259
+ from?: number | null;
260
+ to?: number | null;
261
+ color?: string | null;
244
262
  }[];
245
263
  data_length?: number;
246
264
  start_value?: number;
247
265
  end_value?: number;
266
+ is_perc?: boolean;
248
267
  emoji_value?: string;
249
268
  max_files?: number;
250
269
  allowed_file_types?: any[];
251
- maxFileSize?: number;
270
+ max_file_size?: number;
252
271
  default_country?: string;
253
272
  date_format?: string;
254
273
  dateTimeFormat?: string;
255
274
  allow_days?: string[];
275
+ /**
276
+ * Dependent-dropdown mapping, for `option_type: 'ENTITY_DATA'`.
277
+ *
278
+ * Each pair links a field on the CURRENT record to the field it filters on in
279
+ * the option-source entity: `def` is the key in the current entity supplying
280
+ * the value, `dpef` is the key in the mapped entity to match it against.
281
+ * Several pairs mean the list is scoped by all of them.
282
+ *
283
+ * Replaces the earlier single `dpef` string, which could only express one
284
+ * dependency and conflated the two sides. A lone `dpef` is still read as
285
+ * `{ def: dpef, dpef: dpef }` for fields saved before this existed.
286
+ */
287
+ df_fields?: {
288
+ def: string;
289
+ dpef: string;
290
+ }[];
291
+ /** Legacy single dependency. Prefer `df_fields`. */
292
+ dpef?: string;
293
+ /** Legacy: key in the current entity paired with `dpef`. */
294
+ def?: string;
295
+ multiple?: boolean;
296
+ searchable?: boolean;
297
+ max_avatars?: number;
298
+ people_card?: string;
299
+ show_people_card?: 'none' | 'hover' | 'click';
300
+ value_true?: string;
301
+ value_false?: string;
256
302
  rich_text?: boolean;
257
303
  is_hyp?: boolean;
258
304
  hypl_nm?: string;
305
+ is_pii?: boolean;
306
+ is_ephi?: boolean;
307
+ to_encrypt?: boolean;
308
+ is_pf?: boolean;
309
+ is_unique?: boolean;
310
+ unq_sa?: boolean;
311
+ system_validate?: string;
312
+ ssv_api?: string;
313
+ can_group?: boolean;
314
+ default_group?: boolean;
315
+ mapped_entity?: string;
316
+ mapped_field?: string;
259
317
  tool_tip?: string;
260
318
  description?: string;
261
319
  default?: any;
262
320
  editable?: boolean | string;
263
321
  is_hidden?: boolean;
264
322
  }
265
- type DataTypes = 'number' | 'textbox' | 'currency' | 'date' | 'datetime' | 'duration' | 'dropdown_single_select' | 'dropdown_multi_select' | 'location' | 'email' | 'people' | 'checkbox' | 'phone' | 'priority' | 'status' | 'progress' | 'attachment' | 'tag' | 'rating' | 'website';
323
+ /**
324
+ * Field attributes a mapped column inherits from the data model.
325
+ *
326
+ * Single source of truth for two things that must agree: what
327
+ * `buildMappedColumnPatch()` merges onto the column on every load, and what the
328
+ * column design panel renders read-only. If a key is inherited but not locked,
329
+ * a user's edit is silently discarded on reload; if it is locked but not
330
+ * inherited, the control is dead. Keep both behaviours driven from this list.
331
+ *
332
+ * The rule: anything the data model authors belongs here; anything that is a
333
+ * property of *this view* of the field does not. Deliberately excluded —
334
+ * field_size, grid_index layout, set by column resize/reorder
335
+ * tool_tip per-grid header help text
336
+ * is_hidden hiding a column in one grid should not require
337
+ * hiding the field in the data model
338
+ * dateTimeFormat legacy alias; the model writes `date_format`
339
+ */
340
+ declare const INHERITED_FIELD_KEYS: readonly string[];
341
+ type DataTypes = 'number' | 'textbox' | 'textarea' | 'currency' | 'date' | 'datetime' | 'duration' | 'time' | 'dropdown_single_select' | 'dropdown_multi_select' | 'location' | 'email' | 'people' | 'checkbox' | 'phone' | 'priority' | 'status' | 'progress' | 'attachment' | 'tag' | 'rating' | 'website';
342
+ /** Resolve a data-model datatype to the canonical DataTypes value. */
343
+ declare function normalizeDatatype(datatype: any): DataTypes;
344
+ /** Short month names, for the `MMM` token. */
345
+ declare const MONTH_SHORT_NAMES: string[];
346
+ /**
347
+ * The date formats offered to users, in every layer.
348
+ *
349
+ * One list so eru-studio, eru-grid and the processo data model present the same
350
+ * choices — previously studio offered eight, while the grid's panel and processo
351
+ * offered three, so a format could be picked on a page and had no equivalent on
352
+ * a field.
353
+ *
354
+ * Values are in token form: `dd` day, `MM` month number, `MMM` month name,
355
+ * `yyyy` year. Casing is significant — lowercase `mm` means minutes in a
356
+ * datetime — so values are normalised rather than lowercased on read.
357
+ */
358
+ declare const DATE_FORMATS: {
359
+ value: string;
360
+ label: string;
361
+ }[];
362
+ /** The same orderings with a 24-hour time part, for datetime fields. */
363
+ declare const DATETIME_FORMATS: {
364
+ value: string;
365
+ label: string;
366
+ }[];
367
+ /**
368
+ * Normalise a date format to token casing, so a value saved in any casing
369
+ * matches the canonical option. The data model uppercases on save
370
+ * (`DD-MMM-YYYY`) and older UIs stored lowercase.
371
+ *
372
+ * `mmm` is handled before `mm`, or the longer token would be half-consumed and
373
+ * `dd-mmm-yyyy` would come out as `dd-MMm-yyyy`.
374
+ */
375
+ declare function normalizeDateFormat(format: string | null | undefined): string;
376
+ /**
377
+ * Normalise a datetime format. Only the date half is token-cased — `mm` in the
378
+ * time half is minutes and must stay lowercase, which is why this cannot just
379
+ * reuse normalizeDateFormat over the whole string. The date half is everything
380
+ * before the time, located by the `hh` token rather than by whitespace, since a
381
+ * date pattern may itself contain a space (`dd MMM yyyy`).
382
+ */
383
+ declare function normalizeDateTimeFormat(format: string | null | undefined): string;
384
+ /**
385
+ * Render a date with a token pattern (`dd`, `MM`, `MMM`, `yyyy`).
386
+ *
387
+ * Shared by both libraries so a date reads identically on a page and in a grid.
388
+ * The alternation lists longer tokens first, so `MMM` is matched before `MM`
389
+ * and `yyyy` is never partly consumed — the bug that a chain of string
390
+ * replacements invites.
391
+ */
392
+ declare function formatDateWithPattern(date: Date | null, pattern: string): string;
393
+ /**
394
+ * Parse text written in a token pattern back to a Date, or null.
395
+ *
396
+ * Built as a regex from the pattern rather than by splitting on a separator:
397
+ * a month name is not numeric, and patterns may use a space or a comma, so
398
+ * splitting cannot recover the field order on its own.
399
+ */
400
+ declare function parseDateWithPattern(text: string, pattern: string): Date | null;
401
+ /** One value band and the colours it paints. Either bound may be open-ended. */
402
+ interface ColorRangeBand {
403
+ from?: number | null;
404
+ to?: number | null;
405
+ color?: string | null;
406
+ background?: string | null;
407
+ }
408
+ /**
409
+ * Resolve a numeric value against a column's `color_ranges`, returning the
410
+ * first matching band or null.
411
+ *
412
+ * Shared by the progress, number and currency cells so a value falling in the
413
+ * same band paints the same way whichever datatype renders it. A blank or
414
+ * absent bound is open-ended — data-model bands are authored that way ("80 and
415
+ * above"), and coercing a missing bound to 0 would stop such a band matching.
416
+ * Bands are checked in order, so the author controls precedence.
417
+ */
418
+ declare function matchColorRange(value: any, ranges: any): ColorRangeBand | null;
266
419
  declare const DATA_TYPES: DataTypes[];
267
420
  type AggregationFunction = 'sum' | 'count' | 'avg' | 'min' | 'max';
268
421
  interface PivotColumnGroupState {
@@ -380,6 +533,25 @@ interface CellValueChange {
380
533
  * </ng-template>
381
534
  * <eru-grid [boardCardTemplate]="myCard" [gridConfig]="cfg"></eru-grid>
382
535
  */
536
+ /**
537
+ * Template context injected into a personCardTemplate on EruGridComponent.
538
+ *
539
+ * eru-studio renders a whole studio page as the person card. eru-grid cannot do
540
+ * that — it has no page renderer, and the dependency runs studio -> grid — so it
541
+ * follows the same pattern as boardCardTemplate: the grid owns the trigger,
542
+ * positioning and dismissal, and the consumer supplies the content.
543
+ *
544
+ * <ng-template #personCard let-userId let-column="column">…</ng-template>
545
+ * <eru-grid [personCardTemplate]="personCard" [gridConfig]="cfg"></eru-grid>
546
+ */
547
+ interface PersonCardContext {
548
+ /** The selected person's id ($implicit — bind with let-userId) */
549
+ $implicit: string;
550
+ /** The people column this avatar belongs to */
551
+ column: Field | null;
552
+ /** Page the field names as the card, from the data model's people_card */
553
+ cardPage?: string;
554
+ }
383
555
  interface BoardCardContext {
384
556
  /** Row data for this card ($implicit — bind with let-row) */
385
557
  $implicit: Row;
@@ -687,6 +859,10 @@ declare class EruGridStore {
687
859
  private readonly _dataRequest;
688
860
  private readonly _requestQueue;
689
861
  private readonly _excelDownloadRequest;
862
+ private readonly _serverValidationNotices;
863
+ readonly serverValidationNotices: _angular_core.Signal<ServerValidationNotice[]>;
864
+ setServerValidationNotice(notice: Omit<ServerValidationNotice, 'id' | 'timestamp'>): void;
865
+ removeServerValidationNotice(id: string): void;
690
866
  private readonly _selectedDesignColumn;
691
867
  readonly selectedDesignColumn: _angular_core.Signal<string | null>;
692
868
  private readonly _columnDesignUpdate;
@@ -1118,6 +1294,13 @@ declare class EruGridComponent implements OnInit, AfterViewInit, OnDestroy {
1118
1294
  * <eru-grid [boardCardTemplate]="myCard" [gridConfig]="cfg"></eru-grid>
1119
1295
  */
1120
1296
  boardCardTemplate?: TemplateRef<BoardCardContext>;
1297
+ /**
1298
+ * Content for a people cell's person card. The grid owns the trigger,
1299
+ * placement and dismissal; the consumer renders the content, because the card
1300
+ * is an eru-studio page and only the consumer can render one. Omit it and the
1301
+ * card trigger stays inert.
1302
+ */
1303
+ personCardTemplate?: TemplateRef<PersonCardContext>;
1121
1304
  /**
1122
1305
  * Height in pixels of each board card slot.
1123
1306
  * Must match the fixed height of the card rendered by boardCardTemplate (or the default card).
@@ -1510,7 +1693,7 @@ declare class EruGridComponent implements OnInit, AfterViewInit, OnDestroy {
1510
1693
  requestBoardRowsForGroup(group: RowGroup): void;
1511
1694
  loadBoardAll(): void;
1512
1695
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EruGridComponent, never>;
1513
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EruGridComponent, "eru-grid", never, { "gridConfig": { "alias": "gridConfig"; "required": false; }; "boardCardTemplate": { "alias": "boardCardTemplate"; "required": false; }; "boardCardHeight": { "alias": "boardCardHeight"; "required": false; }; "boardCardGap": { "alias": "boardCardGap"; "required": false; }; "boardCardPadding": { "alias": "boardCardPadding"; "required": false; }; }, { "rowSelect": "rowSelect"; }, never, never, true, never>;
1696
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EruGridComponent, "eru-grid", never, { "gridConfig": { "alias": "gridConfig"; "required": false; }; "boardCardTemplate": { "alias": "boardCardTemplate"; "required": false; }; "personCardTemplate": { "alias": "personCardTemplate"; "required": false; }; "boardCardHeight": { "alias": "boardCardHeight"; "required": false; }; "boardCardGap": { "alias": "boardCardGap"; "required": false; }; "boardCardPadding": { "alias": "boardCardPadding"; "required": false; }; }, { "rowSelect": "rowSelect"; }, never, never, true, never>;
1514
1697
  }
1515
1698
  interface GroupItem {
1516
1699
  type: 'header' | 'row' | 'table-header' | 'row-place-holder' | 'ghost-loading';
@@ -1549,6 +1732,15 @@ declare class AttachmentComponent implements OnInit {
1549
1732
  fileDeleted: EventEmitter<string>;
1550
1733
  fileViewed: EventEmitter<string>;
1551
1734
  currentValue: _angular_core.WritableSignal<string[]>;
1735
+ /**
1736
+ * Why the last attempted file was refused, or '' when none.
1737
+ *
1738
+ * The checks were already here but only reached console.warn, so a user
1739
+ * dropping an oversized file saw nothing happen at all. Shown in the panel,
1740
+ * matching how eru-studio's attachment reports a rejection.
1741
+ */
1742
+ rejectionMessage: _angular_core.WritableSignal<string>;
1743
+ private rejectFile;
1552
1744
  isOverlayOpen: _angular_core.WritableSignal<boolean>;
1553
1745
  isDragging: _angular_core.WritableSignal<boolean>;
1554
1746
  fileInput: ElementRef<HTMLInputElement>;
@@ -1581,25 +1773,38 @@ declare class AttachmentComponent implements OnInit {
1581
1773
  }
1582
1774
 
1583
1775
  declare class CheckboxComponent {
1584
- value: _angular_core.InputSignal<boolean>;
1776
+ value: _angular_core.InputSignal<any>;
1777
+ /** Column config; supplies value_true / value_false when the field uses literals. */
1778
+ config: _angular_core.InputSignal<Field | null>;
1585
1779
  isEditable: _angular_core.InputSignal<boolean>;
1586
1780
  isActive: _angular_core.InputSignal<boolean>;
1587
1781
  isDrillable: _angular_core.InputSignal<boolean>;
1588
1782
  label: _angular_core.InputSignal<string>;
1589
- valueChange: EventEmitter<boolean>;
1590
- change: EventEmitter<boolean>;
1591
- blur: EventEmitter<boolean>;
1783
+ valueChange: EventEmitter<any>;
1784
+ change: EventEmitter<any>;
1785
+ blur: EventEmitter<any>;
1592
1786
  focus: EventEmitter<void>;
1593
- drilldownClick: EventEmitter<boolean>;
1787
+ drilldownClick: EventEmitter<any>;
1594
1788
  editModeChange: EventEmitter<boolean>;
1595
1789
  currentValue: _angular_core.WritableSignal<boolean>;
1596
1790
  constructor();
1791
+ private toBool;
1792
+ private literals;
1793
+ /**
1794
+ * Stored representation -> checked state. When the field configures
1795
+ * value_true / value_false the backend may hold arbitrary literals ('Y'/'N',
1796
+ * 'Yes'/'No'); left blank, both fall through to the generic coercion. Ported
1797
+ * from the eru-studio checkbox so the two agree on what a stored value means.
1798
+ */
1799
+ private resolveCheckboxState;
1800
+ /** Inverse: checked state -> the representation to store. */
1801
+ private checkboxValueFor;
1597
1802
  onValueChange(event: boolean): void;
1598
1803
  onActivate(): void;
1599
1804
  onBlur(): void;
1600
1805
  onDrillableClick(event: Event): void;
1601
1806
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<CheckboxComponent, never>;
1602
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<CheckboxComponent, "eru-checkbox", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "isEditable": { "alias": "isEditable"; "required": false; "isSignal": true; }; "isActive": { "alias": "isActive"; "required": false; "isSignal": true; }; "isDrillable": { "alias": "isDrillable"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "change": "change"; "blur": "blur"; "focus": "focus"; "drilldownClick": "drilldownClick"; "editModeChange": "editModeChange"; }, never, never, true, never>;
1807
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<CheckboxComponent, "eru-checkbox", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; "isEditable": { "alias": "isEditable"; "required": false; "isSignal": true; }; "isActive": { "alias": "isActive"; "required": false; "isSignal": true; }; "isDrillable": { "alias": "isDrillable"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "change": "change"; "blur": "blur"; "focus": "focus"; "drilldownClick": "drilldownClick"; "editModeChange": "editModeChange"; }, never, never, true, never>;
1603
1808
  }
1604
1809
 
1605
1810
  interface CurrencyValidationResult {
@@ -1615,6 +1820,12 @@ declare class CurrencyComponent {
1615
1820
  isDrillable: _angular_core.InputSignal<boolean>;
1616
1821
  replaceZeroValue: _angular_core.InputSignal<string | undefined>;
1617
1822
  placeholder: _angular_core.InputSignal<string>;
1823
+ /**
1824
+ * The row this cell belongs to. Needed for `symbol_field`, which names another
1825
+ * field on the SAME record supplying the currency symbol — so a multi-currency
1826
+ * column renders each row with its own symbol instead of one column-wide one.
1827
+ */
1828
+ row: _angular_core.InputSignal<any>;
1618
1829
  valueChange: EventEmitter<string | number | null>;
1619
1830
  blur: EventEmitter<string | number | null>;
1620
1831
  focus: EventEmitter<void>;
@@ -1623,6 +1834,20 @@ declare class CurrencyComponent {
1623
1834
  editModeChange: EventEmitter<boolean>;
1624
1835
  currentValue: _angular_core.WritableSignal<string | number | null>;
1625
1836
  error: _angular_core.WritableSignal<string>;
1837
+ /**
1838
+ * Colours from the column's configured value ranges, applied over the cell's
1839
+ * normal appearance. View mode only — tinting an active input reads as a
1840
+ * validation state rather than a value. Null when no band matches, matching
1841
+ * the eru-studio currency component so a value paints the same on a page and here.
1842
+ */
1843
+ /**
1844
+ * Symbol to show. `symbol_field` wins when set — read from this row, so each
1845
+ * record can carry its own currency — otherwise the column's static `symbol`.
1846
+ * Rows may nest their values under entity_data.
1847
+ */
1848
+ resolvedSymbol: _angular_core.Signal<string>;
1849
+ rangeColor: _angular_core.Signal<string | null>;
1850
+ rangeBackground: _angular_core.Signal<string | null>;
1626
1851
  constructor();
1627
1852
  formatNumberSignal: _angular_core.Signal<string | undefined>;
1628
1853
  onActivate(): void;
@@ -1632,7 +1857,7 @@ declare class CurrencyComponent {
1632
1857
  private validateField;
1633
1858
  private focusAndPositionCursor;
1634
1859
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<CurrencyComponent, never>;
1635
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<CurrencyComponent, "eru-currency", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; "isEditable": { "alias": "isEditable"; "required": false; "isSignal": true; }; "isActive": { "alias": "isActive"; "required": false; "isSignal": true; }; "isDrillable": { "alias": "isDrillable"; "required": false; "isSignal": true; }; "replaceZeroValue": { "alias": "replaceZeroValue"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "blur": "blur"; "focus": "focus"; "drilldownClick": "drilldownClick"; "validationError": "validationError"; "editModeChange": "editModeChange"; }, never, never, true, never>;
1860
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<CurrencyComponent, "eru-currency", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; "isEditable": { "alias": "isEditable"; "required": false; "isSignal": true; }; "isActive": { "alias": "isActive"; "required": false; "isSignal": true; }; "isDrillable": { "alias": "isDrillable"; "required": false; "isSignal": true; }; "replaceZeroValue": { "alias": "replaceZeroValue"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "row": { "alias": "row"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "blur": "blur"; "focus": "focus"; "drilldownClick": "drilldownClick"; "validationError": "validationError"; "editModeChange": "editModeChange"; }, never, never, true, never>;
1636
1861
  }
1637
1862
 
1638
1863
  declare class DateComponent {
@@ -1644,7 +1869,7 @@ declare class DateComponent {
1644
1869
  isActive: _angular_core.InputSignal<boolean>;
1645
1870
  isDrillable: _angular_core.InputSignal<boolean>;
1646
1871
  placeholder: _angular_core.InputSignal<string>;
1647
- readonly dateFormat: _angular_core.Signal<any>;
1872
+ readonly dateFormat: _angular_core.Signal<string>;
1648
1873
  readonly allowDays: _angular_core.Signal<string[]>;
1649
1874
  readonly dayFilter: _angular_core.Signal<(d: Date | null) => boolean>;
1650
1875
  valueChange: EventEmitter<string | null>;
@@ -1653,10 +1878,22 @@ declare class DateComponent {
1653
1878
  editModeChange: EventEmitter<boolean>;
1654
1879
  currentValue: _angular_core.WritableSignal<Date | null>;
1655
1880
  private shouldOpenPicker;
1881
+ /**
1882
+ * The column's configured default, resolved to a real date.
1883
+ *
1884
+ * The data model expresses it as a rule rather than a fixed value: the
1885
+ * `Current_Date` sentinel in `default`, optionally shifted by
1886
+ * `default_value_offset_days` (negative goes back). Resolving it here means a
1887
+ * cell with no stored value shows what the field defines, matching eru-studio
1888
+ * — previously the grid ignored both and showed an empty cell.
1889
+ */
1890
+ private resolveDefaultDate;
1656
1891
  constructor();
1657
1892
  onActivate(): void;
1658
1893
  onDateChange(date: Date | null): void;
1659
1894
  onDrillableClick(event: Event): void;
1895
+ /** Render a date in the column's configured format, via the shared token
1896
+ * formatter so a page and a grid read identically. */
1660
1897
  formatDate(date: Date | null): string;
1661
1898
  parseDateString(dateString: string): Date | null;
1662
1899
  getDisplayValue(): string;
@@ -1876,6 +2113,14 @@ declare class NumberComponent {
1876
2113
  editModeChange: EventEmitter<boolean>;
1877
2114
  currentValue: _angular_core.WritableSignal<string | number | null>;
1878
2115
  error: _angular_core.WritableSignal<string>;
2116
+ /**
2117
+ * Colours from the column's configured value ranges, applied over the cell's
2118
+ * normal appearance. View mode only — tinting an active input reads as a
2119
+ * validation state rather than a value. Null when no band matches, matching
2120
+ * the eru-studio number component so a value paints the same on a page and here.
2121
+ */
2122
+ rangeColor: _angular_core.Signal<string | null>;
2123
+ rangeBackground: _angular_core.Signal<string | null>;
1879
2124
  constructor();
1880
2125
  formatNumberSignal: _angular_core.Signal<string | undefined>;
1881
2126
  onActivate(): void;
@@ -1904,11 +2149,47 @@ declare class PeopleComponent implements OnInit {
1904
2149
  isDrillable: _angular_core.InputSignal<boolean>;
1905
2150
  columnWidth: _angular_core.InputSignal<number>;
1906
2151
  eruGridStore: _angular_core.InputSignal<EruGridStore | null>;
2152
+ /**
2153
+ * Content for the person card, supplied by the consumer. The grid owns the
2154
+ * trigger, positioning and dismissal; only the consumer can render a page,
2155
+ * which is what eru-studio's card is. Without a template the trigger is inert.
2156
+ */
2157
+ personCardTemplate: _angular_core.InputSignal<TemplateRef<PersonCardContext> | null | undefined>;
1907
2158
  valueChange: EventEmitter<string[] | null>;
1908
2159
  blur: EventEmitter<string[] | null>;
1909
2160
  focus: EventEmitter<void>;
1910
2161
  drilldownClick: EventEmitter<string[] | null>;
1911
2162
  editModeChange: EventEmitter<boolean>;
2163
+ /**
2164
+ * Whether more than one person may be picked. From the column config, so a
2165
+ * field declared single-select in the data model is enforced here too. The
2166
+ * stored value stays a list either way — the flag only limits how many may be
2167
+ * chosen, so no consumer has to handle a shape change.
2168
+ */
2169
+ isMultiple: _angular_core.Signal<boolean>;
2170
+ /** Show the filter box inside the dropdown. Configurable, as in eru-studio. */
2171
+ isSearchable: _angular_core.Signal<boolean>;
2172
+ /** mat-select holds a list when multiple, a single id otherwise. */
2173
+ selectValue: _angular_core.Signal<any>;
2174
+ /** Page the field names as the card. Blank means no card. */
2175
+ cardPage: _angular_core.Signal<string>;
2176
+ /** 'none' | 'hover' | 'click' — 'none' whenever no card page is mapped. */
2177
+ cardTrigger: _angular_core.Signal<"none" | "hover" | "click">;
2178
+ /** The card is a view-mode affordance, and needs content to show. */
2179
+ cardEnabled: _angular_core.Signal<boolean>;
2180
+ /** Beside the avatar first (right, then left), dropping below only when
2181
+ * neither side fits. Offsets stay small: on hover the cursor has to cross the
2182
+ * gap to reach the card, and a wide one makes that a miss. */
2183
+ readonly cardPositions: ConnectedPosition[];
2184
+ /** Person whose card is open, or null. */
2185
+ openCardUserId: _angular_core.WritableSignal<string | null>;
2186
+ private static readonly CARD_CLOSE_DELAY_MS;
2187
+ private closeCardTimer;
2188
+ onAvatarEnter(userId: string): void;
2189
+ onAvatarLeave(): void;
2190
+ onAvatarCardClick(event: Event, userId: string): void;
2191
+ closeCard(): void;
2192
+ private cancelPendingClose;
1912
2193
  currentValue: _angular_core.WritableSignal<string[]>;
1913
2194
  searchText: _angular_core.WritableSignal<string>;
1914
2195
  selectContainer: ElementRef<HTMLElement>;
@@ -1925,6 +2206,13 @@ declare class PeopleComponent implements OnInit {
1925
2206
  onActivate(): void;
1926
2207
  onBlur(event: Event): void;
1927
2208
  onValueChange(value: string[]): void;
2209
+ /**
2210
+ * mat-select emits a list when multiple and a bare id otherwise. The stored
2211
+ * value is a list in both cases, so a single selection is re-wrapped here —
2212
+ * that way turning cardinality off never changes the value's shape for
2213
+ * consumers or for the data model.
2214
+ */
2215
+ onSelectionChange(value: any): void;
1928
2216
  onSearchChange(searchValue: string): void;
1929
2217
  onDrillableClick(event: Event): void;
1930
2218
  onOpenedChange(opened: boolean): void;
@@ -1935,7 +2223,7 @@ declare class PeopleComponent implements OnInit {
1935
2223
  selectedMultiSelect(value: string[]): void;
1936
2224
  trackByValueAndIndex(index: number, option: any): string;
1937
2225
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<PeopleComponent, never>;
1938
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<PeopleComponent, "eru-people", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; "isEditable": { "alias": "isEditable"; "required": false; "isSignal": true; }; "isActive": { "alias": "isActive"; "required": false; "isSignal": true; }; "isDrillable": { "alias": "isDrillable"; "required": false; "isSignal": true; }; "columnWidth": { "alias": "columnWidth"; "required": false; "isSignal": true; }; "eruGridStore": { "alias": "eruGridStore"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "blur": "blur"; "focus": "focus"; "drilldownClick": "drilldownClick"; "editModeChange": "editModeChange"; }, never, never, true, never>;
2226
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PeopleComponent, "eru-people", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; "isEditable": { "alias": "isEditable"; "required": false; "isSignal": true; }; "isActive": { "alias": "isActive"; "required": false; "isSignal": true; }; "isDrillable": { "alias": "isDrillable"; "required": false; "isSignal": true; }; "columnWidth": { "alias": "columnWidth"; "required": false; "isSignal": true; }; "eruGridStore": { "alias": "eruGridStore"; "required": false; "isSignal": true; }; "personCardTemplate": { "alias": "personCardTemplate"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "blur": "blur"; "focus": "focus"; "drilldownClick": "drilldownClick"; "editModeChange": "editModeChange"; }, never, never, true, never>;
1939
2227
  }
1940
2228
 
1941
2229
  interface CountryCode {
@@ -2075,6 +2363,14 @@ declare class ProgressComponent implements OnInit {
2075
2363
  currentValue: _angular_core.WritableSignal<number>;
2076
2364
  displayValue: _angular_core.Signal<number>;
2077
2365
  barColor: _angular_core.Signal<string | null>;
2366
+ /**
2367
+ * Whether to show the value as a percentage. `is_perc` is the data model's
2368
+ * flag; it only reads as a percentage on a 0-100 scale, so a column with a
2369
+ * custom scale shows the raw value against its bound instead.
2370
+ */
2371
+ showPercent: _angular_core.Signal<boolean>;
2372
+ /** Label beside the bar: "45%" on a percentage scale, "3 / 5" otherwise. */
2373
+ valueLabel: _angular_core.Signal<string>;
2078
2374
  constructor();
2079
2375
  ngOnInit(): void;
2080
2376
  onActivate(): void;
@@ -2121,6 +2417,12 @@ declare class SelectComponent implements OnInit, AfterViewInit {
2121
2417
  fieldSize: _angular_core.InputSignal<number>;
2122
2418
  multiple: _angular_core.InputSignal<boolean>;
2123
2419
  eruGridStore: _angular_core.InputSignal<EruGridStore | null>;
2420
+ /**
2421
+ * The row this cell belongs to. Needed for a dependent dropdown: `dpef` names
2422
+ * another field on the SAME record whose value scopes this list, so the list
2423
+ * differs per row and cannot be resolved from the column config alone.
2424
+ */
2425
+ row: _angular_core.InputSignal<any>;
2124
2426
  valueChange: EventEmitter<any>;
2125
2427
  blur: EventEmitter<any>;
2126
2428
  focus: EventEmitter<void>;
@@ -2142,6 +2444,34 @@ declare class SelectComponent implements OnInit, AfterViewInit {
2142
2444
  onBlur(event: Event): void;
2143
2445
  onValueChange(value: any | any[] | null): void;
2144
2446
  toggleSelectAll(checked: boolean): void;
2447
+ /**
2448
+ * Dependency pairs for a cascading dropdown, normalised.
2449
+ *
2450
+ * `def` names the field on THIS ROW supplying a value; `dpef` names the field
2451
+ * in the option-source entity to match it against. A column saved before
2452
+ * `df_fields` existed carries a single `dpef`, read as `{ def: dpef, dpef }`.
2453
+ */
2454
+ dependencyPairs: _angular_core.Signal<{
2455
+ def: string;
2456
+ dpef: string;
2457
+ }[]>;
2458
+ /** Legacy single-dependency value from this row, for API sources. */
2459
+ private legacyDpefValue;
2460
+ /** True when this column's options are scoped by another field on the row. */
2461
+ isDependent: _angular_core.Signal<boolean>;
2462
+ /**
2463
+ * Filter sent with the options request: keys are fields in the option-source
2464
+ * entity, values read from this row. A pair whose source value is absent is
2465
+ * omitted rather than sent blank, since filtering on a blank would narrow the
2466
+ * list to nothing.
2467
+ */
2468
+ dependencyFilter: _angular_core.Signal<Record<string, any>>;
2469
+ /**
2470
+ * Map key the option list is cached under. Must be identical at request time
2471
+ * and at lookup time, or the response is written under one key and read from
2472
+ * another — so both go through this.
2473
+ */
2474
+ optionsCacheKey: _angular_core.Signal<string>;
2145
2475
  onSearchChange(searchValue: string): void;
2146
2476
  onOpenedChange(opened: boolean): void;
2147
2477
  ngAfterViewInit(): void;
@@ -2162,7 +2492,7 @@ declare class SelectComponent implements OnInit, AfterViewInit {
2162
2492
  */
2163
2493
  private firstPrimitiveValue;
2164
2494
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SelectComponent, never>;
2165
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SelectComponent, "eru-select", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; "isEditable": { "alias": "isEditable"; "required": false; "isSignal": true; }; "isActive": { "alias": "isActive"; "required": false; "isSignal": true; }; "isDrillable": { "alias": "isDrillable"; "required": false; "isSignal": true; }; "columnWidth": { "alias": "columnWidth"; "required": false; "isSignal": true; }; "fieldSize": { "alias": "fieldSize"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "eruGridStore": { "alias": "eruGridStore"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "blur": "blur"; "focus": "focus"; "drilldownClick": "drilldownClick"; "editModeChange": "editModeChange"; }, never, never, true, never>;
2495
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SelectComponent, "eru-select", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; "isEditable": { "alias": "isEditable"; "required": false; "isSignal": true; }; "isActive": { "alias": "isActive"; "required": false; "isSignal": true; }; "isDrillable": { "alias": "isDrillable"; "required": false; "isSignal": true; }; "columnWidth": { "alias": "columnWidth"; "required": false; "isSignal": true; }; "fieldSize": { "alias": "fieldSize"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "eruGridStore": { "alias": "eruGridStore"; "required": false; "isSignal": true; }; "row": { "alias": "row"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "blur": "blur"; "focus": "focus"; "drilldownClick": "drilldownClick"; "editModeChange": "editModeChange"; }, never, never, true, never>;
2166
2496
  }
2167
2497
 
2168
2498
  declare class StatusComponent implements OnInit {
@@ -2449,5 +2779,5 @@ declare class ThemeToggleComponent {
2449
2779
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ThemeToggleComponent, "eru-theme-toggle", never, {}, {}, never, never, true, never>;
2450
2780
  }
2451
2781
 
2452
- export { AttachmentComponent, CheckboxComponent, ColumnConstraintsService, CurrencyComponent, CustomVirtualScrollStrategy, DATA_TYPES, DateComponent, DatetimeComponent, DurationComponent, EmailComponent, EruGridComponent, EruGridService, EruGridStore, LocationComponent, MATERIAL_MODULES, MATERIAL_PROVIDERS, NumberComponent, PRESET_CONFIG_DEFAULTS, PRESET_MANAGED_FIELDS, PeopleComponent, PhoneComponent, PriorityComponent, ProgressComponent, RatingComponent, SelectComponent, StatusComponent, TagComponent, TextareaComponent, TextboxComponent, ThemeService, ThemeToggleComponent, WebsiteComponent };
2453
- export type { ActionClick, AggregationFunction, AttachmentConfig, BoardCardContext, CellValueChange, ColumnReorder, ColumnResize, ColumnWidthConstraints, CurrencyValidationResult, DataTypes, Drilldown, DurationValue, DynamicDataRequest, EmailValidationResult, ExcelDownloadPayload, ExcelDownloadRequest, Field, GridConfiguration, GridFeatures, GridMode, GridPreset, GridStyles, GridTokens, GroupItem$1 as GroupItem, LocationConfig, LocationValidationResult, NumberValidationResult, PeopleOption, PivotColumnGroupState, PivotColumnHeader, PivotConfiguration, PivotHeaderStructure, PivotMetadata, PivotResult, PivotRow, RatingConfig, Row, RowDataRequest, RowDataResponse, RowGrandTotal, RowGroup, TextareaValidationResult, TextboxValidationResult, Theme, WebsiteValidationResult };
2782
+ export { AttachmentComponent, CheckboxComponent, ColumnConstraintsService, CurrencyComponent, CustomVirtualScrollStrategy, DATA_TYPES, DATETIME_FORMATS, DATE_FORMATS, DateComponent, DatetimeComponent, DurationComponent, EmailComponent, EruGridComponent, EruGridService, EruGridStore, INHERITED_FIELD_KEYS, LocationComponent, MATERIAL_MODULES, MATERIAL_PROVIDERS, MONTH_SHORT_NAMES, NumberComponent, PRESET_CONFIG_DEFAULTS, PRESET_MANAGED_FIELDS, PeopleComponent, PhoneComponent, PriorityComponent, ProgressComponent, RatingComponent, SelectComponent, StatusComponent, TagComponent, TextareaComponent, TextboxComponent, ThemeService, ThemeToggleComponent, WebsiteComponent, formatDateWithPattern, matchColorRange, normalizeDatatype, normalizeDateFormat, normalizeDateTimeFormat, parseDateWithPattern };
2783
+ export type { ActionClick, AggregationFunction, AttachmentConfig, BoardCardContext, CellValueChange, ColorRangeBand, ColumnReorder, ColumnResize, ColumnWidthConstraints, CurrencyValidationResult, DataTypes, Drilldown, DurationValue, DynamicDataRequest, EmailValidationResult, ExcelDownloadPayload, ExcelDownloadRequest, Field, GridConfiguration, GridFeatures, GridMode, GridPreset, GridStyles, GridTokens, GroupItem$1 as GroupItem, LocationConfig, LocationValidationResult, NumberValidationResult, PeopleOption, PersonCardContext, PivotColumnGroupState, PivotColumnHeader, PivotConfiguration, PivotHeaderStructure, PivotMetadata, PivotResult, PivotRow, RatingConfig, Row, RowDataRequest, RowDataResponse, RowGrandTotal, RowGroup, ServerValidationNotice, TextareaValidationResult, TextboxValidationResult, Theme, WebsiteValidationResult };