iss-ctrl 0.0.8 → 0.0.10

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 (27) hide show
  1. package/fesm2022/{iss-ctrl-iss-ctrl-BLjj_Aqu.mjs → iss-ctrl-iss-ctrl-hz0ibyPg.mjs} +929 -438
  2. package/fesm2022/iss-ctrl-iss-ctrl-hz0ibyPg.mjs.map +1 -0
  3. package/fesm2022/{iss-ctrl-load-event-Co3HQu91.mjs → iss-ctrl-load-event-BW5dKG8R.mjs} +3 -3
  4. package/fesm2022/{iss-ctrl-load-event-Co3HQu91.mjs.map → iss-ctrl-load-event-BW5dKG8R.mjs.map} +1 -1
  5. package/fesm2022/{iss-ctrl-load-options-event-BIn0aiHl.mjs → iss-ctrl-load-options-event-ClX9TiiD.mjs} +3 -3
  6. package/fesm2022/{iss-ctrl-load-options-event-BIn0aiHl.mjs.map → iss-ctrl-load-options-event-ClX9TiiD.mjs.map} +1 -1
  7. package/fesm2022/{iss-ctrl-load-value-event-idouR9ST.mjs → iss-ctrl-load-value-event-DThpI9BA.mjs} +3 -3
  8. package/fesm2022/{iss-ctrl-load-value-event-idouR9ST.mjs.map → iss-ctrl-load-value-event-DThpI9BA.mjs.map} +1 -1
  9. package/fesm2022/{iss-ctrl-note.field-CuTQ3YwJ.mjs → iss-ctrl-note.field-4uLl0oS_.mjs} +4 -4
  10. package/fesm2022/{iss-ctrl-note.field-CuTQ3YwJ.mjs.map → iss-ctrl-note.field-4uLl0oS_.mjs.map} +1 -1
  11. package/fesm2022/{iss-ctrl-replace-api-keys-Df8ruZtV.mjs → iss-ctrl-replace-api-keys-BQL9zimJ.mjs} +2 -2
  12. package/fesm2022/{iss-ctrl-replace-api-keys-Df8ruZtV.mjs.map → iss-ctrl-replace-api-keys-BQL9zimJ.mjs.map} +1 -1
  13. package/fesm2022/{iss-ctrl-set-value-event-BNNMVIeT.mjs → iss-ctrl-set-value-event-BbEYFBe3.mjs} +2 -2
  14. package/fesm2022/{iss-ctrl-set-value-event-BNNMVIeT.mjs.map → iss-ctrl-set-value-event-BbEYFBe3.mjs.map} +1 -1
  15. package/fesm2022/iss-ctrl-table.field-kPIwve-M.mjs +517 -0
  16. package/fesm2022/iss-ctrl-table.field-kPIwve-M.mjs.map +1 -0
  17. package/fesm2022/{iss-ctrl-trigger-based-on-json-value-6yh7NOEH.mjs → iss-ctrl-trigger-based-on-json-value-DuLD4SOS.mjs} +2 -2
  18. package/fesm2022/{iss-ctrl-trigger-based-on-json-value-6yh7NOEH.mjs.map → iss-ctrl-trigger-based-on-json-value-DuLD4SOS.mjs.map} +1 -1
  19. package/fesm2022/{iss-ctrl-user-picker.field-Cx0CCAFC.mjs → iss-ctrl-user-picker.field-l-0bW6lP.mjs} +36 -20
  20. package/fesm2022/iss-ctrl-user-picker.field-l-0bW6lP.mjs.map +1 -0
  21. package/fesm2022/iss-ctrl.mjs +1 -1
  22. package/package.json +1 -1
  23. package/types/iss-ctrl.d.ts +44 -29
  24. package/fesm2022/iss-ctrl-iss-ctrl-BLjj_Aqu.mjs.map +0 -1
  25. package/fesm2022/iss-ctrl-table.field-Bs6l5tWV.mjs +0 -310
  26. package/fesm2022/iss-ctrl-table.field-Bs6l5tWV.mjs.map +0 -1
  27. package/fesm2022/iss-ctrl-user-picker.field-Cx0CCAFC.mjs.map +0 -1
@@ -1,10 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Pipe, input, signal, output, Component, effect, CUSTOM_ELEMENTS_SCHEMA, HostListener, Directive, computed, ChangeDetectionStrategy, Injectable, inject, contentChild, isSignal, Injector, InjectionToken, DestroyRef } from '@angular/core';
2
+ import { Pipe, input, signal, output, Component, CUSTOM_ELEMENTS_SCHEMA, HostListener, Directive, computed, effect, ChangeDetectionStrategy, Injectable, inject, ElementRef, contentChild, isSignal, Injector, InjectionToken, DestroyRef } from '@angular/core';
3
3
  import * as i1$2 from '@angular/forms';
4
4
  import { FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormGroup, FormsModule, Validators, ControlContainer } from '@angular/forms';
5
5
  import * as i1 from '@angular/material/checkbox';
6
6
  import { MatCheckboxModule } from '@angular/material/checkbox';
7
- import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
7
  import * as i4 from '@angular/material/chips';
9
8
  import { MatChipGrid, MatChipRow, MatChipInput, MatChipRemove, MatChipsModule, MatChipSet, MatChip } from '@angular/material/chips';
10
9
  import { MatFormField, MatFormFieldModule, MatHint, MatPrefix, MatError, MatLabel } from '@angular/material/form-field';
