@ngneers/controls 0.0.1-next.0 → 0.0.1-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/fesm2022/ngneers-controls-api-ng.mjs +11 -18
  2. package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -1
  3. package/fesm2022/ngneers-controls-api-resize.mjs +7 -0
  4. package/fesm2022/ngneers-controls-api-resize.mjs.map +1 -1
  5. package/fesm2022/ngneers-controls-badge.mjs +160 -0
  6. package/fesm2022/ngneers-controls-badge.mjs.map +1 -0
  7. package/fesm2022/ngneers-controls-base.mjs +85 -8
  8. package/fesm2022/ngneers-controls-base.mjs.map +1 -1
  9. package/fesm2022/{ngneers-controls-calendar-time-CJT5AFlA.mjs → ngneers-controls-calendar-time-paM6WVpV.mjs} +2 -2
  10. package/fesm2022/{ngneers-controls-calendar-time-CJT5AFlA.mjs.map → ngneers-controls-calendar-time-paM6WVpV.mjs.map} +1 -1
  11. package/fesm2022/ngneers-controls-calendar.mjs +13 -6
  12. package/fesm2022/ngneers-controls-calendar.mjs.map +1 -1
  13. package/fesm2022/ngneers-controls-checkbox.mjs +3 -3
  14. package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -1
  15. package/fesm2022/ngneers-controls-color-picker.mjs +336 -0
  16. package/fesm2022/ngneers-controls-color-picker.mjs.map +1 -0
  17. package/fesm2022/ngneers-controls-default-icons.mjs +5 -0
  18. package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -1
  19. package/fesm2022/ngneers-controls-directives.mjs +64 -7
  20. package/fesm2022/ngneers-controls-directives.mjs.map +1 -1
  21. package/fesm2022/ngneers-controls-edit-inplace.mjs +15 -5
  22. package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -1
  23. package/fesm2022/ngneers-controls-errors.mjs +203 -148
  24. package/fesm2022/ngneers-controls-errors.mjs.map +1 -1
  25. package/fesm2022/ngneers-controls-filter.mjs +2 -2
  26. package/fesm2022/ngneers-controls-filter.mjs.map +1 -1
  27. package/fesm2022/ngneers-controls-hint.mjs +18 -1
  28. package/fesm2022/ngneers-controls-hint.mjs.map +1 -1
  29. package/fesm2022/ngneers-controls-i18n-translations-de.mjs +17 -0
  30. package/fesm2022/ngneers-controls-i18n-translations-de.mjs.map +1 -1
  31. package/fesm2022/ngneers-controls-i18n-translations-en.mjs +19 -0
  32. package/fesm2022/ngneers-controls-i18n-translations-en.mjs.map +1 -1
  33. package/fesm2022/ngneers-controls-icon.mjs +23 -6
  34. package/fesm2022/ngneers-controls-icon.mjs.map +1 -1
  35. package/fesm2022/ngneers-controls-input-field.mjs +18 -2
  36. package/fesm2022/ngneers-controls-input-field.mjs.map +1 -1
  37. package/fesm2022/ngneers-controls-input.mjs +49 -9
  38. package/fesm2022/ngneers-controls-input.mjs.map +1 -1
  39. package/fesm2022/ngneers-controls-list-box.mjs +2 -2
  40. package/fesm2022/ngneers-controls-list-box.mjs.map +1 -1
  41. package/fesm2022/ngneers-controls-mask-input.mjs +4 -1
  42. package/fesm2022/ngneers-controls-mask-input.mjs.map +1 -1
  43. package/fesm2022/ngneers-controls-number-input.mjs +4 -4
  44. package/fesm2022/ngneers-controls-number-input.mjs.map +1 -1
  45. package/fesm2022/ngneers-controls-otp.mjs +263 -0
  46. package/fesm2022/ngneers-controls-otp.mjs.map +1 -0
  47. package/fesm2022/ngneers-controls-paginator.mjs +36 -9
  48. package/fesm2022/ngneers-controls-paginator.mjs.map +1 -1
  49. package/fesm2022/ngneers-controls-radio.mjs +4 -4
  50. package/fesm2022/ngneers-controls-radio.mjs.map +1 -1
  51. package/fesm2022/ngneers-controls-rating.mjs +180 -0
  52. package/fesm2022/ngneers-controls-rating.mjs.map +1 -0
  53. package/fesm2022/ngneers-controls-scroll-shadow.mjs +33 -7
  54. package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -1
  55. package/fesm2022/ngneers-controls-scroller.mjs +4 -2
  56. package/fesm2022/ngneers-controls-scroller.mjs.map +1 -1
  57. package/fesm2022/ngneers-controls-select-button.mjs +2 -1
  58. package/fesm2022/ngneers-controls-select-button.mjs.map +1 -1
  59. package/fesm2022/ngneers-controls-select.mjs +24 -4
  60. package/fesm2022/ngneers-controls-select.mjs.map +1 -1
  61. package/fesm2022/ngneers-controls-slider.mjs +3 -3
  62. package/fesm2022/ngneers-controls-slider.mjs.map +1 -1
  63. package/fesm2022/ngneers-controls-splitter.mjs +43 -12
  64. package/fesm2022/ngneers-controls-splitter.mjs.map +1 -1
  65. package/fesm2022/ngneers-controls-stepper.mjs +142 -0
  66. package/fesm2022/ngneers-controls-stepper.mjs.map +1 -0
  67. package/fesm2022/ngneers-controls-switch.mjs +3 -3
  68. package/fesm2022/ngneers-controls-switch.mjs.map +1 -1
  69. package/fesm2022/ngneers-controls-table.mjs +282 -15
  70. package/fesm2022/ngneers-controls-table.mjs.map +1 -1
  71. package/fesm2022/ngneers-controls-tabs.mjs +2 -2
  72. package/fesm2022/ngneers-controls-tabs.mjs.map +1 -1
  73. package/fesm2022/ngneers-controls-toggle-button.mjs +3 -3
  74. package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -1
  75. package/fesm2022/ngneers-controls-tooltip.mjs +16 -28
  76. package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -1
  77. package/fesm2022/ngneers-controls-tree.mjs +2 -2
  78. package/fesm2022/ngneers-controls-tree.mjs.map +1 -1
  79. package/fesm2022/ngneers-controls-utils.mjs +138 -1
  80. package/fesm2022/ngneers-controls-utils.mjs.map +1 -1
  81. package/package.json +22 -2
  82. package/types/ngneers-controls-api-ng.d.ts +12 -9
  83. package/types/ngneers-controls-badge.d.ts +67 -0
  84. package/types/ngneers-controls-base.d.ts +61 -4
  85. package/types/ngneers-controls-breadcrumb.d.ts +17 -0
  86. package/types/ngneers-controls-calendar.d.ts +24 -6
  87. package/types/ngneers-controls-chip.d.ts +17 -0
  88. package/types/ngneers-controls-color-picker.d.ts +119 -0
  89. package/types/ngneers-controls-dialog.d.ts +17 -0
  90. package/types/ngneers-controls-directives.d.ts +31 -2
  91. package/types/ngneers-controls-drawer.d.ts +17 -0
  92. package/types/ngneers-controls-edit-inplace.d.ts +23 -0
  93. package/types/ngneers-controls-errors.d.ts +105 -16
  94. package/types/ngneers-controls-filter.d.ts +30 -13
  95. package/types/ngneers-controls-hint.d.ts +8 -1
  96. package/types/ngneers-controls-i18n-translations-de.d.ts +17 -0
  97. package/types/ngneers-controls-i18n-translations-en.d.ts +17 -0
  98. package/types/ngneers-controls-i18n.d.ts +17 -0
  99. package/types/ngneers-controls-icon.d.ts +2 -2
  100. package/types/ngneers-controls-input-field.d.ts +28 -2
  101. package/types/ngneers-controls-input.d.ts +35 -3
  102. package/types/ngneers-controls-list-box.d.ts +17 -0
  103. package/types/ngneers-controls-mask-input.d.ts +17 -0
  104. package/types/ngneers-controls-otp.d.ts +262 -0
  105. package/types/ngneers-controls-paginator.d.ts +34 -3
  106. package/types/ngneers-controls-rating.d.ts +69 -0
  107. package/types/ngneers-controls-scroll-shadow.d.ts +11 -2
  108. package/types/ngneers-controls-scroller.d.ts +3 -1
  109. package/types/ngneers-controls-select.d.ts +30 -3
  110. package/types/ngneers-controls-snackbar.d.ts +17 -0
  111. package/types/ngneers-controls-splitter.d.ts +26 -2
  112. package/types/ngneers-controls-state.d.ts +2 -2
  113. package/types/ngneers-controls-stepper.d.ts +68 -0
  114. package/types/ngneers-controls-table.d.ts +276 -169
  115. package/types/ngneers-controls-toast.d.ts +17 -0
  116. package/types/ngneers-controls-tooltip.d.ts +0 -1
  117. package/types/ngneers-controls-tree.d.ts +17 -0
  118. package/types/ngneers-controls-upload.d.ts +17 -0
  119. package/types/ngneers-controls-utils.d.ts +27 -2
