ngx-semantic 2.0.7-beta.0 → 2.0.7-beta.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 (116) hide show
  1. package/fesm2022/ngx-semantic-collections-breadcrumb.mjs +13 -10
  2. package/fesm2022/ngx-semantic-collections-breadcrumb.mjs.map +1 -1
  3. package/fesm2022/ngx-semantic-collections-form.mjs +987 -16
  4. package/fesm2022/ngx-semantic-collections-form.mjs.map +1 -1
  5. package/fesm2022/ngx-semantic-collections-grid.mjs +13 -10
  6. package/fesm2022/ngx-semantic-collections-grid.mjs.map +1 -1
  7. package/fesm2022/ngx-semantic-collections-menu.mjs +25 -13
  8. package/fesm2022/ngx-semantic-collections-menu.mjs.map +1 -1
  9. package/fesm2022/ngx-semantic-collections-message.mjs +20 -20
  10. package/fesm2022/ngx-semantic-collections-message.mjs.map +1 -1
  11. package/fesm2022/ngx-semantic-collections-table.mjs +17 -13
  12. package/fesm2022/ngx-semantic-collections-table.mjs.map +1 -1
  13. package/fesm2022/ngx-semantic-core-base.mjs +1 -0
  14. package/fesm2022/ngx-semantic-core-base.mjs.map +1 -1
  15. package/fesm2022/ngx-semantic-core-util.mjs.map +1 -1
  16. package/fesm2022/ngx-semantic-elements-button.mjs +9 -7
  17. package/fesm2022/ngx-semantic-elements-button.mjs.map +1 -1
  18. package/fesm2022/ngx-semantic-elements-container.mjs.map +1 -1
  19. package/fesm2022/ngx-semantic-elements-divider.mjs.map +1 -1
  20. package/fesm2022/ngx-semantic-elements-flag.mjs +5 -4
  21. package/fesm2022/ngx-semantic-elements-flag.mjs.map +1 -1
  22. package/fesm2022/ngx-semantic-elements-header.mjs.map +1 -1
  23. package/fesm2022/ngx-semantic-elements-icon.mjs +5 -4
  24. package/fesm2022/ngx-semantic-elements-icon.mjs.map +1 -1
  25. package/fesm2022/ngx-semantic-elements-image.mjs +5 -4
  26. package/fesm2022/ngx-semantic-elements-image.mjs.map +1 -1
  27. package/fesm2022/ngx-semantic-elements-input.mjs +5 -4
  28. package/fesm2022/ngx-semantic-elements-input.mjs.map +1 -1
  29. package/fesm2022/ngx-semantic-elements-label.mjs +5 -4
  30. package/fesm2022/ngx-semantic-elements-label.mjs.map +1 -1
  31. package/fesm2022/ngx-semantic-elements-list.mjs.map +1 -1
  32. package/fesm2022/ngx-semantic-elements-loader.mjs.map +1 -1
  33. package/fesm2022/ngx-semantic-elements-placeholder.mjs.map +1 -1
  34. package/fesm2022/ngx-semantic-elements-rail.mjs.map +1 -1
  35. package/fesm2022/ngx-semantic-elements-reveal.mjs.map +1 -1
  36. package/fesm2022/ngx-semantic-elements-segment.mjs +5 -4
  37. package/fesm2022/ngx-semantic-elements-segment.mjs.map +1 -1
  38. package/fesm2022/ngx-semantic-elements-steps.mjs +5 -4
  39. package/fesm2022/ngx-semantic-elements-steps.mjs.map +1 -1
  40. package/fesm2022/ngx-semantic-modules-accordion.mjs.map +1 -1
  41. package/fesm2022/ngx-semantic-modules-checkbox.mjs +19 -19
  42. package/fesm2022/ngx-semantic-modules-checkbox.mjs.map +1 -1
  43. package/fesm2022/ngx-semantic-modules-dimmer.mjs +42 -33
  44. package/fesm2022/ngx-semantic-modules-dimmer.mjs.map +1 -1
  45. package/fesm2022/ngx-semantic-modules-dropdown.mjs.map +1 -1
  46. package/fesm2022/ngx-semantic-modules-embed.mjs +22 -22
  47. package/fesm2022/ngx-semantic-modules-embed.mjs.map +1 -1
  48. package/fesm2022/ngx-semantic-modules-modal.mjs +31 -32
  49. package/fesm2022/ngx-semantic-modules-modal.mjs.map +1 -1
  50. package/fesm2022/ngx-semantic-modules-popup.mjs +22 -23
  51. package/fesm2022/ngx-semantic-modules-popup.mjs.map +1 -1
  52. package/fesm2022/ngx-semantic-modules-progress.mjs +6 -6
  53. package/fesm2022/ngx-semantic-modules-progress.mjs.map +1 -1
  54. package/fesm2022/ngx-semantic-modules-rating.mjs +15 -17
  55. package/fesm2022/ngx-semantic-modules-rating.mjs.map +1 -1
  56. package/fesm2022/ngx-semantic-modules-search.mjs +34 -34
  57. package/fesm2022/ngx-semantic-modules-search.mjs.map +1 -1
  58. package/fesm2022/ngx-semantic-modules-select.mjs +87 -88
  59. package/fesm2022/ngx-semantic-modules-select.mjs.map +1 -1
  60. package/fesm2022/ngx-semantic-modules-shape.mjs +6 -6
  61. package/fesm2022/ngx-semantic-modules-shape.mjs.map +1 -1
  62. package/fesm2022/ngx-semantic-modules-sidebar.mjs +13 -12
  63. package/fesm2022/ngx-semantic-modules-sidebar.mjs.map +1 -1
  64. package/fesm2022/ngx-semantic-modules-sticky.mjs +9 -12
  65. package/fesm2022/ngx-semantic-modules-sticky.mjs.map +1 -1
  66. package/fesm2022/ngx-semantic-modules-tabs.mjs +30 -30
  67. package/fesm2022/ngx-semantic-modules-tabs.mjs.map +1 -1
  68. package/fesm2022/ngx-semantic-modules-transition.mjs +721 -0
  69. package/fesm2022/ngx-semantic-modules-transition.mjs.map +1 -0
  70. package/fesm2022/ngx-semantic-views-advertisement.mjs +5 -4
  71. package/fesm2022/ngx-semantic-views-advertisement.mjs.map +1 -1
  72. package/fesm2022/ngx-semantic-views-card.mjs +6 -9
  73. package/fesm2022/ngx-semantic-views-card.mjs.map +1 -1
  74. package/fesm2022/ngx-semantic-views-comment.mjs.map +1 -1
  75. package/fesm2022/ngx-semantic-views-feed.mjs +26 -26
  76. package/fesm2022/ngx-semantic-views-feed.mjs.map +1 -1
  77. package/fesm2022/ngx-semantic-views-items.mjs +13 -10
  78. package/fesm2022/ngx-semantic-views-items.mjs.map +1 -1
  79. package/fesm2022/ngx-semantic-views-statistics.mjs +16 -16
  80. package/fesm2022/ngx-semantic-views-statistics.mjs.map +1 -1
  81. package/fesm2022/ngx-semantic.mjs +11 -5
  82. package/fesm2022/ngx-semantic.mjs.map +1 -1
  83. package/modules/transition/package.json +4 -0
  84. package/package.json +5 -1
  85. package/types/ngx-semantic-collections-breadcrumb.d.ts +4 -17
  86. package/types/ngx-semantic-collections-form.d.ts +150 -20
  87. package/types/ngx-semantic-collections-grid.d.ts +4 -17
  88. package/types/ngx-semantic-collections-menu.d.ts +4 -17
  89. package/types/ngx-semantic-collections-message.d.ts +1 -3
  90. package/types/ngx-semantic-collections-table.d.ts +5 -22
  91. package/types/ngx-semantic-core-base.d.ts +1 -1
  92. package/types/ngx-semantic-elements-button.d.ts +3 -8
  93. package/types/ngx-semantic-elements-flag.d.ts +2 -3
  94. package/types/ngx-semantic-elements-icon.d.ts +1 -2
  95. package/types/ngx-semantic-elements-image.d.ts +2 -7
  96. package/types/ngx-semantic-elements-input.d.ts +2 -7
  97. package/types/ngx-semantic-elements-label.d.ts +2 -7
  98. package/types/ngx-semantic-elements-segment.d.ts +1 -6
  99. package/types/ngx-semantic-elements-steps.d.ts +1 -6
  100. package/types/ngx-semantic-modules-checkbox.d.ts +3 -4
  101. package/types/ngx-semantic-modules-dimmer.d.ts +2 -3
  102. package/types/ngx-semantic-modules-embed.d.ts +2 -5
  103. package/types/ngx-semantic-modules-modal.d.ts +2 -2
  104. package/types/ngx-semantic-modules-popup.d.ts +2 -4
  105. package/types/ngx-semantic-modules-rating.d.ts +4 -4
  106. package/types/ngx-semantic-modules-select.d.ts +1 -2
  107. package/types/ngx-semantic-modules-shape.d.ts +1 -2
  108. package/types/ngx-semantic-modules-sidebar.d.ts +1 -15
  109. package/types/ngx-semantic-modules-sticky.d.ts +1 -2
  110. package/types/ngx-semantic-modules-transition.d.ts +107 -0
  111. package/types/ngx-semantic-views-advertisement.d.ts +2 -7
  112. package/types/ngx-semantic-views-card.d.ts +2 -2
  113. package/types/ngx-semantic-views-feed.d.ts +2 -11
  114. package/types/ngx-semantic-views-items.d.ts +4 -9
  115. package/types/ngx-semantic-views-statistics.d.ts +13 -26
  116. package/types/ngx-semantic.d.ts +2 -1
