@nexterajs/ui 0.0.1 → 0.0.2

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.
Files changed (44) hide show
  1. package/fesm2022/nexterajs-ui-core.mjs +189 -5
  2. package/fesm2022/nexterajs-ui-core.mjs.map +1 -1
  3. package/fesm2022/nexterajs-ui-dropdowns.mjs +314 -50
  4. package/fesm2022/nexterajs-ui-dropdowns.mjs.map +1 -1
  5. package/fesm2022/nexterajs-ui-file-uploader.mjs +6 -3
  6. package/fesm2022/nexterajs-ui-file-uploader.mjs.map +1 -1
  7. package/fesm2022/nexterajs-ui-forms.mjs +729 -370
  8. package/fesm2022/nexterajs-ui-forms.mjs.map +1 -1
  9. package/fesm2022/nexterajs-ui-grid.mjs +1188 -208
  10. package/fesm2022/nexterajs-ui-grid.mjs.map +1 -1
  11. package/fesm2022/nexterajs-ui-intl.mjs +15 -5
  12. package/fesm2022/nexterajs-ui-intl.mjs.map +1 -1
  13. package/fesm2022/nexterajs-ui-notify.mjs +351 -0
  14. package/fesm2022/nexterajs-ui-notify.mjs.map +1 -0
  15. package/fesm2022/nexterajs-ui-pivot.mjs +146 -116
  16. package/fesm2022/nexterajs-ui-pivot.mjs.map +1 -1
  17. package/fesm2022/nexterajs-ui-styling.mjs +568 -3
  18. package/fesm2022/nexterajs-ui-styling.mjs.map +1 -1
  19. package/fesm2022/nexterajs-ui-tree-view.mjs +147 -18
  20. package/fesm2022/nexterajs-ui-tree-view.mjs.map +1 -1
  21. package/fesm2022/nexterajs-ui-validation.mjs +247 -92
  22. package/fesm2022/nexterajs-ui-validation.mjs.map +1 -1
  23. package/fesm2022/nexterajs-ui.mjs +1 -0
  24. package/fesm2022/nexterajs-ui.mjs.map +1 -1
  25. package/package.json +5 -1
  26. package/styles/_dropdown.scss +149 -0
  27. package/styles/_editor.scss +23 -4
  28. package/styles/_icons.scss +92 -0
  29. package/styles/_mixins.scss +104 -1
  30. package/styles/_scrollbar.scss +134 -0
  31. package/styles/_toast.scss +243 -0
  32. package/styles/_tokens.scss +48 -0
  33. package/styles/index.scss +4 -0
  34. package/types/nexterajs-ui-core.d.ts +142 -22
  35. package/types/nexterajs-ui-dropdowns.d.ts +145 -27
  36. package/types/nexterajs-ui-file-uploader.d.ts +4 -1
  37. package/types/nexterajs-ui-forms.d.ts +103 -19
  38. package/types/nexterajs-ui-grid.d.ts +445 -57
  39. package/types/nexterajs-ui-notify.d.ts +147 -0
  40. package/types/nexterajs-ui-pivot.d.ts +35 -22
  41. package/types/nexterajs-ui-styling.d.ts +163 -3
  42. package/types/nexterajs-ui-tree-view.d.ts +50 -1
  43. package/types/nexterajs-ui-validation.d.ts +94 -15
  44. package/types/nexterajs-ui.d.ts +1 -0
@@ -1,5 +1,7 @@
1
+ import { NxeNumberSeparators } from '@nexterajs/ui/intl';
1
2
  import * as _angular_core from '@angular/core';
2
3
  import { NgZone, ViewContainerRef, OnDestroy, EventEmitter, TemplateRef } from '@angular/core';
4
+ import { NxeExpr } from '@nexterajs/ui/core';
3
5
  import { Overlay, OverlayRef } from '@angular/cdk/overlay';
4
6
 
5
7
  /** A DevExtreme-style column `format`: a number-mask string, a date format string, or `{ type }`. */
@@ -10,6 +12,12 @@ interface NxeColumnFormatObject {
10
12
  formatter?: (value: any) => string;
11
13
  }
12
14
  type NxeColumnFormat = string | NxeColumnFormatObject;
15
+ /**
16
+ * A column's declared `dataType`. Leaving it unset is not the same as `'string'`: as in dx
17
+ * (m_columns_controller.js `updateColumnDataTypes`), an unset type is inferred from the loaded
18
+ * values, which is what right-aligns and formats a numeric column nobody typed.
19
+ */
20
+ type NxeColumnDataType = 'string' | 'number' | 'date' | 'boolean';
13
21
  /** Column `customizeText` callback — `(info) => string` (used by `formatAccounting`). */
