eru-grid 0.0.49 → 0.0.51

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.
@@ -256,6 +256,8 @@ interface DynamicDataRequest {
256
256
  apiName?: string;
257
257
  apiField?: string;
258
258
  search?: string;
259
+ limit?: number;
260
+ skip?: number;
259
261
  filter?: Record<string, any>;
260
262
  }
261
263
  interface Field {
@@ -269,6 +271,7 @@ interface Field {
269
271
  minWidth?: number;
270
272
  maxWidth?: number;
271
273
  symbol?: string;
274
+ symbol_field?: string;
272
275
  options?: any[];
273
276
  option_type?: 'STATIC' | 'API' | 'ENTITY_DATA';
274
277
  entity_name?: string;
@@ -300,6 +303,7 @@ interface Field {
300
303
  to?: number | null;
301
304
  color?: string | null;
302
305
  }[];
306
+ cell_rules?: CellFormatRule[];
303
307
  data_length?: number;
304
308
  start_value?: number;
305
309
  end_value?: number;
@@ -342,6 +346,12 @@ interface Field {
342
346
  rich_text?: boolean;
343
347
  is_hyp?: boolean;
344
348
  hypl_nm?: string;
349
+ composite_fields?: string[];
350
+ composite_direction?: 'stack' | 'inline';
351
+ composite_separator?: string;
352
+ cell_page_id?: string;
353
+ cell_style?: CellTextStyle;
354
+ cell_style_secondary?: CellTextStyle;
345
355
  is_pii?: boolean;
346
356
  is_ephi?: boolean;
347
357
  to_encrypt?: boolean;
@@ -378,9 +388,55 @@ interface Field {
378
388
  * searchable whether a picker needs a filter box depends on how
379
389
  * many options this grid shows, not on the field
380
390
  * dateTimeFormat legacy alias; the model writes `date_format`
391
+ * cell_rules conditional formatting is a grid feature; it
392
+ * overrides the field's `color_ranges` per grid
393
+ *
394
+ * See SEEDED_FIELD_KEYS for the middle ground: keys the data model supplies a
395
+ * starting value for, which this grid may then override.
381
396
  */
382
397
  declare const INHERITED_FIELD_KEYS: readonly string[];
383
- 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';
398
+ /**
399
+ * The overridable subset of INHERITED_FIELD_KEYS: the data model still supplies
400
+ * the starting value, but only while the column has none of its own, and the
401
+ * design panel leaves the control editable instead of locking it. An override is
402
+ * a normal column edit — emitted as a columnMetaPatch and persisted with the
403
+ * page, unlike the keys that are purely inherited.
404
+ *
405
+ * These are presentation choices that legitimately differ per grid over the very
406
+ * same field: whether a figure reads 1,25,00,000 or 1.25 Cr, which scale it
407
+ * abbreviates on, which sibling field carries each row's currency symbol in a
408
+ * multi-currency dataset, and how wide a date may be spelled — a dense report
409
+ * wants 'dd-MM-yy' where a detail grid over the same field wants the model's.
410
+ *
411
+ * Kept INSIDE the inherited list rather than removed from it, because that list
412
+ * is also what eru-studio's properties panel reads to show a bound field's real
413
+ * value; dropping a key there would make the panel and the page disagree.
414
+ */
415
+ /**
416
+ * Datatypes that describe how a column is DRAWN rather than what it holds.
417
+ *
418
+ * A composite shows other columns; a page renders a layout. The data model has
419
+ * no such field type, so a mapped column keeps these instead of inheriting the
420
+ * model's datatype over them (see buildMappedColumnPatch).
421
+ */
422
+ declare const PRESENTATION_DATATYPES: ReadonlySet<string>;
423
+ /**
424
+ * Datatypes that colour their own value from the data — a status pill, a
425
+ * priority chip, a progress band.
426
+ *
427
+ * A column-level text colour has nothing to say about these: the colour IS the
428
+ * value's meaning, authored on the field's options. They still take the
429
+ * column's typography, and a `cell` background still tints the cell around
430
+ * them, which is the one colour choice that remains meaningful.
431
+ */
432
+ declare const SELF_COLOURED_DATATYPES: ReadonlySet<string>;
433
+ /**
434
+ * Narrowest the action column may be: what the word "Action" needs in the
435
+ * header, which is wider than a single action icon.
436
+ */
437
+ declare const ACTION_COLUMN_MIN_WIDTH = 56;
438
+ declare const SEEDED_FIELD_KEYS: readonly string[];
439
+ 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' | 'composite' | 'page';
384
440
  /** Resolve a data-model datatype to the canonical DataTypes value. */
385
441
  declare function normalizeDatatype(datatype: any): DataTypes;
386
442
  /** The three colours a status pill is painted with. */
@@ -487,24 +543,169 @@ declare function formatDateWithPattern(date: Date | null, pattern: string): stri
487
543
  * splitting cannot recover the field order on its own.
488
544
  */
489
545
  declare function parseDateWithPattern(text: string, pattern: string): Date | null;
490
- /** One value band and the colours it paints. Either bound may be open-ended. */
491
- interface ColorRangeBand {
492
- from?: number | null;
493
- to?: number | null;
546
+ /**
547
+ * Resolve a value against a column's `color_ranges`, returning the first
548
+ * matching band or null.
549
+ *
550
+ * Kept as the name the progress, number and currency cells already call, now a
551
+ * thin alias over matchCellRule so bands and conditional-format rules can never
552
+ * disagree about what matches.
553
+ */
554
+ declare function matchColorRange(value: any, ranges: any): ColorRangeBand | null;
555
+ /**
556
+ * Operators a conditional-format rule can test with.
557
+ *
558
+ * `between` is the historical behaviour of a `color_ranges` band and stays the
559
+ * default when a rule names no operator, so bands authored before this — in the
560
+ * data model or the design pane — keep matching exactly as they did.
561
+ */
562
+ type CellRuleOperator = 'between' | 'gt' | 'gte' | 'lt' | 'lte' | 'eq' | 'neq' | 'contains' | 'not_contains' | 'starts_with' | 'ends_with' | 'is_empty' | 'is_not_empty' | 'before' | 'after';
563
+ declare const CELL_RULE_OPERATORS: {
564
+ value: CellRuleOperator;
565
+ label: string;
566
+ operands: 0 | 1 | 2;
567
+ }[];
568
+ /**
569
+ * One conditional-format rule on a column.
570
+ *
571
+ * A superset of ColorRangeBand: the same `from`/`to`/`color`/`background` keys
572
+ * mean the same thing, so every existing `color_ranges` value is already a
573
+ * valid rule list and no migration is needed. What is new is `op` (so a rule
574
+ * can test text and dates, not only numeric ranges), the text/cell `fill`
575
+ * choice, `bold`, and `bar` for a data bar.
576
+ *
577
+ * Rules are checked in order and the first match wins — the author controls
578
+ * precedence by ordering, as `color_ranges` already did.
579
+ */
580
+ interface CellFormatRule {
581
+ op?: CellRuleOperator;
582
+ from?: number | string | null;
583
+ to?: number | string | null;
494
584
  color?: string | null;
495
585
  background?: string | null;
586
+ fill?: 'text' | 'cell';
587
+ bold?: boolean;
588
+ /**
589
+ * Draw the value's magnitude as a bar behind it. `bar_min`/`bar_max` scale
590
+ * it; without them the bar cannot be drawn, because a bar is relative to the
591
+ * column and a cell cannot see the column. Column-relative scaling (and
592
+ * top/bottom-N) needs per-column stats from the server — see the grid docs.
593
+ */
594
+ bar?: boolean;
595
+ bar_min?: number | null;
596
+ bar_max?: number | null;
597
+ /** Scale the bar from the column's own min/max instead of fixed bounds. */
598
+ bar_auto?: boolean;
599
+ /**
600
+ * Operands taken from a column statistic rather than typed in. This is how
601
+ * "top 10 items" or "above average" are expressed: the server returns one
602
+ * scalar per statistic and it is substituted for `from`/`to` before matching,
603
+ * so the rule stays an ordinary comparison and nothing downstream changes.
604
+ */
605
+ from_stat?: CellStatRef | null;
606
+ to_stat?: CellStatRef | null;
607
+ }
608
+ /** Statistics a rule operand may be drawn from. Mirrors the aggregate verbs
609
+ * the server exposes; `min`, `max` and `avg` are the ones it already has. */
610
+ type CellStatVerb = 'min' | 'max' | 'avg' | 'stddev' | 'percentile' | 'nth';
611
+ interface CellStatRef {
612
+ stat: CellStatVerb;
613
+ /** percentile: 0-100. */
614
+ p?: number;
615
+ /** nth: 1-based rank. */
616
+ n?: number;
617
+ /** nth: which end to count from. */
618
+ dir?: 'asc' | 'desc';
619
+ /**
620
+ * Multiples of the column's standard deviation added to the base statistic,
621
+ * which is how "one std dev above average" is written: `{ stat: 'avg',
622
+ * offset_stddev: 1 }`. Requests the stddev alongside the base.
623
+ */
624
+ offset_stddev?: number;
625
+ }
626
+ /** One aggregate the grid needs for a column, in the shape the totals call
627
+ * already uses. `args` carries the operands the verb takes, if any. */
628
+ interface ColumnStatRequest {
629
+ func: CellStatVerb;
630
+ field: string;
631
+ alias: string;
632
+ args?: Record<string, any>;
496
633
  }
497
634
  /**
498
- * Resolve a numeric value against a column's `color_ranges`, returning the
499
- * first matching band or null.
635
+ * Deterministic alias for a statistic.
500
636
  *
501
- * Shared by the progress, number and currency cells so a value falling in the
502
- * same band paints the same way whichever datatype renders it. A blank or
503
- * absent bound is open-ended — data-model bands are authored that way ("80 and
504
- * above"), and coercing a missing bound to 0 would stop such a band matching.
505
- * Bands are checked in order, so the author controls precedence.
637
+ * Both sides key off this: the grid asks for it and looks the answer up by the
638
+ * same string, so nothing has to correlate requests with responses positionally.
506
639
  */
507
- declare function matchColorRange(value: any, ranges: any): ColorRangeBand | null;
640
+ declare function cellStatAlias(field: string, stat: CellStatVerb, args?: {
641
+ p?: number;
642
+ n?: number;
643
+ dir?: string;
644
+ }): string;
645
+ /**
646
+ * Every statistic the current columns' rules need, deduplicated by alias.
647
+ *
648
+ * Deliberately derived from the rules rather than configured separately: a
649
+ * column asks for exactly the aggregates its formatting uses, so removing a
650
+ * rule stops the request without anyone remembering to.
651
+ */
652
+ declare function collectColumnStatRequests(columns: Field[] | null | undefined): ColumnStatRequest[];
653
+ /**
654
+ * True when this grid's column carries conditional formatting of its own.
655
+ *
656
+ * The test for "configured" is a non-empty list: an empty `cell_rules` is what
657
+ * removing the last rule leaves behind, and that has to read as "nothing
658
+ * configured here" so the data model's bands come back rather than the column
659
+ * being stuck with no formatting at all.
660
+ */
661
+ declare function hasOwnCellRules(column: Field | null | undefined): boolean;
662
+ /**
663
+ * The rules in force on a column: its own conditional formatting if configured,
664
+ * otherwise the data model's `color_ranges` bands.
665
+ *
666
+ * Conditional formatting is a property of this grid's view, `color_ranges` a
667
+ * property of the field — so the two are separate keys and this is the one place
668
+ * that decides between them. Every consumer (cell painting, stat requests, the
669
+ * design pane) reads through here, so they cannot disagree about which list is
670
+ * live.
671
+ */
672
+ declare function columnCellRules(column: Field | null | undefined): CellFormatRule[];
673
+ /** A statistic's value, or null when it has not come back (or cannot apply). */
674
+ declare function resolveStatValue(field: string, ref: CellStatRef | null | undefined, stats: Record<string, number> | null | undefined): number | null;
675
+ /**
676
+ * A column's rules with every statistic-backed operand replaced by its value.
677
+ *
678
+ * A rule whose statistic has not arrived is dropped rather than evaluated: with
679
+ * the operand missing it would either match everything or nothing, and both are
680
+ * worse than leaving the cell unformatted until the numbers land.
681
+ */
682
+ declare function resolveColumnRules(column: Field | null | undefined, stats: Record<string, number> | null | undefined): CellFormatRule[];
683
+ /** Legacy alias: a colour band is a rule with no operator. */
684
+ interface ColorRangeBand extends CellFormatRule {
685
+ }
686
+ /**
687
+ * Does one rule match this value?
688
+ *
689
+ * Numeric operators fall back to a string comparison when either side is not a
690
+ * number, so `eq` works on a status name as readily as on an amount and an
691
+ * author does not have to know which operator family their datatype belongs to.
692
+ */
693
+ declare function cellRuleMatches(value: any, rule: CellFormatRule, field?: Field | null): boolean;
694
+ /**
695
+ * First rule in the list that matches, or null.
696
+ *
697
+ * Every datatype resolves its rules through this, which is the point: colour
698
+ * bands used to be wired into the number, currency and progress cells only, so
699
+ * a status or a date column could not be conditionally formatted at all.
700
+ */
701
+ declare function matchCellRule(value: any, rules: any, field?: Field | null): CellFormatRule | null;
702
+ /**
703
+ * Percentage of the way `value` sits between a rule's bar bounds, or null when
704
+ * the bar cannot be scaled.
705
+ */
706
+ declare function cellRuleBarPercent(value: any, rule: CellFormatRule | null): number | null;
707
+ /** A matched rule as an ngStyle map, merged over the column's own cell style. */
708
+ declare function cellRuleToCss(value: any, rule: CellFormatRule | null): Record<string, string>;
508
709
  declare const DATA_TYPES: DataTypes[];
509
710
  type AggregationFunction = 'sum' | 'count' | 'avg' | 'min' | 'max';
510
711
  interface PivotColumnGroupState {
@@ -684,6 +885,130 @@ interface CellValueChange {
684
885
  * <ng-template #personCard let-userId let-column="column">…</ng-template>
685
886
  * <eru-grid [personCardTemplate]="personCard" [gridConfig]="cfg"></eru-grid>
686
887
  */
888
+ /**
889
+ * Basic text styling a column may set on its own cells.
890
+ *
891
+ * Kept to four properties on purpose. Cell appearance is otherwise owned by the
892
+ * grid preset and its tokens, and a column that can set arbitrary CSS stops
893
+ * inheriting the theme — the reason this is not a `style` string. These four
894
+ * are what a designer actually reaches for: making a secondary value smaller
895
+ * and quieter, or an identifier bolder.
896
+ */
897
+ interface CellTextStyle {
898
+ font_size?: number;
899
+ font_weight?: number;
900
+ italic?: boolean;
901
+ color?: string;
902
+ background?: string;
903
+ /**
904
+ * Where `background` is painted. `text` wraps the value in a pill that hugs
905
+ * it (the shape status and tag cells already use); `cell` tints the whole
906
+ * cell, which is how a column-wide band reads. Ignored without a background.
907
+ */
908
+ fill?: 'text' | 'cell';
909
+ }
910
+ /**
911
+ * Colour tokens a grid column may pick, mirroring eru-studio's picker but over
912
+ * the grid's own `--grid-*` set — a column styled with a token keeps tracking
913
+ * the preset/theme instead of freezing one hex.
914
+ */
915
+ /**
916
+ * Theme colours offerable on a column or a conditional-format rule.
917
+ *
918
+ * A one-for-one mirror of eru-studio's `colorTokens`, label for label, with
919
+ * `--studio-` swapped for `--grid-`: the two libraries dress the same page, so a
920
+ * colour chosen on a field and the same colour chosen on a grid column have to
921
+ * mean the same thing. Keep the two lists in step when either changes.
922
+ *
923
+ * 'Primary Light' used to sit in here and was never part of that set — nor was
924
+ * `--grid-primary-light` ever defined, so picking it produced an invalid
925
+ * `var()` and silently dropped whatever it was applied to. Its M3 equivalent is
926
+ * Primary Container, which is what studio calls it.
927
+ */
928
+ declare const GRID_COLOR_TOKENS: {
929
+ label: string;
930
+ value: string;
931
+ }[];
932
+ interface ParsedColorValue {
933
+ /** `var(--grid-x)` when the value names a token, else null. */
934
+ token: string | null;
935
+ /** Hex for the custom-colour input; '#000000' when the value is a token. */
936
+ hex: string;
937
+ /** 0-100. */
938
+ alpha: number;
939
+ }
940
+ /**
941
+ * Read one of the three shapes a colour value takes: a bare token, a token
942
+ * wrapped in `color-mix(... N%, transparent)` for partial opacity, or a plain
943
+ * `rgba()`/hex. Same contract as eru-studio's property editor, so a colour
944
+ * authored on a page and one authored on a column mean the same thing.
945
+ *
946
+ * Opacity rides in `color-mix` rather than being baked into a hex precisely so
947
+ * a token stays a token: bake it and the colour stops following the theme.
948
+ */
949
+ declare function parseColorValue(value: any): ParsedColorValue;
950
+ /** Inverse of parseColorValue. Returns null for "nothing set", so a cleared
951
+ * colour falls back to the preset rather than being pinned to black. */
952
+ declare function composeColorValue(token: string | null, hex: string | null, alpha: number): string | null;
953
+ /**
954
+ * CellTextStyle as an ngStyle map. Empty when nothing is set, so the cell keeps
955
+ * the preset's own values rather than being pinned to defaults.
956
+ *
957
+ * A `text` fill turns the container inline-block so it hugs the value and reads
958
+ * as a pill; a `cell` fill leaves the box alone and just tints it.
959
+ */
960
+ declare function cellTextStyleToCss(style: CellTextStyle | null | undefined): Record<string, string>;
961
+ /**
962
+ * Abbreviate a number to k / L / Cr (lacs) or k / mn / bn / tn (millions).
963
+ *
964
+ * Shared: the number cell, the currency cell and a composite part must all
965
+ * abbreviate identically. It lived as a private copy in both number.component
966
+ * and currency.component — byte-identical, and two places to fix.
967
+ */
968
+ declare function abbreviateNumber(num: number, system: 'lacs' | 'mn', decimals: number): string;
969
+ /**
970
+ * Which scale a value abbreviates on when no `display_number_as` is chosen:
971
+ * the one its digit grouping already implies.
972
+ *
973
+ * `seperator` and `display_number_as` were asking the same question twice — the
974
+ * data model already says whether a field reads Indian or Western, and a column
975
+ * grouped 1,00,00,000 that abbreviates to 'mn' is a contradiction. So the
976
+ * grouping supplies the scale and 'Display as' stays editable to override it.
977
+ */
978
+ declare function abbreviationScaleFor(seperator: string | null | undefined): 'lacs' | 'mn';
979
+ /**
980
+ * Render a number or currency value the way its column configures it —
981
+ * decimals, separator locale, abbreviation, optional symbol prefix.
982
+ *
983
+ * The single implementation behind the number cell, the currency cell and a
984
+ * composite part, so the same value never reads two ways in one grid.
985
+ * A non-numeric string is returned untouched: the grid shows what the row
986
+ * holds rather than 'NaN'.
987
+ */
988
+ declare function formatNumberValue(value: any, cfg: Partial<Field> | null | undefined, options?: {
989
+ prefix?: string;
990
+ replaceZero?: string;
991
+ }): string;
992
+ /**
993
+ * Best-effort parse of a stored date value, trying the column's own pattern
994
+ * before falling back to ISO and then the platform parser. Returns null rather
995
+ * than an Invalid Date so callers can render an empty cell.
996
+ */
997
+ declare function parseCellDate(value: any, pattern?: string): Date | null;
998
+ /**
999
+ * Display text for a value under a field's own formatting rules.
1000
+ *
1001
+ * Used where a value must be shown but not edited — today a composite cell's
1002
+ * parts. It deliberately renders text only: a status pill or an avatar stack is
1003
+ * the interactive cell's job, and a composite that tried to host those would be
1004
+ * a layout engine. A column needing that is a `page` cell.
1005
+ */
1006
+ declare function formatCellValue(value: any, field: Field | null | undefined): string;
1007
+ /**
1008
+ * Read a field's value off a row, tolerating both shapes the grid is handed:
1009
+ * a flat row and one wrapping its values in `entity_data`.
1010
+ */
1011
+ declare function readRowValue(row: any, fieldName: string): any;
687
1012
  interface PersonCardContext {
688
1013
  /** The selected person's id ($implicit — bind with let-userId) */
689
1014
  $implicit: string;
@@ -692,6 +1017,16 @@ interface PersonCardContext {
692
1017
  /** Page the field names as the card, from the data model's people_card */
693
1018
  cardPage?: string;
694
1019
  }
1020
+ /** Context a `page` cell hands its renderer — one row, one column. Mirrors
1021
+ * BoardCardContext so a consumer can reuse the same card host for both. */
1022
+ interface CellTemplateContext {
1023
+ /** Row data for this cell ($implicit — bind with let-row) */
1024
+ $implicit: Row;
1025
+ /** The column being rendered, carrying `cell_page_id` */
1026
+ column: Field;
1027
+ /** All visible columns, for a card that shows more than its own field */
1028
+ columns: Field[];
1029
+ }
695
1030
  interface BoardCardContext {
696
1031
  /** Row data for this card ($implicit — bind with let-row) */
697
1032
  $implicit: Row;
@@ -1065,6 +1400,23 @@ declare class EruGridStore {
1065
1400
  name: string;
1066
1401
  patch: Partial<Field>;
1067
1402
  } | null>;
1403
+ /**
1404
+ * Column-wide statistics behind stat-backed conditional-format rules, keyed
1405
+ * by the alias `cellStatAlias` builds. Supplied by the consuming app, which
1406
+ * owns the round trip; the grid only says which aggregates it needs.
1407
+ */
1408
+ private readonly _columnStats;
1409
+ readonly columnStats: _angular_core.Signal<Record<string, number>>;
1410
+ /** Aggregates the current rules need, or null when none do. */
1411
+ private readonly _columnStatsRequest;
1412
+ readonly columnStatsRequest: _angular_core.Signal<ColumnStatRequest[] | null>;
1413
+ setColumnStats(stats: Record<string, number> | null | undefined): void;
1414
+ /**
1415
+ * Publish the aggregate list. Compared before writing so a re-resolve that
1416
+ * produces the same set does not re-trigger the consumer's fetch — the
1417
+ * columns signal changes on every design edit and resize.
1418
+ */
1419
+ setColumnStatsRequest(requests: ColumnStatRequest[] | null): void;
1068
1420
  readonly columns: _angular_core.Signal<Field[]>;
1069
1421
  readonly groups: _angular_core.Signal<RowGroup[]>;
1070
1422
  readonly rows: _angular_core.Signal<Row[]>;
@@ -1573,6 +1925,20 @@ declare class EruGridComponent implements OnInit, AfterViewInit, OnDestroy {
1573
1925
  * card trigger stays inert.
1574
1926
  */
1575
1927
  personCardTemplate?: TemplateRef<PersonCardContext>;
1928
+ /**
1929
+ * Renderer for a `page` column's cells — the same contract as
1930
+ * boardCardTemplate, one level down. The grid invokes it once per row for
1931
+ * every column whose datatype is `page`, handing it the row, that column
1932
+ * (which carries `cell_page_id`) and the visible columns. Omit it and such a
1933
+ * column falls back to showing its raw value.
1934
+ *
1935
+ * Example:
1936
+ * <ng-template #myCell let-row let-column="column">
1937
+ * <my-page-host [row]="row" [pageId]="column.cell_page_id"></my-page-host>
1938
+ * </ng-template>
1939
+ * <eru-grid [cellTemplate]="myCell" [gridConfig]="cfg"></eru-grid>
1940
+ */
1941
+ cellTemplate?: TemplateRef<CellTemplateContext>;
1576
1942
  /**
1577
1943
  * Height in pixels of each board card slot.
1578
1944
  * Must match the fixed height of the card rendered by boardCardTemplate (or the default card).
@@ -1906,6 +2272,10 @@ declare class EruGridComponent implements OnInit, AfterViewInit, OnDestroy {
1906
2272
  * Width of the action column. Icons sit side by side, so the column has to
1907
2273
  * grow with the number configured or they wrap; a kebab is always one icon.
1908
2274
  * Capped so a long action list scrolls the icons rather than eating the grid.
2275
+ *
2276
+ * The floor is the header's own width, not the icons': one action needed only
2277
+ * 48px of icon but the word "Action" is wider than that, so the header was
2278
+ * clipped on every single-action grid.
1909
2279
  */
1910
2280
  readonly actionColumnWidth: _angular_core.Signal<number>;
1911
2281
  onSortColumn(event: Event, column: Field, direction: 'asc' | 'desc'): void;
@@ -1926,6 +2296,12 @@ declare class EruGridComponent implements OnInit, AfterViewInit, OnDestroy {
1926
2296
  * Attributes a mapped column inherits from its data-model field. Only keys
1927
2297
  * the field actually defines are returned, so a sparse field never blanks
1928
2298
  * out what the column already has.
2299
+ *
2300
+ * SEEDED_FIELD_KEYS are merged only while the column carries no value of its
2301
+ * own: the model supplies the starting point, and this grid's own choice —
2302
+ * including an explicit `false` on a checkbox — wins from then on. Without the
2303
+ * column in hand there is no way to tell "not set" from "set to the opposite",
2304
+ * so an override was overwritten on every resolve.
1929
2305
  */
1930
2306
  private buildMappedColumnPatch;
1931
2307
  /** Get the field name used for grouping */
@@ -2100,7 +2476,7 @@ declare class EruGridComponent implements OnInit, AfterViewInit, OnDestroy {
2100
2476
  requestBoardRowsForGroup(group: RowGroup): void;
2101
2477
  loadBoardAll(): void;
2102
2478
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EruGridComponent, never>;
2103
- 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"; "actionClick": "actionClick"; }, never, never, true, never>;
2479
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EruGridComponent, "eru-grid", never, { "gridConfig": { "alias": "gridConfig"; "required": false; }; "boardCardTemplate": { "alias": "boardCardTemplate"; "required": false; }; "personCardTemplate": { "alias": "personCardTemplate"; "required": false; }; "cellTemplate": { "alias": "cellTemplate"; "required": false; }; "boardCardHeight": { "alias": "boardCardHeight"; "required": false; }; "boardCardGap": { "alias": "boardCardGap"; "required": false; }; "boardCardPadding": { "alias": "boardCardPadding"; "required": false; }; }, { "rowSelect": "rowSelect"; "actionClick": "actionClick"; }, never, never, true, never>;
2104
2480
  }
2105
2481
  interface GroupItem {
2106
2482
  type: 'header' | 'row' | 'table-header' | 'row-place-holder' | 'ghost-loading';
@@ -2141,6 +2517,50 @@ declare function resolveRowValue(row: any, fieldName: string): any;
2141
2517
  */
2142
2518
  declare function evaluateRowCondition(expression: string | null | undefined, row: any): boolean;
2143
2519
 
2520
+ /**
2521
+ * Restricts an `<input type="text">` to a numeric value.
2522
+ *
2523
+ * Lives in eru-grid and is exported from its public API so the grid's cells and
2524
+ * eru-studio's page controls share one implementation — the same value typed in
2525
+ * a grid and on a page has to be accepted or rejected identically.
2526
+ *
2527
+ * Text rather than `type="number"` on purpose: a number input reformats the
2528
+ * value on its own, refuses to show a grouped or symbol-prefixed value, and
2529
+ * renders spinners the design does not use. Every eru-studio numeric field is
2530
+ * therefore a text input — which accepted any string at all until this.
2531
+ *
2532
+ * Filtering happens at `beforeinput`, so the character is judged in the context
2533
+ * it would land in: this is what tells a second '.' from the first, and a '-'
2534
+ * typed at the start from one typed mid-value. A `keypress` filter cannot see
2535
+ * either, and blur-time validation lets the user type a whole wrong value first.
2536
+ */
2537
+ declare class NumericInputDirective {
2538
+ private el;
2539
+ /** Allow a decimal point. Off for an integer-only field. */
2540
+ readonly allowDecimal: _angular_core.InputSignal<boolean>;
2541
+ /** Allow a leading minus. Off where a negative makes no sense. */
2542
+ readonly allowNegative: _angular_core.InputSignal<boolean>;
2543
+ /**
2544
+ * Would this be a valid numeric string, allowing the in-progress forms a
2545
+ * user necessarily passes through — '', '-', '1.' — none of which parse as a
2546
+ * number but all of which are on the way to one.
2547
+ */
2548
+ private isAcceptable;
2549
+ /** The value the input would hold if this event were allowed through. */
2550
+ private projectedValue;
2551
+ onBeforeInput(event: InputEvent): void;
2552
+ /**
2553
+ * A paste is filtered rather than blocked: pasting a formatted amount
2554
+ * ("$1,234.50") is a normal thing to do, and stripping it to 1234.50 is what
2555
+ * the user meant. Only a paste with no digits at all is rejected outright.
2556
+ */
2557
+ onPaste(event: ClipboardEvent): void;
2558
+ /** Digits, at most one decimal point, and a single leading minus. */
2559
+ private clean;
2560
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NumericInputDirective, never>;
2561
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NumericInputDirective, "input[eruNumericInput]", never, { "allowDecimal": { "alias": "eruAllowDecimal"; "required": false; "isSignal": true; }; "allowNegative": { "alias": "eruAllowNegative"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2562
+ }
2563
+
2144
2564
  interface AttachmentConfig {
2145
2565
  disabled?: boolean;
2146
2566
  required?: boolean;
@@ -2244,6 +2664,116 @@ declare class CheckboxComponent {
2244
2664
  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>;
2245
2665
  }
2246
2666
 
2667
+ /** One chip. `color`/`background`/`dot` are optional: without them the chip
2668
+ * takes the neutral surface, which is what a plain value wants. */
2669
+ interface ChipListItem {
2670
+ label: string;
2671
+ color?: string | null;
2672
+ background?: string | null;
2673
+ dot?: string | null;
2674
+ }
2675
+ /**
2676
+ * A row of chips that fits its container, with a `+N` for the rest.
2677
+ *
2678
+ * Shared by the grid's and eru-studio's multi-selects so a set of values reads
2679
+ * the same in a cell, on a page and inside a select's trigger.
2680
+ *
2681
+ * Widths are MEASURED, not estimated. The tag cell assumes ~30px per chip,
2682
+ * which is close enough for short tags and wrong for the entity names a select
2683
+ * holds ("Acme Industries Pvt Ltd" is four times that) — it either wraps or
2684
+ * hides chips that would have fitted. Measuring with a canvas rather than by
2685
+ * rendering avoids the two-pass flicker of laying every chip out and then
2686
+ * hiding the overflow.
2687
+ */
2688
+ declare class ChipListComponent implements OnDestroy {
2689
+ private host;
2690
+ private readonly _chips;
2691
+ /** Plain labels — the neutral chip. */
2692
+ set labels(value: string[] | null | undefined);
2693
+ /**
2694
+ * Chips that carry their own colours — a tag or a status, where the colour is
2695
+ * part of the value's meaning rather than decoration. Takes precedence over
2696
+ * `labels`; set one or the other.
2697
+ */
2698
+ set chips(value: ChipListItem[] | null | undefined);
2699
+ private readonly _compact;
2700
+ set compact(value: boolean | null | undefined);
2701
+ /**
2702
+ * Width to fit into. 0 means "measure the host", which is what a page control
2703
+ * wants; a grid cell passes its column width, which is known before layout.
2704
+ */
2705
+ private readonly _availableWidth;
2706
+ set availableWidth(value: number | null | undefined);
2707
+ readonly isCompact: _angular_core.Signal<boolean>;
2708
+ /** Width assumed when nothing above the row constrains it. */
2709
+ private static readonly UNCONSTRAINED_WIDTH;
2710
+ private readonly measuredWidth;
2711
+ private observer;
2712
+ constructor();
2713
+ private observe;
2714
+ ngOnDestroy(): void;
2715
+ /**
2716
+ * The width to fit into.
2717
+ *
2718
+ * A grid cell hands us its column width and that is the constraint. A page
2719
+ * control does not: eru-studio sizes every field shell with `width:
2720
+ * fit-content`, so the host hugs whatever we just rendered — measuring the
2721
+ * host would mean measuring our own output, and the row could never grow past
2722
+ * the chip it happens to be showing. That is why view mode sat at one chip
2723
+ * while edit mode, which gets Material's fixed infix width, showed three.
2724
+ *
2725
+ * So when no width is given, look UP for the first ancestor that is wider
2726
+ * than the host: that is the box actually constraining the layout, and it
2727
+ * does not change when our content does.
2728
+ */
2729
+ private measure;
2730
+ /** Chip text width for the host's actual font, via a shared canvas. */
2731
+ private static canvas;
2732
+ private textWidth;
2733
+ private readonly fit;
2734
+ readonly visible: _angular_core.Signal<ChipListItem[]>;
2735
+ readonly hiddenCount: _angular_core.Signal<number>;
2736
+ readonly hiddenLabels: _angular_core.Signal<string>;
2737
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ChipListComponent, never>;
2738
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ChipListComponent, "eru-chip-list", never, { "labels": { "alias": "labels"; "required": false; }; "chips": { "alias": "chips"; "required": false; }; "compact": { "alias": "compact"; "required": false; }; "availableWidth": { "alias": "availableWidth"; "required": false; }; }, {}, never, never, true, never>;
2739
+ }
2740
+
2741
+ interface CompositePart {
2742
+ name: string;
2743
+ label: string;
2744
+ text: string;
2745
+ }
2746
+ /**
2747
+ * Several of the row's own columns rendered as one cell — a name over an email,
2748
+ * an amount over a date.
2749
+ *
2750
+ * View-only, and text-only, by design. Each part is formatted by its OWN
2751
+ * field's rules (`formatCellValue`), so a currency part keeps its symbol and
2752
+ * decimals and a date part keeps its pattern; the composite itself decides only
2753
+ * arrangement and emphasis. Editing stays on the underlying columns, which are
2754
+ * usually hidden once composed.
2755
+ */
2756
+ declare class CompositeComponent {
2757
+ row: _angular_core.InputSignal<any>;
2758
+ column: _angular_core.InputSignal<Field | null>;
2759
+ columns: _angular_core.InputSignal<Field[]>;
2760
+ isDrillable: _angular_core.InputSignal<boolean>;
2761
+ drilldownClick: _angular_core.OutputEmitterRef<string>;
2762
+ readonly inline: _angular_core.Signal<boolean>;
2763
+ readonly separator: _angular_core.Signal<string>;
2764
+ readonly primaryCss: _angular_core.Signal<Record<string, string>>;
2765
+ readonly secondaryCss: _angular_core.Signal<Record<string, string>>;
2766
+ /**
2767
+ * Falls back to the composite column itself when `composite_fields` is unset
2768
+ * or names nothing that exists, so a column switched to `composite` before it
2769
+ * is configured still shows its own value instead of going blank.
2770
+ */
2771
+ readonly parts: _angular_core.Signal<CompositePart[]>;
2772
+ onDrilldown(event: Event, part: CompositePart): void;
2773
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<CompositeComponent, never>;
2774
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<CompositeComponent, "eru-composite", never, { "row": { "alias": "row"; "required": false; "isSignal": true; }; "column": { "alias": "column"; "required": false; "isSignal": true; }; "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "isDrillable": { "alias": "isDrillable"; "required": false; "isSignal": true; }; }, { "drilldownClick": "drilldownClick"; }, never, never, true, never>;
2775
+ }
2776
+
2247
2777
  interface CurrencyValidationResult {
2248
2778
  isValid: boolean;
2249
2779
  error?: string;
@@ -2286,7 +2816,7 @@ declare class CurrencyComponent {
2286
2816
  rangeColor: _angular_core.Signal<string | null>;
2287
2817
  rangeBackground: _angular_core.Signal<string | null>;
2288
2818
  constructor();
2289
- formatNumberSignal: _angular_core.Signal<string | undefined>;
2819
+ formatNumberSignal: _angular_core.Signal<string>;
2290
2820
  onActivate(): void;
2291
2821
  onBlur(): void;
2292
2822
  onValueChange(event: any): void;
@@ -2559,7 +3089,7 @@ declare class NumberComponent {
2559
3089
  rangeColor: _angular_core.Signal<string | null>;
2560
3090
  rangeBackground: _angular_core.Signal<string | null>;
2561
3091
  constructor();
2562
- formatNumberSignal: _angular_core.Signal<string | undefined>;
3092
+ formatNumberSignal: _angular_core.Signal<string>;
2563
3093
  onActivate(): void;
2564
3094
  onBlur(): void;
2565
3095
  onValueChange(event: any): void;
@@ -2867,6 +3397,15 @@ declare class SelectComponent implements OnInit, AfterViewInit {
2867
3397
  editModeChange: EventEmitter<boolean>;
2868
3398
  currentValue: _angular_core.WritableSignal<any>;
2869
3399
  searchText: _angular_core.WritableSignal<string>;
3400
+ private static readonly OPTIONS_PAGE_SIZE;
3401
+ /** The rendered mat-select, for its `panel` element once the overlay opens. */
3402
+ private matSelect?;
3403
+ /** Debounced search text, sent to the consumer as `search` (→ field_str). */
3404
+ private readonly serverSearch;
3405
+ private readonly optionsSkip;
3406
+ /** Set when a short page comes back — nothing more to ask for. */
3407
+ private readonly optionsExhausted;
3408
+ private searchDebounce;
2870
3409
  error: _angular_core.WritableSignal<string>;
2871
3410
  isAllSelected: _angular_core.Signal<boolean>;
2872
3411
  isIndeterminate: _angular_core.Signal<boolean>;
@@ -2910,11 +3449,31 @@ declare class SelectComponent implements OnInit, AfterViewInit {
2910
3449
  */
2911
3450
  optionsCacheKey: _angular_core.Signal<string>;
2912
3451
  onSearchChange(searchValue: string): void;
3452
+ /**
3453
+ * Ask the consumer for the next page when the panel nears its end.
3454
+ *
3455
+ * Options are appended, never recycled: mat-select addresses MatOption
3456
+ * instances for its selection, trigger text and keyboard navigation, so a
3457
+ * virtual scroller destroying them as they leave the viewport breaks all
3458
+ * three. Paging the data while keeping the DOM is what is wanted here.
3459
+ */
3460
+ private panelScrollTeardown;
3461
+ /** Attach the paging listener to mat-select's panel once the overlay exists. */
3462
+ private attachPanelScroll;
3463
+ private detachPanelScroll;
3464
+ private onPanelScroll;
2913
3465
  onOpenedChange(opened: boolean): void;
2914
3466
  ngAfterViewInit(): void;
2915
3467
  onDrillableClick(event: Event): void;
2916
3468
  getProperty(property: string): any;
2917
3469
  hasRequiredValidation(): boolean;
3470
+ /**
3471
+ * Selected values as their option labels, for the chip row.
3472
+ *
3473
+ * Same resolution as formatDisplayValue — the chip list only changes how they
3474
+ * are laid out, so the two must never disagree about what a value is called.
3475
+ */
3476
+ readonly selectedLabels: _angular_core.Signal<string[]>;
2918
3477
  formatDisplayValue(): string;
2919
3478
  trackByValueAndIndex(index: number, option: any): string;
2920
3479
  /**
@@ -3053,6 +3612,13 @@ declare class TagComponent implements OnInit {
3053
3612
  label: any;
3054
3613
  color: any;
3055
3614
  }[]>;
3615
+ /**
3616
+ * Selected tags as coloured chips for the shared chip row.
3617
+ *
3618
+ * The colour is part of a tag's meaning, so it travels with the label rather
3619
+ * than the chip list assuming a neutral pill.
3620
+ */
3621
+ readonly tagChips: _angular_core.Signal<ChipListItem[]>;
3056
3622
  maxDisplayTags: _angular_core.Signal<number>;
3057
3623
  tagDisplay: _angular_core.Signal<{
3058
3624
  displayTags: string[];
@@ -3221,5 +3787,5 @@ declare class ThemeToggleComponent {
3221
3787
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ThemeToggleComponent, "eru-theme-toggle", never, {}, {}, never, never, true, never>;
3222
3788
  }
3223
3789
 
3224
- 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, evaluateRowCondition, formatDateWithPattern, matchColorRange, normalizeDatatype, normalizeDateFormat, normalizeDateTimeFormat, parseDateWithPattern, resolveRowValue, statusPillColors, tagPillColors };
3225
- export type { ActionClick, AggregationFunction, AttachmentConfig, BoardCardContext, CellValueChange, ColorRangeBand, ColumnReorder, ColumnResize, ColumnWidthConstraints, CurrencyValidationResult, DataTypes, Drilldown, DurationValue, DynamicDataRequest, EmailValidationResult, ExcelDownloadPayload, ExcelDownloadRequest, ExcelPivotConfig, Field, GridAction, GridConfiguration, GridFeatures, GridMode, GridPreset, GridStyles, GridTokens, GroupItem$1 as GroupItem, LocationConfig, LocationValidationResult, NumberValidationResult, PeopleOption, PersonCardContext, PivotColumnGroupState, PivotColumnHeader, PivotColumnKind, PivotConfiguration, PivotHeaderStructure, PivotMetadata, PivotResult, PivotRow, PivotRowKind, RatingConfig, Row, RowDataRequest, RowDataResponse, RowGrandTotal, RowGroup, ServerValidationNotice, StatusPillColors, TagPillColors, TextareaValidationResult, TextboxValidationResult, Theme, WebsiteValidationResult };
3790
+ 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, evaluateRowCondition, formatCellValue, formatDateWithPattern, formatNumberValue, hasOwnCellRules, matchCellRule, matchColorRange, normalizeDatatype, normalizeDateFormat, normalizeDateTimeFormat, parseCellDate, parseColorValue, parseDateWithPattern, readRowValue, resolveColumnRules, resolveRowValue, resolveStatValue, statusPillColors, tagPillColors };
3791
+ export type { ActionClick, AggregationFunction, AttachmentConfig, BoardCardContext, CellFormatRule, CellRuleOperator, CellStatRef, CellStatVerb, CellTemplateContext, CellTextStyle, CellValueChange, ChipListItem, ColorRangeBand, ColumnReorder, ColumnResize, ColumnStatRequest, ColumnWidthConstraints, CompositePart, CurrencyValidationResult, DataTypes, Drilldown, DurationValue, DynamicDataRequest, EmailValidationResult, ExcelDownloadPayload, ExcelDownloadRequest, ExcelPivotConfig, Field, GridAction, GridConfiguration, GridFeatures, GridMode, GridPreset, GridStyles, GridTokens, GroupItem$1 as GroupItem, LocationConfig, LocationValidationResult, NumberValidationResult, ParsedColorValue, PeopleOption, PersonCardContext, PivotColumnGroupState, PivotColumnHeader, PivotColumnKind, PivotConfiguration, PivotHeaderStructure, PivotMetadata, PivotResult, PivotRow, PivotRowKind, RatingConfig, Row, RowDataRequest, RowDataResponse, RowGrandTotal, RowGroup, ServerValidationNotice, StatusPillColors, TagPillColors, TextareaValidationResult, TextboxValidationResult, Theme, WebsiteValidationResult };