@nexterajs/ui 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +279 -34
  2. package/fesm2022/nexterajs-ui-charts.mjs +1096 -0
  3. package/fesm2022/nexterajs-ui-charts.mjs.map +1 -0
  4. package/fesm2022/nexterajs-ui-core.mjs +835 -11
  5. package/fesm2022/nexterajs-ui-core.mjs.map +1 -1
  6. package/fesm2022/nexterajs-ui-display.mjs +1142 -0
  7. package/fesm2022/nexterajs-ui-display.mjs.map +1 -0
  8. package/fesm2022/nexterajs-ui-dropdowns.mjs +621 -69
  9. package/fesm2022/nexterajs-ui-dropdowns.mjs.map +1 -1
  10. package/fesm2022/nexterajs-ui-file-uploader.mjs +576 -11
  11. package/fesm2022/nexterajs-ui-file-uploader.mjs.map +1 -1
  12. package/fesm2022/nexterajs-ui-forms.mjs +2702 -452
  13. package/fesm2022/nexterajs-ui-forms.mjs.map +1 -1
  14. package/fesm2022/nexterajs-ui-grid.mjs +2350 -336
  15. package/fesm2022/nexterajs-ui-grid.mjs.map +1 -1
  16. package/fesm2022/nexterajs-ui-intl.mjs +117 -15
  17. package/fesm2022/nexterajs-ui-intl.mjs.map +1 -1
  18. package/fesm2022/nexterajs-ui-navigation.mjs +1054 -0
  19. package/fesm2022/nexterajs-ui-navigation.mjs.map +1 -0
  20. package/fesm2022/nexterajs-ui-notify.mjs +353 -0
  21. package/fesm2022/nexterajs-ui-notify.mjs.map +1 -0
  22. package/fesm2022/nexterajs-ui-overlay.mjs +1671 -0
  23. package/fesm2022/nexterajs-ui-overlay.mjs.map +1 -0
  24. package/fesm2022/nexterajs-ui-pivot.mjs +191 -136
  25. package/fesm2022/nexterajs-ui-pivot.mjs.map +1 -1
  26. package/fesm2022/nexterajs-ui-shell.mjs +542 -0
  27. package/fesm2022/nexterajs-ui-shell.mjs.map +1 -0
  28. package/fesm2022/nexterajs-ui-styling.mjs +568 -3
  29. package/fesm2022/nexterajs-ui-styling.mjs.map +1 -1
  30. package/fesm2022/nexterajs-ui-tree-view.mjs +150 -18
  31. package/fesm2022/nexterajs-ui-tree-view.mjs.map +1 -1
  32. package/fesm2022/nexterajs-ui-validation.mjs +283 -97
  33. package/fesm2022/nexterajs-ui-validation.mjs.map +1 -1
  34. package/fesm2022/nexterajs-ui.mjs +6 -0
  35. package/fesm2022/nexterajs-ui.mjs.map +1 -1
  36. package/package.json +37 -2
  37. package/styles/_dropdown.scss +150 -1
  38. package/styles/_editor.scss +75 -21
  39. package/styles/_icons.scss +92 -0
  40. package/styles/_mixins.scss +104 -1
  41. package/styles/_scrollbar.scss +134 -0
  42. package/styles/_status.scss +21 -0
  43. package/styles/_toast.scss +243 -0
  44. package/styles/_tokens.scss +467 -2
  45. package/styles/_widget.scss +14 -3
  46. package/styles/index.scss +10 -4
  47. package/types/nexterajs-ui-charts.d.ts +446 -0
  48. package/types/nexterajs-ui-core.d.ts +515 -30
  49. package/types/nexterajs-ui-display.d.ts +720 -0
  50. package/types/nexterajs-ui-dropdowns.d.ts +318 -57
  51. package/types/nexterajs-ui-file-uploader.d.ts +240 -8
  52. package/types/nexterajs-ui-forms.d.ts +772 -43
  53. package/types/nexterajs-ui-grid.d.ts +878 -135
  54. package/types/nexterajs-ui-intl.d.ts +39 -4
  55. package/types/nexterajs-ui-navigation.d.ts +531 -0
  56. package/types/nexterajs-ui-notify.d.ts +149 -0
  57. package/types/nexterajs-ui-overlay.d.ts +703 -0
  58. package/types/nexterajs-ui-pivot.d.ts +91 -70
  59. package/types/nexterajs-ui-shell.d.ts +294 -0
  60. package/types/nexterajs-ui-styling.d.ts +163 -3
  61. package/types/nexterajs-ui-tree-view.d.ts +56 -4
  62. package/types/nexterajs-ui-validation.d.ts +110 -22
  63. package/types/nexterajs-ui.d.ts +6 -0
@@ -1,11 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, inject, contentChildren, signal, Injector, DestroyRef } from '@angular/core';
2
+ import { inject, input, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, contentChildren, output, signal, Injector, DestroyRef } from '@angular/core';
3
+ import { NxeMessageService, NXE_VALIDATION_TARGET, NxeValidationGroupRegistry, NXE_VALIDATION_GROUP } from '@nexterajs/ui/core';
3
4
  import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
- import { distinctUntilChanged, startWith, pairwise } from 'rxjs';
5
- import { NXE_VALIDATION_TARGET, NxeValidationGroupRegistry } from '@nexterajs/ui/core';
5
+ import { distinctUntilChanged, startWith, pairwise, skip } from 'rxjs';
6
6
 
7
7
  /**
8
- * The message shown when a rule fails and the caller gave none.
8
+ * The English message shown when a rule fails and the caller gave none. The components resolve it
9
+ * through `NxeMessageService` (`validation.rule.*`), which starts as these same strings and
10
+ * follows the active language; this table stays for code that reads it directly.
9
11
  *
10
12
  * These are DevExtreme's own defaults, verbatim from its `validation-*` message keys. Without
11
13
  * them a rule with no `message` blocks its group and displays nothing at all, because every
@@ -19,6 +21,78 @@ const NXE_DEFAULT_VALIDATION_MESSAGES = {
19
21
  compare: 'Values do not match',
20
22
  custom: 'Value is invalid',
21
23
  };
24
+ /** DevExtreme RequiredRule semantics: null/undefined/''/false are "not specified". */
25
+ function nxeIsEmptyValue(value) {
26
+ return (value === null ||
27
+ value === undefined ||
28
+ value === '' ||
29
+ value === false ||
30
+ (Array.isArray(value) && value.length === 0));
31
+ }
32
+ /**
33
+ * The message for a rule with none of its own: the active language's when a message service is
34
+ * given, English otherwise.
35
+ */
36
+ function nxeDefaultMessage(type, messages) {
37
+ return messages && type in NXE_DEFAULT_VALIDATION_MESSAGES
38
+ ? messages.t(`validation.rule.${type}`)
39
+ : (NXE_DEFAULT_VALIDATION_MESSAGES[type] ?? '');
40
+ }
41
+ /** Whether `value` satisfies `spec`. The single evaluator behind both rule forms. */
42
+ function nxeEvaluateRule(spec, value, rule) {
43
+ switch (spec.type) {
44
+ case 'required':
45
+ return !nxeIsEmptyValue(value);
46
+ case 'compare': {
47
+ const target = spec.comparisonTarget?.();
48
+ switch (spec.comparisonType) {
49
+ case '===':
50
+ return value === target;
51
+ case '!=':
52
+ // eslint-disable-next-line eqeqeq
53
+ return value != target;
54
+ case '!==':
55
+ return value !== target;
56
+ case '>':
57
+ return value > target;
58
+ case '<':
59
+ return value < target;
60
+ case '>=':
61
+ return value >= target;
62
+ case '<=':
63
+ return value <= target;
64
+ case '==':
65
+ default:
66
+ // eslint-disable-next-line eqeqeq
67
+ return value == target;
68
+ }
69
+ }
70
+ case 'range': {
71
+ if (nxeIsEmptyValue(value)) {
72
+ return true;
73
+ }
74
+ const num = Number(value);
75
+ if (Number.isNaN(num)) {
76
+ return false;
77
+ }
78
+ return ((spec.min === undefined || num >= spec.min) &&
79
+ (spec.max === undefined || num <= spec.max));
80
+ }
81
+ case 'pattern': {
82
+ if (nxeIsEmptyValue(value) || !spec.pattern) {
83
+ return true;
84
+ }
85
+ const regex = typeof spec.pattern === 'string' ? new RegExp(spec.pattern) : spec.pattern;
86
+ return regex.test(String(value));
87
+ }
88
+ case 'email':
89
+ return nxeIsEmptyValue(value) || /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(String(value));
90
+ case 'custom':
91
+ return spec.validationCallback?.({ value, rule }) ?? true;
92
+ default:
93
+ return true;
94
+ }
95
+ }
22
96
  /**
23
97
  * `nxe-validation-rule` — drop-in for DevExtreme `dxi-validation-rule`.
24
98
  *
@@ -27,6 +101,7 @@ const NXE_DEFAULT_VALIDATION_MESSAGES = {
27
101
  * `type` input covers 100% of usage.
28
102
  */
