@nexterajs/ui 0.0.1 → 0.1.0

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 (63) hide show
  1. package/README.md +279 -34
  2. package/fesm2022/nexterajs-ui-charts.mjs +1096 -0
  3. package/fesm2022/nexterajs-ui-charts.mjs.map +1 -0
  4. package/fesm2022/nexterajs-ui-core.mjs +835 -11
  5. package/fesm2022/nexterajs-ui-core.mjs.map +1 -1
  6. package/fesm2022/nexterajs-ui-display.mjs +1142 -0
  7. package/fesm2022/nexterajs-ui-display.mjs.map +1 -0
  8. package/fesm2022/nexterajs-ui-dropdowns.mjs +621 -69
  9. package/fesm2022/nexterajs-ui-dropdowns.mjs.map +1 -1
  10. package/fesm2022/nexterajs-ui-file-uploader.mjs +576 -11
  11. package/fesm2022/nexterajs-ui-file-uploader.mjs.map +1 -1
  12. package/fesm2022/nexterajs-ui-forms.mjs +2702 -452
  13. package/fesm2022/nexterajs-ui-forms.mjs.map +1 -1
  14. package/fesm2022/nexterajs-ui-grid.mjs +2350 -336
  15. package/fesm2022/nexterajs-ui-grid.mjs.map +1 -1
  16. package/fesm2022/nexterajs-ui-intl.mjs +117 -15
  17. package/fesm2022/nexterajs-ui-intl.mjs.map +1 -1
  18. package/fesm2022/nexterajs-ui-navigation.mjs +1054 -0
  19. package/fesm2022/nexterajs-ui-navigation.mjs.map +1 -0
  20. package/fesm2022/nexterajs-ui-notify.mjs +353 -0
  21. package/fesm2022/nexterajs-ui-notify.mjs.map +1 -0
  22. package/fesm2022/nexterajs-ui-overlay.mjs +1671 -0
  23. package/fesm2022/nexterajs-ui-overlay.mjs.map +1 -0
  24. package/fesm2022/nexterajs-ui-pivot.mjs +191 -136
  25. package/fesm2022/nexterajs-ui-pivot.mjs.map +1 -1
  26. package/fesm2022/nexterajs-ui-shell.mjs +542 -0
  27. package/fesm2022/nexterajs-ui-shell.mjs.map +1 -0
  28. package/fesm2022/nexterajs-ui-styling.mjs +568 -3
  29. package/fesm2022/nexterajs-ui-styling.mjs.map +1 -1
  30. package/fesm2022/nexterajs-ui-tree-view.mjs +150 -18
  31. package/fesm2022/nexterajs-ui-tree-view.mjs.map +1 -1
  32. package/fesm2022/nexterajs-ui-validation.mjs +283 -97
  33. package/fesm2022/nexterajs-ui-validation.mjs.map +1 -1
  34. package/fesm2022/nexterajs-ui.mjs +6 -0
  35. package/fesm2022/nexterajs-ui.mjs.map +1 -1
  36. package/package.json +37 -2
  37. package/styles/_dropdown.scss +150 -1
  38. package/styles/_editor.scss +75 -21
  39. package/styles/_icons.scss +92 -0
  40. package/styles/_mixins.scss +104 -1
  41. package/styles/_scrollbar.scss +134 -0
  42. package/styles/_status.scss +21 -0
  43. package/styles/_toast.scss +243 -0
  44. package/styles/_tokens.scss +467 -2
  45. package/styles/_widget.scss +14 -3
  46. package/styles/index.scss +10 -4
  47. package/types/nexterajs-ui-charts.d.ts +446 -0
  48. package/types/nexterajs-ui-core.d.ts +515 -30
  49. package/types/nexterajs-ui-display.d.ts +720 -0
  50. package/types/nexterajs-ui-dropdowns.d.ts +318 -57
  51. package/types/nexterajs-ui-file-uploader.d.ts +240 -8
  52. package/types/nexterajs-ui-forms.d.ts +772 -43
  53. package/types/nexterajs-ui-grid.d.ts +878 -135
  54. package/types/nexterajs-ui-intl.d.ts +39 -4
  55. package/types/nexterajs-ui-navigation.d.ts +531 -0
  56. package/types/nexterajs-ui-notify.d.ts +149 -0
  57. package/types/nexterajs-ui-overlay.d.ts +703 -0
  58. package/types/nexterajs-ui-pivot.d.ts +91 -70
  59. package/types/nexterajs-ui-shell.d.ts +294 -0
  60. package/types/nexterajs-ui-styling.d.ts +163 -3
  61. package/types/nexterajs-ui-tree-view.d.ts +56 -4
  62. package/types/nexterajs-ui-validation.d.ts +110 -22
  63. package/types/nexterajs-ui.d.ts +6 -0
@@ -1,6 +1,9 @@
1
+ import { NxeNumberSeparators } from '@nexterajs/ui/intl';
1
2
  import * as _angular_core from '@angular/core';
2
- import { NgZone, ViewContainerRef, OnDestroy, EventEmitter, TemplateRef } from '@angular/core';
3
+ import { NgZone, ViewContainerRef, Signal, OnDestroy, InputSignal, EventEmitter, TemplateRef } from '@angular/core';
4
+ import { NxeExpr, NxeMessageService, NxeMessageKey } from '@nexterajs/ui/core';
3
5
  import { Overlay, OverlayRef } from '@angular/cdk/overlay';
6
+ import { Observable } from 'rxjs';
4
7
 
5
8
  /** A DevExtreme-style column `format`: a number-mask string, a date format string, or `{ type }`. */