@@ -23,6 +22,7 @@ import { Subscription, BehaviorSubject, merge, of, map, from, tap, debounceTime,
23
22
  import * as i2$1 from '@angular/material/timepicker';
24
23
  import { MatTimepickerModule } from '@angular/material/timepicker';
25
24
  import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
25
+ import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
26
26
  import { parsePhoneNumber, getExampleNumber } from 'libphonenumber-js/mobile';
27
27
  import examples from 'libphonenumber-js/mobile/examples';
28
28
  import * as i1$4 from '@angular/material/button-toggle';
@@ -53,7 +53,10 @@ class ControlBase {
53
53
  formControlName = input("", ...(ngDevMode ? [{ debugName: "formControlName" }] : /* istanbul ignore next */ []));
54
54
  min = input(null, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
55
55
  max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
56
+ minLength = input(null, ...(ngDevMode ? [{ debugName: "minLength" }] : /* istanbul ignore next */ []));
57
+ maxLength = input(null, ...(ngDevMode ? [{ debugName: "maxLength" }] : /* istanbul ignore next */ []));
56
58
  disabled = signal(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
59
+ readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
57
60
  multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
58
61
  placeholder = input("", ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
59
62
  hints = input([], ...(ngDevMode ? [{ debugName: "hints" }] : /* istanbul ignore next */ []));
@@ -61,8 +64,6 @@ class ControlBase {
61
64
  touched = signal(false, ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
62
65
  onChange = output();
63
66
  ctrl = new FormControl(null);
64
- onTouch = () => { };
65
- onPropagateValue = (_) => { };
66
67
  registerOnChange(fn) {
67
68
  this.onPropagateValue = fn;
68
69
  }
@@ -79,7 +80,7 @@ class ControlBase {
79
80
  }
80
81
  }
81
82
  writeValue(value) {
82
- this.ctrl.setValue(value);
83
+ this.ctrl.setValue(value, { emitEvent: false });
83
84
  }
84
85
  markAsTouched() {
85
86
  this.touched.set(true);
@@ -88,6 +89,8 @@ class ControlBase {
88
89
  validate(control) {
89
90
  return this.ctrl.errors;
90
91
  }
92
+ onTouch = () => { };
93
+ onPropagateValue = (_) => { };
91
94
  emitValue(value) {
92
95
  let emissionValue = this.ctrl.getRawValue();
93
96
  if (value !== undefined) {
@@ -111,12 +114,12 @@ class ControlBase {
111
114
  this.onChange.emit(emissionValue);
112
115
  }
113
116
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ControlBase, deps: [], target: i0.ɵɵFactoryTarget.Component });
114
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: ControlBase, isStandalone: true, selector: "ng-component", inputs: { formControlName: { classPropertyName: "formControlName", publicName: "formControlName", 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 }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, hints: { classPropertyName: "hints", publicName: "hints", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onChange: "onChange" }, ngImport: i0, template: ``, isInline: true });
117
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: ControlBase, isStandalone: true, selector: "ng-component", inputs: { formControlName: { classPropertyName: "formControlName", publicName: "formControlName", 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 }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, hints: { classPropertyName: "hints", publicName: "hints", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onChange: "onChange" }, ngImport: i0, template: ``, isInline: true });
115
118
  }
116
119
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ControlBase, decorators: [{
117
120
  type: Component,
118
121
  args: [{ template: `` }]
119
- }], propDecorators: { formControlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "formControlName", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hints: [{ type: i0.Input, args: [{ isSignal: true, alias: "hints", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], onChange: [{ type: i0.Output, args: ["onChange"] }] } });
122
+ }], propDecorators: { formControlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "formControlName", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hints: [{ type: i0.Input, args: [{ isSignal: true, alias: "hints", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], onChange: [{ type: i0.Output, args: ["onChange"] }] } });
120
123
 
121
124
  class CheckboxControl extends ControlBase {
122
125
  options = input.required(...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
@@ -133,25 +136,26 @@ class CheckboxControl extends ControlBase {
133
136
  }
134
137
  addToSelectedOptionsMap(option) {
135
138
  if (option) {
136
- this.selectedOptions = signal(option, ...(ngDevMode ? [{ debugName: "selectedOptions" }] : /* istanbul ignore next */ []));
139
+ this.selectedOptions.set(option);
137
140
  }
138
141
  }
139
142
  onCheckboxChange(checked, value) {
143
+ if (this.readonly()) {
144
+ return;
145
+ }
146
+ this.markAsTouched();
140
147
  const selectedOption = this.options().find((option) => option.value === value);
141
148
  if (!selectedOption) {
142
149
  return;
143
150
  }
144
- this.selectedOptions.update((prev) => {
145
- const options = prev;
146
- if (checked) {
147
- options.set(selectedOption.value, selectedOption);
148
- }
149
- else {
150
- options.delete(selectedOption.value);
151
- }
152
- return options;
153
- });
154
- this.getEmissionValue(this.selectedOptions());
151
+ const options = new Map(this.selectedOptions());
152
+ if (checked) {
153
+ options.set(selectedOption.value, selectedOption);
154
+ }
155
+ else {
156
+ options.delete(selectedOption.value);
157
+ }
158
+ this.getEmissionValue(options);
155
159
  }
156
160
  stringifySelectedOptions() {
157
161
  if (!this.selectedOptions()?.size) {
@@ -161,7 +165,13 @@ class CheckboxControl extends ControlBase {
161
165
  }
162
166
  getEmissionValue(options) {
163
167
  this.addToSelectedOptionsMap(options);
164
- this.emitValue(this.stringifySelectedOptions());
168
+ // the form keeps the stringified values, while (onChange) consumers get the resolved options
169
+ this.propagateValue(this.stringifySelectedOptions());
170
+ this.emitOnChangeEvent(this.#convertSelectedMapToArray());
171
+ }
172
+ #convertSelectedMapToArray() {
173
+ const selectedOptions = Array.from(this.selectedOptions().values());
174
+ return selectedOptions.length ? selectedOptions : null;
165
175
  }
166
176
  #selectedOptionsMapFactory(selectedOptions) {
167
177
  const selectedOptionsMap = new Map();
@@ -171,7 +181,7 @@ class CheckboxControl extends ControlBase {
171
181
  return selectedOptionsMap;
172
182
  }
173
183
  #reset() {
174
- this.selectedOptions().clear();
184
+ this.selectedOptions.set(new Map());
175
185
  this.ctrl.setValue(null);
176
186
  }
177
187
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CheckboxControl, deps: null, target: i0.ɵɵFactoryTarget.Component });
@@ -181,7 +191,7 @@ class CheckboxControl extends ControlBase {
181
191
  <ul class="list-unstyled">
182
192
  @for (option of options(); track option.value) {
183
193
  <li class="mb-2">
184
- <mat-checkbox [checked]="option | selectStatus: selectedOptions()" (change)="onCheckboxChange($event.checked, option.value)">
194
+ <mat-checkbox [checked]="option | selectStatus: selectedOptions()" [disabled]="disabled() || readonly()" (change)="onCheckboxChange($event.checked, option.value)">
185
195
  {{ option.label }}
186
196
  </mat-checkbox>
187
197
  </li>
@@ -201,7 +211,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
201
211
  <ul class="list-unstyled">
202
212
  @for (option of options(); track option.value) {
203
213
  <li class="mb-2">
204
- <mat-checkbox [checked]="option | selectStatus: selectedOptions()" (change)="onCheckboxChange($event.checked, option.value)">
214
+ <mat-checkbox [checked]="option | selectStatus: selectedOptions()" [disabled]="disabled() || readonly()" (change)="onCheckboxChange($event.checked, option.value)">
205
215
  {{ option.label }}
206
216
  </mat-checkbox>
207
217
  </li>
@@ -213,12 +223,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
213
223
 
214
224
  class ChipsControl extends ControlBase {
215
225
  keywords = signal([], ...(ngDevMode ? [{ debugName: "keywords" }] : /* istanbul ignore next */ []));
216
- #ctrlChanges = toSignal(this.ctrl.valueChanges);
217
- #effect = effect(() => {
218
- if (this.#ctrlChanges()) {
219
- this.emitValue(this.#stringifyValues());
220
- }
221
- }, ...(ngDevMode ? [{ debugName: "#effect" }] : /* istanbul ignore next */ []));
222
226
  writeValue(value) {
223
227
  if (typeof value === "string") {
224
228
  this.keywords.set(this.#parseValues(value));
@@ -228,6 +232,10 @@ class ChipsControl extends ControlBase {
228
232
  }
229
233
  }
230
234
  remove(keyword) {
235
+ if (this.disabled() || this.readonly()) {
236
+ return;
237
+ }
238
+ this.markAsTouched();
231
239
  this.keywords.update((keywords) => {
232
240
  const index = keywords.indexOf(keyword);
233
241
  if (index < 0) {
@@ -239,6 +247,10 @@ class ChipsControl extends ControlBase {
239
247
  this.emitValue(this.#stringifyValues());
240
248
  }
241
249
  add(event) {
250
+ if (this.disabled() || this.readonly()) {
251
+ return;
252
+ }
253
+ this.markAsTouched();
242
254
  const value = (event.value || "").trim();
243
255
  if (value) {
244
256
  this.keywords.update((keywords) => [...keywords, value]);
@@ -246,9 +258,6 @@ class ChipsControl extends ControlBase {
246
258
  event.chipInput.clear();
247
259
  this.emitValue(this.#stringifyValues());
248
260
  }
249
- ngOnDestroy() {
250
- this.#effect.destroy();
251
- }
252
261
  #stringifyValues() {
253
262
  return this.keywords().map((s) => s.trim()).join();
254
263
  }
@@ -265,8 +274,10 @@ class ChipsControl extends ControlBase {
265
274
  </mat-label>
266
275
  <input
267
276
  matInput
268
- [placeholder]="placeholder()"
269
- [matChipInputFor]="chipGrid"
277
+ [placeholder]="placeholder()"
278
+ [matChipInputFor]="chipGrid"
279
+ [disabled]="disabled()"
280
+ [readonly]="readonly()"
270
281
  (matChipInputTokenEnd)="add($event)"
271
282
  />
272
283
  </mat-form-field>
@@ -274,7 +285,7 @@ class ChipsControl extends ControlBase {
274
285
  @for (keyword of keywords(); track keyword) {
275
286
  <mat-chip-row (removed)="remove(keyword)">
276
287
  {{ keyword }}
277
- <button matChipRemove>
288
+ <button matChipRemove [disabled]="disabled() || readonly()">
278
289
  <ds-icon icon="trash" class="fc-red"></ds-icon>
279
290
  </button>
280
291
  </mat-chip-row>
@@ -307,8 +318,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
307
318
  </mat-label>
308
319
  <input
309
320
  matInput
310
- [placeholder]="placeholder()"
311
- [matChipInputFor]="chipGrid"
321
+ [placeholder]="placeholder()"
322
+ [matChipInputFor]="chipGrid"
323
+ [disabled]="disabled()"
324
+ [readonly]="readonly()"
312
325
  (matChipInputTokenEnd)="add($event)"
313
326
  />
314
327
  </mat-form-field>
@@ -316,7 +329,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
316
329
  @for (keyword of keywords(); track keyword) {
317
330
  <mat-chip-row (removed)="remove(keyword)">
318
331
  {{ keyword }}
319
- <button matChipRemove>
332
+ <button matChipRemove [disabled]="disabled() || readonly()">
320
333
  <ds-icon icon="trash" class="fc-red"></ds-icon>
321
334
  </button>
322
335
  </mat-chip-row>
@@ -346,21 +359,35 @@ class DatePickerControl extends ControlBase {
346
359
  disabledDays = input([], ...(ngDevMode ? [{ debugName: "disabledDays" }] : /* istanbul ignore next */ []));
347
360
  minDate = input(null, ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
348
361
  maxDate = input(null, ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
349
- calendarDaysFilter = (d) => {
350
- const day = (d || new Date()).getDay();
351
- return !(this.disabledDays().includes(day));
352
- };
353
362
  clear(event) {
363
+ if (this.readonly()) {
364
+ return;
365
+ }
354
366
  event.stopImmediatePropagation();
355
367
  this.ctrl.setValue(null);
356
368
  this.propagateValue(null);
357
369
  this.emitOnChangeEvent(null);
358
370
  }
371
+ calendarDaysFilter = (d) => {
372
+ const day = (d || new Date()).getDay();
373
+ return !(this.disabledDays().includes(day));
374
+ };
359
375
  emit($event) {
376
+ if (this.readonly()) {
377
+ return;
378
+ }
379
+ this.markAsTouched();
360
380
  const selectedDateString = format($event.value, "yyyy-MM-dd");
361
381
  this.propagateValue(selectedDateString);
362
382
  this.emitOnChangeEvent(selectedDateString);
363
383
  }
384
+ openPicker(picker) {
385
+ if (this.readonly()) {
386
+ return;
387
+ }
388
+ picker.open();
389
+ this.markAsTouched();
390
+ }
364
391
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatePickerControl, deps: null, target: i0.ɵɵFactoryTarget.Component });
365
392
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DatePickerControl, isStandalone: true, selector: "iss-ctrl-date-picker", inputs: { disabledDays: { classPropertyName: "disabledDays", publicName: "disabledDays", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
366
393
  DatePipe,
@@ -378,8 +405,9 @@ class DatePickerControl extends ControlBase {
378
405
  matInput
379
406
  readonly
380
407
  [matDatepicker]="picker"
381
- (click)="picker.open(); markAsTouched()"
382
- [formControl]="ctrl"
408
+ (click)="openPicker(picker)"
409
+ [formControl]="ctrl"
410
+ [readonly]="readonly()"
383
411
  [min]="minDate()"
384
412
  [max]="maxDate()"
385
413
  [matDatepickerFilter]="calendarDaysFilter"
@@ -388,7 +416,7 @@ class DatePickerControl extends ControlBase {
388
416
  @if (ctrl.getRawValue()) {
389
417
  <mat-icon matSuffix style="cursor: pointer" (click)="clear($event)">close</mat-icon>
390
418
  }
391
- <mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
419
+ <mat-datepicker-toggle matIconSuffix [for]="picker" [disabled]="readonly()"></mat-datepicker-toggle>
392
420
  <mat-datepicker #picker></mat-datepicker>
393
421
 
394
422
  </mat-form-field>
@@ -412,8 +440,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
412
440
  matInput
413
441
  readonly
414
442
  [matDatepicker]="picker"
415
- (click)="picker.open(); markAsTouched()"
416
- [formControl]="ctrl"
443
+ (click)="openPicker(picker)"
444
+ [formControl]="ctrl"
445
+ [readonly]="readonly()"
417
446
  [min]="minDate()"
418
447
  [max]="maxDate()"
419
448
  [matDatepickerFilter]="calendarDaysFilter"
@@ -422,7 +451,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
422
451
  @if (ctrl.getRawValue()) {
423
452
  <mat-icon matSuffix style="cursor: pointer" (click)="clear($event)">close</mat-icon>
424
453
  }
425
- <mat-datepicker-toggle matIconSuffix [for]="picker"></mat-datepicker-toggle>
454
+ <mat-datepicker-toggle matIconSuffix [for]="picker" [disabled]="readonly()"></mat-datepicker-toggle>
426
455
  <mat-datepicker #picker></mat-datepicker>
427
456
 
428
457
  </mat-form-field>
@@ -438,20 +467,36 @@ class DateRangePickerControl extends ControlBase {
438
467
  start: new FormControl(null),
439
468
  end: new FormControl(null),
440
469
  });
441
- calendarDaysFilter = (d) => {
442
- const day = (d || new Date()).getDay();
443
- return !(this.disabledDays().includes(day));
444
- };
445
470
  #sub = new Subscription();
446
471
  ngOnInit() {
447
472
  this.#sub.add(this.range.valueChanges.subscribe((values) => {
448
- if (values.start && values.end) {
449
- this.emitValue(this.#getEmissionValue(values.start, values.end));
450
- this.onDateRangeObjectChange.emit(this.#getEmissionObject(values.start, values.end));
473
+ if (this.readonly()) {
474
+ return;
475
+ }
476
+ if (!values.start || !values.end) {
477
+ // an incomplete range is not a value, clearing one endpoint must clear the form value
478
+ this.emitValue(null);
479
+ this.onDateRangeObjectChange.emit(null);
480
+ return;
451
481
  }
482
+ this.emitValue(this.#getEmissionValue(values.start, values.end));
483
+ this.onDateRangeObjectChange.emit(this.#getEmissionObject(values.start, values.end));
452
484
  }));
453
485
  }
486
+ setDisabledState(isDisabled) {
487
+ super.setDisabledState(isDisabled);
488
+ // the template binds `range`, disabling ctrl alone leaves the inputs live
489
+ if (isDisabled) {
490
+ this.range.disable({ emitEvent: false });
491
+ }
492
+ else {
493
+ this.range.enable({ emitEvent: false });
494
+ }
495
+ }
454
496
  clear(event) {
497
+ if (this.readonly()) {
498
+ return;
499
+ }
455
500
  event.stopImmediatePropagation();
456
501
  this.ctrl.setValue(null);
457
502
  this.range.setValue({
@@ -461,19 +506,33 @@ class DateRangePickerControl extends ControlBase {
461
506
  this.propagateValue(null);
462
507
  this.emitOnChangeEvent(null);
463
508
  }
509
+ openPicker(picker) {
510
+ if (this.readonly()) {
511
+ return;
512
+ }
513
+ picker.open();
514
+ this.markAsTouched();
515
+ }
464
516
  writeValue(value) {
465
- if (typeof value === "string") {
466
- const valArr = value?.split(",");
467
- const valObj = {
468
- start: valArr[0],
469
- end: valArr[1],
470
- };
471
- this.range.patchValue(valObj, { emitEvent: false });
517
+ if (typeof value !== "string" || !value) {
518
+ this.ctrl.setValue(null, { emitEvent: false });
519
+ this.range.reset({ start: null, end: null }, { emitEvent: false });
520
+ return;
472
521
  }
522
+ const valArr = value.split(",");
523
+ const valObj = {
524
+ start: valArr[0] ?? null,
525
+ end: valArr[1] ?? null,
526
+ };
527
+ this.range.patchValue(valObj, { emitEvent: false });
473
528
  }
474
529
  ngOnDestroy() {
475
530
  this.#sub.unsubscribe();
476
531
  }
532
+ calendarDaysFilter = (d) => {
533
+ const day = (d || new Date()).getDay();
534
+ return !(this.disabledDays().includes(day));
535
+ };
477
536
  #getDuration(startDate, endDate) {
478
537
  const start = new Date(startDate).getTime();
479
538
  const end = new Date(endDate).getTime();
@@ -518,21 +577,23 @@ class DateRangePickerControl extends ControlBase {
518
577
  >
519
578
  <input
520
579
  matStartDate
521
- formControlName="start"
580
+ formControlName="start"
581
+ [readonly]="readonly()"
522
582
  placeholder="dd/mm/yyyy"
523
- (click)="picker.open(); markAsTouched()"
583
+ (click)="openPicker(picker)"
524
584
  />
525
585
  <input
526
586
  matEndDate
527
- formControlName="end"
587
+ formControlName="end"
588
+ [readonly]="readonly()"
528
589
  placeholder="dd/mm/yyyy"
529
- (click)="picker.open(); markAsTouched()"
590
+ (click)="openPicker(picker)"
530
591
  />
531
592
  </mat-date-range-input>
532
593
  @if (range.getRawValue().start || range.getRawValue().end) {
533
594
  <mat-icon matSuffix style="cursor: pointer" (click)="clear($event)">close</mat-icon>
534
595
  }
535
- <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
596
+ <mat-datepicker-toggle matSuffix [for]="picker" [disabled]="readonly()"></mat-datepicker-toggle>
536
597
  <mat-date-range-picker #picker></mat-date-range-picker>
537
598
 
538
599
  </mat-form-field>
@@ -568,21 +629,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
568
629
  >
569
630
  <input
570
631
  matStartDate
571
- formControlName="start"
632
+ formControlName="start"
633
+ [readonly]="readonly()"
572
634
  placeholder="dd/mm/yyyy"
573
- (click)="picker.open(); markAsTouched()"
635
+ (click)="openPicker(picker)"
574
636
  />
575
637
  <input
576
638
  matEndDate
577
- formControlName="end"
639
+ formControlName="end"
640
+ [readonly]="readonly()"
578
641
  placeholder="dd/mm/yyyy"
579
- (click)="picker.open(); markAsTouched()"
642
+ (click)="openPicker(picker)"
580
643
  />
581
644
  </mat-date-range-input>
582
645
  @if (range.getRawValue().start || range.getRawValue().end) {
583
646
  <mat-icon matSuffix style="cursor: pointer" (click)="clear($event)">close</mat-icon>
584
647
  }
585
- <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
648
+ <mat-datepicker-toggle matSuffix [for]="picker" [disabled]="readonly()"></mat-datepicker-toggle>
586
649
  <mat-date-range-picker #picker></mat-date-range-picker>
587
650
 
588
651
  </mat-form-field>
@@ -595,23 +658,32 @@ class TimePickerControl extends ControlBase {
595
658
  minTime = input(null, ...(ngDevMode ? [{ debugName: "minTime" }] : /* istanbul ignore next */ []));
596
659
  maxTime = input(null, ...(ngDevMode ? [{ debugName: "maxTime" }] : /* istanbul ignore next */ []));
597
660
  emit(evt) {
661
+ if (this.readonly()) {
662
+ return;
663
+ }
598
664
  this.propagateValue(this.#stringifyTime(evt.value));
599
665
  this.emitOnChangeEvent(this.#stringifyTime(evt.value));
600
666
  }
601
667
  clear(event) {
668
+ if (this.readonly()) {
669
+ return;
670
+ }
602
671
  event.stopImmediatePropagation();
603
672
  this.ctrl.setValue(null);
604
673
  this.propagateValue(null);
605
674
  this.emitOnChangeEvent(null);
606
675
  }
607
676
  writeValue(value) {
608
- if (value) {
609
- const d = new Date();
610
- d.setHours(value.split(":")[0]);
611
- d.setMinutes(value.split(":")[1]);
612
- d.setSeconds(value.split(":")[2] ?? "00");
613
- this.ctrl.setValue(d);
677
+ if (!value) {
678
+ this.ctrl.setValue(null, { emitEvent: false });
679
+ return;
614
680
  }
681
+ const parts = String(value).split(":");
682
+ const d = new Date();
683
+ d.setHours(Number(parts[0]));
684
+ d.setMinutes(Number(parts[1]));
685
+ d.setSeconds(Number(parts[2] ?? "00"));
686
+ this.ctrl.setValue(d, { emitEvent: false });
615
687
  }
616
688
  #stringifyTime(value) {
617
689
  let formatedTime;
@@ -641,14 +713,15 @@ class TimePickerControl extends ControlBase {
641
713
  [formControl]="ctrl"
642
714
  [matTimepicker]="picker"
643
715
  [matTimepickerMin]="minTime()"
644
- [matTimepickerMax]="maxTime()"
716
+ [matTimepickerMax]="maxTime()"
717
+ [readonly]="readonly()"
645
718
  readonly
646
719
  />
647
720
  @if (ctrl.getRawValue()) {
648
721
  <mat-icon matSuffix style="cursor: pointer" (click)="clear($event)">close</mat-icon>
649
722
  }
650
- <mat-timepicker-toggle matIconSuffix [for]="picker" />
651
- <mat-timepicker (selected)="emit($event)" [interval]="interval()" #picker />
723
+ <mat-timepicker-toggle matIconSuffix [for]="picker" [disabled]="readonly()"/>
724
+ <mat-timepicker (selected)="emit($event)" [interval]="interval()" #picker/>
652
725
 
653
726
  </mat-form-field>
654
727
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i1$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i1$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTimepickerModule }, { kind: "component", type: i2$1.MatTimepicker, selector: "mat-timepicker", inputs: ["interval", "options", "disableRipple", "aria-label", "aria-labelledby", "panelClass"], outputs: ["selected", "opened", "closed"], exportAs: ["matTimepicker"] }, { kind: "directive", type: i2$1.MatTimepickerInput, selector: "input[matTimepicker]", inputs: ["value", "matTimepicker", "matTimepickerMin", "matTimepickerMax", "matTimepickerOpenOnClick", "disabled"], outputs: ["valueChange"], exportAs: ["matTimepickerInput"] }, { kind: "component", type: i2$1.MatTimepickerToggle, selector: "mat-timepicker-toggle", inputs: ["for", "aria-label", "aria-labelledby", "disabled", "tabIndex", "disableRipple"], exportAs: ["matTimepickerToggle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
@@ -676,14 +749,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
676
749
  [formControl]="ctrl"
677
750
  [matTimepicker]="picker"
678
751
  [matTimepickerMin]="minTime()"
679
- [matTimepickerMax]="maxTime()"
752
+ [matTimepickerMax]="maxTime()"
753
+ [readonly]="readonly()"
680
754
  readonly
681
755
  />
682
756
  @if (ctrl.getRawValue()) {
683
757
  <mat-icon matSuffix style="cursor: pointer" (click)="clear($event)">close</mat-icon>
684
758
  }
685
- <mat-timepicker-toggle matIconSuffix [for]="picker" />
686
- <mat-timepicker (selected)="emit($event)" [interval]="interval()" #picker />
759
+ <mat-timepicker-toggle matIconSuffix [for]="picker" [disabled]="readonly()"/>
760
+ <mat-timepicker (selected)="emit($event)" [interval]="interval()" #picker/>
687
761
 
688
762
  </mat-form-field>
689
763
  `,
@@ -700,7 +774,20 @@ class DatetimePickerControl extends ControlBase {
700
774
  maxDate = input(null, ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
701
775
  minTime = input(null, ...(ngDevMode ? [{ debugName: "minTime" }] : /* istanbul ignore next */ []));
702
776
  maxTime = input(null, ...(ngDevMode ? [{ debugName: "maxTime" }] : /* istanbul ignore next */ []));
777
+ setDisabledState(isDisabled) {
778
+ super.setDisabledState(isDisabled);
779
+ // the template binds `datetimeForm`, disabling ctrl alone leaves the pickers live
780
+ if (isDisabled) {
781
+ this.datetimeForm.disable({ emitEvent: false });
782
+ }
783
+ else {
784
+ this.datetimeForm.enable({ emitEvent: false });
785
+ }
786
+ }
703
787
  emit() {
788
+ if (this.readonly()) {
789
+ return;
790
+ }
704
791
  const datetimeStr = this.#stringifyDateTime(this.datetimeForm.getRawValue());
705
792
  this.propagateValue(datetimeStr);
706
793
  this.emitOnChangeEvent(datetimeStr);
@@ -752,7 +839,7 @@ class DatetimePickerControl extends ControlBase {
752
839
  { provide: NG_VALIDATORS, useExisting: DatetimePickerControl, multi: true },
753
840
  ], usesInheritance: true, ngImport: i0, template: `
754
841
  <form [formGroup]="datetimeForm" class="d-flex flex-row gap-1">
755
- <iss-ctrl-date-picker (focusout)="markAsTouched()" [disabledDays]="disabledDays()" [maxDate]="maxDate()" [minDate]="minDate()" formControlName="date" (onChange)="emit()">
842
+ <iss-ctrl-date-picker (focusout)="markAsTouched()" [disabledDays]="disabledDays()" [maxDate]="maxDate()" [minDate]="minDate()" [readonly]="readonly()" formControlName="date" (onChange)="emit()">
756
843
  <label>
757
844
  <ng-content select="label"></ng-content>
758
845
  </label>
@@ -760,7 +847,8 @@ class DatetimePickerControl extends ControlBase {
760
847
  <iss-ctrl-time-picker
761
848
  (focusout)="markAsTouched()"
762
849
  [maxTime]="maxTime()"
763
- [minTime]="minTime()"
850
+ [minTime]="minTime()"
851
+ [readonly]="readonly()"
764
852
  formControlName="time"
765
853
  (onChange)="emit()"
766
854
  />
@@ -786,7 +874,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
786
874
  ],
787
875
  template: `
788
876
  <form [formGroup]="datetimeForm" class="d-flex flex-row gap-1">
789
- <iss-ctrl-date-picker (focusout)="markAsTouched()" [disabledDays]="disabledDays()" [maxDate]="maxDate()" [minDate]="minDate()" formControlName="date" (onChange)="emit()">
877
+ <iss-ctrl-date-picker (focusout)="markAsTouched()" [disabledDays]="disabledDays()" [maxDate]="maxDate()" [minDate]="minDate()" [readonly]="readonly()" formControlName="date" (onChange)="emit()">
790
878
  <label>
791
879
  <ng-content select="label"></ng-content>
792
880
  </label>
@@ -794,7 +882,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
794
882
  <iss-ctrl-time-picker
795
883
  (focusout)="markAsTouched()"
796
884
  [maxTime]="maxTime()"
797
- [minTime]="minTime()"
885
+ [minTime]="minTime()"
886
+ [readonly]="readonly()"
798
887
  formControlName="time"
799
888
  (onChange)="emit()"
800
889
  />
@@ -910,7 +999,6 @@ class FilePickerControl extends ControlBase {
910
999
  attachments = signal([], ...(ngDevMode ? [{ debugName: "attachments" }] : /* istanbul ignore next */ []));
911
1000
  #uploadedFilesEffectRef = effect(() => {
912
1001
  this.#markUploadedFiles();
913
- this.filesValue = computed(() => this.#returnAttachmentViewObject());
914
1002
  }, ...(ngDevMode ? [{ debugName: "#uploadedFilesEffectRef" }] : /* istanbul ignore next */ []));
915
1003
  /*todo needs review and refactor*/
916
1004
  ngOnDestroy() {
@@ -927,8 +1015,12 @@ class FilePickerControl extends ControlBase {
927
1015
  }
928
1016
  onDelete(files) {
929
1017
  this.attachments.set(files);
1018
+ this.emitValue(this.#formDataFactory(this.attachments()));
930
1019
  }
931
1020
  captureFile($event) {
1021
+ if (this.readonly() || this.disabled()) {
1022
+ return;
1023
+ }
932
1024
  if (!$event) {
933
1025
  return;
934
1026
  }
@@ -947,13 +1039,21 @@ class FilePickerControl extends ControlBase {
947
1039
  };
948
1040
  });
949
1041
  this.#updateAttachments(issFiles);
950
- const emission = computed(() => this.#formDataFactory(this.attachments()), ...(ngDevMode ? [{ debugName: "emission" }] : /* istanbul ignore next */ []));
951
- this.emitOnChangeEvent(emission());
1042
+ // The field component uploads this output. Emit only the files selected in
1043
+ // this interaction: `attachments` also contains files selected earlier, so
1044
+ // serialising it would upload those files again whenever more are added.
1045
+ this.emitValue(this.#formDataFactory(issFiles));
1046
+ }
1047
+ openFilePicker(input) {
1048
+ if (this.readonly() || this.disabled()) {
1049
+ return;
1050
+ }
1051
+ input.click();
1052
+ this.markAsTouched();
952
1053
  }
953
1054
  #updateAttachments(selectedFiles) {
954
1055
  if (!this.multiple()) {
955
- this.attachments.set([]);
956
- this.attachments()[0] = selectedFiles[0];
1056
+ this.attachments.set([selectedFiles[0]]);
957
1057
  return;
958
1058
  }
959
1059
  this.attachments.update((files) => {
@@ -1008,7 +1108,7 @@ class FilePickerControl extends ControlBase {
1008
1108
  return {
1009
1109
  config: {
1010
1110
  showDownloadButton: this.showDownloadButton(),
1011
- showDeleteButton: this.showDownloadButton()
1111
+ showDeleteButton: this.showDeleteButton()
1012
1112
  },
1013
1113
  ctrlJsonValue: signal(this.attachments())
1014
1114
  };
@@ -1017,20 +1117,21 @@ class FilePickerControl extends ControlBase {
1017
1117
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: FilePickerControl, isStandalone: true, selector: "iss-ctrl-file-picker", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, showDownloadButton: { classPropertyName: "showDownloadButton", publicName: "showDownloadButton", isSignal: true, isRequired: false, transformFunction: null }, showDeleteButton: { classPropertyName: "showDeleteButton", publicName: "showDeleteButton", isSignal: true, isRequired: false, transformFunction: null }, uploading: { classPropertyName: "uploading", publicName: "uploading", isSignal: true, isRequired: false, transformFunction: null }, uploadedFiles: { classPropertyName: "uploadedFiles", publicName: "uploadedFiles", isSignal: true, isRequired: false, transformFunction: null }, uploadingError: { classPropertyName: "uploadingError", publicName: "uploadingError", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
1018
1118
  { provide: NG_VALUE_ACCESSOR, useExisting: FilePickerControl, multi: true }
1019
1119
  ], usesInheritance: true, ngImport: i0, template: `
1020
- <ds-file-picker (click)="inputFile.click(); markAsTouched()" (onDrop)="captureFile($event)" droppable>
1120
+ <ds-file-picker (click)="openFilePicker(inputFile)" (onDrop)="captureFile($event)" droppable>
1021
1121
  <input
1022
1122
  #inputFile
1023
1123
  (change)="captureFile($any($event.target).files)"
1124
+ (click)="$event.stopPropagation()"
1024
1125
  [accept]="accept()"
1025
1126
  [formControl]="ctrl"
1026
1127
  [multiple]="multiple()"
1128
+ [disabled]="readonly()"
1027
1129
  [style.display]="'none'"
1028
- id="file"
1029
1130
  type="file"
1030
1131
  />
1031
1132
  </ds-file-picker>
1032
1133
 
1033
- <iss-ctrl-hints [hints]="hints()" [touched]="touched()" />
1134
+ <iss-ctrl-hints [hints]="hints()" [touched]="touched()"/>
1034
1135
  <!--<iss-viewer-attachment (deleteOut)="onDelete($event)" [field]="filesValue()" [uploading]="uploading()"/>-->
1035
1136
  `, isInline: true, styles: [".file-uploader{--uploader-height: 150px;--uploader-width: 100%;--uploader-bg: var(--off-white);--uploader-border: 1px dashed var(--dark-gray);--uploader-radius: var(--box-radius);--uploader-padding: 1rem;height:var(--uploader-height);background-color:var(--uploader-bg);border:var(--uploader-border);border-radius:var(--uploader-radius);padding:var(--uploader-padding);text-align:center;cursor:pointer}.file-uploader --input{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:.75rem}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: Droppable, selector: "[droppable]", outputs: ["onDrop"] }, { kind: "component", type: HintsComponent, selector: "iss-ctrl-hints", inputs: ["hints", "touched"] }] });
1036
1137
  }
@@ -1043,20 +1144,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1043
1144
  ], providers: [
1044
1145
  { provide: NG_VALUE_ACCESSOR, useExisting: FilePickerControl, multi: true }
1045
1146
  ], template: `
1046
- <ds-file-picker (click)="inputFile.click(); markAsTouched()" (onDrop)="captureFile($event)" droppable>
1147
+ <ds-file-picker (click)="openFilePicker(inputFile)" (onDrop)="captureFile($event)" droppable>
1047
1148
  <input
1048
1149
  #inputFile
1049
1150
  (change)="captureFile($any($event.target).files)"
1151
+ (click)="$event.stopPropagation()"
1050
1152
  [accept]="accept()"
1051
1153
  [formControl]="ctrl"
1052
1154
  [multiple]="multiple()"
1155
+ [disabled]="readonly()"
1053
1156
  [style.display]="'none'"
1054
- id="file"
1055
1157
  type="file"
1056
1158
  />
1057
1159
  </ds-file-picker>
1058
1160
 
1059
- <iss-ctrl-hints [hints]="hints()" [touched]="touched()" />
1161
+ <iss-ctrl-hints [hints]="hints()" [touched]="touched()"/>
1060
1162
  <!--<iss-viewer-attachment (deleteOut)="onDelete($event)" [field]="filesValue()" [uploading]="uploading()"/>-->
1061
1163
  `, styles: [".file-uploader{--uploader-height: 150px;--uploader-width: 100%;--uploader-bg: var(--off-white);--uploader-border: 1px dashed var(--dark-gray);--uploader-radius: var(--box-radius);--uploader-padding: 1rem;height:var(--uploader-height);background-color:var(--uploader-bg);border:var(--uploader-border);border-radius:var(--uploader-radius);padding:var(--uploader-padding);text-align:center;cursor:pointer}.file-uploader --input{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:.75rem}\n"] }]
1062
1164
  }], propDecorators: { accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], showDownloadButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDownloadButton", required: false }] }], showDeleteButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDeleteButton", required: false }] }], uploading: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploading", required: false }] }], uploadedFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadedFiles", required: false }] }], uploadingError: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadingError", required: false }] }] } });
@@ -1069,12 +1171,6 @@ class InputControl extends ControlBase {
1069
1171
  suffixText = input("", ...(ngDevMode ? [{ debugName: "suffixText" }] : /* istanbul ignore next */ []));
1070
1172
  onPrefixClick = output();
1071
1173
  onSuffixClick = output();
1072
- #ctrlChanges = toSignal(this.ctrl.valueChanges);
1073
- #effect = effect(() => {
1074
- if (this.#ctrlChanges()) {
1075
- this.emitValue(this.#ctrlChanges());
1076
- }
1077
- }, ...(ngDevMode ? [{ debugName: "#effect" }] : /* istanbul ignore next */ []));
1078
1174
  #disabledEffect = effect(() => {
1079
1175
  if (this.disabled()) {
1080
1176
  this.ctrl.disable();
@@ -1090,7 +1186,6 @@ class InputControl extends ControlBase {
1090
1186
  this.onPrefixClick.emit(this.ctrl.getRawValue());
1091
1187
  }
1092
1188
  ngOnDestroy() {
1093
- this.#effect.destroy();
1094
1189
  this.#disabledEffect.destroy();
1095
1190
  }
1096
1191
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: InputControl, deps: null, target: i0.ɵɵFactoryTarget.Component });
@@ -1114,11 +1209,14 @@ class InputControl extends ControlBase {
1114
1209
  <input
1115
1210
  matInput
1116
1211
  (blur)="markAsTouched()"
1117
- (input)="emitOnChangeEvent()"
1212
+ (input)="emitValue()"
1118
1213
  [formControl]="ctrl"
1119
- [maxlength]="max()"
1120
- [minlength]="min()"
1121
- [placeholder]="placeholder()"
1214
+ [attr.maxlength]="maxLength()"
1215
+ [attr.minlength]="minLength()"
1216
+ [attr.max]="max()"
1217
+ [attr.min]="min()"
1218
+ [placeholder]="placeholder()"
1219
+ [readonly]="readonly()"
1122
1220
  [type]="type()"
1123
1221
  />
1124
1222
  <ng-content select="[issSuffix]"></ng-content>
@@ -1130,9 +1228,9 @@ class InputControl extends ControlBase {
1130
1228
  <ds-icon matSuffix icon="{{suffixIcon()}}"></ds-icon>
1131
1229
  }
1132
1230
  </div>
1133
- <iss-ctrl-hints [hints]="hints()" />
1231
+ <iss-ctrl-hints [hints]="hints()" [touched]="touched()"/>
1134
1232
  </mat-form-field>
1135
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1$2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i1$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i1$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: HintsComponent, selector: "iss-ctrl-hints", inputs: ["hints", "touched"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatInputModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1233
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i1$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i1$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: HintsComponent, selector: "iss-ctrl-hints", inputs: ["hints", "touched"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatInputModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1136
1234
  }
1137
1235
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: InputControl, decorators: [{
1138
1236
  type: Component,
@@ -1168,11 +1266,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1168
1266
  <input
1169
1267
  matInput
1170
1268
  (blur)="markAsTouched()"
1171
- (input)="emitOnChangeEvent()"
1269
+ (input)="emitValue()"
1172
1270
  [formControl]="ctrl"
1173
- [maxlength]="max()"
1174
- [minlength]="min()"
1175
- [placeholder]="placeholder()"
1271
+ [attr.maxlength]="maxLength()"
1272
+ [attr.minlength]="minLength()"
1273
+ [attr.max]="max()"
1274
+ [attr.min]="min()"
1275
+ [placeholder]="placeholder()"
1276
+ [readonly]="readonly()"
1176
1277
  [type]="type()"
1177
1278
  />
1178
1279
  <ng-content select="[issSuffix]"></ng-content>
@@ -1184,7 +1285,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1184
1285
  <ds-icon matSuffix icon="{{suffixIcon()}}"></ds-icon>
1185
1286
  }
1186
1287
  </div>
1187
- <iss-ctrl-hints [hints]="hints()" />
1288
+ <iss-ctrl-hints [hints]="hints()" [touched]="touched()"/>
1188
1289
  </mat-form-field>
1189
1290
  `
1190
1291
  }]
@@ -3333,7 +3434,6 @@ class MobileControl extends ControlBase {
3333
3434
  preferredCountries = input(["SA"], ...(ngDevMode ? [{ debugName: "preferredCountries" }] : /* istanbul ignore next */ []));
3334
3435
  searchInput = new FormControl("");
3335
3436
  filteredCountries = computed(() => this.#searchInCountries(), ...(ngDevMode ? [{ debugName: "filteredCountries" }] : /* istanbul ignore next */ []));
3336
- #ctrlChanges = toSignal(this.ctrl.valueChanges);
3337
3437
  #searchQuery = toSignal(this.searchInput.valueChanges);
3338
3438
  #disabledEffect = effect(() => {
3339
3439
  if (this.disabled()) {
@@ -3344,33 +3444,34 @@ class MobileControl extends ControlBase {
3344
3444
  }
3345
3445
  }, ...(ngDevMode ? [{ debugName: "#disabledEffect" }] : /* istanbul ignore next */ []));
3346
3446
  writeValue(value) {
3347
- if (value) {
3348
- const mobileObject = this.phoneService.getPhoneNumberObject(value);
3349
- if (mobileObject) {
3350
- this.selectedCountry.set(mobileObject);
3351
- this.emitOnChangeEvent(this.selectedCountry());
3352
- }
3353
- this.ctrl.setValue(mobileObject?.nationalNumber);
3447
+ if (!value) {
3448
+ this.selectedCountry.set(DEFAULT_COUNTRY);
3449
+ this.ctrl.setValue(null, { emitEvent: false });
3450
+ return;
3451
+ }
3452
+ const mobileObject = this.phoneService.getPhoneNumberObject(value);
3453
+ if (mobileObject) {
3454
+ this.selectedCountry.set(mobileObject);
3354
3455
  }
3456
+ this.ctrl.setValue(mobileObject?.nationalNumber ?? value, { emitEvent: false });
3355
3457
  }
3356
3458
  emit() {
3357
- if (this.#ctrlChanges()) {
3358
- const mobileInput = `+${this.selectedCountry().dailCode}${this.#ctrlChanges()}`;
3359
- const numberObject = this.phoneService.getPhoneNumberObject(mobileInput);
3360
- if (numberObject) {
3361
- this.selectedCountry.set(numberObject);
3362
- this.propagateValue(`+${numberObject.dailCode}${numberObject.nationalNumber}`);
3363
- this.emitOnChangeEvent(this.selectedCountry());
3364
- }
3365
- else {
3366
- this.propagateValue(mobileInput);
3367
- this.emitOnChangeEvent(this.selectedCountry());
3368
- }
3459
+ const nationalNumber = this.ctrl.getRawValue();
3460
+ if (!nationalNumber) {
3461
+ this.propagateValue(null);
3462
+ this.emitOnChangeEvent(this.selectedCountry());
3463
+ return;
3464
+ }
3465
+ const mobileInput = `+${this.selectedCountry().dailCode}${nationalNumber}`;
3466
+ const numberObject = this.phoneService.getPhoneNumberObject(mobileInput);
3467
+ if (numberObject) {
3468
+ this.selectedCountry.set(numberObject);
3469
+ this.propagateValue(`+${numberObject.dailCode}${numberObject.nationalNumber}`);
3369
3470
  }
3370
3471
  else {
3371
- this.propagateValue(null);
3372
- this.emitOnChangeEvent(null);
3472
+ this.propagateValue(mobileInput);
3373
3473
  }
3474
+ this.emitOnChangeEvent(this.selectedCountry());
3374
3475
  }
3375
3476
  validate(control) {
3376
3477
  if (!control.getRawValue()) {
@@ -3405,35 +3506,54 @@ class MobileControl extends ControlBase {
3405
3506
  }
3406
3507
  }
3407
3508
  onSelectCountry(country) {
3509
+ if (this.readonly()) {
3510
+ return;
3511
+ }
3408
3512
  this.selectedCountry.set(country);
3409
3513
  this.searchInput.setValue("");
3410
3514
  this.closeCountryCodeMenu();
3411
- this.propagateValue(`+${this.selectedCountry().dailCode}${this.#ctrlChanges()}`);
3412
- this.emitOnChangeEvent(`+${this.selectedCountry().dailCode}${this.#ctrlChanges()}`);
3515
+ const nationalNumber = this.ctrl.getRawValue();
3516
+ if (!nationalNumber) {
3517
+ this.propagateValue(null);
3518
+ this.emitOnChangeEvent(this.selectedCountry());
3519
+ return;
3520
+ }
3521
+ const mobileInput = `+${country.dailCode}${nationalNumber}`;
3522
+ this.propagateValue(mobileInput);
3523
+ this.emitOnChangeEvent(this.selectedCountry());
3413
3524
  }
3414
3525
  closeCountryCodeMenu() {
3415
3526
  this.isOpen.set(false);
3416
3527
  }
3417
3528
  toggleCountryCodeMenu() {
3529
+ if (this.readonly()) {
3530
+ return;
3531
+ }
3418
3532
  this.isOpen.update((isOpen) => !isOpen);
3419
3533
  }
3420
3534
  #sortCountries() {
3535
+ // copies, never the shared COUNTRIES entries: order is per instance and must not leak globally
3421
3536
  return COUNTRIES.map((country) => {
3422
- if (this.preferredCountries().includes(country.code)) {
3423
- country.order = country.priority + (-1 * (this.preferredCountries().indexOf(country.code) - this.preferredCountries().length)) * 100;
3537
+ const preferredIndex = this.preferredCountries().indexOf(country.code);
3538
+ if (preferredIndex === -1) {
3539
+ return { ...country };
3424
3540
  }
3425
- return country;
3541
+ return {
3542
+ ...country,
3543
+ order: country.priority + (-1 * (preferredIndex - this.preferredCountries().length)) * 100,
3544
+ };
3426
3545
  }).sort((a, b) => b.order - a.order);
3427
3546
  }
3428
3547
  #searchInCountries() {
3429
- if (this.#searchQuery()) {
3430
- return this.#sortCountries().filter((country) => {
3431
- return (country.code.toLowerCase().includes(this.#searchQuery())
3432
- || country.name.toLowerCase().includes(this.#searchQuery())
3433
- || country.dailCode.toLowerCase().includes(this.#searchQuery()));
3434
- });
3435
- }
3436
- return this.#sortCountries();
3548
+ const query = (this.#searchQuery() ?? "").toLowerCase();
3549
+ if (!query) {
3550
+ return this.#sortCountries();
3551
+ }
3552
+ return this.#sortCountries().filter((country) => {
3553
+ return (country.code.toLowerCase().includes(query)
3554
+ || country.name.toLowerCase().includes(query)
3555
+ || country.dailCode.toLowerCase().includes(query));
3556
+ });
3437
3557
  }
3438
3558
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MobileControl, deps: null, target: i0.ɵɵFactoryTarget.Component });
3439
3559
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MobileControl, isStandalone: true, selector: "iss-ctrl-mobile", inputs: { preferredCountries: { classPropertyName: "preferredCountries", publicName: "preferredCountries", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
@@ -3462,7 +3582,8 @@ class MobileControl extends ControlBase {
3462
3582
  (blur)="markAsTouched()"
3463
3583
  (keydown)="onInput($event)"
3464
3584
  (input)="emit()"
3465
- [formControl]="ctrl"
3585
+ [formControl]="ctrl"
3586
+ [readonly]="readonly()"
3466
3587
  [placeholder]="selectedCountry().placeholder"
3467
3588
  />
3468
3589
  </mat-form-field>
@@ -3477,7 +3598,7 @@ class MobileControl extends ControlBase {
3477
3598
  <div class="country-dropdown bc-white box-shadow p-2">
3478
3599
  <div class="search-container">
3479
3600
  <mat-form-field>
3480
- <input matInput id="country-search-box" [formControl]="searchInput" (click)="$event.stopPropagation()" />
3601
+ <input matInput id="country-search-box" [formControl]="searchInput" (click)="$event.stopPropagation()"/>
3481
3602
  </mat-form-field>
3482
3603
  </div>
3483
3604
  <ul class="country-list">
@@ -3538,7 +3659,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3538
3659
  (blur)="markAsTouched()"
3539
3660
  (keydown)="onInput($event)"
3540
3661
  (input)="emit()"
3541
- [formControl]="ctrl"
3662
+ [formControl]="ctrl"
3663
+ [readonly]="readonly()"
3542
3664
  [placeholder]="selectedCountry().placeholder"
3543
3665
  />
3544
3666
  </mat-form-field>
@@ -3553,7 +3675,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3553
3675
  <div class="country-dropdown bc-white box-shadow p-2">
3554
3676
  <div class="search-container">
3555
3677
  <mat-form-field>
3556
- <input matInput id="country-search-box" [formControl]="searchInput" (click)="$event.stopPropagation()" />
3678
+ <input matInput id="country-search-box" [formControl]="searchInput" (click)="$event.stopPropagation()"/>
3557
3679
  </mat-form-field>
3558
3680
  </div>
3559
3681
  <ul class="country-list">
@@ -3603,54 +3725,59 @@ class PlateControl extends ControlBase {
3603
3725
  lettersAr = signal(Array(3).fill(""), ...(ngDevMode ? [{ debugName: "lettersAr" }] : /* istanbul ignore next */ []));
3604
3726
  lettersEn = signal(Array(3).fill(""), ...(ngDevMode ? [{ debugName: "lettersEn" }] : /* istanbul ignore next */ []));
3605
3727
  letterErrors = signal(Array(3).fill(false), ...(ngDevMode ? [{ debugName: "letterErrors" }] : /* istanbul ignore next */ []));
3728
+ #host = inject(ElementRef);
3606
3729
  writeValue(value) {
3607
- const val = value;
3608
- if (val) {
3609
- const parts = val.split("-");
3610
- const letters = parts[0]?.split("") || [];
3611
- const numbers = parts[1]?.split("") || [];
3612
- // Initialize letters
3613
- const lAr = [];
3614
- const lEn = [];
3615
- for (let i = 0; i < 3; i++) {
3616
- const char = letters[i] || "";
3617
- if (char) {
3618
- const plateLetter = PLATE_LETTERS.find(l => l.value === char || l.label.en === char || l.label.ar === char);
3619
- if (plateLetter) {
3620
- lAr.push(plateLetter.label.ar);
3621
- lEn.push(plateLetter.label.en);
3622
- }
3623
- else {
3624
- lAr.push("");
3625
- lEn.push("");
3626
- }
3730
+ if (!value) {
3731
+ this.#resetPlate();
3732
+ return;
3733
+ }
3734
+ const parts = value.split("-");
3735
+ const letters = parts[0]?.split("") || [];
3736
+ const numbers = parts[1]?.split("") || [];
3737
+ // Initialize letters
3738
+ const lAr = [];
3739
+ const lEn = [];
3740
+ for (let i = 0; i < 3; i++) {
3741
+ const char = letters[i] || "";
3742
+ if (char) {
3743
+ const plateLetter = PLATE_LETTERS.find(l => l.value === char || l.label.en === char || l.label.ar === char);
3744
+ if (plateLetter) {
3745
+ lAr.push(plateLetter.label.ar);
3746
+ lEn.push(plateLetter.label.en);
3627
3747
  }
3628
3748
  else {
3629
3749
  lAr.push("");
3630
3750
  lEn.push("");
3631
3751
  }
3632
3752
  }
3633
- this.lettersAr.set(lAr);
3634
- this.lettersEn.set(lEn);
3635
- // Initialize numbers
3636
- const nAr = [];
3637
- const nEn = [];
3638
- for (let i = 0; i < 4; i++) {
3639
- const char = numbers[i] || "";
3640
- if (char) {
3641
- nAr.push(this.convertDigit(char, "ar"));
3642
- nEn.push(this.convertDigit(char, "en"));
3643
- }
3644
- else {
3645
- nAr.push("");
3646
- nEn.push("");
3647
- }
3753
+ else {
3754
+ lAr.push("");
3755
+ lEn.push("");
3756
+ }
3757
+ }
3758
+ this.lettersAr.set(lAr);
3759
+ this.lettersEn.set(lEn);
3760
+ // Initialize numbers
3761
+ const nAr = [];
3762
+ const nEn = [];
3763
+ for (let i = 0; i < 4; i++) {
3764
+ const char = numbers[i] || "";
3765
+ if (char) {
3766
+ nAr.push(this.convertDigit(char, "ar"));
3767
+ nEn.push(this.convertDigit(char, "en"));
3768
+ }
3769
+ else {
3770
+ nAr.push("");
3771
+ nEn.push("");
3648
3772
  }
3649
- this.numbersAr.set(nAr);
3650
- this.numbersEn.set(nEn);
3651
3773
  }
3774
+ this.numbersAr.set(nAr);
3775
+ this.numbersEn.set(nEn);
3652
3776
  }
3653
3777
  onNumberInput(index, event, lang) {
3778
+ if (this.readonly()) {
3779
+ return;
3780
+ }
3654
3781
  const input = event.target;
3655
3782
  let value = input.value;
3656
3783
  // Allow only numbers
@@ -3670,6 +3797,7 @@ class PlateControl extends ControlBase {
3670
3797
  newEn[index] = "";
3671
3798
  this.numbersAr.set(newAr);
3672
3799
  this.numbersEn.set(newEn);
3800
+ this.emitPlate();
3673
3801
  return;
3674
3802
  }
3675
3803
  const arDigits = ["٠", "١", "٢", "٣", "٤", "٥", "٦", "٧", "٨", "٩"];
@@ -3698,8 +3826,12 @@ class PlateControl extends ControlBase {
3698
3826
  }
3699
3827
  this.numbersAr.set(newAr);
3700
3828
  this.numbersEn.set(newEn);
3829
+ this.emitPlate();
3701
3830
  }
3702
3831
  onLetterInput(index, event, lang) {
3832
+ if (this.readonly()) {
3833
+ return;
3834
+ }
3703
3835
  const input = event.target;
3704
3836
  let value = input.value;
3705
3837
  if (!value) {
@@ -3712,6 +3844,7 @@ class PlateControl extends ControlBase {
3712
3844
  this.lettersAr.set(newAr);
3713
3845
  this.lettersEn.set(newEn);
3714
3846
  this.letterErrors.set(newErrors);
3847
+ this.emitPlate();
3715
3848
  return;
3716
3849
  }
3717
3850
  // Take only the last character
@@ -3752,15 +3885,40 @@ class PlateControl extends ControlBase {
3752
3885
  this.lettersAr.set(newAr);
3753
3886
  this.lettersEn.set(newEn);
3754
3887
  this.letterErrors.set(newErrors);
3888
+ this.emitPlate();
3755
3889
  }
3756
3890
  preventNonDigits($event, ar) {
3757
3891
  if ($event.key.length === 1 && !/^[0-9\u0660-\u0669]$/.test($event.key)) {
3758
3892
  $event.preventDefault();
3759
3893
  }
3760
3894
  }
3895
+ #resetPlate() {
3896
+ this.numbersAr.set(Array(4).fill(""));
3897
+ this.numbersEn.set(Array(4).fill(""));
3898
+ this.lettersAr.set(Array(3).fill(""));
3899
+ this.lettersEn.set(Array(3).fill(""));
3900
+ this.letterErrors.set(Array(3).fill(false));
3901
+ this.ctrl.setValue(null, { emitEvent: false });
3902
+ }
3903
+ emitPlate() {
3904
+ this.markAsTouched();
3905
+ this.emitValue(this.stringifyPlate());
3906
+ }
3907
+ stringifyPlate() {
3908
+ // lettersEn holds PlateLetter.label.en, which equals PlateLetter.value for every entry,
3909
+ // so this round-trips through writeValue and the viewer. Invalid letters are held in the
3910
+ // opposite-language row and serialize as empty.
3911
+ const letters = this.lettersEn().join("");
3912
+ const numbers = this.numbersEn().join("");
3913
+ if (!letters && !numbers) {
3914
+ return null;
3915
+ }
3916
+ return `${letters}-${numbers}`;
3917
+ }
3761
3918
  focusInput(id) {
3762
3919
  setTimeout(() => {
3763
- const element = document.getElementById(id);
3920
+ // scoped to this host: several plates can be on the same page
3921
+ const element = this.#host.nativeElement.querySelector("[data-plate-input=\"" + id + "\"]");
3764
3922
  if (element) {
3765
3923
  element.focus();
3766
3924
  // Optional: Select text so user can overwrite easily if they cycle back
@@ -3792,8 +3950,9 @@ class PlateControl extends ControlBase {
3792
3950
  type="text"
3793
3951
  class="plate-input number-input"
3794
3952
  maxlength="1"
3795
- [id]="'num-ar-' + $index"
3796
- [value]="num"
3953
+ [attr.data-plate-input]="'num-ar-' + $index"
3954
+ [value]="num"
3955
+ [readonly]="readonly()"
3797
3956
  placeholder="١"
3798
3957
  (keydown)="preventNonDigits($event, 'ar')"
3799
3958
  (keyup)="preventNonDigits($event, 'ar')"
@@ -3808,8 +3967,9 @@ class PlateControl extends ControlBase {
3808
3967
  type="text"
3809
3968
  class="plate-input number-input"
3810
3969
  maxlength="1"
3811
- [id]="'num-en-' + $index"
3812
- [value]="num"
3970
+ [attr.data-plate-input]="'num-en-' + $index"
3971
+ [value]="num"
3972
+ [readonly]="readonly()"
3813
3973
  placeholder="1"
3814
3974
  (keydown)="preventNonDigits($event, 'en')"
3815
3975
  (keyup)="preventNonDigits($event, 'en')"
@@ -3838,9 +3998,10 @@ class PlateControl extends ControlBase {
3838
3998
  type="text"
3839
3999
  class="plate-input letter-input"
3840
4000
  maxlength="1"
3841
- [id]="'let-ar-' + $index"
4001
+ [attr.data-plate-input]="'let-ar-' + $index"
3842
4002
  [class.invalid]="letterErrors()[$index]"
3843
- [value]="letter"
4003
+ [value]="letter"
4004
+ [readonly]="readonly()"
3844
4005
  placeholder="س"
3845
4006
  (input)="onLetterInput($index, $event, 'ar')"
3846
4007
  />
@@ -3858,9 +4019,10 @@ class PlateControl extends ControlBase {
3858
4019
  type="text"
3859
4020
  class="plate-input letter-input"
3860
4021
  maxlength="1"
3861
- [id]="'let-en-' + $index"
4022
+ [attr.data-plate-input]="'let-en-' + $index"
3862
4023
  [class.invalid]="letterErrors()[$index]"
3863
- [value]="letter"
4024
+ [value]="letter"
4025
+ [readonly]="readonly()"
3864
4026
  placeholder="S"
3865
4027
  (input)="onLetterInput($index, $event, 'en')"
3866
4028
  />
@@ -3898,8 +4060,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3898
4060
  type="text"
3899
4061
  class="plate-input number-input"
3900
4062
  maxlength="1"
3901
- [id]="'num-ar-' + $index"
3902
- [value]="num"
4063
+ [attr.data-plate-input]="'num-ar-' + $index"
4064
+ [value]="num"
4065
+ [readonly]="readonly()"
3903
4066
  placeholder="١"
3904
4067
  (keydown)="preventNonDigits($event, 'ar')"
3905
4068
  (keyup)="preventNonDigits($event, 'ar')"
@@ -3914,8 +4077,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3914
4077
  type="text"
3915
4078
  class="plate-input number-input"
3916
4079
  maxlength="1"
3917
- [id]="'num-en-' + $index"
3918
- [value]="num"
4080
+ [attr.data-plate-input]="'num-en-' + $index"
4081
+ [value]="num"
4082
+ [readonly]="readonly()"
3919
4083
  placeholder="1"
3920
4084
  (keydown)="preventNonDigits($event, 'en')"
3921
4085
  (keyup)="preventNonDigits($event, 'en')"
@@ -3944,9 +4108,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3944
4108
  type="text"
3945
4109
  class="plate-input letter-input"
3946
4110
  maxlength="1"
3947
- [id]="'let-ar-' + $index"
4111
+ [attr.data-plate-input]="'let-ar-' + $index"
3948
4112
  [class.invalid]="letterErrors()[$index]"
3949
- [value]="letter"
4113
+ [value]="letter"
4114
+ [readonly]="readonly()"
3950
4115
  placeholder="س"
3951
4116
  (input)="onLetterInput($index, $event, 'ar')"
3952
4117
  />
@@ -3964,9 +4129,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3964
4129
  type="text"
3965
4130
  class="plate-input letter-input"
3966
4131
  maxlength="1"
3967
- [id]="'let-en-' + $index"
4132
+ [attr.data-plate-input]="'let-en-' + $index"
3968
4133
  [class.invalid]="letterErrors()[$index]"
3969
- [value]="letter"
4134
+ [value]="letter"
4135
+ [readonly]="readonly()"
3970
4136
  placeholder="S"
3971
4137
  (input)="onLetterInput($index, $event, 'en')"
3972
4138
  />
@@ -3993,24 +4159,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3993
4159
  class RadioControl extends ControlBase {
3994
4160
  options = input.required(...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
3995
4161
  writeValue(value) {
3996
- if (value === null || value === undefined) {
4162
+ if (!value) {
4163
+ this.ctrl.setValue(null, { emitEvent: false });
3997
4164
  return;
3998
4165
  }
4166
+ // single selection mode, only the first value of a comma separated list is honoured
3999
4167
  const values = value.split(",").map((val) => val.trim());
4000
- if (values.length > 1) {
4001
- this.emitValue(null);
4002
- throw new Error("You cannot set multiple values while the control is in single selection mode!");
4003
- }
4004
- this.ctrl.setValue(value);
4168
+ this.ctrl.setValue(values[0], { emitEvent: false });
4005
4169
  }
4006
4170
  onRadioChange(value) {
4007
- this.emitValue(value);
4171
+ if (this.readonly()) {
4172
+ return;
4173
+ }
4174
+ this.markAsTouched();
4175
+ this.ctrl.setValue(value, { emitEvent: false });
4176
+ const selectedOption = this.options().find((option) => option.value === value);
4177
+ // the form keeps the raw value, while (onChange) consumers get the resolved option
4178
+ this.propagateValue(value);
4179
+ this.emitOnChangeEvent(selectedOption ? [selectedOption] : null);
4008
4180
  }
4009
4181
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RadioControl, deps: null, target: i0.ɵɵFactoryTarget.Component });
4010
4182
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: RadioControl, isStandalone: true, selector: "iss-ctrl-radio", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null } }, providers: [
4011
4183
  { provide: NG_VALUE_ACCESSOR, useExisting: RadioControl, multi: true }
4012
4184
  ], usesInheritance: true, ngImport: i0, template: `
4013
- <mat-button-toggle-group [hideSingleSelectionIndicator]="true">
4185
+ <mat-button-toggle-group [hideSingleSelectionIndicator]="true" [disabled]="disabled() || readonly()">
4014
4186
  @for (option of options(); track option.value) {
4015
4187
  <mat-button-toggle
4016
4188
  [value]="option.value"
@@ -4035,7 +4207,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4035
4207
  { provide: NG_VALUE_ACCESSOR, useExisting: RadioControl, multi: true }
4036
4208
  ],
4037
4209
  template: `
4038
- <mat-button-toggle-group [hideSingleSelectionIndicator]="true">
4210
+ <mat-button-toggle-group [hideSingleSelectionIndicator]="true" [disabled]="disabled() || readonly()">
4039
4211
  @for (option of options(); track option.value) {
4040
4212
  <mat-button-toggle
4041
4213
  [value]="option.value"
@@ -4057,12 +4229,6 @@ class SelectControl extends ControlBase {
4057
4229
  filteredOptions = computed(() => this.#filter(this.query()), ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
4058
4230
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
4059
4231
  onQueryChange = output();
4060
- displayFn = (options) => {
4061
- if (Array.isArray(options) && !this.multiple()) {
4062
- return options[0]?.label;
4063
- }
4064
- return "";
4065
- };
4066
4232
  optionTemplate = contentChild("optionTemplate", ...(ngDevMode ? [{ debugName: "optionTemplate" }] : /* istanbul ignore next */ []));
4067
4233
  cardViewerTemplate = contentChild("cardViewerTemplate", ...(ngDevMode ? [{ debugName: "cardViewerTemplate" }] : /* istanbul ignore next */ []));
4068
4234
  allSelected = signal(false, ...(ngDevMode ? [{ debugName: "allSelected" }] : /* istanbul ignore next */ []));
@@ -4079,6 +4245,12 @@ class SelectControl extends ControlBase {
4079
4245
  this.ctrl.enable();
4080
4246
  }
4081
4247
  }, ...(ngDevMode ? [{ debugName: "#disabledEffectRef" }] : /* istanbul ignore next */ []));
4248
+ displayFn = (options) => {
4249
+ if (Array.isArray(options) && !this.multiple()) {
4250
+ return options[0]?.label;
4251
+ }
4252
+ return "";
4253
+ };
4082
4254
  /*todo: add pagination for async options*/
4083
4255
  writeValue(value) {
4084
4256
  this.#reset();
@@ -4088,19 +4260,21 @@ class SelectControl extends ControlBase {
4088
4260
  const values = value.split(",").map(val => val.trim());
4089
4261
  let selectedOptions = this.options().filter((option) => values.includes(option.value));
4090
4262
  if (!this.multiple()) {
4091
- if (selectedOptions.length > 1) {
4092
- this.#emit();
4093
- throw new Error("You cannot set multiple values while the control is in single selection mode!");
4094
- }
4095
- selectedOptions = selectedOptions.splice(-selectedOptions.length, selectedOptions.length - selectedOptions.length + 1);
4263
+ // single selection mode, only the first matching option is honoured
4264
+ selectedOptions = selectedOptions.slice(0, 1);
4096
4265
  }
4266
+ const next = new Map();
4097
4267
  for (let i = 0; i < selectedOptions.length; i++) {
4098
4268
  const val = selectedOptions[i];
4099
- this.#addToSelectedOptionsMap(val);
4269
+ next.set(val.value, val);
4100
4270
  }
4271
+ this.selectedOptions.set(next);
4101
4272
  this.ctrl.setValue(selectedOptions);
4102
4273
  }
4103
4274
  captureValue($event) {
4275
+ if (this.readonly()) {
4276
+ return;
4277
+ }
4104
4278
  if ($event?.option.value === "_SELECT_ALL_OPTIONS_") {
4105
4279
  return;
4106
4280
  }
@@ -4108,26 +4282,42 @@ class SelectControl extends ControlBase {
4108
4282
  this.#emit();
4109
4283
  }
4110
4284
  removeFromSelectedOptionsMap(option) {
4111
- this.selectedOptions().delete(option.value);
4112
- this.filteredOptions = computed(() => this.#filter(this.query()), ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
4285
+ if (this.readonly()) {
4286
+ return;
4287
+ }
4288
+ this.selectedOptions.update((selected) => {
4289
+ const next = new Map(selected);
4290
+ next.delete(option.value);
4291
+ return next;
4292
+ });
4113
4293
  this.allSelected.set(false);
4114
4294
  this.#emit();
4115
4295
  }
4116
4296
  clearSelection() {
4297
+ if (this.readonly()) {
4298
+ return;
4299
+ }
4117
4300
  this.#reset();
4118
4301
  this.allSelected.set(false);
4119
4302
  this.#emit();
4120
4303
  }
4121
4304
  toggleAllSelection() {
4305
+ if (this.readonly()) {
4306
+ return;
4307
+ }
4122
4308
  if (this.selectedOptions().size === this.options().length) {
4123
4309
  this.#reset();
4124
4310
  this.#emit();
4125
4311
  this.allSelected.set(false);
4126
4312
  return;
4127
4313
  }
4314
+ const next = new Map();
4128
4315
  for (let i = 0; i < this.options().length; i++) {
4129
- this.#addToSelectedOptionsMap(this.options()[i]);
4316
+ const option = this.options()[i];
4317
+ next.set(option.value, option);
4130
4318
  }
4319
+ this.selectedOptions.set(next);
4320
+ this.ctrl.setValue(this.#convertSelectedMapToArray());
4131
4321
  this.allSelected.set(true);
4132
4322
  this.#emit();
4133
4323
  }
@@ -4138,10 +4328,12 @@ class SelectControl extends ControlBase {
4138
4328
  #addToSelectedOptionsMap(option) {
4139
4329
  if (option) {
4140
4330
  const selectedOption = option;
4141
- if (!this.multiple()) {
4142
- this.#reset();
4143
- }
4144
- this.selectedOptions().set(selectedOption.value, selectedOption);
4331
+ this.selectedOptions.update((selected) => {
4332
+ // single selection replaces the map instead of adding to it
4333
+ const next = this.multiple() ? new Map(selected) : new Map();
4334
+ next.set(selectedOption.value, selectedOption);
4335
+ return next;
4336
+ });
4145
4337
  this.ctrl.setValue(this.#convertSelectedMapToArray());
4146
4338
  }
4147
4339
  }
@@ -4157,18 +4349,16 @@ class SelectControl extends ControlBase {
4157
4349
  return this.options();
4158
4350
  }
4159
4351
  const query = queryString.toLowerCase();
4160
- const filteredOptions = this.options().filter((option) => {
4352
+ return this.options().filter((option) => {
4161
4353
  return (option.value.toLowerCase().includes(query) || option.label.toLowerCase().includes(query));
4162
4354
  });
4163
- return JSON.parse(JSON.stringify(filteredOptions));
4164
4355
  }
4165
4356
  #emit() {
4166
4357
  this.propagateValue(this.#stringifySelectedOptions());
4167
4358
  this.emitOnChangeEvent(this.#convertSelectedMapToArray());
4168
4359
  }
4169
4360
  #reset() {
4170
- this.selectedOptions().clear();
4171
- this.filteredOptions = computed(() => this.#filter(this.query()), ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
4361
+ this.selectedOptions.set(new Map());
4172
4362
  this.ctrl.setValue(null);
4173
4363
  }
4174
4364
  #convertSelectedMapToArray() {
@@ -4183,7 +4373,7 @@ class SelectControl extends ControlBase {
4183
4373
  <mat-label class="fs-16 fc-black">
4184
4374
  <ng-content select="label"></ng-content>
4185
4375
  </mat-label>
4186
- <input [formControl]="ctrl" [matAutocomplete]="auto" [matChipInputFor]="chipGrid" [placeholder]="placeholder()" class="m-0" matInput />
4376
+ <input [formControl]="ctrl" [matAutocomplete]="auto" [matChipInputFor]="chipGrid" [placeholder]="placeholder()" [readonly]="readonly()" (blur)="markAsTouched()" class="m-0" matInput/>
4187
4377
  @if (!disabled()) {
4188
4378
  @if (selectedOptions().size) {
4189
4379
  <span matSuffix class="sfi sfi-close fs-18 fc-black cursor-pointer" (click)="clearSelection()"></span>
@@ -4207,7 +4397,7 @@ class SelectControl extends ControlBase {
4207
4397
  <cdk-virtual-scroll-viewport itemSize="5" [ngStyle]="{'height': height()}">
4208
4398
  <mat-option *cdkVirtualFor="let option of filteredOptions()" [value]="option" [ngClass]="{'mdc-list-item__selected': option | selectStatus: selectedOptions()}">
4209
4399
  @if (optionTemplate()) {
4210
- <ng-container *ngTemplateOutlet="optionTemplate(); context: { $implicit: option}" />
4400
+ <ng-container *ngTemplateOutlet="optionTemplate(); context: { $implicit: option}"/>
4211
4401
  } @else {
4212
4402
  {{ option.label }}
4213
4403
  }
@@ -4231,7 +4421,7 @@ class SelectControl extends ControlBase {
4231
4421
  } @else {
4232
4422
  {{ chip[1].label }}
4233
4423
  }
4234
- <button matChipRemove [attr.aria-label]="'remove ' + chip[1].label">
4424
+ <button matChipRemove [disabled]="readonly()" [attr.aria-label]="'remove ' + chip[1].label">
4235
4425
  <ds-icon icon="trash" class="fc-red"></ds-icon>
4236
4426
  </button>
4237
4427
  </mat-chip-row>
@@ -4262,7 +4452,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4262
4452
  <mat-label class="fs-16 fc-black">
4263
4453
  <ng-content select="label"></ng-content>
4264
4454
  </mat-label>
4265
- <input [formControl]="ctrl" [matAutocomplete]="auto" [matChipInputFor]="chipGrid" [placeholder]="placeholder()" class="m-0" matInput />
4455
+ <input [formControl]="ctrl" [matAutocomplete]="auto" [matChipInputFor]="chipGrid" [placeholder]="placeholder()" [readonly]="readonly()" (blur)="markAsTouched()" class="m-0" matInput/>
4266
4456
  @if (!disabled()) {
4267
4457
  @if (selectedOptions().size) {
4268
4458
  <span matSuffix class="sfi sfi-close fs-18 fc-black cursor-pointer" (click)="clearSelection()"></span>
@@ -4286,7 +4476,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4286
4476
  <cdk-virtual-scroll-viewport itemSize="5" [ngStyle]="{'height': height()}">
4287
4477
  <mat-option *cdkVirtualFor="let option of filteredOptions()" [value]="option" [ngClass]="{'mdc-list-item__selected': option | selectStatus: selectedOptions()}">
4288
4478
  @if (optionTemplate()) {
4289
- <ng-container *ngTemplateOutlet="optionTemplate(); context: { $implicit: option}" />
4479
+ <ng-container *ngTemplateOutlet="optionTemplate(); context: { $implicit: option}"/>
4290
4480
  } @else {
4291
4481
  {{ option.label }}
4292
4482
  }
@@ -4310,7 +4500,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4310
4500
  } @else {
4311
4501
  {{ chip[1].label }}
4312
4502
  }
4313
- <button matChipRemove [attr.aria-label]="'remove ' + chip[1].label">
4503
+ <button matChipRemove [disabled]="readonly()" [attr.aria-label]="'remove ' + chip[1].label">
4314
4504
  <ds-icon icon="trash" class="fc-red"></ds-icon>
4315
4505
  </button>
4316
4506
  </mat-chip-row>
@@ -4322,6 +4512,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4322
4512
 
4323
4513
  class SlideToggleControl extends ControlBase {
4324
4514
  onSlideToggleChange(checked) {
4515
+ if (this.readonly()) {
4516
+ return;
4517
+ }
4518
+ this.markAsTouched();
4325
4519
  this.emitValue(checked);
4326
4520
  }
4327
4521
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SlideToggleControl, deps: null, target: i0.ɵɵFactoryTarget.Component });
@@ -4329,7 +4523,8 @@ class SlideToggleControl extends ControlBase {
4329
4523
  { provide: NG_VALUE_ACCESSOR, useExisting: SlideToggleControl, multi: true }
4330
4524
  ], usesInheritance: true, ngImport: i0, template: `
4331
4525
  <mat-slide-toggle
4332
- class="d-block" [formControl]="ctrl"
4526
+ class="d-block" [formControl]="ctrl"
4527
+ [disabled]="readonly()"
4333
4528
  (change)="onSlideToggleChange($event.checked)"
4334
4529
  >
4335
4530
  </mat-slide-toggle>
@@ -4345,7 +4540,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4345
4540
  ],
4346
4541
  template: `
4347
4542
  <mat-slide-toggle
4348
- class="d-block" [formControl]="ctrl"
4543
+ class="d-block" [formControl]="ctrl"
4544
+ [disabled]="readonly()"
4349
4545
  (change)="onSlideToggleChange($event.checked)"
4350
4546
  >
4351
4547
  </mat-slide-toggle>
@@ -4378,15 +4574,16 @@ class TextareaControl extends ControlBase {
4378
4574
  matInput
4379
4575
  [placeholder]="placeholder()"
4380
4576
  [formControl]="ctrl"
4381
- [maxlength]="max()"
4382
- [minlength]="min()"
4577
+ [attr.maxlength]="maxLength()"
4578
+ [attr.minlength]="minLength()"
4579
+ [readonly]="readonly()"
4383
4580
  (input)="emitValue()"
4384
4581
  (focusout)="markAsTouched()"
4385
4582
  >
4386
4583
  </textarea>
4387
- <iss-ctrl-hints [hints]="hints()" />
4584
+ <iss-ctrl-hints [hints]="hints()" [touched]="touched()"/>
4388
4585
  </mat-form-field>
4389
- `, isInline: true, dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: HintsComponent, selector: "iss-ctrl-hints", inputs: ["hints", "touched"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1$2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "directive", type: i1$1.MatLabel, selector: "mat-label" }] });
4586
+ `, isInline: true, dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: HintsComponent, selector: "iss-ctrl-hints", inputs: ["hints", "touched"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "directive", type: i1$1.MatLabel, selector: "mat-label" }] });
4390
4587
  }
4391
4588
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TextareaControl, decorators: [{
4392
4589
  type: Component,
@@ -4414,13 +4611,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4414
4611
  matInput
4415
4612
  [placeholder]="placeholder()"
4416
4613
  [formControl]="ctrl"
4417
- [maxlength]="max()"
4418
- [minlength]="min()"
4614
+ [attr.maxlength]="maxLength()"
4615
+ [attr.minlength]="minLength()"
4616
+ [readonly]="readonly()"
4419
4617
  (input)="emitValue()"
4420
4618
  (focusout)="markAsTouched()"
4421
4619
  >
4422
4620
  </textarea>
4423
- <iss-ctrl-hints [hints]="hints()" />
4621
+ <iss-ctrl-hints [hints]="hints()" [touched]="touched()"/>
4424
4622
  </mat-form-field>
4425
4623
  `
4426
4624
  }]
@@ -4431,46 +4629,48 @@ class ToggleButtonControl extends ControlBase {
4431
4629
  selectedOptions = signal(new Map(), ...(ngDevMode ? [{ debugName: "selectedOptions" }] : /* istanbul ignore next */ []));
4432
4630
  writeValue(value) {
4433
4631
  this.#reset();
4434
- if (value === null || value === undefined) {
4632
+ if (!value) {
4435
4633
  return;
4436
4634
  }
4437
4635
  const values = value.split(",").map((val) => val.trim());
4438
4636
  const selectedOptions = this.options().filter((option) => values.includes(option.value));
4439
4637
  if (!this.multiple()) {
4440
- if (selectedOptions.length > 1) {
4441
- this.emitValue(null);
4442
- throw new Error("You cannot set multiple values while the control is in single selection mode!");
4443
- }
4444
- this.ctrl.setValue(values[0]);
4445
- this.addToSelectedOptionsMap(this.#selectedOptionsMapFactory([selectedOptions[0]]));
4638
+ // single selection mode, only the first matching option is honoured
4639
+ const selectedOption = selectedOptions[0];
4640
+ this.ctrl.setValue(selectedOption ? selectedOption.value : null, { emitEvent: false });
4641
+ this.addToSelectedOptionsMap(this.#selectedOptionsMapFactory(selectedOption ? [selectedOption] : []));
4446
4642
  return;
4447
4643
  }
4448
4644
  this.addToSelectedOptionsMap(this.#selectedOptionsMapFactory(selectedOptions));
4449
- this.ctrl.setValue(values);
4645
+ this.ctrl.setValue(selectedOptions.map((option) => option.value), { emitEvent: false });
4450
4646
  }
4451
4647
  addToSelectedOptionsMap(option) {
4452
4648
  if (option) {
4453
- this.selectedOptions = signal(option, ...(ngDevMode ? [{ debugName: "selectedOptions" }] : /* istanbul ignore next */ []));
4649
+ this.selectedOptions.set(option);
4454
4650
  }
4455
4651
  }
4456
4652
  onToggleButtonChange(checked, value) {
4653
+ if (this.readonly()) {
4654
+ return;
4655
+ }
4656
+ this.markAsTouched();
4457
4657
  if (!this.multiple()) {
4458
- this.emitValue(value);
4658
+ const selectedOption = this.options().find((o) => o.value === value);
4659
+ this.addToSelectedOptionsMap(this.#selectedOptionsMapFactory(selectedOption ? [selectedOption] : []));
4660
+ this.propagateValue(value);
4661
+ this.emitOnChangeEvent(selectedOption ? [selectedOption] : null);
4459
4662
  }
4460
4663
  else {
4461
4664
  const option = this.options().find((o) => o.value === value);
4462
4665
  if (!option) {
4463
4666
  return;
4464
4667
  }
4465
- this.selectedOptions.update((prev) => {
4466
- const options = prev;
4467
- if (checked)
4468
- options.set(option.value, option);
4469
- else
4470
- options.delete(option.value);
4471
- return options;
4472
- });
4473
- this.getEmissionValue(this.selectedOptions());
4668
+ const options = new Map(this.selectedOptions());
4669
+ if (checked)
4670
+ options.set(option.value, option);
4671
+ else
4672
+ options.delete(option.value);
4673
+ this.getEmissionValue(options);
4474
4674
  }
4475
4675
  }
4476
4676
  stringifySelectedOptions() {
@@ -4481,7 +4681,13 @@ class ToggleButtonControl extends ControlBase {
4481
4681
  }
4482
4682
  getEmissionValue(options) {
4483
4683
  this.addToSelectedOptionsMap(options);
4484
- this.emitValue(this.stringifySelectedOptions());
4684
+ // the form keeps the stringified values, while (onChange) consumers get the resolved options
4685
+ this.propagateValue(this.stringifySelectedOptions());
4686
+ this.emitOnChangeEvent(this.#convertSelectedMapToArray());
4687
+ }
4688
+ #convertSelectedMapToArray() {
4689
+ const selectedOptions = Array.from(this.selectedOptions().values());
4690
+ return selectedOptions.length ? selectedOptions : null;
4485
4691
  }
4486
4692
  #selectedOptionsMapFactory(selectedOptions) {
4487
4693
  const selectedOptionsMap = new Map();
@@ -4491,15 +4697,16 @@ class ToggleButtonControl extends ControlBase {
4491
4697
  return selectedOptionsMap;
4492
4698
  }
4493
4699
  #reset() {
4494
- this.selectedOptions().clear();
4495
- this.ctrl.setValue(null);
4700
+ this.selectedOptions.set(new Map());
4701
+ this.ctrl.setValue(null, { emitEvent: false });
4496
4702
  }
4497
4703
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ToggleButtonControl, deps: null, target: i0.ɵɵFactoryTarget.Component });
4498
4704
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: ToggleButtonControl, isStandalone: true, selector: "iss-ctrl-toggle-button", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null } }, providers: [
4499
4705
  { provide: NG_VALUE_ACCESSOR, useExisting: ToggleButtonControl, multi: true }
4500
4706
  ], usesInheritance: true, ngImport: i0, template: `
4501
4707
  <mat-button-toggle-group
4502
- [formControl]="ctrl"
4708
+ [formControl]="ctrl"
4709
+ [disabled]="readonly()"
4503
4710
  [multiple]=multiple()
4504
4711
  [hideMultipleSelectionIndicator]="true"
4505
4712
  [hideSingleSelectionIndicator]="true"
@@ -4516,7 +4723,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4516
4723
  type: Component,
4517
4724
  args: [{ selector: "iss-ctrl-toggle-button", imports: [MatButtonToggleModule, ReactiveFormsModule], template: `
4518
4725
  <mat-button-toggle-group
4519
- [formControl]="ctrl"
4726
+ [formControl]="ctrl"
4727
+ [disabled]="readonly()"
4520
4728
  [multiple]=multiple()
4521
4729
  [hideMultipleSelectionIndicator]="true"
4522
4730
  [hideSingleSelectionIndicator]="true"
@@ -4541,28 +4749,28 @@ class FieldAsyncValidation {
4541
4749
 
4542
4750
  class FieldConfig {
4543
4751
  type = signal(undefined, ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
4544
- suffix = signal(undefined, ...(ngDevMode ? [{ debugName: "suffix" }] : /* istanbul ignore next */ []));
4752
+ disabled = signal(undefined, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4753
+ readonly = signal(undefined, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
4545
4754
  placeholder = signal(undefined, ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
4755
+ suffix = signal(undefined, ...(ngDevMode ? [{ debugName: "suffix" }] : /* istanbul ignore next */ []));
4756
+ hideLabel = signal(undefined, ...(ngDevMode ? [{ debugName: "hideLabel" }] : /* istanbul ignore next */ []));
4546
4757
  multiple = signal(undefined, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
4758
+ minLength = signal(undefined, ...(ngDevMode ? [{ debugName: "minLength" }] : /* istanbul ignore next */ []));
4759
+ maxLength = signal(undefined, ...(ngDevMode ? [{ debugName: "maxLength" }] : /* istanbul ignore next */ []));
4547
4760
  min = signal(undefined, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
4548
4761
  max = signal(undefined, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
4549
4762
  minDate = signal(undefined, ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
4550
4763
  maxDate = signal(undefined, ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
4551
- minLength = signal(undefined, ...(ngDevMode ? [{ debugName: "minLength" }] : /* istanbul ignore next */ []));
4552
- maxLength = signal(undefined, ...(ngDevMode ? [{ debugName: "maxLength" }] : /* istanbul ignore next */ []));
4553
- allowedFileSize = signal(undefined, ...(ngDevMode ? [{ debugName: "allowedFileSize" }] : /* istanbul ignore next */ []));
4554
- allowedFileTypes = signal(undefined, ...(ngDevMode ? [{ debugName: "allowedFileTypes" }] : /* istanbul ignore next */ []));
4555
- readonly = signal(undefined, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
4556
- disabled = signal(undefined, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4557
4764
  dateFilter = signal(undefined, ...(ngDevMode ? [{ debugName: "dateFilter" }] : /* istanbul ignore next */ []));
4558
4765
  templateId = signal(undefined, ...(ngDevMode ? [{ debugName: "templateId" }] : /* istanbul ignore next */ []));
4559
4766
  templateUrl = signal(undefined, ...(ngDevMode ? [{ debugName: "templateUrl" }] : /* istanbul ignore next */ []));
4767
+ allowedFileSize = signal(undefined, ...(ngDevMode ? [{ debugName: "allowedFileSize" }] : /* istanbul ignore next */ []));
4768
+ allowedFileTypes = signal(undefined, ...(ngDevMode ? [{ debugName: "allowedFileTypes" }] : /* istanbul ignore next */ []));
4560
4769
  maxFilesCount = signal(undefined, ...(ngDevMode ? [{ debugName: "maxFilesCount" }] : /* istanbul ignore next */ []));
4561
4770
  minFilesCount = signal(undefined, ...(ngDevMode ? [{ debugName: "minFilesCount" }] : /* istanbul ignore next */ []));
4562
4771
  maxUsersCount = signal(undefined, ...(ngDevMode ? [{ debugName: "maxUsersCount" }] : /* istanbul ignore next */ []));
4563
4772
  minUsersCount = signal(undefined, ...(ngDevMode ? [{ debugName: "minUsersCount" }] : /* istanbul ignore next */ []));
4564
4773
  showInTableColumns = signal(undefined, ...(ngDevMode ? [{ debugName: "showInTableColumns" }] : /* istanbul ignore next */ []));
4565
- hideLabel = signal(undefined, ...(ngDevMode ? [{ debugName: "hideLabel" }] : /* istanbul ignore next */ []));
4566
4774
  tableWithFilter = signal(undefined, ...(ngDevMode ? [{ debugName: "tableWithFilter" }] : /* istanbul ignore next */ []));
4567
4775
  tableWithSort = signal(undefined, ...(ngDevMode ? [{ debugName: "tableWithSort" }] : /* istanbul ignore next */ []));
4568
4776
  tableWithPagination = signal(undefined, ...(ngDevMode ? [{ debugName: "tableWithPagination" }] : /* istanbul ignore next */ []));
@@ -4573,11 +4781,13 @@ class FieldConfig {
4573
4781
  this.readonly.set(config?.readonly);
4574
4782
  this.disabled.set(config?.disabled);
4575
4783
  this.multiple.set(config?.multiple);
4784
+ this.hideLabel.set(config?.hideLabel);
4576
4785
  this.min.set(config?.min);
4577
4786
  this.max.set(config?.max);
4578
4787
  this.minDate.set(config?.minDate);
4579
4788
  this.maxDate.set(config?.maxDate);
4580
4789
  this.dateFilter.set(config?.acceptWorkDaysOnly);
4790
+ this.minLength.set(config?.minLength);
4581
4791
  this.maxLength.set(config?.maxLength);
4582
4792
  this.allowedFileSize.set(config?.allowedSize);
4583
4793
  this.allowedFileTypes.set(config?.allowedFileTypes);
@@ -4588,7 +4798,6 @@ class FieldConfig {
4588
4798
  this.maxUsersCount.set(config?.maxUsersCount);
4589
4799
  this.minUsersCount.set(config?.minUsersCount);
4590
4800
  this.showInTableColumns.set(config?.showInTableColumns);
4591
- this.hideLabel.set(config?.hideLabel);
4592
4801
  this.tableWithFilter.set(config?.tableWithFilter);
4593
4802
  this.tableWithSort.set(config?.tableWithSort);
4594
4803
  this.tableWithPagination.set(config?.tableWithPagination);
@@ -4702,7 +4911,7 @@ class IssField {
4702
4911
  this.label = field.label;
4703
4912
  }
4704
4913
  else {
4705
- this.label = signal(field.title, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
4914
+ this.label = signal(field.label ?? field.title, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
4706
4915
  }
4707
4916
  if (isSignal(field.hint)) {
4708
4917
  this.hint = field.hint;
@@ -4732,7 +4941,8 @@ class IssField {
4732
4941
  if (!field.events) {
4733
4942
  field.events = [];
4734
4943
  }
4735
- this.visible = signal((!field.events.filter((obj) => obj.name === "show" && field.mode === "edit").length), ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
4944
+ // this.mode() and not field.mode: the caller may have passed a signal
4945
+ this.visible = signal(!field.events.some((obj) => obj.name === "show" && this.mode() === "edit"), ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
4736
4946
  this.events = field.events.map((action) => new FieldEvent(action, loader));
4737
4947
  this.validation = field.validation;
4738
4948
  this.asyncValidation = new FieldAsyncValidation(field.asyncValidation);
@@ -4747,13 +4957,7 @@ class IssField {
4747
4957
  this.apiResponse = new BehaviorSubject(null);
4748
4958
  }
4749
4959
  updateVisibility(value) {
4750
- this.visible.update((v) => {
4751
- if (!this.wasVisible) {
4752
- this.wasVisible = signal(v, ...(ngDevMode ? [{ debugName: "wasVisible" }] : /* istanbul ignore next */ []));
4753
- }
4754
- this.wasVisible.set(v);
4755
- return v;
4756
- });
4960
+ this.wasVisible.set(this.visible());
4757
4961
  this.visible.set(value);
4758
4962
  }
4759
4963
  }
@@ -4779,7 +4983,58 @@ const VALIDATORS = {
4779
4983
  };
4780
4984
 
4781
4985
  class FormEngine {
4782
- fields = [];
4986
+ // Fields are stored per host FormGroup, never on the service itself: a single root
4987
+ // FormEngine serves every form on the page, including the nested outlet that
4988
+ // iss-field-table renders against its own tableOutletForm.
4989
+ // The outer WeakMap lets a form's entry be collected with the form. The inner Map is
4990
+ // keyed by the outlet that registered them, so several outlets can share one host form
4991
+ // without overwriting each other, and re-registering the same outlet replaces its own
4992
+ // contribution instead of accumulating stale entries.
4993
+ #registry = new WeakMap();
4994
+ registerFields(form, owner, fields) {
4995
+ this.#registration(form, owner).fields = fields;
4996
+ }
4997
+ // The names an outlet expects to see as controls on the host form. An outlet is ready
4998
+ // once every one of them has been added, so this is registered up front in ngOnInit,
4999
+ // before the fields themselves resolve.
5000
+ registerExpectedFieldNames(form, owner, names) {
5001
+ this.#registration(form, owner).expectedFieldNames = names;
5002
+ }
5003
+ unregisterOutlet(form, owner) {
5004
+ const owners = this.#registry.get(form);
5005
+ if (!owners) {
5006
+ return;
5007
+ }
5008
+ owners.delete(owner);
5009
+ if (!owners.size) {
5010
+ this.#registry.delete(form);
5011
+ }
5012
+ }
5013
+ getFields(form) {
5014
+ return this.#registrations(form).map((registration) => registration.fields).flat();
5015
+ }
5016
+ getExpectedFieldNames(form) {
5017
+ const names = this.#registrations(form).map((registration) => registration.expectedFieldNames).flat();
5018
+ return Array.from(new Set(names));
5019
+ }
5020
+ // Searches nested children too, so a field inside a table can be found from the form
5021
+ // that hosts the table.
5022
+ getField(form, name, fields = this.getFields(form)) {
5023
+ const match = fields.find((field) => field.name === name);
5024
+ if (match) {
5025
+ return match;
5026
+ }
5027
+ return fields
5028
+ .filter((field) => field.children?.length)
5029
+ .reduce((found, field) => found ?? this.getField(form, name, field.children), undefined);
5030
+ }
5031
+ getControlValue(form, name) {
5032
+ const ctrlValue = form.get(name)?.getRawValue();
5033
+ if (ctrlValue !== null && ctrlValue !== undefined && ctrlValue !== "") {
5034
+ return ctrlValue;
5035
+ }
5036
+ return this.getField(form, name)?.value;
5037
+ }
4783
5038
  createValidators(field) {
4784
5039
  if (!this.fieldHasValidation(field)) {
4785
5040
  return [Validators.nullValidator];
@@ -4821,7 +5076,7 @@ class FormEngine {
4821
5076
  return;
4822
5077
  }
4823
5078
  const masterCtrl = form.get(masterName);
4824
- const masterField = this.fields.find((field) => field.name === masterName);
5079
+ const masterField = this.getField(form, masterName);
4825
5080
  if (!masterField) {
4826
5081
  return;
4827
5082
  }
@@ -4859,6 +5114,26 @@ class FormEngine {
4859
5114
  return true;
4860
5115
  }).every((status) => status);
4861
5116
  }
5117
+ #registrations(form) {
5118
+ const owners = this.#registry.get(form);
5119
+ if (!owners) {
5120
+ return [];
5121
+ }
5122
+ return Array.from(owners.values());
5123
+ }
5124
+ #registration(form, owner) {
5125
+ let owners = this.#registry.get(form);
5126
+ if (!owners) {
5127
+ owners = new Map();
5128
+ this.#registry.set(form, owners);
5129
+ }
5130
+ let registration = owners.get(owner);
5131
+ if (!registration) {
5132
+ registration = { fields: [], expectedFieldNames: [] };
5133
+ owners.set(owner, registration);
5134
+ }
5135
+ return registration;
5136
+ }
4862
5137
  #validatorsToNgValidators(field) {
4863
5138
  return this.#mapNgValidators(field.validation).map((validatorValue) => validatorValue?.fn(field));
4864
5139
  }
@@ -4902,28 +5177,52 @@ const COMPONENTS = {
4902
5177
  textarea: () => Promise.resolve().then(function () { return textarea_field; }).then((c) => c.TextareaField),
4903
5178
  chips: () => Promise.resolve().then(function () { return chips_field; }).then((c) => c.ChipsField),
4904
5179
  plate: () => Promise.resolve().then(function () { return plate_field; }).then((c) => c.PlateField),
4905
- note: () => import('./iss-ctrl-note.field-CuTQ3YwJ.mjs').then((c) => c.NoteField),
5180
+ note: () => import('./iss-ctrl-note.field-4uLl0oS_.mjs').then((c) => c.NoteField),
4906
5181
  slideToggle: () => Promise.resolve().then(function () { return slideToggle_field; }).then((c) => c.SlideToggleField),
4907
5182
  toggle: () => Promise.resolve().then(function () { return toggleButton_field; }).then((c) => c.ToggleButtonField),
4908
5183
  time: () => Promise.resolve().then(function () { return timePicker_field; }).then((c) => c.TimePickerField),
4909
5184
  mobile: () => Promise.resolve().then(function () { return mobile_field; }).then((c) => c.MobileField),
4910
- table: () => import('./iss-ctrl-table.field-Bs6l5tWV.mjs').then((c) => c.TableField),
4911
- userPicker: () => import('./iss-ctrl-user-picker.field-Cx0CCAFC.mjs').then((c) => c.UserPickerField),
4912
- userPicker2: () => import('./iss-ctrl-user-picker.field-Cx0CCAFC.mjs').then((c) => c.UserPickerField),
5185
+ table: () => import('./iss-ctrl-table.field-kPIwve-M.mjs').then((c) => c.TableField),
5186
+ userPicker: () => import('./iss-ctrl-user-picker.field-l-0bW6lP.mjs').then((c) => c.UserPickerField),
5187
+ userPicker2: () => import('./iss-ctrl-user-picker.field-l-0bW6lP.mjs').then((c) => c.UserPickerField),
4913
5188
  };
4914
5189
 
4915
5190
  const SERVICES = {
4916
- setValue: () => import('./iss-ctrl-set-value-event-BNNMVIeT.mjs').then((s) => s.SetValueEvent),
5191
+ setValue: () => import('./iss-ctrl-set-value-event-BbEYFBe3.mjs').then((s) => s.SetValueEvent),
4917
5192
  show: () => import('./iss-ctrl-show-event-BcRCwAfo.mjs').then((s) => s.ShowEvent),
4918
- load: () => import('./iss-ctrl-load-value-event-idouR9ST.mjs').then((s) => s.LoadValueEvent),
4919
- loadOptions: () => import('./iss-ctrl-load-options-event-BIn0aiHl.mjs').then((s) => s.LoadOptionsEvent),
4920
- triggerBasedOnJsonValue: () => import('./iss-ctrl-trigger-based-on-json-value-6yh7NOEH.mjs').then((s) => s.TriggerBasedOnJsonValue),
4921
- replaceApiKeyDefaultImpl: () => import('./iss-ctrl-replace-api-keys-Df8ruZtV.mjs').then((s) => s.ReplaceApiKeys),
5193
+ load: () => import('./iss-ctrl-load-value-event-DThpI9BA.mjs').then((s) => s.LoadValueEvent),
5194
+ loadOptions: () => import('./iss-ctrl-load-options-event-ClX9TiiD.mjs').then((s) => s.LoadOptionsEvent),
5195
+ triggerBasedOnJsonValue: () => import('./iss-ctrl-trigger-based-on-json-value-DuLD4SOS.mjs').then((s) => s.TriggerBasedOnJsonValue),
5196
+ replaceApiKeyDefaultImpl: () => import('./iss-ctrl-replace-api-keys-BQL9zimJ.mjs').then((s) => s.ReplaceApiKeys),
5197
+ };
5198
+
5199
+ // Read-only counterpart of COMPONENTS: the component used to render a field's value
5200
+ // without an editable control, keyed by the same field types.
5201
+ const VIEWERS = {
5202
+ date: () => Promise.resolve().then(function () { return datePicker_viewer; }).then((c) => c.DatePickerViewer),
5203
+ datetime: () => Promise.resolve().then(function () { return datetimePicker_viewer; }).then((c) => c.DatePickerTimeViewer),
5204
+ dateRange: () => Promise.resolve().then(function () { return dateRangePicker_viewer; }).then((c) => c.DateRangePickerViewer),
5205
+ date_range: () => Promise.resolve().then(function () { return dateRangePicker_viewer; }).then((c) => c.DateRangePickerViewer),
5206
+ select: () => Promise.resolve().then(function () { return select_viewer; }).then((c) => c.SelectViewer),
5207
+ input: () => Promise.resolve().then(function () { return input_viewer; }).then((c) => c.InputViewer),
5208
+ text: () => Promise.resolve().then(function () { return input_viewer; }).then((c) => c.InputViewer),
5209
+ checkbox: () => Promise.resolve().then(function () { return checkbox_viewer; }).then((c) => c.CheckboxViewer),
5210
+ file: () => Promise.resolve().then(function () { return attachment_viewer; }).then((c) => c.AttachmentViewer),
5211
+ file2: () => Promise.resolve().then(function () { return attachment_viewer; }).then((c) => c.AttachmentViewer),
5212
+ radio: () => Promise.resolve().then(function () { return radio_viewer; }).then((c) => c.RadioViewer),
5213
+ textarea: () => Promise.resolve().then(function () { return textarea_viewer; }).then((c) => c.TextareaViewer),
5214
+ chips: () => Promise.resolve().then(function () { return chips_viewer; }).then((c) => c.ChipsViewer),
5215
+ plate: () => Promise.resolve().then(function () { return plate_viewer; }).then((c) => c.PlateViewer),
5216
+ slideToggle: () => Promise.resolve().then(function () { return slideToggle_viewer; }).then((c) => c.SlideToggleViewer),
5217
+ toggle: () => Promise.resolve().then(function () { return toggleButton_viewer; }).then((c) => c.ToggleButtonViewer),
5218
+ time: () => Promise.resolve().then(function () { return timePicker_viewer; }).then((c) => c.TimePickerViewer),
5219
+ mobile: () => Promise.resolve().then(function () { return mobile_viewer; }).then((c) => c.MobileViewer),
4922
5220
  };
4923
5221
 
4924
5222
  class Loader {
4925
5223
  #injector = inject(Injector);
4926
5224
  #loadedComponents = new Map();
5225
+ #loadedViewers = new Map();
4927
5226
  #loadedServices = new Map();
4928
5227
  loadComponent(field) {
4929
5228
  const loadedComponent = this.#loadedComponents.get(field.type);
@@ -4934,7 +5233,18 @@ class Loader {
4934
5233
  if (!componentImportFn) {
4935
5234
  return of(null);
4936
5235
  }
4937
- return this.#createObservableFromPromise(componentImportFn, field.type);
5236
+ return this.#createObservableFromPromise(componentImportFn, field.type, this.#loadedComponents);
5237
+ }
5238
+ loadViewer(type) {
5239
+ const loadedViewer = this.#loadedViewers.get(type);
5240
+ if (loadedViewer) {
5241
+ return of(loadedViewer);
5242
+ }
5243
+ const viewerImportFn = VIEWERS[type];
5244
+ if (!viewerImportFn) {
5245
+ return of(null);
5246
+ }
5247
+ return this.#createObservableFromPromise(viewerImportFn, type, this.#loadedViewers);
4938
5248
  }
4939
5249
  loadService(serviceName) {
4940
5250
  const loadedService = this.#loadedServices.get(serviceName);
@@ -4952,9 +5262,9 @@ class Loader {
4952
5262
  return loadedService;
4953
5263
  });
4954
5264
  }
4955
- #createObservableFromPromise(componentImportFn, type) {
5265
+ #createObservableFromPromise(componentImportFn, type, cache) {
4956
5266
  return from(componentImportFn()).pipe(tap((component) => {
4957
- this.#loadedComponents.set(type, component);
5267
+ cache.set(type, component);
4958
5268
  }));
4959
5269
  }
4960
5270
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: Loader, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
@@ -4988,33 +5298,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4988
5298
  args: [{ name: "FieldDataInjector" }]
4989
5299
  }] });
4990
5300
 
4991
- const outletRegistry = new Map();
4992
5301
  class Outlet {
4993
5302
  ready = output();
4994
5303
  formReady = signal(false, ...(ngDevMode ? [{ debugName: "formReady" }] : /* istanbul ignore next */ []));
4995
5304
  fields = input.required(...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
4996
5305
  components = signal(null, ...(ngDevMode ? [{ debugName: "components" }] : /* istanbul ignore next */ []));
4997
5306
  #controlContainer = inject(ControlContainer);
5307
+ #destroyRef = inject(DestroyRef);
4998
5308
  #formEngine = inject(FormEngine);
4999
5309
  #loader = inject(Loader);
5000
- #effectRef = effect(async () => {
5001
- const components = await this.#componentsFactory(this.fields());
5002
- this.components.set(components);
5003
- if (components) {
5004
- this.#formEngine.fields = components.map((component) => component.data);
5005
- }
5310
+ #effectRef = effect((onCleanup) => {
5311
+ // Dynamic imports cannot be cancelled, but an old result must never replace
5312
+ // the components produced for a newer fields input (or a destroyed outlet).
5313
+ let active = true;
5314
+ onCleanup(() => {
5315
+ active = false;
5316
+ });
5317
+ // Remove the previous outlet immediately. This destroys its field components
5318
+ // (which remove their controls in ngOnDestroy) and prevents A's metadata from
5319
+ // being returned while B is still loading.
5320
+ this.components.set(null);
5321
+ this.#formEngine.registerFields(this.hostFormGroup, this, []);
5322
+ void this.#componentsFactory(this.fields()).then((components) => {
5323
+ if (!active) {
5324
+ return;
5325
+ }
5326
+ this.components.set(components);
5327
+ this.#formEngine.registerFields(this.hostFormGroup, this, components.map((component) => component.data));
5328
+ });
5006
5329
  }, ...(ngDevMode ? [{ debugName: "#effectRef" }] : /* istanbul ignore next */ []));
5007
5330
  get hostFormGroup() {
5008
5331
  return this.#controlContainer.control;
5009
5332
  }
5010
5333
  ngOnInit() {
5011
- if (!this.hostFormGroup.id) {
5012
- this.hostFormGroup.id = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
5013
- }
5014
- const existingFields = outletRegistry.get(this.hostFormGroup.id) || [];
5015
- const newFieldNames = this.fields().filter((f) => f.mode() === "edit" || (f.mode() === "view" && f.value)).map((f) => f.name);
5016
- outletRegistry.set(this.hostFormGroup.id, Array.from(new Set([...existingFields, ...newFieldNames])));
5017
- this.#listenToHostFormGroupChanges().subscribe((isReady) => {
5334
+ const expectedFieldNames = this.fields()
5335
+ .filter((f) => f.mode() === "edit" || (f.mode() === "view" && f.value))
5336
+ .map((f) => f.name);
5337
+ this.#formEngine.registerExpectedFieldNames(this.hostFormGroup, this, expectedFieldNames);
5338
+ this.#listenToHostFormGroupChanges().pipe(takeUntilDestroyed(this.#destroyRef)).subscribe((isReady) => {
5018
5339
  if (isReady) {
5019
5340
  this.formReady.set(true);
5020
5341
  this.ready.emit(true);
@@ -5023,6 +5344,7 @@ class Outlet {
5023
5344
  }
5024
5345
  ngOnDestroy() {
5025
5346
  this.#effectRef.destroy();
5347
+ this.#formEngine.unregisterOutlet(this.hostFormGroup, this);
5026
5348
  }
5027
5349
  async #componentsFactory(fields) {
5028
5350
  const fieldsComponentPromise = fields.map(async (field) => {
@@ -5078,7 +5400,7 @@ class Outlet {
5078
5400
  #listenToHostFormGroupChanges() {
5079
5401
  return this.hostFormGroup.valueChanges.pipe(debounceTime(500), map(() => {
5080
5402
  const controlNames = Object.keys(this.hostFormGroup.getRawValue());
5081
- const fieldNames = outletRegistry.get(this.hostFormGroup.id);
5403
+ const fieldNames = this.#formEngine.getExpectedFieldNames(this.hostFormGroup);
5082
5404
  const diff = fieldNames.filter((x) => !controlNames.includes(x));
5083
5405
  return diff.length === 0;
5084
5406
  }), first((v) => v));
@@ -5265,7 +5587,8 @@ class ValidationMessageComponent {
5265
5587
  getValidationMessage(error) {
5266
5588
  const errorMessageGenerator = VALIDATION_MESSAGES[error.key];
5267
5589
  if (!errorMessageGenerator) {
5268
- throw new Error(`THERE IS NO VALIDATION MESSAGE FOR KEY "${error.key}"`);
5590
+ console.warn(`THERE IS NO VALIDATION MESSAGE FOR KEY "${error.key}"`);
5591
+ return error.key;
5269
5592
  }
5270
5593
  return errorMessageGenerator(error.value);
5271
5594
  }
@@ -5442,16 +5765,27 @@ class FieldBase {
5442
5765
  ngOnDestroy() {
5443
5766
  this.#valueChangeEffectRef.destroy();
5444
5767
  this.#eventEffectRef.destroy();
5768
+ this.#removeControlFromForm();
5445
5769
  }
5446
5770
  #addControlToForm() {
5447
5771
  this.ctrl.setValue(this.formEngine.getFieldValue(this.field));
5448
5772
  this.ctrl.setValidators(this.formEngine.createValidators(this.field));
5449
5773
  this.ctrl.setAsyncValidators(this.formEngine.createAsyncValidators(this.field));
5774
+ if (this.field.config.disabled() === true) {
5775
+ this.ctrl.disable({ emitEvent: false });
5776
+ }
5450
5777
  this.form.addControl(this.field.name, this.ctrl);
5451
5778
  if (this.ctrl.getRawValue()) {
5452
5779
  this.ctrl.markAsTouched();
5453
5780
  }
5454
5781
  }
5782
+ // a conditionally rendered field would otherwise leave its control behind on the host
5783
+ // form, keeping it in the form value and in the outlet's readiness diff forever
5784
+ #removeControlFromForm() {
5785
+ if (this.form?.get(this.field.name) === this.ctrl) {
5786
+ this.form.removeControl(this.field.name);
5787
+ }
5788
+ }
5455
5789
  #initiateEvents() {
5456
5790
  // todo don't start events in view mode, just set value
5457
5791
  if (this.field.events && this.field.events.length) {
@@ -5513,17 +5847,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5513
5847
  }]
5514
5848
  }] });
5515
5849
 
5850
+ var input_viewer = /*#__PURE__*/Object.freeze({
5851
+ __proto__: null,
5852
+ InputViewer: InputViewer
5853
+ });
5854
+
5516
5855
  class InputField extends FieldBase {
5517
5856
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: InputField, deps: null, target: i0.ɵɵFactoryTarget.Component });
5518
5857
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: InputField, isStandalone: true, selector: "iss-field-input", usesInheritance: true, ngImport: i0, template: `
5519
5858
  @if (field.visible()) {
5520
5859
  @if (field.mode() === "edit") {
5521
- <iss-ctrl-input [formControlName]="field.name" [type]="field.config.type() ?? ''" [placeholder]="field.config.placeholder() ?? ''" [min]="field.config.min() ?? null" [max]="field.config.max() ?? null">
5860
+ <iss-ctrl-input
5861
+ [formControlName]="field.name"
5862
+ [type]="field.config.type() ?? ''"
5863
+ [placeholder]="field.config.placeholder() ?? ''"
5864
+ [min]="field.config.min() ?? null"
5865
+ [max]="field.config.max() ?? null"
5866
+ [readonly]="field.config.readonly() ?? false"
5867
+ [minLength]="field.config.minLength() ?? null"
5868
+ [maxLength]="field.config.maxLength() ?? null"
5869
+ >
5522
5870
  <label>{{ field.label() }}</label>
5523
5871
  </iss-ctrl-input>
5524
- <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched" />
5872
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5525
5873
  } @else {
5526
- <iss-viewer-input [field]="field" />
5874
+ <iss-viewer-input [field]="field"/>
5527
5875
  }
5528
5876
  }
5529
5877
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: InputControl, selector: "iss-ctrl-input", inputs: ["type", "prefixIcon", "prefixText", "suffixIcon", "suffixText"], outputs: ["onPrefixClick", "onSuffixClick"] }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: InputViewer, selector: "iss-viewer-input" }], viewProviders: [VIEW_PROVIDER] });
@@ -5542,12 +5890,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5542
5890
  template: `
5543
5891
  @if (field.visible()) {
5544
5892
  @if (field.mode() === "edit") {
5545
- <iss-ctrl-input [formControlName]="field.name" [type]="field.config.type() ?? ''" [placeholder]="field.config.placeholder() ?? ''" [min]="field.config.min() ?? null" [max]="field.config.max() ?? null">
5893
+ <iss-ctrl-input
5894
+ [formControlName]="field.name"
5895
+ [type]="field.config.type() ?? ''"
5896
+ [placeholder]="field.config.placeholder() ?? ''"
5897
+ [min]="field.config.min() ?? null"
5898
+ [max]="field.config.max() ?? null"
5899
+ [readonly]="field.config.readonly() ?? false"
5900
+ [minLength]="field.config.minLength() ?? null"
5901
+ [maxLength]="field.config.maxLength() ?? null"
5902
+ >
5546
5903
  <label>{{ field.label() }}</label>
5547
5904
  </iss-ctrl-input>
5548
- <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched" />
5905
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5549
5906
  } @else {
5550
- <iss-viewer-input [field]="field" />
5907
+ <iss-viewer-input [field]="field"/>
5551
5908
  }
5552
5909
  }
5553
5910
  `
@@ -5575,17 +5932,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5575
5932
  }]
5576
5933
  }] });
5577
5934
 
5935
+ var textarea_viewer = /*#__PURE__*/Object.freeze({
5936
+ __proto__: null,
5937
+ TextareaViewer: TextareaViewer
5938
+ });
5939
+
5578
5940
  class TextareaField extends FieldBase {
5579
5941
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TextareaField, deps: null, target: i0.ɵɵFactoryTarget.Component });
5580
5942
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: TextareaField, isStandalone: true, selector: "iss-field-textarea", usesInheritance: true, ngImport: i0, template: `
5581
5943
  @if (field.visible()) {
5582
5944
  @if (field.mode() === "edit") {
5583
- <iss-ctrl-textarea [formControlName]="field.name" [min]="field.config.min() ?? null" [max]="field.config.max() ?? null">
5945
+ <iss-ctrl-textarea [formControlName]="field.name" [minLength]="field.config.minLength() ?? null" [maxLength]="field.config.maxLength() ?? null" [readonly]="field.config.readonly() ?? false">
5584
5946
  <label>{{ field.label() }}</label>
5585
5947
  </iss-ctrl-textarea>
5586
- <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched" />
5948
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5587
5949
  } @else {
5588
- <iss-viewer-textarea [field]="field" />
5950
+ <iss-viewer-textarea [field]="field"/>
5589
5951
  }
5590
5952
  }
5591
5953
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: TextareaControl, selector: "iss-ctrl-textarea" }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: TextareaViewer, selector: "iss-viewer-textarea" }], viewProviders: [VIEW_PROVIDER] });
@@ -5604,12 +5966,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5604
5966
  template: `
5605
5967
  @if (field.visible()) {
5606
5968
  @if (field.mode() === "edit") {
5607
- <iss-ctrl-textarea [formControlName]="field.name" [min]="field.config.min() ?? null" [max]="field.config.max() ?? null">
5969
+ <iss-ctrl-textarea [formControlName]="field.name" [minLength]="field.config.minLength() ?? null" [maxLength]="field.config.maxLength() ?? null" [readonly]="field.config.readonly() ?? false">
5608
5970
  <label>{{ field.label() }}</label>
5609
5971
  </iss-ctrl-textarea>
5610
- <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched" />
5972
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5611
5973
  } @else {
5612
- <iss-viewer-textarea [field]="field" />
5974
+ <iss-viewer-textarea [field]="field"/>
5613
5975
  }
5614
5976
  }
5615
5977
  `
@@ -5652,17 +6014,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5652
6014
  }]
5653
6015
  }] });
5654
6016
 
6017
+ var chips_viewer = /*#__PURE__*/Object.freeze({
6018
+ __proto__: null,
6019
+ ChipsViewer: ChipsViewer
6020
+ });
6021
+
5655
6022
  class ChipsField extends FieldBase {
5656
6023
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ChipsField, deps: null, target: i0.ɵɵFactoryTarget.Component });
5657
6024
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: ChipsField, isStandalone: true, selector: "iss-field-chips", usesInheritance: true, ngImport: i0, template: `
5658
6025
  @if (field.visible()) {
5659
6026
  @if (field.mode() === "edit") {
5660
- <iss-ctrl-chips [formControlName]="field.name"><label>{{ field.label() }}</label></iss-ctrl-chips>
6027
+ <iss-ctrl-chips [formControlName]="field.name" [readonly]="field.config.readonly() ?? false"><label>{{ field.label() }}</label></iss-ctrl-chips>
6028
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5661
6029
  } @else {
5662
- <iss-viewer-chips [field]="field" />
6030
+ <iss-viewer-chips [field]="field"/>
5663
6031
  }
5664
6032
  }
5665
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: ChipsControl, selector: "iss-ctrl-chips" }, { kind: "component", type: ChipsViewer, selector: "iss-viewer-chips" }], viewProviders: [VIEW_PROVIDER] });
6033
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: ChipsControl, selector: "iss-ctrl-chips" }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: ChipsViewer, selector: "iss-viewer-chips" }], viewProviders: [VIEW_PROVIDER] });
5666
6034
  }
5667
6035
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ChipsField, decorators: [{
5668
6036
  type: Component,
@@ -5672,14 +6040,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5672
6040
  imports: [
5673
6041
  ReactiveFormsModule,
5674
6042
  ChipsControl,
6043
+ ValidationMessageComponent,
5675
6044
  ChipsViewer
5676
6045
  ],
5677
6046
  template: `
5678
6047
  @if (field.visible()) {
5679
6048
  @if (field.mode() === "edit") {
5680
- <iss-ctrl-chips [formControlName]="field.name"><label>{{ field.label() }}</label></iss-ctrl-chips>
6049
+ <iss-ctrl-chips [formControlName]="field.name" [readonly]="field.config.readonly() ?? false"><label>{{ field.label() }}</label></iss-ctrl-chips>
6050
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5681
6051
  } @else {
5682
- <iss-viewer-chips [field]="field" />
6052
+ <iss-viewer-chips [field]="field"/>
5683
6053
  }
5684
6054
  }
5685
6055
  `
@@ -5715,6 +6085,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5715
6085
  }]
5716
6086
  }] });
5717
6087
 
6088
+ var radio_viewer = /*#__PURE__*/Object.freeze({
6089
+ __proto__: null,
6090
+ RadioViewer: RadioViewer
6091
+ });
6092
+
5718
6093
  class RadioField extends FieldBase {
5719
6094
  onChange($event) {
5720
6095
  this.field.ctrlJsonValue.set($event);
@@ -5723,18 +6098,19 @@ class RadioField extends FieldBase {
5723
6098
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: RadioField, isStandalone: true, selector: "iss-field-radio", usesInheritance: true, ngImport: i0, template: `
5724
6099
  @if (field.visible()) {
5725
6100
  <div style="display: flex; flex-direction: column">
5726
- @if (!(field.config.hideLabel)) {
6101
+ @if (!field.config.hideLabel()) {
5727
6102
  <mat-label>{{ field.label() }}</mat-label>
5728
6103
  }
5729
6104
 
5730
6105
  @if (field.mode() === "edit") {
5731
- <iss-ctrl-radio [formControlName]="field.name" [options]="field.options()" (onChange)="onChange($event)" />
6106
+ <iss-ctrl-radio [formControlName]="field.name" [options]="field.options()" [readonly]="field.config.readonly() ?? false" (onChange)="onChange($event)"/>
6107
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5732
6108
  } @else {
5733
- <iss-viewer-radio [field]="field" />
6109
+ <iss-viewer-radio [field]="field"/>
5734
6110
  }
5735
6111
  </div>
5736
6112
  }
5737
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: RadioControl, selector: "iss-ctrl-radio", inputs: ["options"] }, { kind: "component", type: RadioViewer, selector: "iss-viewer-radio" }], viewProviders: [VIEW_PROVIDER] });
6113
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: RadioControl, selector: "iss-ctrl-radio", inputs: ["options"] }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: RadioViewer, selector: "iss-viewer-radio" }], viewProviders: [VIEW_PROVIDER] });
5738
6114
  }
5739
6115
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RadioField, decorators: [{
5740
6116
  type: Component,
@@ -5745,19 +6121,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5745
6121
  ReactiveFormsModule,
5746
6122
  MatLabel,
5747
6123
  RadioControl,
6124
+ ValidationMessageComponent,
5748
6125
  RadioViewer
5749
6126
  ],
5750
6127
  template: `
5751
6128
  @if (field.visible()) {
5752
6129
  <div style="display: flex; flex-direction: column">
5753
- @if (!(field.config.hideLabel)) {
6130
+ @if (!field.config.hideLabel()) {
5754
6131
  <mat-label>{{ field.label() }}</mat-label>
5755
6132
  }
5756
6133
 
5757
6134
  @if (field.mode() === "edit") {
5758
- <iss-ctrl-radio [formControlName]="field.name" [options]="field.options()" (onChange)="onChange($event)" />
6135
+ <iss-ctrl-radio [formControlName]="field.name" [options]="field.options()" [readonly]="field.config.readonly() ?? false" (onChange)="onChange($event)"/>
6136
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5759
6137
  } @else {
5760
- <iss-viewer-radio [field]="field" />
6138
+ <iss-viewer-radio [field]="field"/>
5761
6139
  }
5762
6140
  </div>
5763
6141
  }
@@ -5794,6 +6172,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5794
6172
  }]
5795
6173
  }] });
5796
6174
 
6175
+ var select_viewer = /*#__PURE__*/Object.freeze({
6176
+ __proto__: null,
6177
+ SelectViewer: SelectViewer
6178
+ });
6179
+
5797
6180
  class SelectField extends FieldBase {
5798
6181
  onChange($event) {
5799
6182
  this.field.ctrlJsonValue.set($event);
@@ -5802,14 +6185,15 @@ class SelectField extends FieldBase {
5802
6185
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SelectField, isStandalone: true, selector: "iss-field-select", usesInheritance: true, ngImport: i0, template: `
5803
6186
  @if (field.visible()) {
5804
6187
  @if (field.mode() === "edit") {
5805
- <iss-ctrl-select [formControlName]="field.name" [options]="field.options()" [multiple]="field.config.multiple()" (onChange)="onChange($event)">
6188
+ <iss-ctrl-select [formControlName]="field.name" [options]="field.options()" [multiple]="field.config.multiple()" [readonly]="field.config.readonly() ?? false" (onChange)="onChange($event)">
5806
6189
  <label>{{ field.label() }}</label>
5807
6190
  </iss-ctrl-select>
6191
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5808
6192
  } @else {
5809
- <iss-viewer-select [field]="field" />
6193
+ <iss-viewer-select [field]="field"/>
5810
6194
  }
5811
6195
  }
5812
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: SelectControl, selector: "iss-ctrl-select", inputs: ["options", "height", "loading"], outputs: ["onQueryChange"] }, { kind: "component", type: SelectViewer, selector: "iss-viewer-select" }], viewProviders: [VIEW_PROVIDER] });
6196
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: SelectControl, selector: "iss-ctrl-select", inputs: ["options", "height", "loading"], outputs: ["onQueryChange"] }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: SelectViewer, selector: "iss-viewer-select" }], viewProviders: [VIEW_PROVIDER] });
5813
6197
  }
5814
6198
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SelectField, decorators: [{
5815
6199
  type: Component,
@@ -5819,16 +6203,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5819
6203
  imports: [
5820
6204
  ReactiveFormsModule,
5821
6205
  SelectControl,
6206
+ ValidationMessageComponent,
5822
6207
  SelectViewer
5823
6208
  ],
5824
6209
  template: `
5825
6210
  @if (field.visible()) {
5826
6211
  @if (field.mode() === "edit") {
5827
- <iss-ctrl-select [formControlName]="field.name" [options]="field.options()" [multiple]="field.config.multiple()" (onChange)="onChange($event)">
6212
+ <iss-ctrl-select [formControlName]="field.name" [options]="field.options()" [multiple]="field.config.multiple()" [readonly]="field.config.readonly() ?? false" (onChange)="onChange($event)">
5828
6213
  <label>{{ field.label() }}</label>
5829
6214
  </iss-ctrl-select>
6215
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5830
6216
  } @else {
5831
- <iss-viewer-select [field]="field" />
6217
+ <iss-viewer-select [field]="field"/>
5832
6218
  }
5833
6219
  }
5834
6220
  `
@@ -5856,16 +6242,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5856
6242
  }]
5857
6243
  }] });
5858
6244
 
6245
+ var mobile_viewer = /*#__PURE__*/Object.freeze({
6246
+ __proto__: null,
6247
+ MobileViewer: MobileViewer
6248
+ });
6249
+
5859
6250
  class MobileField extends FieldBase {
5860
6251
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MobileField, deps: null, target: i0.ɵɵFactoryTarget.Component });
5861
6252
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MobileField, isStandalone: true, selector: "iss-field-mobile", usesInheritance: true, ngImport: i0, template: `
5862
6253
  @if (field.visible()) {
5863
6254
  @if (field.mode() === "edit") {
5864
- <iss-ctrl-mobile [formControlName]="field.name">
6255
+ <iss-ctrl-mobile [formControlName]="field.name" [readonly]="field.config.readonly() ?? false">
5865
6256
  <label>{{ field.label() }}</label>
5866
6257
  </iss-ctrl-mobile>
5867
6258
  } @else {
5868
- <iss-viewer-mobile [field]="field" />
6259
+ <iss-viewer-mobile [field]="field"/>
5869
6260
  }
5870
6261
  }
5871
6262
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: MobileControl, selector: "iss-ctrl-mobile", inputs: ["preferredCountries"] }, { kind: "component", type: MobileViewer, selector: "iss-viewer-mobile" }], viewProviders: [VIEW_PROVIDER] });
@@ -5883,11 +6274,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5883
6274
  template: `
5884
6275
  @if (field.visible()) {
5885
6276
  @if (field.mode() === "edit") {
5886
- <iss-ctrl-mobile [formControlName]="field.name">
6277
+ <iss-ctrl-mobile [formControlName]="field.name" [readonly]="field.config.readonly() ?? false">
5887
6278
  <label>{{ field.label() }}</label>
5888
6279
  </iss-ctrl-mobile>
5889
6280
  } @else {
5890
- <iss-viewer-mobile [field]="field" />
6281
+ <iss-viewer-mobile [field]="field"/>
5891
6282
  }
5892
6283
  }
5893
6284
  `
@@ -5924,24 +6315,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5924
6315
  }]
5925
6316
  }] });
5926
6317
 
6318
+ var checkbox_viewer = /*#__PURE__*/Object.freeze({
6319
+ __proto__: null,
6320
+ CheckboxViewer: CheckboxViewer
6321
+ });
6322
+
5927
6323
  class CheckboxField extends FieldBase {
5928
6324
  updateField($event) {
5929
- this.field.jsonValue = $event;
6325
+ this.field.ctrlJsonValue.set($event);
5930
6326
  }
5931
6327
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CheckboxField, deps: null, target: i0.ɵɵFactoryTarget.Component });
5932
6328
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: CheckboxField, isStandalone: true, selector: "iss-field-checkbox", usesInheritance: true, ngImport: i0, template: `
5933
6329
  @if (field.visible()) {
5934
6330
  @if (field.mode() === "edit") {
5935
- <iss-ctrl-checkbox [formControlName]="field.name" [options]="field.options()" (onChange)="updateField($event)">
5936
- @if (!(field.config.hideLabel)) {
6331
+ <iss-ctrl-checkbox [formControlName]="field.name" [options]="field.options()" [readonly]="field.config.readonly() ?? false" (onChange)="updateField($event)">
6332
+ @if (!field.config.hideLabel()) {
5937
6333
  <label>{{ field.label() }}</label>
5938
6334
  }
5939
6335
  </iss-ctrl-checkbox>
6336
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5940
6337
  } @else {
5941
- <iss-viewer-checkbox [field]="field" />
6338
+ <iss-viewer-checkbox [field]="field"/>
5942
6339
  }
5943
6340
  }
5944
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: CheckboxControl, selector: "iss-ctrl-checkbox", inputs: ["options"] }, { kind: "component", type: CheckboxViewer, selector: "iss-viewer-checkbox" }], viewProviders: [VIEW_PROVIDER] });
6341
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: CheckboxControl, selector: "iss-ctrl-checkbox", inputs: ["options"] }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: CheckboxViewer, selector: "iss-viewer-checkbox" }], viewProviders: [VIEW_PROVIDER] });
5945
6342
  }
5946
6343
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CheckboxField, decorators: [{
5947
6344
  type: Component,
@@ -5951,18 +6348,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5951
6348
  imports: [
5952
6349
  ReactiveFormsModule,
5953
6350
  CheckboxControl,
6351
+ ValidationMessageComponent,
5954
6352
  CheckboxViewer
5955
6353
  ],
5956
6354
  template: `
5957
6355
  @if (field.visible()) {
5958
6356
  @if (field.mode() === "edit") {
5959
- <iss-ctrl-checkbox [formControlName]="field.name" [options]="field.options()" (onChange)="updateField($event)">
5960
- @if (!(field.config.hideLabel)) {
6357
+ <iss-ctrl-checkbox [formControlName]="field.name" [options]="field.options()" [readonly]="field.config.readonly() ?? false" (onChange)="updateField($event)">
6358
+ @if (!field.config.hideLabel()) {
5961
6359
  <label>{{ field.label() }}</label>
5962
6360
  }
5963
6361
  </iss-ctrl-checkbox>
6362
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
5964
6363
  } @else {
5965
- <iss-viewer-checkbox [field]="field" />
6364
+ <iss-viewer-checkbox [field]="field"/>
5966
6365
  }
5967
6366
  }
5968
6367
  `
@@ -5998,6 +6397,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5998
6397
  }]
5999
6398
  }] });
6000
6399
 
6400
+ var toggleButton_viewer = /*#__PURE__*/Object.freeze({
6401
+ __proto__: null,
6402
+ ToggleButtonViewer: ToggleButtonViewer
6403
+ });
6404
+
6001
6405
  class ToggleButtonField extends FieldBase {
6002
6406
  onChange($event) {
6003
6407
  this.field.ctrlJsonValue.set($event);
@@ -6005,21 +6409,23 @@ class ToggleButtonField extends FieldBase {
6005
6409
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ToggleButtonField, deps: null, target: i0.ɵɵFactoryTarget.Component });
6006
6410
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: ToggleButtonField, isStandalone: true, selector: "iss-field-toggle-button", usesInheritance: true, ngImport: i0, template: `
6007
6411
  @if (field.visible()) {
6008
- @if (!(field.config.hideLabel)) {
6412
+ @if (!field.config.hideLabel()) {
6009
6413
  <mat-label>{{ field.label() }}</mat-label>
6010
6414
  }
6011
6415
  @if (field.mode() === "edit") {
6012
6416
  <iss-ctrl-toggle-button
6013
6417
  [formControlName]="field.name"
6014
- [options]="field.options()"
6015
- [multiple]="field.config.multiple()"
6418
+ [options]="field.options()"
6419
+ [multiple]="field.config.multiple()"
6420
+ [readonly]="field.config.readonly() ?? false"
6016
6421
  (onChange)="onChange($event)"
6017
6422
  />
6423
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
6018
6424
  } @else {
6019
- <iss-viewer-toggle-button [field]="field" />
6425
+ <iss-viewer-toggle-button [field]="field"/>
6020
6426
  }
6021
6427
  }
6022
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: ToggleButtonControl, selector: "iss-ctrl-toggle-button", inputs: ["options"] }, { kind: "component", type: ToggleButtonViewer, selector: "iss-viewer-toggle-button" }], viewProviders: [VIEW_PROVIDER] });
6428
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: ToggleButtonControl, selector: "iss-ctrl-toggle-button", inputs: ["options"] }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: ToggleButtonViewer, selector: "iss-viewer-toggle-button" }], viewProviders: [VIEW_PROVIDER] });
6023
6429
  }
6024
6430
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ToggleButtonField, decorators: [{
6025
6431
  type: Component,
@@ -6030,22 +6436,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6030
6436
  ReactiveFormsModule,
6031
6437
  MatLabel,
6032
6438
  ToggleButtonControl,
6439
+ ValidationMessageComponent,
6033
6440
  ToggleButtonViewer
6034
6441
  ],
6035
6442
  template: `
6036
6443
  @if (field.visible()) {
6037
- @if (!(field.config.hideLabel)) {
6444
+ @if (!field.config.hideLabel()) {
6038
6445
  <mat-label>{{ field.label() }}</mat-label>
6039
6446
  }
6040
6447
  @if (field.mode() === "edit") {
6041
6448
  <iss-ctrl-toggle-button
6042
6449
  [formControlName]="field.name"
6043
- [options]="field.options()"
6044
- [multiple]="field.config.multiple()"
6450
+ [options]="field.options()"
6451
+ [multiple]="field.config.multiple()"
6452
+ [readonly]="field.config.readonly() ?? false"
6045
6453
  (onChange)="onChange($event)"
6046
6454
  />
6455
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
6047
6456
  } @else {
6048
- <iss-viewer-toggle-button [field]="field" />
6457
+ <iss-viewer-toggle-button [field]="field"/>
6049
6458
  }
6050
6459
  }
6051
6460
  `
@@ -6058,9 +6467,13 @@ var toggleButton_field = /*#__PURE__*/Object.freeze({
6058
6467
  });
6059
6468
 
6060
6469
  class SlideToggleViewer extends ViewerBase {
6470
+ // the control writes a boolean, while a value coming from the server is a string
6471
+ isChecked(value) {
6472
+ return value === true || value === "true";
6473
+ }
6061
6474
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SlideToggleViewer, deps: null, target: i0.ɵɵFactoryTarget.Component });
6062
6475
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SlideToggleViewer, isStandalone: true, selector: "iss-viewer-slide-toggle", usesInheritance: true, ngImport: i0, template: `
6063
- @if (field().ctrlValue() === 'true') {
6476
+ @if (isChecked(field().ctrlValue())) {
6064
6477
  {{ field().label() + ' Yes' }}
6065
6478
  } @else {
6066
6479
  {{ field().label() + ' No' }}
@@ -6072,7 +6485,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6072
6485
  args: [{
6073
6486
  selector: "iss-viewer-slide-toggle",
6074
6487
  template: `
6075
- @if (field().ctrlValue() === 'true') {
6488
+ @if (isChecked(field().ctrlValue())) {
6076
6489
  {{ field().label() + ' Yes' }}
6077
6490
  } @else {
6078
6491
  {{ field().label() + ' No' }}
@@ -6081,41 +6494,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6081
6494
  }]
6082
6495
  }] });
6083
6496
 
6497
+ var slideToggle_viewer = /*#__PURE__*/Object.freeze({
6498
+ __proto__: null,
6499
+ SlideToggleViewer: SlideToggleViewer
6500
+ });
6501
+
6084
6502
  class SlideToggleField extends FieldBase {
6085
6503
  onChange($event) {
6086
- this.field.jsonValue = $event;
6504
+ this.field.ctrlJsonValue.set($event);
6087
6505
  }
6088
6506
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SlideToggleField, deps: null, target: i0.ɵɵFactoryTarget.Component });
6089
6507
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SlideToggleField, isStandalone: true, selector: "iss-field-slide-toggle", usesInheritance: true, ngImport: i0, template: `
6090
6508
  @if (field.visible()) {
6091
- @if (!(field.config.hideLabel)) {
6509
+ @if (!field.config.hideLabel()) {
6092
6510
  <mat-label>{{ field.label() }}</mat-label>
6093
6511
  }
6094
6512
 
6095
6513
  @if (field.mode() === "edit") {
6096
- <iss-ctrl-slide-toggle [formControlName]="field.name" (onChange)="onChange($event)" />
6514
+ <iss-ctrl-slide-toggle [formControlName]="field.name" [readonly]="field.config.readonly() ?? false" (onChange)="onChange($event)"/>
6515
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
6097
6516
  } @else {
6098
- <iss-viewer-slide-toggle [field]="field" />
6517
+ <iss-viewer-slide-toggle [field]="field"/>
6099
6518
  }
6100
6519
  }
6101
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: SlideToggleControl, selector: "iss-ctrl-slide-toggle" }, { kind: "component", type: SlideToggleViewer, selector: "iss-viewer-slide-toggle" }], viewProviders: [VIEW_PROVIDER] });
6520
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: SlideToggleControl, selector: "iss-ctrl-slide-toggle" }, { kind: "component", type: SlideToggleViewer, selector: "iss-viewer-slide-toggle" }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }], viewProviders: [VIEW_PROVIDER] });
6102
6521
  }
6103
6522
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SlideToggleField, decorators: [{
6104
6523
  type: Component,
6105
6524
  args: [{
6106
6525
  selector: "iss-field-slide-toggle",
6107
6526
  viewProviders: [VIEW_PROVIDER],
6108
- imports: [ReactiveFormsModule, MatLabel, SlideToggleControl, SlideToggleViewer],
6527
+ imports: [ReactiveFormsModule, MatLabel, SlideToggleControl, SlideToggleViewer, ValidationMessageComponent],
6109
6528
  template: `
6110
6529
  @if (field.visible()) {
6111
- @if (!(field.config.hideLabel)) {
6530
+ @if (!field.config.hideLabel()) {
6112
6531
  <mat-label>{{ field.label() }}</mat-label>
6113
6532
  }
6114
6533
 
6115
6534
  @if (field.mode() === "edit") {
6116
- <iss-ctrl-slide-toggle [formControlName]="field.name" (onChange)="onChange($event)" />
6535
+ <iss-ctrl-slide-toggle [formControlName]="field.name" [readonly]="field.config.readonly() ?? false" (onChange)="onChange($event)"/>
6536
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
6117
6537
  } @else {
6118
- <iss-viewer-slide-toggle [field]="field" />
6538
+ <iss-viewer-slide-toggle [field]="field"/>
6119
6539
  }
6120
6540
  }
6121
6541
  `
@@ -6299,6 +6719,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6299
6719
  `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".plate-container{width:600px;height:130px;border:4px solid #1D252D;border-radius:10px;background:#fff;display:flex;box-shadow:0 4px 10px #0003;overflow:hidden;position:relative}.section{display:flex;flex-direction:column;justify-content:center;align-items:center}.numbers-section{flex:2;padding:0 10px}.digit-row{display:flex;justify-content:space-between;width:80%;font-size:38px;line-height:1;color:#1d252d;margin:2px 0}.emblem-section{flex:1.2;padding:10px}.emblem-svg{width:100%;height:100%;max-height:90px}.letters-section{flex:1.8;border-right:2px solid #1D252D;padding:0 10px}.arabic-row{font-family:Noto Sans Arabic,sans-serif;font-size:32px;display:flex;justify-content:space-around;width:100%;margin-bottom:5px;color:#1d252d}.english-row{font-size:38px;display:flex;justify-content:space-around;width:100%;color:#1d252d}.ksa-strip{width:50px;display:flex;flex-direction:column;justify-content:space-between;align-items:center;padding:10px 0;background-color:#fff}.mini-emblem{width:25px;height:auto}.ksa-text{writing-mode:vertical-rl;text-orientation:upright;font-weight:900;font-size:16px;letter-spacing:2px;color:#1d252d;margin-top:auto;margin-bottom:5px}.plate-input{background:transparent;border:none;text-align:center;font-family:inherit;font-size:inherit;font-weight:700;color:#1d252d;width:100%;padding:0;margin:0;outline:none}.plate-input:focus{border-bottom:2px solid #1D252D}.digit-row,.arabic-row,.english-row{display:flex;justify-content:space-between;width:100%}.number-input,.letter-input{flex:1}.plate-input::placeholder{color:#ccc;opacity:1}.input-wrapper{position:relative;display:flex;flex-direction:column;align-items:center;flex:1}.error-tooltip{position:absolute;bottom:-15px;font-size:10px;color:red;white-space:nowrap}.plate-input.invalid{color:red}\n"] }]
6300
6720
  }] });
6301
6721
 
6722
+ var plate_viewer = /*#__PURE__*/Object.freeze({
6723
+ __proto__: null,
6724
+ PlateViewer: PlateViewer
6725
+ });
6726
+
6302
6727
  class PlateField extends FieldBase {
6303
6728
  updateField($event) {
6304
6729
  this.field.ctrlJsonValue.set($event);
@@ -6306,17 +6731,18 @@ class PlateField extends FieldBase {
6306
6731
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: PlateField, deps: null, target: i0.ɵɵFactoryTarget.Component });
6307
6732
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: PlateField, isStandalone: true, selector: "iss-field-plate-number", usesInheritance: true, ngImport: i0, template: `
6308
6733
  @if (field.visible()) {
6309
- @if (!(field.config.hideLabel)) {
6734
+ @if (!field.config.hideLabel()) {
6310
6735
  <mat-label>{{ field.label() }}</mat-label>
6311
6736
  }
6312
6737
 
6313
6738
  @if (field.mode() === "edit") {
6314
- <iss-ctrl-plate-number [formControlName]="field.name" (onChange)="updateField($event)" />
6739
+ <iss-ctrl-plate-number [formControlName]="field.name" [readonly]="field.config.readonly() ?? false" (onChange)="updateField($event)"/>
6740
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
6315
6741
  } @else {
6316
- <iss-viewer-plate [field]="field" />
6742
+ <iss-viewer-plate [field]="field"/>
6317
6743
  }
6318
6744
  }
6319
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: PlateControl, selector: "iss-ctrl-plate-number" }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: PlateViewer, selector: "iss-viewer-plate" }], viewProviders: [VIEW_PROVIDER] });
6745
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: PlateControl, selector: "iss-ctrl-plate-number" }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: PlateViewer, selector: "iss-viewer-plate" }], viewProviders: [VIEW_PROVIDER] });
6320
6746
  }
6321
6747
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: PlateField, decorators: [{
6322
6748
  type: Component,
@@ -6326,19 +6752,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6326
6752
  imports: [
6327
6753
  ReactiveFormsModule,
6328
6754
  PlateControl,
6755
+ ValidationMessageComponent,
6329
6756
  MatLabel,
6330
6757
  PlateViewer
6331
6758
  ],
6332
6759
  template: `
6333
6760
  @if (field.visible()) {
6334
- @if (!(field.config.hideLabel)) {
6761
+ @if (!field.config.hideLabel()) {
6335
6762
  <mat-label>{{ field.label() }}</mat-label>
6336
6763
  }
6337
6764
 
6338
6765
  @if (field.mode() === "edit") {
6339
- <iss-ctrl-plate-number [formControlName]="field.name" (onChange)="updateField($event)" />
6766
+ <iss-ctrl-plate-number [formControlName]="field.name" [readonly]="field.config.readonly() ?? false" (onChange)="updateField($event)"/>
6767
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
6340
6768
  } @else {
6341
- <iss-viewer-plate [field]="field" />
6769
+ <iss-viewer-plate [field]="field"/>
6342
6770
  }
6343
6771
  }
6344
6772
  `
@@ -6452,6 +6880,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6452
6880
  `, schemas: [CUSTOM_ELEMENTS_SCHEMA], styles: [".insideTableStyle{width:272px}.files-list{--file-item-width: 270px;display:grid;grid-template-columns:repeat(auto-fill,var(--file-item-width));grid-gap:.5rem}@media(max-width:768px){.files-list{grid-template-columns:repeat(auto-fill,minmax(var(--file-item-width),1fr))}}.mat-mdc-progress-bar{--mat-progress-bar-active-indicator-color: var(--purple);--mat-progress-bar-track-color: rgba(var(--rgb-purple), .25)}.file-item-container .file-item{--file-bg: var(--off-white);--file-radius: var(--box-radius);--icon-color: var(--purple);--icon-size: 2rem;--file-border: transparent;display:flex;align-items:center;background-color:var(--file-bg);border:1px solid var(--file-border);border-radius:var(--file-radius);padding:0 1rem;height:70px;gap:.75rem}.file-item-container .file-item.error{--file-bg: rgba(var(--rgb-red), 10%);--file-border: var(--red);--icon-color: var(--red)}.file-item-container .file-item .icon{color:var(--icon-color);font-size:var(--icon-size)}.file-item-container .file-item .file-action{display:flex;align-items:center;gap:.5rem}ds-attachments::part(base){--file-width: 250px}\n"] }]
6453
6881
  }], propDecorators: { uploading: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploading", required: false }] }], uploadingError: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadingError", required: false }] }], deleteOut: [{ type: i0.Output, args: ["deleteOut"] }] } });
6454
6882
 
6883
+ var attachment_viewer = /*#__PURE__*/Object.freeze({
6884
+ __proto__: null,
6885
+ AttachmentViewer: AttachmentViewer
6886
+ });
6887
+
6455
6888
  const ATTACHMENT_UPLOAD_URL = new InjectionToken("ATTACHMENT UPLOAD URL");
6456
6889
  const ATTACHMENT_UPLOAD_RESPONSE_MAPPER = new InjectionToken("Map backend response to required response interface.");
6457
6890
  const ATTACHMENT_DOWNLOAD_URL = new InjectionToken("Attachment download URL");
@@ -6477,9 +6910,8 @@ class FileHandler {
6477
6910
  #units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
6478
6911
  upload(data) {
6479
6912
  const url = this.ATTACHMENT_UPLOAD_URL;
6480
- return this.#http.post(url, data, {
6481
- headers: { "Content-Type": "multipart/form-data" },
6482
- }).pipe(map((res) => this.attachmentAdapter(res)));
6913
+ // no Content-Type header: the browser adds it with the FormData boundary
6914
+ return this.#http.post(url, data).pipe(map((res) => this.attachmentAdapter(res)));
6483
6915
  }
6484
6916
  formDataFactory(files, multiple) {
6485
6917
  if (files.length === 0) {
@@ -6572,17 +7004,30 @@ class FilePickerField extends FieldBase {
6572
7004
  viewField = computed(() => this.#generateViewField(this.field.ctrlJsonValue()), ...(ngDevMode ? [{ debugName: "viewField" }] : /* istanbul ignore next */ []));
6573
7005
  attachments = signal([], ...(ngDevMode ? [{ debugName: "attachments" }] : /* istanbul ignore next */ []));
6574
7006
  uploadingError = signal(undefined, ...(ngDevMode ? [{ debugName: "uploadingError" }] : /* istanbul ignore next */ []));
7007
+ accept = this.#acceptFactory();
7008
+ // the ids the picker already holds files for; without it the effect would re-download
7009
+ // the very files captureFile just uploaded, since it writes them back into ctrl
7010
+ #hydratedValue = null;
6575
7011
  #valueEffect = effect(() => {
6576
- if (this.ctrlValueChange() && this.field.value) {
6577
- this.fileHandlerService.fetchFiles(this.ctrlValueChange()).subscribe((res) => {
6578
- const filesOnly = res.map((attachment) => attachment.file);
6579
- const formDataValue = this.fileHandlerService.formDataFactory(filesOnly, !!this.field.config.multiple());
6580
- this.picker.setValue(formDataValue);
6581
- this.field.ctrlJsonValue.set(res);
6582
- });
7012
+ const value = this.ctrlValueChange();
7013
+ if (!value || !this.field.value || value === this.#hydratedValue) {
7014
+ return;
6583
7015
  }
7016
+ this.#hydratedValue = value;
7017
+ this.fileHandlerService.fetchFiles(value)
7018
+ .pipe(takeUntilDestroyed(this.destroyRef))
7019
+ .subscribe((res) => {
7020
+ const filesOnly = res.map((attachment) => attachment.file);
7021
+ const formDataValue = this.fileHandlerService.formDataFactory(filesOnly, !!this.field.config.multiple());
7022
+ this.attachments.set(res);
7023
+ this.picker.setValue(formDataValue);
7024
+ this.field.ctrlJsonValue.set(res);
7025
+ });
6584
7026
  }, ...(ngDevMode ? [{ debugName: "#valueEffect" }] : /* istanbul ignore next */ []));
6585
7027
  captureFile(formData) {
7028
+ if (!formData) {
7029
+ return;
7030
+ }
6586
7031
  this.uploading.set(true);
6587
7032
  const notUploadedFiles = this.#getNotUploadedFiles(formData);
6588
7033
  if (this.fileHandlerService.formDataHasValue(notUploadedFiles)) {
@@ -6592,9 +7037,16 @@ class FilePickerField extends FieldBase {
6592
7037
  console.error(err);
6593
7038
  return EMPTY;
6594
7039
  })).subscribe((res) => {
6595
- this.attachments.set(res);
7040
+ this.attachments.update((attachments) => {
7041
+ if (!this.field.config.multiple()) {
7042
+ return res;
7043
+ }
7044
+ return [...attachments, ...res];
7045
+ });
6596
7046
  const stringifiedValue = this.stringifyIssAttachment(this.attachments());
7047
+ this.#hydratedValue = stringifiedValue;
6597
7048
  this.ctrl.setValue(stringifiedValue);
7049
+ this.field.ctrlJsonValue.set(this.attachments());
6598
7050
  this.uploading.set(false);
6599
7051
  });
6600
7052
  }
@@ -6609,6 +7061,20 @@ class FilePickerField extends FieldBase {
6609
7061
  super.ngOnDestroy();
6610
7062
  this.#valueEffect.destroy();
6611
7063
  }
7064
+ // the outlet rewrites every validation entry into {fn, value}, so the extensions live under value
7065
+ #acceptFactory() {
7066
+ const fileType = this.field.validation?.["fileType"];
7067
+ const extensions = fileType && fileType.hasOwnProperty("fn") ? fileType.value : fileType;
7068
+ if (!extensions) {
7069
+ return "";
7070
+ }
7071
+ const list = Array.isArray(extensions) ? extensions : String(extensions).split(",");
7072
+ return list
7073
+ .map((extension) => extension.trim())
7074
+ .filter((extension) => !!extension)
7075
+ .map((extension) => extension.startsWith(".") ? extension : `.${extension}`)
7076
+ .join(",");
7077
+ }
6612
7078
  #generateViewField(value) {
6613
7079
  return {
6614
7080
  config: {
@@ -6639,16 +7105,17 @@ class FilePickerField extends FieldBase {
6639
7105
  <!-- [showDeleteButton]="field.config.showDeleteButton" [showDownloadButton]="field.config.showDownloadButton"-->
6640
7106
  <iss-ctrl-file-picker
6641
7107
  [formControl]="picker"
6642
- [accept]="field.validation.fileType"
7108
+ [accept]="accept"
6643
7109
  (onChange)="captureFile($event)"
6644
- [multiple]="field.config.multiple()"
7110
+ [multiple]="field.config.multiple()"
7111
+ [readonly]="field.config.readonly() ?? false"
6645
7112
  [uploading]="uploading()"
6646
7113
  [uploadedFiles]="attachments()"
6647
7114
  [uploadingError]="uploadingError()"
6648
7115
  />
6649
- <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="ctrl.touched" />
7116
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="ctrl.touched"/>
6650
7117
  } @else {
6651
- <iss-viewer-attachment [uploading]="uploading()" [field]="viewField()" />
7118
+ <iss-viewer-attachment [uploading]="uploading()" [field]="viewField()"/>
6652
7119
  }
6653
7120
  }
6654
7121
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: FilePickerControl, selector: "iss-ctrl-file-picker", inputs: ["accept", "showDownloadButton", "showDeleteButton", "uploading", "uploadedFiles", "uploadingError"] }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: AttachmentViewer, selector: "iss-viewer-attachment", inputs: ["uploading", "uploadingError"], outputs: ["deleteOut"] }, { kind: "directive", type: MatLabel$1, selector: "mat-label" }], viewProviders: [VIEW_PROVIDER] });
@@ -6673,16 +7140,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6673
7140
  <!-- [showDeleteButton]="field.config.showDeleteButton" [showDownloadButton]="field.config.showDownloadButton"-->
6674
7141
  <iss-ctrl-file-picker
6675
7142
  [formControl]="picker"
6676
- [accept]="field.validation.fileType"
7143
+ [accept]="accept"
6677
7144
  (onChange)="captureFile($event)"
6678
- [multiple]="field.config.multiple()"
7145
+ [multiple]="field.config.multiple()"
7146
+ [readonly]="field.config.readonly() ?? false"
6679
7147
  [uploading]="uploading()"
6680
7148
  [uploadedFiles]="attachments()"
6681
7149
  [uploadingError]="uploadingError()"
6682
7150
  />
6683
- <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="ctrl.touched" />
7151
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="ctrl.touched"/>
6684
7152
  } @else {
6685
- <iss-viewer-attachment [uploading]="uploading()" [field]="viewField()" />
7153
+ <iss-viewer-attachment [uploading]="uploading()" [field]="viewField()"/>
6686
7154
  }
6687
7155
  }