29
103
  class NxeValidationRuleComponent {
104
+ messages = inject(NxeMessageService);
30
105
  type = input('required', /* @ts-ignore */
31
106
  ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
32
107
  message = input('', /* @ts-ignore */
@@ -52,75 +127,24 @@ class NxeValidationRuleComponent {
52
127
  * The message to show when this rule fails: the caller's, or DevExtreme's default for the
53
128
  * rule type. Whitespace counts as absent — a message of spaces is still an empty bubble.
54
129
  */
55
- resolvedMessage = computed(() => this.message().trim() || NXE_DEFAULT_VALIDATION_MESSAGES[this.type()] || '', /* @ts-ignore */
130
+ resolvedMessage = computed(() => this.message().trim() || nxeDefaultMessage(this.type(), this.messages), /* @ts-ignore */
56
131
  ...(ngDevMode ? [{ debugName: "resolvedMessage" }] : /* istanbul ignore next */ []));
132
+ /** This rule as plain data, so both rule forms evaluate through one function. */
133
+ toSpec() {
134
+ return {
135
+ type: this.type(),
136
+ message: this.message(),
137
+ comparisonTarget: this.comparisonTarget(),
138
+ comparisonType: this.comparisonType(),
139
+ validationCallback: this.validationCallback(),
140
+ pattern: this.pattern(),
141
+ min: this.min(),
142
+ max: this.max(),
143
+ };
144
+ }
57
145
  /** Whether `value` satisfies this rule. */
58
146
  evaluate(value) {
59
- switch (this.type()) {
60
- case 'required':
61
- return !this.isEmpty(value);
62
- case 'compare':
63
- return this.compare(value);
64
- case 'range':
65
- return this.isEmpty(value) || this.inRange(value);
66
- case 'pattern':
67
- return this.isEmpty(value) || this.matchesPattern(value);
68
- case 'email':
69
- return this.isEmpty(value) || /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(String(value));
70
- case 'custom':
71
- return this.validationCallback()?.({ value, rule: this }) ?? true;
72
- default:
73
- return true;
74
- }
75
- }
76
- /** DevExtreme RequiredRule semantics: null/undefined/''/false are "not specified". */
77
- isEmpty(value) {
78
- return (value === null ||
79
- value === undefined ||
80
- value === '' ||
81
- value === false ||
82
- (Array.isArray(value) && value.length === 0));
83
- }
84
- compare(value) {
85
- const target = this.comparisonTarget()?.();
86
- switch (this.comparisonType()) {
87
- case '===':
88
- return value === target;
89
- case '!=':
90
- // eslint-disable-next-line eqeqeq
91
- return value != target;
92
- case '!==':
93
- return value !== target;
94
- case '>':
95
- return value > target;
96
- case '<':
97
- return value < target;
98
- case '>=':
99
- return value >= target;
100
- case '<=':
101
- return value <= target;
102
- case '==':
103
- default:
104
- // eslint-disable-next-line eqeqeq
105
- return value == target;
106
- }
107
- }
108
- inRange(value) {
109
- const num = Number(value);
110
- if (Number.isNaN(num)) {
111
- return false;
112
- }
113
- const min = this.min();
114
- const max = this.max();
115
- return (min === undefined || num >= min) && (max === undefined || num <= max);
116
- }
117
- matchesPattern(value) {
118
- const pattern = this.pattern();
119
- if (!pattern) {
120
- return true;
121
- }
122
- const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern;
123
- return regex.test(String(value));
147
+ return nxeEvaluateRule(this.toSpec(), value, this);
124
148
  }
125
149
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeValidationRuleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
126
150
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.5", type: NxeValidationRuleComponent, isStandalone: true, selector: "nxe-validation-rule", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, comparisonTarget: { classPropertyName: "comparisonTarget", publicName: "comparisonTarget", isSignal: true, isRequired: false, transformFunction: null }, comparisonType: { classPropertyName: "comparisonType", publicName: "comparisonType", isSignal: true, isRequired: false, transformFunction: null }, validationCallback: { classPropertyName: "validationCallback", publicName: "validationCallback", isSignal: true, isRequired: false, transformFunction: null }, pattern: { classPropertyName: "pattern", publicName: "pattern", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
@@ -140,69 +164,173 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
140
164
  * `nxe-validator` — drop-in for DevExtreme `dx-validator`.
141
165
  *
142
166
  * Placed inside an editor (`<nxe-text-box><nxe-validator><nxe-validation-rule .../></nxe-validator></nxe-text-box>`),
143
- * it adapts the editor via {@link NXE_VALIDATION_TARGET}, collects its `nxe-validation-rule`
144
- * children, and registers into the {@link NxeValidationGroupRegistry} under the editor's
145
- * `validationGroup`. Validation is on-demand (matching DevExtreme: not auto-run on change) —
146
- * `validate()` is invoked by `nxe-button[validationGroup]` (group-level) or directly.
167
+ * it adapts the editor via {@link NXE_VALIDATION_TARGET}, collects its rules, and registers into
168
+ * the {@link NxeValidationGroupRegistry} under the editor's `validationGroup`.
169
+ *
170
+ * Validation is on demand, as in DevExtreme — `validate()` is invoked by
171
+ * `nxe-button[validationGroup]` or directly. With one exception, which is also dx's: once a
172
+ * validator HAS been validated, it re-validates as the value changes, so a field that has gone
173
+ * red clears itself as the user fixes it rather than staying red until the next submit.
147
174
  */
148
175
  class NxeValidatorComponent {
149
176
  target = inject(NXE_VALIDATION_TARGET, { optional: true });
150
177
  registry = inject(NxeValidationGroupRegistry);
178
+ messages = inject(NxeMessageService);
179
+ groupScope = inject(NXE_VALIDATION_GROUP, { optional: true });
151
180
  rules = contentChildren(NxeValidationRuleComponent, /* @ts-ignore */
152
181
  ...(ngDevMode ? [{ debugName: "rules" }] : /* istanbul ignore next */ []));
182
+ /**
183
+ * dx `validationRules` — rules as data instead of projected children. Both forms combine;
184
+ * projected children come first, matching declaration order.
185
+ */
186
+ validationRules = input(undefined, /* @ts-ignore */
187
+ ...(ngDevMode ? [{ debugName: "validationRules" }] : /* istanbul ignore next */ []));
153
188
  /** Explicit group; otherwise inherited from the wrapped editor's `validationGroup`. */
154
189
  validationGroupInput = input(undefined, { ...(ngDevMode ? { debugName: "validationGroupInput" } : /* istanbul ignore next */ {}), alias: 'validationGroup' });
155
- // Bare validators (no group) join the implicit default group `''` — mirroring DevExtreme's
156
- // global default group — so a groupless `nxe-button` validates them.
157
- group = computed(() => this.validationGroupInput() ?? this.target?.validationGroup() ?? '', /* @ts-ignore */
190
+ /** dx `onValidated`, fired on every validation, valid or not. */
191
+ onValidated = output();
192
+ // An explicit name wins over a surrounding `nxe-validation-group`, as in dx. Bare validators
193
+ // with neither join the implicit default group `''` — dx's global group — so a groupless
194
+ // `nxe-button` validates them.
195
+ group = computed(() => this.validationGroupInput() ??
196
+ this.target?.validationGroup() ??
197
+ this.groupScope?.groupKey ??
198
+ '', /* @ts-ignore */
158
199
  ...(ngDevMode ? [{ debugName: "group" }] : /* istanbul ignore next */ []));
159
200
  /** Last validation outcome (false once a rule has failed, until re-validated). */
160
201
  isValid = signal(true, /* @ts-ignore */
161
202
  ...(ngDevMode ? [{ debugName: "isValid" }] : /* istanbul ignore next */ []));
203
+ /** Every rule broken by the last validation, in declaration order. */
204
+ brokenRules = signal([], /* @ts-ignore */
205
+ ...(ngDevMode ? [{ debugName: "brokenRules" }] : /* istanbul ignore next */ []));
206
+ /** Whether `validate()` has ever run — the gate on live re-validation. */
207
+ validated = signal(false, /* @ts-ignore */
208
+ ...(ngDevMode ? [{ debugName: "validated" }] : /* istanbul ignore next */ []));
162
209
  /** DevExtreme parity: existing code calls `validator.instance.validate()` via `@ViewChild`. */
163
210
  get instance() {
164
211
  return this;
165
212
  }
166
- member = { validate: () => this.validate() };
213
+ member = { validate: () => this.validate(), reset: () => this.reset() };
214
+ /** Projected children plus `[validationRules]`, as one evaluable list. */
215
+ allRules = computed(() => [
216
+ ...this.rules().map((rule) => ({
217
+ type: rule.type(),
218
+ evaluate: (value) => rule.evaluate(value),
219
+ message: () => rule.resolvedMessage(),
220
+ })),
221
+ ...(this.validationRules() ?? []).map((spec) => ({
222
+ type: spec.type,
223
+ evaluate: (value) => nxeEvaluateRule(spec, value),
224
+ message: () => spec.message?.trim() || nxeDefaultMessage(spec.type, this.messages),
225
+ })),
226
+ ], /* @ts-ignore */
227
+ ...(ngDevMode ? [{ debugName: "allRules" }] : /* istanbul ignore next */ []));
167
228
  constructor() {
229
+ const injector = inject(Injector);
168
230
  // Register/unregister into the named group reactively (handles dynamic group changes).
169
231
  /* ⚠ NOT an effect - UI-48. The registration follows ONE signal, and the unregister that
170
232
  `onCleanup` used to run is the pairwise step below: a group change unregisters the old
171
233
  one before registering the new, and destruction unregisters the last. */
172
- toObservable(this.group, { injector: inject(Injector) })
234
+ toObservable(this.group, { injector })
173
235
  .pipe(distinctUntilChanged(), startWith(undefined), pairwise(), takeUntilDestroyed())
174
236
  .subscribe(([previous, group]) => {
175
237
  if (previous !== undefined) {
176
238
  this.registry.unregister(previous, this.member);
177
239
  }
178
- this.registry.register(group, this.member);
240
+ if (group !== undefined) {
241
+ this.registry.register(group, this.member);
242
+ }
243
+ });
244
+ // dx re-validates a field that has already been validated as its value changes, so an
245
+ // error clears while the user types rather than surviving until the next submit.
246
+ /* ⚠ NOT an effect - UI-48. This takes exactly one dependency, the target's value, named
247
+ here rather than inferred from whatever `validate()` happens to read. */
248
+ toObservable(computed(() => this.target?.getValue()), { injector })
249
+ .pipe(skip(1), takeUntilDestroyed())
250
+ .subscribe(() => {
251
+ if (this.validated()) {
252
+ this.validate();
253
+ }
179
254
  });
180
- inject(DestroyRef).onDestroy(() => this.registry.unregister(this.group(), this.member));
255
+ // A bubble that is already showing is a string the editor holds, so it would keep the old
256
+ // language after a switch: rewrite it when the bundle changes.
257
+ /* NOT an effect: one named dependency, the active bundle. */
258
+ toObservable(this.messages.messages, { injector })
259
+ .pipe(skip(1), takeUntilDestroyed())
260
+ .subscribe(() => this.refreshMessages());
261
+ // Tell the editor whether it is required, so it can render aria-required. The rule is
262
+ // where the requirement is stated; the editor should not have to be told twice.
263
+ /* ⚠ NOT an effect - UI-48: one named dependency, the resolved rule list. */
264
+ toObservable(computed(() => this.allRules().some((rule) => rule.type === 'required')), { injector })
265
+ .pipe(takeUntilDestroyed())
266
+ .subscribe((required) => this.target?.setRequired?.(required));
267
+ inject(DestroyRef).onDestroy(() => {
268
+ this.registry.unregister(this.group(), this.member);
269
+ // The editor outlives its validator when the validator sits inside an @if, and a
270
+ // field left advertising aria-required by a rule that no longer exists is worse than
271
+ // one that never advertised it.
272
+ this.target?.setRequired?.(false);
273
+ this.target?.setValidationError(null);
274
+ });
275
+ }
276
+ /** Re-word the broken rules after a language switch, without validating again or emitting. */
277
+ refreshMessages() {
278
+ if (!this.validated() || this.isValid()) {
279
+ return;
280
+ }
281
+ const value = this.target?.getValue();
282
+ const broken = this.allRules()
283
+ .filter((rule) => !rule.evaluate(value))
284
+ .map((rule) => ({ type: rule.type, message: rule.message() }));
285
+ if (broken.length) {
286
+ this.brokenRules.set(broken);
287
+ this.target?.setValidationError(broken[0].message);
288
+ }
181
289
  }
182
- /** Run all rules against the editor's current value; pushes the first failing message. */
290
+ /**
291
+ * Run every rule against the editor's current value.
292
+ *
293
+ * dx reports ALL broken rules, not just the first — a `brokenRules` array with at most one
294
+ * entry cannot tell a caller whether a field failed once or three times. The editor's bubble
295
+ * still shows the first message, which is what dx renders.
296
+ */
183
297
  validate() {
184
298
  const value = this.target?.getValue();
185
- for (const rule of this.rules()) {
299
+ const broken = [];
300
+ for (const rule of this.allRules()) {
186
301
  if (!rule.evaluate(value)) {
187
302
  // resolvedMessage(), not message(): a rule declared without one would otherwise
188
303
  // push the empty string, and every editor guards its bubble with
189
304
  // `@if (validationError())` — so the field would block the group in silence.
190
- const message = rule.resolvedMessage();
191
- this.isValid.set(false);
192
- this.target?.setValidationError(message);
193
- return {
194
- isValid: false,
195
- brokenRules: [{ type: rule.type(), message }],
196
- status: 'invalid',
197
- };
305
+ broken.push({ type: rule.type, message: rule.message() });
198
306
  }
199
307
  }
308
+ this.validated.set(true);
309
+ this.brokenRules.set(broken);
310
+ this.isValid.set(broken.length === 0);
311
+ this.target?.setValidationError(broken.length ? broken[0].message : null);
312
+ const result = {
313
+ isValid: broken.length === 0,
314
+ brokenRules: broken,
315
+ status: broken.length === 0 ? 'valid' : 'invalid',
316
+ };
317
+ this.onValidated.emit({ ...result, value, validationGroup: this.group() });
318
+ return result;
319
+ }
320
+ /**
321
+ * Clear the error state without validating (dx `reset()`).
322
+ *
323
+ * Also clears `validated`, so the field goes back to staying quiet until it is validated
324
+ * again — otherwise a reset field would light up on the next keystroke.
325
+ */
326
+ reset() {
327
+ this.validated.set(false);
328
+ this.brokenRules.set([]);
200
329
  this.isValid.set(true);
201
330
  this.target?.setValidationError(null);
202
- return { isValid: true, brokenRules: [], status: 'valid' };
203
331
  }
204
332
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeValidatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
205
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.5", type: NxeValidatorComponent, isStandalone: true, selector: "nxe-validator", inputs: { validationGroupInput: { classPropertyName: "validationGroupInput", publicName: "validationGroup", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "rules", predicate: NxeValidationRuleComponent, isSignal: true }], ngImport: i0, template: '<ng-content />', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
333
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.5", type: NxeValidatorComponent, isStandalone: true, selector: "nxe-validator", inputs: { validationRules: { classPropertyName: "validationRules", publicName: "validationRules", isSignal: true, isRequired: false, transformFunction: null }, validationGroupInput: { classPropertyName: "validationGroupInput", publicName: "validationGroup", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onValidated: "onValidated" }, queries: [{ propertyName: "rules", predicate: NxeValidationRuleComponent, isSignal: true }], ngImport: i0, template: '<ng-content />', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
206
334
  }
207
335
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeValidatorComponent, decorators: [{
208
336
  type: Component,
@@ -213,7 +341,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
213
341
  changeDetection: ChangeDetectionStrategy.OnPush,
214
342
  encapsulation: ViewEncapsulation.None,
215
343
  }]
216
- }], ctorParameters: () => [], propDecorators: { rules: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NxeValidationRuleComponent), { isSignal: true }] }], validationGroupInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationGroup", required: false }] }] } });
344
+ }], ctorParameters: () => [], propDecorators: { rules: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NxeValidationRuleComponent), { isSignal: true }] }], validationRules: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationRules", required: false }] }], validationGroupInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationGroup", required: false }] }], onValidated: [{ type: i0.Output, args: ["onValidated"] }] } });
217
345
 