@@ -1,12 +1,16 @@
1
1
  import { __decorate } from 'tslib';
2
2
  import * as i0 from '@angular/core';
3
- import { Input, Directive, NgModule } from '@angular/core';
3
+ import { inject, ElementRef, Input, Directive, Renderer2, NgZone, EventEmitter, HostListener, Output, NgModule } from '@angular/core';
4
4
  import { ClassUtils, InputBoolean } from 'ngx-semantic/core/util';
5
5
  import { BaseDirective } from 'ngx-semantic/core/base';
6
6
  import { CommonModule } from '@angular/common';
7
+ import { NgForm, FormGroupDirective, FormsModule, ReactiveFormsModule } from '@angular/forms';
8
+ import { Subject, fromEvent, merge } from 'rxjs';
9
+ import { debounceTime, takeUntil } from 'rxjs/operators';
7
10
 
8
11
  class SuiFormFieldDirective extends BaseDirective {
9
- constructor(element) {
12
+ constructor() {
13
+ const element = inject(ElementRef);
10
14
  super(element);
11
15
  this.suiWidth = null;
12
16
  this.suiError = false;
@@ -25,7 +29,7 @@ class SuiFormFieldDirective extends BaseDirective {
25
29
  ClassUtils.getPropClass(this.suiError, 'error')
26
30
  ].join(' ');
27
31
  }
28
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormFieldDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
32
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormFieldDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
29
33
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.7", type: SuiFormFieldDirective, isStandalone: true, selector: "[suiFormField]", inputs: { suiWidth: "suiWidth", suiError: "suiError", suiInline: "suiInline", disabled: "disabled", suiRequired: "suiRequired" }, exportAs: ["suiFormField"], usesInheritance: true, ngImport: i0 }); }
30
34
  }
31
35
  __decorate([
@@ -47,7 +51,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
47
51
  exportAs: 'suiFormField',
48
52
  selector: '[suiFormField]'
49
53
  }]
50
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { suiWidth: [{
54
+ }], ctorParameters: () => [], propDecorators: { suiWidth: [{
51
55
  type: Input
52
56
  }], suiError: [{
53
57
  type: Input
@@ -60,7 +64,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
60
64
  }] } });
61
65
 
62
66
  class SuiFormFieldsDirective extends BaseDirective {
63
- constructor(element) {
67
+ constructor() {
68
+ const element = inject(ElementRef);
64
69
  super(element);
65
70
  this.suiWidth = null;
66
71
  this.suiInline = false;
@@ -76,7 +81,7 @@ class SuiFormFieldsDirective extends BaseDirective {
76
81
  'fields'
77
82
  ].join(' ');
78
83
  }
79
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormFieldsDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
84
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormFieldsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
80
85
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.7", type: SuiFormFieldsDirective, isStandalone: true, selector: "[suiFormFields]", inputs: { suiWidth: "suiWidth", suiInline: "suiInline", suiGrouped: "suiGrouped", suiEqualWidth: "suiEqualWidth" }, exportAs: ["suiFormFields"], usesInheritance: true, ngImport: i0 }); }
81
86
  }
82
87
  __decorate([
@@ -95,7 +100,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
95
100
  exportAs: 'suiFormFields',
96
101
  selector: '[suiFormFields]'
97
102
  }]
98
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { suiWidth: [{
103
+ }], ctorParameters: () => [], propDecorators: { suiWidth: [{
99
104
  type: Input
100
105
  }], suiInline: [{
101
106
  type: Input
@@ -106,7 +111,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
106
111
  }] } });
107
112
 
108
113
  class SuiFormDirective extends BaseDirective {
109
- constructor(element) {
114
+ constructor() {
115
+ const element = inject(ElementRef);
110
116
  super(element);
111
117
  this.suiState = null;
112
118
  this.suiSize = null;
@@ -127,7 +133,7 @@ class SuiFormDirective extends BaseDirective {
127
133
  this.suiState
128
134
  ].join(' ');
129
135
  }
130
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
136
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
131
137
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.7", type: SuiFormDirective, isStandalone: true, selector: "[sui-form]", inputs: { suiState: "suiState", suiSize: "suiSize", suiLoading: "suiLoading", suiEqualWidth: "suiEqualWidth", suiInverted: "suiInverted", suiReply: "suiReply" }, exportAs: ["suiForm"], usesInheritance: true, ngImport: i0 }); }
132
138
  }
133
139
  __decorate([
@@ -149,7 +155,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
149
155
  selector: '[sui-form]',
150
156
  exportAs: 'suiForm'
151
157
  }]
152
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { suiState: [{
158
+ }], ctorParameters: () => [], propDecorators: { suiState: [{
153
159
  type: Input
154
160
  }], suiSize: [{
155
161
  type: Input
@@ -163,32 +169,997 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
163
169
  type: Input
164
170
  }] } });