6688
7156
  `
@@ -6710,16 +7178,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6710
7178
  }]
6711
7179
  }] });
6712
7180
 
7181
+ var timePicker_viewer = /*#__PURE__*/Object.freeze({
7182
+ __proto__: null,
7183
+ TimePickerViewer: TimePickerViewer
7184
+ });
7185
+
6713
7186
  class TimePickerField extends FieldBase {
6714
7187
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TimePickerField, deps: null, target: i0.ɵɵFactoryTarget.Component });
6715
7188
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: TimePickerField, isStandalone: true, selector: "iss-field-time-picker", usesInheritance: true, ngImport: i0, template: `
6716
7189
  @if (field.visible()) {
6717
7190
  @if (field.mode() === "edit") {
6718
- <iss-ctrl-time-picker [formControlName]="field.name">
7191
+ <iss-ctrl-time-picker [formControlName]="field.name" [readonly]="field.config.readonly() ?? false">
6719
7192
  <label>{{ field.label() }}</label>
6720
7193
  </iss-ctrl-time-picker>
6721
7194
  } @else {
6722
- <iss-viewer-time-picker [field]="field" />
7195
+ <iss-viewer-time-picker [field]="field"/>
6723
7196
  }
6724
7197
  }
6725
7198
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: TimePickerControl, selector: "iss-ctrl-time-picker", inputs: ["interval", "minTime", "maxTime"] }, { kind: "component", type: TimePickerViewer, selector: "iss-viewer-time-picker" }], viewProviders: [VIEW_PROVIDER] });
@@ -6737,11 +7210,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6737
7210
  template: `
6738
7211
  @if (field.visible()) {
6739
7212
  @if (field.mode() === "edit") {
6740
- <iss-ctrl-time-picker [formControlName]="field.name">
7213
+ <iss-ctrl-time-picker [formControlName]="field.name" [readonly]="field.config.readonly() ?? false">
6741
7214
  <label>{{ field.label() }}</label>
6742
7215
  </iss-ctrl-time-picker>
6743
7216
  } @else {
6744
- <iss-viewer-time-picker [field]="field" />
7217
+ <iss-viewer-time-picker [field]="field"/>
6745
7218
  }
6746
7219
  }
6747
7220
  `