@@ -1,8 +1,76 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, input, signal, computed, DestroyRef, effect, Directive } from '@angular/core';
2
+ import { inject, DestroyRef, signal, computed, InjectionToken, input, effect, Directive } from '@angular/core';
3
3
  import { NgControl, ControlContainer } from '@angular/forms';
4
4
  import { NGN_CONTROL } from '@ngneers/controls/base';
5
5
  import '@ngneers/controls/hint';
6
+ import { I18n } from '@ngneers/controls/i18n';
7
+
8
+ /**
9
+ * Resolves and reactively tracks the {@link NgnControlState} of the host
10
+ * element. Must be called in an injection context (field/directive constructor).
11
+ */
12
+ function injectNgnControlState() {
13
+ const ngControl = inject(NgControl, { optional: true, self: true });
14
+ const selfContainer = inject(ControlContainer, { optional: true, self: true });
15
+ const parentContainer = inject(ControlContainer, { optional: true, skipSelf: true });
16
+ const ngnControl = inject(NGN_CONTROL, { optional: true, self: true });
17
+ const destroyRef = inject(DestroyRef);
18
+ const revision = signal(0, /* @ts-ignore */
19
+ ...(ngDevMode ? [{ debugName: "revision" }] : /* istanbul ignore next */ []));
20
+ const bump = () => revision.update(value => value + 1);
21
+ const host = () => ngControl?.control ?? selfContainer?.control ?? null;
22
+ const parent = () => ngControl?.control?.parent ?? parentContainer?.control ?? null;
23
+ const root = () => host()?.root ?? parent()?.root ?? null;
24
+ // No-form touched: prefer the control's own `touched` signal (also reflects an
25
+ // external `[touched]` binding), and honor a `touch` output emission (blur) for
26
+ // controls that report blur only that way.
27
+ const touchedFromOutput = signal(false, /* @ts-ignore */
28
+ ...(ngDevMode ? [{ debugName: "touchedFromOutput" }] : /* istanbul ignore next */ []));
29
+ const touch = ngnControl?.touch;
30
+ const touchSubscription = touch?.subscribe(() => touchedFromOutput.set(true));
31
+ if (touchSubscription) {
32
+ destroyRef.onDestroy(() => touchSubscription.unsubscribe());
33
+ }
34
+ const touchedFallback = () => {
35
+ const value = ngnControl?.touched;
36
+ const fromSignal = typeof value === 'function' ? Boolean(value()) : Boolean(value);
37
+ return fromSignal || touchedFromOutput();
38
+ };
39
+ // Controls resolve their `AbstractControl` after the value accessor registers,
40
+ // so defer a microtask, then watch each distinct control's unified `events`.
41
+ queueMicrotask(() => {
42
+ const controls = new Set([host(), parent(), root()].filter((control) => !!control));
43
+ for (const control of controls) {
44
+ const subscription = control.events?.subscribe(bump);
45
+ if (subscription) {
46
+ destroyRef.onDestroy(() => subscription.unsubscribe());
47
+ }
48
+ }
49
+ bump();
50
+ });
51
+ const submitted = () => {
52
+ const formDirective = selfContainer?.formDirective ?? parentContainer?.formDirective;
53
+ const source = (formDirective ?? selfContainer ?? parentContainer);
54
+ return source?.submitted ?? false;
55
+ };
56
+ const dirtyFallback = () => {
57
+ const value = ngnControl?.dirty;
58
+ return typeof value === 'function' ? Boolean(value()) : Boolean(value);
59
+ };
60
+ return {
61
+ name: ngControl?.name ?? null,
62
+ errors: computed(() => (revision(), host()?.errors ?? null)),
63
+ parentErrors: computed(() => {
64
+ revision();
65
+ const parentControl = parent();
66
+ return parentControl && parentControl !== host() ? (parentControl.errors ?? null) : null;
67
+ }),
68
+ touched: computed(() => (revision(), host()?.touched ?? touchedFallback())),
69
+ dirty: computed(() => (revision(), host()?.dirty ?? dirtyFallback())),
70
+ pending: computed(() => (revision(), host()?.pending ?? false)),
71
+ submitted: computed(() => (revision(), submitted())),
72
+ };
73
+ }
6
74
 