165
171
 
172
+ /** Resolve control like Semantic UI: id, then name, then data-validate. */
173
+ function resolveFieldElement(root, identifier) {
174
+ if (!identifier) {
175
+ return null;
176
+ }
177
+ const byId = root.querySelector(`#${escapeCssId(identifier)}`);
178
+ if (byId instanceof HTMLElement) {
179
+ return byId;
180
+ }
181
+ const byName = root.querySelector(`[name="${escapeAttrSelector(identifier)}"]`);
182
+ if (byName instanceof HTMLElement) {
183
+ return byName;
184
+ }
185
+ const byMeta = root.querySelector(`[data-validate="${escapeAttrSelector(identifier)}"]`);
186
+ return byMeta instanceof HTMLElement ? byMeta : null;
187
+ }
188
+ function escapeCssId(id) {
189
+ if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {
190
+ return CSS.escape(id);
191
+ }
192
+ return id.replace(/([^a-zA-Z0-9_-])/g, '\\$1');
193
+ }
194
+ function escapeAttrSelector(s) {
195
+ return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
196
+ }
197
+ function readControlValue(el) {
198
+ if (el instanceof HTMLInputElement) {
199
+ if (el.type === 'checkbox') {
200
+ return el.checked;
201
+ }
202
+ if (el.type === 'radio') {
203
+ const form = el.form ?? el.closest('form');
204
+ if (form) {
205
+ const checked = form.querySelector(`input[type="radio"][name="${escapeAttrSelector(el.name)}"]:checked`);
206
+ return checked ? checked.value : '';
207
+ }
208
+ return el.checked ? el.value : '';
209
+ }
210
+ return el.value;
211
+ }
212
+ if (el instanceof HTMLSelectElement) {
213
+ if (el.multiple) {
214
+ return Array.from(el.selectedOptions).map(o => o.value);
215
+ }
216
+ return el.value;
217
+ }
218
+ if (el instanceof HTMLTextAreaElement) {
219
+ return el.value;
220
+ }
221
+ return '';
222
+ }
223
+ function findSemanticField(el) {
224
+ return el.closest('.field');
225
+ }
226
+ /** Label text for prompts: label[for=id], aria-label, placeholder. */
227
+ function inferFieldName(el, identifier) {
228
+ if (el.id) {
229
+ const label = el.ownerDocument?.querySelector(`label[for="${escapeAttrSelector(el.id)}"]`);
230
+ if (label?.textContent?.trim()) {
231
+ return label.textContent.trim();
232
+ }
233
+ }
234
+ const aria = el.getAttribute('aria-label');
235
+ if (aria) {
236
+ return aria;
237
+ }
238
+ if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
239
+ const ph = el.getAttribute('placeholder');
240
+ if (ph) {
241
+ return ph;
242
+ }
243
+ }
244
+ return identifier;
245
+ }
246
+
247
+ function toRuleSpecs(rules) {
248
+ if (typeof rules === 'string') {
249
+ return [{ type: rules }];
250
+ }
251
+ if (Array.isArray(rules)) {
252
+ return rules.map(r => (typeof r === 'string' ? { type: r } : r));
253
+ }
254
+ return rules;
255
+ }
256
+ function normalizeFields(input) {
257
+ const out = [];
258
+ for (const key of Object.keys(input)) {
259
+ const raw = input[key];
260
+ if (typeof raw === 'string' || Array.isArray(raw)) {
261
+ out.push({
262
+ key,
263
+ identifier: key,
264
+ rules: toRuleSpecs(raw),
265
+ optional: false
266
+ });
267
+ continue;
268
+ }
269
+ const spec = raw;
270
+ out.push({
271
+ key,
272
+ identifier: spec.identifier ?? key,
273
+ rules: toRuleSpecs(spec.rules),
274
+ optional: !!spec.optional,
275
+ depends: spec.depends
276
+ });
277
+ }
278
+ return out;
279
+ }
280
+
281
+ /** Split `ruleName[param]` (supports nested brackets in regExp by using `value` on rule spec instead). */
282
+ function parseRuleType(type) {
283
+ const t = type.trim();
284
+ const open = t.indexOf('[');
285
+ if (open === -1) {
286
+ return { name: t };
287
+ }
288
+ const close = t.lastIndexOf(']');
289
+ if (close <= open) {
290
+ return { name: t };
291
+ }
292
+ return {
293
+ name: t.slice(0, open),
294
+ bracket: t.slice(open + 1, close)
295
+ };
296
+ }
297
+ function asString(v) {
298
+ if (v === null || v === undefined) {
299
+ return '';
300
+ }
301
+ if (Array.isArray(v)) {
302
+ return v.join(',');
303
+ }
304
+ if (typeof v === 'boolean') {
305
+ return v ? 'true' : '';
306
+ }
307
+ return String(v);
308
+ }
309
+ function isEmptyValue$1(v) {
310
+ if (v === null || v === undefined) {
311
+ return true;
312
+ }
313
+ if (typeof v === 'boolean') {
314
+ return !v;
315
+ }
316
+ if (Array.isArray(v)) {
317
+ return v.length === 0;
318
+ }
319
+ return String(v).trim() === '';
320
+ }
321
+ const EMAIL_RE = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
322
+ function isValidUrl(s) {
323
+ try {
324
+ const u = new URL(s);
325
+ return u.protocol === 'http:' || u.protocol === 'https:';
326
+ }
327
+ catch {
328
+ return false;
329
+ }
330
+ }
331
+ function parseIntegerRange(bracket) {
332
+ if (!bracket) {
333
+ return {};
334
+ }
335
+ const m = /^(-?\d+)\.\.(-?\d+)$/.exec(bracket.trim());
336
+ if (m) {
337
+ return { min: Number(m[1]), max: Number(m[2]) };
338
+ }
339
+ return null;
340
+ }
341
+ function luhnValid(digits) {
342
+ const d = digits.replace(/\D/g, '');
343
+ if (d.length < 13) {
344
+ return false;
345
+ }
346
+ let sum = 0;
347
+ let alt = false;
348
+ for (let i = d.length - 1; i >= 0; i--) {
349
+ let n = parseInt(d.charAt(i), 10);
350
+ if (alt) {
351
+ n *= 2;
352
+ if (n > 9) {
353
+ n -= 9;
354
+ }
355
+ }
356
+ sum += n;
357
+ alt = !alt;
358
+ }
359
+ return sum % 10 === 0;
360
+ }
361
+ const CARD_TYPES = {
362
+ visa: /^4[0-9]{12}(?:[0-9]{3})?$/,
363
+ mastercard: /^5[1-5][0-9]{14}$/,
364
+ amex: /^3[47][0-9]{13}$/,
365
+ discover: /^6(?:011|5[0-9]{2})[0-9]{12}$/,
366
+ unionpay: /^62[0-9]{14,17}$/,
367
+ jcb: /^(?:2131|1800|35\d{3})\d{11}$/,
368
+ dinersClub: /^3(?:0[0-5]|[68][0-9])[0-9]{11}$/,
369
+ maestro: /^(?:5[0678]\d{2}|6304|6706|6771|6709)\d{8,15}$/,
370
+ laser: /^(6304|6706|6709|6771)[0-9]{12,15}$/,
371
+ visaElectron: /^(4026|417500|4405|4508|4844|4913|4917)\d+$/
372
+ };
373
+ /**
374
+ * Returns true if the value satisfies the rule.
375
+ */
376
+ function evaluateRule(rule, fieldValue, fieldIdentifier, ctx) {
377
+ const { name, bracket } = parseRuleType(rule.type);
378
+ const s = asString(fieldValue);
379
+ const lower = name.toLowerCase();
380
+ switch (lower) {
381
+ case 'empty':
382
+ return !isEmptyValue$1(fieldValue);
383
+ case 'checked':
384
+ return fieldValue === true;
385
+ case 'email':
386
+ return s === '' || EMAIL_RE.test(s);
387
+ case 'url':
388
+ return s === '' || isValidUrl(s);
389
+ case 'decimal':
390
+ return s === '' || /^-?\d+\.\d+$/.test(s.trim());
391
+ case 'number':
392
+ return s === '' || /^-?\d+(\.\d+)?$/.test(s.trim());
393
+ case 'integer': {
394
+ if (s.trim() === '') {
395
+ return true;
396
+ }
397
+ if (!/^-?\d+$/.test(s.trim())) {
398
+ return false;
399
+ }
400
+ const n = parseInt(s, 10);
401
+ const range = parseIntegerRange(bracket);
402
+ if (range && range.min !== undefined && range.max !== undefined) {
403
+ return n >= range.min && n <= range.max;
404
+ }
405
+ return true;
406
+ }
407
+ case 'regexp': {
408
+ let re = null;
409
+ if (rule.value instanceof RegExp) {
410
+ re = rule.value;
411
+ }
412
+ else if (bracket) {
413
+ const inner = bracket.trim();
414
+ const m = inner.match(/^\/(.*)\/([gimuy]*)$/);
415
+ if (m) {
416
+ re = new RegExp(m[1], m[2] || '');
417
+ }
418
+ }
419
+ if (!re) {
420
+ return false;
421
+ }
422
+ return re.test(s);
423
+ }
424
+ case 'minlength': {
425
+ const min = bracket ? parseInt(bracket, 10) : NaN;
426
+ if (Number.isNaN(min)) {
427
+ return false;
428
+ }
429
+ return s.length >= min;
430
+ }
431
+ case 'maxlength': {
432
+ const max = bracket ? parseInt(bracket, 10) : NaN;
433
+ if (Number.isNaN(max)) {
434
+ return false;
435
+ }
436
+ return s.length <= max;
437
+ }
438
+ case 'exactlength': {
439
+ const ex = bracket ? parseInt(bracket, 10) : NaN;
440
+ if (Number.isNaN(ex)) {
441
+ return false;
442
+ }
443
+ return s.length === ex;
444
+ }
445
+ case 'match': {
446
+ if (!bracket) {
447
+ return false;
448
+ }
449
+ const other = asString(ctx.getFieldValue(bracket));
450
+ return s === other;
451
+ }
452
+ case 'different': {
453
+ if (!bracket) {
454
+ return false;
455
+ }
456
+ const other = asString(ctx.getFieldValue(bracket));
457
+ return s !== other || s === '';
458
+ }
459
+ case 'mincount': {
460
+ const min = bracket ? parseInt(bracket, 10) : NaN;
461
+ if (!Number.isFinite(min)) {
462
+ return false;
463
+ }
464
+ const arr = Array.isArray(fieldValue) ? fieldValue : [];
465
+ return arr.length >= min;
466
+ }
467
+ case 'maxcount': {
468
+ const max = bracket ? parseInt(bracket, 10) : NaN;
469
+ if (!Number.isFinite(max)) {
470
+ return false;
471
+ }
472
+ const arr = Array.isArray(fieldValue) ? fieldValue : [];
473
+ return arr.length <= max;
474
+ }
475
+ case 'exactcount': {
476
+ const ex = bracket ? parseInt(bracket, 10) : NaN;
477
+ if (!Number.isFinite(ex)) {
478
+ return false;
479
+ }
480
+ const arr = Array.isArray(fieldValue) ? fieldValue : [];
481
+ return arr.length === ex;
482
+ }
483
+ case 'contains':
484
+ return bracket ? s.toLowerCase().includes(bracket.toLowerCase()) : false;
485
+ case 'containsexactly':
486
+ return bracket ? s.includes(bracket) : false;
487
+ case 'doesntcontain':
488
+ return bracket ? !s.toLowerCase().includes(bracket.toLowerCase()) : false;
489
+ case 'doesntcontainexactly':
490
+ return bracket ? !s.includes(bracket) : false;
491
+ case 'is':
492
+ return bracket ? s.toLowerCase() === bracket.toLowerCase() : false;
493
+ case 'isexactly':
494
+ return bracket ? s === bracket : false;
495
+ case 'not':
496
+ return bracket ? s.toLowerCase() !== bracket.toLowerCase() : false;
497
+ case 'notexactly':
498
+ return bracket ? s !== bracket : false;
499
+ case 'exactly':
500
+ return bracket ? s.toLowerCase() === bracket.toLowerCase() : false;
501
+ case 'creditcard': {
502
+ const digits = s.replace(/\s/g, '');
503
+ if (!luhnValid(digits)) {
504
+ return false;
505
+ }
506
+ if (!bracket) {
507
+ return true;
508
+ }
509
+ const types = bracket.split(',').map(t => t.trim().toLowerCase());
510
+ return types.some(t => {
511
+ const rx = CARD_TYPES[t];
512
+ return rx && rx.test(digits);
513
+ });
514
+ }
515
+ default:
516
+ return true;
517
+ }
518
+ }
519
+
520
+ const DEFAULT_PROMPTS = {
521
+ empty: '{name} must have a value',
522
+ checked: '{name} must be checked',
523
+ email: '{name} must be a valid e-mail',
524
+ url: '{name} must be a valid url',
525
+ regexp: '{name} is not formatted correctly',
526
+ integer: '{name} must be an integer',
527
+ decimal: '{name} must be a decimal number',
528
+ number: '{name} must be set to a number',
529
+ is: "{name} must be '{ruleValue}'",
530
+ isexactly: "{name} must be exactly '{ruleValue}'",
531
+ not: "{name} cannot be set to '{ruleValue}'",
532
+ notexactly: "{name} cannot be set to exactly '{ruleValue}'",
533
+ contains: "{name} must contain '{ruleValue}'",
534
+ containsexactly: "{name} must contain exactly '{ruleValue}'",
535
+ doesntcontain: "{name} must not contain '{ruleValue}'",
536
+ doesntcontainexactly: "{name} must not contain exactly '{ruleValue}'",
537
+ minlength: '{name} must be at least {ruleValue} characters',
538
+ exactlength: '{name} must be exactly {ruleValue} characters',
539
+ maxlength: '{name} cannot be longer than {ruleValue} characters',
540
+ match: '{name} must match {ruleValue} field',
541
+ different: '{name} must have a different value than {ruleValue} field',
542
+ creditcard: '{name} must be a valid credit card number',
543
+ mincount: '{name} must have at least {ruleValue} choices',
544
+ exactcount: '{name} must have exactly {ruleValue} choices',
545
+ maxcount: '{name} must have {ruleValue} or less choices',
546
+ exactly: "{name} must be '{ruleValue}'"
547
+ };
548
+ function formatValidationPrompt(ruleType, fieldName, fieldIdentifier, fieldValue, customPrompt, ruleValue) {
549
+ if (typeof customPrompt === 'function') {
550
+ return customPrompt(fieldValue);
551
+ }
552
+ if (customPrompt) {
553
+ return applyTemplate(customPrompt, fieldName, fieldIdentifier, fieldValue, ruleValue);
554
+ }
555
+ const { name } = parseRuleType(ruleType);
556
+ const key = name.toLowerCase();
557
+ const template = DEFAULT_PROMPTS[key] ?? 'Please enter a valid value';
558
+ return applyTemplate(template, fieldName, fieldIdentifier, fieldValue, ruleValue);
559
+ }
560
+ function applyTemplate(template, name, identifier, value, ruleValue) {
561
+ const v = value === null || value === undefined
562
+ ? ''
563
+ : Array.isArray(value)
564
+ ? value.join(',')
565
+ : String(value);
566
+ return template
567
+ .replace(/\{name\}/g, name)
568
+ .replace(/\{identifier\}/g, identifier)
569
+ .replace(/\{value\}/g, v)
570
+ .replace(/\{ruleValue\}/g, ruleValue);
571
+ }
572
+
573
+ /**
574
+ * Types for Semantic UI [Form Validation](https://semantic-ui.com/behaviors/form.html) — Angular port.
575
+ * Lives with `ngx-semantic/collections/form` (use with `sui-form` + `suiFormValidation` on the same `<form>`).
576
+ */
577
+ /** `AbstractControl.errors` key for Semantic rule failures (string message). Works with built-in validators. */
578
+ const SUI_FORM_VALIDATION_ERROR_KEY = 'suiFormValidation';
579
+
580
+ /** Merge or clear Semantic validation errors without wiping other validator errors on the control. */
581
+ function patchSuiFormValidationControlError(control, message) {
582
+ const key = SUI_FORM_VALIDATION_ERROR_KEY;
583
+ const cur = control.errors;
584
+ if (message === null) {
585
+ if (!cur?.[key]) {
586
+ return;
587
+ }
588
+ const rest = { ...cur };
589
+ delete rest[key];
590
+ control.setErrors(Object.keys(rest).length ? rest : null);
591
+ }
592
+ else {
593
+ control.setErrors({ ...(cur || {}), [key]: message });
594
+ }
595
+ }
596
+
597
+ function isEmptyValue(v) {
598
+ if (v === null || v === undefined) {
599
+ return true;
600
+ }
601
+ if (typeof v === 'boolean') {
602
+ return !v;
603
+ }
604
+ if (Array.isArray(v)) {
605
+ return v.length === 0;
606
+ }
607
+ return String(v).trim() === '';
608
+ }
609
+ class SuiFormValidationDirective {
610
+ constructor() {
611
+ this.el = inject(ElementRef);
612
+ this.renderer = inject(Renderer2);
613
+ this.zone = inject(NgZone);
614
+ this.ngForm = inject(NgForm, { optional: true, host: true });
615
+ this.formGroupDir = inject(FormGroupDirective, { optional: true, host: true });
616
+ /** Validation field definitions (shorthand or longhand), matching Semantic UI `fields`. */
617
+ this.suiFields = null;
618
+ /** `submit`, `blur`, or `change` — when to validate fields. */
619
+ this.suiOn = 'submit';
620
+ this.suiInline = false;
621
+ this.suiRevalidate = true;
622
+ /** Debounce (ms) for `change` / revalidation; `true` uses 300ms; `false` disables. */
623
+ this.suiDelay = true;
624
+ this.suiKeyboardShortcuts = true;
625
+ /** When using `NgForm` / `FormGroup`, mark all controls touched after a failed submit (shows native errors). */
626
+ this.suiMarkControlsTouchedOnInvalid = true;
627
+ this.suiOnSuccess = new EventEmitter();
628
+ this.suiOnFailure = new EventEmitter();
629
+ this.destroy$ = new Subject();
630
+ this.normalized = [];
631
+ this.defaultValues = new Map();
632
+ this.teardownFieldSubs = null;
633
+ const el = this.el;
634
+ this.host = el.nativeElement;
635
+ }
636
+ /** Reactive `[formGroup]` or template-driven `NgForm` on this host, if any. */
637
+ getAngularForm() {
638
+ if (this.formGroupDir?.form) {
639
+ return this.formGroupDir.form;
640
+ }
641
+ if (this.ngForm?.form) {
642
+ return this.ngForm.form;
643
+ }
644
+ return null;
645
+ }
646
+ /** Control whose name matches `suiFields` identifier (flat `FormGroup` only). */
647
+ resolveControl(identifier) {
648
+ const g = this.getAngularForm();
649
+ const c = g?.get(identifier);
650
+ return c ?? null;
651
+ }
652
+ ngOnChanges(changes) {
653
+ if (changes['suiFields']) {
654
+ this.normalized = this.suiFields ? normalizeFields(this.suiFields) : [];
655
+ }
656
+ if (changes['suiFields'] || changes['suiOn'] || changes['suiDelay'] || changes['suiRevalidate']) {
657
+ this.bindFieldTriggers();
658
+ }
659
+ }
660
+ ngOnDestroy() {
661
+ this.teardownFieldSubs?.();
662
+ this.destroy$.next();
663
+ this.destroy$.complete();
664
+ }
665
+ onHostSubmit(event) {
666
+ if (!this.normalized.length) {
667
+ return;
668
+ }
669
+ this.cacheDefaultsOnce();
670
+ const r = this.runValidationWithUi();
671
+ const form = this.getAngularForm();
672
+ const ok = r.valid && (!form || form.valid);
673
+ // Always prevent native form navigation; consumers use suiOnSuccess for follow-up.
674
+ event.preventDefault();
675
+ if (!ok) {
676
+ if (this.suiMarkControlsTouchedOnInvalid && form) {
677
+ form.markAllAsTouched();
678
+ }
679
+ event.stopPropagation();
680
+ const errorsOut = r.errors.length > 0
681
+ ? r.errors
682
+ : form && !form.valid
683
+ ? ['This form has validation errors.']
684
+ : r.errors;
685
+ this.suiOnFailure.emit({ errors: errorsOut, fields: r.fieldIds });
686
+ return;
687
+ }
688
+ this.suiOnSuccess.emit(event);
689
+ }
690
+ onHostKeydown(ev) {
691
+ if (!this.suiKeyboardShortcuts) {
692
+ return;
693
+ }
694
+ if (!this.host.contains(ev.target)) {
695
+ return;
696
+ }
697
+ if (ev.key === 'Escape') {
698
+ ev.target?.blur?.();
699
+ }
700
+ }
701
+ /** Validate the whole form, update UI, emit success/failure. */
702
+ validateForm() {
703
+ this.cacheDefaultsOnce();
704
+ const r = this.runValidationWithUi();
705
+ const form = this.getAngularForm();
706
+ const ok = r.valid && (!form || form.valid);
707
+ if (!ok) {
708
+ if (this.suiMarkControlsTouchedOnInvalid && form) {
709
+ form.markAllAsTouched();
710
+ }
711
+ const errorsOut = r.errors.length > 0
712
+ ? r.errors
713
+ : form && !form.valid
714
+ ? ['This form has validation errors.']
715
+ : r.errors;
716
+ this.suiOnFailure.emit({ errors: errorsOut, fields: r.fieldIds });
717
+ }
718
+ else {
719
+ this.suiOnSuccess.emit(new Event('semantic-form-valid'));
720
+ }
721
+ return ok;
722
+ }
723
+ runValidationWithUi() {
724
+ const errors = [];
725
+ const fieldIds = [];
726
+ this.clearFormLevelMessage();
727
+ this.clearAllFieldUi();
728
+ for (const f of this.normalized) {
729
+ const msg = this.evaluateFieldMessage(f);
730
+ const ctrl = this.resolveControl(f.identifier);
731
+ if (msg) {
732
+ errors.push(msg);
733
+ fieldIds.push(f.identifier);
734
+ this.applyFieldError(f.identifier, msg);
735
+ if (ctrl) {
736
+ patchSuiFormValidationControlError(ctrl, msg);
737
+ }
738
+ }
739
+ }
740
+ this.renderFormLevelErrors(errors);
741
+ return { valid: errors.length === 0, errors, fieldIds };
742
+ }
743
+ /** Validate one field by identifier (updates UI and `AbstractControl` errors when bound). */
744
+ validateField(identifier) {
745
+ const field = this.normalized.find(x => x.identifier === identifier || x.key === identifier);
746
+ if (!field) {
747
+ return true;
748
+ }
749
+ this.clearFieldUi(field.identifier);
750
+ const msg = this.evaluateFieldMessage(field);
751
+ const ctrl = this.resolveControl(field.identifier);
752
+ if (msg) {
753
+ if (ctrl) {
754
+ patchSuiFormValidationControlError(ctrl, msg);
755
+ }
756
+ this.applyFieldError(field.identifier, msg);
757
+ return false;
758
+ }
759
+ return true;
760
+ }
761
+ /** Whether the form (or one field) passes rules; does not update UI (Semantic `is valid`). */
762
+ isValid(identifier) {
763
+ if (identifier) {
764
+ const field = this.normalized.find(x => x.identifier === identifier || x.key === identifier);
765
+ return field ? this.evaluateFieldMessage(field) === null : true;
766
+ }
767
+ return this.normalized.every(f => this.evaluateFieldMessage(f) === null);
768
+ }
769
+ getFieldElement(identifier) {
770
+ return resolveFieldElement(this.host, identifier);
771
+ }
772
+ getValue(identifier) {
773
+ const ctrl = this.resolveControl(identifier);
774
+ if (ctrl) {
775
+ return ctrl.value;
776
+ }
777
+ const el = this.getFieldElement(identifier);
778
+ return el ? readControlValue(el) : undefined;
779
+ }
780
+ getValues(identifiers) {
781
+ const ids = identifiers ??
782
+ this.normalized.map(f => f.identifier);
783
+ const out = {};
784
+ for (const id of ids) {
785
+ const ctrl = this.resolveControl(id);
786
+ if (ctrl) {
787
+ out[id] = ctrl.value;
788
+ continue;
789
+ }
790
+ const el = resolveFieldElement(this.host, id);
791
+ if (!el) {
792
+ continue;
793
+ }
794
+ const name = el.getAttribute('name') ?? el.getAttribute('id') ?? el.getAttribute('data-validate') ?? id;
795
+ out[name] = readControlValue(el);
796
+ }
797
+ return out;
798
+ }
799
+ setValue(identifier, value) {
800
+ const ctrl = this.resolveControl(identifier);
801
+ if (ctrl) {
802
+ ctrl.setValue(value);
803
+ return;
804
+ }
805
+ const el = resolveFieldElement(this.host, identifier);
806
+ if (!el) {
807
+ return;
808
+ }
809
+ this.writeControlValue(el, value);
810
+ }
811
+ setValues(values) {
812
+ for (const k of Object.keys(values)) {
813
+ this.setValue(k, values[k]);
814
+ }
815
+ }
816
+ /** Restore values cached at first validation init (Semantic `reset`). */
817
+ reset() {
818
+ const fields = this.host.querySelectorAll('input, select, textarea');
819
+ fields.forEach(control => {
820
+ const key = control.name || control.id;
821
+ if (!key) {
822
+ return;
823
+ }
824
+ if (this.defaultValues.has(key)) {
825
+ this.writeControlValue(control, this.defaultValues.get(key));
826
+ }
827
+ });
828
+ }
829
+ /** Clear all field values (Semantic `clear`). */
830
+ clear() {
831
+ this.host.querySelectorAll('input, select, textarea').forEach(control => {
832
+ if (control instanceof HTMLInputElement) {
833
+ if (control.type === 'checkbox' || control.type === 'radio') {
834
+ control.checked = false;
835
+ }
836
+ else {
837
+ control.value = '';
838
+ }
839
+ }
840
+ else if (control instanceof HTMLSelectElement) {
841
+ if (control.multiple) {
842
+ Array.from(control.options).forEach(o => (o.selected = false));
843
+ }
844
+ else {
845
+ control.selectedIndex = 0;
846
+ }
847
+ }
848
+ else {
849
+ control.value = '';
850
+ }
851
+ });
852
+ }
853
+ debounceMs() {
854
+ if (this.suiDelay === false) {
855
+ return 0;
856
+ }
857
+ if (this.suiDelay === true) {
858
+ return 300;
859
+ }
860
+ return Math.max(0, this.suiDelay);
861
+ }
862
+ ctx() {
863
+ return {
864
+ getFieldValue: id => {
865
+ const c = this.resolveControl(id);
866
+ if (c) {
867
+ return c.value;
868
+ }
869
+ const el = resolveFieldElement(this.host, id);
870
+ return el ? readControlValue(el) : '';
871
+ },
872
+ getFieldElement: id => resolveFieldElement(this.host, id)
873
+ };
874
+ }
875
+ evaluateFieldMessage(field) {
876
+ const ctx = this.ctx();
877
+ if (field.depends) {
878
+ const dep = ctx.getFieldValue(field.depends);
879
+ if (isEmptyValue(dep)) {
880
+ return null;
881
+ }
882
+ }
883
+ const ctrl = this.resolveControl(field.identifier);
884
+ const el = resolveFieldElement(this.host, field.identifier);
885
+ const value = ctrl ? ctrl.value : el ? readControlValue(el) : undefined;
886
+ if (field.optional && isEmptyValue(value)) {
887
+ return null;
888
+ }
889
+ const fieldName = el ? inferFieldName(el, field.identifier) : field.identifier;
890
+ for (const rule of field.rules) {
891
+ if (!evaluateRule(rule, value, field.identifier, ctx)) {
892
+ const { bracket } = parseRuleType(rule.type);
893
+ return formatValidationPrompt(rule.type, fieldName, field.identifier, value, rule.prompt, bracket ?? '');
894
+ }
895
+ }
896
+ return null;
897
+ }
898
+ cacheDefaultsOnce() {
899
+ if (this.defaultValues.size) {
900
+ return;
901
+ }
902
+ this.host.querySelectorAll('input, select, textarea').forEach(control => {
903
+ const key = control.name || control.id;
904
+ if (!key) {
905
+ return;
906
+ }
907
+ if (control instanceof HTMLInputElement && (control.type === 'checkbox' || control.type === 'radio')) {
908
+ this.defaultValues.set(key, control.checked ? '1' : '');
909
+ }
910
+ else {
911
+ this.defaultValues.set(key, control.value);
912
+ }
913
+ });
914
+ }
915
+ bindFieldTriggers() {
916
+ this.teardownFieldSubs?.();
917
+ this.teardownFieldSubs = null;
918
+ if (!this.normalized.length || this.suiOn === 'submit') {
919
+ return;
920
+ }
921
+ const sub = new Subject();
922
+ const stop = () => {
923
+ sub.next();
924
+ sub.complete();
925
+ };
926
+ this.teardownFieldSubs = stop;
927
+ const ms = this.debounceMs();
928
+ this.zone.runOutsideAngular(() => {
929
+ for (const f of this.normalized) {
930
+ const el = resolveFieldElement(this.host, f.identifier);
931
+ if (!el) {
932
+ continue;
933
+ }
934
+ const ev = this.suiOn === 'blur'
935
+ ? fromEvent(el, 'blur')
936
+ : merge(fromEvent(el, 'input'), fromEvent(el, 'change'));
937
+ const piped = ms > 0 ? ev.pipe(debounceTime(ms)) : ev;
938
+ piped.pipe(takeUntil(sub), takeUntil(this.destroy$)).subscribe(() => {
939
+ this.zone.run(() => {
940
+ this.validateField(f.identifier);
941
+ });
942
+ });
943
+ }
944
+ });
945
+ if (this.suiRevalidate && this.suiOn === 'blur') {
946
+ this.zone.runOutsideAngular(() => {
947
+ for (const f of this.normalized) {
948
+ const el = resolveFieldElement(this.host, f.identifier);
949
+ if (!el) {
950
+ continue;
951
+ }
952
+ const piped = ms > 0
953
+ ? fromEvent(el, 'input').pipe(debounceTime(ms))
954
+ : fromEvent(el, 'input');
955
+ piped.pipe(takeUntil(sub), takeUntil(this.destroy$)).subscribe(() => {
956
+ const group = findSemanticField(el);
957
+ if (group?.classList.contains('error')) {
958
+ this.zone.run(() => this.validateField(f.identifier));
959
+ }
960
+ });
961
+ }
962
+ });
963
+ }
964
+ }
965
+ clearAllFieldUi() {
966
+ for (const f of this.normalized) {
967
+ this.clearFieldUi(f.identifier);
968
+ }
969
+ }
970
+ clearFieldUi(identifier) {
971
+ const ctrl = this.resolveControl(identifier);
972
+ if (ctrl) {
973
+ patchSuiFormValidationControlError(ctrl, null);
974
+ }
975
+ const el = resolveFieldElement(this.host, identifier);
976
+ const group = el ? findSemanticField(el) : null;
977
+ if (group) {
978
+ this.renderer.removeClass(group, 'error');
979
+ const prompt = group.querySelector('.prompt');
980
+ if (prompt) {
981
+ this.renderer.setProperty(prompt, 'textContent', '');
982
+ if (this.suiInline) {
983
+ this.renderer.removeStyle(prompt, 'display');
984
+ }
985
+ else {
986
+ this.renderer.setStyle(prompt, 'display', 'none');
987
+ }
988
+ }
989
+ }
990
+ }
991
+ applyFieldError(identifier, message) {
992
+ const el = resolveFieldElement(this.host, identifier);
993
+ const group = el ? findSemanticField(el) : null;
994
+ if (!group) {
995
+ return;
996
+ }
997
+ this.renderer.addClass(group, 'error');
998
+ if (!this.suiInline) {
999
+ return;
1000
+ }
1001
+ let prompt = group.querySelector('.prompt');
1002
+ if (!prompt) {
1003
+ prompt = this.renderer.createElement('div');
1004
+ this.renderer.addClass(prompt, 'ui');
1005
+ this.renderer.addClass(prompt, 'basic');
1006
+ this.renderer.addClass(prompt, 'red');
1007
+ this.renderer.addClass(prompt, 'pointing');
1008
+ this.renderer.addClass(prompt, 'label');
1009
+ this.renderer.addClass(prompt, 'prompt');
1010
+ this.renderer.setAttribute(prompt, 'role', 'alert');
1011
+ this.renderer.appendChild(group, prompt);
1012
+ }
1013
+ this.renderer.setProperty(prompt, 'textContent', message);
1014
+ this.renderer.removeStyle(prompt, 'display');
1015
+ }
1016
+ renderFormLevelErrors(messages) {
1017
+ const block = this.host.querySelector('.ui.error.message, .error.message');
1018
+ if (!block || !messages.length) {
1019
+ return;
1020
+ }
1021
+ const ul = this.renderer.createElement('ul');
1022
+ ul.className = 'list';
1023
+ for (const m of messages) {
1024
+ const li = this.renderer.createElement('li');
1025
+ li.textContent = m;
1026
+ ul.appendChild(li);
1027
+ }
1028
+ block.innerHTML = '';
1029
+ block.appendChild(ul);
1030
+ this.renderer.removeClass(block, 'hidden');
1031
+ }
1032
+ clearFormLevelMessage() {
1033
+ const block = this.host.querySelector('.ui.error.message, .error.message');
1034
+ if (block) {
1035
+ block.innerHTML = '';
1036
+ }
1037
+ }
1038
+ writeControlValue(el, value) {
1039
+ if (el instanceof HTMLInputElement) {
1040
+ if (el.type === 'checkbox') {
1041
+ el.checked = Boolean(value);
1042
+ return;
1043
+ }
1044
+ if (el.type === 'radio') {
1045
+ const form = el.form ?? this.host;
1046
+ const name = el.name;
1047
+ const str = String(value ?? '');
1048
+ form
1049
+ .querySelectorAll(`input[type="radio"][name="${escapeAttrSelector(name)}"]`)
1050
+ .forEach(r => {
1051
+ r.checked = r.value === str;
1052
+ });
1053
+ return;
1054
+ }
1055
+ el.value = value === null || value === undefined ? '' : String(value);
1056
+ return;
1057
+ }
1058
+ if (el instanceof HTMLSelectElement) {
1059
+ if (el.multiple && Array.isArray(value)) {
1060
+ const set = new Set(value.map(String));
1061
+ Array.from(el.options).forEach(o => {
1062
+ o.selected = set.has(o.value);
1063
+ });
1064
+ }
1065
+ else {
1066
+ el.value = String(value ?? '');
1067
+ }
1068
+ return;
1069
+ }
1070
+ if (el instanceof HTMLTextAreaElement) {
1071
+ el.value = value === null || value === undefined ? '' : String(value);
1072
+ }
1073
+ }
1074
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormValidationDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1075
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.7", type: SuiFormValidationDirective, isStandalone: true, selector: "form[suiFormValidation]", inputs: { suiFields: "suiFields", suiOn: "suiOn", suiInline: "suiInline", suiRevalidate: "suiRevalidate", suiDelay: "suiDelay", suiKeyboardShortcuts: "suiKeyboardShortcuts", suiMarkControlsTouchedOnInvalid: "suiMarkControlsTouchedOnInvalid" }, outputs: { suiOnSuccess: "suiOnSuccess", suiOnFailure: "suiOnFailure" }, host: { listeners: { "submit": "onHostSubmit($event)", "keydown": "onHostKeydown($event)" } }, exportAs: ["suiFormValidation"], usesOnChanges: true, ngImport: i0 }); }
1076
+ }
1077
+ __decorate([
1078
+ InputBoolean()
1079
+ ], SuiFormValidationDirective.prototype, "suiInline", void 0);
1080
+ __decorate([
1081
+ InputBoolean()
1082
+ ], SuiFormValidationDirective.prototype, "suiRevalidate", void 0);
1083
+ __decorate([
1084
+ InputBoolean()
1085
+ ], SuiFormValidationDirective.prototype, "suiKeyboardShortcuts", void 0);
1086
+ __decorate([
1087
+ InputBoolean()
1088
+ ], SuiFormValidationDirective.prototype, "suiMarkControlsTouchedOnInvalid", void 0);
1089
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormValidationDirective, decorators: [{
1090
+ type: Directive,
1091
+ args: [{
1092
+ standalone: true,
1093
+ selector: 'form[suiFormValidation]',
1094
+ exportAs: 'suiFormValidation'
1095
+ }]
1096
+ }], ctorParameters: () => [], propDecorators: { suiFields: [{
1097
+ type: Input
1098
+ }], suiOn: [{
1099
+ type: Input
1100
+ }], suiInline: [{
1101
+ type: Input
1102
+ }], suiRevalidate: [{
1103
+ type: Input
1104
+ }], suiDelay: [{
1105
+ type: Input
1106
+ }], suiKeyboardShortcuts: [{
1107
+ type: Input
1108
+ }], suiMarkControlsTouchedOnInvalid: [{
1109
+ type: Input
1110
+ }], suiOnSuccess: [{
1111
+ type: Output
1112
+ }], suiOnFailure: [{
1113
+ type: Output
1114
+ }], onHostSubmit: [{
1115
+ type: HostListener,
1116
+ args: ['submit', ['$event']]
1117
+ }], onHostKeydown: [{
1118
+ type: HostListener,
1119
+ args: ['keydown', ['$event']]
1120
+ }] } });
1121
+
166
1122
  /**
167
1123
  * Created by bolor on 5/26/2020
168
1124
  */