@@ -6772,19 +7245,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6772
7245
  }]
6773
7246
  }] });
6774
7247
 
7248
+ var datePicker_viewer = /*#__PURE__*/Object.freeze({
7249
+ __proto__: null,
7250
+ DatePickerViewer: DatePickerViewer
7251
+ });
7252
+
6775
7253
  class DatePickerField extends FieldBase {
6776
7254
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatePickerField, deps: null, target: i0.ɵɵFactoryTarget.Component });
6777
7255
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DatePickerField, isStandalone: true, selector: "iss-field-date-picker", usesInheritance: true, ngImport: i0, template: `
6778
7256
  @if (field.visible()) {
6779
7257
  @if (field.mode() === "edit") {
6780
- <iss-ctrl-date-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null">
7258
+ <iss-ctrl-date-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null" [readonly]="field.config.readonly() ?? false">
6781
7259
  <label>{{ field.label() }}</label>
6782
7260
  </iss-ctrl-date-picker>
7261
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
6783
7262
  } @else {
6784
- <iss-viewer-date-picker [field]="field" />
7263
+ <iss-viewer-date-picker [field]="field"/>
6785
7264
  }
6786
7265
  }
6787
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: DatePickerControl, selector: "iss-ctrl-date-picker", inputs: ["disabledDays", "minDate", "maxDate"] }, { kind: "component", type: DatePickerViewer, selector: "iss-viewer-date-picker" }], viewProviders: [VIEW_PROVIDER] });
7266
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: DatePickerControl, selector: "iss-ctrl-date-picker", inputs: ["disabledDays", "minDate", "maxDate"] }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: DatePickerViewer, selector: "iss-viewer-date-picker" }], viewProviders: [VIEW_PROVIDER] });
6788
7267
  }
6789
7268
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatePickerField, decorators: [{
6790
7269
  type: Component,
@@ -6794,16 +7273,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6794
7273
  imports: [
6795
7274
  ReactiveFormsModule,
6796
7275
  DatePickerControl,
7276
+ ValidationMessageComponent,
6797
7277
  DatePickerViewer
6798
7278
  ],
6799
7279
  template: `
