@nexterajs/ui 0.0.2 → 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 (59) 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 +652 -12
  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 +321 -33
  9. package/fesm2022/nexterajs-ui-dropdowns.mjs.map +1 -1
  10. package/fesm2022/nexterajs-ui-file-uploader.mjs +572 -10
  11. package/fesm2022/nexterajs-ui-file-uploader.mjs.map +1 -1
  12. package/fesm2022/nexterajs-ui-forms.mjs +2027 -136
  13. package/fesm2022/nexterajs-ui-forms.mjs.map +1 -1
  14. package/fesm2022/nexterajs-ui-grid.mjs +1222 -188
  15. package/fesm2022/nexterajs-ui-grid.mjs.map +1 -1
  16. package/fesm2022/nexterajs-ui-intl.mjs +103 -11
  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 +46 -44
  21. package/fesm2022/nexterajs-ui-notify.mjs.map +1 -1
  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 +51 -26
  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-tree-view.mjs +5 -2
  29. package/fesm2022/nexterajs-ui-tree-view.mjs.map +1 -1
  30. package/fesm2022/nexterajs-ui-validation.mjs +42 -11
  31. package/fesm2022/nexterajs-ui-validation.mjs.map +1 -1
  32. package/fesm2022/nexterajs-ui.mjs +5 -0
  33. package/fesm2022/nexterajs-ui.mjs.map +1 -1
  34. package/package.json +33 -2
  35. package/styles/_dropdown.scss +5 -5
  36. package/styles/_editor.scss +52 -17
  37. package/styles/_mixins.scss +1 -1
  38. package/styles/_scrollbar.scss +5 -5
  39. package/styles/_status.scss +21 -0
  40. package/styles/_toast.scss +14 -14
  41. package/styles/_tokens.scss +420 -3
  42. package/styles/_widget.scss +14 -3
  43. package/styles/index.scss +6 -4
  44. package/types/nexterajs-ui-charts.d.ts +446 -0
  45. package/types/nexterajs-ui-core.d.ts +387 -22
  46. package/types/nexterajs-ui-display.d.ts +720 -0
  47. package/types/nexterajs-ui-dropdowns.d.ts +191 -48
  48. package/types/nexterajs-ui-file-uploader.d.ts +236 -7
  49. package/types/nexterajs-ui-forms.d.ts +675 -30
  50. package/types/nexterajs-ui-grid.d.ts +448 -93
  51. package/types/nexterajs-ui-intl.d.ts +39 -4
  52. package/types/nexterajs-ui-navigation.d.ts +531 -0
  53. package/types/nexterajs-ui-notify.d.ts +4 -2
  54. package/types/nexterajs-ui-overlay.d.ts +703 -0
  55. package/types/nexterajs-ui-pivot.d.ts +56 -48
  56. package/types/nexterajs-ui-shell.d.ts +294 -0
  57. package/types/nexterajs-ui-tree-view.d.ts +6 -3
  58. package/types/nexterajs-ui-validation.d.ts +20 -11
  59. package/types/nexterajs-ui.d.ts +5 -0
@@ -1,11 +1,15 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { OnChanges, SimpleChanges, OnDestroy } from '@angular/core';
3
- import { NxeValidationResult, NxeFormControlBase } from '@nexterajs/ui/core';
2
+ import { OnChanges, SimpleChanges, OnDestroy, InjectionToken, EnvironmentProviders, AfterContentInit, TemplateRef } from '@angular/core';
3
+ import { NxeValidationResult, NxeFormControlBase, NxeMessageKey, NxeExpr } from '@nexterajs/ui/core';
4
4
  import { DateTime } from 'luxon';
5
5
  import { NxeCalendarZoom, NxePeriodCell } from '@nexterajs/ui/intl';
6
6
 
7
7
  /** DevExtreme button `type` (style intent), as used in this codebase. */
8
8
  type NxeButtonType = 'normal' | 'default' | 'success' | 'danger';
9
+ /** Fill treatment: dx `stylingMode`. `contained` (filled) is the default and the legacy look. */
10
+ type NxeButtonStylingMode = 'contained' | 'outlined' | 'text';
11
+ /** Button scale. `md` is the legacy size and adds no class. */
12
+ type NxeButtonSize = 'sm' | 'md' | 'lg';
9
13
  /**
10
14
  * The `validationGroup` object carried on a {@link NxeButtonClickEvent}, mirroring the
11
15
  * DevExtreme button-click `event.validationGroup`. Its `validate()` runs every member of the
@@ -39,6 +43,10 @@ interface NxeButtonClickEvent {
39
43
  * Content is projected (`<nxe-icon> + <span>`, the dominant pattern). The legacy `text`/`type`
40
44
  * inputs render a `.nxe-button-text` label for the ~25 attribute-styled buttons.
41
45
  *
46
+ * `stylingMode`, `size`, `loading` and `block` are additive: with none of them set the host renders
47
+ * exactly as before. An icon can be projected into the leading slot as
48
+ * `<svg nxeButtonIcon ...>` (or `[nxeIcon]`), placed before the label.
49
+ *
42
50
  * `onClick` emits {@link NxeButtonClickEvent}; native `(click)` also fires (27 sites bind it).
43
51
  */