169
1125
  class SuiFormModule {
170
1126
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
171
1127
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.7", ngImport: i0, type: SuiFormModule, imports: [CommonModule,
1128
+ FormsModule,
1129
+ ReactiveFormsModule,
172
1130
  SuiFormFieldDirective,
173
1131
  SuiFormDirective,
174
- SuiFormFieldsDirective], exports: [SuiFormFieldDirective,
1132
+ SuiFormFieldsDirective,
1133
+ SuiFormValidationDirective], exports: [FormsModule,
1134
+ ReactiveFormsModule,
1135
+ SuiFormFieldDirective,
175
1136
  SuiFormDirective,
176
- SuiFormFieldsDirective] }); }
177
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormModule, imports: [CommonModule] }); }
1137
+ SuiFormFieldsDirective,
1138
+ SuiFormValidationDirective] }); }
1139
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormModule, imports: [CommonModule,
1140
+ FormsModule,
1141
+ ReactiveFormsModule, FormsModule,
1142
+ ReactiveFormsModule] }); }
178
1143
  }
179
1144
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SuiFormModule, decorators: [{
180
1145
  type: NgModule,
181
1146
  args: [{
182
1147
  imports: [
183
1148
  CommonModule,
1149
+ FormsModule,
1150
+ ReactiveFormsModule,
184
1151
  SuiFormFieldDirective,
185
1152
  SuiFormDirective,
186
- SuiFormFieldsDirective
1153
+ SuiFormFieldsDirective,
1154
+ SuiFormValidationDirective
187
1155
  ],
188
1156
  exports: [
1157
+ FormsModule,
1158
+ ReactiveFormsModule,
189
1159
  SuiFormFieldDirective,
190
1160
  SuiFormDirective,
191
- SuiFormFieldsDirective
1161
+ SuiFormFieldsDirective,
1162
+ SuiFormValidationDirective
192
1163
  ]
193
1164
  }]
194
1165
  }] });
@@ -201,5 +1172,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
201
1172
  * Generated bundle index. Do not edit.
202
1173
  */
203
1174
 
204
- export { SuiFormDirective, SuiFormFieldDirective, SuiFormFieldsDirective, SuiFormModule };
1175
+ export { SUI_FORM_VALIDATION_ERROR_KEY, SuiFormDirective, SuiFormFieldDirective, SuiFormFieldsDirective, SuiFormModule, SuiFormValidationDirective, escapeAttrSelector, evaluateRule, findSemanticField, formatValidationPrompt, inferFieldName, normalizeFields, parseRuleType, patchSuiFormValidationControlError, readControlValue, resolveFieldElement };
205
1176
  //# sourceMappingURL=ngx-semantic-collections-form.mjs.map