6800
7280
  @if (field.visible()) {
6801
7281
  @if (field.mode() === "edit") {
6802
- <iss-ctrl-date-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null">
7282
+ <iss-ctrl-date-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null" [readonly]="field.config.readonly() ?? false">
6803
7283
  <label>{{ field.label() }}</label>
6804
7284
  </iss-ctrl-date-picker>
7285
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
6805
7286
  } @else {
6806
- <iss-viewer-date-picker [field]="field" />
7287
+ <iss-viewer-date-picker [field]="field"/>
6807
7288
  }
6808
7289
  }
6809
7290
  `
@@ -6835,17 +7316,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6835
7316
  }]
6836
7317
  }] });
6837
7318
 
7319
+ var datetimePicker_viewer = /*#__PURE__*/Object.freeze({
7320
+ __proto__: null,
7321
+ DatePickerTimeViewer: DatePickerTimeViewer
7322
+ });
7323
+
6838
7324
  class DatetimePickerField extends FieldBase {
6839
7325
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatetimePickerField, deps: null, target: i0.ɵɵFactoryTarget.Component });
6840
7326
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DatetimePickerField, isStandalone: true, selector: "iss-field-datetime-picker", usesInheritance: true, ngImport: i0, template: `
6841
7327
  @if (field.visible()) {
6842
7328
  @if (field.mode() === "edit") {
6843
- <iss-ctrl-datetime-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null">
7329
+ <iss-ctrl-datetime-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null" [readonly]="field.config.readonly() ?? false">
6844
7330
  <label>{{ field.label() }}</label>
6845
7331
  </iss-ctrl-datetime-picker>
6846
- <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched" />
7332
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
6847
7333
  } @else {
6848
- <iss-viewer-datetime-picker [field]="field" />
7334
+ <iss-viewer-datetime-picker [field]="field"/>
6849
7335
  }
6850
7336
  }