218
346
  class NxeValidators {
219
347
  /**
@@ -240,8 +368,8 @@ class NxeValidators {
240
368
  return null;
241
369
  }
242
370
  // Delete the mustMatch error to reset the error on the matching control
243
- if (matchingControl.hasError('mustMatch')) {
244
- delete matchingControl.errors.mustMatch;
371
+ if (matchingControl.errors?.['mustMatch']) {
372
+ delete matchingControl.errors['mustMatch'];
245
373
  matchingControl.updateValueAndValidity();
246
374
  }
247
375
  // Don't validate empty values on the matching control
@@ -259,11 +387,69 @@ class NxeValidators {
259
387
  }
260
388
  }
261
389
 
390
+ let nextGroupId = 0;
391
+ /**
392
+ * `nxe-validation-group` — drop-in for DevExtreme `dx-validation-group`.
393
+ *
394
+ * DevExtreme lets a group be addressed two ways. Either a string name is repeated on the
395
+ * validator and on the button, or both are wrapped in a group element and the relationship is
396
+ * structural. Only the string form existed here, which meant a form could not be validated as a
397
+ * unit without inventing a name for it and keeping that name in two places.
398
+ *
399
+ * The key is generated, not derived from anything the application controls, so two groups on one
400
+ * page cannot collide and a group can never accidentally share a name with a string group. An
401
+ * explicit `validationGroup` on a descendant still wins, as in dx — the structural form is a
402
+ * default, not an override.
403
+ */
404
+ class NxeValidationGroupComponent {
405
+ registry = inject(NxeValidationGroupRegistry);
406
+ scope = inject(NXE_VALIDATION_GROUP);
407
+ /** The generated key descendants register under. Exposed for a caller that needs to name it. */
408
+ get groupKey() {
409
+ return this.scope.groupKey;
410
+ }
411
+ /** DevExtreme parity: `group.instance.validate()` / `.reset()`. */
412
+ get instance() {
413
+ return this;
414
+ }
415
+ /** Validate every validator inside this group. */
416
+ validate() {
417
+ return this.registry.validate(this.scope.groupKey);
418
+ }
419
+ /** Clear every validator inside this group without validating (dx `reset()`). */
420
+ reset() {
421
+ this.registry.reset(this.scope.groupKey);
422
+ }
423
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeValidationGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
424
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.5", type: NxeValidationGroupComponent, isStandalone: true, selector: "nxe-validation-group", providers: [
425
+ {
426
+ provide: NXE_VALIDATION_GROUP,
427
+ useFactory: () => ({ groupKey: `nxe-group-${nextGroupId++}` }),
428
+ },
429
+ ], ngImport: i0, template: '<ng-content />', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
430
+ }
431
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeValidationGroupComponent, decorators: [{
432
+ type: Component,
433
+ args: [{
434
+ selector: 'nxe-validation-group',
435
+ standalone: true,
436
+ template: '<ng-content />',
437
+ providers: [
438
+ {
439
+ provide: NXE_VALIDATION_GROUP,
440
+ useFactory: () => ({ groupKey: `nxe-group-${nextGroupId++}` }),
441
+ },
442
+ ],
443
+ changeDetection: ChangeDetectionStrategy.OnPush,
444
+ encapsulation: ViewEncapsulation.None,
445
+ }]
446
+ }] });
447
+
262
448
  // @nexterajs/ui/validation — public API.