6
9
  interface NxeColumnFormatObject {
@@ -10,6 +13,12 @@ interface NxeColumnFormatObject {
10
13
  formatter?: (value: any) => string;
11
14
  }
12
15
  type NxeColumnFormat = string | NxeColumnFormatObject;
16
+ /**
17
+ * A column's declared `dataType`. Leaving it unset is not the same as `'string'`: as in dx
18
+ * (m_columns_controller.js `updateColumnDataTypes`), an unset type is inferred from the loaded
19
+ * values, which is what right-aligns and formats a numeric column nobody typed.
20
+ */
21
+ type NxeColumnDataType = 'string' | 'number' | 'date' | 'boolean';
13
22
  /** Column `customizeText` callback — `(info) => string` (used by `formatAccounting`). */
14
23
  type NxeCustomizeText = (info: {
15
24
  value: any;
@@ -161,7 +170,8 @@ interface NxeGridSelectionChangedEvent<T = any> {
161
170
  interface NxeExportColumn {
162
171
  caption: string;
163
172
  dataField: string;
164
- dataType: 'string' | 'number' | 'date' | 'boolean';
173
+ /** The declared type, else the inferred one; `undefined` while nothing could be inferred. */
174
+ dataType: NxeColumnDataType | undefined;
165
175
  format: NxeColumnFormat | undefined;
166
176
  }
167
177
  /** dx TreeList node shape — the slice `exportTreeToExcel` walks (`data` + `children`). */
@@ -175,6 +185,13 @@ interface NxeTreeListNode<T = any> {
175
185
  interface NxeGridInstance<T = any> {
176
186
  getSelectedRowsData(): T[];
177
187
  getSelectedRowKeys(): T[];
188
+ /** dx `clearSelection()`: deselects every row, including rows the current filter hides. */
189
+ clearSelection(): void;
190
+ /**
191
+ * dx `deselectAll()`: deselects the rows the current filter shows (the current page of a
192
+ * server-side grid). A tree list deselects every node.
193
+ */
194
+ deselectAll(): void;
178
195
  refresh(): void;
179
196
  /**
180
197
  * dx `clearFilter()`: no argument clears the filter row AND header filters (including
@@ -227,20 +244,82 @@ declare function nxeNumberMask(format: NxeColumnFormat | undefined): string | un
227
244
  * `{ formatter }`). No format / an unresolvable one → `String(value)` (the grid's untyped
228
245
  * rendering).
229
246
  */
230
- declare function nxeFormatNumberValue(value: number, format: NxeColumnFormat | undefined): string;
247
+ declare function nxeFormatNumberValue(value: number, format: NxeColumnFormat | undefined, separators?: NxeNumberSeparators): string;
231
248
  /**
232
- * Derive the Excel `numFmt` mask for a `format`: string masks pass through, fixedPoint/currency
249
+ * Derive the Excel `numFmt` mask for a `format`: string masks go through
250
+ * {@link nxeExcelNumberMask} (the same conversion the grid export uses), fixedPoint/currency
233
251
  * come from {@link nxeNumberMask}, percent appends a bare `%` (Excel multiplies by 100 itself),
234
252
  * and any other `{ type }` is a date pattern used as-is. `{ formatter }`-only objects →
235
253
  * `undefined` (callers fall back to exporting the formatted text).
236
254
  */
237
255
  declare function nxeExcelNumFmt(format: NxeColumnFormat | undefined): string | undefined;
256
+ /**
257
+ * Excel `numFmt` for a dx LDML string mask.
258
+ *
259
+ * The two languages disagree on one character. In Excel an unquoted comma after the last digit
260
+ * placeholder SCALES the value by 1/1000, while dx's LDML engine ignores it (see the golden
261
+ * transcript in `@nexterajs/ui/intl`). The consumer percent mask `#,##0.00,'%'` therefore
262
+ * exported 100 as "0.10%". Dropped here, per `;` section: every unquoted comma after the
263
+ * section's decimal point, and a comma that follows a digit placeholder with no placeholder
264
+ * after it before the section's decimal point or end (`#,##0,'%'`, `#,##0,.00`) — none of them
265
+ * ever meant anything to dx. Grouping commas between integer placeholders stay, and
266
+ * single-quoted literals become Excel's double-quoted ones.
267
+ */
268
+ declare function nxeExcelNumberMask(mask: string): string;
269
+ /**
270
+ * dx `getValueDataType` (m_columns_controller_utils.js:230-236): the type a value contributes
271
+ * to column type inference. `null`, arrays and functions contribute nothing.
272
+ */
273
+ declare function nxeValueDataType(v: unknown): NxeColumnDataType | 'object' | undefined;
274
+ /**
275
+ * dx's number-column deserializer (m_columns_controller_utils.js:263 `updateSerializers`):
276
+ * `parseFloat`, so "20.5" is 20.5 and "1,234" is 1 exactly as dx shows it. `NaN` when the
277
+ * value is not numeric at all, which callers render raw.
278
+ */
279
+ declare function nxeNumberValue(raw: unknown): number;
238
280
  /**
239
281
  * 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.
282
+ * `dataType`/`format` → display text.
283
+ *
284
+ * dx's format_helper (format_helper.js:23-43) formats by the VALUE, not by the column type: any
285
+ * JS number meets any valid `format`, whatever the column declared. So a number in an untyped
286
+ * or string column still takes its mask, and a number column honours object formats as well.
242
287
  */
243
- declare function nxeDefaultFormat(dataType: 'string' | 'number' | 'date' | 'boolean' | undefined, format: NxeColumnFormat | undefined, raw: any): string;
288
+ declare function nxeDefaultFormat(dataType: NxeColumnDataType | 'object' | undefined, format: NxeColumnFormat | undefined, raw: any, separators?: NxeNumberSeparators, locale?: string): string;
289
+
290
+ /**
291
+ * `nxe-header-filter` — drop-in replacement for DevExtreme `dxo-header-filter` (grid child option).
292
+ *
293
+ * Adds a funnel icon to each filterable column header; clicking it opens a popup of the column's
294
+ * distinct values (checkbox list + optional search) to filter the grid. The app uses essentially
295
+ * one shape: `[search]="{ enabled: true }" [visible]="true"` (60 files). Legacy `[allowSearch]`
296
+ * (1×) folds into `search.enabled`.
297
+ */
298
+ declare class NxeHeaderFilterComponent {
299
+ readonly visible: _angular_core.InputSignal<boolean>;
300
+ readonly search: _angular_core.InputSignal<{
301
+ enabled?: boolean;
302
+ } | undefined>;
303
+ /** Legacy alias for `search.enabled` (one holdout). */
304
+ readonly allowSearch: _angular_core.InputSignal<boolean | undefined>;
305
+ /**
306
+ * dx `headerFilter.dataSource` — a fixed `{ text, value }` list shown instead of the values
307
+ * derived from the rows.
308
+ */
309
+ readonly dataSource: _angular_core.InputSignal<readonly {
310
+ text?: string;
311
+ value?: any;
312
+ }[] | undefined>;
313
+ /** Whether the in-popup search box shows.
314
+ *
315
+ * Defaults to **true**, matching the legacy DevExtreme deployment: a popup listing every
316
+ * distinct value in a column is close to unusable without one. 60 of the app's templates
317
+ * already opt in explicitly, so this only changes the handful that never said either way —
318
+ * and an explicit `[search]="{ enabled: false }"` still turns it off. */
319
+ searchEnabled(): boolean;
320
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeHeaderFilterComponent, never>;
321
+ 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>;
322
+ }
244
323
 
245
324
  /**
246
325
  * `nxe-column` — drop-in replacement for DevExtreme `dxi-column`.
@@ -254,17 +333,21 @@ declare class NxeColumnComponent {
254
333
  readonly caption: _angular_core.InputSignal<string>;
255
334
  /** Bound row field (supports dotted paths and bound/ternary expressions). */
256
335
  readonly dataField: _angular_core.InputSignal<string>;
257
- readonly dataType: _angular_core.InputSignal<"string" | "number" | "boolean" | "date">;
336
+ /**
337
+ * Unset = dx: inferred from the loaded data (m_columns_controller.js:914-975), so a numeric
338
+ * column nobody typed still right-aligns and honours its `format`. Declaring it wins.
339
+ */
340
+ readonly dataType: _angular_core.InputSignal<NxeColumnDataType | undefined>;
258
341
  /** px width — number (`[width]="150"`) or numeric string (`width="200"`). */
259
- readonly width: _angular_core.InputSignal<string | number>;
260
- readonly minWidth: _angular_core.InputSignal<string | number>;
261
- readonly alignment: _angular_core.InputSignal<"left" | "center" | "right">;
342
+ readonly width: _angular_core.InputSignal<string | number | undefined>;
343
+ readonly minWidth: _angular_core.InputSignal<string | number | undefined>;
344
+ readonly alignment: _angular_core.InputSignal<"left" | "center" | "right" | undefined>;
262
345
  /** Number-mask string (`#,##0.00`), date format string, or `{ type: 'dd MMM yyyy' }`. */
263
- readonly format: _angular_core.InputSignal<NxeColumnFormat>;
346
+ readonly format: _angular_core.InputSignal<NxeColumnFormat | undefined>;
264
347
  /** `(info:{value}) => string` (e.g. `formatAccounting` for parenthesized negatives). */
265
- readonly customizeText: _angular_core.InputSignal<NxeCustomizeText>;
348
+ readonly customizeText: _angular_core.InputSignal<NxeCustomizeText | undefined>;
266
349
  /** Name of a sibling `<ng-template nxeTemplate="...">` to render the cell. */
267
- readonly cellTemplate: _angular_core.InputSignal<string>;
350
+ readonly cellTemplate: _angular_core.InputSignal<string | undefined>;
268
351
  /**
269
352
  * DevExtreme column `lookup` (orbit addition): raw values resolve to display text through
270
353
  * `dataSource` (`{ lookup: { dataSource, valueExpr: 'lovCode', displayExpr: 'lovName' } }`).
@@ -272,9 +355,9 @@ declare class NxeColumnComponent {
272
355
  */
273
356
  readonly lookup: _angular_core.InputSignal<{
274
357
  dataSource: readonly any[];
275
- valueExpr?: string;
276
- displayExpr?: string;
277
- }>;
358
+ valueExpr?: NxeExpr;
359
+ displayExpr?: NxeExpr;
360
+ } | undefined>;
278
361
  readonly allowSorting: _angular_core.InputSignal<boolean>;
279
362
  /** dx parity: whether the header can be dragged into the group panel (default true). */
280
363
  readonly allowGrouping: _angular_core.InputSignal<boolean>;
@@ -286,18 +369,18 @@ declare class NxeColumnComponent {
286
369
  readonly allowSearch: _angular_core.InputSignal<boolean>;
287
370
  readonly allowHeaderFiltering: _angular_core.InputSignal<boolean>;
288
371
  readonly visible: _angular_core.InputSignal<boolean>;
289
- readonly sortOrder: _angular_core.InputSignal<"asc" | "desc">;
290
- readonly sortIndex: _angular_core.InputSignal<number>;
291
- readonly groupIndex: _angular_core.InputSignal<number>;
372
+ readonly sortOrder: _angular_core.InputSignal<"asc" | "desc" | undefined>;
373
+ readonly sortIndex: _angular_core.InputSignal<number | undefined>;
374
+ readonly groupIndex: _angular_core.InputSignal<number | undefined>;
292
375
  readonly fixed: _angular_core.InputSignal<boolean>;
293
- readonly filterValues: _angular_core.InputSignal<any[]>;
376
+ readonly filterValues: _angular_core.InputSignal<any[] | undefined>;
294
377
  /** Column `type` (only `type="date"` seen — a display hint, NOT a command column). */
295
- readonly type: _angular_core.InputSignal<string>;
378
+ readonly type: _angular_core.InputSignal<string | undefined>;
296
379
  /**
297
380
  * dx `cssClass` — extra classes on this column's header, filter and data cells. The escape
298
381
  * hatch for one-off cell styling that does not warrant a `cellTemplate`.
299
382
  */
300
- readonly cssClass: _angular_core.InputSignal<string>;
383
+ readonly cssClass: _angular_core.InputSignal<string | undefined>;
301
384
  /**
302
385
  * dx `editCellTemplate` — the sibling `<ng-template nxeTemplate="NAME">` rendered while this
303
386
  * cell is being edited, in place of the built-in editor. Its context is a
@@ -305,12 +388,12 @@ declare class NxeColumnComponent {
305
388
  * `ctx.data.field = $event` writes straight through, which is how dx's own edit templates
306
389
  * were used.
307
390
  */
308
- readonly editCellTemplate: _angular_core.InputSignal<string>;
391
+ readonly editCellTemplate: _angular_core.InputSignal<string | undefined>;
309
392
  /**
310
393
  * dx `groupCellTemplate` — replaces the `"Caption: value"` text in a group row's caption
311
394
  * cell when this column is the one grouped by.
312
395
  */
313
- readonly groupCellTemplate: _angular_core.InputSignal<string>;
396
+ readonly groupCellTemplate: _angular_core.InputSignal<string | undefined>;
314
397
  /**
315
398
  * Child columns declared inside this one — dx's banded header. Empty for an ordinary column.
316
399
  *
@@ -318,10 +401,15 @@ declare class NxeColumnComponent {
318
401
  * its children, and flattening them here would collapse the very nesting the band expresses.
319
402
  */
320
403
  readonly childColumns: _angular_core.Signal<readonly NxeColumnComponent[]>;
404
+ /**
405
+ * A `<nxe-header-filter>` declared inside this column, dx's per-column `headerFilter`
406
+ * config. Only its `dataSource` is read here; visibility and search stay grid-level.
407
+ */
408
+ readonly headerFilterConfig: _angular_core.Signal<NxeHeaderFilterComponent | undefined>;
321
409
  /** dx `isBand`: a column that groups other columns under a shared header instead of a field. */
322
410
  readonly isBand: _angular_core.Signal<boolean>;
323
411
  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>;
412
+ 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
413
  }
326
414
 
327
415
  /**
@@ -453,32 +541,6 @@ declare class NxeSelectionComponent {
453
541
  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
542
  }
455
543
 
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
544
  /**
483
545
  * Header-filter state model — the draft/apply/search machinery extracted from
484
546
  * `nxe-data-grid` (pivot parity Phase 3a) so the pivot's field panel can reuse it on pages
@@ -490,6 +552,8 @@ declare class NxeHeaderFilterComponent {
490
552
  * {@link NxeHeaderFilterSource} adapter, so it owns only the frozen semantics:
491
553
  * `(Blanks)` first, Yes/No boolean buckets, the date Year > Month > Day tree, dx search
492
554
  * scoping of (Select All), check-on-open, and "everything checked = no filter" on apply.
555
+ * (Select All)'s indeterminate state ({@link NxeHeaderFilterModel.someChecked}) is derived
556
+ * from the same draft and search scope, and changes none of these rules.
493
557
  */
494
558
  /**
495
559
  * Header-filter key for blank-ish values (`null`/`undefined`/`''`) — dx lists them as one
@@ -526,17 +590,39 @@ interface NxeHeaderFilterSource<C> {
526
590
  boolLabel(column: C, raw: any): 'Yes' | 'No' | null;
527
591
  /** Raw-value comparator (numbers numeric, dates by instant, else locale strings). */
528
592
  compare(a: any, b: any): number;
593
+ /**
594
+ * dx `headerFilter.dataSource` on a column: a fixed `{ text, value }` list replacing the
595
+ * values derived from the rows. Returning `null` (the default) keeps the derived list.
596
+ *
597
+ * A fixed list is used verbatim — declared order, no `(Blanks)` sentinel, and values that
598
+ * never occur in the data still listed. That is dx's behaviour and the point of supplying
599
+ * one: a boolean column whose rows are all `true` should still offer "No".
600
+ */
601
+ fixedItems?(column: C): {
602
+ key: any;
603
+ text: string;
604
+ }[] | null;
605
+ }
606
+ /** The words the popup writes itself; each a function so a host can read a signal inside it. */
607
+ interface NxeHeaderFilterLabels {
608
+ blanks(): string;
609
+ yes(): string;
610
+ no(): string;
611
+ /** Month name for a 1-based month number. */
612
+ month(month: number): string;
529
613
  }
530
614
  declare class NxeHeaderFilterModel<C = unknown> {
531
615
  private readonly source;
532
- private static readonly MONTH_NAMES;
616
+ private readonly labels;
533
617
  /** The column whose popup is open (`null` = closed). */
534
- readonly column: _angular_core.WritableSignal<C>;
618
+ readonly column: _angular_core.WritableSignal<C | null>;
535
619
  /** The checked key set being edited in the popup (committed by the host on OK). */
536
620
  readonly draft: _angular_core.WritableSignal<ReadonlySet<any>>;
537
621
  readonly search: _angular_core.WritableSignal<string>;
538
622
  readonly expanded: _angular_core.WritableSignal<ReadonlySet<string>>;
539
- constructor(source: NxeHeaderFilterSource<C>);
623
+ constructor(source: NxeHeaderFilterSource<C>, labels?: NxeHeaderFilterLabels);
624
+ /** The popup's wording for a boolean bucket. */
625
+ private boolText;
540
626
  /** This column's header-filter key for a raw value. */
541
627
  key(column: C, raw: any): any;
542
628
  /** Distinct {key, text} for the open column — `(Blanks)` first, then Yes/No or values ascending. */
@@ -567,6 +653,13 @@ declare class NxeHeaderFilterModel<C = unknown> {
567
653
  readonly visibleKeys: _angular_core.Signal<any[]>;
568
654
  /** (Select All) state/toggle operate on the VISIBLE keys (dx: search scopes Select All). */
569
655
  readonly allChecked: _angular_core.Signal<boolean>;
656
+ /**
657
+ * (Select All)'s third state: some visible keys checked, but not all. dx shows it as the
658
+ * indeterminate square (the select-all dxCheckBox's `value: undefined`). It is never true
659
+ * together with {@link allChecked}, so check-on-open, which checks everything when no filter
660
+ * is applied, always opens on a fully checked box and never on a half-checked one.
661
+ */
662
+ readonly someChecked: _angular_core.Signal<boolean>;
570
663
  /** Whether every key of the whole column is checked — decides "no filter" on apply. */
571
664
  readonly fullChecked: _angular_core.Signal<boolean>;
572
665
  /**
@@ -589,14 +682,18 @@ declare class NxeHeaderFilterModel<C = unknown> {
589
682
  * The header-filter popup panel — the `.nxe-hf-*` chunk extracted from `nxe-data-grid`'s
590
683
  * template (pivot parity Phase 3a) so the pivot can open the same popup on pages with no
591
684
  * 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.
685
+ * the host element carries `nxe-header-filter-menu nxe-hf-panel`, exactly where the old
686
+ * template's root `<div>` sat inside the overlay pane. The dx-parity pass only added to that DOM:
687
+ * the `input.nxe-hf-search` now sits in a `.nxe-hf-searchbox` with its magnifier and clear
688
+ * button, and rows also carry `.nxe-hf-list-item` / `.nxe-hf-tree-item`.
594
689
  *
595
690
  * Renders whatever {@link NxeHeaderFilterModel} it is given — checkbox list or the date
596
691
  * Year > Month > Day tree — and emits `applied`/`cancelled` for the host to commit/close;
597
692
  * the panel itself never touches the host's filter state.
598
693
  */
599
694
  declare class NxeHeaderFilterPanelComponent {
695
+ /** The library's runtime-switchable messages. */
696
+ protected readonly messages: NxeMessageService;
600
697
  readonly model: _angular_core.InputSignal<NxeHeaderFilterModel<any>>;
601
698
  /** Live predicate (re-read every CD, like the grid template did) — a mid-open config
602
699
  change to `search.enabled` still shows/hides the box. */
@@ -605,7 +702,11 @@ declare class NxeHeaderFilterPanelComponent {
605
702
  readonly applied: _angular_core.OutputEmitterRef<void>;
606
703
  /** Cancel — the host closes without committing. */
607
704
  readonly cancelled: _angular_core.OutputEmitterRef<void>;
705
+ private readonly searchInput;
608
706
  protected showSearch(): boolean;
707
+ /** The search box's clear button (dx search mode forces `showClearButton`): empties the
708
+ search and keeps the caret in the box, as dx's clear does. */
709
+ protected clearSearch(): void;
609
710
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeHeaderFilterPanelComponent, never>;
610
711
  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
712
  }
@@ -665,15 +766,15 @@ declare abstract class NxeSummaryItemBase implements NxeSummaryItemLike {
665
766
  readonly column: _angular_core.InputSignal<string>;
666
767
  readonly summaryType: _angular_core.InputSignal<"sum" | "count" | "min" | "max" | "avg" | "custom">;
667
768
  /** Identifier passed to `calculateCustomSummary` for `summaryType='custom'` items. */
668
- readonly name: _angular_core.InputSignal<string>;
769
+ readonly name: _angular_core.InputSignal<string | undefined>;
669
770
  /** Positional template; `{0}` is replaced by the customized/formatted value. */
670
771
  readonly displayFormat: _angular_core.InputSignal<string>;
671
- readonly customizeText: _angular_core.InputSignal<NxeCustomizeText>;
672
- readonly valueFormat: _angular_core.InputSignal<NxeColumnFormat>;
772
+ readonly customizeText: _angular_core.InputSignal<NxeCustomizeText | undefined>;
773
+ readonly valueFormat: _angular_core.InputSignal<NxeColumnFormat | undefined>;
673
774
  readonly alignByColumn: _angular_core.InputSignal<boolean>;
674
775
  /** Render this group aggregate in a footer row under the group instead of the group row. */
675
776
  readonly showInGroupFooter: _angular_core.InputSignal<boolean>;
676
- readonly showInColumn: _angular_core.InputSignal<string>;
777
+ readonly showInColumn: _angular_core.InputSignal<string | undefined>;
677
778
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeSummaryItemBase, never>;
678
779
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NxeSummaryItemBase, never, never, { "column": { "alias": "column"; "required": false; "isSignal": true; }; "summaryType": { "alias": "summaryType"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "displayFormat": { "alias": "displayFormat"; "required": false; "isSignal": true; }; "customizeText": { "alias": "customizeText"; "required": false; "isSignal": true; }; "valueFormat": { "alias": "valueFormat"; "required": false; "isSignal": true; }; "alignByColumn": { "alias": "alignByColumn"; "required": false; "isSignal": true; }; "showInGroupFooter": { "alias": "showInGroupFooter"; "required": false; "isSignal": true; }; "showInColumn": { "alias": "showInColumn"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
679
780
  }
@@ -694,7 +795,7 @@ declare class NxeSummaryComponent {
694
795
  * `{name, summaryProcess:'start'}`, then `'calculate'` once per row (`options.value` = the
695
796
  * row object), then `'finalize'`; the handler mutates `options.totalValue`.
696
797
  */
697
- readonly calculateCustomSummary: _angular_core.InputSignal<(options: any) => void>;
798
+ readonly calculateCustomSummary: _angular_core.InputSignal<((options: any) => void) | undefined>;
698
799
  readonly totalItems: _angular_core.Signal<readonly NxeTotalItemComponent[]>;
699
800
  readonly groupItems: _angular_core.Signal<readonly NxeGroupItemComponent[]>;
700
801
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeSummaryComponent, never>;
@@ -704,21 +805,32 @@ declare class NxeSummaryComponent {
704
805
  /** `dxo-paging` — internal client paging. Presence enables paging; `[pageSize]`/`[pageIndex]` used. */
705
806
  declare class NxePagingComponent {
706
807
  readonly pageSize: _angular_core.InputSignal<number>;
707
- readonly pageIndex: _angular_core.InputSignal<number>;
808
+ /** Two-way: dx supports `[(pageIndex)]`, and a consumer drives the pager from outside with it. */
809
+ readonly pageIndex: _angular_core.ModelSignal<number>;
708
810
  readonly enabled: _angular_core.InputSignal<boolean>;
709
811
  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>;
812
+ 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
813
  }
712
814
  /** `dxo-pager` — the pager UI config. */
713
815
  declare class NxePagerComponent {
816
+ /** `'auto'` shows the pager only when there is more than one page (dx `isVisible`). */
714
817
  readonly visible: _angular_core.InputSignal<boolean | "auto">;
818
+ /** `'auto'` offers half, the same and double the page size (dx `getPageSizes`). */
715
819
  readonly allowedPageSizes: _angular_core.InputSignal<number[] | "auto">;
716
820
  readonly showPageSizeSelector: _angular_core.InputSignal<boolean>;
717
821
  readonly showNavigationButtons: _angular_core.InputSignal<boolean>;
718
822
  readonly showInfo: _angular_core.InputSignal<boolean>;
823
+ /**
824
+ * `'full'` and `'adaptive'` list the page sizes as buttons; `'compact'` uses a drop-down and
825
+ * always shows the navigation buttons, as dx's small mode does.
826
+ */
719
827
  readonly displayMode: _angular_core.InputSignal<"adaptive" | "compact" | "full">;
828
+ /** dx `pager.infoText`: `{0}` the page, `{1}` the page count, `{2}` the item count. */
829
+ readonly infoText: _angular_core.InputSignal<string | undefined>;
830
+ /** dx `pager.label`: the pager's accessible name. */
831
+ readonly label: _angular_core.InputSignal<string | undefined>;
720
832
  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>;
833
+ 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
834
  }
723
835
 
724
836
  /** A tracked inline change (matches the app's local `Change<T>` shape). */
@@ -754,7 +866,7 @@ declare class NxeEditingComponent {
754
866
  declare class NxeFilterRowComponent {
755
867
  readonly visible: _angular_core.InputSignal<boolean>;
756
868
  /** DevExtreme parity (`auto`/`onClick`); the nxe filter row always applies on input. */
757
- readonly applyFilter: _angular_core.InputSignal<string>;
869
+ readonly applyFilter: _angular_core.InputSignal<string | undefined>;
758
870
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeFilterRowComponent, never>;
759
871
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeFilterRowComponent, "nxe-filter-row", never, { "visible": { "alias": "visible"; "required": false; "isSignal": true; }; "applyFilter": { "alias": "applyFilter"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
760
872
  }
@@ -763,8 +875,8 @@ declare class NxeFilterRowComponent {
763
875
  * `nxe-master-detail` — drop-in for DevExtreme `dxo-master-detail` (orbit addition).
764
876
  *
765
877
  * 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
878
+ * prepends a `nxe-command-expand` column; expanding a row renders a
879
+ * `<tr class="nxe-master-detail-row">` hosting the named sibling
768
880
  * `<ng-template nxeTemplate="...">` with `{ data, rowIndex, key }` — instantiated ONLY while
769
881
  * expanded, preserving the app's lazy nested-grid loading (`onRowExpanding` fires first).
770
882
  */
@@ -833,6 +945,228 @@ interface NxeExportDataGridOptions {
833
945
  */
834
946
  declare function exportDataGrid(opts: NxeExportDataGridOptions): Promise<void>;
835
947
 
948
+ /** One sort level: DevExtreme's `{ selector, desc }`. */
949
+ interface NxeSortDescriptor {
950
+ /** The column's `dataField`. */
951
+ selector: string;
952
+ desc: boolean;
953
+ }
954
+ /** Operators a filter-row or header-filter condition can carry (DevExtreme's filter operations). */
955
+ type NxeFilterOperator = '=' | '<>' | '<' | '<=' | '>' | '>=' | 'contains' | 'notcontains' | 'startswith' | 'endswith';
956
+ /**
957
+ * A DevExtreme-style filter expression, the shape the grid's filter row and header filter produce
958
+ * for a remote source:
959
+ *
960
+ * - a condition: `['name', 'contains', 'acme']`
961
+ * - a negation: `['!', expression]`
962
+ * - a group: `[expression, 'and', expression, 'or', expression]`, which may nest
963
+ *
964
+ * `between` is expanded the way DevExtreme expands it: `[[f, '>=', a], 'and', [f, '<=', b]]`.
965
+ * Values are JSON-safe: strings, numbers, booleans and `null`; dates are `'yyyy-MM-dd'` strings.
966
+ */
967
+ type NxeFilterExpression = readonly unknown[];
968
+ /** What the application (or the grid) states: the page, its size, the sort and the filter. */
969
+ interface NxeRemoteQuery {
970
+ /** 0-based page index. */
971
+ pageIndex: number;
972
+ pageSize: number;
973
+ sort: readonly NxeSortDescriptor[];
974
+ filter: NxeFilterExpression | null;
975
+ }
976
+ /**
977
+ * What `load` receives: the query plus the same page as `skip`/`take`, so an adapter can take
978
+ * whichever the endpoint speaks (`pageNo = pageIndex + 1`, or `offset = skip`).
979
+ */
980
+ interface NxeLoadOptions extends NxeRemoteQuery {
981
+ readonly skip: number;
982
+ readonly take: number;
983
+ /** Always `true`: the grid needs the total to draw its pager. DevExtreme `CustomStore` parity. */
984
+ readonly requireTotalCount: true;
985
+ }
986
+ interface NxeLoadResult<T = any> {
987
+ data: readonly T[];
988
+ /** Rows matching the filter across every page. */
989
+ totalCount: number;
990
+ }
991
+ interface NxeLoadContext {
992
+ /** Aborted when a newer request supersedes this one, or the source is cancelled or destroyed. */
993
+ readonly signal: AbortSignal;
994
+ }
995
+ /** The load function: a promise or an observable (an `HttpClient` call passes straight in). */
996
+ type NxeRemoteLoadFn<T = any> = (options: NxeLoadOptions, context: NxeLoadContext) => Promise<NxeLoadResult<T> | readonly T[]> | Observable<NxeLoadResult<T> | readonly T[]>;
997
+ interface NxeRemoteDataSourceConfig {
998
+ /** The row key field. The grid takes its `keyExpr` from here when none is bound. */
999
+ key?: string;
1000
+ /** The query the first load runs with. Anything left out is page 0, 20 rows, no sort, no filter. */
1001
+ initial?: Partial<NxeRemoteQuery>;
1002
+ /** Milliseconds a grid waits after the last filter-row keystroke before asking. Default 300. */
1003
+ filterDebounce?: number;
1004
+ }
1005
+ /** The default page size of a source (and of a remote grid with no `nxe-paging`). */
1006
+ declare const NXE_REMOTE_DEFAULT_PAGE_SIZE = 20;
1007
+ /** Structural equality of two queries; what keeps a re-stated, unchanged query from reloading. */
1008
+ declare function nxeSameQuery(a: NxeRemoteQuery, b: NxeRemoteQuery): boolean;
1009
+ /** Builds the load options for a query. */
1010
+ declare function nxeLoadOptions(query: NxeRemoteQuery): NxeLoadOptions;
1011
+ /**
1012
+ * One condition of a flattened filter, in the form a REST adapter wants. `in` is an OR of `=` on one
1013
+ * field (a header-filter selection), with all its values.
1014
+ */
1015
+ interface NxeFlatFilterCondition {
1016
+ field: string;
1017
+ operator: NxeFilterOperator | 'in';
1018
+ value: unknown;
1019
+ }
1020
+ /**
1021
+ * Flattens the AND-chain a grid builds into simple conditions, for endpoints that take a flat
1022
+ * parameter list (`status=sent,confirmed&supplierId=123`). An OR of `=` on one field becomes one
1023
+ * `in` condition. `exact` is `false` when part of the expression could not be flattened (a `!`, or
1024
+ * an OR across fields); those parts are left out of `conditions`, so an adapter that cannot take
1025
+ * `exact: false` should send the expression as JSON instead.
1026
+ */
1027
+ declare function nxeFlattenFilter(filter: NxeFilterExpression | null | undefined): {
1028
+ conditions: NxeFlatFilterCondition[];
1029
+ exact: boolean;
1030
+ };
1031
+ /** Combines expressions with AND; `null` and empty ones are dropped, a single one is returned as is. */
1032
+ declare function nxeFilterAnd(...parts: readonly (NxeFilterExpression | null | undefined)[]): NxeFilterExpression | null;
1033
+ /** Combines expressions with OR, as {@link nxeFilterAnd} does with AND. */
1034
+ declare function nxeFilterOr(...parts: readonly (NxeFilterExpression | null | undefined)[]): NxeFilterExpression | null;
1035
+ /**
1036
+ * A server-backed list for `nxe-data-grid` and `nxe-paginator`. It holds one page at a time:
1037
+ * the grid (or the app) states a query, the source asks the server for exactly that page, and the
1038
+ * result lands in signals.
1039
+ *
1040
+ * ```ts
1041
+ * readonly orders = new NxeRemoteDataSource<Order>(
1042
+ * (o, { signal }) => this.http.get<OrderList>('/api/orders', { params: toParams(o) }).pipe(map(toResult)),
1043
+ * { key: 'id' },
1044
+ * );
1045
+ * ```
1046
+ *
1047
+ * A request still in flight is aborted (its `AbortSignal` fires, or its observable is
1048
+ * unsubscribed) when a newer one starts, and a late response that slips past is ignored, so the
1049
+ * rows on screen always belong to the latest query.
1050
+ *
1051
+ * The source is lazy: nothing loads until {@link setOptions} or {@link reload} is called, which a
1052
+ * grid bound to it does on its own.
1053
+ */
1054
+ declare class NxeRemoteDataSource<T = any> {
1055
+ private readonly loadFn;
1056
+ /** The row key field, when the source was given one. */
1057
+ readonly key: string | undefined;
1058
+ /** Milliseconds a grid waits after the last filter-row keystroke before asking. */
1059
+ readonly filterDebounce: number;
1060
+ private readonly itemsState;
1061
+ private readonly totalCountState;
1062
+ private readonly loadingState;
1063
+ private readonly errorState;
1064
+ private readonly loadedState;
1065
+ private readonly queryState;
1066
+ private readonly optionsState;
1067
+ /** The rows of the current page. Kept (not cleared) while the next page loads. */
1068
+ readonly items: Signal<readonly T[]>;
1069
+ /** Rows matching the filter across every page, as the last successful response said. */
1070
+ readonly totalCount: Signal<number>;
1071
+ /** A request is in flight. */
1072
+ readonly loading: Signal<boolean>;
1073
+ /** The last request's failure, or `null` after a success or while a retry runs. */
1074
+ readonly error: Signal<unknown>;
1075
+ /** A response has arrived at least once. */
1076
+ readonly loaded: Signal<boolean>;
1077
+ /** The query and the `skip`/`take` of the page the next (or current) request uses. */
1078
+ readonly options: Signal<NxeLoadOptions>;
1079
+ private started;
1080
+ private seq;
1081
+ private controller;
1082
+ private unsubscribe;
1083
+ constructor(loadFn: NxeRemoteLoadFn<T>, config?: NxeRemoteDataSourceConfig);
1084
+ /**
1085
+ * States a new query (any of its parts) and loads it. Nothing happens when it is the query
1086
+ * already stated, so a caller can re-state its inputs freely. The first call starts the source.
1087
+ */
1088
+ setOptions(patch: Partial<NxeRemoteQuery>): void;
1089
+ /** Loads the current query again (after an edit, or as a retry). */
1090
+ reload(): void;
1091
+ /** Aborts the request in flight and ignores its response. */
1092
+ cancel(): void;
1093
+ private abort;
1094
+ private run;
1095
+ /** The last value an observable emits; rejects when it errors or completes empty. */
1096
+ private fromObservable;
1097
+ }
1098
+ /** A DevExtreme `CustomStore`-shaped source: what `remoteOperations` makes the grid load page by page. */
1099
+ interface NxeStoreLike<T = any> {
1100
+ load: NxeRemoteLoadFn<T>;
1101
+ key?: string;
1102
+ }
1103
+ /** True for an object with a `load` function that is not an array or a {@link NxeRemoteDataSource}. */
1104
+ declare function nxeIsStoreLike(value: unknown): value is NxeStoreLike;
1105
+
1106
+ /**
1107
+ * The pager's page list and info text, as DevExtreme 24.2.3's pagination builds them.
1108
+ *
1109
+ * Pure functions, so the grid holds the only state (the sliding window, carried between renders
1110
+ * by a linkedSignal) and the rules can be specced without rendering a grid.
1111
+ */
1112
+
1113
+ /** One entry of the page list: a page button, or dx's `. . .` separator. */
1114
+ interface NxePagerItem {
1115
+ /** Stable track key: the page index, or `low` / `high` for the separators. */
1116
+ readonly key: string;
1117
+ /** 0-based page index; -1 for a separator. */
1118
+ readonly page: number;
1119
+ readonly separator: boolean;
1120
+ }
1121
+
1122
+ /**
1123
+ * A page navigator that stands on its own: page buttons with the grid pager's sliding window,
1124
+ * previous/next, a page-size selector and the total. Use it beside any list, or bind it to the same
1125
+ * state a remote grid pages with.
1126
+ *
1127
+ * ```html
1128
+ * <nxe-paginator [(pageIndex)]="page" [(pageSize)]="size" [totalCount]="orders.totalCount()" />
1129
+ * ```
1130
+ *
1131
+ * Text comes from `NxeMessageService`, so it follows the language. The nav is a labelled
1132
+ * `navigation` landmark; the current page carries `aria-current="page"`. With focus on a page
1133
+ * button, Left/Right move to the previous/next page and Home/End to the first/last.
1134
+ */
1135
+ declare class NxePaginatorComponent {
1136
+ private readonly messages;
1137
+ private readonly separators;
1138
+ private readonly host;
1139
+ private readonly injector;
1140
+ /** The 0-based current page. Two-way. */
1141
+ readonly pageIndex: _angular_core.ModelSignal<number>;
1142
+ /** Rows per page. Two-way; changing it returns to the first page. */
1143
+ readonly pageSize: _angular_core.ModelSignal<number>;
1144
+ /** Rows across every page. */
1145
+ readonly totalCount: _angular_core.InputSignalWithTransform<number, unknown>;
1146
+ /** The sizes the selector offers. The current size is always offered too. */
1147
+ readonly pageSizes: _angular_core.InputSignal<readonly number[]>;
1148
+ readonly showPageSizeSelector: _angular_core.InputSignalWithTransform<boolean, unknown>;
1149
+ readonly showInfo: _angular_core.InputSignalWithTransform<boolean, unknown>;
1150
+ /** Greys the controls out and ignores clicks, e.g. while a page loads. */
1151
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
1152
+ /** The navigation landmark's accessible name; default is the `grid.pager.navigation` message. */
1153
+ readonly label: _angular_core.InputSignal<string | undefined>;
1154
+ protected t(key: NxeMessageKey, ...args: readonly unknown[]): string;
1155
+ protected readonly pageCount: _angular_core.Signal<number>;
1156
+ /** The page shown: `pageIndex` held inside the page count. */
1157
+ protected readonly currentPage: _angular_core.Signal<number>;
1158
+ private readonly window;
1159
+ protected readonly items: _angular_core.Signal<readonly NxePagerItem[]>;
1160
+ protected readonly sizes: _angular_core.Signal<number[]>;
1161
+ protected readonly info: _angular_core.Signal<string>;
1162
+ protected readonly total: _angular_core.Signal<string>;
1163
+ protected go(index: number): void;
1164
+ protected onSizeChange(event: Event): void;
1165
+ protected onKeydown(event: KeyboardEvent): void;
1166
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxePaginatorComponent, never>;
1167
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxePaginatorComponent, "nxe-paginator", never, { "pageIndex": { "alias": "pageIndex"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "totalCount": { "alias": "totalCount"; "required": false; "isSignal": true; }; "pageSizes": { "alias": "pageSizes"; "required": false; "isSignal": true; }; "showPageSizeSelector": { "alias": "showPageSizeSelector"; "required": false; "isSignal": true; }; "showInfo": { "alias": "showInfo"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, { "pageIndex": "pageIndexChange"; "pageSize": "pageSizeChange"; }, never, never, true, never>;
1168
+ }
1169
+
836
1170
  /**
837
1171
  * Pointer-based column-resize engine (dx `allowColumnResizing` parity).
838
1172
  *
@@ -846,6 +1180,8 @@ interface NxeGridResizeHooks {
846
1180
  zone: NgZone;
847
1181
  host: () => HTMLElement;
848
1182
  mode: () => 'nextColumn' | 'widget';
1183
+ /** Widget mode only: the trailing column takes what a narrowed table leaves of the rows view. */
1184
+ fillsGrid: () => boolean;
849
1185
  minWidth: () => number;
850
1186
  /** Drag started — the grid pauses its auto-width engine. */
851
1187
  onStart: () => void;
@@ -873,10 +1209,16 @@ declare class NxeGridResizeController {
873
1209
  destroy(): void;
874
1210
  private pointerMove;
875
1211
  private pointerUp;
1212
+ private restore;
876
1213
  private cancel;
877
1214
  private teardown;
878
1215
  }
879
1216
 
1217
+ /**
1218
+ * A column's effective type: declared, or inferred from its values. dx's inference can also land
1219
+ * on 'object' (m_columns_controller_utils.js getValueDataType), which aligns and formats as text.
1220
+ */
1221
+ type NxeColType = NxeColumnDataType | 'object';
880
1222
  /** dx filter-row operations (`selectedFilterOperation`). */
881
1223
  type NxeFilterOp = 'contains' | 'notcontains' | 'startswith' | 'endswith' | '=' | '<>' | '<' | '<=' | '>' | '>=' | 'between';
882
1224
  /** Flattened render rows when grouping or tree hierarchy is active. */
@@ -921,10 +1263,12 @@ interface NxeHeaderCell {
921
1263
  /**
922
1264
  * `nxe-data-grid` — drop-in replacement for DevExtreme `dx-data-grid` (read-only core, slice 9a).
923
1265
  *
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
1266
+ * Renders a single `<table>` under the library's own class namespace (`nxe-widget nxe-datagrid`,
1267
+ * `nxe-row`/`nxe-header-row`/`nxe-data-row`/`nxe-datagrid-headers`/`nxe-datagrid-rowsview`, `td`s).
1268
+ * The structure still mirrors dx's, name for name, so the visual contract carries over
1269
+ * (4px cell padding, #f8fafc header, #525252 header text, row/hover/alt colours) — but the
1270
+ * `dx-` prefix is gone; `provideNxeDxCompat()` re-emits it for stylesheets not yet migrated.
1271
+ * Columns are
928
1272
  * collected from `nxe-column` content children; named `cellTemplate`s resolve to sibling
929
1273
  * `<ng-template nxeTemplate="name">` blocks rendered with `{ data, rowIndex, columnIndex, ... }`
930
1274
  * (the live row object, so in-cell `nxe-*` editors can write through `key.data.<field>`).
@@ -938,7 +1282,13 @@ declare class NxeDataGridComponent implements OnDestroy {
938
1282
  private readonly overlay;
939
1283
  private readonly viewContainerRef;
940
1284
  private readonly ngZone;
1285
+ private readonly destroyRef;
941
1286
  private readonly defaults;
1287
+ /** Locale separators for every number this grid renders — cells, summaries and group text. */
1288
+ private readonly separators;
1289
+ private readonly messages;
1290
+ /** One library message for the active language; reading it in a template re-renders on a switch. */
1291
+ protected t(key: NxeMessageKey, ...args: readonly unknown[]): string;
942
1292
  /** Pointer drag engine: drag-to-group, chip drag-out (ungroup), chip/column reorder. */
943
1293
  protected readonly drag: NxeGridDragController;
944
1294
  /** Pointer resize engine (dx allowColumnResizing). */
@@ -960,35 +1310,70 @@ declare class NxeDataGridComponent implements OnDestroy {
960
1310
  /** True when any declared column is a band, i.e. the header needs more than one row. */
961
1311
  protected readonly hasBands: _angular_core.Signal<boolean>;
962
1312
  private readonly templates;
963
- protected readonly selection: _angular_core.Signal<NxeSelectionComponent>;
964
- protected readonly headerFilter: _angular_core.Signal<NxeHeaderFilterComponent>;
965
- protected readonly grouping: _angular_core.Signal<NxeGroupingComponent>;
966
- protected readonly groupPanel: _angular_core.Signal<NxeGroupPanelComponent>;
967
- protected readonly summaryCmp: _angular_core.Signal<NxeSummaryComponent>;
968
- protected readonly pagingCmp: _angular_core.Signal<NxePagingComponent>;
969
- protected readonly pagerCmp: _angular_core.Signal<NxePagerComponent>;
970
- protected readonly editingCmp: _angular_core.Signal<NxeEditingComponent>;
971
- protected readonly filterRowCmp: _angular_core.Signal<NxeFilterRowComponent>;
972
- protected readonly masterDetail: _angular_core.Signal<NxeMasterDetailComponent>;
973
- readonly dataSource: _angular_core.InputSignal<readonly any[]>;
974
- readonly keyExpr: _angular_core.InputSignal<string>;
975
- readonly hoverStateEnabledInput: _angular_core.InputSignal<boolean>;
976
- readonly showColumnLinesInput: _angular_core.InputSignal<boolean>;
977
- readonly showRowLinesInput: _angular_core.InputSignal<boolean>;
978
- readonly showBordersInput: _angular_core.InputSignal<boolean>;
979
- readonly wordWrapEnabledInput: _angular_core.InputSignal<boolean>;
980
- readonly columnAutoWidthInput: _angular_core.InputSignal<boolean>;
981
- readonly rowAlternationEnabledInput: _angular_core.InputSignal<boolean>;
1313
+ protected readonly selection: _angular_core.Signal<NxeSelectionComponent | undefined>;
1314
+ protected readonly headerFilter: _angular_core.Signal<NxeHeaderFilterComponent | undefined>;
1315
+ protected readonly grouping: _angular_core.Signal<NxeGroupingComponent | undefined>;
1316
+ protected readonly groupPanel: _angular_core.Signal<NxeGroupPanelComponent | undefined>;
1317
+ protected readonly summaryCmp: _angular_core.Signal<NxeSummaryComponent | undefined>;
1318
+ protected readonly pagingCmp: _angular_core.Signal<NxePagingComponent | undefined>;
1319
+ protected readonly pagerCmp: _angular_core.Signal<NxePagerComponent | undefined>;
1320
+ protected readonly editingCmp: _angular_core.Signal<NxeEditingComponent | undefined>;
1321
+ protected readonly filterRowCmp: _angular_core.Signal<NxeFilterRowComponent | undefined>;
1322
+ protected readonly masterDetail: _angular_core.Signal<NxeMasterDetailComponent | undefined>;
1323
+ /**
1324
+ * The rows: a plain array (paged, sorted and filtered here), a {@link NxeRemoteDataSource}
1325
+ * (paged, sorted and filtered by the server; the grid holds one page), or, with
1326
+ * `remoteOperations` on, a `CustomStore`-shaped `{ load, key? }`.
1327
+ */
1328
+ readonly dataSource: InputSignal<readonly any[] | NxeStoreLike<any> | NxeRemoteDataSource<any> | null | undefined>;
1329
+ readonly keyExpr: InputSignal<string | undefined>;
1330
+ readonly hoverStateEnabledInput: InputSignal<boolean | undefined>;
1331
+ readonly showColumnLinesInput: InputSignal<boolean | undefined>;
1332
+ readonly showRowLinesInput: InputSignal<boolean | undefined>;
1333
+ readonly showBordersInput: InputSignal<boolean | undefined>;
1334
+ readonly wordWrapEnabledInput: InputSignal<boolean | undefined>;
1335
+ readonly columnAutoWidthInput: InputSignal<boolean | undefined>;
1336
+ readonly rowAlternationEnabledInput: InputSignal<boolean | undefined>;
1337
+ readonly resizeFillsGridInput: InputSignal<boolean | undefined>;
982
1338
  /** dx resolves `nxe-tree-list` against the `treeList` slice, not `dataGrid`. */
983
1339
  private readonly widgetDefaults;
984
1340
  readonly hoverStateEnabled: _angular_core.Signal<boolean>;
1341
+ /**
1342
+ * Widget-mode resizing that never leaves a blank strip: the trailing column takes whatever a
1343
+ * narrowed layout leaves of the grid's width. Not dx - dx lets the table end short - so off
1344
+ * unless bound or set app-wide through `NXE_DEFAULT_OPTIONS`.
1345
+ */
1346
+ readonly resizeFillsGrid: _angular_core.Signal<boolean>;
985
1347
  readonly showColumnLines: _angular_core.Signal<boolean>;
986
1348
  readonly showRowLines: _angular_core.Signal<boolean>;
987
1349
  readonly showBorders: _angular_core.Signal<boolean>;
988
1350
  readonly wordWrapEnabled: _angular_core.Signal<boolean>;
989
1351
  readonly columnAutoWidth: _angular_core.Signal<boolean>;
990
1352
  readonly rowAlternationEnabled: _angular_core.Signal<boolean>;
991
- readonly remoteOperations: _angular_core.InputSignal<any>;
1353
+ /**
1354
+ * dx `remoteOperations`. With a `{ load }` store as the `dataSource`, any truthy value makes the
1355
+ * grid page, sort and filter on the server (`{ paging, sorting, filtering }` is read as all or
1356
+ * nothing). A {@link NxeRemoteDataSource} is remote whatever this says.
1357
+ */
1358
+ readonly remoteOperations: InputSignal<any>;
1359
+ /** The 0-based page. Two-way. */
1360
+ readonly pageIndexModel: _angular_core.ModelSignal<number | undefined>;
1361
+ /** Rows per page. Two-way. */
1362
+ readonly pageSizeModel: _angular_core.ModelSignal<number | undefined>;
1363
+ /** The sort, `[{ selector, desc }]`. Two-way; a header click writes its single level here. */
1364
+ readonly sortModel: _angular_core.ModelSignal<readonly NxeSortDescriptor[] | undefined>;
1365
+ /**
1366
+ * A filter the application owns (a filter bar, a status tab), in {@link NxeFilterExpression}
1367
+ * form. A remote grid ANDs it with the filter row and header filter and sends the lot to the
1368
+ * server. Changing it returns the grid to the first page.
1369
+ */
1370
+ readonly filter: InputSignal<NxeFilterExpression | null | undefined>;
1371
+ /**
1372
+ * The options of every request a remote grid hands its source: page, size, sort and the
1373
+ * combined filter. One emission per distinct request, after filter-row typing settles. This is
1374
+ * where an application mirrors the grid's state into the URL.
1375
+ */
1376
+ readonly optionsChange: _angular_core.OutputEmitterRef<NxeLoadOptions>;
992
1377
  /**
993
1378
  * dx `syncLookupFilterValues` — accepted, and deliberately inert. It asks dx to narrow a
994
1379
  * lookup column's header-filter list to the values still reachable under the other filters.
@@ -996,48 +1381,55 @@ declare class NxeDataGridComponent implements OnDestroy {
996
1381
  * already what it does; `true` is the option we do not honour.
997
1382
  * @nxeInert grid — parity/data-grid.yaml "syncLookupFilterValues".
998
1383
  */
999
- readonly syncLookupFilterValues: _angular_core.InputSignal<boolean>;
1000
- readonly columnMinWidth: _angular_core.InputSignal<number>;
1001
- readonly noDataTextInput: _angular_core.InputSignal<string>;
1384
+ readonly syncLookupFilterValues: InputSignal<boolean>;
1385
+ readonly columnMinWidth: InputSignal<number | undefined>;
1386
+ /** dx `height` — a bare number means px, anything else passes through. */
1387
+ readonly height: InputSignal<string | number | undefined>;
1388
+ protected readonly heightStyle: _angular_core.Signal<string | null>;
1389
+ readonly noDataTextInput: InputSignal<string | undefined>;
1002
1390
  readonly noDataText: _angular_core.Signal<string>;
1003
- readonly loadPanel: _angular_core.InputSignal<{
1391
+ readonly loadPanel: InputSignal<{
1004
1392
  enabled?: boolean;
1005
- }>;
1006
- readonly paging: _angular_core.InputSignal<{
1393
+ } | undefined>;
1394
+ readonly paging: InputSignal<{
1007
1395
  enabled?: boolean;
1008
1396
  pageSize?: number;
1009
- }>;
1010
- readonly filterRow: _angular_core.InputSignal<{
1397
+ } | undefined>;
1398
+ readonly filterRow: InputSignal<{
1011
1399
  visible?: boolean;
1012
- }>;
1400
+ } | undefined>;
1013
1401
  /** Object-input forms of the config elements (dx accepts both; orbit uses both styles). */
1014
- readonly headerFilterOptions: _angular_core.InputSignal<{
1402
+ readonly headerFilterOptions: InputSignal<{
1015
1403
  visible?: boolean;
1016
1404
  search?: {
1017
1405
  enabled?: boolean;
1018
1406
  };
1019
- }>;
1020
- readonly groupingOptions: _angular_core.InputSignal<{
1407
+ } | undefined>;
1408
+ readonly groupingOptions: InputSignal<{
1021
1409
  autoExpandAll?: boolean;
1022
- }>;
1023
- readonly groupPanelOptions: _angular_core.InputSignal<{
1410
+ } | undefined>;
1411
+ readonly groupPanelOptions: InputSignal<{
1024
1412
  visible?: boolean;
1025
- }>;
1026
- readonly scrolling: _angular_core.InputSignal<any>;
1027
- readonly sorting: _angular_core.InputSignal<{
1413
+ } | undefined>;
1414
+ readonly scrolling: InputSignal<any>;
1415
+ /**
1416
+ * dx `sorting`. Unbound means dx's default mode 'single'. `false` and `null` are accepted as dx
1417
+ * reads them - no mode, so headers keep their sort slot but a click never sorts.
1418
+ */
1419
+ readonly sorting: InputSignal<false | {
1028
1420
  mode?: "single" | "multiple" | "none";
1029
- }>;
1030
- readonly allowColumnResizing: _angular_core.InputSignal<boolean>;
1421
+ } | null | undefined>;
1422
+ readonly allowColumnResizing: InputSignal<boolean>;
1031
1423
  /** dx columnResizingMode: 'nextColumn' (default) trades width; 'widget' grows the table. */
1032
- readonly columnResizingMode: _angular_core.InputSignal<"nextColumn" | "widget">;
1424
+ readonly columnResizingMode: InputSignal<"nextColumn" | "widget">;
1033
1425
  /** dx allowColumnReordering (default false — matches every dx-era template in this app). */
1034
- readonly allowColumnReordering: _angular_core.InputSignal<boolean>;
1035
- readonly repaintChangesOnly: _angular_core.InputSignal<boolean>;
1036
- readonly showColumnHeaders: _angular_core.InputSignal<boolean>;
1037
- readonly parentIdExpr: _angular_core.InputSignal<string>;
1038
- readonly dataStructure: _angular_core.InputSignal<"plain" | "tree">;
1039
- readonly rootValue: _angular_core.InputSignal<unknown>;
1040
- readonly autoExpandAll: _angular_core.InputSignal<boolean>;
1426
+ readonly allowColumnReordering: InputSignal<boolean>;
1427
+ readonly repaintChangesOnly: InputSignal<boolean>;
1428
+ readonly showColumnHeaders: InputSignal<boolean>;
1429
+ readonly parentIdExpr: InputSignal<string | undefined>;
1430
+ readonly dataStructure: InputSignal<"plain" | "tree">;
1431
+ readonly rootValue: InputSignal<unknown>;
1432
+ readonly autoExpandAll: InputSignal<boolean>;
1041
1433
  readonly expandedRowKeys: _angular_core.ModelSignal<any[]>;
1042
1434
  /** Tree-list multi-select keys (`[(selectedRowKeys)]`); recursive via dxo-selection. */
1043
1435
  readonly selectedRowKeys: _angular_core.ModelSignal<any[]>;
@@ -1046,7 +1438,7 @@ declare class NxeDataGridComponent implements OnDestroy {
1046
1438
  * dataFields merged hierarchically left→right — consecutive rows with equal values render one
1047
1439
  * cell with `rowspan`; a break in an earlier field breaks all later fields.
1048
1440
  */
1049
- readonly mergeColumns: _angular_core.InputSignal<readonly string[]>;
1441
+ readonly mergeColumns: InputSignal<readonly string[]>;
1050
1442
  readonly onSelectionChanged: _angular_core.OutputEmitterRef<NxeGridSelectionChangedEvent<any>>;
1051
1443
  readonly onRowClick: _angular_core.OutputEmitterRef<{
1052
1444
  data: any;
@@ -1065,7 +1457,7 @@ declare class NxeDataGridComponent implements OnDestroy {
1065
1457
  */
1066
1458
  readonly onOptionChanged: _angular_core.OutputEmitterRef<NxeGridOptionChangedEvent>;
1067
1459
  /** Extra CSS classes for a data row (replaces dx `onRowPrepared` class tweaks). */
1068
- readonly rowClass: _angular_core.InputSignal<(row: any) => string>;
1460
+ readonly rowClass: InputSignal<((row: any) => string) | undefined>;
1069
1461
  /**
1070
1462
  * dx `onCellPrepared` / `onRowPrepared`, fired once per rendered cell and row after the DOM
1071
1463
  * is written, with the real element for handlers that mutate it.
@@ -1111,9 +1503,17 @@ declare class NxeDataGridComponent implements OnDestroy {
1111
1503
  * its rows/columns through the source adapter and keeps the aliases the specs poke.
1112
1504
  */
1113
1505
  protected readonly hf: NxeHeaderFilterModel<NxeColumnComponent>;
1114
- protected readonly hfColumn: _angular_core.WritableSignal<NxeColumnComponent>;
1506
+ protected readonly hfColumn: _angular_core.WritableSignal<NxeColumnComponent | null>;
1115
1507
  protected readonly hfDraft: _angular_core.WritableSignal<ReadonlySet<any>>;
1116
1508
  private hfOverlayRef?;
1509
+ /**
1510
+ * The current page.
1511
+ *
1512
+ * A `linkedSignal` over `nxe-paging`'s own `pageIndex` so the binding is genuinely two-way:
1513
+ * a host that sets `[(pageIndex)]` moves the grid, and `goToPage` writes back. Declaring the
1514
+ * input without this would satisfy an audit while doing nothing — the option would resolve
1515
+ * and be ignored, which is worse than not accepting it.
1516
+ */
1117
1517
  protected readonly pageIndex: _angular_core.WritableSignal<number>;
1118
1518
  private readonly userPageSize;
1119
1519
  /** DevExtreme `.instance` facade — `#gridAll.instance.<method>()`. */
@@ -1136,17 +1536,53 @@ declare class NxeDataGridComponent implements OnDestroy {
1136
1536
  */
1137
1537
  private frozenColWidth;
1138
1538
  protected effectiveColWidth(col: NxeColumnComponent, idx: number): string | null;
1539
+ /** What a frozen layout's columns add up to, the command columns included. */
1540
+ private frozenTableWidth;
1541
+ /**
1542
+ * With `resizeFillsGrid`, the room a frozen widget-mode layout leaves on the right, which the
1543
+ * trailing column takes. 0 when the columns fill or overflow the grid - that table scrolls.
1544
+ */
1545
+ private readonly frozenSlack;
1139
1546
  protected tableWidthStyle(): string;
1140
1547
  protected readonly selectionEnabled: _angular_core.Signal<boolean>;
1141
1548
  protected readonly selectionMode: _angular_core.Signal<"none" | "single" | "multiple">;
1142
1549
  protected readonly showCheckboxes: _angular_core.Signal<boolean>;
1550
+ /**
1551
+ * The detached checkbox column. A tree list has none: dx draws its row checkbox in the first
1552
+ * cell, after the expander, and its select-all in the first column's header.
1553
+ */
1554
+ protected readonly selectColumnVisible: _angular_core.Signal<boolean>;
1555
+ protected readonly treeSelectAll: _angular_core.Signal<boolean>;
1556
+ private readonly recursiveTreeSelection;
1557
+ /**
1558
+ * Each node's recursive selection state as dx draws it: `all` when its key is selected or an
1559
+ * ancestor's is, or when every child is; `some` when only some descendants are; absent for none.
1560
+ * Keys hold the selection, so the hierarchy - not the key list - decides what a parent shows.
1561
+ */
1562
+ private readonly treeCheckState;
1563
+ /** Parent of each tree node, for settling ancestors after a recursive toggle. */
1564
+ private readonly treeParents;
1143
1565
  /** Selected rows in dataSource order (stale refs from old data naturally filtered out). */
1144
1566
  protected readonly selectedRowsData: _angular_core.Signal<any[]>;
1145
1567
  protected readonly allSelected: _angular_core.Signal<boolean>;
1146
1568
  protected readonly someSelected: _angular_core.Signal<boolean>;
1569
+ /** The key field: the bound `keyExpr`, else the remote source's own `key`. */
1570
+ private readonly keyField;
1147
1571
  private rowKey;
1148
1572
  private treeDescendantKeys;
1149
1573
  private toggleTreeSelection;
1574
+ /**
1575
+ * dx recursive selection. Clicking a node that is not fully checked (unchecked, or a parent
1576
+ * showing indeterminate) selects its whole subtree; clicking a checked one clears it. Then its
1577
+ * ancestors follow their children, checked once all of them are and cleared as soon as one is
1578
+ * not, so deselecting one child leaves its parent indeterminate rather than still ticked.
1579
+ *
1580
+ * The key list is rewritten as every checked node, which dx's `getSelectedRowKeys('all')`
1581
+ * also reports. The event's current keys are what changed on screen.
1582
+ */
1583
+ private toggleRecursiveTreeSelection;
1584
+ /** A recursive tree list's parent with only some descendants selected (dx's indeterminate box). */
1585
+ protected isTreeIndeterminate(row: any): boolean;
1150
1586
  private emitTreeSelectionChanged;
1151
1587
  /** `grouping.autoExpandAll` from the child element or object input; dx default is true. */
1152
1588
  private readonly groupAutoExpand;
@@ -1165,14 +1601,67 @@ declare class NxeDataGridComponent implements OnDestroy {
1165
1601
  protected readonly hasTotalFooter: _angular_core.Signal<boolean>;
1166
1602
  /** Grand-total sums keyed by dataField → formatted text (alignByColumn placement). */
1167
1603
  protected readonly totalSums: _angular_core.Signal<Map<string, string>>;
1604
+ /** A `{ load }` store handed to `dataSource` with `remoteOperations`, wrapped once per store. */
1605
+ private readonly storeWrappers;
1606
+ private wrapped;
1607
+ private readonly remoteWarnings;
1608
+ /** Rows picked on earlier pages, by key, so `selectedRowsData` still has them. */
1609
+ private readonly remoteRowCache;
1610
+ /** Set by filter-row typing; the next request waits `filterDebounce` before it is sent. */
1611
+ private remoteTyping;
1612
+ /** What the last request was for, to tell a changed filter, sort or size from a changed page. */
1613
+ private remoteSignature;
1614
+ private warnOnce;
1615
+ /**
1616
+ * The source the grid pages through, or `null` for a client-side array. An
1617
+ * {@link NxeRemoteDataSource} is used as it is; a `{ load }` store becomes one when
1618
+ * `remoteOperations` is on.
1619
+ */
1620
+ protected readonly remote: _angular_core.Signal<NxeRemoteDataSource<any> | null>;
1621
+ /** The rows every client-side computed reads: the array, or the remote source's current page. */
1622
+ private readonly sourceRows;
1623
+ private readonly selectedKeySet;
1624
+ /** The sort a request carries: the controlled `sort`, else the header sort. */
1625
+ private readonly remoteSort;
1626
+ /** The filter a request carries: filter row AND header filters AND the application's `filter`. */
1627
+ private readonly remoteFilter;
1628
+ /** The request the remote source should be showing, or `null` for a client-side grid. */
1629
+ private readonly remoteRequest;
1630
+ private dispatchRemote;
1631
+ private remoteFilterRowExpression;
1632
+ private filterRowCondition;
1633
+ private remoteHeaderFilterExpression;
1634
+ private toggleRemoteSelection;
1635
+ private toggleRemoteSelectAll;
1636
+ private emitRemoteSelectionChanged;
1168
1637
  protected readonly pagingEnabled: _angular_core.Signal<boolean>;
1169
1638
  protected readonly effectivePageSize: _angular_core.Signal<number>;
1170
1639
  protected readonly pageCount: _angular_core.Signal<number>;
1640
+ /**
1641
+ * The page shown: `pageIndex` held inside the page count. A filter can leave the bound index
1642
+ * past the last page; dx moves it back (m_data_source_adapter.js:454), and the rows, the
1643
+ * pager's selected button, its info text and prev/next all read this one value.
1644
+ */
1645
+ protected readonly currentPage: _angular_core.Signal<number>;
1171
1646
  /** The filtered/sorted rows visible on the current page (all rows when paging is off). */
1172
- private readonly pagedRows;
1647
+ protected readonly pagedRows: _angular_core.Signal<any[]>;
1648
+ /**
1649
+ * dx `PagerView.isVisible` (m_pager.js): `true` always, `'auto'` only while there is more than
1650
+ * one page to move between, and never under virtual or infinite scrolling.
1651
+ */
1173
1652
  protected readonly pagerVisible: _angular_core.Signal<boolean>;
1174
1653
  protected readonly pageSizes: _angular_core.Signal<number[]>;
1175
- protected readonly pageNumbers: _angular_core.Signal<number[]>;
1654
+ /** dx's small mode: a page-size drop-down, and the navigation buttons whatever the option says. */
1655
+ protected readonly pagerCompact: _angular_core.Signal<boolean>;
1656
+ /** dx `getRenderPrevButton` (pages/page_index_selector.js): `!isLargeDisplayMode || showNavigationButtons`. */
1657
+ protected readonly pagerNavVisible: _angular_core.Signal<boolean>;
1658
+ /**
1659
+ * The page buttons and `. . .` separators. A linkedSignal because dx's list keeps a sliding
1660
+ * window between renders (pages/large.js): the previous window decides whether the next one
1661
+ * moves.
1662
+ */
1663
+ private readonly pagerWindow;
1664
+ protected readonly pageItems: _angular_core.Signal<readonly NxePagerItem[]>;
1176
1665
  /** True when the host element is `<nxe-tree-list>` (vs `<nxe-data-grid>`). */
1177
1666
  protected readonly treeMode: boolean;
1178
1667
  private readonly expandOverride;
@@ -1182,6 +1671,15 @@ declare class NxeDataGridComponent implements OnDestroy {
1182
1671
  protected toggleTreeNode(key: any, event: Event): void;
1183
1672
  /** Flattened group + data render rows (tree hierarchy, then grouping, then flat). */
1184
1673
  protected readonly renderRows: _angular_core.Signal<GridRenderRow[]>;
1674
+ /**
1675
+ * The dataField a group summary renders under: dx `_calculateSummaryCells` (m_summary.js:443)
1676
+ * takes `showInColumn` when it names a column, else the item's own `column`.
1677
+ *
1678
+ * Keying footers on `column()` alone put an item that names only `showInColumn` (broker's
1679
+ * `name="totalSumInsured" showInColumn="totalSumInsured" summaryType="custom"`) under its
1680
+ * empty `column` - i.e. in whichever column happens to have no dataField.
1681
+ */
1682
+ private summaryTarget;
1185
1683
  /** Computes + formats a summary item over a set of rows. */
1186
1684
  private summaryText;
1187
1685
  /**
@@ -1196,6 +1694,7 @@ declare class NxeDataGridComponent implements OnDestroy {
1196
1694
  protected nextPage(): void;
1197
1695
  protected changePageSize(size: number): void;
1198
1696
  protected onPageSizeSelect(event: Event): void;
1697
+ /** dx's info text, `Page {0} of {1} ({2} items)` unless `infoText` says otherwise (pagination/info.js). */
1199
1698
  protected readonly pageInfo: _angular_core.Signal<string>;
1200
1699
  private static readonly fieldArrayEqual;
1201
1700
  /**
@@ -1269,8 +1768,16 @@ declare class NxeDataGridComponent implements OnDestroy {
1269
1768
  /** True when this header renders a resize handle on its right edge. */
1270
1769
  protected resizeHandleVisible(col: NxeColumnComponent, ci: number): boolean;
1271
1770
  protected startColumnResize(col: NxeColumnComponent, ci: number, event: PointerEvent): void;
1771
+ /** A visible column's position, for cells rendered outside the main `@for` index. */
1772
+ protected visibleIndexOf(col: NxeColumnComponent): number;
1272
1773
  /** Command columns preceding the data columns — the offset dx's 1-based aria-colindex counts from. */
1273
1774
  protected leadCellCount(): number;
1775
+ /** The master-detail expander's aria-colindex: straight after the group indents. */
1776
+ protected readonly expandColIndex: _angular_core.Signal<number>;
1777
+ /** The selection column's aria-colindex: after the indents and the expander. */
1778
+ protected readonly selectColIndex: _angular_core.Signal<number>;
1779
+ /** The row-edit command column's aria-colindex: after the last data column. */
1780
+ protected readonly editColIndex: _angular_core.Signal<number>;
1274
1781
  /** Resize commit: freeze all rendered widths (dx post-resize behavior) + emit width events. */
1275
1782
  private commitColumnResize;
1276
1783
  protected readonly filterRowVisible: _angular_core.Signal<boolean>;
@@ -1278,7 +1785,7 @@ declare class NxeDataGridComponent implements OnDestroy {
1278
1785
  private readonly expandedDetails;
1279
1786
  protected isDetailExpanded(row: any): boolean;
1280
1787
  protected toggleDetail(row: any, event: Event): void;
1281
- protected detailTemplate(): TemplateRef<unknown>;
1788
+ protected detailTemplate(): TemplateRef<unknown> | null;
1282
1789
  protected detailContext(row: any, rowIndex: number): {
1283
1790
  $implicit: {
1284
1791
  data: any;
@@ -1308,13 +1815,33 @@ declare class NxeDataGridComponent implements OnDestroy {
1308
1815
  protected readonly editingCell: _angular_core.WritableSignal<{
1309
1816
  row: any;
1310
1817
  field: string;
1311
- }>;
1818
+ } | null>;
1312
1819
  /** Buffered values for the row in edit mode (`row` mode), keyed by dataField. */
1313
1820
  protected readonly rowEditBuffer: _angular_core.WritableSignal<Record<string, any>>;
1314
1821
  protected isEditingCell(row: any, col: NxeColumnComponent): boolean;
1315
1822
  protected isRowEditing(row: any): boolean;
1316
1823
  protected editableCell(col: NxeColumnComponent): boolean;
1317
1824
  protected onCellClick(row: any, col: NxeColumnComponent, event: Event): void;
1825
+ /**
1826
+ * dx `readOnly: !options.setValue` (m_editing.js:210): a grid checkbox is editable only where
1827
+ * dx gives the cell a setValue - cell mode on an editable column (m_editing.js:1832-1847). Row
1828
+ * mode's resting rows stay read-only; the row being edited renders the editor instead.
1829
+ */
1830
+ protected booleanReadOnly(col: NxeColumnComponent): boolean;
1831
+ /**
1832
+ * A boolean cell that is its own editor: cell mode, editable, and no editCellTemplate asking
1833
+ * to take over. The resting checkbox toggles in place, as dx's always-shown dxCheckBox does.
1834
+ */
1835
+ protected booleanInPlace(col: NxeColumnComponent): boolean;
1836
+ /** A resting boolean checkbox was clicked: toggle it when it is editable, else let it be. */
1837
+ protected onBooleanCellClick(row: any, col: NxeColumnComponent, event: Event): void;
1838
+ /**
1839
+ * The checkbox editor's state: the buffered value in the row being edited, else the row's.
1840
+ * Read as the native editor it replaces read it - any value but '' and 'false' is checked.
1841
+ */
1842
+ protected editorChecked(row: any, col: NxeColumnComponent): boolean;
1843
+ /** A click or Space on the checkbox editor. Space must not scroll the page or reach the grid. */
1844
+ protected toggleEditorCheck(row: any, col: NxeColumnComponent, mode: 'cell' | 'row', event: Event): void;
1318
1845
  /** dx cell-mode semantics: commit to the row on blur/Enter (client-side array write-through). */
1319
1846
  protected commitCellEdit(row: any, col: NxeColumnComponent, rawInput: any): void;
1320
1847
  /** Write a cell value through the grid's own path, leaving edit state alone (dx `setValue`). */
@@ -1367,19 +1894,149 @@ declare class NxeDataGridComponent implements OnDestroy {
1367
1894
  */
1368
1895
  protected readonly displayRows: _angular_core.Signal<any[]>;
1369
1896
  protected readonly loading: _angular_core.Signal<boolean>;
1897
+ /** A remote request is in flight over rows already on screen: the overlay, not the empty-body spinner. */
1898
+ protected readonly remoteBusy: _angular_core.Signal<boolean>;
1899
+ protected readonly remoteFailed: _angular_core.Signal<boolean>;
1900
+ protected retryRemote(): void;
1901
+ /**
1902
+ * dx `isEmpty()` = `!items().length` (m_data_controller.js:1044-1046), toggled as `dx-empty`
1903
+ * on the rows view in `_renderCore` (m_rows_view.js:710) whether loading or not.
1904
+ *
1905
+ * Keyed on `renderRows` - what the `<tbody>` iterates, dx's `items()` - not `displayRows`. The
1906
+ * two agree for flat and grouped grids (`pagedRows` clamps the page), but a tree list builds
1907
+ * `renderRows` from the unfiltered source while `displayRows` applies the filters, so a tree
1908
+ * whose filter matches nothing still renders rows and must not also show an empty body.
1909
+ */
1910
+ protected readonly isEmpty: _angular_core.Signal<boolean>;
1911
+ /**
1912
+ * What the last reading of this grid's own layout found.
1913
+ *
1914
+ * - `bounded` is dx's `hasHeight`: the rows view is sized by the grid, not by its rows.
1915
+ * - `room` is dx's `elementHeightWithoutScrollbar - contentHeight` (m_rows_view.js:840-842): the
1916
+ * rows view's inner height less the table as it stands with the free-space row empty and the
1917
+ * last row keeping its line. Neither the row's height nor the last-row-border state changes
1918
+ * it, so what it decides cannot change the next reading.
1919
+ * - `fill` is dx's `resultHeight` (:844-848): the height that leaves the table 1px short of
1920
+ * the rows view (`_getHeightCorrection`, 1 in Chrome).
1921
+ * - `rowHeight` is dx's `_rowHeight`, the average rendered row. It sizes the gap that a short
1922
+ * last page leaves.
1923
+ */
1924
+ private readonly freeSpaceMeasure;
1925
+ /**
1926
+ * dx's three cases, in its order:
1927
+ * - no rows: the row shows at 0px, so the last row keeps no border to lose (:857-860);
1928
+ * - a bounded grid whose rows leave room: the row fills it, less the 1px dx subtracts in
1929
+ * Chrome (`_getHeightCorrection`), and column lines run to the bottom (:837-855);
1930
+ * - an unbounded grid on a short page of several: the row holds the missing rows' height, so
1931
+ * the grid keeps its height from page to page (:823-835).
1932
+ *
1933
+ * `visible` drives the last row's border in a bordered grid (`_updateLastRowBorder`, :931-937).
1934
+ */
1935
+ private readonly freeSpace;
1936
+ protected readonly freeSpaceHeight: _angular_core.Signal<number>;
1937
+ protected readonly freeSpaceVisible: _angular_core.Signal<boolean>;
1938
+ /**
1939
+ * One reading of the rows view, from the ResizeObserver that watches it and its table.
1940
+ *
1941
+ * dx hides the free-space row and then measures (m_rows_view.js:837-848). This reading cannot
1942
+ * hide it - that would shrink the document and could clamp its scroll position - so it takes
1943
+ * the row's part of the table back out arithmetically, from quantities that do not move with
1944
+ * what the reading decides:
1945
+ *
1946
+ * - The row's box comes out whole: the height it was given, or with none, its halves of the
1947
+ * collapsed borders above and below it (0.5px under a last row with a 1px frame).
1948
+ * - With no free space shown, a bordered grid's last row gives its line to the box
1949
+ * (`nxe-last-row-border`, `border-bottom-width:0`), which makes the table a line shorter.
1950
+ * That line is put back, at the grid's line width, so the answer is the same either way.
1951
+ *
1952
+ * Read naively off the table, either one feeds back: the half-line reads as room, which shows
1953
+ * the row, which gives the line back, which removes the room - a flip every frame - and a
1954
+ * filled row in a rows view that hugs its table reads as its own room, 1px less each time.
1955
+ *
1956
+ * The second case gets its own rule. A rows view that hugs its table while the row adds height
1957
+ * of its own is sized by that row, so it has no room, whatever `height` says: the row lets go
1958
+ * at once. (A bounded grid resized to exactly its table height less the 1px lands here too, and
1959
+ * fills again on the next reading.)
1960
+ *
1961
+ * Whether the grid is bounded (dx `hasHeight`): an explicit `height` other than 'auto' always
1962
+ * is (m_grid_view.js:597-604, `isHeightSpecified`), even a percentage that resolves to auto,
1963
+ * and then has no room while its rows view hugs. Otherwise a rows view that does not hug is
1964
+ * bounded by CSS, and one that hugs while the row holds free space is not, so a grid that
1965
+ * loses its height lets go of the space. When they coincide with no free space held (an exact
1966
+ * fit), the previous answer stands.
1967
+ */
1968
+ private measureFreeSpace;
1969
+ /**
1970
+ * dx `updateColumnDataTypes` (m_columns_controller.js:914-977): a column with no declared
1971
+ * `dataType` takes the type of its values, and mixed values make it 'string'.
1972
+ *
1973
+ * A lookup column is typed by its DISPLAY values - dx's `lookup.dataType`, which is what dx
1974
+ * aligns, formats and edits a lookup by (:901) - and a value with no lookup match contributes
1975
+ * nothing: dx's `lookup.calculateCellValue` returns `valueMap[value]`, undefined for a missing
1976
+ * key, so `getValueDataType` skips it (:946). A not-yet-loaded list must not type the column.
1977
+ *
1978
+ * Once inferred, a type sticks. dx infers only while `dataType` is unset (:935) and stops once
1979
+ * every column is typed (:289), so a later load of mixed values cannot flip a column; the
1980
+ * previous value of this linkedSignal carries that. The refresh tick is in the source because
1981
+ * rows mutated in place (inline editing, `refresh()`) can type a column that was all blanks.
1982
+ *
1983
+ * dx samples the loaded page (or the first group's leaves); this scans the client array,
1984
+ * which only differs when the types are mixed beyond that sample.
1985
+ */
1986
+ private readonly inferredTypes;
1987
+ /** A lookup's value → raw display value, first match winning as in `lookupText`; null without one. */
1988
+ private lookupDisplayMap;
1989
+ /**
1990
+ * The column's effective type - its declared `dataType`, else the inferred one - which is what
1991
+ * dx's `column.dataType` holds after inference. Every type-driven decision reads this, never
1992
+ * `col.dataType()` directly, or an undeclared column would behave as text in one place and
1993
+ * as a number in the next.
1994
+ */
1995
+ protected colType(col: NxeColumnComponent): NxeColType | undefined;
1370
1996
  /** The active sort: an explicit user click, else the first column declaring `sortOrder`. */
1371
1997
  private effectiveSort;
1998
+ /** A user sort change: the grid's own state, and the controlled `sort` when it is in play. */
1999
+ private setSort;
2000
+ /**
2001
+ * dx `getAlignmentByDataType` (m_columns_controller_utils.js) over the effective type:
2002
+ * number right, boolean centre, everything else left. A lookup aligns by its display text
2003
+ * (dx `lookup.dataType`, :901), which is text - so left, even over numeric codes.
2004
+ */
2005
+ /**
2006
+ * Columns whose cellTemplate draws a check box (nxe-check-box, mat-checkbox, a native
2007
+ * checkbox input or anything with role=checkbox). align() centres them like a boolean column,
2008
+ * header and cells, unless the column asks for center or right. dx centres only by dataType,
2009
+ * so a template check box over non-boolean data (or no dataField) would sit at the left there;
2010
+ * a check box reads as a boolean whatever feeds it, so here it is centred too.
2011
+ *
2012
+ * Sticky, as an inferred type is: a column that drew a check box keeps its alignment when a
2013
+ * filter or an empty page leaves no row to look at. A column that goes away drops out.
2014
+ */
2015
+ private readonly templateCheckboxColumns;
2016
+ private detectTemplateCheckboxColumns;
2017
+ /** Boolean data (declared or inferred) or a cellTemplate seen drawing a check box. */
2018
+ private isCheckboxColumn;
1372
2019
  protected align(col: NxeColumnComponent): string;
2020
+ /**
2021
+ * The filter cell's text-align: dx sets the filter editor's input to the column alignment
2022
+ * unless it is centre (m_filter_row.js:390-395).
2023
+ */
2024
+ protected filterCellAlign(col: NxeColumnComponent): string | null;
1373
2025
  protected colWidth(col: NxeColumnComponent): string | null;
1374
2026
  protected getRaw(row: any, dataField: string): any;
2027
+ /**
2028
+ * dx `showEditorAlways` (m_columns_controller.js:912): a boolean-typed column - declared or
2029
+ * inferred - renders a checkbox, unless it is a lookup or has its own cellTemplate. Decided
2030
+ * per column, not per value: a column whose values are mixed infers 'string' and shows text,
2031
+ * as in dx.
2032
+ */
2033
+ protected isBooleanCell(col: NxeColumnComponent): boolean;
1375
2034
  /** 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
2035
  protected cellText(col: NxeColumnComponent, row: any): string;
1379
2036
  /** Column `lookup` resolution (orbit addition): raw value → display text via the lookup list. */
1380
2037
  private lookupText;
1381
2038
  private defaultFormat;
1382
- protected templateFor(name: string | undefined): TemplateRef<unknown>;
2039
+ protected templateFor(name: string | undefined): TemplateRef<unknown> | null;
1383
2040
  protected cellContext(row: any, rowIndex: number, columnIndex: number, col: NxeColumnComponent): {
1384
2041
  $implicit: NxeCellInfo;
1385
2042
  };
@@ -1406,14 +2063,89 @@ declare class NxeDataGridComponent implements OnDestroy {
1406
2063
  * failure the `effect()` ban exists to prevent.
1407
2064
  */
1408
2065
  private emitPreparedEvents;
2066
+ /**
2067
+ * The single cell holding the grid's tab stop.
2068
+ *
2069
+ * A grid is one stop in the tab order, not one per cell — a 200-row table would otherwise be
2070
+ * 1,400 tab presses to get past. Row `-1` is the header, so ArrowUp from the first data row
2071
+ * reaches the column headers rather than stopping dead.
2072
+ */
2073
+ protected readonly focusedCell: _angular_core.WritableSignal<{
2074
+ row: number;
2075
+ col: number;
2076
+ }>;
2077
+ /**
2078
+ * The rows the keyboard and `aria-rowindex` address: the data rows actually RENDERED.
2079
+ *
2080
+ * Emphatically not `pagedRows()`. That is the page's data BEFORE grouping and tree collapse
2081
+ * take rows out of the DOM, while `renderRows` renumbers densely over what it emits - so with
2082
+ * one group collapsed `pagedRows()` counts three rows where two have a cell to focus. Every
2083
+ * consumer of the count is then wrong in a different way: `aria-rowcount` claims a row a
2084
+ * reader cannot reach, Ctrl+End targets an index with no DOM behind it and movement silently
2085
+ * does nothing, and Enter and Space read `pagedRows()[row]` - which is a DIFFERENT row than
2086
+ * the one under the cursor, so Space selects something the user is not looking at.
2087
+ */
2088
+ protected readonly navigableRows: _angular_core.Signal<any[]>;
2089
+ protected isCellTabStop(row: number, col: number): boolean;
2090
+ protected focusCell(row: number, col: number): void;
2091
+ /** dx marks the sorted column for assistive tech as well as with the arrow glyph. */
2092
+ protected ariaSort(col: NxeColumnComponent): 'ascending' | 'descending' | null;
2093
+ /**
2094
+ * The DOM cell at a logical position, addressed by `aria-colindex` rather than by offset.
2095
+ *
2096
+ * Offsets break twice over. A band cell carries `role="columnheader"` too, so counting
2097
+ * header cells finds bands as well as leaves; and `mergeColumns` deletes the covered `<td>`s,
2098
+ * so a data row can be short. `aria-colindex` is emitted on every cell and already includes
2099
+ * the lead command columns, so it names the position exactly rather than by counting.
2100
+ *
2101
+ * Both lookups stay inside this grid's own table: a master-detail grid renders a whole second
2102
+ * grid into a `<td>`, and its rows would otherwise answer to the outer grid's arrow keys.
2103
+ */
2104
+ private cellAt;
2105
+ /** Move the tab stop and put real DOM focus on the cell that now holds it. */
2106
+ private moveFocus;
2107
+ /**
2108
+ * Arrow-key movement over cells.
2109
+ *
2110
+ * Deliberately not bound to the rowsview: a projected editor inside a cell needs its own
2111
+ * arrow keys, so anything originating inside an editor is left alone.
2112
+ */
2113
+ protected onGridKeydown(event: KeyboardEvent): void;
1409
2114
  /** True when this header can start a drag gesture (to the group panel and/or a reorder). */
1410
2115
  protected headerDraggable(col: NxeColumnComponent): boolean;
1411
2116
  protected onHeaderPointerDown(col: NxeColumnComponent, ci: number, event: PointerEvent): void;
1412
2117
  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. */
2118
+ /**
2119
+ * Chip click (dx): toggle the grouped column's sort — unless this click ends a drag. dx sends
2120
+ * it through the same changeSortOrder as a header click (m_sorting.js:134), so the same gate.
2121
+ */
1414
2122
  protected onChipClick(col: NxeColumnComponent): void;
1415
2123
  protected onHeaderClick(col: NxeColumnComponent): void;
1416
2124
  protected sortDir(col: NxeColumnComponent): 'asc' | 'desc' | null;
2125
+ /**
2126
+ * `sorting.mode` as dx's sorting code reads the option: unbound is dx's default 'single', an
2127
+ * object gives its `mode` (default 'single'), and anything else - `[sorting]="false"` or
2128
+ * `null` - has no mode at all.
2129
+ */
2130
+ private sortingMode;
2131
+ /**
2132
+ * dx changeSortOrder (m_columns_controller.js:758-766) acts only in mode 'single' or
2133
+ * 'multiple', and only on a column that allows sorting. `[sorting]="false"` still renders the
2134
+ * sort slot, but a header or chip click never sorts.
2135
+ */
2136
+ private clickSorts;
2137
+ /**
2138
+ * dx `isSortingAllowed` (m_sorting_mixin.js:37): a sortable column, unless the mode is
2139
+ * literally 'none'. A column with no dataField is never sortable (m_columns_controller.js:
2140
+ * 1383-1388). Gates the `nxe-datagrid-action` class, and the sort slot of an unsorted column.
2141
+ */
2142
+ protected sortAllowed(col: NxeColumnComponent): boolean;
2143
+ /**
2144
+ * Whether the header renders dx's sort span: always for the sorted column, and as an empty
2145
+ * `nxe-sort-none` placeholder for any other sortable one (m_sorting_mixin.js:38-46). The
2146
+ * placeholder is display:none in the fixed table and 14px wide in the best-fit pass.
2147
+ */
2148
+ protected sortSlot(col: NxeColumnComponent): boolean;
1417
2149
  protected onRowEnter(index: number): void;
1418
2150
  protected onRowLeave(): void;
1419
2151
  protected isHovered(index: number): boolean;
@@ -1423,12 +2155,18 @@ declare class NxeDataGridComponent implements OnDestroy {
1423
2155
  protected onCheckboxClick(row: any, event: Event): void;
1424
2156
  private toggleRow;
1425
2157
  protected toggleSelectAll(event: Event): void;
2158
+ /**
2159
+ * `clearSelection()` (`scope` null: every selected row) and `deselectAll()` (`scope`: the rows
2160
+ * on show). One `onSelectionChanged` with the dropped keys as deselected, none when nothing
2161
+ * was selected.
2162
+ */
2163
+ private deselectRows;
1426
2164
  private emitSelectionChanged;
1427
2165
  private rowKeys;
1428
2166
  private static readonly STRING_OPS;
1429
2167
  private static readonly SCALAR_OPS;
1430
2168
  private static readonly OP_ICON_SLUGS;
1431
- protected readonly frMenuColumn: _angular_core.WritableSignal<NxeColumnComponent>;
2169
+ protected readonly frMenuColumn: _angular_core.WritableSignal<NxeColumnComponent | null>;
1432
2170
  private frMenuOverlayRef?;
1433
2171
  private readonly frMenuPanel;
1434
2172
  /** Which editor a filter cell renders (mirrors dx: select for lookup/boolean, typed inputs). */
@@ -1439,7 +2177,12 @@ declare class NxeDataGridComponent implements OnDestroy {
1439
2177
  protected frOp(col: NxeColumnComponent): NxeFilterOp;
1440
2178
  protected frText(col: NxeColumnComponent): string;
1441
2179
  protected frText2(col: NxeColumnComponent): string;
1442
- protected frOpIconClass(col: NxeColumnComponent): string;
2180
+ /**
2181
+ * The chooser glyph: dx's magnifier ('default') until an operation is picked from the menu,
2182
+ * then that operation's own. Only the slug varies - the template emits `nxe-icon-filter-operation-*`
2183
+ * and nothing prefixed `dx-`, which the compat mirror would strip for want of an nxe twin.
2184
+ */
2185
+ protected frOpIconSlug(col: NxeColumnComponent): string;
1443
2186
  protected opIconSlug(op: NxeFilterOp): string;
1444
2187
  protected setFilterRowText(col: NxeColumnComponent, text: string, second?: boolean): void;
1445
2188
  protected openFilterOpMenu(col: NxeColumnComponent, event: Event): void;
@@ -1478,8 +2221,8 @@ declare class NxeDataGridComponent implements OnDestroy {
1478
2221
  private compare;
1479
2222
  ngOnDestroy(): void;
1480
2223
  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>;
2224
+ 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; }; "resizeFillsGridInput": { "alias": "resizeFillsGrid"; "required": false; "isSignal": true; }; "remoteOperations": { "alias": "remoteOperations"; "required": false; "isSignal": true; }; "pageIndexModel": { "alias": "pageIndex"; "required": false; "isSignal": true; }; "pageSizeModel": { "alias": "pageSize"; "required": false; "isSignal": true; }; "sortModel": { "alias": "sort"; "required": false; "isSignal": true; }; "filter": { "alias": "filter"; "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; }; }, { "pageIndexModel": "pageIndexChange"; "pageSizeModel": "pageSizeChange"; "sortModel": "sortChange"; "optionsChange": "optionsChange"; "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
2225
  }
1483
2226
 
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 };
2227
+ export { NXE_HF_BLANKS, NXE_REMOTE_DEFAULT_PAGE_SIZE, NxeColumnComponent, NxeDataGridComponent, NxeDragGesture, NxeEditingComponent, NxeFilterRowComponent, NxeGestureClickSuppression, NxeGridDragController, NxeGroupItemComponent, NxeGroupPanelComponent, NxeGroupingComponent, NxeHeaderFilterComponent, NxeHeaderFilterModel, NxeHeaderFilterPanelComponent, NxeMasterDetailComponent, NxePagerComponent, NxePaginatorComponent, NxePagingComponent, NxeRemoteDataSource, NxeScrollingComponent, NxeSelectionComponent, NxeSummaryComponent, NxeTotalItemComponent, exportDataGrid, exportNxeGridToExcel, nxeDatePattern, nxeDefaultFormat, nxeExcelNumFmt, nxeExcelNumberMask, nxeFilterAnd, nxeFilterOr, nxeFlattenFilter, nxeFormatNumberValue, nxeHfKey, nxeIsStoreLike, nxeLoadOptions, nxeNumberMask, nxeNumberValue, nxeSameQuery, nxeValueDataType, openNxeHeaderFilterOverlay };
2228
+ export type { NxeCellInfo, NxeCellPreparedEvent, NxeColumnDataType, NxeColumnFormat, NxeColumnFormatObject, NxeCustomizeText, NxeDragGestureDelegate, NxeEditCellInfo, NxeExcelExportOptions, NxeExportColumn, NxeExportDataGridOptions, NxeFilterExpression, NxeFilterOp, NxeFilterOperator, NxeFlatFilterCondition, NxeGridChange, NxeGridDragHooks, NxeGridInstance, NxeGridOptionChangedEvent, NxeGridSelectionChangedEvent, NxeGroupCellInfo, NxeHeaderCell, NxeHeaderFilterLabels, NxeHeaderFilterOverlayOptions, NxeHeaderFilterSource, NxeHfDateNode, NxeLoadContext, NxeLoadOptions, NxeLoadResult, NxeRemoteDataSourceConfig, NxeRemoteLoadFn, NxeRemoteQuery, NxeRowInfo, NxeRowPreparedEvent, NxeRowType, NxeSortDescriptor, NxeStoreLike, NxeSummaryItemLike, NxeTreeListNode };