6851
7337
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: DatetimePickerControl, selector: "iss-ctrl-datetime-picker", inputs: ["disabledDays", "minDate", "maxDate", "minTime", "maxTime"] }, { kind: "component", type: ValidationMessageComponent, selector: "iss-validation-message-viewer", inputs: ["errors", "showMessage"] }, { kind: "component", type: DatePickerTimeViewer, selector: "iss-viewer-datetime-picker" }], viewProviders: [VIEW_PROVIDER] });
@@ -6864,12 +7350,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6864
7350
  template: `
6865
7351
  @if (field.visible()) {
6866
7352
  @if (field.mode() === "edit") {
6867
- <iss-ctrl-datetime-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null">
7353
+ <iss-ctrl-datetime-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null" [readonly]="field.config.readonly() ?? false">
6868
7354
  <label>{{ field.label() }}</label>
6869
7355
  </iss-ctrl-datetime-picker>
6870
- <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched" />
7356
+ <iss-validation-message-viewer [errors]="ctrl.errors" [showMessage]="!ctrl.untouched"/>
6871
7357
  } @else {
6872
- <iss-viewer-datetime-picker [field]="field" />
7358
+ <iss-viewer-datetime-picker [field]="field"/>
6873
7359
  }
6874
7360
  }
6875
7361
  `