263
449
 
264
450
  /**
265
451
  * Generated bundle index. Do not edit.
266
452
  */
267
453
 
268
- export { NXE_DEFAULT_VALIDATION_MESSAGES, NxeValidationRuleComponent, NxeValidatorComponent, NxeValidators };
454
+ export { NXE_DEFAULT_VALIDATION_MESSAGES, NxeValidationGroupComponent, NxeValidationRuleComponent, NxeValidatorComponent, NxeValidators, nxeDefaultMessage, nxeEvaluateRule, nxeIsEmptyValue };
269
455
  //# sourceMappingURL=nexterajs-ui-validation.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"nexterajs-ui-validation.mjs","sources":["../../../projects/nextera-ui/validation/src/lib/validation/nxe-validation-rule.component.ts","../../../projects/nextera-ui/validation/src/lib/validation/nxe-validator.component.ts","../../../projects/nextera-ui/validation/src/lib/validation/nxe-validators.ts","../../../projects/nextera-ui/validation/src/public-api.ts","../../../projects/nextera-ui/validation/src/nexterajs-ui-validation.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core';\r\n\r\n/** DevExtreme validation rule type (the codebase uses `required`, `compare`, and `range`). */\r\nexport type NxeValidationRuleType = 'required' | 'compare' | 'custom' | 'pattern' | 'email' | 'range';\r\n\r\n/**\r\n * The message shown when a rule fails and the caller gave none.\r\n *\r\n * These are DevExtreme's own defaults, verbatim from its `validation-*` message keys. Without\r\n * them a rule with no `message` blocks its group and displays nothing at all, because every\r\n * editor renders its error bubble under `@if (validationError())` — falsy for the empty string.\r\n */\r\nexport const NXE_DEFAULT_VALIDATION_MESSAGES: Readonly<Record<NxeValidationRuleType, string>> = {\r\n required: 'Required',\r\n email: 'Email is invalid',\r\n pattern: 'Value does not match pattern',\r\n range: 'Value is out of range',\r\n compare: 'Values do not match',\r\n custom: 'Value is invalid',\r\n};\r\n\r\n/** Payload passed to a `custom` rule's `validationCallback` (mirrors DevExtreme). */\r\nexport interface NxeCustomRuleData {\r\n value: unknown;\r\n rule: NxeValidationRuleComponent;\r\n}\r\n\r\n/**\r\n * `nxe-validation-rule` — drop-in for DevExtreme `dxi-validation-rule`.\r\n *\r\n * A no-template configuration child of `nxe-validator`. The codebase uses the universal element\r\n * with a `type` attribute (overwhelmingly `type=\"required\"`), so a single component with a\r\n * `type` input covers 100% of usage.\r\n */\r\n@Component({\r\n selector: 'nxe-validation-rule',\r\n standalone: true,\r\n template: '',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class NxeValidationRuleComponent {\r\n readonly type = input<NxeValidationRuleType>('required');\r\n readonly message = input<string>('');\r\n\r\n /** `compare` rule: returns the value to compare against. Mirrors `comparisonTarget`. */\r\n readonly comparisonTarget = input<(() => unknown) | undefined>(undefined);\r\n /** `compare` rule comparison operator (default `==`). */\r\n readonly comparisonType = input<'==' | '===' | '!=' | '!==' | '>' | '<' | '>=' | '<='>('==');\r\n\r\n /** `custom` rule: returns true when valid. Mirrors `validationCallback`. */\r\n readonly validationCallback = input<((data: NxeCustomRuleData) => boolean) | undefined>(undefined);\r\n\r\n /** `pattern` rule. */\r\n readonly pattern = input<string | RegExp | undefined>(undefined);\r\n\r\n /** `range` rule bounds (DevExtreme RangeRule: empty values pass, bounds are inclusive). */\r\n readonly min = input<number | undefined>(undefined);\r\n readonly max = input<number | undefined>(undefined);\r\n\r\n /**\r\n * The message to show when this rule fails: the caller's, or DevExtreme's default for the\r\n * rule type. Whitespace counts as absent — a message of spaces is still an empty bubble.\r\n */\r\n readonly resolvedMessage = computed(\r\n () => this.message().trim() || NXE_DEFAULT_VALIDATION_MESSAGES[this.type()] || '',\r\n );\r\n\r\n /** Whether `value` satisfies this rule. */\r\n evaluate(value: unknown): boolean {\r\n switch (this.type()) {\r\n case 'required':\r\n return !this.isEmpty(value);\r\n case 'compare':\r\n return this.compare(value);\r\n case 'range':\r\n return this.isEmpty(value) || this.inRange(value);\r\n case 'pattern':\r\n return this.isEmpty(value) || this.matchesPattern(value);\r\n case 'email':\r\n return this.isEmpty(value) || /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(String(value));\r\n case 'custom':\r\n return this.validationCallback()?.({ value, rule: this }) ?? true;\r\n default:\r\n return true;\r\n }\r\n }\r\n\r\n /** DevExtreme RequiredRule semantics: null/undefined/''/false are \"not specified\". */\r\n private isEmpty(value: unknown): boolean {\r\n return (\r\n value === null ||\r\n value === undefined ||\r\n value === '' ||\r\n value === false ||\r\n (Array.isArray(value) && value.length === 0)\r\n );\r\n }\r\n\r\n private compare(value: unknown): boolean {\r\n const target = this.comparisonTarget()?.();\r\n switch (this.comparisonType()) {\r\n case '===':\r\n return value === target;\r\n case '!=':\r\n // eslint-disable-next-line eqeqeq\r\n return value != target;\r\n case '!==':\r\n return value !== target;\r\n case '>':\r\n return (value as number) > (target as number);\r\n case '<':\r\n return (value as number) < (target as number);\r\n case '>=':\r\n return (value as number) >= (target as number);\r\n case '<=':\r\n return (value as number) <= (target as number);\r\n case '==':\r\n default:\r\n // eslint-disable-next-line eqeqeq\r\n return value == target;\r\n }\r\n }\r\n\r\n private inRange(value: unknown): boolean {\r\n const num = Number(value);\r\n if (Number.isNaN(num)) {\r\n return false;\r\n }\r\n const min = this.min();\r\n const max = this.max();\r\n return (min === undefined || num >= min) && (max === undefined || num <= max);\r\n }\r\n\r\n private matchesPattern(value: unknown): boolean {\r\n const pattern = this.pattern();\r\n if (!pattern) {\r\n return true;\r\n }\r\n const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern;\r\n return regex.test(String(value));\r\n }\r\n}\r\n","import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n contentChildren,\r\n DestroyRef,\r\n inject,\r\n Injector,\r\n input,\r\n signal,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';\r\nimport { distinctUntilChanged, pairwise, startWith } from 'rxjs';\r\nimport { NxeValidationGroupRegistry } from '@nexterajs/ui/core';\r\nimport {\r\n NXE_VALIDATION_TARGET,\r\n NxeValidationResult,\r\n} from '@nexterajs/ui/core';\r\nimport { NxeValidationRuleComponent } from './nxe-validation-rule.component';\r\n\r\n/**\r\n * `nxe-validator` — drop-in for DevExtreme `dx-validator`.\r\n *\r\n * Placed inside an editor (`<nxe-text-box><nxe-validator><nxe-validation-rule .../></nxe-validator></nxe-text-box>`),\r\n * it adapts the editor via {@link NXE_VALIDATION_TARGET}, collects its `nxe-validation-rule`\r\n * children, and registers into the {@link NxeValidationGroupRegistry} under the editor's\r\n * `validationGroup`. Validation is on-demand (matching DevExtreme: not auto-run on change) —\r\n * `validate()` is invoked by `nxe-button[validationGroup]` (group-level) or directly.\r\n */\r\n@Component({\r\n selector: 'nxe-validator',\r\n standalone: true,\r\n template: '<ng-content />',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class NxeValidatorComponent {\r\n private readonly target = inject(NXE_VALIDATION_TARGET, { optional: true });\r\n private readonly registry = inject(NxeValidationGroupRegistry);\r\n\r\n readonly rules = contentChildren(NxeValidationRuleComponent);\r\n\r\n /** Explicit group; otherwise inherited from the wrapped editor's `validationGroup`. */\r\n readonly validationGroupInput = input<string | undefined>(undefined, { alias: 'validationGroup' });\r\n // Bare validators (no group) join the implicit default group `''` — mirroring DevExtreme's\r\n // global default group — so a groupless `nxe-button` validates them.\r\n private readonly group = computed(\r\n () => this.validationGroupInput() ?? this.target?.validationGroup() ?? '',\r\n );\r\n\r\n /** Last validation outcome (false once a rule has failed, until re-validated). */\r\n readonly isValid = signal(true);\r\n\r\n /** DevExtreme parity: existing code calls `validator.instance.validate()` via `@ViewChild`. */\r\n get instance(): NxeValidatorComponent {\r\n return this;\r\n }\r\n\r\n private readonly member = { validate: () => this.validate() };\r\n\r\n constructor() {\r\n // Register/unregister into the named group reactively (handles dynamic group changes).\r\n /* ⚠ NOT an effect - UI-48. The registration follows ONE signal, and the unregister that\r\n `onCleanup` used to run is the pairwise step below: a group change unregisters the old\r\n one before registering the new, and destruction unregisters the last. */\r\n toObservable(this.group, { injector: inject(Injector) })\r\n .pipe(\r\n distinctUntilChanged(),\r\n startWith(undefined as string | undefined),\r\n pairwise(),\r\n takeUntilDestroyed(),\r\n )\r\n .subscribe(([ previous, group ]) => {\r\n if (previous !== undefined) {\r\n this.registry.unregister(previous, this.member);\r\n }\r\n\r\n this.registry.register(group, this.member);\r\n });\r\n\r\n inject(DestroyRef).onDestroy(() => this.registry.unregister(this.group(), this.member));\r\n }\r\n\r\n /** Run all rules against the editor's current value; pushes the first failing message. */\r\n validate(): NxeValidationResult {\r\n const value = this.target?.getValue();\r\n for (const rule of this.rules()) {\r\n if (!rule.evaluate(value)) {\r\n // resolvedMessage(), not message(): a rule declared without one would otherwise\r\n // push the empty string, and every editor guards its bubble with\r\n // `@if (validationError())` — so the field would block the group in silence.\r\n const message = rule.resolvedMessage();\r\n this.isValid.set(false);\r\n this.target?.setValidationError(message);\r\n return {\r\n isValid: false,\r\n brokenRules: [{ type: rule.type(), message }],\r\n status: 'invalid',\r\n };\r\n }\r\n }\r\n this.isValid.set(true);\r\n this.target?.setValidationError(null);\r\n return { isValid: true, brokenRules: [], status: 'valid' };\r\n }\r\n}\r\n","import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';\r\n\r\nexport class NxeValidators {\r\n /**\r\n * Check for empty (optional fields) values\r\n *\r\n * @param value\r\n */\r\n static isEmptyInputValue(value: any): boolean {\r\n return value == null || value.length === 0;\r\n }\r\n\r\n /**\r\n * Must match validator\r\n *\r\n * @param controlPath A dot-delimited string values that define the path to the control.\r\n * @param matchingControlPath A dot-delimited string values that define the path to the matching control.\r\n */\r\n static mustMatch(controlPath: string, matchingControlPath: string): ValidatorFn {\r\n return (formGroup: AbstractControl): ValidationErrors | null => {\r\n // Get the control and matching control\r\n const control = formGroup.get(controlPath);\r\n const matchingControl = formGroup.get(matchingControlPath);\r\n\r\n // Return if control or matching control doesn't exist\r\n if (!control || !matchingControl) {\r\n return null;\r\n }\r\n\r\n // Delete the mustMatch error to reset the error on the matching control\r\n if (matchingControl.hasError('mustMatch')) {\r\n delete matchingControl.errors.mustMatch;\r\n matchingControl.updateValueAndValidity();\r\n }\r\n\r\n // Don't validate empty values on the matching control\r\n // Don't validate if values are matching\r\n if (this.isEmptyInputValue(matchingControl.value) || control.value === matchingControl.value) {\r\n return null;\r\n }\r\n\r\n // Prepare the validation errors\r\n const errors = { mustMatch: true };\r\n\r\n // Set the validation error on the matching control\r\n matchingControl.setErrors(errors);\r\n\r\n // Return the errors\r\n return errors;\r\n };\r\n }\r\n}\r\n","// @nexterajs/ui/validation — public API.\r\nexport * from './lib/validation/nxe-validation-rule.component';\r\nexport * from './lib/validation/nxe-validator.component';\r\nexport * from './lib/validation/nxe-validators';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nexterajs-ui-validation.mjs","sources":["../../../projects/nextera-ui/validation/src/lib/validation/nxe-validation-rule.component.ts","../../../projects/nextera-ui/validation/src/lib/validation/nxe-validator.component.ts","../../../projects/nextera-ui/validation/src/lib/validation/nxe-validators.ts","../../../projects/nextera-ui/validation/src/lib/validation/nxe-validation-group.component.ts","../../../projects/nextera-ui/validation/src/public-api.ts","../../../projects/nextera-ui/validation/src/nexterajs-ui-validation.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input, ViewEncapsulation } from '@angular/core';\r\nimport { NxeMessageService } from '@nexterajs/ui/core';\r\n\r\n/** DevExtreme validation rule type (the codebase uses `required`, `compare`, and `range`). */\r\nexport type NxeValidationRuleType = 'required' | 'compare' | 'custom' | 'pattern' | 'email' | 'range';\r\n\r\n/**\r\n * The English message shown when a rule fails and the caller gave none. The components resolve it\r\n * through `NxeMessageService` (`validation.rule.*`), which starts as these same strings and\r\n * follows the active language; this table stays for code that reads it directly.\r\n *\r\n * These are DevExtreme's own defaults, verbatim from its `validation-*` message keys. Without\r\n * them a rule with no `message` blocks its group and displays nothing at all, because every\r\n * editor renders its error bubble under `@if (validationError())` — falsy for the empty string.\r\n */\r\nexport const NXE_DEFAULT_VALIDATION_MESSAGES: Readonly<Record<NxeValidationRuleType, string>> = {\r\n required: 'Required',\r\n email: 'Email is invalid',\r\n pattern: 'Value does not match pattern',\r\n range: 'Value is out of range',\r\n compare: 'Values do not match',\r\n custom: 'Value is invalid',\r\n};\r\n\r\n/** Payload passed to a `custom` rule's `validationCallback` (mirrors DevExtreme). */\r\nexport interface NxeCustomRuleData {\r\n value: unknown;\r\n /** Absent when the rule came from `[validationRules]` rather than a projected element. */\r\n rule?: NxeValidationRuleComponent;\r\n}\r\n\r\n/**\r\n * A rule as plain data.\r\n *\r\n * The projected `<nxe-validation-rule>` and the `[validationRules]` array are two ways of saying\r\n * the same thing, so they evaluate through one function rather than two implementations that\r\n * could drift.\r\n */\r\nexport interface NxeRuleSpec {\r\n type: NxeValidationRuleType;\r\n message?: string;\r\n comparisonTarget?: () => unknown;\r\n comparisonType?: '==' | '===' | '!=' | '!==' | '>' | '<' | '>=' | '<=';\r\n validationCallback?: (data: NxeCustomRuleData) => boolean;\r\n pattern?: string | RegExp;\r\n min?: number;\r\n max?: number;\r\n}\r\n\r\n/** DevExtreme RequiredRule semantics: null/undefined/''/false are \"not specified\". */\r\nexport function nxeIsEmptyValue(value: unknown): boolean {\r\n return (\r\n value === null ||\r\n value === undefined ||\r\n value === '' ||\r\n value === false ||\r\n (Array.isArray(value) && value.length === 0)\r\n );\r\n}\r\n\r\n/**\r\n * The message for a rule with none of its own: the active language's when a message service is\r\n * given, English otherwise.\r\n */\r\nexport function nxeDefaultMessage(type: NxeValidationRuleType, messages?: NxeMessageService): string {\r\n return messages && type in NXE_DEFAULT_VALIDATION_MESSAGES\r\n ? messages.t(`validation.rule.${type}`)\r\n : (NXE_DEFAULT_VALIDATION_MESSAGES[type] ?? '');\r\n}\r\n\r\n/** Whether `value` satisfies `spec`. The single evaluator behind both rule forms. */\r\nexport function nxeEvaluateRule(spec: NxeRuleSpec, value: unknown, rule?: NxeValidationRuleComponent): boolean {\r\n switch (spec.type) {\r\n case 'required':\r\n return !nxeIsEmptyValue(value);\r\n case 'compare': {\r\n const target = spec.comparisonTarget?.();\r\n switch (spec.comparisonType) {\r\n case '===':\r\n return value === target;\r\n case '!=':\r\n // eslint-disable-next-line eqeqeq\r\n return value != target;\r\n case '!==':\r\n return value !== target;\r\n case '>':\r\n return (value as number) > (target as number);\r\n case '<':\r\n return (value as number) < (target as number);\r\n case '>=':\r\n return (value as number) >= (target as number);\r\n case '<=':\r\n return (value as number) <= (target as number);\r\n case '==':\r\n default:\r\n // eslint-disable-next-line eqeqeq\r\n return value == target;\r\n }\r\n }\r\n case 'range': {\r\n if (nxeIsEmptyValue(value)) {\r\n return true;\r\n }\r\n const num = Number(value);\r\n if (Number.isNaN(num)) {\r\n return false;\r\n }\r\n return (\r\n (spec.min === undefined || num >= spec.min) &&\r\n (spec.max === undefined || num <= spec.max)\r\n );\r\n }\r\n case 'pattern': {\r\n if (nxeIsEmptyValue(value) || !spec.pattern) {\r\n return true;\r\n }\r\n const regex = typeof spec.pattern === 'string' ? new RegExp(spec.pattern) : spec.pattern;\r\n return regex.test(String(value));\r\n }\r\n case 'email':\r\n return nxeIsEmptyValue(value) || /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(String(value));\r\n case 'custom':\r\n return spec.validationCallback?.({ value, rule }) ?? true;\r\n default:\r\n return true;\r\n }\r\n}\r\n\r\n/**\r\n * `nxe-validation-rule` — drop-in for DevExtreme `dxi-validation-rule`.\r\n *\r\n * A no-template configuration child of `nxe-validator`. The codebase uses the universal element\r\n * with a `type` attribute (overwhelmingly `type=\"required\"`), so a single component with a\r\n * `type` input covers 100% of usage.\r\n */\r\n@Component({\r\n selector: 'nxe-validation-rule',\r\n standalone: true,\r\n template: '',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class NxeValidationRuleComponent {\r\n private readonly messages = inject(NxeMessageService);\r\n\r\n readonly type = input<NxeValidationRuleType>('required');\r\n readonly message = input<string>('');\r\n\r\n /** `compare` rule: returns the value to compare against. Mirrors `comparisonTarget`. */\r\n readonly comparisonTarget = input<(() => unknown) | undefined>(undefined);\r\n /** `compare` rule comparison operator (default `==`). */\r\n readonly comparisonType = input<'==' | '===' | '!=' | '!==' | '>' | '<' | '>=' | '<='>('==');\r\n\r\n /** `custom` rule: returns true when valid. Mirrors `validationCallback`. */\r\n readonly validationCallback = input<((data: NxeCustomRuleData) => boolean) | undefined>(undefined);\r\n\r\n /** `pattern` rule. */\r\n readonly pattern = input<string | RegExp | undefined>(undefined);\r\n\r\n /** `range` rule bounds (DevExtreme RangeRule: empty values pass, bounds are inclusive). */\r\n readonly min = input<number | undefined>(undefined);\r\n readonly max = input<number | undefined>(undefined);\r\n\r\n /**\r\n * The message to show when this rule fails: the caller's, or DevExtreme's default for the\r\n * rule type. Whitespace counts as absent — a message of spaces is still an empty bubble.\r\n */\r\n readonly resolvedMessage = computed(\r\n () => this.message().trim() || nxeDefaultMessage(this.type(), this.messages),\r\n );\r\n\r\n /** This rule as plain data, so both rule forms evaluate through one function. */\r\n toSpec(): NxeRuleSpec {\r\n return {\r\n type: this.type(),\r\n message: this.message(),\r\n comparisonTarget: this.comparisonTarget(),\r\n comparisonType: this.comparisonType(),\r\n validationCallback: this.validationCallback(),\r\n pattern: this.pattern(),\r\n min: this.min(),\r\n max: this.max(),\r\n };\r\n }\r\n\r\n /** Whether `value` satisfies this rule. */\r\n evaluate(value: unknown): boolean {\r\n return nxeEvaluateRule(this.toSpec(), value, this);\r\n }\r\n}\r\n","import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n contentChildren,\r\n DestroyRef,\r\n inject,\r\n Injector,\r\n input,\r\n output,\r\n signal,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';\r\nimport { distinctUntilChanged, pairwise, skip, startWith } from 'rxjs';\r\nimport { NxeMessageService, NxeValidationGroupRegistry } from '@nexterajs/ui/core';\r\nimport {\r\n NXE_VALIDATION_GROUP,\r\n NXE_VALIDATION_TARGET,\r\n NxeBrokenRule,\r\n NxeValidatedEvent,\r\n NxeValidationResult,\r\n} from '@nexterajs/ui/core';\r\nimport {\r\n nxeDefaultMessage,\r\n nxeEvaluateRule,\r\n NxeRuleSpec,\r\n NxeValidationRuleComponent,\r\n} from './nxe-validation-rule.component';\r\n\r\n/**\r\n * `nxe-validator` — drop-in for DevExtreme `dx-validator`.\r\n *\r\n * Placed inside an editor (`<nxe-text-box><nxe-validator><nxe-validation-rule .../></nxe-validator></nxe-text-box>`),\r\n * it adapts the editor via {@link NXE_VALIDATION_TARGET}, collects its rules, and registers into\r\n * the {@link NxeValidationGroupRegistry} under the editor's `validationGroup`.\r\n *\r\n * Validation is on demand, as in DevExtreme — `validate()` is invoked by\r\n * `nxe-button[validationGroup]` or directly. With one exception, which is also dx's: once a\r\n * validator HAS been validated, it re-validates as the value changes, so a field that has gone\r\n * red clears itself as the user fixes it rather than staying red until the next submit.\r\n */\r\n@Component({\r\n selector: 'nxe-validator',\r\n standalone: true,\r\n template: '<ng-content />',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class NxeValidatorComponent {\r\n private readonly target = inject(NXE_VALIDATION_TARGET, { optional: true });\r\n private readonly registry = inject(NxeValidationGroupRegistry);\r\n private readonly messages = inject(NxeMessageService);\r\n private readonly groupScope = inject(NXE_VALIDATION_GROUP, { optional: true });\r\n\r\n readonly rules = contentChildren(NxeValidationRuleComponent);\r\n\r\n /**\r\n * dx `validationRules` — rules as data instead of projected children. Both forms combine;\r\n * projected children come first, matching declaration order.\r\n */\r\n readonly validationRules = input<readonly NxeRuleSpec[] | undefined>(undefined);\r\n\r\n /** Explicit group; otherwise inherited from the wrapped editor's `validationGroup`. */\r\n readonly validationGroupInput = input<string | undefined>(undefined, { alias: 'validationGroup' });\r\n\r\n /** dx `onValidated`, fired on every validation, valid or not. */\r\n readonly onValidated = output<NxeValidatedEvent>();\r\n\r\n // An explicit name wins over a surrounding `nxe-validation-group`, as in dx. Bare validators\r\n // with neither join the implicit default group `''` — dx's global group — so a groupless\r\n // `nxe-button` validates them.\r\n private readonly group = computed(\r\n () =>\r\n this.validationGroupInput() ??\r\n this.target?.validationGroup() ??\r\n this.groupScope?.groupKey ??\r\n '',\r\n );\r\n\r\n /** Last validation outcome (false once a rule has failed, until re-validated). */\r\n readonly isValid = signal(true);\r\n\r\n /** Every rule broken by the last validation, in declaration order. */\r\n readonly brokenRules = signal<readonly NxeBrokenRule[]>([]);\r\n\r\n /** Whether `validate()` has ever run — the gate on live re-validation. */\r\n private readonly validated = signal(false);\r\n\r\n /** DevExtreme parity: existing code calls `validator.instance.validate()` via `@ViewChild`. */\r\n get instance(): NxeValidatorComponent {\r\n return this;\r\n }\r\n\r\n private readonly member = { validate: () => this.validate(), reset: () => this.reset() };\r\n\r\n /** Projected children plus `[validationRules]`, as one evaluable list. */\r\n private readonly allRules = computed(() => [\r\n ...this.rules().map((rule) => ({\r\n type: rule.type(),\r\n evaluate: (value: unknown) => rule.evaluate(value),\r\n message: () => rule.resolvedMessage(),\r\n })),\r\n ...(this.validationRules() ?? []).map((spec) => ({\r\n type: spec.type,\r\n evaluate: (value: unknown) => nxeEvaluateRule(spec, value),\r\n message: () => spec.message?.trim() || nxeDefaultMessage(spec.type, this.messages),\r\n })),\r\n ]);\r\n\r\n constructor() {\r\n const injector = inject(Injector);\r\n\r\n // Register/unregister into the named group reactively (handles dynamic group changes).\r\n /* ⚠ NOT an effect - UI-48. The registration follows ONE signal, and the unregister that\r\n `onCleanup` used to run is the pairwise step below: a group change unregisters the old\r\n one before registering the new, and destruction unregisters the last. */\r\n toObservable(this.group, { injector })\r\n .pipe(\r\n distinctUntilChanged(),\r\n startWith(undefined as string | undefined),\r\n pairwise(),\r\n takeUntilDestroyed(),\r\n )\r\n .subscribe(([previous, group]) => {\r\n if (previous !== undefined) {\r\n this.registry.unregister(previous, this.member);\r\n }\r\n\r\n if (group !== undefined) {\n this.registry.register(group, this.member);\n }\r\n });\r\n\r\n // dx re-validates a field that has already been validated as its value changes, so an\r\n // error clears while the user types rather than surviving until the next submit.\r\n /* ⚠ NOT an effect - UI-48. This takes exactly one dependency, the target's value, named\r\n here rather than inferred from whatever `validate()` happens to read. */\r\n toObservable(\r\n computed(() => this.target?.getValue()),\r\n { injector },\r\n )\r\n .pipe(skip(1), takeUntilDestroyed())\r\n .subscribe(() => {\r\n if (this.validated()) {\r\n this.validate();\r\n }\r\n });\r\n\r\n // A bubble that is already showing is a string the editor holds, so it would keep the old\r\n // language after a switch: rewrite it when the bundle changes.\r\n /* NOT an effect: one named dependency, the active bundle. */\r\n toObservable(this.messages.messages, { injector })\r\n .pipe(skip(1), takeUntilDestroyed())\r\n .subscribe(() => this.refreshMessages());\r\n\r\n // Tell the editor whether it is required, so it can render aria-required. The rule is\r\n // where the requirement is stated; the editor should not have to be told twice.\r\n /* ⚠ NOT an effect - UI-48: one named dependency, the resolved rule list. */\r\n toObservable(\r\n computed(() => this.allRules().some((rule) => rule.type === 'required')),\r\n { injector },\r\n )\r\n .pipe(takeUntilDestroyed())\r\n .subscribe((required) => this.target?.setRequired?.(required));\r\n\r\n inject(DestroyRef).onDestroy(() => {\r\n this.registry.unregister(this.group(), this.member);\r\n // The editor outlives its validator when the validator sits inside an @if, and a\r\n // field left advertising aria-required by a rule that no longer exists is worse than\r\n // one that never advertised it.\r\n this.target?.setRequired?.(false);\r\n this.target?.setValidationError(null);\r\n });\r\n }\r\n\r\n\r\n /** Re-word the broken rules after a language switch, without validating again or emitting. */\r\n private refreshMessages(): void {\r\n if (!this.validated() || this.isValid()) {\r\n return;\r\n }\r\n const value = this.target?.getValue();\r\n const broken = this.allRules()\r\n .filter((rule) => !rule.evaluate(value))\r\n .map((rule) => ({ type: rule.type, message: rule.message() }));\r\n if (broken.length) {\r\n this.brokenRules.set(broken);\r\n this.target?.setValidationError(broken[0].message);\r\n }\r\n }\r\n\r\n /**\r\n * Run every rule against the editor's current value.\r\n *\r\n * dx reports ALL broken rules, not just the first — a `brokenRules` array with at most one\r\n * entry cannot tell a caller whether a field failed once or three times. The editor's bubble\r\n * still shows the first message, which is what dx renders.\r\n */\r\n validate(): NxeValidationResult {\r\n const value = this.target?.getValue();\r\n const broken: NxeBrokenRule[] = [];\r\n for (const rule of this.allRules()) {\r\n if (!rule.evaluate(value)) {\r\n // resolvedMessage(), not message(): a rule declared without one would otherwise\r\n // push the empty string, and every editor guards its bubble with\r\n // `@if (validationError())` — so the field would block the group in silence.\r\n broken.push({ type: rule.type, message: rule.message() });\r\n }\r\n }\r\n this.validated.set(true);\r\n this.brokenRules.set(broken);\r\n this.isValid.set(broken.length === 0);\r\n this.target?.setValidationError(broken.length ? broken[0].message : null);\r\n\r\n const result: NxeValidationResult = {\r\n isValid: broken.length === 0,\r\n brokenRules: broken,\r\n status: broken.length === 0 ? 'valid' : 'invalid',\r\n };\r\n this.onValidated.emit({ ...result, value, validationGroup: this.group() });\r\n return result;\r\n }\r\n\r\n /**\r\n * Clear the error state without validating (dx `reset()`).\r\n *\r\n * Also clears `validated`, so the field goes back to staying quiet until it is validated\r\n * again — otherwise a reset field would light up on the next keystroke.\r\n */\r\n reset(): void {\r\n this.validated.set(false);\r\n this.brokenRules.set([]);\r\n this.isValid.set(true);\r\n this.target?.setValidationError(null);\r\n }\r\n}\r\n","import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';\n\nexport class NxeValidators {\n /**\n * Check for empty (optional fields) values\n *\n * @param value\n */\n static isEmptyInputValue(value: any): boolean {\n return value == null || value.length === 0;\n }\n\n /**\n * Must match validator\n *\n * @param controlPath A dot-delimited string values that define the path to the control.\n * @param matchingControlPath A dot-delimited string values that define the path to the matching control.\n */\n static mustMatch(controlPath: string, matchingControlPath: string): ValidatorFn {\n return (formGroup: AbstractControl): ValidationErrors | null => {\n // Get the control and matching control\n const control = formGroup.get(controlPath);\n const matchingControl = formGroup.get(matchingControlPath);\n\n // Return if control or matching control doesn't exist\n if (!control || !matchingControl) {\n return null;\n }\n\n // Delete the mustMatch error to reset the error on the matching control\n if (matchingControl.errors?.['mustMatch']) {\n delete matchingControl.errors['mustMatch'];\n matchingControl.updateValueAndValidity();\n }\n\n // Don't validate empty values on the matching control\n // Don't validate if values are matching\n if (this.isEmptyInputValue(matchingControl.value) || control.value === matchingControl.value) {\n return null;\n }\n\n // Prepare the validation errors\n const errors = { mustMatch: true };\n\n // Set the validation error on the matching control\n matchingControl.setErrors(errors);\n\n // Return the errors\n return errors;\n };\n }\n}\n","import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n inject,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport {\r\n NXE_VALIDATION_GROUP,\r\n NxeValidationGroupRegistry,\r\n NxeValidationResult,\r\n} from '@nexterajs/ui/core';\r\n\r\nlet nextGroupId = 0;\r\n\r\n/**\r\n * `nxe-validation-group` — drop-in for DevExtreme `dx-validation-group`.\r\n *\r\n * DevExtreme lets a group be addressed two ways. Either a string name is repeated on the\r\n * validator and on the button, or both are wrapped in a group element and the relationship is\r\n * structural. Only the string form existed here, which meant a form could not be validated as a\r\n * unit without inventing a name for it and keeping that name in two places.\r\n *\r\n * The key is generated, not derived from anything the application controls, so two groups on one\r\n * page cannot collide and a group can never accidentally share a name with a string group. An\r\n * explicit `validationGroup` on a descendant still wins, as in dx — the structural form is a\r\n * default, not an override.\r\n */\r\n@Component({\r\n selector: 'nxe-validation-group',\r\n standalone: true,\r\n template: '<ng-content />',\r\n providers: [\r\n {\r\n provide: NXE_VALIDATION_GROUP,\r\n useFactory: () => ({ groupKey: `nxe-group-${nextGroupId++}` }),\r\n },\r\n ],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class NxeValidationGroupComponent {\r\n private readonly registry = inject(NxeValidationGroupRegistry);\r\n private readonly scope = inject(NXE_VALIDATION_GROUP);\r\n\r\n /** The generated key descendants register under. Exposed for a caller that needs to name it. */\r\n get groupKey(): string {\r\n return this.scope.groupKey;\r\n }\r\n\r\n /** DevExtreme parity: `group.instance.validate()` / `.reset()`. */\r\n get instance(): NxeValidationGroupComponent {\r\n return this;\r\n }\r\n\r\n /** Validate every validator inside this group. */\r\n validate(): NxeValidationResult {\r\n return this.registry.validate(this.scope.groupKey);\r\n }\r\n\r\n /** Clear every validator inside this group without validating (dx `reset()`). */\r\n reset(): void {\r\n this.registry.reset(this.scope.groupKey);\r\n }\r\n}\r\n","// @nexterajs/ui/validation — public API.\r\nexport * from './lib/validation/nxe-validation-rule.component';\r\nexport * from './lib/validation/nxe-validator.component';\r\nexport * from './lib/validation/nxe-validators';\r\nexport * from './lib/validation/nxe-validation-group.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,12 +1,18 @@
1
1
  export * from '@nexterajs/ui/animations';
2
+ export * from '@nexterajs/ui/charts';
2
3
  export * from '@nexterajs/ui/core';
4
+ export * from '@nexterajs/ui/display';
3
5
  export * from '@nexterajs/ui/dropdowns';
4
6
  export * from '@nexterajs/ui/excel';
5
7
  export * from '@nexterajs/ui/file-uploader';
6
8
  export * from '@nexterajs/ui/forms';
7
9
  export * from '@nexterajs/ui/grid';
8
10
  export * from '@nexterajs/ui/intl';
11
+ export * from '@nexterajs/ui/navigation';
12
+ export * from '@nexterajs/ui/notify';
13
+ export * from '@nexterajs/ui/overlay';
9
14
  export * from '@nexterajs/ui/pivot';
15
+ export * from '@nexterajs/ui/shell';
10
16
  export * from '@nexterajs/ui/styling';
11
17
  export * from '@nexterajs/ui/tree-view';
12
18
  export * from '@nexterajs/ui/validation';