7
75
  /**
8
76
  * Multi-provider token for validation error message maps.
@@ -29,42 +97,33 @@ function provideNgnErrorsMessages(messages) {
29
97
  function injectNgnErrorsMessages() {
30
98
  return Object.assign({}, ...inject(NGN_ERRORS_MESSAGES));
31
99
  }
32
- const defaultNgnErrorsMessages = {
33
- required: 'Required',
34
- email: 'Enter a valid email address',
35
- minlength: ({ params }) => {
36
- const requiredLength = params['requiredLength'];
37
- return `Use at least ${requiredLength} characters`;
38
- },
39
- maxlength: ({ params }) => {
40
- const requiredLength = params['requiredLength'];
41
- return `Use at most ${requiredLength} characters`;
42
- },
43
- min: ({ params }) => {
44
- const min = params['min'];
45
- return `Must be at least ${min}`;
46
- },
47
- max: ({ params }) => {
48
- const max = params['max'];
49
- return `Must be at most ${max}`;
50
- },
51
- pattern: 'Invalid format',
52
- };
53
- function resolveNgnErrorMessage(context, messages) {
100
+ /**
101
+ * Resolves a message for the context's key from a user-provided message map,
102
+ * or `undefined` if the map has no (non-null) entry for it. Function resolvers
103
+ * that return `null`/`undefined` are treated as "no message" so resolution can
104
+ * fall through to the next source.
105
+ */
106
+ function resolveUserMessage(context, messages) {
54
107
  const resolver = messages[context.key];
108
+ // Empty strings count as "no message" so resolution falls through to the next source.
55
109
  if (typeof resolver === 'function') {
56
- return resolver(context) ?? context.key;
57
- }
58
- if (resolver) {
59
- return resolver;
110
+ return resolver(context) || undefined;
60
111
  }
61
- if (typeof context.value === 'string') {
62
- return context.value;
112
+ return resolver || undefined;
113
+ }
114
+ /**
115
+ * A message carried on the error itself — a plain-string error value, or a
116
+ * `message` field on the error object (e.g. a signal-forms validator's
117
+ * `{ message }`, or a group error). `undefined` when the error carries none.
118
+ */
119
+ function carriedMessage(value) {
120
+ if (typeof value === 'string') {
121
+ return value;
63
122
  }
64
- if (isRecord(context.value) && typeof context.value['message'] === 'string') {
65
- return context.value['message'];
123
+ if (isRecord(value) && typeof value['message'] === 'string') {
124
+ return value['message'];
66
125
  }
67
- return context.key;
126
+ return undefined;
68
127
  }
69
128
  function paramsFromValue(value) {
70
129
  if (!isRecord(value)) {
@@ -79,6 +138,24 @@ function isControlNameKey(key) {
79
138
  return ['control', 'controlName', 'controls', 'controlNames', 'field', 'fields'].includes(key);
80
139
  }
81
140
 
141
+ /**
142
+ * Resolves validation errors for the control it sits on and exposes them as
143
+ * normalized, message-mapped signals — ready to bridge into an {@link NgnHint}
144
+ * via {@link NgnErrors.ngnErrorsHint}.
145
+ *
146
+ * Which form paradigm is in play is abstracted away by {@link injectNgnControlState}:
147
+ * template-driven (`ngModel`), reactive (`formControl` / `formControlName`) and
148
+ * signal forms (`[formField]`) all surface as one reactive state, including
149
+ * relevant parent-group errors and a no-form `touched` fallback.
150
+ *
151
+ * `ngnErrorsCustom` layers on additional errors independently of any form, and
152
+ * {@link NgnErrors.ngnErrorsShowOn} controls when messages surface (defaults to
153
+ * `touched`). Error keys map onto the shared message table; signal-forms
154
+ * `minLength` / `maxLength` kinds have their own table entries alongside the
155
+ * classic lowercase `minlength` / `maxlength`.
156
+ *
157
+ * @category control
158
+ */
82
159
  class NgnErrors {
83
160
  /**
84
161
  * Hint instance that receives the resolved validation state.
@@ -110,55 +187,57 @@ class NgnErrors {
110
187
  */
111
188
  ngnErrorsCustom = input(null, /* @ts-ignore */
112
189
  ...(ngDevMode ? [{ debugName: "ngnErrorsCustom" }] : /* istanbul ignore next */ []));
113
- _ngControl = inject(NgControl, { optional: true, self: true });
114
- _selfContainer = inject(ControlContainer, { optional: true, self: true });
115
- _parentContainer = inject(ControlContainer, { optional: true, skipSelf: true });
116
- _ngnControl = inject(NGN_CONTROL, {
117
- optional: true,
118
- self: true,
119
- });
190
+ /** Paradigm-agnostic view of the host control (see {@link injectNgnControlState}). */
191
+ _state = injectNgnControlState();
192
+ _i18n = inject(I18n).translations;
120
193
  _globalMessages = injectNgnErrorsMessages();
121
- _version = signal(0, /* @ts-ignore */
122
- ...(ngDevMode ? [{ debugName: "_version" }] : /* istanbul ignore next */ []));
123
- errors = computed(() => {
124
- this._version();
125
- const messages = {
126
- ...defaultNgnErrorsMessages,
127
- ...this._globalMessages,
128
- ...this.ngnErrorsMessages(),
129
- };
130
- return [
131
- ...this._normalizeErrors(this._hostControl()?.errors, 'control', messages),
132
- ...this._normalizeGroupErrors(messages),
133
- ...this._normalizeCustomErrors(messages),
134
- ];
135
- }, /* @ts-ignore */
194
+ errors = computed(() => [
195
+ ...this._normalizeErrors(this._state.errors(), 'control'),
196
+ ...this._normalizeGroupErrors(),
197
+ ...this._normalizeCustomErrors(),
198
+ ], /* @ts-ignore */
136
199
  ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
137
200
  firstError = computed(() => this.errors()[0] ?? null, /* @ts-ignore */
138
201
  ...(ngDevMode ? [{ debugName: "firstError" }] : /* istanbul ignore next */ []));
139
- pending = computed(() => {
140
- this._version();
141
- return this._hostControl()?.pending ?? false;
142
- }, /* @ts-ignore */
202
+ pending = computed(() => this._state.pending(), /* @ts-ignore */
143
203
  ...(ngDevMode ? [{ debugName: "pending" }] : /* istanbul ignore next */ []));
204
+ /**
205
+ * Whether the i18n error defaults are loaded. Probes a core key that every
206
+ * locale defines — until the async translation import lands, `_translate`
207
+ * echoes the key path back, which this detects.
208
+ */
209
+ _i18nReady = computed(() => this._translate('required', {}) !== undefined, /* @ts-ignore */
210
+ ...(ngDevMode ? [{ debugName: "_i18nReady" }] : /* istanbul ignore next */ []));
211
+ /**
212
+ * Whether display should wait: some visible error depends on the i18n default
213
+ * (no instance / carried / global message) and translations aren't loaded yet.
214
+ */
215
+ _awaitingI18n = computed(() => !this._i18nReady() && this.errors().some(error => this._dependsOnI18n(error)), /* @ts-ignore */
216
+ ...(ngDevMode ? [{ debugName: "_awaitingI18n" }] : /* istanbul ignore next */ []));
144
217
  visible = computed(() => {
145
218
  if (!this.errors().length && !this.pending()) {
146
219
  return false;
147
220
  }
148
- const control = this._hostControl();
149
- const showOn = this.ngnErrorsShowOn();
150
- switch (showOn) {
221
+ // Hold error display until the i18n defaults have loaded, so a default
222
+ // message never flashes as its raw key ("required") before the async
223
+ // translation import lands. Only affects errors with no instance/carried/
224
+ // global message (those show immediately); pending always shows (it has a
225
+ // hardcoded fallback). See {@link _awaitingI18n}.
226
+ if (!this.pending() && this._awaitingI18n()) {
227
+ return false;
228
+ }
229
+ switch (this.ngnErrorsShowOn()) {
151
230
  case 'always':
152
231
  return true;
153
232
  case 'never':
154
233
  return false;
155
234
  case 'dirty':
156
- return control?.dirty ?? readNgnBoolean(this._ngnControl, 'dirty');
235
+ return this._state.dirty();
157
236
  case 'submitted':
158
- return this._submitted();
237
+ return this._state.submitted();
159
238
  case 'touched':
160
239
  default:
161
- return control?.touched ?? readNgnBoolean(this._ngnControl, 'touched');
240
+ return this._state.touched();
162
241
  }
163
242
  }, /* @ts-ignore */
164
243
  ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
@@ -167,7 +246,7 @@ class NgnErrors {
167
246
  return null;
168
247
  }
169
248
  if (this.pending()) {
170
- return 'Validating...';
249
+ return this._translate('pending', {}) ?? 'Validating...';
171
250
  }
172
251
  const errors = this.errors();
173
252
  if (this.ngnErrorsMode() === 'all') {
@@ -185,107 +264,87 @@ class NgnErrors {
185
264
  }), /* @ts-ignore */
186
265
  ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
187
266
  constructor() {
188
- const destroyRef = inject(DestroyRef);
189
- queueMicrotask(() => {
190
- const controls = new Set([this._hostControl(), this._parentControl(), this._rootControl()].filter((control) => !!control));
191
- for (const control of controls) {
192
- this._watchControl(control, destroyRef);
193
- }
194
- });
267
+ // `ngnErrors` renders the error *message* only — it never touches invalid
268
+ // styling. The control owns its invalid border (via its own `invalidOn`
269
+ // trigger), and the input field mirrors its child. See {@link NgnErrors}.
195
270
  effect(() => {
196
271
  this.ngnErrorsHint()?.setValidationState(this.state());
197
272
  });
198
273
  }
199
- _watchControl(control, destroyRef) {
200
- if (!control) {
201
- return;
202
- }
203
- const markChanged = () => this._version.update(value => value + 1);
204
- const statusSubscription = control.statusChanges?.subscribe(markChanged);
205
- const valueSubscription = control.valueChanges?.subscribe(markChanged);
206
- const eventsSubscription = control.events?.subscribe(markChanged);
207
- destroyRef.onDestroy(() => {
208
- statusSubscription?.unsubscribe();
209
- valueSubscription?.unsubscribe();
210
- eventsSubscription?.unsubscribe();
211
- });
212
- markChanged();
213
- }
214
- _hostControl() {
215
- return this._ngControl?.control ?? this._selfContainer?.control ?? null;
216
- }
217
- _parentControl() {
218
- return this._ngControl?.control?.parent ?? this._parentContainer?.control ?? null;
219
- }
220
- _rootControl() {
221
- return this._hostControl()?.root ?? this._parentControl()?.root ?? null;
222
- }
223
- _submittedSource() {
224
- const formDirective = this._selfContainer?.formDirective ?? this._parentContainer?.formDirective;
225
- return (formDirective ?? this._selfContainer ?? this._parentContainer);
226
- }
227
- _submitted() {
228
- return this._submittedSource()?.submitted ?? false;
229
- }
230
- _normalizeGroupErrors(messages) {
231
- const parent = this._parentControl();
232
- const host = this._hostControl();
233
- if (!parent || parent === host) {
234
- return [];
235
- }
236
- return this._normalizeErrors(parent.errors, 'group', messages).filter(error => this._isGroupErrorRelevant(error.value));
274
+ _normalizeGroupErrors() {
275
+ return this._normalizeErrors(this._state.parentErrors(), 'group').filter(error => this._isGroupErrorRelevant(error.value));
237
276
  }
238
- _normalizeCustomErrors(messages) {
277
+ _normalizeCustomErrors() {
239
278
  const errors = this.ngnErrorsCustom();
240
279
  if (!errors) {
241
280
  return [];
242
281
  }
243
282
  if (Array.isArray(errors)) {
244
- return errors.map(error => {
245
- if (typeof error === 'string') {
246
- return this._createError(error, true, 'custom', messages);
247
- }
248
- return this._createCustomEntryError(error, messages);
249
- });
283
+ return errors.map(error => typeof error === 'string'
284
+ ? this._createError(error, true, 'custom')
285
+ : this._createCustomEntryError(error));
250
286
  }
251
- return this._normalizeErrors(errors, 'custom', messages);
287
+ return this._normalizeErrors(errors, 'custom');
252
288
  }
253
- _normalizeErrors(errors, source, messages) {
289
+ _normalizeErrors(errors, source) {
254
290
  if (!errors) {
255
291
  return [];
256
292
  }
257
- return Object.entries(errors).map(([key, value]) => this._createError(key, value, source, messages));
293
+ return Object.entries(errors).map(([key, value]) => this._createError(key, value, source));
258
294
  }
259
- _createCustomEntryError(error, messages) {
295
+ _createCustomEntryError(error) {
260
296
  const value = error.value ?? true;
261
297
  const params = error.params ?? paramsFromValue(value);
262
- const message = error.message ??
263
- resolveNgnErrorMessage({
264
- key: error.key,
265
- value,
266
- source: 'custom',
267
- params,
268
- }, messages);
269
- return {
270
- key: error.key,
271
- value,
272
- source: 'custom',
273
- params,
274
- message,
275
- };
298
+ const context = { key: error.key, value, source: 'custom', params };
299
+ const message = error.message ?? this._resolveMessage(context);
300
+ return { key: error.key, value, source: 'custom', params, message };
276
301
  }
277
- _createError(key, value, source, messages) {
302
+ _createError(key, value, source) {
278
303
  const params = paramsFromValue(value);
279
- return {
280
- key,
281
- value,
282
- source,
283
- params,
284
- message: resolveNgnErrorMessage({ key, value, source, params }, messages),
304
+ const context = { key, value, source, params };
305
+ return { key, value, source, params, message: this._resolveMessage(context) };
306
+ }
307
+ /**
308
+ * Resolves a display message for an error, in priority order:
309
+ * per-instance {@link ngnErrorsMessages} a message carried on the error
310
+ * itself → globally provided messages → the i18n `errors.*` default → the key.
311
+ */
312
+ _resolveMessage(context) {
313
+ return (resolveUserMessage(context, this.ngnErrorsMessages() ?? {}) ??
314
+ carriedMessage(context.value) ??
315
+ resolveUserMessage(context, this._globalMessages) ??
316
+ this._translate(context.key, context.params) ??
317
+ context.key);
318
+ }
319
+ /**
320
+ * Whether an error's message can only come from the i18n default (or the raw
321
+ * key fallback) — i.e. no per-instance, carried, or global message applies.
322
+ * Such errors are held from display until translations load (see
323
+ * {@link _awaitingI18n}); all others show immediately.
324
+ */
325
+ _dependsOnI18n(error) {
326
+ const context = {
327
+ key: error.key,
328
+ value: error.value,
329
+ source: error.source,
330
+ params: error.params,
285
331
  };
332
+ return (resolveUserMessage(context, this.ngnErrorsMessages() ?? {}) === undefined &&
333
+ carriedMessage(context.value) === undefined &&
334
+ resolveUserMessage(context, this._globalMessages) === undefined);
335
+ }
336
+ /**
337
+ * Looks up the i18n `errors.<key>` default, interpolating the error params.
338
+ * Returns `undefined` when the key is unknown or translations aren't loaded —
339
+ * `signal-translate` echoes the flat path back in that case, which we detect.
340
+ */
341
+ _translate(key, params) {
342
+ const path = `errors_${key}`;
343
+ const message = this._i18n._unsafe[path]?.(params);
344
+ return message === undefined || message === path ? undefined : message;
286
345
  }
287
346
  _isGroupErrorRelevant(value) {
288
- const name = this._ngControl?.name;
347
+ const name = this._state.name;
289
348
  if (name === null || name === undefined || !isRecord(value)) {
290
349
  return false;
291
350
  }
@@ -316,14 +375,10 @@ function hasMatchingName(value, controlName) {
316
375
  }
317
376
  return String(value) === controlName;
318
377
  }
319
- function readNgnBoolean(control, key) {
320
- const value = control?.[key];
321
- return typeof value === 'function' ? Boolean(value()) : Boolean(value);
322
- }
323
378
 
324
379
  /**
325
380
  * Generated bundle index. Do not edit.
326
381
  */
327
382
 
328
- export { NGN_ERRORS_MESSAGES, NgnErrors, defaultNgnErrorsMessages, injectNgnErrorsMessages, isRecord, paramsFromValue, provideNgnErrorsMessages, resolveNgnErrorMessage };
383
+ export { NGN_ERRORS_MESSAGES, NgnErrors, carriedMessage, injectNgnControlState, injectNgnErrorsMessages, isRecord, paramsFromValue, provideNgnErrorsMessages, resolveUserMessage };
329
384
  //# sourceMappingURL=ngneers-controls-errors.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ngneers-controls-errors.mjs","sources":["../../src/errors/messages.ts","../../src/errors/errors.ts","../../src/errors/ngneers-controls-errors.ts"],"sourcesContent":["import { inject, InjectionToken, type Provider } from '@angular/core';\n\nimport type { NgnErrorsMessageContext, NgnErrorsMessages } from './types';\n\n/**\n * Multi-provider token for validation error message maps.\n * @category providers\n */\nexport const NGN_ERRORS_MESSAGES = new InjectionToken<readonly NgnErrorsMessages[]>(\n 'NGN_ERRORS_MESSAGES',\n {\n factory: () => [],\n }\n);\n\n/**\n * Provide custom validation error messages that are merged with defaults.\n * @category providers\n */\nexport function provideNgnErrorsMessages(messages: NgnErrorsMessages): Provider {\n return {\n provide: NGN_ERRORS_MESSAGES,\n useValue: messages,\n multi: true,\n };\n}\n\n/**\n * Inject all provided validation message maps as a single merged object.\n * @category providers\n */\nexport function injectNgnErrorsMessages(): NgnErrorsMessages {\n return Object.assign({}, ...inject(NGN_ERRORS_MESSAGES));\n}\n\nexport const defaultNgnErrorsMessages: NgnErrorsMessages = {\n required: 'Required',\n email: 'Enter a valid email address',\n minlength: ({ params }) => {\n const requiredLength = params['requiredLength'];\n return `Use at least ${requiredLength} characters`;\n },\n maxlength: ({ params }) => {\n const requiredLength = params['requiredLength'];\n return `Use at most ${requiredLength} characters`;\n },\n min: ({ params }) => {\n const min = params['min'];\n return `Must be at least ${min}`;\n },\n max: ({ params }) => {\n const max = params['max'];\n return `Must be at most ${max}`;\n },\n pattern: 'Invalid format',\n};\n\nexport function resolveNgnErrorMessage(\n context: NgnErrorsMessageContext,\n messages: NgnErrorsMessages\n): string {\n const resolver = messages[context.key];\n if (typeof resolver === 'function') {\n return resolver(context) ?? context.key;\n }\n if (resolver) {\n return resolver;\n }\n if (typeof context.value === 'string') {\n return context.value;\n }\n if (isRecord(context.value) && typeof context.value['message'] === 'string') {\n return context.value['message'];\n }\n return context.key;\n}\n\nexport function paramsFromValue(value: unknown): Record<string, unknown> {\n if (!isRecord(value)) {\n return {};\n }\n\n return Object.fromEntries(\n Object.entries(value).filter(([key]) => key !== 'message' && !isControlNameKey(key))\n );\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isControlNameKey(key: string): boolean {\n return ['control', 'controlName', 'controls', 'controlNames', 'field', 'fields'].includes(key);\n}\n","import {\n computed,\n DestroyRef,\n Directive,\n effect,\n inject,\n input,\n signal,\n type Signal,\n} from '@angular/core';\nimport {\n AbstractControl,\n ControlContainer,\n NgControl,\n type ValidationErrors,\n} from '@angular/forms';\nimport { NGN_CONTROL, type FullAnyNgnBase } from '@ngneers/controls/base';\nimport { NgnHint } from '@ngneers/controls/hint';\n\nimport {\n defaultNgnErrorsMessages,\n injectNgnErrorsMessages,\n isRecord,\n paramsFromValue,\n resolveNgnErrorMessage,\n} from './messages';\n\nimport type {\n NgnError,\n NgnErrorsCustom,\n NgnErrorsCustomEntry,\n NgnErrorsMessages,\n NgnErrorsMode,\n NgnErrorsShowOn,\n NgnErrorsSource,\n NgnErrorsState,\n} from './types';\n\n@Directive({\n selector: '[ngnErrors]',\n exportAs: 'ngnErrors',\n})\nexport class NgnErrors {\n /**\n * Hint instance that receives the resolved validation state.\n * @category inputs\n */\n public readonly ngnErrorsHint = input<NgnHint | undefined>();\n\n /**\n * Interaction state that controls when errors become visible.\n * @category inputs\n */\n public readonly ngnErrorsShowOn = input<NgnErrorsShowOn>('touched');\n\n /**\n * Whether to show the first matching error or all messages.\n * @category inputs\n */\n public readonly ngnErrorsMode = input<NgnErrorsMode>('first');\n\n /**\n * Per-instance messages that override globally provided defaults.\n * @category inputs\n */\n public readonly ngnErrorsMessages = input<NgnErrorsMessages | null>(null);\n\n /**\n * Additional errors supplied independently of Angular form validation.\n * @category inputs\n */\n public readonly ngnErrorsCustom = input<NgnErrorsCustom>(null);\n\n private readonly _ngControl = inject(NgControl, { optional: true, self: true });\n private readonly _selfContainer = inject(ControlContainer, { optional: true, self: true });\n private readonly _parentContainer = inject(ControlContainer, { optional: true, skipSelf: true });\n private readonly _ngnControl = inject(NGN_CONTROL, {\n optional: true,\n self: true,\n }) as FullAnyNgnBase | null;\n private readonly _globalMessages = injectNgnErrorsMessages();\n private readonly _version = signal(0);\n\n public readonly errors: Signal<readonly NgnError[]> = computed(() => {\n this._version();\n\n const messages = {\n ...defaultNgnErrorsMessages,\n ...this._globalMessages,\n ...this.ngnErrorsMessages(),\n };\n\n return [\n ...this._normalizeErrors(this._hostControl()?.errors, 'control', messages),\n ...this._normalizeGroupErrors(messages),\n ...this._normalizeCustomErrors(messages),\n ];\n });\n\n public readonly firstError = computed(() => this.errors()[0] ?? null);\n\n public readonly pending = computed(() => {\n this._version();\n return this._hostControl()?.pending ?? false;\n });\n\n public readonly visible = computed(() => {\n if (!this.errors().length && !this.pending()) {\n return false;\n }\n\n const control = this._hostControl();\n const showOn = this.ngnErrorsShowOn();\n\n switch (showOn) {\n case 'always':\n return true;\n case 'never':\n return false;\n case 'dirty':\n return control?.dirty ?? readNgnBoolean(this._ngnControl, 'dirty');\n case 'submitted':\n return this._submitted();\n case 'touched':\n default:\n return control?.touched ?? readNgnBoolean(this._ngnControl, 'touched');\n }\n });\n\n public readonly message = computed(() => {\n if (!this.visible()) {\n return null;\n }\n\n if (this.pending()) {\n return 'Validating...';\n }\n\n const errors = this.errors();\n if (this.ngnErrorsMode() === 'all') {\n return errors.map(error => error.message).join('\\n') || null;\n }\n\n return errors[0]?.message ?? null;\n });\n\n public readonly state: Signal<NgnErrorsState> = computed(() => ({\n visible: this.visible(),\n pending: this.pending(),\n errors: this.errors(),\n firstError: this.firstError(),\n message: this.message(),\n }));\n\n constructor() {\n const destroyRef = inject(DestroyRef);\n\n queueMicrotask(() => {\n const controls = new Set(\n [this._hostControl(), this._parentControl(), this._rootControl()].filter(\n (control): control is AbstractControl => !!control\n )\n );\n for (const control of controls) {\n this._watchControl(control, destroyRef);\n }\n });\n\n effect(() => {\n this.ngnErrorsHint()?.setValidationState(this.state());\n });\n }\n\n private _watchControl(control: AbstractControl | null | undefined, destroyRef: DestroyRef): void {\n if (!control) {\n return;\n }\n\n const markChanged = () => this._version.update(value => value + 1);\n const statusSubscription = control.statusChanges?.subscribe(markChanged);\n const valueSubscription = control.valueChanges?.subscribe(markChanged);\n const eventsSubscription = (control as ControlWithEvents).events?.subscribe(markChanged);\n\n destroyRef.onDestroy(() => {\n statusSubscription?.unsubscribe();\n valueSubscription?.unsubscribe();\n eventsSubscription?.unsubscribe();\n });\n\n markChanged();\n }\n\n private _hostControl(): AbstractControl | null {\n return this._ngControl?.control ?? this._selfContainer?.control ?? null;\n }\n\n private _parentControl(): AbstractControl | null {\n return this._ngControl?.control?.parent ?? this._parentContainer?.control ?? null;\n }\n\n private _rootControl(): AbstractControl | null {\n return this._hostControl()?.root ?? this._parentControl()?.root ?? null;\n }\n\n private _submittedSource(): { submitted?: boolean } | null {\n const formDirective =\n this._selfContainer?.formDirective ?? this._parentContainer?.formDirective;\n return (formDirective ?? this._selfContainer ?? this._parentContainer) as {\n submitted?: boolean;\n } | null;\n }\n\n private _submitted(): boolean {\n return this._submittedSource()?.submitted ?? false;\n }\n\n private _normalizeGroupErrors(messages: NgnErrorsMessages): readonly NgnError[] {\n const parent = this._parentControl();\n const host = this._hostControl();\n if (!parent || parent === host) {\n return [];\n }\n\n return this._normalizeErrors(parent.errors, 'group', messages).filter(error =>\n this._isGroupErrorRelevant(error.value)\n );\n }\n\n private _normalizeCustomErrors(messages: NgnErrorsMessages): readonly NgnError[] {\n const errors = this.ngnErrorsCustom();\n if (!errors) {\n return [];\n }\n\n if (Array.isArray(errors)) {\n return errors.map(error => {\n if (typeof error === 'string') {\n return this._createError(error, true, 'custom', messages);\n }\n return this._createCustomEntryError(error, messages);\n });\n }\n\n return this._normalizeErrors(errors, 'custom', messages);\n }\n\n private _normalizeErrors(\n errors: ValidationErrors | null | undefined,\n source: NgnErrorsSource,\n messages: NgnErrorsMessages\n ): readonly NgnError[] {\n if (!errors) {\n return [];\n }\n\n return Object.entries(errors).map(([key, value]) =>\n this._createError(key, value, source, messages)\n );\n }\n\n private _createCustomEntryError(\n error: NgnErrorsCustomEntry,\n messages: NgnErrorsMessages\n ): NgnError {\n const value = error.value ?? true;\n const params = error.params ?? paramsFromValue(value);\n const message =\n error.message ??\n resolveNgnErrorMessage(\n {\n key: error.key,\n value,\n source: 'custom',\n params,\n },\n messages\n );\n\n return {\n key: error.key,\n value,\n source: 'custom',\n params,\n message,\n };\n }\n\n private _createError(\n key: string,\n value: unknown,\n source: NgnErrorsSource,\n messages: NgnErrorsMessages\n ): NgnError {\n const params = paramsFromValue(value);\n\n return {\n key,\n value,\n source,\n params,\n message: resolveNgnErrorMessage({ key, value, source, params }, messages),\n };\n }\n\n private _isGroupErrorRelevant(value: unknown): boolean {\n const name = this._ngControl?.name;\n if (name === null || name === undefined || !isRecord(value)) {\n return false;\n }\n\n const controlName = String(name);\n return (\n hasMatchingName(value['control'], controlName) ||\n hasMatchingName(value['controlName'], controlName) ||\n hasMatchingName(value['field'], controlName) ||\n hasMatchingName(value['controls'], controlName) ||\n hasMatchingName(value['controlNames'], controlName) ||\n hasMatchingName(value['fields'], controlName)\n );\n }\n}\n\ninterface ControlWithEvents {\n events?: { subscribe: (next: () => void) => { unsubscribe: () => void } };\n}\n\nfunction hasMatchingName(value: unknown, controlName: string): boolean {\n if (Array.isArray(value)) {\n return value.some(item => hasMatchingName(item, controlName));\n }\n if (value === null || value === undefined || isRecord(value)) {\n return false;\n }\n return String(value) === controlName;\n}\n\nfunction readNgnBoolean(control: FullAnyNgnBase | null, key: 'dirty' | 'touched'): boolean {\n const value = control?.[key as keyof FullAnyNgnBase];\n return typeof value === 'function' ? Boolean((value as () => unknown)()) : Boolean(value);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngneers-controls-errors.mjs","sources":["../../src/errors/control-state.ts","../../src/errors/messages.ts","../../src/errors/errors.ts","../../src/errors/ngneers-controls-errors.ts"],"sourcesContent":["import { computed, DestroyRef, inject, type OutputRef, signal, type Signal } from '@angular/core';\nimport {\n AbstractControl,\n ControlContainer,\n NgControl,\n type ValidationErrors,\n} from '@angular/forms';\nimport { NGN_CONTROL, type FullAnyNgnBase } from '@ngneers/controls/base';\n\n/**\n * A paradigm-agnostic, reactive view of the form control sitting on the host\n * element — the compatibility layer `ngnErrors` reads so it never has to branch\n * on which form paradigm is in play.\n *\n * Angular 22 has no single signal API spanning the three paradigms, so this\n * bridges them onto one shape:\n * - **Reactive** (`formControl` / `formControlName`) and **template-driven**\n * (`ngModel`) both expose an `NgControl` → `AbstractControl`.\n * - **Signal forms** (`[formField]`) provide the same `NgControl` via interop,\n * backed by the field's `errors()` / `touched()` / `dirty()` / `pending()`.\n * - **No form**: there is no `AbstractControl`, so `touched` falls back to the\n * control's own `touched` signal (also reflecting an external `[touched]`\n * binding), OR-ed with its `touch` output (blur) for controls that only report\n * blur that way.\n *\n * `AbstractControl` isn't signal-based; its single `events` stream (value /\n * status / touched / pristine) is the one subscription that keeps these signals\n * live — replacing separate `valueChanges` + `statusChanges` watches.\n */\nexport interface NgnControlState {\n /** Host control name, used to match relevant parent/group errors. */\n readonly name: string | number | null;\n /** Validation errors of the host control (all paradigms), or `null`. */\n readonly errors: Signal<ValidationErrors | null>;\n /** Validation errors of the parent group, when distinct from the host. */\n readonly parentErrors: Signal<ValidationErrors | null>;\n readonly touched: Signal<boolean>;\n readonly dirty: Signal<boolean>;\n readonly pending: Signal<boolean>;\n readonly submitted: Signal<boolean>;\n}\n\ninterface ControlWithEvents {\n events?: { subscribe: (next: () => void) => { unsubscribe: () => void } };\n}\n\n/**\n * Resolves and reactively tracks the {@link NgnControlState} of the host\n * element. Must be called in an injection context (field/directive constructor).\n */\nexport function injectNgnControlState(): NgnControlState {\n const ngControl = inject(NgControl, { optional: true, self: true });\n const selfContainer = inject(ControlContainer, { optional: true, self: true });\n const parentContainer = inject(ControlContainer, { optional: true, skipSelf: true });\n const ngnControl = inject(NGN_CONTROL, { optional: true, self: true }) as FullAnyNgnBase | null;\n const destroyRef = inject(DestroyRef);\n\n const revision = signal(0);\n const bump = (): void => revision.update(value => value + 1);\n\n const host = (): AbstractControl | null => ngControl?.control ?? selfContainer?.control ?? null;\n const parent = (): AbstractControl | null =>\n ngControl?.control?.parent ?? parentContainer?.control ?? null;\n const root = (): AbstractControl | null => host()?.root ?? parent()?.root ?? null;\n\n // No-form touched: prefer the control's own `touched` signal (also reflects an\n // external `[touched]` binding), and honor a `touch` output emission (blur) for\n // controls that report blur only that way.\n const touchedFromOutput = signal(false);\n const touch = (ngnControl as { touch?: OutputRef<void> } | null)?.touch;\n const touchSubscription = touch?.subscribe(() => touchedFromOutput.set(true));\n if (touchSubscription) {\n destroyRef.onDestroy(() => touchSubscription.unsubscribe());\n }\n const touchedFallback = (): boolean => {\n const value = (ngnControl as { touched?: unknown } | null)?.touched;\n const fromSignal =\n typeof value === 'function' ? Boolean((value as () => unknown)()) : Boolean(value);\n return fromSignal || touchedFromOutput();\n };\n\n // Controls resolve their `AbstractControl` after the value accessor registers,\n // so defer a microtask, then watch each distinct control's unified `events`.\n queueMicrotask(() => {\n const controls = new Set(\n [host(), parent(), root()].filter((control): control is AbstractControl => !!control)\n );\n for (const control of controls) {\n const subscription = (control as ControlWithEvents).events?.subscribe(bump);\n if (subscription) {\n destroyRef.onDestroy(() => subscription.unsubscribe());\n }\n }\n bump();\n });\n\n const submitted = (): boolean => {\n const formDirective = selfContainer?.formDirective ?? parentContainer?.formDirective;\n const source = (formDirective ?? selfContainer ?? parentContainer) as {\n submitted?: boolean;\n } | null;\n return source?.submitted ?? false;\n };\n\n const dirtyFallback = (): boolean => {\n const value = (ngnControl as { dirty?: unknown } | null)?.dirty;\n return typeof value === 'function' ? Boolean((value as () => unknown)()) : Boolean(value);\n };\n\n return {\n name: ngControl?.name ?? null,\n errors: computed(() => (revision(), host()?.errors ?? null)),\n parentErrors: computed(() => {\n revision();\n const parentControl = parent();\n return parentControl && parentControl !== host() ? (parentControl.errors ?? null) : null;\n }),\n touched: computed(() => (revision(), host()?.touched ?? touchedFallback())),\n dirty: computed(() => (revision(), host()?.dirty ?? dirtyFallback())),\n pending: computed(() => (revision(), host()?.pending ?? false)),\n submitted: computed(() => (revision(), submitted())),\n };\n}\n","import { inject, InjectionToken, type Provider } from '@angular/core';\n\nimport type { NgnErrorsMessageContext, NgnErrorsMessages } from './types';\n\n/**\n * Multi-provider token for validation error message maps.\n * @category providers\n */\nexport const NGN_ERRORS_MESSAGES = new InjectionToken<readonly NgnErrorsMessages[]>(\n 'NGN_ERRORS_MESSAGES',\n {\n factory: () => [],\n }\n);\n\n/**\n * Provide custom validation error messages that are merged with defaults.\n * @category providers\n */\nexport function provideNgnErrorsMessages(messages: NgnErrorsMessages): Provider {\n return {\n provide: NGN_ERRORS_MESSAGES,\n useValue: messages,\n multi: true,\n };\n}\n\n/**\n * Inject all provided validation message maps as a single merged object.\n * @category providers\n */\nexport function injectNgnErrorsMessages(): NgnErrorsMessages {\n return Object.assign({}, ...inject(NGN_ERRORS_MESSAGES));\n}\n\n/**\n * Resolves a message for the context's key from a user-provided message map,\n * or `undefined` if the map has no (non-null) entry for it. Function resolvers\n * that return `null`/`undefined` are treated as \"no message\" so resolution can\n * fall through to the next source.\n */\nexport function resolveUserMessage(\n context: NgnErrorsMessageContext,\n messages: NgnErrorsMessages\n): string | undefined {\n const resolver = messages[context.key];\n // Empty strings count as \"no message\" so resolution falls through to the next source.\n if (typeof resolver === 'function') {\n return resolver(context) || undefined;\n }\n return resolver || undefined;\n}\n\n/**\n * A message carried on the error itself — a plain-string error value, or a\n * `message` field on the error object (e.g. a signal-forms validator's\n * `{ message }`, or a group error). `undefined` when the error carries none.\n */\nexport function carriedMessage(value: unknown): string | undefined {\n if (typeof value === 'string') {\n return value;\n }\n if (isRecord(value) && typeof value['message'] === 'string') {\n return value['message'];\n }\n return undefined;\n}\n\nexport function paramsFromValue(value: unknown): Record<string, unknown> {\n if (!isRecord(value)) {\n return {};\n }\n\n return Object.fromEntries(\n Object.entries(value).filter(([key]) => key !== 'message' && !isControlNameKey(key))\n );\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isControlNameKey(key: string): boolean {\n return ['control', 'controlName', 'controls', 'controlNames', 'field', 'fields'].includes(key);\n}\n","import { computed, Directive, effect, inject, input, type Signal } from '@angular/core';\nimport type { ValidationErrors } from '@angular/forms';\nimport { NgnHint } from '@ngneers/controls/hint';\nimport { I18n } from '@ngneers/controls/i18n';\n\nimport { injectNgnControlState } from './control-state';\nimport {\n carriedMessage,\n injectNgnErrorsMessages,\n isRecord,\n paramsFromValue,\n resolveUserMessage,\n} from './messages';\n\nimport type {\n NgnError,\n NgnErrorsCustom,\n NgnErrorsCustomEntry,\n NgnErrorsMessageContext,\n NgnErrorsMessages,\n NgnErrorsMode,\n NgnErrorsShowOn,\n NgnErrorsSource,\n NgnErrorsState,\n} from './types';\n\n/**\n * Resolves validation errors for the control it sits on and exposes them as\n * normalized, message-mapped signals — ready to bridge into an {@link NgnHint}\n * via {@link NgnErrors.ngnErrorsHint}.\n *\n * Which form paradigm is in play is abstracted away by {@link injectNgnControlState}:\n * template-driven (`ngModel`), reactive (`formControl` / `formControlName`) and\n * signal forms (`[formField]`) all surface as one reactive state, including\n * relevant parent-group errors and a no-form `touched` fallback.\n *\n * `ngnErrorsCustom` layers on additional errors independently of any form, and\n * {@link NgnErrors.ngnErrorsShowOn} controls when messages surface (defaults to\n * `touched`). Error keys map onto the shared message table; signal-forms\n * `minLength` / `maxLength` kinds have their own table entries alongside the\n * classic lowercase `minlength` / `maxlength`.\n *\n * @category control\n */\n@Directive({\n selector: '[ngnErrors]',\n exportAs: 'ngnErrors',\n})\nexport class NgnErrors {\n /**\n * Hint instance that receives the resolved validation state.\n * @category inputs\n */\n public readonly ngnErrorsHint = input<NgnHint | undefined>();\n\n /**\n * Interaction state that controls when errors become visible.\n * @category inputs\n */\n public readonly ngnErrorsShowOn = input<NgnErrorsShowOn>('touched');\n\n /**\n * Whether to show the first matching error or all messages.\n * @category inputs\n */\n public readonly ngnErrorsMode = input<NgnErrorsMode>('first');\n\n /**\n * Per-instance messages that override globally provided defaults.\n * @category inputs\n */\n public readonly ngnErrorsMessages = input<NgnErrorsMessages | null>(null);\n\n /**\n * Additional errors supplied independently of Angular form validation.\n * @category inputs\n */\n public readonly ngnErrorsCustom = input<NgnErrorsCustom>(null);\n\n /** Paradigm-agnostic view of the host control (see {@link injectNgnControlState}). */\n private readonly _state = injectNgnControlState();\n private readonly _i18n = inject(I18n).translations;\n private readonly _globalMessages = injectNgnErrorsMessages();\n\n public readonly errors: Signal<readonly NgnError[]> = computed(() => [\n ...this._normalizeErrors(this._state.errors(), 'control'),\n ...this._normalizeGroupErrors(),\n ...this._normalizeCustomErrors(),\n ]);\n\n public readonly firstError = computed(() => this.errors()[0] ?? null);\n\n public readonly pending = computed(() => this._state.pending());\n\n /**\n * Whether the i18n error defaults are loaded. Probes a core key that every\n * locale defines — until the async translation import lands, `_translate`\n * echoes the key path back, which this detects.\n */\n private readonly _i18nReady = computed(() => this._translate('required', {}) !== undefined);\n\n /**\n * Whether display should wait: some visible error depends on the i18n default\n * (no instance / carried / global message) and translations aren't loaded yet.\n */\n private readonly _awaitingI18n = computed(\n () => !this._i18nReady() && this.errors().some(error => this._dependsOnI18n(error))\n );\n\n public readonly visible = computed(() => {\n if (!this.errors().length && !this.pending()) {\n return false;\n }\n\n // Hold error display until the i18n defaults have loaded, so a default\n // message never flashes as its raw key (\"required\") before the async\n // translation import lands. Only affects errors with no instance/carried/\n // global message (those show immediately); pending always shows (it has a\n // hardcoded fallback). See {@link _awaitingI18n}.\n if (!this.pending() && this._awaitingI18n()) {\n return false;\n }\n\n switch (this.ngnErrorsShowOn()) {\n case 'always':\n return true;\n case 'never':\n return false;\n case 'dirty':\n return this._state.dirty();\n case 'submitted':\n return this._state.submitted();\n case 'touched':\n default:\n return this._state.touched();\n }\n });\n\n public readonly message = computed(() => {\n if (!this.visible()) {\n return null;\n }\n\n if (this.pending()) {\n return this._translate('pending', {}) ?? 'Validating...';\n }\n\n const errors = this.errors();\n if (this.ngnErrorsMode() === 'all') {\n return errors.map(error => error.message).join('\\n') || null;\n }\n\n return errors[0]?.message ?? null;\n });\n\n public readonly state: Signal<NgnErrorsState> = computed(() => ({\n visible: this.visible(),\n pending: this.pending(),\n errors: this.errors(),\n firstError: this.firstError(),\n message: this.message(),\n }));\n\n constructor() {\n // `ngnErrors` renders the error *message* only — it never touches invalid\n // styling. The control owns its invalid border (via its own `invalidOn`\n // trigger), and the input field mirrors its child. See {@link NgnErrors}.\n effect(() => {\n this.ngnErrorsHint()?.setValidationState(this.state());\n });\n }\n\n private _normalizeGroupErrors(): readonly NgnError[] {\n return this._normalizeErrors(this._state.parentErrors(), 'group').filter(error =>\n this._isGroupErrorRelevant(error.value)\n );\n }\n\n private _normalizeCustomErrors(): readonly NgnError[] {\n const errors = this.ngnErrorsCustom();\n if (!errors) {\n return [];\n }\n\n if (Array.isArray(errors)) {\n return errors.map(error =>\n typeof error === 'string'\n ? this._createError(error, true, 'custom')\n : this._createCustomEntryError(error)\n );\n }\n\n return this._normalizeErrors(errors, 'custom');\n }\n\n private _normalizeErrors(\n errors: ValidationErrors | null | undefined,\n source: NgnErrorsSource\n ): readonly NgnError[] {\n if (!errors) {\n return [];\n }\n\n return Object.entries(errors).map(([key, value]) => this._createError(key, value, source));\n }\n\n private _createCustomEntryError(error: NgnErrorsCustomEntry): NgnError {\n const value = error.value ?? true;\n const params = error.params ?? paramsFromValue(value);\n const context: NgnErrorsMessageContext = { key: error.key, value, source: 'custom', params };\n const message = error.message ?? this._resolveMessage(context);\n\n return { key: error.key, value, source: 'custom', params, message };\n }\n\n private _createError(key: string, value: unknown, source: NgnErrorsSource): NgnError {\n const params = paramsFromValue(value);\n const context: NgnErrorsMessageContext = { key, value, source, params };\n\n return { key, value, source, params, message: this._resolveMessage(context) };\n }\n\n /**\n * Resolves a display message for an error, in priority order:\n * per-instance {@link ngnErrorsMessages} → a message carried on the error\n * itself → globally provided messages → the i18n `errors.*` default → the key.\n */\n private _resolveMessage(context: NgnErrorsMessageContext): string {\n return (\n resolveUserMessage(context, this.ngnErrorsMessages() ?? {}) ??\n carriedMessage(context.value) ??\n resolveUserMessage(context, this._globalMessages) ??\n this._translate(context.key, context.params) ??\n context.key\n );\n }\n\n /**\n * Whether an error's message can only come from the i18n default (or the raw\n * key fallback) — i.e. no per-instance, carried, or global message applies.\n * Such errors are held from display until translations load (see\n * {@link _awaitingI18n}); all others show immediately.\n */\n private _dependsOnI18n(error: NgnError): boolean {\n const context: NgnErrorsMessageContext = {\n key: error.key,\n value: error.value,\n source: error.source,\n params: error.params,\n };\n return (\n resolveUserMessage(context, this.ngnErrorsMessages() ?? {}) === undefined &&\n carriedMessage(context.value) === undefined &&\n resolveUserMessage(context, this._globalMessages) === undefined\n );\n }\n\n /**\n * Looks up the i18n `errors.<key>` default, interpolating the error params.\n * Returns `undefined` when the key is unknown or translations aren't loaded —\n * `signal-translate` echoes the flat path back in that case, which we detect.\n */\n private _translate(key: string, params: Record<string, unknown>): string | undefined {\n const path = `errors_${key}`;\n const message = this._i18n._unsafe[path]?.(params);\n return message === undefined || message === path ? undefined : message;\n }\n\n private _isGroupErrorRelevant(value: unknown): boolean {\n const name = this._state.name;\n if (name === null || name === undefined || !isRecord(value)) {\n return false;\n }\n\n const controlName = String(name);\n return (\n hasMatchingName(value['control'], controlName) ||\n hasMatchingName(value['controlName'], controlName) ||\n hasMatchingName(value['field'], controlName) ||\n hasMatchingName(value['controls'], controlName) ||\n hasMatchingName(value['controlNames'], controlName) ||\n hasMatchingName(value['fields'], controlName)\n );\n }\n}\n\nfunction hasMatchingName(value: unknown, controlName: string): boolean {\n if (Array.isArray(value)) {\n return value.some(item => hasMatchingName(item, controlName));\n }\n if (value === null || value === undefined || isRecord(value)) {\n return false;\n }\n return String(value) === controlName;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}