@@ -6908,16 +7394,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6908
7394
  }]
6909
7395
  }] });
6910
7396
 
7397
+ var dateRangePicker_viewer = /*#__PURE__*/Object.freeze({
7398
+ __proto__: null,
7399
+ DateRangePickerViewer: DateRangePickerViewer
7400
+ });
7401
+
6911
7402
  class DateRangePickerField extends FieldBase {
6912
7403
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DateRangePickerField, deps: null, target: i0.ɵɵFactoryTarget.Component });
6913
7404
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DateRangePickerField, isStandalone: true, selector: "iss-field-date-range-picker", usesInheritance: true, ngImport: i0, template: `
6914
7405
  @if (field.visible()) {
6915
7406
  @if (field.mode() === "edit") {
6916
- <iss-ctrl-date-range-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null">
7407
+ <iss-ctrl-date-range-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null" [readonly]="field.config.readonly() ?? false">
6917
7408
  <label>{{ field.label() }}</label>
6918
7409
  </iss-ctrl-date-range-picker>
6919
7410
  } @else {
6920
- <iss-viewer-date-range-picker [field]="field" />
7411
+ <iss-viewer-date-range-picker [field]="field"/>
6921
7412
  }
6922
7413
  }
6923
7414
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: DateRangePickerControl, selector: "iss-ctrl-date-range-picker", inputs: ["disabledDays", "minDate", "maxDate"], outputs: ["onDateRangeObjectChange"] }, { kind: "component", type: DateRangePickerViewer, selector: "iss-viewer-date-range-picker" }], viewProviders: [VIEW_PROVIDER] });
@@ -6935,11 +7426,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6935
7426
  template: `