14
22
  type NxeCustomizeText = (info: {
15
23
  value: any;
@@ -161,7 +169,8 @@ interface NxeGridSelectionChangedEvent<T = any> {
161
169
  interface NxeExportColumn {
162
170
  caption: string;
163
171
  dataField: string;
164
- dataType: 'string' | 'number' | 'date' | 'boolean';
172
+ /** The declared type, else the inferred one; `undefined` while nothing could be inferred. */
173
+ dataType: NxeColumnDataType | undefined;
165
174
  format: NxeColumnFormat | undefined;
166
175
  }
167
176
  /** dx TreeList node shape — the slice `exportTreeToExcel` walks (`data` + `children`). */
@@ -227,20 +236,82 @@ declare function nxeNumberMask(format: NxeColumnFormat | undefined): string | un
227
236
  * `{ formatter }`). No format / an unresolvable one → `String(value)` (the grid's untyped
228
237
  * rendering).
229
238
  */
230
- declare function nxeFormatNumberValue(value: number, format: NxeColumnFormat | undefined): string;
239
+ declare function nxeFormatNumberValue(value: number, format: NxeColumnFormat | undefined, separators?: NxeNumberSeparators): string;
231
240
  /**
232
- * Derive the Excel `numFmt` mask for a `format`: string masks pass through, fixedPoint/currency
241
+ * Derive the Excel `numFmt` mask for a `format`: string masks go through
242
+ * {@link nxeExcelNumberMask} (the same conversion the grid export uses), fixedPoint/currency
233
243
  * come from {@link nxeNumberMask}, percent appends a bare `%` (Excel multiplies by 100 itself),
234
244
  * and any other `{ type }` is a date pattern used as-is. `{ formatter }`-only objects →
235
245
  * `undefined` (callers fall back to exporting the formatted text).
236
246
  */
237
247
  declare function nxeExcelNumFmt(format: NxeColumnFormat | undefined): string | undefined;
248
+ /**
249
+ * Excel `numFmt` for a dx LDML string mask.
250
+ *
251
+ * The two languages disagree on one character. In Excel an unquoted comma after the last digit
252
+ * placeholder SCALES the value by 1/1000, while dx's LDML engine ignores it (see the golden
253
+ * transcript in `@nexterajs/ui/intl`). The consumer percent mask `#,##0.00,'%'` therefore
254
+ * exported 100 as "0.10%". Dropped here, per `;` section: every unquoted comma after the
255
+ * section's decimal point, and a comma that follows a digit placeholder with no placeholder
256
+ * after it before the section's decimal point or end (`#,##0,'%'`, `#,##0,.00`) — none of them
257
+ * ever meant anything to dx. Grouping commas between integer placeholders stay, and
258
+ * single-quoted literals become Excel's double-quoted ones.
259
+ */
260
+ declare function nxeExcelNumberMask(mask: string): string;
261
+ /**
262
+ * dx `getValueDataType` (m_columns_controller_utils.js:230-236): the type a value contributes
263
+ * to column type inference. `null`, arrays and functions contribute nothing.
264
+ */
265
+ declare function nxeValueDataType(v: unknown): NxeColumnDataType | 'object' | undefined;
266
+ /**
267
+ * dx's number-column deserializer (m_columns_controller_utils.js:263 `updateSerializers`):
268
+ * `parseFloat`, so "20.5" is 20.5 and "1,234" is 1 exactly as dx shows it. `NaN` when the
269
+ * value is not numeric at all, which callers render raw.
270
+ */
271
+ declare function nxeNumberValue(raw: unknown): number;
238
272
  /**
239
273
  * The data grid's default cell rendering (pure extraction of nxe-data-grid's `defaultFormat`):
240
- * `dataType`/`format` → display text. Number cells honour string masks only — exactly the
241
- * grid's pre-extraction behaviour.
274
+ * `dataType`/`format` → display text.
275
+ *
276
+ * dx's format_helper (format_helper.js:23-43) formats by the VALUE, not by the column type: any
277
+ * JS number meets any valid `format`, whatever the column declared. So a number in an untyped
278
+ * or string column still takes its mask, and a number column honours object formats as well.
242
279
  */
243
- declare function nxeDefaultFormat(dataType: 'string' | 'number' | 'date' | 'boolean' | undefined, format: NxeColumnFormat | undefined, raw: any): string;
280
+ declare function nxeDefaultFormat(dataType: NxeColumnDataType | 'object' | undefined, format: NxeColumnFormat | undefined, raw: any, separators?: NxeNumberSeparators): string;
281
+
282
+ /**
283
+ * `nxe-header-filter` — drop-in replacement for DevExtreme `dxo-header-filter` (grid child option).
284
+ *
285
+ * Adds a funnel icon to each filterable column header; clicking it opens a popup of the column's
286
+ * distinct values (checkbox list + optional search) to filter the grid. The app uses essentially
287
+ * one shape: `[search]="{ enabled: true }" [visible]="true"` (60 files). Legacy `[allowSearch]`
288
+ * (1×) folds into `search.enabled`.
289
+ */
290
+ declare class NxeHeaderFilterComponent {
291
+ readonly visible: _angular_core.InputSignal<boolean>;
292
+ readonly search: _angular_core.InputSignal<{
293
+ enabled?: boolean;
294
+ }>;
295
+ /** Legacy alias for `search.enabled` (one holdout). */
296
+ readonly allowSearch: _angular_core.InputSignal<boolean>;
297
+ /**
298
+ * dx `headerFilter.dataSource` — a fixed `{ text, value }` list shown instead of the values
299
+ * derived from the rows.
300
+ */
301
+ readonly dataSource: _angular_core.InputSignal<readonly {
302
+ text?: string;
303
+ value?: any;
304
+ }[]>;
305
+ /** Whether the in-popup search box shows.
306
+ *
307
+ * Defaults to **true**, matching the legacy DevExtreme deployment: a popup listing every
308
+ * distinct value in a column is close to unusable without one. 60 of the app's templates
309
+ * already opt in explicitly, so this only changes the handful that never said either way —
310
+ * and an explicit `[search]="{ enabled: false }"` still turns it off. */
311
+ searchEnabled(): boolean;
312
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeHeaderFilterComponent, never>;
313
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeHeaderFilterComponent, "nxe-header-filter", never, { "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "search": { "alias": "search"; "required": false; "isSignal": true; }; "allowSearch": { "alias": "allowSearch"; "required": false; "isSignal": true; }; "dataSource": { "alias": "dataSource"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
314
+ }
244
315
 
245
316
  /**
246
317
  * `nxe-column` — drop-in replacement for DevExtreme `dxi-column`.
@@ -254,7 +325,11 @@ declare class NxeColumnComponent {
254
325
  readonly caption: _angular_core.InputSignal<string>;
255
326
  /** Bound row field (supports dotted paths and bound/ternary expressions). */
256
327
  readonly dataField: _angular_core.InputSignal<string>;
257
- readonly dataType: _angular_core.InputSignal<"string" | "number" | "boolean" | "date">;
328
+ /**
329
+ * Unset = dx: inferred from the loaded data (m_columns_controller.js:914-975), so a numeric
330
+ * column nobody typed still right-aligns and honours its `format`. Declaring it wins.
331
+ */
332
+ readonly dataType: _angular_core.InputSignal<NxeColumnDataType>;
258
333
  /** px width — number (`[width]="150"`) or numeric string (`width="200"`). */
259
334
  readonly width: _angular_core.InputSignal<string | number>;
260
335
  readonly minWidth: _angular_core.InputSignal<string | number>;
@@ -272,8 +347,8 @@ declare class NxeColumnComponent {
272
347
  */
273
348
  readonly lookup: _angular_core.InputSignal<{
274
349
  dataSource: readonly any[];
275
- valueExpr?: string;
276
- displayExpr?: string;
350
+ valueExpr?: NxeExpr;
351
+ displayExpr?: NxeExpr;
277
352
  }>;
278
353
  readonly allowSorting: _angular_core.InputSignal<boolean>;
279
354
  /** dx parity: whether the header can be dragged into the group panel (default true). */
@@ -318,10 +393,15 @@ declare class NxeColumnComponent {
318
393
  * its children, and flattening them here would collapse the very nesting the band expresses.
319
394
  */
320
395
  readonly childColumns: _angular_core.Signal<readonly NxeColumnComponent[]>;
396
+ /**
397
+ * A `<nxe-header-filter>` declared inside this column, dx's per-column `headerFilter`
398
+ * config. Only its `dataSource` is read here; visibility and search stay grid-level.
399
+ */
400
+ readonly headerFilterConfig: _angular_core.Signal<NxeHeaderFilterComponent>;
321
401
  /** dx `isBand`: a column that groups other columns under a shared header instead of a field. */
322
402
  readonly isBand: _angular_core.Signal<boolean>;
323
403
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeColumnComponent, never>;
324
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeColumnComponent, "nxe-column", never, { "caption": { "alias": "caption"; "required": false; "isSignal": true; }; "dataField": { "alias": "dataField"; "required": false; "isSignal": true; }; "dataType": { "alias": "dataType"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "minWidth": { "alias": "minWidth"; "required": false; "isSignal": true; }; "alignment": { "alias": "alignment"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "customizeText": { "alias": "customizeText"; "required": false; "isSignal": true; }; "cellTemplate": { "alias": "cellTemplate"; "required": false; "isSignal": true; }; "lookup": { "alias": "lookup"; "required": false; "isSignal": true; }; "allowSorting": { "alias": "allowSorting"; "required": false; "isSignal": true; }; "allowGrouping": { "alias": "allowGrouping"; "required": false; "isSignal": true; }; "allowResizing": { "alias": "allowResizing"; "required": false; "isSignal": true; }; "allowFiltering": { "alias": "allowFiltering"; "required": false; "isSignal": true; }; "allowEditing": { "alias": "allowEditing"; "required": false; "isSignal": true; }; "allowExporting": { "alias": "allowExporting"; "required": false; "isSignal": true; }; "allowSearch": { "alias": "allowSearch"; "required": false; "isSignal": true; }; "allowHeaderFiltering": { "alias": "allowHeaderFiltering"; "required": false; "isSignal": true; }; "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "sortOrder": { "alias": "sortOrder"; "required": false; "isSignal": true; }; "sortIndex": { "alias": "sortIndex"; "required": false; "isSignal": true; }; "groupIndex": { "alias": "groupIndex"; "required": false; "isSignal": true; }; "fixed": { "alias": "fixed"; "required": false; "isSignal": true; }; "filterValues": { "alias": "filterValues"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "cssClass": { "alias": "cssClass"; "required": false; "isSignal": true; }; "editCellTemplate": { "alias": "editCellTemplate"; "required": false; "isSignal": true; }; "groupCellTemplate": { "alias": "groupCellTemplate"; "required": false; "isSignal": true; }; }, {}, ["childColumns"], never, true, never>;
404
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeColumnComponent, "nxe-column", never, { "caption": { "alias": "caption"; "required": false; "isSignal": true; }; "dataField": { "alias": "dataField"; "required": false; "isSignal": true; }; "dataType": { "alias": "dataType"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "minWidth": { "alias": "minWidth"; "required": false; "isSignal": true; }; "alignment": { "alias": "alignment"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "customizeText": { "alias": "customizeText"; "required": false; "isSignal": true; }; "cellTemplate": { "alias": "cellTemplate"; "required": false; "isSignal": true; }; "lookup": { "alias": "lookup"; "required": false; "isSignal": true; }; "allowSorting": { "alias": "allowSorting"; "required": false; "isSignal": true; }; "allowGrouping": { "alias": "allowGrouping"; "required": false; "isSignal": true; }; "allowResizing": { "alias": "allowResizing"; "required": false; "isSignal": true; }; "allowFiltering": { "alias": "allowFiltering"; "required": false; "isSignal": true; }; "allowEditing": { "alias": "allowEditing"; "required": false; "isSignal": true; }; "allowExporting": { "alias": "allowExporting"; "required": false; "isSignal": true; }; "allowSearch": { "alias": "allowSearch"; "required": false; "isSignal": true; }; "allowHeaderFiltering": { "alias": "allowHeaderFiltering"; "required": false; "isSignal": true; }; "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "sortOrder": { "alias": "sortOrder"; "required": false; "isSignal": true; }; "sortIndex": { "alias": "sortIndex"; "required": false; "isSignal": true; }; "groupIndex": { "alias": "groupIndex"; "required": false; "isSignal": true; }; "fixed": { "alias": "fixed"; "required": false; "isSignal": true; }; "filterValues": { "alias": "filterValues"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "cssClass": { "alias": "cssClass"; "required": false; "isSignal": true; }; "editCellTemplate": { "alias": "editCellTemplate"; "required": false; "isSignal": true; }; "groupCellTemplate": { "alias": "groupCellTemplate"; "required": false; "isSignal": true; }; }, {}, ["childColumns", "headerFilterConfig"], never, true, never>;
325
405
  }
326
406
 
327
407
  /**
@@ -453,32 +533,6 @@ declare class NxeSelectionComponent {
453
533
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeSelectionComponent, "nxe-selection", never, { "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "selectAllMode": { "alias": "selectAllMode"; "required": false; "isSignal": true; }; "showCheckBoxesMode": { "alias": "showCheckBoxesMode"; "required": false; "isSignal": true; }; "recursive": { "alias": "recursive"; "required": false; "isSignal": true; }; "selectByClick": { "alias": "selectByClick"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
454
534
  }
455
535
 
456
- /**
457
- * `nxe-header-filter` — drop-in replacement for DevExtreme `dxo-header-filter` (grid child option).
458
- *
459
- * Adds a funnel icon to each filterable column header; clicking it opens a popup of the column's
460
- * distinct values (checkbox list + optional search) to filter the grid. The app uses essentially
461
- * one shape: `[search]="{ enabled: true }" [visible]="true"` (60 files). Legacy `[allowSearch]`
462
- * (1×) folds into `search.enabled`.
463
- */
464
- declare class NxeHeaderFilterComponent {
465
- readonly visible: _angular_core.InputSignal<boolean>;
466
- readonly search: _angular_core.InputSignal<{
467
- enabled?: boolean;
468
- }>;
469
- /** Legacy alias for `search.enabled` (one holdout). */
470
- readonly allowSearch: _angular_core.InputSignal<boolean>;
471
- /** Whether the in-popup search box shows.
472
- *
473
- * Defaults to **true**, matching the legacy DevExtreme deployment: a popup listing every
474
- * distinct value in a column is close to unusable without one. 60 of the app's templates
475
- * already opt in explicitly, so this only changes the handful that never said either way —
476
- * and an explicit `[search]="{ enabled: false }"` still turns it off. */
477
- searchEnabled(): boolean;
478
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeHeaderFilterComponent, never>;
479
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeHeaderFilterComponent, "nxe-header-filter", never, { "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "search": { "alias": "search"; "required": false; "isSignal": true; }; "allowSearch": { "alias": "allowSearch"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
480
- }
481
-
482
536
  /**
483
537
  * Header-filter state model — the draft/apply/search machinery extracted from
484
538
  * `nxe-data-grid` (pivot parity Phase 3a) so the pivot's field panel can reuse it on pages
@@ -490,6 +544,8 @@ declare class NxeHeaderFilterComponent {
490
544
  * {@link NxeHeaderFilterSource} adapter, so it owns only the frozen semantics:
491
545
  * `(Blanks)` first, Yes/No boolean buckets, the date Year > Month > Day tree, dx search
492
546
  * scoping of (Select All), check-on-open, and "everything checked = no filter" on apply.
547
+ * (Select All)'s indeterminate state ({@link NxeHeaderFilterModel.someChecked}) is derived
548
+ * from the same draft and search scope, and changes none of these rules.
493
549
  */
494
550
  /**
495
551
  * Header-filter key for blank-ish values (`null`/`undefined`/`''`) — dx lists them as one
@@ -526,6 +582,18 @@ interface NxeHeaderFilterSource<C> {
526
582
  boolLabel(column: C, raw: any): 'Yes' | 'No' | null;
527
583
  /** Raw-value comparator (numbers numeric, dates by instant, else locale strings). */
528
584
  compare(a: any, b: any): number;
585
+ /**
586
+ * dx `headerFilter.dataSource` on a column: a fixed `{ text, value }` list replacing the
587
+ * values derived from the rows. Returning `null` (the default) keeps the derived list.
588
+ *
589
+ * A fixed list is used verbatim — declared order, no `(Blanks)` sentinel, and values that
590
+ * never occur in the data still listed. That is dx's behaviour and the point of supplying
591
+ * one: a boolean column whose rows are all `true` should still offer "No".
592
+ */
593
+ fixedItems?(column: C): {
594
+ key: any;
595
+ text: string;
596
+ }[] | null;
529
597
  }
530
598
  declare class NxeHeaderFilterModel<C = unknown> {
531
599
  private readonly source;
@@ -567,6 +635,13 @@ declare class NxeHeaderFilterModel<C = unknown> {
567
635
  readonly visibleKeys: _angular_core.Signal<any[]>;
568
636
  /** (Select All) state/toggle operate on the VISIBLE keys (dx: search scopes Select All). */
569
637
  readonly allChecked: _angular_core.Signal<boolean>;
638
+ /**
639
+ * (Select All)'s third state: some visible keys checked, but not all. dx shows it as the
640
+ * indeterminate square (the select-all dxCheckBox's `value: undefined`). It is never true
641
+ * together with {@link allChecked}, so check-on-open, which checks everything when no filter
642
+ * is applied, always opens on a fully checked box and never on a half-checked one.
643
+ */
644
+ readonly someChecked: _angular_core.Signal<boolean>;
570
645
  /** Whether every key of the whole column is checked — decides "no filter" on apply. */
571
646
  readonly fullChecked: _angular_core.Signal<boolean>;
572
647
  /**
@@ -589,8 +664,10 @@ declare class NxeHeaderFilterModel<C = unknown> {
589
664
  * The header-filter popup panel — the `.nxe-hf-*` chunk extracted from `nxe-data-grid`'s
590
665
  * template (pivot parity Phase 3a) so the pivot can open the same popup on pages with no
591
666
  * data grid. Same DOM, same FROZEN classes (`.nxe-hf-item`/`.nxe-hf-search`/… — DEFAULTS.md):
592
- * the host element carries `dx-header-filter-menu nxe-hf-panel`, exactly where the old
593
- * template's root `<div>` sat inside the overlay pane.
667
+ * the host element carries `nxe-header-filter-menu nxe-hf-panel`, exactly where the old
668
+ * template's root `<div>` sat inside the overlay pane. The dx-parity pass only added to that DOM:
669
+ * the `input.nxe-hf-search` now sits in a `.nxe-hf-searchbox` with its magnifier and clear
670
+ * button, and rows also carry `.nxe-hf-list-item` / `.nxe-hf-tree-item`.
594
671
  *
595
672
  * Renders whatever {@link NxeHeaderFilterModel} it is given — checkbox list or the date
596
673
  * Year > Month > Day tree — and emits `applied`/`cancelled` for the host to commit/close;
@@ -605,7 +682,11 @@ declare class NxeHeaderFilterPanelComponent {
605
682
  readonly applied: _angular_core.OutputEmitterRef<void>;
606
683
  /** Cancel — the host closes without committing. */
607
684
  readonly cancelled: _angular_core.OutputEmitterRef<void>;
685
+ private readonly searchInput;
608
686
  protected showSearch(): boolean;
687
+ /** The search box's clear button (dx search mode forces `showClearButton`): empties the
688
+ search and keeps the caret in the box, as dx's clear does. */
689
+ protected clearSearch(): void;
609
690
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeHeaderFilterPanelComponent, never>;
610
691
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeHeaderFilterPanelComponent, "nxe-header-filter-panel", never, { "model": { "alias": "model"; "required": true; "isSignal": true; }; "searchEnabled": { "alias": "searchEnabled"; "required": false; "isSignal": true; }; }, { "applied": "applied"; "cancelled": "cancelled"; }, never, never, true, never>;
611
692
  }
@@ -704,21 +785,32 @@ declare class NxeSummaryComponent {
704
785
  /** `dxo-paging` — internal client paging. Presence enables paging; `[pageSize]`/`[pageIndex]` used. */
705
786
  declare class NxePagingComponent {
706
787
  readonly pageSize: _angular_core.InputSignal<number>;
707
- readonly pageIndex: _angular_core.InputSignal<number>;
788
+ /** Two-way: dx supports `[(pageIndex)]`, and a consumer drives the pager from outside with it. */
789
+ readonly pageIndex: _angular_core.ModelSignal<number>;
708
790
  readonly enabled: _angular_core.InputSignal<boolean>;
709
791
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxePagingComponent, never>;
710
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxePagingComponent, "nxe-paging", never, { "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "pageIndex": { "alias": "pageIndex"; "required": false; "isSignal": true; }; "enabled": { "alias": "enabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
792
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxePagingComponent, "nxe-paging", never, { "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "pageIndex": { "alias": "pageIndex"; "required": false; "isSignal": true; }; "enabled": { "alias": "enabled"; "required": false; "isSignal": true; }; }, { "pageIndex": "pageIndexChange"; }, never, never, true, never>;
711
793
  }
712
794
  /** `dxo-pager` — the pager UI config. */
713
795
  declare class NxePagerComponent {
796
+ /** `'auto'` shows the pager only when there is more than one page (dx `isVisible`). */
714
797
  readonly visible: _angular_core.InputSignal<boolean | "auto">;
798
+ /** `'auto'` offers half, the same and double the page size (dx `getPageSizes`). */
715
799
  readonly allowedPageSizes: _angular_core.InputSignal<number[] | "auto">;
716
800
  readonly showPageSizeSelector: _angular_core.InputSignal<boolean>;
717
801
  readonly showNavigationButtons: _angular_core.InputSignal<boolean>;
718
802
  readonly showInfo: _angular_core.InputSignal<boolean>;
803
+ /**
804
+ * `'full'` and `'adaptive'` list the page sizes as buttons; `'compact'` uses a drop-down and
805
+ * always shows the navigation buttons, as dx's small mode does.
806
+ */
719
807
  readonly displayMode: _angular_core.InputSignal<"adaptive" | "compact" | "full">;
808
+ /** dx `pager.infoText`: `{0}` the page, `{1}` the page count, `{2}` the item count. */
809
+ readonly infoText: _angular_core.InputSignal<string>;
810
+ /** dx `pager.label`: the pager's accessible name. */
811
+ readonly label: _angular_core.InputSignal<string>;
720
812
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxePagerComponent, never>;
721
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxePagerComponent, "nxe-pager", never, { "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "allowedPageSizes": { "alias": "allowedPageSizes"; "required": false; "isSignal": true; }; "showPageSizeSelector": { "alias": "showPageSizeSelector"; "required": false; "isSignal": true; }; "showNavigationButtons": { "alias": "showNavigationButtons"; "required": false; "isSignal": true; }; "showInfo": { "alias": "showInfo"; "required": false; "isSignal": true; }; "displayMode": { "alias": "displayMode"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
813
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxePagerComponent, "nxe-pager", never, { "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "allowedPageSizes": { "alias": "allowedPageSizes"; "required": false; "isSignal": true; }; "showPageSizeSelector": { "alias": "showPageSizeSelector"; "required": false; "isSignal": true; }; "showNavigationButtons": { "alias": "showNavigationButtons"; "required": false; "isSignal": true; }; "showInfo": { "alias": "showInfo"; "required": false; "isSignal": true; }; "displayMode": { "alias": "displayMode"; "required": false; "isSignal": true; }; "infoText": { "alias": "infoText"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
722
814
  }
723
815
 
724
816
  /** A tracked inline change (matches the app's local `Change<T>` shape). */
@@ -763,8 +855,8 @@ declare class NxeFilterRowComponent {
763
855
  * `nxe-master-detail` — drop-in for DevExtreme `dxo-master-detail` (orbit addition).
764
856
  *
765
857
  * A no-render config element read by the parent `nxe-data-grid`. When `enabled`, the grid
766
- * prepends a `dx-command-expand` column; expanding a row renders a
767
- * `<tr class="dx-master-detail-row">` hosting the named sibling
858
+ * prepends a `nxe-command-expand` column; expanding a row renders a
859
+ * `<tr class="nxe-master-detail-row">` hosting the named sibling
768
860
  * `<ng-template nxeTemplate="...">` with `{ data, rowIndex, key }` — instantiated ONLY while
769
861
  * expanded, preserving the app's lazy nested-grid loading (`onRowExpanding` fires first).
770
862
  */
@@ -833,6 +925,15 @@ interface NxeExportDataGridOptions {
833
925
  */
834
926
  declare function exportDataGrid(opts: NxeExportDataGridOptions): Promise<void>;
835
927
 
928
+ /** One entry of the page list: a page button, or dx's `. . .` separator. */
929
+ interface NxePagerItem {
930
+ /** Stable track key: the page index, or `low` / `high` for the separators. */
931
+ readonly key: string;
932
+ /** 0-based page index; -1 for a separator. */
933
+ readonly page: number;
934
+ readonly separator: boolean;
935
+ }
936
+
836
937
  /**
837
938
  * Pointer-based column-resize engine (dx `allowColumnResizing` parity).
838
939
  *
@@ -877,6 +978,11 @@ declare class NxeGridResizeController {
877
978
  private teardown;
878
979
  }
879
980
 
981
+ /**
982
+ * A column's effective type: declared, or inferred from its values. dx's inference can also land
983
+ * on 'object' (m_columns_controller_utils.js getValueDataType), which aligns and formats as text.
984
+ */
985
+ type NxeColType = NxeColumnDataType | 'object';
880
986
  /** dx filter-row operations (`selectedFilterOperation`). */
881
987
  type NxeFilterOp = 'contains' | 'notcontains' | 'startswith' | 'endswith' | '=' | '<>' | '<' | '<=' | '>' | '>=' | 'between';
882
988
  /** Flattened render rows when grouping or tree hierarchy is active. */
@@ -921,10 +1027,12 @@ interface NxeHeaderCell {
921
1027
  /**
922
1028
  * `nxe-data-grid` — drop-in replacement for DevExtreme `dx-data-grid` (read-only core, slice 9a).
923
1029
  *
924
- * Renders a single `<table>` emitting the dx DOM-class contract (`dx-widget dx-datagrid`,
925
- * `dx-row`/`dx-header-row`/`dx-data-row`/`dx-datagrid-headers`/`dx-datagrid-rowsview`, `td`s) so
926
- * the globally-loaded `dx.light.css` + repo `_grid.scss` overrides style it pixel-for-pixel
927
- * (4px cell padding, #f8fafc header, #525252 header text, row/hover/alt colours). Columns are
1030
+ * Renders a single `<table>` under the library's own class namespace (`nxe-widget nxe-datagrid`,
1031
+ * `nxe-row`/`nxe-header-row`/`nxe-data-row`/`nxe-datagrid-headers`/`nxe-datagrid-rowsview`, `td`s).
1032
+ * The structure still mirrors dx's, name for name, so the visual contract carries over
1033
+ * (4px cell padding, #f8fafc header, #525252 header text, row/hover/alt colours) — but the
1034
+ * `dx-` prefix is gone; `provideNxeDxCompat()` re-emits it for stylesheets not yet migrated.
1035
+ * Columns are
928
1036
  * collected from `nxe-column` content children; named `cellTemplate`s resolve to sibling
929
1037
  * `<ng-template nxeTemplate="name">` blocks rendered with `{ data, rowIndex, columnIndex, ... }`
930
1038
  * (the live row object, so in-cell `nxe-*` editors can write through `key.data.<field>`).
@@ -938,7 +1046,10 @@ declare class NxeDataGridComponent implements OnDestroy {
938
1046
  private readonly overlay;
939
1047
  private readonly viewContainerRef;
940
1048
  private readonly ngZone;
1049
+ private readonly destroyRef;
941
1050
  private readonly defaults;
1051
+ /** Locale separators for every number this grid renders — cells, summaries and group text. */
1052
+ private readonly separators;
942
1053
  /** Pointer drag engine: drag-to-group, chip drag-out (ungroup), chip/column reorder. */
943
1054
  protected readonly drag: NxeGridDragController;
944
1055
  /** Pointer resize engine (dx allowColumnResizing). */
@@ -998,6 +1109,9 @@ declare class NxeDataGridComponent implements OnDestroy {
998
1109
  */
999
1110
  readonly syncLookupFilterValues: _angular_core.InputSignal<boolean>;
1000
1111
  readonly columnMinWidth: _angular_core.InputSignal<number>;
1112
+ /** dx `height` — a bare number means px, anything else passes through. */
1113
+ readonly height: _angular_core.InputSignal<string | number>;
1114
+ protected readonly heightStyle: _angular_core.Signal<string>;
1001
1115
  readonly noDataTextInput: _angular_core.InputSignal<string>;
1002
1116
  readonly noDataText: _angular_core.Signal<string>;
1003
1117
  readonly loadPanel: _angular_core.InputSignal<{
@@ -1024,7 +1138,11 @@ declare class NxeDataGridComponent implements OnDestroy {
1024
1138
  visible?: boolean;
1025
1139
  }>;
1026
1140
  readonly scrolling: _angular_core.InputSignal<any>;
1027
- readonly sorting: _angular_core.InputSignal<{
1141
+ /**
1142
+ * dx `sorting`. Unbound means dx's default mode 'single'. `false` and `null` are accepted as dx
1143
+ * reads them - no mode, so headers keep their sort slot but a click never sorts.
1144
+ */
1145
+ readonly sorting: _angular_core.InputSignal<false | {
1028
1146
  mode?: "single" | "multiple" | "none";
1029
1147
  }>;
1030
1148
  readonly allowColumnResizing: _angular_core.InputSignal<boolean>;
@@ -1114,6 +1232,14 @@ declare class NxeDataGridComponent implements OnDestroy {
1114
1232
  protected readonly hfColumn: _angular_core.WritableSignal<NxeColumnComponent>;
1115
1233
  protected readonly hfDraft: _angular_core.WritableSignal<ReadonlySet<any>>;
1116
1234
  private hfOverlayRef?;
1235
+ /**
1236
+ * The current page.
1237
+ *
1238
+ * A `linkedSignal` over `nxe-paging`'s own `pageIndex` so the binding is genuinely two-way:
1239
+ * a host that sets `[(pageIndex)]` moves the grid, and `goToPage` writes back. Declaring the
1240
+ * input without this would satisfy an audit while doing nothing — the option would resolve
1241
+ * and be ignored, which is worse than not accepting it.
1242
+ */
1117
1243
  protected readonly pageIndex: _angular_core.WritableSignal<number>;
1118
1244
  private readonly userPageSize;
1119
1245
  /** DevExtreme `.instance` facade — `#gridAll.instance.<method>()`. */
@@ -1168,11 +1294,31 @@ declare class NxeDataGridComponent implements OnDestroy {
1168
1294
  protected readonly pagingEnabled: _angular_core.Signal<boolean>;
1169
1295
  protected readonly effectivePageSize: _angular_core.Signal<number>;
1170
1296
  protected readonly pageCount: _angular_core.Signal<number>;
1297
+ /**
1298
+ * The page shown: `pageIndex` held inside the page count. A filter can leave the bound index
1299
+ * past the last page; dx moves it back (m_data_source_adapter.js:454), and the rows, the
1300
+ * pager's selected button, its info text and prev/next all read this one value.
1301
+ */
1302
+ protected readonly currentPage: _angular_core.Signal<number>;
1171
1303
  /** The filtered/sorted rows visible on the current page (all rows when paging is off). */
1172
- private readonly pagedRows;
1304
+ protected readonly pagedRows: _angular_core.Signal<any[]>;
1305
+ /**
1306
+ * dx `PagerView.isVisible` (m_pager.js): `true` always, `'auto'` only while there is more than
1307
+ * one page to move between, and never under virtual or infinite scrolling.
1308
+ */
1173
1309
  protected readonly pagerVisible: _angular_core.Signal<boolean>;
1174
1310
  protected readonly pageSizes: _angular_core.Signal<number[]>;
1175
- protected readonly pageNumbers: _angular_core.Signal<number[]>;
1311
+ /** dx's small mode: a page-size drop-down, and the navigation buttons whatever the option says. */
1312
+ protected readonly pagerCompact: _angular_core.Signal<boolean>;
1313
+ /** dx `getRenderPrevButton` (pages/page_index_selector.js): `!isLargeDisplayMode || showNavigationButtons`. */
1314
+ protected readonly pagerNavVisible: _angular_core.Signal<boolean>;
1315
+ /**
1316
+ * The page buttons and `. . .` separators. A linkedSignal because dx's list keeps a sliding
1317
+ * window between renders (pages/large.js): the previous window decides whether the next one
1318
+ * moves.
1319
+ */
1320
+ private readonly pagerWindow;
1321
+ protected readonly pageItems: _angular_core.Signal<readonly NxePagerItem[]>;
1176
1322
  /** True when the host element is `<nxe-tree-list>` (vs `<nxe-data-grid>`). */
1177
1323
  protected readonly treeMode: boolean;
1178
1324
  private readonly expandOverride;
@@ -1182,6 +1328,15 @@ declare class NxeDataGridComponent implements OnDestroy {
1182
1328
  protected toggleTreeNode(key: any, event: Event): void;
1183
1329
  /** Flattened group + data render rows (tree hierarchy, then grouping, then flat). */
1184
1330
  protected readonly renderRows: _angular_core.Signal<GridRenderRow[]>;
1331
+ /**
1332
+ * The dataField a group summary renders under: dx `_calculateSummaryCells` (m_summary.js:443)
1333
+ * takes `showInColumn` when it names a column, else the item's own `column`.
1334
+ *
1335
+ * Keying footers on `column()` alone put an item that names only `showInColumn` (broker's
1336
+ * `name="totalSumInsured" showInColumn="totalSumInsured" summaryType="custom"`) under its
1337
+ * empty `column` - i.e. in whichever column happens to have no dataField.
1338
+ */
1339
+ private summaryTarget;
1185
1340
  /** Computes + formats a summary item over a set of rows. */
1186
1341
  private summaryText;
1187
1342
  /**
@@ -1196,6 +1351,7 @@ declare class NxeDataGridComponent implements OnDestroy {
1196
1351
  protected nextPage(): void;
1197
1352
  protected changePageSize(size: number): void;
1198
1353
  protected onPageSizeSelect(event: Event): void;
1354
+ /** dx's info text, `Page {0} of {1} ({2} items)` unless `infoText` says otherwise (pagination/info.js). */
1199
1355
  protected readonly pageInfo: _angular_core.Signal<string>;
1200
1356
  private static readonly fieldArrayEqual;
1201
1357
  /**
@@ -1269,8 +1425,16 @@ declare class NxeDataGridComponent implements OnDestroy {
1269
1425
  /** True when this header renders a resize handle on its right edge. */
1270
1426
  protected resizeHandleVisible(col: NxeColumnComponent, ci: number): boolean;
1271
1427
  protected startColumnResize(col: NxeColumnComponent, ci: number, event: PointerEvent): void;
1428
+ /** A visible column's position, for cells rendered outside the main `@for` index. */
1429
+ protected visibleIndexOf(col: NxeColumnComponent): number;
1272
1430
  /** Command columns preceding the data columns — the offset dx's 1-based aria-colindex counts from. */
1273
1431
  protected leadCellCount(): number;
1432
+ /** The master-detail expander's aria-colindex: straight after the group indents. */
1433
+ protected readonly expandColIndex: _angular_core.Signal<number>;
1434
+ /** The selection column's aria-colindex: after the indents and the expander. */
1435
+ protected readonly selectColIndex: _angular_core.Signal<number>;
1436
+ /** The row-edit command column's aria-colindex: after the last data column. */
1437
+ protected readonly editColIndex: _angular_core.Signal<number>;
1274
1438
  /** Resize commit: freeze all rendered widths (dx post-resize behavior) + emit width events. */
1275
1439
  private commitColumnResize;
1276
1440
  protected readonly filterRowVisible: _angular_core.Signal<boolean>;
@@ -1315,6 +1479,26 @@ declare class NxeDataGridComponent implements OnDestroy {
1315
1479
  protected isRowEditing(row: any): boolean;
1316
1480
  protected editableCell(col: NxeColumnComponent): boolean;
1317
1481
  protected onCellClick(row: any, col: NxeColumnComponent, event: Event): void;
1482
+ /**
1483
+ * dx `readOnly: !options.setValue` (m_editing.js:210): a grid checkbox is editable only where
1484
+ * dx gives the cell a setValue - cell mode on an editable column (m_editing.js:1832-1847). Row
1485
+ * mode's resting rows stay read-only; the row being edited renders the editor instead.
1486
+ */
1487
+ protected booleanReadOnly(col: NxeColumnComponent): boolean;
1488
+ /**
1489
+ * A boolean cell that is its own editor: cell mode, editable, and no editCellTemplate asking
1490
+ * to take over. The resting checkbox toggles in place, as dx's always-shown dxCheckBox does.
1491
+ */
1492
+ protected booleanInPlace(col: NxeColumnComponent): boolean;
1493
+ /** A resting boolean checkbox was clicked: toggle it when it is editable, else let it be. */
1494
+ protected onBooleanCellClick(row: any, col: NxeColumnComponent, event: Event): void;
1495
+ /**
1496
+ * The checkbox editor's state: the buffered value in the row being edited, else the row's.
1497
+ * Read as the native editor it replaces read it - any value but '' and 'false' is checked.
1498
+ */
1499
+ protected editorChecked(row: any, col: NxeColumnComponent): boolean;
1500
+ /** A click or Space on the checkbox editor. Space must not scroll the page or reach the grid. */
1501
+ protected toggleEditorCheck(row: any, col: NxeColumnComponent, mode: 'cell' | 'row', event: Event): void;
1318
1502
  /** dx cell-mode semantics: commit to the row on blur/Enter (client-side array write-through). */
1319
1503
  protected commitCellEdit(row: any, col: NxeColumnComponent, rawInput: any): void;
1320
1504
  /** Write a cell value through the grid's own path, leaving edit state alone (dx `setValue`). */
@@ -1367,14 +1551,138 @@ declare class NxeDataGridComponent implements OnDestroy {
1367
1551
  */
1368
1552
  protected readonly displayRows: _angular_core.Signal<any[]>;
1369
1553
  protected readonly loading: _angular_core.Signal<boolean>;
1554
+ /**
1555
+ * dx `isEmpty()` = `!items().length` (m_data_controller.js:1044-1046), toggled as `dx-empty`
1556
+ * on the rows view in `_renderCore` (m_rows_view.js:710) whether loading or not.
1557
+ *
1558
+ * Keyed on `renderRows` - what the `<tbody>` iterates, dx's `items()` - not `displayRows`. The
1559
+ * two agree for flat and grouped grids (`pagedRows` clamps the page), but a tree list builds
1560
+ * `renderRows` from the unfiltered source while `displayRows` applies the filters, so a tree
1561
+ * whose filter matches nothing still renders rows and must not also show an empty body.
1562
+ */
1563
+ protected readonly isEmpty: _angular_core.Signal<boolean>;
1564
+ /**
1565
+ * What the last reading of this grid's own layout found.
1566
+ *
1567
+ * - `bounded` is dx's `hasHeight`: the rows view is sized by the grid, not by its rows.
1568
+ * - `room` is dx's `elementHeightWithoutScrollbar - contentHeight` (m_rows_view.js:840-842): the
1569
+ * rows view's inner height less the table as it stands with the free-space row empty and the
1570
+ * last row keeping its line. Neither the row's height nor the last-row-border state changes
1571
+ * it, so what it decides cannot change the next reading.
1572
+ * - `fill` is dx's `resultHeight` (:844-848): the height that leaves the table 1px short of
1573
+ * the rows view (`_getHeightCorrection`, 1 in Chrome).
1574
+ * - `rowHeight` is dx's `_rowHeight`, the average rendered row. It sizes the gap that a short
1575
+ * last page leaves.
1576
+ */
1577
+ private readonly freeSpaceMeasure;
1578
+ /**
1579
+ * dx's three cases, in its order:
1580
+ * - no rows: the row shows at 0px, so the last row keeps no border to lose (:857-860);
1581
+ * - a bounded grid whose rows leave room: the row fills it, less the 1px dx subtracts in
1582
+ * Chrome (`_getHeightCorrection`), and column lines run to the bottom (:837-855);
1583
+ * - an unbounded grid on a short page of several: the row holds the missing rows' height, so
1584
+ * the grid keeps its height from page to page (:823-835).
1585
+ *
1586
+ * `visible` drives the last row's border in a bordered grid (`_updateLastRowBorder`, :931-937).
1587
+ */
1588
+ private readonly freeSpace;
1589
+ protected readonly freeSpaceHeight: _angular_core.Signal<number>;
1590
+ protected readonly freeSpaceVisible: _angular_core.Signal<boolean>;
1591
+ /**
1592
+ * One reading of the rows view, from the ResizeObserver that watches it and its table.
1593
+ *
1594
+ * dx hides the free-space row and then measures (m_rows_view.js:837-848). This reading cannot
1595
+ * hide it - that would shrink the document and could clamp its scroll position - so it takes
1596
+ * the row's part of the table back out arithmetically, from quantities that do not move with
1597
+ * what the reading decides:
1598
+ *
1599
+ * - The row's box comes out whole: the height it was given, or with none, its halves of the
1600
+ * collapsed borders above and below it (0.5px under a last row with a 1px frame).
1601
+ * - With no free space shown, a bordered grid's last row gives its line to the box
1602
+ * (`nxe-last-row-border`, `border-bottom-width:0`), which makes the table a line shorter.
1603
+ * That line is put back, at the grid's line width, so the answer is the same either way.
1604
+ *
1605
+ * Read naively off the table, either one feeds back: the half-line reads as room, which shows
1606
+ * the row, which gives the line back, which removes the room - a flip every frame - and a
1607
+ * filled row in a rows view that hugs its table reads as its own room, 1px less each time.
1608
+ *
1609
+ * The second case gets its own rule. A rows view that hugs its table while the row adds height
1610
+ * of its own is sized by that row, so it has no room, whatever `height` says: the row lets go
1611
+ * at once. (A bounded grid resized to exactly its table height less the 1px lands here too, and
1612
+ * fills again on the next reading.)
1613
+ *
1614
+ * Whether the grid is bounded (dx `hasHeight`): an explicit `height` other than 'auto' always
1615
+ * is (m_grid_view.js:597-604, `isHeightSpecified`), even a percentage that resolves to auto,
1616
+ * and then has no room while its rows view hugs. Otherwise a rows view that does not hug is
1617
+ * bounded by CSS, and one that hugs while the row holds free space is not, so a grid that
1618
+ * loses its height lets go of the space. When they coincide with no free space held (an exact
1619
+ * fit), the previous answer stands.
1620
+ */
1621
+ private measureFreeSpace;
1622
+ /**
1623
+ * dx `updateColumnDataTypes` (m_columns_controller.js:914-977): a column with no declared
1624
+ * `dataType` takes the type of its values, and mixed values make it 'string'.
1625
+ *
1626
+ * A lookup column is typed by its DISPLAY values - dx's `lookup.dataType`, which is what dx
1627
+ * aligns, formats and edits a lookup by (:901) - and a value with no lookup match contributes
1628
+ * nothing: dx's `lookup.calculateCellValue` returns `valueMap[value]`, undefined for a missing
1629
+ * key, so `getValueDataType` skips it (:946). A not-yet-loaded list must not type the column.
1630
+ *
1631
+ * Once inferred, a type sticks. dx infers only while `dataType` is unset (:935) and stops once
1632
+ * every column is typed (:289), so a later load of mixed values cannot flip a column; the
1633
+ * previous value of this linkedSignal carries that. The refresh tick is in the source because
1634
+ * rows mutated in place (inline editing, `refresh()`) can type a column that was all blanks.
1635
+ *
1636
+ * dx samples the loaded page (or the first group's leaves); this scans the client array,
1637
+ * which only differs when the types are mixed beyond that sample.
1638
+ */
1639
+ private readonly inferredTypes;
1640
+ /** A lookup's value → raw display value, first match winning as in `lookupText`; null without one. */
1641
+ private lookupDisplayMap;
1642
+ /**
1643
+ * The column's effective type - its declared `dataType`, else the inferred one - which is what
1644
+ * dx's `column.dataType` holds after inference. Every type-driven decision reads this, never
1645
+ * `col.dataType()` directly, or an undeclared column would behave as text in one place and
1646
+ * as a number in the next.
1647
+ */
1648
+ protected colType(col: NxeColumnComponent): NxeColType | undefined;
1370
1649
  /** The active sort: an explicit user click, else the first column declaring `sortOrder`. */
1371
1650
  private effectiveSort;
1651
+ /**
1652
+ * dx `getAlignmentByDataType` (m_columns_controller_utils.js) over the effective type:
1653
+ * number right, boolean centre, everything else left. A lookup aligns by its display text
1654
+ * (dx `lookup.dataType`, :901), which is text - so left, even over numeric codes.
1655
+ */
1656
+ /**
1657
+ * Columns whose cellTemplate draws a check box (nxe-check-box, mat-checkbox, a native
1658
+ * checkbox input or anything with role=checkbox). align() centres them like a boolean column,
1659
+ * header and cells, unless the column asks for center or right. dx centres only by dataType,
1660
+ * so a template check box over non-boolean data (or no dataField) would sit at the left there;
1661
+ * a check box reads as a boolean whatever feeds it, so here it is centred too.
1662
+ *
1663
+ * Sticky, as an inferred type is: a column that drew a check box keeps its alignment when a
1664
+ * filter or an empty page leaves no row to look at. A column that goes away drops out.
1665
+ */
1666
+ private readonly templateCheckboxColumns;
1667
+ private detectTemplateCheckboxColumns;
1668
+ /** Boolean data (declared or inferred) or a cellTemplate seen drawing a check box. */
1669
+ private isCheckboxColumn;
1372
1670
  protected align(col: NxeColumnComponent): string;
1671
+ /**
1672
+ * The filter cell's text-align: dx sets the filter editor's input to the column alignment
1673
+ * unless it is centre (m_filter_row.js:390-395).
1674
+ */
1675
+ protected filterCellAlign(col: NxeColumnComponent): string | null;
1373
1676
  protected colWidth(col: NxeColumnComponent): string | null;
1374
1677
  protected getRaw(row: any, dataField: string): any;
1678
+ /**
1679
+ * dx `showEditorAlways` (m_columns_controller.js:912): a boolean-typed column - declared or
1680
+ * inferred - renders a checkbox, unless it is a lookup or has its own cellTemplate. Decided
1681
+ * per column, not per value: a column whose values are mixed infers 'string' and shows text,
1682
+ * as in dx.
1683
+ */
1684
+ protected isBooleanCell(col: NxeColumnComponent): boolean;
1375
1685
  /** Resolve the displayed cell text: lookup > customizeText > dataType/format > raw string. */
1376
- /** dx parity: boolean values render as read-only checkboxes (dx dataType inference did this). */
1377
- protected isBooleanCell(col: NxeColumnComponent, row: any): boolean;
1378
1686
  protected cellText(col: NxeColumnComponent, row: any): string;
1379
1687
  /** Column `lookup` resolution (orbit addition): raw value → display text via the lookup list. */
1380
1688
  private lookupText;
@@ -1406,14 +1714,89 @@ declare class NxeDataGridComponent implements OnDestroy {
1406
1714
  * failure the `effect()` ban exists to prevent.
1407
1715
  */
1408
1716
  private emitPreparedEvents;
1717
+ /**
1718
+ * The single cell holding the grid's tab stop.
1719
+ *
1720
+ * A grid is one stop in the tab order, not one per cell — a 200-row table would otherwise be
1721
+ * 1,400 tab presses to get past. Row `-1` is the header, so ArrowUp from the first data row
1722
+ * reaches the column headers rather than stopping dead.
1723
+ */
1724
+ protected readonly focusedCell: _angular_core.WritableSignal<{
1725
+ row: number;
1726
+ col: number;
1727
+ }>;
1728
+ /**
1729
+ * The rows the keyboard and `aria-rowindex` address: the data rows actually RENDERED.
1730
+ *
1731
+ * Emphatically not `pagedRows()`. That is the page's data BEFORE grouping and tree collapse
1732
+ * take rows out of the DOM, while `renderRows` renumbers densely over what it emits - so with
1733
+ * one group collapsed `pagedRows()` counts three rows where two have a cell to focus. Every
1734
+ * consumer of the count is then wrong in a different way: `aria-rowcount` claims a row a
1735
+ * reader cannot reach, Ctrl+End targets an index with no DOM behind it and movement silently
1736
+ * does nothing, and Enter and Space read `pagedRows()[row]` - which is a DIFFERENT row than
1737
+ * the one under the cursor, so Space selects something the user is not looking at.
1738
+ */
1739
+ protected readonly navigableRows: _angular_core.Signal<any[]>;
1740
+ protected isCellTabStop(row: number, col: number): boolean;
1741
+ protected focusCell(row: number, col: number): void;
1742
+ /** dx marks the sorted column for assistive tech as well as with the arrow glyph. */
1743
+ protected ariaSort(col: NxeColumnComponent): 'ascending' | 'descending' | null;
1744
+ /**
1745
+ * The DOM cell at a logical position, addressed by `aria-colindex` rather than by offset.
1746
+ *
1747
+ * Offsets break twice over. A band cell carries `role="columnheader"` too, so counting
1748
+ * header cells finds bands as well as leaves; and `mergeColumns` deletes the covered `<td>`s,
1749
+ * so a data row can be short. `aria-colindex` is emitted on every cell and already includes
1750
+ * the lead command columns, so it names the position exactly rather than by counting.
1751
+ *
1752
+ * Both lookups stay inside this grid's own table: a master-detail grid renders a whole second
1753
+ * grid into a `<td>`, and its rows would otherwise answer to the outer grid's arrow keys.
1754
+ */
1755
+ private cellAt;
1756
+ /** Move the tab stop and put real DOM focus on the cell that now holds it. */
1757
+ private moveFocus;
1758
+ /**
1759
+ * Arrow-key movement over cells.
1760
+ *
1761
+ * Deliberately not bound to the rowsview: a projected editor inside a cell needs its own
1762
+ * arrow keys, so anything originating inside an editor is left alone.
1763
+ */
1764
+ protected onGridKeydown(event: KeyboardEvent): void;
1409
1765
  /** True when this header can start a drag gesture (to the group panel and/or a reorder). */
1410
1766
  protected headerDraggable(col: NxeColumnComponent): boolean;
1411
1767
  protected onHeaderPointerDown(col: NxeColumnComponent, ci: number, event: PointerEvent): void;
1412
1768
  protected onChipPointerDown(col: NxeColumnComponent, level: number, event: PointerEvent): void;
1413
- /** Chip click (dx): toggle the grouped column's sort — unless this click ends a drag. */
1769
+ /**
1770
+ * Chip click (dx): toggle the grouped column's sort — unless this click ends a drag. dx sends
1771
+ * it through the same changeSortOrder as a header click (m_sorting.js:134), so the same gate.
1772
+ */
1414
1773
  protected onChipClick(col: NxeColumnComponent): void;
1415
1774
  protected onHeaderClick(col: NxeColumnComponent): void;
1416
1775
  protected sortDir(col: NxeColumnComponent): 'asc' | 'desc' | null;
1776
+ /**
1777
+ * `sorting.mode` as dx's sorting code reads the option: unbound is dx's default 'single', an
1778
+ * object gives its `mode` (default 'single'), and anything else - `[sorting]="false"` or
1779
+ * `null` - has no mode at all.
1780
+ */
1781
+ private sortingMode;
1782
+ /**
1783
+ * dx changeSortOrder (m_columns_controller.js:758-766) acts only in mode 'single' or
1784
+ * 'multiple', and only on a column that allows sorting. `[sorting]="false"` still renders the
1785
+ * sort slot, but a header or chip click never sorts.
1786
+ */
1787
+ private clickSorts;
1788
+ /**
1789
+ * dx `isSortingAllowed` (m_sorting_mixin.js:37): a sortable column, unless the mode is
1790
+ * literally 'none'. A column with no dataField is never sortable (m_columns_controller.js:
1791
+ * 1383-1388). Gates the `nxe-datagrid-action` class, and the sort slot of an unsorted column.
1792
+ */
1793
+ protected sortAllowed(col: NxeColumnComponent): boolean;
1794
+ /**
1795
+ * Whether the header renders dx's sort span: always for the sorted column, and as an empty
1796
+ * `nxe-sort-none` placeholder for any other sortable one (m_sorting_mixin.js:38-46). The
1797
+ * placeholder is display:none in the fixed table and 14px wide in the best-fit pass.
1798
+ */
1799
+ protected sortSlot(col: NxeColumnComponent): boolean;
1417
1800
  protected onRowEnter(index: number): void;
1418
1801
  protected onRowLeave(): void;
1419
1802
  protected isHovered(index: number): boolean;
@@ -1439,7 +1822,12 @@ declare class NxeDataGridComponent implements OnDestroy {
1439
1822
  protected frOp(col: NxeColumnComponent): NxeFilterOp;
1440
1823
  protected frText(col: NxeColumnComponent): string;
1441
1824
  protected frText2(col: NxeColumnComponent): string;
1442
- protected frOpIconClass(col: NxeColumnComponent): string;
1825
+ /**
1826
+ * The chooser glyph: dx's magnifier ('default') until an operation is picked from the menu,
1827
+ * then that operation's own. Only the slug varies - the template emits `nxe-icon-filter-operation-*`
1828
+ * and nothing prefixed `dx-`, which the compat mirror would strip for want of an nxe twin.
1829
+ */
1830
+ protected frOpIconSlug(col: NxeColumnComponent): string;
1443
1831
  protected opIconSlug(op: NxeFilterOp): string;
1444
1832
  protected setFilterRowText(col: NxeColumnComponent, text: string, second?: boolean): void;
1445
1833
  protected openFilterOpMenu(col: NxeColumnComponent, event: Event): void;
@@ -1478,8 +1866,8 @@ declare class NxeDataGridComponent implements OnDestroy {
1478
1866
  private compare;
1479
1867
  ngOnDestroy(): void;
1480
1868
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeDataGridComponent, never>;
1481
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeDataGridComponent, "nxe-data-grid, nxe-tree-list", never, { "dataSource": { "alias": "dataSource"; "required": false; "isSignal": true; }; "keyExpr": { "alias": "keyExpr"; "required": false; "isSignal": true; }; "hoverStateEnabledInput": { "alias": "hoverStateEnabled"; "required": false; "isSignal": true; }; "showColumnLinesInput": { "alias": "showColumnLines"; "required": false; "isSignal": true; }; "showRowLinesInput": { "alias": "showRowLines"; "required": false; "isSignal": true; }; "showBordersInput": { "alias": "showBorders"; "required": false; "isSignal": true; }; "wordWrapEnabledInput": { "alias": "wordWrapEnabled"; "required": false; "isSignal": true; }; "columnAutoWidthInput": { "alias": "columnAutoWidth"; "required": false; "isSignal": true; }; "rowAlternationEnabledInput": { "alias": "rowAlternationEnabled"; "required": false; "isSignal": true; }; "remoteOperations": { "alias": "remoteOperations"; "required": false; "isSignal": true; }; "syncLookupFilterValues": { "alias": "syncLookupFilterValues"; "required": false; "isSignal": true; }; "columnMinWidth": { "alias": "columnMinWidth"; "required": false; "isSignal": true; }; "noDataTextInput": { "alias": "noDataText"; "required": false; "isSignal": true; }; "loadPanel": { "alias": "loadPanel"; "required": false; "isSignal": true; }; "paging": { "alias": "paging"; "required": false; "isSignal": true; }; "filterRow": { "alias": "filterRow"; "required": false; "isSignal": true; }; "headerFilterOptions": { "alias": "headerFilter"; "required": false; "isSignal": true; }; "groupingOptions": { "alias": "grouping"; "required": false; "isSignal": true; }; "groupPanelOptions": { "alias": "groupPanel"; "required": false; "isSignal": true; }; "scrolling": { "alias": "scrolling"; "required": false; "isSignal": true; }; "sorting": { "alias": "sorting"; "required": false; "isSignal": true; }; "allowColumnResizing": { "alias": "allowColumnResizing"; "required": false; "isSignal": true; }; "columnResizingMode": { "alias": "columnResizingMode"; "required": false; "isSignal": true; }; "allowColumnReordering": { "alias": "allowColumnReordering"; "required": false; "isSignal": true; }; "repaintChangesOnly": { "alias": "repaintChangesOnly"; "required": false; "isSignal": true; }; "showColumnHeaders": { "alias": "showColumnHeaders"; "required": false; "isSignal": true; }; "parentIdExpr": { "alias": "parentIdExpr"; "required": false; "isSignal": true; }; "dataStructure": { "alias": "dataStructure"; "required": false; "isSignal": true; }; "rootValue": { "alias": "rootValue"; "required": false; "isSignal": true; }; "autoExpandAll": { "alias": "autoExpandAll"; "required": false; "isSignal": true; }; "expandedRowKeys": { "alias": "expandedRowKeys"; "required": false; "isSignal": true; }; "selectedRowKeys": { "alias": "selectedRowKeys"; "required": false; "isSignal": true; }; "mergeColumns": { "alias": "mergeColumns"; "required": false; "isSignal": true; }; "rowClass": { "alias": "rowClass"; "required": false; "isSignal": true; }; }, { "expandedRowKeys": "expandedRowKeysChange"; "selectedRowKeys": "selectedRowKeysChange"; "onSelectionChanged": "onSelectionChanged"; "onRowClick": "onRowClick"; "onContentReady": "onContentReady"; "onOptionChanged": "onOptionChanged"; "onCellPrepared": "onCellPrepared"; "onRowPrepared": "onRowPrepared"; "onRowExpanding": "onRowExpanding"; "onRowExpanded": "onRowExpanded"; }, ["declaredColumns", "templates", "selection", "headerFilter", "grouping", "groupPanel", "summaryCmp", "pagingCmp", "pagerCmp", "editingCmp", "filterRowCmp", "masterDetail"], never, true, never>;
1869
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeDataGridComponent, "nxe-data-grid, nxe-tree-list", never, { "dataSource": { "alias": "dataSource"; "required": false; "isSignal": true; }; "keyExpr": { "alias": "keyExpr"; "required": false; "isSignal": true; }; "hoverStateEnabledInput": { "alias": "hoverStateEnabled"; "required": false; "isSignal": true; }; "showColumnLinesInput": { "alias": "showColumnLines"; "required": false; "isSignal": true; }; "showRowLinesInput": { "alias": "showRowLines"; "required": false; "isSignal": true; }; "showBordersInput": { "alias": "showBorders"; "required": false; "isSignal": true; }; "wordWrapEnabledInput": { "alias": "wordWrapEnabled"; "required": false; "isSignal": true; }; "columnAutoWidthInput": { "alias": "columnAutoWidth"; "required": false; "isSignal": true; }; "rowAlternationEnabledInput": { "alias": "rowAlternationEnabled"; "required": false; "isSignal": true; }; "remoteOperations": { "alias": "remoteOperations"; "required": false; "isSignal": true; }; "syncLookupFilterValues": { "alias": "syncLookupFilterValues"; "required": false; "isSignal": true; }; "columnMinWidth": { "alias": "columnMinWidth"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "noDataTextInput": { "alias": "noDataText"; "required": false; "isSignal": true; }; "loadPanel": { "alias": "loadPanel"; "required": false; "isSignal": true; }; "paging": { "alias": "paging"; "required": false; "isSignal": true; }; "filterRow": { "alias": "filterRow"; "required": false; "isSignal": true; }; "headerFilterOptions": { "alias": "headerFilter"; "required": false; "isSignal": true; }; "groupingOptions": { "alias": "grouping"; "required": false; "isSignal": true; }; "groupPanelOptions": { "alias": "groupPanel"; "required": false; "isSignal": true; }; "scrolling": { "alias": "scrolling"; "required": false; "isSignal": true; }; "sorting": { "alias": "sorting"; "required": false; "isSignal": true; }; "allowColumnResizing": { "alias": "allowColumnResizing"; "required": false; "isSignal": true; }; "columnResizingMode": { "alias": "columnResizingMode"; "required": false; "isSignal": true; }; "allowColumnReordering": { "alias": "allowColumnReordering"; "required": false; "isSignal": true; }; "repaintChangesOnly": { "alias": "repaintChangesOnly"; "required": false; "isSignal": true; }; "showColumnHeaders": { "alias": "showColumnHeaders"; "required": false; "isSignal": true; }; "parentIdExpr": { "alias": "parentIdExpr"; "required": false; "isSignal": true; }; "dataStructure": { "alias": "dataStructure"; "required": false; "isSignal": true; }; "rootValue": { "alias": "rootValue"; "required": false; "isSignal": true; }; "autoExpandAll": { "alias": "autoExpandAll"; "required": false; "isSignal": true; }; "expandedRowKeys": { "alias": "expandedRowKeys"; "required": false; "isSignal": true; }; "selectedRowKeys": { "alias": "selectedRowKeys"; "required": false; "isSignal": true; }; "mergeColumns": { "alias": "mergeColumns"; "required": false; "isSignal": true; }; "rowClass": { "alias": "rowClass"; "required": false; "isSignal": true; }; }, { "expandedRowKeys": "expandedRowKeysChange"; "selectedRowKeys": "selectedRowKeysChange"; "onSelectionChanged": "onSelectionChanged"; "onRowClick": "onRowClick"; "onContentReady": "onContentReady"; "onOptionChanged": "onOptionChanged"; "onCellPrepared": "onCellPrepared"; "onRowPrepared": "onRowPrepared"; "onRowExpanding": "onRowExpanding"; "onRowExpanded": "onRowExpanded"; }, ["declaredColumns", "templates", "selection", "headerFilter", "grouping", "groupPanel", "summaryCmp", "pagingCmp", "pagerCmp", "editingCmp", "filterRowCmp", "masterDetail"], never, true, never>;
1482
1870
  }
1483
1871
 
1484
- export { NXE_HF_BLANKS, NxeColumnComponent, NxeDataGridComponent, NxeDragGesture, NxeEditingComponent, NxeFilterRowComponent, NxeGestureClickSuppression, NxeGridDragController, NxeGroupItemComponent, NxeGroupPanelComponent, NxeGroupingComponent, NxeHeaderFilterComponent, NxeHeaderFilterModel, NxeHeaderFilterPanelComponent, NxeMasterDetailComponent, NxePagerComponent, NxePagingComponent, NxeScrollingComponent, NxeSelectionComponent, NxeSummaryComponent, NxeTotalItemComponent, exportDataGrid, exportNxeGridToExcel, nxeDatePattern, nxeDefaultFormat, nxeExcelNumFmt, nxeFormatNumberValue, nxeHfKey, nxeNumberMask, openNxeHeaderFilterOverlay };
1485
- export type { NxeCellInfo, NxeCellPreparedEvent, NxeColumnFormat, NxeColumnFormatObject, NxeCustomizeText, NxeDragGestureDelegate, NxeEditCellInfo, NxeExcelExportOptions, NxeExportColumn, NxeExportDataGridOptions, NxeFilterOp, NxeGridChange, NxeGridDragHooks, NxeGridInstance, NxeGridOptionChangedEvent, NxeGridSelectionChangedEvent, NxeGroupCellInfo, NxeHeaderCell, NxeHeaderFilterOverlayOptions, NxeHeaderFilterSource, NxeHfDateNode, NxeRowInfo, NxeRowPreparedEvent, NxeRowType, NxeSummaryItemLike, NxeTreeListNode };
1872
+ export { NXE_HF_BLANKS, NxeColumnComponent, NxeDataGridComponent, NxeDragGesture, NxeEditingComponent, NxeFilterRowComponent, NxeGestureClickSuppression, NxeGridDragController, NxeGroupItemComponent, NxeGroupPanelComponent, NxeGroupingComponent, NxeHeaderFilterComponent, NxeHeaderFilterModel, NxeHeaderFilterPanelComponent, NxeMasterDetailComponent, NxePagerComponent, NxePagingComponent, NxeScrollingComponent, NxeSelectionComponent, NxeSummaryComponent, NxeTotalItemComponent, exportDataGrid, exportNxeGridToExcel, nxeDatePattern, nxeDefaultFormat, nxeExcelNumFmt, nxeExcelNumberMask, nxeFormatNumberValue, nxeHfKey, nxeNumberMask, nxeNumberValue, nxeValueDataType, openNxeHeaderFilterOverlay };
1873
+ export type { NxeCellInfo, NxeCellPreparedEvent, NxeColumnDataType, NxeColumnFormat, NxeColumnFormatObject, NxeCustomizeText, NxeDragGestureDelegate, NxeEditCellInfo, NxeExcelExportOptions, NxeExportColumn, NxeExportDataGridOptions, NxeFilterOp, NxeGridChange, NxeGridDragHooks, NxeGridInstance, NxeGridOptionChangedEvent, NxeGridSelectionChangedEvent, NxeGroupCellInfo, NxeHeaderCell, NxeHeaderFilterOverlayOptions, NxeHeaderFilterSource, NxeHfDateNode, NxeRowInfo, NxeRowPreparedEvent, NxeRowType, NxeSummaryItemLike, NxeTreeListNode };