44
52
  declare class NxeButtonComponent {
@@ -46,9 +54,20 @@ declare class NxeButtonComponent {
46
54
  private readonly groupScope;
47
55
  private readonly elementRef;
48
56
  /** Legacy `text` label (rendered as `.nxe-button-text` when set, no projected content). */
49
- readonly text: _angular_core.InputSignal<string>;
57
+ readonly text: _angular_core.InputSignal<string | undefined>;
50
58
  /** Style intent (`default`=blue, `success`=green, `danger`=red, `normal`=white). */
51
59
  readonly type: _angular_core.InputSignal<NxeButtonType>;
60
+ /** Fill treatment: `contained` (default), `outlined` (transparent with a coloured border) or `text` (no border). */
61
+ readonly stylingMode: _angular_core.InputSignal<NxeButtonStylingMode>;
62
+ /** Scale: `sm`, `md` (default, the legacy size) or `lg`. */
63
+ readonly size: _angular_core.InputSignal<NxeButtonSize>;
64
+ /**
65
+ * In-flight state: shows a spinner over the (hidden but still laid out, so the width holds)
66
+ * content, sets `aria-busy="true"` and swallows clicks and Enter/Space until it clears.
67
+ */
68
+ readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
69
+ /** Full-width button (`display: flex; width: 100%`). An explicit `width` still wins. */
70
+ readonly block: _angular_core.InputSignalWithTransform<boolean, unknown>;
52
71
  /** Disabled state (`[disabled]`); blocks `onClick` and dims the button. */
53
72
  readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
54
73
  /** Named validation group resolved via {@link NxeValidationGroupRegistry} on click. */
@@ -60,19 +79,19 @@ declare class NxeButtonComponent {
60
79
  * `.nxe-button-content`, before any projected content. Projection is the dominant pattern
61
80
  * here (a `mat-icon` plus a `span`), so the two must coexist rather than compete.
62
81
  */
63
- readonly icon: _angular_core.InputSignal<string>;
82
+ readonly icon: _angular_core.InputSignal<string | undefined>;
64
83
  /** dx `height` — a bare number (or numeric string) means px; anything else passes through. */
65
- readonly height: _angular_core.InputSignal<string | number>;
84
+ readonly height: _angular_core.InputSignal<string | number | undefined>;
66
85
  /** dx `width` — same coercion as {@link height}. */
67
- readonly width: _angular_core.InputSignal<string | number>;
68
- protected readonly heightStyle: _angular_core.Signal<string>;
69
- protected readonly widthStyle: _angular_core.Signal<string>;
86
+ readonly width: _angular_core.InputSignal<string | number | undefined>;
87
+ protected readonly heightStyle: _angular_core.Signal<string | null>;
88
+ protected readonly widthStyle: _angular_core.Signal<string | null>;
70
89
  /** DevExtreme-compatible click event. */
71
90
  readonly onClick: _angular_core.OutputEmitterRef<NxeButtonClickEvent>;
72
91
  protected handleKeydown(event: KeyboardEvent): void;
73
92
  protected handleClick(event: Event): void;
74
93
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeButtonComponent, never>;
75
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeButtonComponent, "nxe-button", never, { "text": { "alias": "text"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "validationGroup": { "alias": "validationGroup"; "required": false; "isSignal": true; }; "focusStateEnabled": { "alias": "focusStateEnabled"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; }, { "onClick": "onClick"; }, never, ["*"], true, never>;
94
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeButtonComponent, "nxe-button", never, { "text": { "alias": "text"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "stylingMode": { "alias": "stylingMode"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "block": { "alias": "block"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "validationGroup": { "alias": "validationGroup"; "required": false; "isSignal": true; }; "focusStateEnabled": { "alias": "focusStateEnabled"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; }, { "onClick": "onClick"; }, never, ["[nxeButtonIcon], [nxeIcon]", "*"], true, never>;
76
95
  }
77
96
 
78
97
  /** Text-box input mode (only `text`/`password` are used in this codebase). */
@@ -93,14 +112,23 @@ type NxeTextBoxMode = 'text' | 'password' | 'email' | 'tel' | 'search';
93
112
  */
94
113
  declare class NxeTextBoxComponent extends NxeFormControlBase<string> {
95
114
  readonly mode: _angular_core.InputSignal<NxeTextBoxMode>;
96
- readonly placeholderInput: _angular_core.InputSignal<string>;
115
+ readonly placeholderInput: _angular_core.InputSignal<string | undefined>;
97
116
  readonly placeholder: _angular_core.Signal<string>;
98
117
  /** DevExtreme parity input; cosmetic, unused in behaviour. */
99
- readonly hoverStateEnabledInput: _angular_core.InputSignalWithTransform<boolean, unknown>;
118
+ readonly hoverStateEnabledInput: _angular_core.InputSignalWithTransform<boolean | undefined, unknown>;
100
119
  readonly hoverStateEnabled: _angular_core.Signal<boolean>;
101
120
  /** dx `showClearButton` — off by default, as in dx; `NXE_DEFAULT_OPTIONS.textBox` can turn it on app-wide. */
102
- readonly showClearButtonInput: _angular_core.InputSignalWithTransform<boolean, unknown>;
121
+ readonly showClearButtonInput: _angular_core.InputSignalWithTransform<boolean | undefined, unknown>;
103
122
  readonly showClearButton: _angular_core.Signal<boolean>;
123
+ /**
124
+ * With `mode="password"`, an icon button after the field that shows the password as text and
125
+ * hides it again. Off by default; it has no effect in any other mode.
126
+ */
127
+ readonly showPasswordToggle: _angular_core.InputSignalWithTransform<boolean, unknown>;
128
+ protected readonly passwordToggleVisible: _angular_core.Signal<boolean>;
129
+ /** Whether the password is showing as text right now; hidden again whenever the toggle goes away. */
130
+ protected readonly passwordRevealed: _angular_core.WritableSignal<boolean>;
131
+ protected readonly passwordToggleLabel: _angular_core.Signal<string>;
104
132
  readonly focused: _angular_core.WritableSignal<boolean>;
105
133
  readonly displayValue: _angular_core.Signal<string>;
106
134
  private readonly fieldRef;
@@ -119,9 +147,10 @@ declare class NxeTextBoxComponent extends NxeFormControlBase<string> {
119
147
  */
120
148
  protected onClear(event: Event): void;
121
149
  protected onTypedKeyup(event: Event): void;
150
+ protected commitPending(event: Event | null): void;
122
151
  protected onBlur(event: Event): void;
123
152
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeTextBoxComponent, never>;
124
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeTextBoxComponent, "nxe-text-box", never, { "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "placeholderInput": { "alias": "placeholder"; "required": false; "isSignal": true; }; "hoverStateEnabledInput": { "alias": "hoverStateEnabled"; "required": false; "isSignal": true; }; "showClearButtonInput": { "alias": "showClearButton"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
153
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeTextBoxComponent, "nxe-text-box", never, { "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "placeholderInput": { "alias": "placeholder"; "required": false; "isSignal": true; }; "hoverStateEnabledInput": { "alias": "hoverStateEnabled"; "required": false; "isSignal": true; }; "showClearButtonInput": { "alias": "showClearButton"; "required": false; "isSignal": true; }; "showPasswordToggle": { "alias": "showPasswordToggle"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
125
154
  }
126
155
 
127
156
  /**
@@ -132,19 +161,48 @@ declare class NxeTextBoxComponent extends NxeFormControlBase<string> {
132
161
  * {@link NXE_VALIDATION_TARGET}.
133
162
  */
134
163
  declare class NxeTextAreaComponent extends NxeFormControlBase<string> {
135
- readonly placeholderInput: _angular_core.InputSignal<string>;
164
+ readonly placeholderInput: _angular_core.InputSignal<string | undefined>;
136
165
  readonly placeholder: _angular_core.Signal<string>;
137
166
  /** Fixed height in px (DevExtreme `[height]`); unset = auto. */
138
- readonly heightInput: _angular_core.InputSignal<number>;
167
+ readonly heightInput: _angular_core.InputSignal<number | undefined>;
139
168
  readonly height: _angular_core.Signal<string | number>;
169
+ /**
170
+ * dx `maxLength`: the most characters the field takes. Sets the native `maxlength`, so typing
171
+ * and pasting stop at the limit, and shows a live "12/200" count under the text. Unset (the
172
+ * default), there is no limit and no counter.
173
+ */
174
+ readonly maxLength: _angular_core.InputSignalWithTransform<number | undefined, unknown>;
175
+ /**
176
+ * Grow with the text between {@link minRows} and {@link maxRows}, scrolling past the maximum.
177
+ * The fixed `height` is ignored while this is on. Off by default.
178
+ */
179
+ readonly autoGrow: _angular_core.InputSignalWithTransform<boolean, unknown>;
180
+ /** Rows the field never shrinks below while `autoGrow` is on. */
181
+ readonly minRows: _angular_core.InputSignalWithTransform<number, unknown>;
182
+ /** Rows after which an auto-growing field scrolls instead of growing; `0` means never. */
183
+ readonly maxRows: _angular_core.InputSignalWithTransform<number, unknown>;
140
184
  readonly focused: _angular_core.WritableSignal<boolean>;
141
185
  readonly displayValue: _angular_core.Signal<string>;
186
+ /** The text in the field right now, typed or not: the counter and the height follow every keystroke. */
187
+ protected readonly fieldText: _angular_core.WritableSignal<string>;
188
+ protected readonly remaining: _angular_core.Signal<number>;
189
+ /** Close enough to the limit to say so: the last 10 characters, or 10% of a long limit. */
190
+ protected readonly nearLimit: _angular_core.Signal<boolean>;
191
+ /** Spoken by the polite status; empty (so silent) until {@link nearLimit}. */
192
+ protected readonly limitStatus: _angular_core.Signal<string>;
193
+ private readonly growState;
194
+ constructor();
142
195
  /** dx `valueChangeEvent: 'input'` — the search-as-you-type case. */
143
196
  protected onTypedInput(event: Event): void;
197
+ /** Size the textarea to its text: collapse it to measure, then clamp to the row limits. */
198
+ private resize;
144
199
  protected onTypedKeyup(event: Event): void;
200
+ protected commitPending(event: Event | null): void;
201
+ /** Enter types a newline here and submits nothing; Ctrl/Cmd+Enter is the submit chord. */
202
+ protected commitsOnEnter(event: KeyboardEvent): boolean;
145
203
  protected onBlur(event: Event): void;
146
204
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeTextAreaComponent, never>;
147
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeTextAreaComponent, "nxe-text-area", never, { "placeholderInput": { "alias": "placeholder"; "required": false; "isSignal": true; }; "heightInput": { "alias": "height"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
205
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeTextAreaComponent, "nxe-text-area", never, { "placeholderInput": { "alias": "placeholder"; "required": false; "isSignal": true; }; "heightInput": { "alias": "height"; "required": false; "isSignal": true; }; "maxLength": { "alias": "maxLength"; "required": false; "isSignal": true; }; "autoGrow": { "alias": "autoGrow"; "required": false; "isSignal": true; }; "minRows": { "alias": "minRows"; "required": false; "isSignal": true; }; "maxRows": { "alias": "maxRows"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
148
206
  }
149
207
 
150
208
  /**
@@ -165,7 +223,7 @@ declare class NxeTextAreaComponent extends NxeFormControlBase<string> {
165
223
  */
166
224
  declare class NxeCheckBoxComponent extends NxeFormControlBase<boolean> implements OnChanges {
167
225
  /** Inline label rendered next to the box. */
168
- readonly text: _angular_core.InputSignal<string>;
226
+ readonly text: _angular_core.InputSignal<string | undefined>;
169
227
  /**
170
228
  * Whether a value has been handed in at all — by the forms API or a `[value]` binding.
171
229
  *
@@ -194,37 +252,83 @@ declare class NxeCheckBoxComponent extends NxeFormControlBase<boolean> implement
194
252
  * Supports `formControlName`/`[(ngModel)]`/`[(value)]`/`[value]`, `[readOnly]`, `[min]`/`[max]`,
195
253
  * `format` (string LDML pattern), `(valueChange)` (numeric), and a projected `nxe-validator`.
196
254
  *
255
+ * Stepping: `step` (default 1) moves the value by exact decimal arithmetic (`0.1` steps never show
256
+ * `0.30000000000000004`) on ArrowUp/ArrowDown (`largeStep`, default 10 steps, on PageUp/PageDown),
257
+ * and on the optional spin buttons (`showSpinButtons`; held buttons repeat unless
258
+ * `[holdToRepeat]="false"`). Results are clamped to `[min, max]` and rounded to the format precision.
259
+ * The input carries `role="spinbutton"` with `aria-valuenow`/`aria-valuemin`/`aria-valuemax`.
260
+ *
261
+ * `currency` (an ISO 4217 code) shows `IDR 1.351.000` / `USD 1,234.50` (separators from
262
+ * `NXE_NUMBER_SEPARATORS`, so it follows the locale) and takes the currency's minor units as the
263
+ * precision. An explicit `format` still sets the number pattern; `currencyDecimals` overrides the
264
+ * minor units (reference data). Focused, the field holds the bare editable number.
265
+ *
197
266
  * `showClearButton` (dx default `false`) adds dx's clear area after the input while the field
198
267
  * holds text and is neither read-only nor disabled; it sets the value to `null`, as dx's
199
268
  * `clear()` does for a number box, and focuses the input.
200
269
  */
201
270
  declare class NxeNumberBoxComponent extends NxeFormControlBase<number> {
202
271
  /** LDML format pattern (e.g. `#,##0.00`); empty = raw number. */
203
- readonly formatInput: _angular_core.InputSignal<string>;
272
+ readonly formatInput: _angular_core.InputSignal<string | undefined>;
204
273
  readonly format: _angular_core.Signal<string>;
205
- readonly min: _angular_core.InputSignalWithTransform<number, unknown>;
206
- readonly max: _angular_core.InputSignalWithTransform<number, unknown>;
274
+ readonly min: _angular_core.InputSignalWithTransform<number | undefined, unknown>;
275
+ readonly max: _angular_core.InputSignalWithTransform<number | undefined, unknown>;
207
276
  /** dx `showClearButton` — off by default, as in dx; `NXE_DEFAULT_OPTIONS.numberBox` can turn it on app-wide. */
208
- readonly showClearButtonInput: _angular_core.InputSignalWithTransform<boolean, unknown>;
277
+ readonly showClearButtonInput: _angular_core.InputSignalWithTransform<boolean | undefined, unknown>;
209
278
  readonly showClearButton: _angular_core.Signal<boolean>;
279
+ /** Amount of one step (ArrowUp/ArrowDown, spin buttons). Default 1; zero, negative or invalid means 1. */
280
+ readonly step: _angular_core.InputSignalWithTransform<number | undefined, unknown>;
281
+ /** PageUp/PageDown amount. Default 10 steps. */
282
+ readonly largeStep: _angular_core.InputSignalWithTransform<number | undefined, unknown>;
283
+ /** Show the increase/decrease buttons after the input. Default off, as in dx. */
284
+ readonly showSpinButtons: _angular_core.InputSignalWithTransform<boolean, unknown>;
285
+ /** Keep stepping while a spin button is held down. Default on. */
286
+ readonly holdToRepeat: _angular_core.InputSignalWithTransform<boolean, unknown>;
287
+ /** ISO 4217 code: shows `CODE amount` and takes the currency's minor units as precision. */
288
+ readonly currency: _angular_core.InputSignal<string | null | undefined>;
289
+ /** The currency's minor units from reference data; overrides the built-in table. */
290
+ readonly currencyDecimals: _angular_core.InputSignalWithTransform<number | undefined, unknown>;
210
291
  readonly focused: _angular_core.WritableSignal<boolean>;
292
+ private readonly currencyCode;
293
+ /** The explicit `format` input only, so a currency does not inherit the app-wide default pattern. */
294
+ private readonly explicitFormat;
211
295
  private readonly parsedFormat;
296
+ private readonly stepAmount;
297
+ private readonly largeStepAmount;
212
298
  private readonly fieldRef;
213
299
  /** Locale characters, configured once by the application. Defaults to en-US. */
214
300
  private readonly separators;
301
+ /** The blurred, formatted text of the committed value. */
302
+ private readonly formattedValue;
303
+ /** `aria-valuetext`: the formatted amount, only when a format or currency makes it differ from the number. */
304
+ protected readonly valueText: _angular_core.Signal<string | null>;
215
305
  readonly displayValue: _angular_core.Signal<string>;
216
306
  /** The text in the field right now; dx shows the clear button by the field's emptiness. */
217
307
  private readonly fieldText;
218
308
  protected readonly clearButtonVisible: _angular_core.Signal<boolean>;
309
+ protected readonly incrementDisabled: _angular_core.Signal<boolean>;
310
+ protected readonly decrementDisabled: _angular_core.Signal<boolean>;
311
+ private spinDelay;
312
+ private spinTimer;
313
+ constructor();
314
+ protected onKeyDownStep(event: KeyboardEvent): void;
315
+ protected onSpinPointerDown(event: PointerEvent, direction: 1 | -1): void;
316
+ /** Keyboard and assistive-technology activation (`detail` 0); pointer presses stepped on pointerdown. */
317
+ protected onSpinClick(event: MouseEvent, direction: 1 | -1): void;
318
+ protected stopSpin(): void;
319
+ /** Step the value; returns false when it did not move (a bound, or nothing to do). */
320
+ private stepBy;
219
321
  protected onFieldInput(event: Event): void;
220
322
  /**
221
323
  * dx `_clearValueHandler`: value to `null`, then focus on the input. The field is emptied by hand
222
324
  * too, for typed-but-uncommitted text over a value that was already `null`.
223
325
  */
224
326
  protected onClear(event: Event): void;
327
+ protected commitPending(event: Event | null): void;
225
328
  protected onBlur(event: Event): void;
329
+ private commitText;
226
330
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeNumberBoxComponent, never>;
227
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeNumberBoxComponent, "nxe-number-box", never, { "formatInput": { "alias": "format"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "showClearButtonInput": { "alias": "showClearButton"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
331
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeNumberBoxComponent, "nxe-number-box", never, { "formatInput": { "alias": "format"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "showClearButtonInput": { "alias": "showClearButton"; "required": false; "isSignal": true; }; "step": { "alias": "step"; "required": false; "isSignal": true; }; "largeStep": { "alias": "largeStep"; "required": false; "isSignal": true; }; "showSpinButtons": { "alias": "showSpinButtons"; "required": false; "isSignal": true; }; "holdToRepeat": { "alias": "holdToRepeat"; "required": false; "isSignal": true; }; "currency": { "alias": "currency"; "required": false; "isSignal": true; }; "currencyDecimals": { "alias": "currencyDecimals"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
228
332
  }
229
333
 
230
334
  /** DevExtreme date-box `type`. */
@@ -253,24 +357,26 @@ declare class NxeDateBoxComponent extends NxeFormControlBase<string> implements
253
357
  private readonly calendarTemplate;
254
358
  private readonly fieldRef;
255
359
  readonly type: _angular_core.InputSignal<NxeDateBoxType>;
256
- readonly displayFormat: _angular_core.InputSignal<string>;
257
- readonly placeholder: _angular_core.InputSignal<string>;
360
+ readonly displayFormat: _angular_core.InputSignal<string | undefined>;
361
+ readonly placeholder: _angular_core.InputSignal<string | undefined>;
258
362
  /** DevExtreme `[min]`/`[max]` bounds: calendar days outside are disabled, typed values clamp. */
259
- readonly min: _angular_core.InputSignal<string | Date>;
260
- readonly max: _angular_core.InputSignal<string | Date>;
363
+ readonly min: _angular_core.InputSignal<string | Date | null>;
364
+ readonly max: _angular_core.InputSignal<string | Date | null>;
261
365
  /** dx `showClearButton` — off by default, as in dx; `NXE_DEFAULT_OPTIONS.dateBox` can turn it on app-wide. */
262
- readonly showClearButtonInput: _angular_core.InputSignalWithTransform<boolean, unknown>;
366
+ readonly showClearButtonInput: _angular_core.InputSignalWithTransform<boolean | undefined, unknown>;
263
367
  readonly showClearButton: _angular_core.Signal<boolean>;
264
368
  readonly focused: _angular_core.WritableSignal<boolean>;
265
369
  readonly opened: _angular_core.WritableSignal<boolean>;
266
370
  readonly viewMonth: _angular_core.WritableSignal<DateTime<boolean>>;
267
371
  /** Calendar zoom level; caption clicks zoom out, cell picks zoom back in (dx-calendar parity). */
268
372
  readonly zoomLevel: _angular_core.WritableSignal<NxeCalendarZoom>;
269
- readonly weekdays: readonly ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
373
+ /** Short weekday names for the active locale, Sunday first (the grid below starts on Sunday). */
374
+ readonly weekdays: _angular_core.Signal<string[]>;
270
375
  /** The format actually used, from `displayFormat` or the default for this `type`. */
271
376
  readonly effectiveDisplayFormat: _angular_core.Signal<string>;
272
377
  /** dx's default placeholder is '', but orbit's global default is the format itself. */
273
378
  readonly effectivePlaceholder: _angular_core.Signal<string>;
379
+ /** The value as a Luxon date in the active locale, so `MMM` and `MMMM` follow the language. */
274
380
  private readonly selected;
275
381
  readonly displayValue: _angular_core.Signal<string>;
276
382
  /** The text in the field right now; dx shows the clear button by the field's emptiness. */
@@ -331,6 +437,9 @@ declare class NxeDateBoxComponent extends NxeFormControlBase<string> implements
331
437
  * re-render on its own and the unparseable text would sit there looking accepted.
332
438
  */
333
439
  protected onBlur(event: Event): void;
440
+ /** Enter commits what was typed, before a surrounding form submits; nothing typed means nothing to do. */
441
+ protected commitPending(event: Event | null): void;
442
+ private commitTypedText;
334
443
  /**
335
444
  * Repaint the field from the committed value and re-baseline {@link textAtFocus}.
336
445
  *
@@ -356,6 +465,222 @@ declare class NxeDateBoxComponent extends NxeFormControlBase<string> implements
356
465
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeDateBoxComponent, "nxe-date-box", never, { "type": { "alias": "type"; "required": false; "isSignal": true; }; "displayFormat": { "alias": "displayFormat"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "showClearButtonInput": { "alias": "showClearButton"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
357
466
  }
358
467
 
468
+ /**
469
+ * The value of `nxe-date-range-box`. Each end is a string in the box's `valueFormat`
470
+ * (`yyyy-MM-dd` unless stated), or `null` while that end is empty. A box with neither end set holds
471
+ * `null`, not an object of two nulls, so `Validators.required` means what it says.
472
+ */
473
+ interface NxeDateRange {
474
+ start: string | null;
475
+ end: string | null;
476
+ }
477
+ /** A date a preset may return: a string in any format `nxe-date-box` accepts, a `Date` or a Luxon `DateTime`. */
478
+ type NxeDateRangeBound = string | Date | DateTime;
479
+ /** What a preset's `range` returns. The box serializes it in its own `valueFormat`. */
480
+ interface NxeDateRangeBounds {
481
+ start: NxeDateRangeBound | null;
482
+ end: NxeDateRangeBound | null;
483
+ }
484
+ /** One entry of the box's quick-range list. */
485
+ interface NxeDateRangePreset {
486
+ /** The text of the entry. Wins over `labelKey`. */
487
+ label?: string;
488
+ /** A message key for the entry, so the label follows the active language. */
489
+ labelKey?: NxeMessageKey;
490
+ /**
491
+ * The range, computed when the entry is chosen. `now` is the box's clock (see
492
+ * {@link NXE_DATE_RANGE_NOW}), so a preset needs no clock of its own and a test can fix it.
493
+ */
494
+ range(now: DateTime): NxeDateRangeBounds;
495
+ }
496
+ /**
497
+ * The clock the box reads for "today": the highlighted day, where the popup opens and what the
498
+ * built-in presets count back from. Provide a fixed one in a test:
499
+ *
500
+ * ```ts
501
+ * { provide: NXE_DATE_RANGE_NOW, useValue: () => DateTime.fromISO('2026-10-07T09:30:00') }
502
+ * ```
503
+ */
504
+ declare const NXE_DATE_RANGE_NOW: InjectionToken<() => DateTime>;
505
+ /** Default `displayFormat`: `1 Okt 2026` in Indonesian, `1 Oct 2026` in English. */
506
+ declare const NXE_DATE_RANGE_DISPLAY_FORMAT = "d MMM yyyy";
507
+ /** Default `valueFormat`: date-only. */
508
+ declare const NXE_DATE_RANGE_VALUE_FORMAT = "yyyy-MM-dd";
509
+ /**
510
+ * The built-in presets. Each call returns a fresh entry whose label comes from the message bundle
511
+ * and whose range counts from the box's clock. Weeks are not offered: the first day of the week is
512
+ * a locale question this library does not decide.
513
+ */
514
+ declare const nxeDateRangePresets: {
515
+ today: () => NxeDateRangePreset;
516
+ yesterday: () => NxeDateRangePreset;
517
+ /** Today and the six days before it. */
518
+ last7Days: () => NxeDateRangePreset;
519
+ /** Today and the 29 days before it. */
520
+ last30Days: () => NxeDateRangePreset;
521
+ /** The first of the month to its last day (not to today). */
522
+ thisMonth: () => NxeDateRangePreset;
523
+ lastMonth: () => NxeDateRangePreset;
524
+ /** 1 January to 31 December. */
525
+ thisYear: () => NxeDateRangePreset;
526
+ /** All seven, in the order above. */
527
+ all: () => NxeDateRangePreset[];
528
+ };
529
+
530
+ /** One day in a month grid, with everything the template needs to draw and label it. */
531
+ interface RangeDayCell {
532
+ readonly date: DateTime;
533
+ /** `yyyy-MM-dd`: the cell's `data-date`, and a string that sorts as a date does. */
534
+ readonly key: string;
535
+ readonly day: number;
536
+ readonly label: string;
537
+ readonly disabled: boolean;
538
+ readonly today: boolean;
539
+ readonly start: boolean;
540
+ readonly end: boolean;
541
+ readonly inRange: boolean;
542
+ /** Drawn from the hover or keyboard preview, not from the committed value. */
543
+ readonly preview: boolean;
544
+ readonly tabbable: boolean;
545
+ }
546
+ interface RangeMonthView {
547
+ readonly key: string;
548
+ readonly caption: string;
549
+ /** Weeks of the month; `null` is a day of a neighbouring month, drawn as a gap. */
550
+ readonly weeks: readonly (readonly (RangeDayCell | null)[])[];
551
+ }
552
+ interface RangePresetItem {
553
+ readonly index: number;
554
+ readonly label: string;
555
+ readonly active: boolean;
556
+ }
557
+ /**
558
+ * `nxe-date-range-box` picks a start and an end date in one field.
559
+ *
560
+ * ```html
561
+ * <nxe-form-field label="Tanggal PO">
562
+ * <nxe-date-range-box formControlName="poDate" [presets]="presets" min="2020-01-01" />
563
+ * </nxe-form-field>
564
+ * ```
565
+ *
566
+ * The value is `{ start, end }`, each a string in `valueFormat` (`yyyy-MM-dd` by default; with a time
567
+ * format the start is the first second of its day and the end the last), `null` while that end is
568
+ * empty, and `null` as a whole when both are. The field shows `1 Okt 2026 - 31 Okt 2026` and opens
569
+ * a popup: two months side by side (one under 720px), a quick-range list, and a typed input for each
570
+ * end. The first click picks the start, the second the end; the days in between preview under the
571
+ * pointer, and a second click before the first swaps the two.
572
+ *
573
+ * Month and weekday names follow the active language of `NxeMessageService`. The field is a
574
+ * read-only combobox, so a label, `aria-invalid` and `aria-describedby` from `nxe-form-field` reach
575
+ * it; the popup is a dialog with the days in an ARIA grid (arrows, Home and End by week, PageUp and
576
+ * PageDown by month, Enter or Space to pick, Esc closes and returns focus to the field).
577
+ */
578
+ declare class NxeDateRangeBoxComponent extends NxeFormControlBase<NxeDateRange> implements OnDestroy {
579
+ private readonly overlay;
580
+ private readonly viewContainerRef;
581
+ private readonly injector;
582
+ private readonly clock;
583
+ private readonly breakpoints;
584
+ private readonly popupTemplate;
585
+ private readonly fieldRef;
586
+ /** How the dates read in the field and in the typed inputs: a Luxon format. */
587
+ readonly displayFormat: _angular_core.InputSignal<string>;
588
+ /** How each end is written to the value. Date-only unless it carries time tokens. */
589
+ readonly valueFormat: _angular_core.InputSignal<string>;
590
+ /** Days before this are disabled, and a typed or preset date before it moves to it. */
591
+ readonly min: _angular_core.InputSignal<string | Date | null>;
592
+ /** Days after this are disabled, and a typed or preset date after it moves to it. */
593
+ readonly max: _angular_core.InputSignal<string | Date | null>;
594
+ /** The quick-range list. Empty draws none. See `nxeDateRangePresets` for the built-in ones. */
595
+ readonly presets: _angular_core.InputSignal<readonly NxeDateRangePreset[]>;
596
+ /** Shown in the field and the start input while the start is empty. Defaults to the message bundle's. */
597
+ readonly startPlaceholder: _angular_core.InputSignal<string | undefined>;
598
+ /** Shown in the field and the end input while the end is empty. Defaults to the message bundle's. */
599
+ readonly endPlaceholder: _angular_core.InputSignal<string | undefined>;
600
+ /** The clear area in the field. On by default: a filter is cleared more often than a form field. */
601
+ readonly showClearButton: _angular_core.InputSignalWithTransform<boolean, unknown>;
602
+ protected readonly focused: _angular_core.WritableSignal<boolean>;
603
+ /** Whether the popup is showing. */
604
+ readonly opened: _angular_core.WritableSignal<boolean>;
605
+ private readonly uid;
606
+ protected readonly popupId: string;
607
+ protected readonly isNarrow: _angular_core.Signal<boolean>;
608
+ protected readonly monthsShown: _angular_core.Signal<1 | 2>;
609
+ private readonly locale;
610
+ private readonly minDate;
611
+ private readonly maxDate;
612
+ /** The committed value as days in the active locale. */
613
+ protected readonly range: _angular_core.Signal<{
614
+ start: DateTime<boolean> | null;
615
+ end: DateTime<boolean> | null;
616
+ }>;
617
+ protected readonly startText: _angular_core.Signal<string>;
618
+ protected readonly endText: _angular_core.Signal<string>;
619
+ protected readonly startHint: _angular_core.Signal<string>;
620
+ protected readonly endHint: _angular_core.Signal<string>;
621
+ /** `1 Okt 2026 - 31 Okt 2026`; an empty end leaves the dash hanging so a half-set range reads as one. */
622
+ protected readonly displayValue: _angular_core.Signal<string>;
623
+ protected readonly fieldPlaceholder: _angular_core.Signal<string>;
624
+ protected readonly clearButtonVisible: _angular_core.Signal<boolean>;
625
+ /** The first click of a range in progress; null when none is. */
626
+ protected readonly draftStart: _angular_core.WritableSignal<DateTime<boolean> | null>;
627
+ /** The day under the pointer or the keyboard while a range is in progress. */
628
+ protected readonly hovered: _angular_core.WritableSignal<DateTime<boolean> | null>;
629
+ /** The day the grid's single tab stop is on. */
630
+ private readonly activeDate;
631
+ /** The first month shown. */
632
+ protected readonly viewMonth: _angular_core.WritableSignal<DateTime<boolean>>;
633
+ protected readonly weekdays: _angular_core.Signal<{
634
+ short: string;
635
+ long: string;
636
+ }[]>;
637
+ private readonly presetResults;
638
+ protected readonly presetItems: _angular_core.Signal<RangePresetItem[]>;
639
+ /** The key of the grid's tab stop: the active day while it is on screen, else the first day shown. */
640
+ private readonly tabKey;
641
+ protected readonly months: _angular_core.Signal<RangeMonthView[]>;
642
+ protected readonly canGoBack: _angular_core.Signal<boolean>;
643
+ protected readonly canGoForward: _angular_core.Signal<boolean>;
644
+ /** The line under the grid, read out as it changes. */
645
+ protected readonly status: _angular_core.Signal<string>;
646
+ private overlayRef?;
647
+ private closeSub?;
648
+ private flipSub?;
649
+ protected onFieldClick(): void;
650
+ protected onFieldKeydown(event: KeyboardEvent): void;
651
+ protected onFocus(): void;
652
+ protected onBlur(): void;
653
+ protected onClearClick(event: Event): void;
654
+ /** Empties the range. The value becomes `null`. */
655
+ protected clear(event: Event | null): void;
656
+ protected pick(cell: RangeDayCell, event: Event): void;
657
+ protected onDayEnter(cell: RangeDayCell): void;
658
+ protected onDayFocus(cell: RangeDayCell): void;
659
+ /** The pointer left the grids: the preview falls back to the keyboard's day. */
660
+ protected onGridLeave(): void;
661
+ protected onDayKeydown(event: KeyboardEvent, cell: RangeDayCell): void;
662
+ private moveTo;
663
+ private ensureVisible;
664
+ private focusDay;
665
+ protected navigate(delta: number): void;
666
+ protected applyPreset(item: RangePresetItem, event: Event): void;
667
+ /** Commit the text typed into one end. Empty clears that end; unreadable text is put back. */
668
+ protected commitTyped(which: 'start' | 'end', event: Event): void;
669
+ protected onTypedKeydown(which: 'start' | 'end', event: KeyboardEvent): void;
670
+ protected onClearInPopup(event: Event): void;
671
+ private toDay;
672
+ private clampDay;
673
+ private serialize;
674
+ private commitRange;
675
+ /** Opens the popup on the start of the range (or today). No-op while open, disabled or read-only. */
676
+ open(): void;
677
+ /** Closes the popup, dropping a range that was only half picked. `restoreFocus` returns focus to the field. */
678
+ close(restoreFocus?: boolean): void;
679
+ ngOnDestroy(): void;
680
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeDateRangeBoxComponent, never>;
681
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeDateRangeBoxComponent, "nxe-date-range-box", never, { "displayFormat": { "alias": "displayFormat"; "required": false; "isSignal": true; }; "valueFormat": { "alias": "valueFormat"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "presets": { "alias": "presets"; "required": false; "isSignal": true; }; "startPlaceholder": { "alias": "startPlaceholder"; "required": false; "isSignal": true; }; "endPlaceholder": { "alias": "endPlaceholder"; "required": false; "isSignal": true; }; "showClearButton": { "alias": "showClearButton"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
682
+ }
683
+
359
684
  /**
360
685
  * `nxe-scroll-view` — drop-in replacement for DevExtreme `dx-scroll-view`.
361
686
  *
@@ -376,5 +701,325 @@ declare class NxeScrollViewComponent {
376
701
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeScrollViewComponent, "nxe-scroll-view", never, { "scrollByContent": { "alias": "scrollByContent"; "required": false; "isSignal": true; }; "scrollByThumb": { "alias": "scrollByThumb"; "required": false; "isSignal": true; }; "showScrollbar": { "alias": "showScrollbar"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
377
702
  }
378
703
 
379
- export { NxeButtonComponent, NxeCheckBoxComponent, NxeDateBoxComponent, NxeNumberBoxComponent, NxeScrollViewComponent, NxeTextAreaComponent, NxeTextBoxComponent };
380
- export type { NxeButtonClickEvent, NxeButtonType, NxeButtonValidationGroup, NxeDateBoxType, NxeTextBoxMode };
704
+ /**
705
+ * One validation message: a fixed string, or a function of the validator's error value (the
706
+ * object Angular puts under the validator key in `control.errors`, e.g. `{ requiredLength: 3,
707
+ * actualLength: 1 }` for `minlength`).
708
+ */
709
+ type NxeErrorMessage = string | ((error: any) => string);
710
+ /** Validator key (`required`, `minlength`, ...) to the message shown for it. */
711
+ type NxeErrorMessages = Readonly<Record<string, NxeErrorMessage>>;
712
+ /** Key used when a failing validator has no entry and carries no message of its own. */
713
+ declare const NXE_FALLBACK_ERROR_KEY = "invalid";
714
+ /** English defaults for Angular's built-in validators. */
715
+ declare const NXE_DEFAULT_ERROR_MESSAGES: NxeErrorMessages;
716
+ /**
717
+ * Validator-key to message map read by `nxe-form-field`.
718
+ *
719
+ * Provide a plain object to replace it; keys you leave out fall back to
720
+ * {@link NXE_DEFAULT_ERROR_MESSAGES}, so a partial map is fine. {@link provideNxeErrorMessages} is
721
+ * the short way to write the provider. A custom validator is covered by adding its key:
722
+ * `{ uen: 'Enter a valid UEN.' }`.
723
+ */
724
+ declare const NXE_ERROR_MESSAGES: InjectionToken<Readonly<Record<string, NxeErrorMessage>>>;
725
+ /**
726
+ * Registers messages for `nxe-form-field`, layered over the English defaults:
727
+ *
728
+ * ```ts
729
+ * provideNxeErrorMessages({
730
+ * required: 'Wajib diisi.',
731
+ * minlength: (e) => `Minimal ${e.requiredLength} karakter.`,
732
+ * })
733
+ * ```
734
+ */
735
+ declare function provideNxeErrorMessages(messages: NxeErrorMessages): EnvironmentProviders;
736
+
737
+ /**
738
+ * `nxe-form-field` wraps one `nxe-*` editor that is bound to a form control (`formControlName`,
739
+ * `[formControl]` or `ngModel`) and gives it the chrome a form needs:
740
+ *
741
+ * - a `label`, associated with the editor's inner control (`for` = the editor's `inputId`);
742
+ * - a required marker, from `Validators.required` on the control or the `required` input;
743
+ * - a `hint`, replaced by the error while one shows;
744
+ * - the message for the FIRST failing validator once the control is touched or its form submitted
745
+ * (`submitted` forces it), looked up in {@link NXE_ERROR_MESSAGES};
746
+ * - `serverErrors` / `serverError` from an API response, shown regardless of touched.
747
+ *
748
+ * It sets `aria-invalid`, `aria-required` and `aria-describedby` on the editor's focusable element
749
+ * and the editor host's `nxe-invalid` class, so the border, badge and colour come from the same
750
+ * `--nxe-ui-invalid*` tokens as an editor invalidated by `nxe-validator`.
751
+ *
752
+ * Reactivity is the control's own `events` stream (value, status, touched, submit, reset) feeding
753
+ * signals; the DOM sync runs through `afterRenderEffect`, which names the one computed it reads.
754
+ */
755
+ declare class NxeFormFieldComponent implements AfterContentInit {
756
+ private readonly destroyRef;
757
+ private readonly tokenMessages;
758
+ private readonly messageService;
759
+ /** The enclosing form, when there is one: a template-driven control joins it a tick late. */
760
+ private readonly container;
761
+ /** Caption above the editor. */
762
+ readonly label: _angular_core.InputSignal<string | undefined>;
763
+ /** Help text under the editor; hidden while an error is showing. */
764
+ readonly hint: _angular_core.InputSignal<string | undefined>;
765
+ /**
766
+ * Force the required marker on or off. Unset, it follows `Validators.required` (and
767
+ * `requiredTrue`) on the control. Needed for a rule the control does not declare as a validator.
768
+ */
769
+ readonly required: _angular_core.InputSignalWithTransform<boolean | undefined, unknown>;
770
+ /**
771
+ * API field errors, shown whether or not the control was touched. A string or a list of them;
772
+ * the app clears them (set `null`) when the user edits the field or resubmits.
773
+ */
774
+ readonly serverErrors: _angular_core.InputSignal<string | readonly string[] | null | undefined>;
775
+ /** Singular spelling of {@link serverErrors}; both may be set, their messages are joined. */
776
+ readonly serverError: _angular_core.InputSignal<string | readonly string[] | null | undefined>;
777
+ /**
778
+ * Show client-side errors now (`true`) or never (`false`). Unset, they show once the control is
779
+ * touched or the surrounding form was submitted.
780
+ */
781
+ readonly submitted: _angular_core.InputSignalWithTransform<boolean | undefined, unknown>;
782
+ /** Messages for this field only, by validator key; override {@link NXE_ERROR_MESSAGES}. */
783
+ readonly errorMessages: _angular_core.InputSignal<Readonly<Record<string, NxeErrorMessage>> | undefined>;
784
+ private readonly ngControl;
785
+ private readonly ngControlHost;
786
+ /**
787
+ * Set when the projected editor carries Angular's Signal Forms `[formField]`. That directive
788
+ * provides a stand-in `NgControl` with no `events` stream, so the field then reads the field
789
+ * state's signals instead of subscribing to a control.
790
+ */
791
+ private readonly formField;
792
+ private readonly formFieldHost;
793
+ private readonly editorHost;
794
+ private readonly fieldState;
795
+ /** Only read in dev mode, to catch a second editor the field would silently not bind. */
796
+ private readonly allControls;
797
+ private readonly touched;
798
+ private readonly controlErrors;
799
+ private readonly validatorRequired;
800
+ private readonly formSubmitted;
801
+ private readonly editor;
802
+ private readonly uid;
803
+ protected readonly labelId: string;
804
+ protected readonly hintId: string;
805
+ protected readonly errorId: string;
806
+ /** The id the label points at: the editor's inner control. Null for a non-nxe editor. */
807
+ protected readonly controlId: _angular_core.Signal<string | null>;
808
+ protected readonly isRequired: _angular_core.Signal<boolean>;
809
+ private readonly reveal;
810
+ private readonly clientMessage;
811
+ /** Everything shown under the editor as an error: the client message, then the server's. */
812
+ protected readonly messages: _angular_core.Signal<readonly string[]>;
813
+ readonly invalid: _angular_core.Signal<boolean>;
814
+ protected readonly showHint: _angular_core.Signal<boolean>;
815
+ private readonly domState;
816
+ /** Attributes this field put on the editor's element, so it only ever removes its own. */
817
+ private applied;
818
+ private invalidClassHost;
819
+ constructor();
820
+ ngAfterContentInit(): void;
821
+ private syncFromControl;
822
+ /**
823
+ * Message for one failing validator: this field's `errorMessages`, then an entry the app put in
824
+ * the {@link NXE_ERROR_MESSAGES} token, then the active `NxeMessageService` bundle (English
825
+ * unless the app switched language), then a string the validator itself returned
826
+ * (`{ uen: 'Not a valid UEN' }` or `{ uen: { message } }`), then the generic fallback.
827
+ *
828
+ * A token entry that IS the built-in default (the same reference, which is what
829
+ * `provideNxeErrorMessages` leaves for the keys it was not given) counts as unset, so it does
830
+ * not pin English over the bundle. Called from the `clientMessage` computed, so a language
831
+ * switch re-renders the error.
832
+ */
833
+ private resolve;
834
+ /**
835
+ * Message for a Signal Forms error `{ kind, message? }`. The kind is mapped to the validator keys
836
+ * above (`minLength` to `minlength`, `maxLength` to `maxlength`; `required`, `min`, `max`, `pattern`
837
+ * and `email` as they are). Lookup order: this field's `errorMessages`, an app token entry, the
838
+ * error's own `message`, the library bundle, then the fallback (`invalid`). So a rule written
839
+ * with a message (`required(p.name, { message: '...' })`) shows it, and a custom kind does too.
840
+ */
841
+ private resolveSignalError;
842
+ private explicitToken;
843
+ /** The library bundle's wording for Angular's built-in validators. */
844
+ private bundled;
845
+ /** The element a screen reader should treat as the field: the inner control, not the host. */
846
+ private focusable;
847
+ private setAttr;
848
+ private syncDom;
849
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeFormFieldComponent, never>;
850
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeFormFieldComponent, "nxe-form-field", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "serverErrors": { "alias": "serverErrors"; "required": false; "isSignal": true; }; "serverError": { "alias": "serverError"; "required": false; "isSignal": true; }; "submitted": { "alias": "submitted"; "required": false; "isSignal": true; }; "errorMessages": { "alias": "errorMessages"; "required": false; "isSignal": true; }; }, {}, ["ngControl", "ngControlHost", "formField", "formFieldHost", "allControls"], ["*"], true, never>;
851
+ }
852
+
853
+ /** What an `nxeRadioItem` template receives for each item. */
854
+ interface NxeRadioItemContext<T = unknown> {
855
+ /** The item, also available as `let-item`. */
856
+ $implicit: T;
857
+ item: T;
858
+ index: number;
859
+ selected: boolean;
860
+ disabled: boolean;
861
+ }
862
+ /**
863
+ * Marks the `<ng-template>` that renders one radio's content in `nxe-radio-group`, in place of the
864
+ * `displayExpr` text:
865
+ *
866
+ * ```html
867
+ * <nxe-radio-group [items]="plans" valueExpr="id">
868
+ * <ng-template nxeRadioItem let-plan let-selected="selected">
869
+ * <strong>{{ plan.name }}</strong> <small>{{ plan.price }}</small>
870
+ * </ng-template>
871
+ * </nxe-radio-group>
872
+ * ```
873
+ *
874
+ * The radio's accessible name is the template's text, so keep it to text and inline markup.
875
+ */
876
+ declare class NxeRadioItemDirective {
877
+ /** The template to render for each item. */
878
+ readonly template: TemplateRef<NxeRadioItemContext<unknown>>;
879
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeRadioItemDirective, never>;
880
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NxeRadioItemDirective, "ng-template[nxeRadioItem]", never, {}, {}, never, never, true, never>;
881
+ }
882
+
883
+ /** How the radios are laid out. */
884
+ type NxeRadioGroupLayout = 'horizontal' | 'vertical';
885
+ /** One radio, resolved from an item. */
886
+ interface RadioEntry {
887
+ readonly item: unknown;
888
+ readonly index: number;
889
+ readonly id: string;
890
+ readonly value: unknown;
891
+ readonly text: string;
892
+ readonly disabled: boolean;
893
+ }
894
+ /**
895
+ * `nxe-radio-group`: single choice among a few options, as the WAI-ARIA radio group pattern.
896
+ *
897
+ * A signal-CVA editor (extends {@link NxeFormControlBase}) working with `[(value)]`, `[(ngModel)]`
898
+ * and Reactive Forms. The options are `items`; `displayExpr` says what each shows (default: the
899
+ * item itself, for a list of strings), `valueExpr` what it puts in the control (default: the item
900
+ * itself) and `disabledExpr` (default `'disabled'`) which are not selectable. An
901
+ * `<ng-template nxeRadioItem>` replaces the text with your own content.
902
+ *
903
+ * Keyboard: the group is one tab stop, on the selected radio (or the first enabled one). Arrow keys
904
+ * move to the next or previous enabled radio, wrap around, and select it; Space selects the focused
905
+ * one. Left and Right are swapped in right-to-left text. A read-only group moves focus but never
906
+ * changes the value.
907
+ *
908
+ * The inner `role="radiogroup"` element carries the `inputId`, `aria-label` and `aria-labelledby`,
909
+ * so an `nxe-form-field` label names the group and its `aria-invalid` / `aria-describedby` land on
910
+ * it. Provides {@link NXE_VALIDATION_TARGET} for a projected `nxe-validator`.
911
+ */
912
+ declare class NxeRadioGroupComponent extends NxeFormControlBase<unknown> {
913
+ /** The options. */
914
+ readonly items: _angular_core.InputSignal<readonly unknown[]>;
915
+ /** Text of an item; unset, the item itself. */
916
+ readonly displayExpr: _angular_core.InputSignal<NxeExpr>;
917
+ /** The value an item stands for; unset, the item itself. */
918
+ readonly valueExpr: _angular_core.InputSignal<NxeExpr>;
919
+ /** Truthy for an item that cannot be chosen. */
920
+ readonly disabledExpr: _angular_core.InputSignal<NxeExpr>;
921
+ /** `vertical` stacks the radios, `horizontal` puts them in a row that wraps. */
922
+ readonly layout: _angular_core.InputSignal<NxeRadioGroupLayout>;
923
+ /** Custom radio content. */
924
+ readonly itemTemplate: _angular_core.Signal<NxeRadioItemDirective | undefined>;
925
+ protected readonly entries: _angular_core.Signal<readonly RadioEntry[]>;
926
+ /** Index of the checked radio, or -1. */
927
+ protected readonly selectedIndex: _angular_core.Signal<number>;
928
+ /** The one radio in the tab order: the checked one when it can be reached, else the first enabled. */
929
+ protected readonly tabbableIndex: _angular_core.Signal<number>;
930
+ protected onItemClick(index: number, event: Event): void;
931
+ protected onItemKeydown(index: number, event: KeyboardEvent): void;
932
+ /** Leaving the group, not moving between its radios, is what touches the control. */
933
+ protected onGroupFocusOut(event: FocusEvent): void;
934
+ /** Focus (and, unless read-only, select) the next enabled radio in `step` direction, wrapping. */
935
+ private move;
936
+ private select;
937
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeRadioGroupComponent, never>;
938
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeRadioGroupComponent, "nxe-radio-group", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "displayExpr": { "alias": "displayExpr"; "required": false; "isSignal": true; }; "valueExpr": { "alias": "valueExpr"; "required": false; "isSignal": true; }; "disabledExpr": { "alias": "disabledExpr"; "required": false; "isSignal": true; }; "layout": { "alias": "layout"; "required": false; "isSignal": true; }; }, {}, ["itemTemplate"], ["*"], true, never>;
939
+ }
940
+
941
+ /** Switch size; `medium` is the default. */
942
+ type NxeSwitchSize = 'small' | 'medium' | 'large';
943
+ /**
944
+ * `nxe-switch`: an on/off setting that takes effect as soon as it is flipped.
945
+ *
946
+ * A signal-CVA editor (extends {@link NxeFormControlBase}) over `boolean`; `null` and unset read as
947
+ * off. Works with `[(value)]`, `[(ngModel)]` and Reactive Forms. The host is the control:
948
+ * `role="switch"`, `aria-checked`, one tab stop; click and Space toggle it. A read-only switch is
949
+ * focusable but never flips; a disabled one leaves the tab order and dims.
950
+ *
951
+ * `text` is the label beside the track and names the switch (inside an `nxe-form-field` the field's
952
+ * label does instead). `onText` and `offText` are optional words drawn inside the track; they are
953
+ * decoration, hidden from assistive tech, because `aria-checked` already says which state it is in.
954
+ */
955
+ declare class NxeSwitchComponent extends NxeFormControlBase<boolean> {
956
+ /** Label beside the track. */
957
+ readonly text: _angular_core.InputSignal<string | undefined>;
958
+ /** Word shown inside the track while on. */
959
+ readonly onText: _angular_core.InputSignal<string | undefined>;
960
+ /** Word shown inside the track while off. */
961
+ readonly offText: _angular_core.InputSignal<string | undefined>;
962
+ /** Track size. */
963
+ readonly size: _angular_core.InputSignal<NxeSwitchSize>;
964
+ readonly checked: _angular_core.Signal<boolean>;
965
+ protected toggle(event: Event): void;
966
+ protected onSpace(event: Event): void;
967
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeSwitchComponent, never>;
968
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeSwitchComponent, "nxe-switch", never, { "text": { "alias": "text"; "required": false; "isSignal": true; }; "onText": { "alias": "onText"; "required": false; "isSignal": true; }; "offText": { "alias": "offText"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
969
+ }
970
+
971
+ /** Which characters a code may hold. */
972
+ type NxeOtpMode = 'numeric' | 'alphanumeric';
973
+ /**
974
+ * `nxe-otp-box`: a one-time code (2FA sign-in, e-mail verification, recovery code) drawn as one
975
+ * cell per character.
976
+ *
977
+ * It is ONE control, not N: a single transparent `<input>` lies over the cells and they only draw
978
+ * its text. That is what keeps a screen reader on one named field with one value, gives native
979
+ * paste, and lets the browser fill the whole code at once from an SMS or password manager
980
+ * (`autocomplete="one-time-code"`). Typing fills the next cell, Backspace empties the last one, and
981
+ * the caret is kept at the end, so the arrow keys do nothing. Pasting a code of `length` characters
982
+ * (or more) replaces the value, with separators such as a dash or space dropped; a shorter paste is
983
+ * appended.
984
+ *
985
+ * A signal-CVA over `string` (extends {@link NxeFormControlBase}); the value follows every
986
+ * keystroke, so a `minLength` validator sees a partial code and `completed` fires once with the full
987
+ * code. Name it with `aria-label`, `aria-labelledby` or an `nxe-form-field` label.
988
+ */
989
+ declare class NxeOtpBoxComponent extends NxeFormControlBase<string> {
990
+ /** Number of characters in the code. */
991
+ readonly length: _angular_core.InputSignalWithTransform<number, unknown>;
992
+ /** `numeric` (default) takes digits and opens the numeric keypad; `alphanumeric` takes letters and digits. */
993
+ readonly mode: _angular_core.InputSignal<NxeOtpMode>;
994
+ /** Focus the field when it first renders. */
995
+ readonly autoFocus: _angular_core.InputSignalWithTransform<boolean, unknown>;
996
+ /** The full code, once, when the last cell is filled (typed, pasted or auto-filled). */
997
+ readonly completed: _angular_core.OutputEmitterRef<string>;
998
+ readonly focused: _angular_core.WritableSignal<boolean>;
999
+ private readonly field;
1000
+ protected readonly displayValue: _angular_core.Signal<string>;
1001
+ protected readonly cells: _angular_core.Signal<{
1002
+ index: number;
1003
+ char: string;
1004
+ active: boolean;
1005
+ }[]>;
1006
+ constructor();
1007
+ /** Drop what the mode does not allow and cut to `length`. */
1008
+ private clean;
1009
+ /** Typing, deleting and autofill: the field's text is the new value. */
1010
+ protected handleInput(event: Event): void;
1011
+ /** A whole code replaces the value; a fragment is appended. */
1012
+ protected onPaste(event: ClipboardEvent): void;
1013
+ /** Keys that would move the caret off the end do nothing: the cells have no caret to move. */
1014
+ protected onKeydown(event: KeyboardEvent): void;
1015
+ protected onFocus(): void;
1016
+ protected onBlur(): void;
1017
+ /** A click or a drag could put the caret mid-code or select it; it always belongs at the end. */
1018
+ protected keepCaretAtEnd(): void;
1019
+ private apply;
1020
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NxeOtpBoxComponent, never>;
1021
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NxeOtpBoxComponent, "nxe-otp-box", never, { "length": { "alias": "length"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "autoFocus": { "alias": "autoFocus"; "required": false; "isSignal": true; }; }, { "completed": "completed"; }, never, ["*"], true, never>;
1022
+ }
1023
+
1024
+ export { NXE_DATE_RANGE_DISPLAY_FORMAT, NXE_DATE_RANGE_NOW, NXE_DATE_RANGE_VALUE_FORMAT, NXE_DEFAULT_ERROR_MESSAGES, NXE_ERROR_MESSAGES, NXE_FALLBACK_ERROR_KEY, NxeButtonComponent, NxeCheckBoxComponent, NxeDateBoxComponent, NxeDateRangeBoxComponent, NxeFormFieldComponent, NxeNumberBoxComponent, NxeOtpBoxComponent, NxeRadioGroupComponent, NxeRadioItemDirective, NxeScrollViewComponent, NxeSwitchComponent, NxeTextAreaComponent, NxeTextBoxComponent, nxeDateRangePresets, provideNxeErrorMessages };
1025
+ export type { NxeButtonClickEvent, NxeButtonSize, NxeButtonStylingMode, NxeButtonType, NxeButtonValidationGroup, NxeDateBoxType, NxeDateRange, NxeDateRangeBound, NxeDateRangeBounds, NxeDateRangePreset, NxeErrorMessage, NxeErrorMessages, NxeOtpMode, NxeRadioGroupLayout, NxeRadioItemContext, NxeSwitchSize, NxeTextBoxMode };