6936
7427
  @if (field.visible()) {
6937
7428
  @if (field.mode() === "edit") {
6938
- <iss-ctrl-date-range-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null">
7429
+ <iss-ctrl-date-range-picker [formControlName]="field.name" [minDate]="field.config.minDate() ?? null" [maxDate]="field.config.maxDate() ?? null" [readonly]="field.config.readonly() ?? false">
6939
7430
  <label>{{ field.label() }}</label>
6940
7431
  </iss-ctrl-date-range-picker>
6941
7432
  } @else {
6942
- <iss-viewer-date-range-picker [field]="field" />
7433
+ <iss-viewer-date-range-picker [field]="field"/>
6943
7434
  }
6944
7435
  }
6945
7436
  `
@@ -7265,5 +7756,5 @@ var time_validator = /*#__PURE__*/Object.freeze({
7265
7756
  * Generated bundle index. Do not edit.
7266
7757
  */
7267
7758
 
7268
- export { PlateViewer as $, FilePickerField as A, TimePickerField as B, CheckboxControl as C, DatePickerControl as D, Employee as E, FieldBase as F, DatePickerField as G, HOST as H, IssField as I, DatetimePickerField as J, DateRangePickerField as K, Loader as L, MobileControl as M, InputViewer as N, Outlet as O, PLACEHOLDER_REGEX as P, TextareaViewer as Q, RadioControl as R, SelectViewer as S, TextareaControl as T, ChipsViewer as U, VIEW_PROVIDER as V, RadioViewer as W, MobileViewer as X, CheckboxViewer as Y, ToggleButtonViewer as Z, SlideToggleViewer as _, ViewerBase as a, AttachmentViewer as a0, TimePickerViewer as a1, DatePickerViewer as a2, DatePickerTimeViewer as a3, DateRangePickerViewer as a4, DEFAULT_COUNTRY as a5, COUNTRIES as a6, DATE_FORMAT_ADAPTER as a7, DATETIME_FORMAT as a8, PLATE_LETTERS as a9, VALIDATION_MESSAGES as aa, COMPONENTS as ab, SERVICES as ac, VALIDATORS as ad, FIELD_DATA as ae, UI_LANGUAGE as af, ATTACHMENT_UPLOAD_URL as ag, ATTACHMENT_UPLOAD_RESPONSE_MAPPER as ah, ATTACHMENT_DOWNLOAD_URL as ai, USER_SEARCH_API as aj, HintsComponent as ak, Droppable as al, ArabicNumberPipe as am, FieldDataInjector as an, PlateLetterPipe as ao, SelectStatusPipe as ap, TranslatePipe as aq, FileHandler as ar, Phone as as, arrayFrom as at, formatFileSize as au, IssValidators as av, FileValidators as aw, TimeValidator as ax, FormEngine as b, SelectControl as c, ValidationMessageComponent as d, ChipsControl as e, DateRangePickerControl as f, getPropertyByPath as g, DatetimePickerControl as h, FilePickerControl as i, InputControl as j, PlateControl as k, SlideToggleControl as l, TimePickerControl as m, ToggleButtonControl as n, ControlBase as o, InputField as p, TextareaField as q, replacePlaceholders as r, ChipsField as s, RadioField as t, SelectField as u, MobileField as v, CheckboxField as w, ToggleButtonField as x, SlideToggleField as y, PlateField as z };
7269
- //# sourceMappingURL=iss-ctrl-iss-ctrl-BLjj_Aqu.mjs.map
7759
+ export { PlateViewer as $, TimePickerField as A, DatePickerField as B, ControlBase as C, DatePickerControl as D, Employee as E, FieldBase as F, DatetimePickerField as G, HOST as H, IssField as I, DateRangePickerField as J, InputViewer as K, Loader as L, MobileControl as M, TextareaViewer as N, Outlet as O, PLACEHOLDER_REGEX as P, ChipsViewer as Q, RadioControl as R, SelectControl as S, TextareaControl as T, RadioViewer as U, VIEW_PROVIDER as V, SelectViewer as W, MobileViewer as X, CheckboxViewer as Y, ToggleButtonViewer as Z, SlideToggleViewer as _, ViewerBase as a, AttachmentViewer as a0, TimePickerViewer as a1, DatePickerViewer as a2, DatePickerTimeViewer as a3, DateRangePickerViewer as a4, DEFAULT_COUNTRY as a5, COUNTRIES as a6, DATE_FORMAT_ADAPTER as a7, DATETIME_FORMAT as a8, PLATE_LETTERS as a9, VALIDATION_MESSAGES as aa, COMPONENTS as ab, SERVICES as ac, VALIDATORS as ad, FIELD_DATA as ae, UI_LANGUAGE as af, ATTACHMENT_UPLOAD_URL as ag, ATTACHMENT_UPLOAD_RESPONSE_MAPPER as ah, ATTACHMENT_DOWNLOAD_URL as ai, USER_SEARCH_API as aj, HintsComponent as ak, Droppable as al, ArabicNumberPipe as am, FieldDataInjector as an, PlateLetterPipe as ao, SelectStatusPipe as ap, TranslatePipe as aq, FileHandler as ar, Phone as as, arrayFrom as at, formatFileSize as au, IssValidators as av, FileValidators as aw, TimeValidator as ax, FormEngine as b, ValidationMessageComponent as c, CheckboxControl as d, ChipsControl as e, DateRangePickerControl as f, getPropertyByPath as g, DatetimePickerControl as h, FilePickerControl as i, InputControl as j, PlateControl as k, SlideToggleControl as l, TimePickerControl as m, ToggleButtonControl as n, InputField as o, TextareaField as p, ChipsField as q, replacePlaceholders as r, RadioField as s, SelectField as t, MobileField as u, CheckboxField as v, ToggleButtonField as w, SlideToggleField as x, PlateField as y, FilePickerField as z };
7760
+ //# sourceMappingURL=iss-ctrl-iss-ctrl-hz0ibyPg.mjs.map