@fovestta2/web-angular 1.0.7 → 1.0.11

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 (24) hide show
  1. package/esm2022/lib/add-update-form/add-update-form.component.mjs +273 -132
  2. package/esm2022/lib/fv-date-field/fv-date-field.component.mjs +4 -6
  3. package/esm2022/lib/fv-dropdown/fv-dropdown.component.mjs +7 -6
  4. package/esm2022/lib/fv-email-field/fv-email-field.component.mjs +2 -5
  5. package/esm2022/lib/fv-entry-field/fv-entry-field.component.mjs +2 -4
  6. package/esm2022/lib/fv-file-selector/fv-file-selector.component.mjs +4 -4
  7. package/esm2022/lib/fv-iban-field/fv-iban-field.component.mjs +4 -3
  8. package/esm2022/lib/fv-image-selector/fv-image-selector.component.mjs +128 -33
  9. package/esm2022/lib/fv-micr-field/fv-micr-field.component.mjs +4 -3
  10. package/esm2022/lib/fv-month-year-field/fv-month-year-field.component.mjs +17 -9
  11. package/esm2022/lib/fv-name-code/fv-name-code.component.mjs +3 -3
  12. package/esm2022/lib/fv-number-field/fv-number-field.component.mjs +4 -6
  13. package/esm2022/lib/fv-password-field/fv-password-field.component.mjs +2 -4
  14. package/esm2022/lib/fv-phone-field/fv-phone-field.component.mjs +15 -6
  15. package/esm2022/lib/fv-radio-group/fv-radio-group.component.mjs +3 -3
  16. package/esm2022/lib/fv-rich-text-editor/fv-rich-text-editor.component.mjs +4 -6
  17. package/esm2022/lib/fv-service-period/fv-service-period.component.mjs +110 -0
  18. package/fesm2022/fovestta2-web-angular.mjs +562 -216
  19. package/fesm2022/fovestta2-web-angular.mjs.map +1 -1
  20. package/lib/add-update-form/add-update-form.component.d.ts +16 -4
  21. package/lib/fv-image-selector/fv-image-selector.component.d.ts +24 -0
  22. package/lib/fv-phone-field/fv-phone-field.component.d.ts +1 -0
  23. package/lib/fv-service-period/fv-service-period.component.d.ts +21 -0
  24. package/package.json +1 -1
@@ -7,6 +7,7 @@ import { ReactiveFormsModule, FormControl, NG_VALUE_ACCESSOR, FormsModule, Valid
7
7
  import { Validator } from '@fovestta2/validation-engine';
8
8
  import * as i1$1 from '@angular/platform-browser';
9
9
  import { distinctUntilChanged } from 'rxjs/operators';
10
+ import { Subscription } from 'rxjs';
10
11
 
11
12
  class FvEntryFieldComponent {
12
13
  label = '';
@@ -38,9 +39,7 @@ class FvEntryFieldComponent {
38
39
  this.valueChange.emit(value);
39
40
  });
40
41
  // Validate initial value
41
- if (this.control.value) {
42
- this.validateValue(this.control.value);
43
- }
42
+ this.validateValue(this.control.value);
44
43
  }
45
44
  ngOnDestroy() {
46
45
  this.subscription?.unsubscribe();
@@ -170,9 +169,7 @@ class FvDateFieldComponent {
170
169
  this.valueChange.emit(value);
171
170
  });
172
171
  // Validate initial value
173
- if (this.control.value) {
174
- this.validateValue(this.control.value);
175
- }
172
+ this.validateValue(this.control.value);
176
173
  }
177
174
  ngOnDestroy() {
178
175
  this.subscription?.unsubscribe();
@@ -244,11 +241,11 @@ class FvDateFieldComponent {
244
241
  return errorMessages[this.errorMessage] || this.errorMessage;
245
242
  }
246
243
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvDateFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
247
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvDateFieldComponent, isStandalone: true, selector: "fv-date-field", inputs: { label: "label", schema: "schema", control: "control", disabled: "disabled", readonly: "readonly", min: "min", max: "max" }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }} <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <input type=\"date\" [formControl]=\"control\" class=\"fv-input\" [class.error]=\"!!errorMessage\" [attr.min]=\"min\"\r\n [attr.max]=\"max\" [readonly]=\"readonly\" (blur)=\"onBlur($event)\" (focus)=\"onFocus($event)\" />\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-error-message\">\r\n {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-date-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48;display:flex;align-items:center;gap:4px}.fv-required-asterisk{color:#e74c3c;font-weight:700}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;transition:all .2s ease-in-out;background-color:#fff;color:#1f2b41;box-sizing:border-box;width:100%;height:34px;min-width:0}.fv-input:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input:hover:not(:disabled):not(:focus){border-color:#3498db}.fv-input-error{border-color:#dc3545!important}.fv-input-error:focus{box-shadow:0 0 0 3px #dc35451a}.fv-input-disabled{background-color:#ecf0f1;cursor:not-allowed;opacity:.6}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c;display:flex;align-items:center;gap:4px}.fv-error-message:before{content:\"\\26a0\";font-size:14px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
244
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvDateFieldComponent, isStandalone: true, selector: "fv-date-field", inputs: { label: "label", schema: "schema", control: "control", disabled: "disabled", readonly: "readonly", min: "min", max: "max" }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }} <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <input type=\"date\" [formControl]=\"control\" class=\"fv-input\" [class.error]=\"errorMessage && control?.touched\"\r\n [attr.min]=\"min\" [attr.max]=\"max\" [readonly]=\"readonly\" (blur)=\"onBlur($event)\" (focus)=\"onFocus($event)\" />\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">\r\n {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-date-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48;display:flex;align-items:center;gap:4px}.fv-required-asterisk{color:#e74c3c;font-weight:700}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;transition:all .2s ease-in-out;background-color:#fff;color:#1f2b41;box-sizing:border-box;width:100%;height:34px;min-width:0}.fv-input:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input:hover:not(:disabled):not(:focus){border-color:#3498db}.fv-input-error{border-color:#dc3545!important}.fv-input-error:focus{box-shadow:0 0 0 3px #dc35451a}.fv-input-disabled{background-color:#ecf0f1;cursor:not-allowed;opacity:.6}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c;display:flex;align-items:center;gap:4px}.fv-error-message:before{content:\"\\26a0\";font-size:14px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
248
245
  }
249
246
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvDateFieldComponent, decorators: [{
250
247
  type: Component,
251
- args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-date-field', template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }} <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <input type=\"date\" [formControl]=\"control\" class=\"fv-input\" [class.error]=\"!!errorMessage\" [attr.min]=\"min\"\r\n [attr.max]=\"max\" [readonly]=\"readonly\" (blur)=\"onBlur($event)\" (focus)=\"onFocus($event)\" />\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-error-message\">\r\n {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-date-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48;display:flex;align-items:center;gap:4px}.fv-required-asterisk{color:#e74c3c;font-weight:700}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;transition:all .2s ease-in-out;background-color:#fff;color:#1f2b41;box-sizing:border-box;width:100%;height:34px;min-width:0}.fv-input:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input:hover:not(:disabled):not(:focus){border-color:#3498db}.fv-input-error{border-color:#dc3545!important}.fv-input-error:focus{box-shadow:0 0 0 3px #dc35451a}.fv-input-disabled{background-color:#ecf0f1;cursor:not-allowed;opacity:.6}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c;display:flex;align-items:center;gap:4px}.fv-error-message:before{content:\"\\26a0\";font-size:14px}\n"] }]
248
+ args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-date-field', template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }} <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <input type=\"date\" [formControl]=\"control\" class=\"fv-input\" [class.error]=\"errorMessage && control?.touched\"\r\n [attr.min]=\"min\" [attr.max]=\"max\" [readonly]=\"readonly\" (blur)=\"onBlur($event)\" (focus)=\"onFocus($event)\" />\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">\r\n {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-date-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48;display:flex;align-items:center;gap:4px}.fv-required-asterisk{color:#e74c3c;font-weight:700}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;transition:all .2s ease-in-out;background-color:#fff;color:#1f2b41;box-sizing:border-box;width:100%;height:34px;min-width:0}.fv-input:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input:hover:not(:disabled):not(:focus){border-color:#3498db}.fv-input-error{border-color:#dc3545!important}.fv-input-error:focus{box-shadow:0 0 0 3px #dc35451a}.fv-input-disabled{background-color:#ecf0f1;cursor:not-allowed;opacity:.6}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c;display:flex;align-items:center;gap:4px}.fv-error-message:before{content:\"\\26a0\";font-size:14px}\n"] }]
252
249
  }], propDecorators: { label: [{
253
250
  type: Input
254
251
  }], schema: [{
@@ -296,10 +293,8 @@ class FvMonthYearFieldComponent {
296
293
  this.updateDisplayValue(value);
297
294
  this.valueChange.emit(value);
298
295
  });
299
- if (this.control.value) {
300
- this.validateValue(this.control.value);
301
- this.updateDisplayValue(this.control.value);
302
- }
296
+ this.validateValue(this.control.value);
297
+ this.updateDisplayValue(this.control.value);
303
298
  }
304
299
  ngOnDestroy() {
305
300
  this.subscription?.unsubscribe();
@@ -363,7 +358,7 @@ class FvMonthYearFieldComponent {
363
358
  if (!this.errorMessage)
364
359
  return '';
365
360
  const errorMessages = {
366
- ERR_REQUIRED: 'Month/Year is required',
361
+ ERR_REQUIRED: 'This field is required',
367
362
  ERR_MIN_DATE: `Date must be after ${this.min}`,
368
363
  ERR_MAX_DATE: `Date must be before ${this.max}`,
369
364
  };
@@ -385,19 +380,29 @@ class FvMonthYearFieldComponent {
385
380
  openPicker(picker) {
386
381
  if (this.disabled || this.readonly)
387
382
  return;
383
+ // Force showPicker programmatically
384
+ // This is the primary way we ensure the picker opens even if the user
385
+ // clicked a "dead zone" of the input.
388
386
  if (picker.showPicker) {
389
- picker.showPicker();
387
+ try {
388
+ picker.showPicker();
389
+ }
390
+ catch (error) {
391
+ // Ignore errors (e.g. if already open or transient state)
392
+ // Fallback to click if needed, though usually showPicker is best
393
+ // picker.click();
394
+ }
390
395
  }
391
396
  else {
392
397
  picker.click();
393
398
  }
394
399
  }
395
400
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvMonthYearFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
396
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvMonthYearFieldComponent, isStandalone: true, selector: "fv-month-year-field", inputs: { label: "label", schema: "schema", control: "control", disabled: "disabled", readonly: "readonly", min: "min", max: "max" }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }} <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-input-wrapper\" (click)=\"openPicker(monthPicker)\">\r\n <input type=\"text\" [value]=\"displayValue\" class=\"fv-input\" [class.error]=\"!!errorMessage\"\r\n placeholder=\"Select Month\" readonly />\r\n <input #monthPicker type=\"month\" [formControl]=\"control\" class=\"fv-hidden-picker\" [attr.min]=\"min\"\r\n [attr.max]=\"max\" (blur)=\"onBlur($event)\" (focus)=\"onFocus($event)\" />\r\n <span class=\"fv-calendar-icon\">\r\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"></rect>\r\n <line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"></line>\r\n <line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"></line>\r\n <line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"></line>\r\n </svg>\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-error-message\">\r\n {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:4px;font-weight:700}.fv-input{padding:5px 35px 5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px;min-width:0;cursor:pointer}.fv-input-wrapper{position:relative;width:100%;cursor:pointer}.fv-hidden-picker{position:absolute;opacity:0;width:0;height:0;pointer-events:none}.fv-calendar-icon{position:absolute;right:10px;top:50%;transform:translateY(-50%);color:#666;pointer-events:none;display:flex;align-items:center}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input.error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
401
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvMonthYearFieldComponent, isStandalone: true, selector: "fv-month-year-field", inputs: { label: "label", schema: "schema", control: "control", disabled: "disabled", readonly: "readonly", min: "min", max: "max" }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }} <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-input-wrapper\" (click)=\"openPicker(monthPicker)\">\r\n <input type=\"text\" [value]=\"displayValue\" class=\"fv-input\" [class.error]=\"errorMessage && control?.touched\"\r\n placeholder=\"Select Month\" readonly tabindex=\"-1\" />\r\n <span class=\"fv-calendar-icon\">\r\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"></rect>\r\n <line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"></line>\r\n <line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"></line>\r\n <line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"></line>\r\n </svg>\r\n </span>\r\n <input #monthPicker type=\"month\" [formControl]=\"control\" class=\"fv-hidden-picker\" [attr.min]=\"min\"\r\n [attr.max]=\"max\" [readonly]=\"readonly\" (blur)=\"onBlur($event)\" (focus)=\"onFocus($event)\" />\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%;position:relative;isolation:isolate}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:4px;font-weight:700}.fv-input{padding:5px 35px 5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px;min-width:0;cursor:pointer}.fv-input-wrapper{position:relative;width:100%;cursor:pointer}.fv-hidden-picker{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;cursor:pointer;z-index:100;pointer-events:auto;padding:0;margin:0;appearance:none;-webkit-appearance:none;border:none}.fv-hidden-picker::-webkit-calendar-picker-indicator{position:absolute;top:0;left:0;width:100%;height:100%;padding:0;margin:0;opacity:0;cursor:pointer}.fv-calendar-icon{position:absolute;right:10px;top:50%;transform:translateY(-50%);color:#666;pointer-events:none;display:flex;align-items:center;z-index:1}.fv-input:focus,.fv-input-wrapper:focus-within .fv-input{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input.error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
397
402
  }
398
403
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvMonthYearFieldComponent, decorators: [{
399
404
  type: Component,
400
- args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-month-year-field', template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }} <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-input-wrapper\" (click)=\"openPicker(monthPicker)\">\r\n <input type=\"text\" [value]=\"displayValue\" class=\"fv-input\" [class.error]=\"!!errorMessage\"\r\n placeholder=\"Select Month\" readonly />\r\n <input #monthPicker type=\"month\" [formControl]=\"control\" class=\"fv-hidden-picker\" [attr.min]=\"min\"\r\n [attr.max]=\"max\" (blur)=\"onBlur($event)\" (focus)=\"onFocus($event)\" />\r\n <span class=\"fv-calendar-icon\">\r\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"></rect>\r\n <line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"></line>\r\n <line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"></line>\r\n <line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"></line>\r\n </svg>\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-error-message\">\r\n {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:4px;font-weight:700}.fv-input{padding:5px 35px 5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px;min-width:0;cursor:pointer}.fv-input-wrapper{position:relative;width:100%;cursor:pointer}.fv-hidden-picker{position:absolute;opacity:0;width:0;height:0;pointer-events:none}.fv-calendar-icon{position:absolute;right:10px;top:50%;transform:translateY(-50%);color:#666;pointer-events:none;display:flex;align-items:center}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input.error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
405
+ args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-month-year-field', template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }} <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-input-wrapper\" (click)=\"openPicker(monthPicker)\">\r\n <input type=\"text\" [value]=\"displayValue\" class=\"fv-input\" [class.error]=\"errorMessage && control?.touched\"\r\n placeholder=\"Select Month\" readonly tabindex=\"-1\" />\r\n <span class=\"fv-calendar-icon\">\r\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"></rect>\r\n <line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"></line>\r\n <line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"></line>\r\n <line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"></line>\r\n </svg>\r\n </span>\r\n <input #monthPicker type=\"month\" [formControl]=\"control\" class=\"fv-hidden-picker\" [attr.min]=\"min\"\r\n [attr.max]=\"max\" [readonly]=\"readonly\" (blur)=\"onBlur($event)\" (focus)=\"onFocus($event)\" />\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%;position:relative;isolation:isolate}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:4px;font-weight:700}.fv-input{padding:5px 35px 5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px;min-width:0;cursor:pointer}.fv-input-wrapper{position:relative;width:100%;cursor:pointer}.fv-hidden-picker{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;cursor:pointer;z-index:100;pointer-events:auto;padding:0;margin:0;appearance:none;-webkit-appearance:none;border:none}.fv-hidden-picker::-webkit-calendar-picker-indicator{position:absolute;top:0;left:0;width:100%;height:100%;padding:0;margin:0;opacity:0;cursor:pointer}.fv-calendar-icon{position:absolute;right:10px;top:50%;transform:translateY(-50%);color:#666;pointer-events:none;display:flex;align-items:center;z-index:1}.fv-input:focus,.fv-input-wrapper:focus-within .fv-input{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input.error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
401
406
  }], propDecorators: { label: [{
402
407
  type: Input
403
408
  }], schema: [{
@@ -448,9 +453,7 @@ class FvNumberFieldComponent {
448
453
  this.validateValue(value);
449
454
  this.valueChange.emit(value);
450
455
  });
451
- if (this.control.value) {
452
- this.validateValue(this.control.value);
453
- }
456
+ this.validateValue(this.control.value);
454
457
  }
455
458
  ngOnDestroy() {
456
459
  this.subscription?.unsubscribe();
@@ -500,11 +503,11 @@ class FvNumberFieldComponent {
500
503
  return errorMessages[this.errorMessage] || this.errorMessage;
501
504
  }
502
505
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvNumberFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
503
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvNumberFieldComponent, isStandalone: true, selector: "fv-number-field", inputs: { label: "label", placeholder: "placeholder", schema: "schema", control: "control", disabled: "disabled", readonly: "readonly", min: "min", max: "max", step: "step" }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-number-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <input type=\"number\" [formControl]=\"control\" [placeholder]=\"placeholder\" [readonly]=\"readonly\" [min]=\"min\" [max]=\"max\"\r\n [step]=\"step\" (blur)=\"onBlur()\" (focus)=\"onFocus()\" class=\"fv-input\" [class.fv-input-error]=\"errorMessage\"\r\n [class.fv-input-disabled]=\"disabled\" />\r\n\r\n <span *ngIf=\"errorMessage\" class=\"fv-error-message\">\r\n {{ getErrorMessage() }}\r\n </span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-number-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48;display:flex;align-items:center;gap:4px}.fv-required-asterisk{color:#e74c3c;font-weight:700}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;transition:all .2s ease-in-out;background-color:#fff;color:#1f2b41;box-sizing:border-box;height:34px;width:100%;min-width:0}.fv-input:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input:hover:not(:disabled):not(:focus){border-color:#3498db}.fv-input-error{border-color:#dc3545!important}.fv-input-error:focus{box-shadow:0 0 0 3px #dc35451a}.fv-input-disabled{background-color:#ecf0f1;cursor:not-allowed;opacity:.6}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c;display:flex;align-items:center;gap:4px}.fv-error-message:before{content:\"\\26a0\";font-size:14px}.fv-input::-webkit-inner-spin-button,.fv-input::-webkit-outer-spin-button{-webkit-appearance:none;appearance:none;margin:0}.fv-input[type=number]{-moz-appearance:textfield;appearance:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
506
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvNumberFieldComponent, isStandalone: true, selector: "fv-number-field", inputs: { label: "label", placeholder: "placeholder", schema: "schema", control: "control", disabled: "disabled", readonly: "readonly", min: "min", max: "max", step: "step" }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-number-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <input type=\"number\" [formControl]=\"control\" [placeholder]=\"placeholder\" [readonly]=\"readonly\" [min]=\"min\" [max]=\"max\"\r\n [step]=\"step\" (blur)=\"onBlur()\" (focus)=\"onFocus()\" class=\"fv-input\"\r\n [class.fv-input-error]=\"errorMessage && control?.touched\" [class.fv-input-disabled]=\"disabled\" />\r\n\r\n <span *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">\r\n {{ getErrorMessage() }}\r\n </span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-number-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48;display:flex;align-items:center;gap:4px}.fv-required-asterisk{color:#e74c3c;font-weight:700}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;transition:all .2s ease-in-out;background-color:#fff;color:#1f2b41;box-sizing:border-box;height:34px;width:100%;min-width:0}.fv-input:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input:hover:not(:disabled):not(:focus){border-color:#3498db}.fv-input-error{border-color:#dc3545!important}.fv-input-error:focus{box-shadow:0 0 0 3px #dc35451a}.fv-input-disabled{background-color:#ecf0f1;cursor:not-allowed;opacity:.6}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c;display:flex;align-items:center;gap:4px}.fv-error-message:before{content:\"\\26a0\";font-size:14px}.fv-input::-webkit-inner-spin-button,.fv-input::-webkit-outer-spin-button{-webkit-appearance:none;appearance:none;margin:0}.fv-input[type=number]{-moz-appearance:textfield;appearance:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
504
507
  }
505
508
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvNumberFieldComponent, decorators: [{
506
509
  type: Component,
507
- args: [{ selector: 'fv-number-field', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: "<div class=\"fv-number-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <input type=\"number\" [formControl]=\"control\" [placeholder]=\"placeholder\" [readonly]=\"readonly\" [min]=\"min\" [max]=\"max\"\r\n [step]=\"step\" (blur)=\"onBlur()\" (focus)=\"onFocus()\" class=\"fv-input\" [class.fv-input-error]=\"errorMessage\"\r\n [class.fv-input-disabled]=\"disabled\" />\r\n\r\n <span *ngIf=\"errorMessage\" class=\"fv-error-message\">\r\n {{ getErrorMessage() }}\r\n </span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-number-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48;display:flex;align-items:center;gap:4px}.fv-required-asterisk{color:#e74c3c;font-weight:700}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;transition:all .2s ease-in-out;background-color:#fff;color:#1f2b41;box-sizing:border-box;height:34px;width:100%;min-width:0}.fv-input:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input:hover:not(:disabled):not(:focus){border-color:#3498db}.fv-input-error{border-color:#dc3545!important}.fv-input-error:focus{box-shadow:0 0 0 3px #dc35451a}.fv-input-disabled{background-color:#ecf0f1;cursor:not-allowed;opacity:.6}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c;display:flex;align-items:center;gap:4px}.fv-error-message:before{content:\"\\26a0\";font-size:14px}.fv-input::-webkit-inner-spin-button,.fv-input::-webkit-outer-spin-button{-webkit-appearance:none;appearance:none;margin:0}.fv-input[type=number]{-moz-appearance:textfield;appearance:none}\n"] }]
510
+ args: [{ selector: 'fv-number-field', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: "<div class=\"fv-number-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <input type=\"number\" [formControl]=\"control\" [placeholder]=\"placeholder\" [readonly]=\"readonly\" [min]=\"min\" [max]=\"max\"\r\n [step]=\"step\" (blur)=\"onBlur()\" (focus)=\"onFocus()\" class=\"fv-input\"\r\n [class.fv-input-error]=\"errorMessage && control?.touched\" [class.fv-input-disabled]=\"disabled\" />\r\n\r\n <span *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">\r\n {{ getErrorMessage() }}\r\n </span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-number-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48;display:flex;align-items:center;gap:4px}.fv-required-asterisk{color:#e74c3c;font-weight:700}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;transition:all .2s ease-in-out;background-color:#fff;color:#1f2b41;box-sizing:border-box;height:34px;width:100%;min-width:0}.fv-input:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input:hover:not(:disabled):not(:focus){border-color:#3498db}.fv-input-error{border-color:#dc3545!important}.fv-input-error:focus{box-shadow:0 0 0 3px #dc35451a}.fv-input-disabled{background-color:#ecf0f1;cursor:not-allowed;opacity:.6}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c;display:flex;align-items:center;gap:4px}.fv-error-message:before{content:\"\\26a0\";font-size:14px}.fv-input::-webkit-inner-spin-button,.fv-input::-webkit-outer-spin-button{-webkit-appearance:none;appearance:none;margin:0}.fv-input[type=number]{-moz-appearance:textfield;appearance:none}\n"] }]
508
511
  }], propDecorators: { label: [{
509
512
  type: Input
510
513
  }], placeholder: [{
@@ -607,11 +610,11 @@ class FvRadioGroupComponent {
607
610
  return this.control.value === value;
608
611
  }
609
612
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvRadioGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
610
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvRadioGroupComponent, isStandalone: true, selector: "fv-radio-group", inputs: { label: "label", control: "control", options: "options", disabled: "disabled", required: "required", name: "name", layout: "layout" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-radio-group-container\">\r\n <label *ngIf=\"label\" class=\"fv-group-label\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-radio-options\" [class.horizontal]=\"layout === 'horizontal'\" [class.vertical]=\"layout === 'vertical'\">\r\n <label *ngFor=\"let option of options\" class=\"fv-radio-label\"\r\n [class.fv-radio-disabled]=\"disabled || option.disabled\">\r\n <input type=\"radio\" [name]=\"name\" [value]=\"option.value\" [checked]=\"isSelected(option.value)\"\r\n [disabled]=\"disabled || option.disabled\" (change)=\"onChange(option.value)\" class=\"fv-radio-input\" />\r\n <span class=\"fv-radio-custom\"></span>\r\n <span class=\"fv-radio-text\">{{ option.label }}</span>\r\n </label>\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-radio-group-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-group-label{display:block;margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48}.fv-required-asterisk{color:#e74c3c;margin-left:4px;font-weight:700}.fv-radio-options{display:flex;gap:12px}.fv-radio-options.vertical{flex-direction:column}.fv-radio-options.horizontal{flex-direction:row;flex-wrap:wrap;gap:24px}.fv-radio-label{display:flex;align-items:center;cursor:pointer;-webkit-user-select:none;user-select:none;position:relative;padding-left:32px;min-height:24px}.fv-radio-input{position:absolute;opacity:0;cursor:pointer;height:0;width:0}.fv-radio-custom{position:absolute;left:0;top:0;height:20px;width:20px;background-color:#fff;border:1px solid #8CBBA8;border-radius:50%;transition:all .2s;box-sizing:border-box}.fv-radio-label:hover .fv-radio-custom{border-color:#006aff}.fv-radio-input:checked~.fv-radio-custom{border-color:#006aff;border-width:1px}.fv-radio-custom:after{content:\"\";position:absolute;display:none;top:50%;left:50%;transform:translate(-50%,-50%);width:12px;height:12px;border-radius:50%;background:#006aff}.fv-radio-input:checked~.fv-radio-custom:after{display:block}.fv-radio-disabled{opacity:.6;cursor:not-allowed}.fv-radio-disabled .fv-radio-custom{background-color:#ecf0f1}.fv-radio-text{font-size:14px;font-weight:400;color:#1f2b41}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }] });
613
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvRadioGroupComponent, isStandalone: true, selector: "fv-radio-group", inputs: { label: "label", control: "control", options: "options", disabled: "disabled", required: "required", name: "name", layout: "layout" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-radio-group-container\">\r\n <label *ngIf=\"label\" class=\"fv-group-label\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-radio-options\" [class.horizontal]=\"layout === 'horizontal'\" [class.vertical]=\"layout === 'vertical'\">\r\n <label *ngFor=\"let option of options\" class=\"fv-radio-label\"\r\n [class.fv-radio-disabled]=\"disabled || option.disabled\">\r\n <input type=\"radio\" [name]=\"name\" [value]=\"option.value\" [checked]=\"isSelected(option.value)\"\r\n [disabled]=\"disabled || option.disabled\" (change)=\"onChange(option.value)\" class=\"fv-radio-input\" />\r\n <span class=\"fv-radio-custom\"></span>\r\n <span class=\"fv-radio-text\">{{ option.label }}</span>\r\n </label>\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-radio-group-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-group-label{display:block;margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48}.fv-required-asterisk{color:#e74c3c;margin-left:4px;font-weight:700}.fv-radio-options{display:flex;gap:12px}.fv-radio-options.vertical{flex-direction:column}.fv-radio-options.horizontal{flex-direction:row;flex-wrap:wrap;gap:15px}.fv-radio-label{display:flex;align-items:center;cursor:pointer;-webkit-user-select:none;user-select:none;position:relative;padding-left:25px;padding-bottom:6px;min-height:24px}.fv-radio-input{position:absolute;opacity:0;cursor:pointer;height:0;width:0}.fv-radio-custom{position:absolute;left:0;top:0;height:20px;width:20px;background-color:#fff;border:1px solid #8CBBA8;border-radius:50%;transition:all .2s;box-sizing:border-box}.fv-radio-label:hover .fv-radio-custom{border-color:#006aff}.fv-radio-input:checked~.fv-radio-custom{border-color:#006aff;border-width:1px}.fv-radio-custom:after{content:\"\";position:absolute;display:none;top:50%;left:50%;transform:translate(-50%,-50%);width:12px;height:12px;border-radius:50%;background:#006aff}.fv-radio-input:checked~.fv-radio-custom:after{display:block}.fv-radio-disabled{opacity:.6;cursor:not-allowed}.fv-radio-disabled .fv-radio-custom{background-color:#ecf0f1}.fv-radio-text{font-size:14px;font-weight:400;color:#1f2b41}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }] });
611
614
  }
612
615
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvRadioGroupComponent, decorators: [{
613
616
  type: Component,
614
- args: [{ selector: 'fv-radio-group', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: "<div class=\"fv-radio-group-container\">\r\n <label *ngIf=\"label\" class=\"fv-group-label\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-radio-options\" [class.horizontal]=\"layout === 'horizontal'\" [class.vertical]=\"layout === 'vertical'\">\r\n <label *ngFor=\"let option of options\" class=\"fv-radio-label\"\r\n [class.fv-radio-disabled]=\"disabled || option.disabled\">\r\n <input type=\"radio\" [name]=\"name\" [value]=\"option.value\" [checked]=\"isSelected(option.value)\"\r\n [disabled]=\"disabled || option.disabled\" (change)=\"onChange(option.value)\" class=\"fv-radio-input\" />\r\n <span class=\"fv-radio-custom\"></span>\r\n <span class=\"fv-radio-text\">{{ option.label }}</span>\r\n </label>\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-radio-group-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-group-label{display:block;margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48}.fv-required-asterisk{color:#e74c3c;margin-left:4px;font-weight:700}.fv-radio-options{display:flex;gap:12px}.fv-radio-options.vertical{flex-direction:column}.fv-radio-options.horizontal{flex-direction:row;flex-wrap:wrap;gap:24px}.fv-radio-label{display:flex;align-items:center;cursor:pointer;-webkit-user-select:none;user-select:none;position:relative;padding-left:32px;min-height:24px}.fv-radio-input{position:absolute;opacity:0;cursor:pointer;height:0;width:0}.fv-radio-custom{position:absolute;left:0;top:0;height:20px;width:20px;background-color:#fff;border:1px solid #8CBBA8;border-radius:50%;transition:all .2s;box-sizing:border-box}.fv-radio-label:hover .fv-radio-custom{border-color:#006aff}.fv-radio-input:checked~.fv-radio-custom{border-color:#006aff;border-width:1px}.fv-radio-custom:after{content:\"\";position:absolute;display:none;top:50%;left:50%;transform:translate(-50%,-50%);width:12px;height:12px;border-radius:50%;background:#006aff}.fv-radio-input:checked~.fv-radio-custom:after{display:block}.fv-radio-disabled{opacity:.6;cursor:not-allowed}.fv-radio-disabled .fv-radio-custom{background-color:#ecf0f1}.fv-radio-text{font-size:14px;font-weight:400;color:#1f2b41}\n"] }]
617
+ args: [{ selector: 'fv-radio-group', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: "<div class=\"fv-radio-group-container\">\r\n <label *ngIf=\"label\" class=\"fv-group-label\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-radio-options\" [class.horizontal]=\"layout === 'horizontal'\" [class.vertical]=\"layout === 'vertical'\">\r\n <label *ngFor=\"let option of options\" class=\"fv-radio-label\"\r\n [class.fv-radio-disabled]=\"disabled || option.disabled\">\r\n <input type=\"radio\" [name]=\"name\" [value]=\"option.value\" [checked]=\"isSelected(option.value)\"\r\n [disabled]=\"disabled || option.disabled\" (change)=\"onChange(option.value)\" class=\"fv-radio-input\" />\r\n <span class=\"fv-radio-custom\"></span>\r\n <span class=\"fv-radio-text\">{{ option.label }}</span>\r\n </label>\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-radio-group-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-group-label{display:block;margin-bottom:6px;font-size:14px;font-weight:600;color:#151d48}.fv-required-asterisk{color:#e74c3c;margin-left:4px;font-weight:700}.fv-radio-options{display:flex;gap:12px}.fv-radio-options.vertical{flex-direction:column}.fv-radio-options.horizontal{flex-direction:row;flex-wrap:wrap;gap:15px}.fv-radio-label{display:flex;align-items:center;cursor:pointer;-webkit-user-select:none;user-select:none;position:relative;padding-left:25px;padding-bottom:6px;min-height:24px}.fv-radio-input{position:absolute;opacity:0;cursor:pointer;height:0;width:0}.fv-radio-custom{position:absolute;left:0;top:0;height:20px;width:20px;background-color:#fff;border:1px solid #8CBBA8;border-radius:50%;transition:all .2s;box-sizing:border-box}.fv-radio-label:hover .fv-radio-custom{border-color:#006aff}.fv-radio-input:checked~.fv-radio-custom{border-color:#006aff;border-width:1px}.fv-radio-custom:after{content:\"\";position:absolute;display:none;top:50%;left:50%;transform:translate(-50%,-50%);width:12px;height:12px;border-radius:50%;background:#006aff}.fv-radio-input:checked~.fv-radio-custom:after{display:block}.fv-radio-disabled{opacity:.6;cursor:not-allowed}.fv-radio-disabled .fv-radio-custom{background-color:#ecf0f1}.fv-radio-text{font-size:14px;font-weight:400;color:#1f2b41}\n"] }]
615
618
  }], propDecorators: { label: [{
616
619
  type: Input
617
620
  }], control: [{
@@ -679,8 +682,8 @@ class FvDropdownComponent {
679
682
  this.filterOptions(term || '');
680
683
  });
681
684
  // Validate initial value
685
+ this.validateValue(this.control.value);
682
686
  if (this.control.value) {
683
- this.validateValue(this.control.value);
684
687
  const selected = this.options.find(opt => opt.value === this.control.value);
685
688
  if (selected) {
686
689
  this.searchControl.setValue(selected.label, { emitEvent: false });
@@ -820,8 +823,9 @@ class FvDropdownComponent {
820
823
  }
821
824
  }
822
825
  scrollToHighlighted() {
823
- const list = document.querySelector('.fv-dropdown-options');
824
- const item = document.querySelectorAll('.fv-dropdown-option')[this.highlightedIndex];
826
+ const list = this.el.nativeElement.querySelector('.fv-dropdown-options');
827
+ const items = this.el.nativeElement.querySelectorAll('.fv-dropdown-option');
828
+ const item = items[this.highlightedIndex];
825
829
  if (list && item) {
826
830
  const listRect = list.getBoundingClientRect();
827
831
  const itemRect = item.getBoundingClientRect();
@@ -870,11 +874,11 @@ class FvDropdownComponent {
870
874
  return option.value === this.control?.value;
871
875
  }
872
876
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvDropdownComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
873
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvDropdownComponent, isStandalone: true, selector: "fv-dropdown", inputs: { label: "label", placeholder: "placeholder", options: "options", schema: "schema", control: "control", disabled: "disabled" }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus" }, host: { listeners: { "keydown": "onKeyDown($event)", "document:click": "onClickOutside($event)" } }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-dropdown-container\">\r\n <label *ngIf=\"label\" class=\"fv-dropdown-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-dropdown-wrapper\">\r\n <div class=\"fv-dropdown\" [class.fv-dropdown-error]=\"errorMessage\" [class.fv-dropdown-disabled]=\"disabled\"\r\n [class.fv-dropdown-open]=\"isOpen\" (click)=\"onContainerClick($event)\">\r\n <input #searchInput type=\"text\" class=\"fv-dropdown-input\" [formControl]=\"searchControl\"\r\n [placeholder]=\"placeholder\" (focus)=\"onInputFocus()\" (click)=\"$event.stopPropagation()\"\r\n autocomplete=\"off\">\r\n <span class=\"fv-dropdown-arrow\" [class.arrow-up]=\"isOpen\" (click)=\"toggleDropdown()\">\u25BC</span>\r\n </div>\r\n\r\n <div *ngIf=\"isOpen\" class=\"fv-dropdown-options\">\r\n <div *ngFor=\"let option of filteredOptions; let i = index\" class=\"fv-dropdown-option\"\r\n [class.fv-dropdown-option-selected]=\"isSelected(option)\"\r\n [class.fv-dropdown-option-highlighted]=\"i === highlightedIndex\" (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"selectOption(option)\">\r\n {{ option.label }}\r\n </div>\r\n <div *ngIf=\"filteredOptions.length === 0\" class=\"fv-dropdown-option fv-no-results\">\r\n No options found\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-dropdown-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.material-icons{font-family:Material Icons!important}.fv-dropdown-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-dropdown-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-dropdown-wrapper{position:relative}.fv-dropdown{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;background-color:#fff;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:border-color .2s;outline:none;font-size:14px;font-weight:400;color:#1f2b41;box-sizing:border-box;height:34px}.fv-dropdown:hover:not(.fv-dropdown-disabled){border-color:#3498db}.fv-dropdown:focus-within:not(.fv-dropdown-disabled){border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-dropdown-error{border-color:#dc3545!important}.fv-dropdown-disabled{background-color:#ecf0f1;opacity:.6;cursor:not-allowed}.fv-dropdown-open{border-color:#3498db}.fv-dropdown-selected{font-size:14px;font-weight:400;color:#1f2b41;flex:1}.fv-dropdown-placeholder{color:#999}.fv-dropdown-arrow{font-size:10px;color:#666;margin-left:8px;margin-right:5px;transition:transform .2s}.arrow-up{transform:rotate(180deg)}.fv-dropdown-options{position:absolute;top:100%;left:0;right:0;margin-top:4px;background-color:#fff;border:1px solid #cccccc;border-radius:4px;max-height:300px;overflow-y:auto;z-index:1000;box-shadow:0 4px 6px #0000001a}.fv-dropdown-option{padding:12px 16px;font-size:14px;color:#333;cursor:pointer;transition:background-color .2s;border-bottom:1px solid #eeeeee}.fv-dropdown-option:last-child{border-bottom:none}.fv-dropdown-option:hover,.fv-dropdown-option-highlighted{background-color:#f8f9fa}.fv-dropdown-option-selected{background-color:#e7f3ff;color:#007bff;font-weight:600}.fv-dropdown-error-message{margin-top:4px;font-size:12px;color:#dc3545}.fv-dropdown-input{border:none!important;outline:none!important;box-shadow:none!important;background:transparent;flex:1;width:100%;min-width:0;appearance:none;-webkit-appearance:none;font-size:14px;color:#1f2b41;padding:0;margin:0;cursor:text;z-index:2;position:relative;pointer-events:auto;height:100%}.fv-dropdown-input::placeholder{color:#999}.fv-no-results{color:#999;font-style:italic;cursor:default;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
877
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvDropdownComponent, isStandalone: true, selector: "fv-dropdown", inputs: { label: "label", placeholder: "placeholder", options: "options", schema: "schema", control: "control", disabled: "disabled" }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus" }, host: { listeners: { "keydown": "onKeyDown($event)", "document:click": "onClickOutside($event)" } }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-dropdown-container\">\r\n <label *ngIf=\"label\" class=\"fv-dropdown-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-dropdown-wrapper\">\r\n <div class=\"fv-dropdown\" [class.fv-dropdown-error]=\"errorMessage && control?.touched\"\r\n [class.fv-dropdown-disabled]=\"disabled\" [class.fv-dropdown-open]=\"isOpen\"\r\n (click)=\"onContainerClick($event)\">\r\n <input #searchInput type=\"text\" class=\"fv-dropdown-input\" [formControl]=\"searchControl\"\r\n [placeholder]=\"placeholder\" (focus)=\"onInputFocus()\" (click)=\"$event.stopPropagation()\"\r\n autocomplete=\"off\">\r\n <span class=\"fv-dropdown-arrow\" [class.arrow-up]=\"isOpen\" (click)=\"toggleDropdown()\">\u25BC</span>\r\n </div>\r\n\r\n <div *ngIf=\"isOpen\" class=\"fv-dropdown-options\">\r\n <div *ngFor=\"let option of filteredOptions; let i = index\" class=\"fv-dropdown-option\"\r\n [class.fv-dropdown-option-selected]=\"isSelected(option)\"\r\n [class.fv-dropdown-option-highlighted]=\"i === highlightedIndex\" (mouseenter)=\"highlightedIndex = i\"\r\n (click)=\"selectOption(option)\">\r\n {{ option.label }}\r\n </div>\r\n <div *ngIf=\"filteredOptions.length === 0\" class=\"fv-dropdown-option fv-no-results\">\r\n No options found\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-dropdown-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.material-icons{font-family:Material Icons!important}.fv-dropdown-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-dropdown-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-dropdown-wrapper{position:relative}.fv-dropdown{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;background-color:#fff;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:border-color .2s;outline:none;font-size:14px;font-weight:400;color:#1f2b41;box-sizing:border-box;height:34px}.fv-dropdown:hover:not(.fv-dropdown-disabled){border-color:#3498db}.fv-dropdown:focus-within:not(.fv-dropdown-disabled){border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-dropdown-error{border-color:#dc3545!important}.fv-dropdown-disabled{background-color:#ecf0f1;opacity:.6;cursor:not-allowed}.fv-dropdown-open{border-color:#3498db}.fv-dropdown-selected{font-size:14px;font-weight:400;color:#1f2b41;flex:1}.fv-dropdown-placeholder{color:#999}.fv-dropdown-arrow{font-size:10px;color:#666;margin-left:8px;margin-right:5px;transition:transform .2s}.arrow-up{transform:rotate(180deg)}.fv-dropdown-options{position:absolute;top:100%;left:0;right:0;margin-top:4px;background-color:#fff;border:1px solid #cccccc;border-radius:4px;max-height:300px;overflow-y:auto;z-index:1000;box-shadow:0 4px 6px #0000001a}.fv-dropdown-option{padding:12px 16px;font-size:14px;color:#333;cursor:pointer;transition:background-color .2s;border-bottom:1px solid #eeeeee}.fv-dropdown-option:last-child{border-bottom:none}.fv-dropdown-option:hover,.fv-dropdown-option-highlighted{background-color:#f8f9fa}.fv-dropdown-option-selected{background-color:#e7f3ff;color:#007bff;font-weight:600}.fv-dropdown-error-message{margin-top:4px;font-size:12px;color:#dc3545}.fv-dropdown-input{border:none!important;outline:none!important;box-shadow:none!important;background:transparent;flex:1;width:100%;min-width:0;appearance:none;-webkit-appearance:none;font-size:14px;color:#1f2b41;padding:0;margin:0;cursor:text;z-index:2;position:relative;pointer-events:auto;height:100%}.fv-dropdown-input::placeholder{color:#999}.fv-no-results{color:#999;font-style:italic;cursor:default;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
874
878
  }
875
879
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvDropdownComponent, decorators: [{
876
880
  type: Component,
877
- args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-dropdown', template: "<div class=\"fv-dropdown-container\">\r\n <label *ngIf=\"label\" class=\"fv-dropdown-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-dropdown-wrapper\">\r\n <div class=\"fv-dropdown\" [class.fv-dropdown-error]=\"errorMessage\" [class.fv-dropdown-disabled]=\"disabled\"\r\n [class.fv-dropdown-open]=\"isOpen\" (click)=\"onContainerClick($event)\">\r\n <input #searchInput type=\"text\" class=\"fv-dropdown-input\" [formControl]=\"searchControl\"\r\n [placeholder]=\"placeholder\" (focus)=\"onInputFocus()\" (click)=\"$event.stopPropagation()\"\r\n autocomplete=\"off\">\r\n <span class=\"fv-dropdown-arrow\" [class.arrow-up]=\"isOpen\" (click)=\"toggleDropdown()\">\u25BC</span>\r\n </div>\r\n\r\n <div *ngIf=\"isOpen\" class=\"fv-dropdown-options\">\r\n <div *ngFor=\"let option of filteredOptions; let i = index\" class=\"fv-dropdown-option\"\r\n [class.fv-dropdown-option-selected]=\"isSelected(option)\"\r\n [class.fv-dropdown-option-highlighted]=\"i === highlightedIndex\" (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"selectOption(option)\">\r\n {{ option.label }}\r\n </div>\r\n <div *ngIf=\"filteredOptions.length === 0\" class=\"fv-dropdown-option fv-no-results\">\r\n No options found\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-dropdown-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.material-icons{font-family:Material Icons!important}.fv-dropdown-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-dropdown-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-dropdown-wrapper{position:relative}.fv-dropdown{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;background-color:#fff;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:border-color .2s;outline:none;font-size:14px;font-weight:400;color:#1f2b41;box-sizing:border-box;height:34px}.fv-dropdown:hover:not(.fv-dropdown-disabled){border-color:#3498db}.fv-dropdown:focus-within:not(.fv-dropdown-disabled){border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-dropdown-error{border-color:#dc3545!important}.fv-dropdown-disabled{background-color:#ecf0f1;opacity:.6;cursor:not-allowed}.fv-dropdown-open{border-color:#3498db}.fv-dropdown-selected{font-size:14px;font-weight:400;color:#1f2b41;flex:1}.fv-dropdown-placeholder{color:#999}.fv-dropdown-arrow{font-size:10px;color:#666;margin-left:8px;margin-right:5px;transition:transform .2s}.arrow-up{transform:rotate(180deg)}.fv-dropdown-options{position:absolute;top:100%;left:0;right:0;margin-top:4px;background-color:#fff;border:1px solid #cccccc;border-radius:4px;max-height:300px;overflow-y:auto;z-index:1000;box-shadow:0 4px 6px #0000001a}.fv-dropdown-option{padding:12px 16px;font-size:14px;color:#333;cursor:pointer;transition:background-color .2s;border-bottom:1px solid #eeeeee}.fv-dropdown-option:last-child{border-bottom:none}.fv-dropdown-option:hover,.fv-dropdown-option-highlighted{background-color:#f8f9fa}.fv-dropdown-option-selected{background-color:#e7f3ff;color:#007bff;font-weight:600}.fv-dropdown-error-message{margin-top:4px;font-size:12px;color:#dc3545}.fv-dropdown-input{border:none!important;outline:none!important;box-shadow:none!important;background:transparent;flex:1;width:100%;min-width:0;appearance:none;-webkit-appearance:none;font-size:14px;color:#1f2b41;padding:0;margin:0;cursor:text;z-index:2;position:relative;pointer-events:auto;height:100%}.fv-dropdown-input::placeholder{color:#999}.fv-no-results{color:#999;font-style:italic;cursor:default;text-align:center}\n"] }]
881
+ args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-dropdown', template: "<div class=\"fv-dropdown-container\">\r\n <label *ngIf=\"label\" class=\"fv-dropdown-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-dropdown-wrapper\">\r\n <div class=\"fv-dropdown\" [class.fv-dropdown-error]=\"errorMessage && control?.touched\"\r\n [class.fv-dropdown-disabled]=\"disabled\" [class.fv-dropdown-open]=\"isOpen\"\r\n (click)=\"onContainerClick($event)\">\r\n <input #searchInput type=\"text\" class=\"fv-dropdown-input\" [formControl]=\"searchControl\"\r\n [placeholder]=\"placeholder\" (focus)=\"onInputFocus()\" (click)=\"$event.stopPropagation()\"\r\n autocomplete=\"off\">\r\n <span class=\"fv-dropdown-arrow\" [class.arrow-up]=\"isOpen\" (click)=\"toggleDropdown()\">\u25BC</span>\r\n </div>\r\n\r\n <div *ngIf=\"isOpen\" class=\"fv-dropdown-options\">\r\n <div *ngFor=\"let option of filteredOptions; let i = index\" class=\"fv-dropdown-option\"\r\n [class.fv-dropdown-option-selected]=\"isSelected(option)\"\r\n [class.fv-dropdown-option-highlighted]=\"i === highlightedIndex\" (mouseenter)=\"highlightedIndex = i\"\r\n (click)=\"selectOption(option)\">\r\n {{ option.label }}\r\n </div>\r\n <div *ngIf=\"filteredOptions.length === 0\" class=\"fv-dropdown-option fv-no-results\">\r\n No options found\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-dropdown-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.material-icons{font-family:Material Icons!important}.fv-dropdown-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-dropdown-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-dropdown-wrapper{position:relative}.fv-dropdown{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;background-color:#fff;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:border-color .2s;outline:none;font-size:14px;font-weight:400;color:#1f2b41;box-sizing:border-box;height:34px}.fv-dropdown:hover:not(.fv-dropdown-disabled){border-color:#3498db}.fv-dropdown:focus-within:not(.fv-dropdown-disabled){border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-dropdown-error{border-color:#dc3545!important}.fv-dropdown-disabled{background-color:#ecf0f1;opacity:.6;cursor:not-allowed}.fv-dropdown-open{border-color:#3498db}.fv-dropdown-selected{font-size:14px;font-weight:400;color:#1f2b41;flex:1}.fv-dropdown-placeholder{color:#999}.fv-dropdown-arrow{font-size:10px;color:#666;margin-left:8px;margin-right:5px;transition:transform .2s}.arrow-up{transform:rotate(180deg)}.fv-dropdown-options{position:absolute;top:100%;left:0;right:0;margin-top:4px;background-color:#fff;border:1px solid #cccccc;border-radius:4px;max-height:300px;overflow-y:auto;z-index:1000;box-shadow:0 4px 6px #0000001a}.fv-dropdown-option{padding:12px 16px;font-size:14px;color:#333;cursor:pointer;transition:background-color .2s;border-bottom:1px solid #eeeeee}.fv-dropdown-option:last-child{border-bottom:none}.fv-dropdown-option:hover,.fv-dropdown-option-highlighted{background-color:#f8f9fa}.fv-dropdown-option-selected{background-color:#e7f3ff;color:#007bff;font-weight:600}.fv-dropdown-error-message{margin-top:4px;font-size:12px;color:#dc3545}.fv-dropdown-input{border:none!important;outline:none!important;box-shadow:none!important;background:transparent;flex:1;width:100%;min-width:0;appearance:none;-webkit-appearance:none;font-size:14px;color:#1f2b41;padding:0;margin:0;cursor:text;z-index:2;position:relative;pointer-events:auto;height:100%}.fv-dropdown-input::placeholder{color:#999}.fv-no-results{color:#999;font-style:italic;cursor:default;text-align:center}\n"] }]
878
882
  }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { label: [{
879
883
  type: Input
880
884
  }], placeholder: [{
@@ -935,8 +939,8 @@ class FvFileSelectorComponent {
935
939
  // Validate initial value
936
940
  if (this.control.value) {
937
941
  this.selectedFile = this.control.value;
938
- this.validateValue(this.control.value);
939
942
  }
943
+ this.validateValue(this.control.value);
940
944
  }
941
945
  ngOnDestroy() {
942
946
  this.subscription?.unsubscribe();
@@ -1019,11 +1023,11 @@ class FvFileSelectorComponent {
1019
1023
  return errorMessages[this.errorMessage] || this.errorMessage;
1020
1024
  }
1021
1025
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvFileSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1022
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvFileSelectorComponent, isStandalone: true, selector: "fv-file-selector", inputs: { label: "label", placeholder: "placeholder", schema: "schema", control: "control", disabled: "disabled", accept: "accept", maxSize: "maxSize" }, outputs: { valueChange: "valueChange", blur: "blur" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-file-selector-container\">\r\n <label *ngIf=\"label\" class=\"fv-file-selector-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <input #fileInput type=\"file\" [accept]=\"accept\" (change)=\"onFileSelected($event)\" style=\"display: none\" />\r\n\r\n <button type=\"button\" class=\"fv-file-selector-button\" [class.fv-file-selector-button-error]=\"errorMessage\"\r\n [class.fv-file-selector-button-disabled]=\"disabled\" (click)=\"openFileDialog()\" [disabled]=\"disabled\">\r\n {{ placeholder }}\r\n </button>\r\n\r\n <div *ngIf=\"selectedFile\" class=\"fv-file-info\">\r\n <div class=\"fv-file-details\">\r\n <div class=\"fv-file-name\">\uD83D\uDCC4 {{ selectedFile.name }}</div>\r\n <div class=\"fv-file-size\">{{ formatFileSize(selectedFile.size) }}</div>\r\n </div>\r\n <button type=\"button\" class=\"fv-file-remove\" (click)=\"removeFile()\" [disabled]=\"disabled\">\r\n \u2715\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-file-selector-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-file-selector-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-file-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-file-selector-button{padding:8px 16px;border:1px solid #3498db;border-radius:8px;background-color:#3498db;color:#fff;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s,border-color .2s;text-align:center}.fv-file-selector-button:hover:not(:disabled){background-color:#2980b9;border-color:#2980b9}.fv-file-selector-button-error{border-color:#e74c3c;background-color:#e74c3c}.fv-file-selector-button-error:hover:not(:disabled){background-color:#c0392b;border-color:#c0392b}.fv-file-selector-button-disabled{background-color:#ecf0f1;border-color:#ecf0f1;color:#7f8c8d;opacity:.6;cursor:not-allowed}.fv-file-info{display:flex;align-items:center;margin-top:8px;padding:10px;background-color:#f8f9fa;border:1px solid #8CBBA8;border-radius:8px}.fv-file-details{flex:1}.fv-file-name{font-size:14px;color:#1f2b41;margin-bottom:4px;word-break:break-all}.fv-file-size{font-size:12px;color:#6c757d}.fv-file-remove{padding:4px 8px;margin-left:8px;background:none;border:none;font-size:18px;color:#e74c3c;font-weight:700;cursor:pointer;transition:color .2s}.fv-file-remove:hover:not(:disabled){color:#c0392b}.fv-file-remove:disabled{opacity:.5;cursor:not-allowed}.fv-file-selector-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }] });
1026
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvFileSelectorComponent, isStandalone: true, selector: "fv-file-selector", inputs: { label: "label", placeholder: "placeholder", schema: "schema", control: "control", disabled: "disabled", accept: "accept", maxSize: "maxSize" }, outputs: { valueChange: "valueChange", blur: "blur" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-file-selector-container\">\r\n <label *ngIf=\"label\" class=\"fv-file-selector-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <input #fileInput type=\"file\" [accept]=\"accept\" (change)=\"onFileSelected($event)\" style=\"display: none\" />\r\n\r\n <button type=\"button\" class=\"fv-file-selector-button\"\r\n [class.fv-file-selector-button-error]=\"errorMessage && control?.touched\"\r\n [class.fv-file-selector-button-disabled]=\"disabled\" (click)=\"openFileDialog()\" [disabled]=\"disabled\">\r\n {{ placeholder }}\r\n </button>\r\n\r\n <div *ngIf=\"selectedFile\" class=\"fv-file-info\">\r\n <div class=\"fv-file-details\">\r\n <div class=\"fv-file-name\">\uD83D\uDCC4 {{ selectedFile.name }}</div>\r\n <div class=\"fv-file-size\">{{ formatFileSize(selectedFile.size) }}</div>\r\n </div>\r\n <button type=\"button\" class=\"fv-file-remove\" (click)=\"removeFile()\" [disabled]=\"disabled\">\r\n \u2715\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-file-selector-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-file-selector-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-file-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-file-selector-button{padding:8px 16px;border:1px solid #3498db;border-radius:8px;background-color:#3498db;color:#fff;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s,border-color .2s;text-align:center}.fv-file-selector-button:hover:not(:disabled){background-color:#2980b9;border-color:#2980b9}.fv-file-selector-button-error{border-color:#e74c3c;background-color:#e74c3c}.fv-file-selector-button-error:hover:not(:disabled){background-color:#c0392b;border-color:#c0392b}.fv-file-selector-button-disabled{background-color:#ecf0f1;border-color:#ecf0f1;color:#7f8c8d;opacity:.6;cursor:not-allowed}.fv-file-info{display:flex;align-items:center;margin-top:8px;padding:10px;background-color:#f8f9fa;border:1px solid #8CBBA8;border-radius:8px}.fv-file-details{flex:1}.fv-file-name{font-size:14px;color:#1f2b41;margin-bottom:4px;word-break:break-all}.fv-file-size{font-size:12px;color:#6c757d}.fv-file-remove{padding:4px 8px;margin-left:8px;background:none;border:none;font-size:18px;color:#e74c3c;font-weight:700;cursor:pointer;transition:color .2s}.fv-file-remove:hover:not(:disabled){color:#c0392b}.fv-file-remove:disabled{opacity:.5;cursor:not-allowed}.fv-file-selector-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }] });
1023
1027
  }
1024
1028
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvFileSelectorComponent, decorators: [{
1025
1029
  type: Component,
1026
- args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-file-selector', template: "<div class=\"fv-file-selector-container\">\r\n <label *ngIf=\"label\" class=\"fv-file-selector-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <input #fileInput type=\"file\" [accept]=\"accept\" (change)=\"onFileSelected($event)\" style=\"display: none\" />\r\n\r\n <button type=\"button\" class=\"fv-file-selector-button\" [class.fv-file-selector-button-error]=\"errorMessage\"\r\n [class.fv-file-selector-button-disabled]=\"disabled\" (click)=\"openFileDialog()\" [disabled]=\"disabled\">\r\n {{ placeholder }}\r\n </button>\r\n\r\n <div *ngIf=\"selectedFile\" class=\"fv-file-info\">\r\n <div class=\"fv-file-details\">\r\n <div class=\"fv-file-name\">\uD83D\uDCC4 {{ selectedFile.name }}</div>\r\n <div class=\"fv-file-size\">{{ formatFileSize(selectedFile.size) }}</div>\r\n </div>\r\n <button type=\"button\" class=\"fv-file-remove\" (click)=\"removeFile()\" [disabled]=\"disabled\">\r\n \u2715\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-file-selector-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-file-selector-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-file-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-file-selector-button{padding:8px 16px;border:1px solid #3498db;border-radius:8px;background-color:#3498db;color:#fff;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s,border-color .2s;text-align:center}.fv-file-selector-button:hover:not(:disabled){background-color:#2980b9;border-color:#2980b9}.fv-file-selector-button-error{border-color:#e74c3c;background-color:#e74c3c}.fv-file-selector-button-error:hover:not(:disabled){background-color:#c0392b;border-color:#c0392b}.fv-file-selector-button-disabled{background-color:#ecf0f1;border-color:#ecf0f1;color:#7f8c8d;opacity:.6;cursor:not-allowed}.fv-file-info{display:flex;align-items:center;margin-top:8px;padding:10px;background-color:#f8f9fa;border:1px solid #8CBBA8;border-radius:8px}.fv-file-details{flex:1}.fv-file-name{font-size:14px;color:#1f2b41;margin-bottom:4px;word-break:break-all}.fv-file-size{font-size:12px;color:#6c757d}.fv-file-remove{padding:4px 8px;margin-left:8px;background:none;border:none;font-size:18px;color:#e74c3c;font-weight:700;cursor:pointer;transition:color .2s}.fv-file-remove:hover:not(:disabled){color:#c0392b}.fv-file-remove:disabled{opacity:.5;cursor:not-allowed}.fv-file-selector-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
1030
+ args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-file-selector', template: "<div class=\"fv-file-selector-container\">\r\n <label *ngIf=\"label\" class=\"fv-file-selector-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <input #fileInput type=\"file\" [accept]=\"accept\" (change)=\"onFileSelected($event)\" style=\"display: none\" />\r\n\r\n <button type=\"button\" class=\"fv-file-selector-button\"\r\n [class.fv-file-selector-button-error]=\"errorMessage && control?.touched\"\r\n [class.fv-file-selector-button-disabled]=\"disabled\" (click)=\"openFileDialog()\" [disabled]=\"disabled\">\r\n {{ placeholder }}\r\n </button>\r\n\r\n <div *ngIf=\"selectedFile\" class=\"fv-file-info\">\r\n <div class=\"fv-file-details\">\r\n <div class=\"fv-file-name\">\uD83D\uDCC4 {{ selectedFile.name }}</div>\r\n <div class=\"fv-file-size\">{{ formatFileSize(selectedFile.size) }}</div>\r\n </div>\r\n <button type=\"button\" class=\"fv-file-remove\" (click)=\"removeFile()\" [disabled]=\"disabled\">\r\n \u2715\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-file-selector-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-file-selector-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-file-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-file-selector-button{padding:8px 16px;border:1px solid #3498db;border-radius:8px;background-color:#3498db;color:#fff;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s,border-color .2s;text-align:center}.fv-file-selector-button:hover:not(:disabled){background-color:#2980b9;border-color:#2980b9}.fv-file-selector-button-error{border-color:#e74c3c;background-color:#e74c3c}.fv-file-selector-button-error:hover:not(:disabled){background-color:#c0392b;border-color:#c0392b}.fv-file-selector-button-disabled{background-color:#ecf0f1;border-color:#ecf0f1;color:#7f8c8d;opacity:.6;cursor:not-allowed}.fv-file-info{display:flex;align-items:center;margin-top:8px;padding:10px;background-color:#f8f9fa;border:1px solid #8CBBA8;border-radius:8px}.fv-file-details{flex:1}.fv-file-name{font-size:14px;color:#1f2b41;margin-bottom:4px;word-break:break-all}.fv-file-size{font-size:12px;color:#6c757d}.fv-file-remove{padding:4px 8px;margin-left:8px;background:none;border:none;font-size:18px;color:#e74c3c;font-weight:700;cursor:pointer;transition:color .2s}.fv-file-remove:hover:not(:disabled){color:#c0392b}.fv-file-remove:disabled{opacity:.5;cursor:not-allowed}.fv-file-selector-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
1027
1031
  }], propDecorators: { label: [{
1028
1032
  type: Input
1029
1033
  }], placeholder: [{
@@ -1054,31 +1058,37 @@ class FvImageSelectorComponent {
1054
1058
  schema;
1055
1059
  control;
1056
1060
  disabled = false;
1057
- maxSize; // Maximum file size in bytes
1061
+ maxSize;
1058
1062
  valueChange = new EventEmitter();
1059
1063
  blur = new EventEmitter();
1060
1064
  imageInput;
1065
+ editorImage;
1061
1066
  errorMessage = null;
1062
1067
  selectedImage = null;
1063
1068
  subscription;
1069
+ // Editor State
1070
+ isEditorOpen = false;
1071
+ editImageSrc = null;
1072
+ editImageName = '';
1073
+ scale = 1;
1074
+ minScale = 0.5;
1075
+ maxScale = 3;
1076
+ step = 0.1;
1077
+ panX = 0;
1078
+ panY = 0;
1079
+ isDragging = false;
1080
+ startX = 0;
1081
+ startY = 0;
1064
1082
  constructor(sanitizer) {
1065
1083
  this.sanitizer = sanitizer;
1066
1084
  }
1067
1085
  ngOnInit() {
1068
- if (!this.control) {
1069
- console.error('FvImageSelector: control is required');
1070
- return;
1071
- }
1072
- if (!this.schema) {
1073
- console.warn('FvImageSelector: schema is not provided, validation will be skipped');
1086
+ if (!this.control)
1074
1087
  return;
1075
- }
1076
- // Subscribe to value changes
1077
1088
  this.subscription = this.control.valueChanges.subscribe((value) => {
1078
1089
  this.validateValue(value);
1079
1090
  this.valueChange.emit(value);
1080
1091
  });
1081
- // Validate initial value
1082
1092
  if (this.control.value) {
1083
1093
  this.selectedImage = this.control.value;
1084
1094
  this.validateValue(this.control.value);
@@ -1103,39 +1113,119 @@ class FvImageSelectorComponent {
1103
1113
  const input = event.target;
1104
1114
  if (input.files && input.files.length > 0) {
1105
1115
  const file = input.files[0];
1106
- // Validate that it's an image
1107
1116
  if (!file.type.startsWith('image/')) {
1108
1117
  alert('Please select an image file');
1109
1118
  input.value = '';
1110
1119
  return;
1111
1120
  }
1112
- // Check file size if maxSize is specified
1113
1121
  if (this.maxSize && file.size > this.maxSize) {
1114
- alert(`Image size exceeds the maximum allowed size of ${this.formatFileSize(this.maxSize)}`);
1122
+ alert(`Image size exceeds ${this.formatFileSize(this.maxSize)}`);
1115
1123
  input.value = '';
1116
1124
  return;
1117
1125
  }
1118
- // Create object URL and get image dimensions
1119
1126
  const reader = new FileReader();
1120
1127
  reader.onload = (e) => {
1121
- const img = new Image();
1122
- img.onload = () => {
1123
- const imageInfo = {
1124
- file: file,
1125
- url: this.sanitizer.bypassSecurityTrustUrl(e.target.result),
1126
- width: img.width,
1127
- height: img.height,
1128
- size: file.size,
1129
- };
1130
- this.selectedImage = imageInfo;
1131
- this.control.setValue(imageInfo);
1132
- this.blur.emit();
1133
- };
1134
- img.src = e.target.result;
1128
+ this.editImageSrc = e.target.result;
1129
+ this.editImageName = file.name;
1130
+ this.openEditor();
1131
+ input.value = '';
1135
1132
  };
1136
1133
  reader.readAsDataURL(file);
1137
1134
  }
1138
1135
  }
1136
+ openEditor() {
1137
+ this.isEditorOpen = true;
1138
+ this.scale = 1;
1139
+ this.panX = 0;
1140
+ this.panY = 0;
1141
+ }
1142
+ closeEditor() {
1143
+ this.isEditorOpen = false;
1144
+ this.editImageSrc = null;
1145
+ }
1146
+ // Drag Logic
1147
+ startDrag(event) {
1148
+ event.preventDefault();
1149
+ this.isDragging = true;
1150
+ this.startX = event.clientX - this.panX;
1151
+ this.startY = event.clientY - this.panY;
1152
+ }
1153
+ onDrag(event) {
1154
+ if (!this.isDragging)
1155
+ return;
1156
+ event.preventDefault();
1157
+ this.panX = event.clientX - this.startX;
1158
+ this.panY = event.clientY - this.startY;
1159
+ }
1160
+ endDrag() {
1161
+ this.isDragging = false;
1162
+ }
1163
+ // Zoom Logic
1164
+ onSliderChange(event) {
1165
+ const value = event.target.value;
1166
+ this.scale = parseFloat(value);
1167
+ }
1168
+ zoomIn() {
1169
+ this.scale = Math.min(this.scale + this.step, this.maxScale);
1170
+ }
1171
+ zoomOut() {
1172
+ this.scale = Math.max(this.scale - this.step, this.minScale);
1173
+ }
1174
+ resetEditor() {
1175
+ this.scale = 1;
1176
+ this.panX = 0;
1177
+ this.panY = 0;
1178
+ }
1179
+ applyCrop() {
1180
+ if (!this.editorImage)
1181
+ return;
1182
+ const img = this.editorImage.nativeElement;
1183
+ const canvas = document.createElement('canvas');
1184
+ const ctx = canvas.getContext('2d');
1185
+ if (!ctx)
1186
+ return;
1187
+ // Output size 300x300 for the avatar
1188
+ const size = 300;
1189
+ canvas.width = size;
1190
+ canvas.height = size;
1191
+ // Fill background
1192
+ ctx.fillStyle = '#ffffff';
1193
+ ctx.fillRect(0, 0, size, size);
1194
+ // Center transform
1195
+ ctx.translate(size / 2, size / 2);
1196
+ ctx.translate(this.panX, this.panY);
1197
+ ctx.scale(this.scale, this.scale);
1198
+ ctx.translate(-img.naturalWidth / 2, -img.naturalHeight / 2);
1199
+ ctx.drawImage(img, 0, 0);
1200
+ const dataUrl = canvas.toDataURL('image/png');
1201
+ // Convert base64 to File object to mimic file selection
1202
+ // We'll use the original name but .png
1203
+ // This 'file' is mocked, but 'url' is what is displayed.
1204
+ // In real app we might want to upload the blob.
1205
+ // For ImageInfo we store the dataURL as url.
1206
+ const imageInfo = {
1207
+ file: new File([this.dataURLtoBlob(dataUrl)], this.editImageName, { type: 'image/png' }),
1208
+ url: this.sanitizer.bypassSecurityTrustUrl(dataUrl),
1209
+ width: size,
1210
+ height: size,
1211
+ size: 0 // Approximate or calc from blob
1212
+ };
1213
+ this.selectedImage = imageInfo;
1214
+ this.control.setValue(imageInfo);
1215
+ this.blur.emit();
1216
+ this.closeEditor();
1217
+ }
1218
+ dataURLtoBlob(dataurl) {
1219
+ const arr = dataurl.split(',');
1220
+ const mime = arr[0].match(/:(.*?);/)[1];
1221
+ const bstr = atob(arr[1]);
1222
+ let n = bstr.length;
1223
+ const u8arr = new Uint8Array(n);
1224
+ while (n--) {
1225
+ u8arr[n] = bstr.charCodeAt(n);
1226
+ }
1227
+ return new Blob([u8arr], { type: mime });
1228
+ }
1139
1229
  openImageDialog() {
1140
1230
  if (!this.disabled) {
1141
1231
  this.imageInput.nativeElement.click();
@@ -1158,7 +1248,7 @@ class FvImageSelectorComponent {
1158
1248
  return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
1159
1249
  }
1160
1250
  isRequired() {
1161
- return (this.schema?.rules?.some((r) => r.name === 'required' && r.params?.['enabled']) || false);
1251
+ return this.schema?.rules?.some(r => r.name === 'required' && r.params?.['enabled']) || false;
1162
1252
  }
1163
1253
  getErrorMessage() {
1164
1254
  if (!this.errorMessage)
@@ -1170,11 +1260,11 @@ class FvImageSelectorComponent {
1170
1260
  return errorMessages[this.errorMessage] || this.errorMessage;
1171
1261
  }
1172
1262
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvImageSelectorComponent, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
1173
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvImageSelectorComponent, isStandalone: true, selector: "fv-image-selector", inputs: { label: "label", placeholder: "placeholder", schema: "schema", control: "control", disabled: "disabled", maxSize: "maxSize" }, outputs: { valueChange: "valueChange", blur: "blur" }, viewQueries: [{ propertyName: "imageInput", first: true, predicate: ["imageInput"], descendants: true }], ngImport: i0, template: "<div class=\"fv-image-selector-container\">\r\n <label *ngIf=\"label\" class=\"fv-image-selector-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <input #imageInput type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" style=\"display: none\" />\r\n\r\n <div *ngIf=\"!selectedImage; else previewTemplate\">\r\n <button type=\"button\" class=\"fv-image-selector-button\" [class.fv-image-selector-button-error]=\"errorMessage\"\r\n [class.fv-image-selector-button-disabled]=\"disabled\" (click)=\"openImageDialog()\" [disabled]=\"disabled\">\r\n \uD83D\uDCF7 {{ placeholder }}\r\n </button>\r\n </div>\r\n\r\n <ng-template #previewTemplate>\r\n <div class=\"fv-image-preview-container\">\r\n <img [src]=\"selectedImage!.url\" class=\"fv-image-preview\" alt=\"Selected image\" />\r\n <div class=\"fv-image-actions\">\r\n <button type=\"button\" class=\"fv-image-change-button\" (click)=\"openImageDialog()\" [disabled]=\"disabled\">\r\n Change\r\n </button>\r\n <button type=\"button\" class=\"fv-image-remove-button\" (click)=\"removeImage()\" [disabled]=\"disabled\">\r\n Remove\r\n </button>\r\n </div>\r\n <div class=\"fv-image-info\">\r\n {{ selectedImage!.width }} \u00D7 {{ selectedImage!.height }} \u2022\r\n {{ formatFileSize(selectedImage!.size) }}\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-image-selector-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-image-selector-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-image-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-image-selector-button{padding:40px;border:2px dashed #3498db;border-radius:8px;background-color:#f8f9fa;color:#3498db;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s,border-color .2s;width:100%;text-align:center;box-sizing:border-box}.fv-image-selector-button:hover:not(:disabled){background-color:#e7f3ff}.fv-image-selector-button-error{border-color:#e74c3c;color:#e74c3c}.fv-image-selector-button-disabled{border-color:#ccc;background-color:#f5f5f5;color:#ccc;opacity:.6;cursor:not-allowed}.fv-image-preview-container{display:flex;flex-direction:column;align-items:center}.fv-image-preview{width:100%;max-width:600px;height:200px;object-fit:cover;border-radius:8px;background-color:#f0f0f0;margin-bottom:8px}.fv-image-actions{display:flex;gap:8px;margin-bottom:4px}.fv-image-change-button{padding:8px 16px;background-color:#3498db;color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s}.fv-image-change-button:hover:not(:disabled){background-color:#2980b9}.fv-image-change-button:disabled{opacity:.6;cursor:not-allowed}.fv-image-remove-button{padding:8px 16px;background-color:#e74c3c;color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s}.fv-image-remove-button:hover:not(:disabled){background-color:#c0392b}.fv-image-remove-button:disabled{opacity:.6;cursor:not-allowed}.fv-image-info{font-size:12px;color:#6c757d;text-align:center}.fv-image-selector-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }] });
1263
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvImageSelectorComponent, isStandalone: true, selector: "fv-image-selector", inputs: { label: "label", placeholder: "placeholder", schema: "schema", control: "control", disabled: "disabled", maxSize: "maxSize" }, outputs: { valueChange: "valueChange", blur: "blur" }, host: { listeners: { "document:mousemove": "onDrag($event)", "document:mouseup": "endDrag()" } }, viewQueries: [{ propertyName: "imageInput", first: true, predicate: ["imageInput"], descendants: true }, { propertyName: "editorImage", first: true, predicate: ["editorImage"], descendants: true }], ngImport: i0, template: "<div class=\"fv-image-selector-container\">\r\n <label *ngIf=\"label\" class=\"fv-image-selector-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <input #imageInput type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" style=\"display: none\" />\r\n\r\n <!-- Initial State -->\r\n <div *ngIf=\"!selectedImage && !isEditorOpen\">\r\n <button type=\"button\" class=\"fv-upload-btn\" (click)=\"openImageDialog()\" [disabled]=\"disabled\">\r\n Upload\r\n <svg viewBox=\"0 0 24 24\" class=\"upload-icon\">\r\n <path d=\"M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z\" />\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <!-- Preview State -->\r\n <div *ngIf=\"selectedImage && !isEditorOpen\" class=\"fv-preview-row\">\r\n <div class=\"fv-preview-wrapper\">\r\n <img [src]=\"selectedImage.url\" class=\"fv-circle-preview\" alt=\"Preview\" />\r\n </div>\r\n <span class=\"fv-filename\">{{ selectedImage.file.name }}</span>\r\n <button type=\"button\" class=\"fv-trash-btn\" (click)=\"removeImage()\" [disabled]=\"disabled\">\r\n <svg viewBox=\"0 0 24 24\" class=\"trash-icon\">\r\n <path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z\" />\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <!-- Modal -->\r\n <div *ngIf=\"isEditorOpen\" class=\"fv-modal-overlay\">\r\n <div class=\"fv-modal-content\">\r\n <div class=\"fv-modal-header\">\r\n <h3>Adjust Photo</h3>\r\n <button type=\"button\" class=\"close-icon\" (click)=\"closeEditor()\">\u00D7</button>\r\n </div>\r\n <div class=\"fv-modal-body\">\r\n <div class=\"fv-crop-container\" (mousedown)=\"startDrag($event)\">\r\n <img #editorImage [src]=\"editImageSrc\" class=\"fv-crop-image\"\r\n [style.transform]=\"'translate(' + panX + 'px, ' + panY + 'px) scale(' + scale + ')'\"\r\n draggable=\"false\" />\r\n <div class=\"fv-crop-mask\"></div>\r\n </div>\r\n\r\n <div class=\"fv-zoom-controls\">\r\n <span class=\"zoom-label\">Zoom</span>\r\n <button type=\"button\" class=\"zoom-btn\" (click)=\"zoomOut()\">-</button>\r\n <input type=\"range\" [min]=\"minScale\" [max]=\"maxScale\" [step]=\"step\" [value]=\"scale\"\r\n (input)=\"onSliderChange($event)\" class=\"zoom-slider\">\r\n <button type=\"button\" class=\"zoom-btn\" (click)=\"zoomIn()\">+</button>\r\n <span class=\"zoom-value\">{{ (scale * 100) | number:'1.0-0' }}%</span>\r\n </div>\r\n\r\n <p class=\"fv-hint-text\">Drag the image to reposition. Use the zoom control to fill the circle.</p>\r\n </div>\r\n\r\n <div class=\"fv-modal-footer\">\r\n <button type=\"button\" class=\"btn-reset\" (click)=\"resetEditor()\">Reset</button>\r\n <div class=\"footer-actions\">\r\n <button type=\"button\" class=\"btn-cancel\" (click)=\"closeEditor()\">Cancel</button>\r\n <button type=\"button\" class=\"btn-apply\" (click)=\"applyCrop()\">Apply</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-image-selector-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif;box-sizing:border-box}.fv-image-selector-container{display:flex;flex-direction:column;margin-bottom:20px;width:100%}.fv-image-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:8px;display:block}.required-asterisk{color:#e74c3c;font-weight:700;margin-left:4px}.fv-upload-btn{display:inline-flex;align-items:center;justify-content:space-between;background:#fff;border:1px solid #dcdcdc;border-radius:8px;padding:0 0 0 20px;font-size:16px;color:#1f2b41;cursor:pointer;overflow:hidden;height:48px;min-width:160px;transition:box-shadow .2s}.fv-upload-btn:hover:not(:disabled){box-shadow:0 2px 5px #0000000d;border-color:#bbb}.fv-upload-btn:disabled{opacity:.6;cursor:not-allowed;background:#f9f9f9}.upload-icon{width:50px;height:100%;padding:12px;background:#f4f5f7;border-left:1px solid #dcdcdc;fill:#333;margin-left:20px}.fv-preview-row{display:flex;align-items:center;gap:16px;padding:5px 0}.fv-preview-wrapper{position:relative;width:60px;height:60px}.fv-circle-preview{width:60px;height:60px;border-radius:50%;border:1px solid #eee;object-fit:cover}.fv-filename{flex:1;font-size:14px;color:#666;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:300px}.fv-trash-btn{background:none;border:none;cursor:pointer;padding:8px;border-radius:50%;transition:background .2s;display:flex}.fv-trash-btn:hover:not(:disabled){background:#fee}.trash-icon{width:24px;height:24px;fill:red}.fv-modal-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#0009;display:flex;align-items:center;justify-content:center;z-index:9999}.fv-modal-content{background:#fff;width:650px;max-width:90vw;border-radius:8px;overflow:hidden;box-shadow:0 10px 30px #0003;display:flex;flex-direction:column}.fv-modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 24px;border-bottom:1px solid #f0f0f0}.fv-modal-header h3{margin:0;font-size:18px;font-weight:600;color:#1f2b41}.close-icon{background:none;border:none;font-size:24px;line-height:1;cursor:pointer;color:#888}.fv-modal-body{padding:24px;display:flex;flex-direction:column;align-items:center;background:#fff}.fv-crop-container{width:100%;height:350px;background:#333;position:relative;overflow:hidden;margin-bottom:24px;border-radius:4px;cursor:move;cursor:grab;display:flex;align-items:center;justify-content:center}.fv-crop-container:active{cursor:grabbing}.fv-crop-image{max-width:none;max-height:none;-webkit-user-select:none;user-select:none;pointer-events:none;transform-origin:center center}.fv-crop-mask{position:absolute;inset:0;background:radial-gradient(circle at center,transparent 150px,rgba(0,0,0,.6) 151px);pointer-events:none}.fv-zoom-controls{display:flex;align-items:center;gap:16px;width:100%;max-width:500px;margin-bottom:12px}.zoom-label{font-size:14px;font-weight:500;color:#333;min-width:40px}.zoom-btn{width:32px;height:32px;border-radius:50%;background:#e6f0ff;color:#006aff;border:none;font-weight:700;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;padding-bottom:2px}.zoom-btn:hover{background:#d0e1ff}.zoom-slider{flex:1;-webkit-appearance:none;height:4px;background:#ddd;border-radius:2px;outline:none}.zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;background:#006aff;cursor:pointer;box-shadow:0 0 0 4px #006aff33}.zoom-value{font-size:14px;color:#666;min-width:40px;text-align:right}.fv-hint-text{font-size:13px;color:#7f8c8d;margin:0;text-align:center}.fv-modal-footer{padding:16px 24px;border-top:1px solid #f0f0f0;display:flex;justify-content:space-between;align-items:center;background:#fff}.footer-actions{display:flex;gap:12px}.btn-reset,.btn-cancel,.btn-apply{padding:8px 20px;border-radius:6px;font-size:14px;font-weight:500;cursor:pointer;transition:background .2s}.btn-reset{background:#f5f5f5;border:1px solid #ddd;color:#333}.btn-reset:hover{background:#eee}.btn-cancel{background:#fff;border:1px solid #ddd;color:#333}.btn-cancel:hover{border-color:#bbb}.btn-apply{background:#006aff;border:1px solid #006aff;color:#fff}.btn-apply:hover{background:#0056cc;border-color:#0056cc}.fv-image-selector-error-message{margin-top:6px;font-size:12px;color:#e74c3c;display:flex;align-items:center;gap:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "ngmodule", type: ReactiveFormsModule }] });
1174
1264
  }
1175
1265
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvImageSelectorComponent, decorators: [{
1176
1266
  type: Component,
1177
- args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-image-selector', template: "<div class=\"fv-image-selector-container\">\r\n <label *ngIf=\"label\" class=\"fv-image-selector-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <input #imageInput type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" style=\"display: none\" />\r\n\r\n <div *ngIf=\"!selectedImage; else previewTemplate\">\r\n <button type=\"button\" class=\"fv-image-selector-button\" [class.fv-image-selector-button-error]=\"errorMessage\"\r\n [class.fv-image-selector-button-disabled]=\"disabled\" (click)=\"openImageDialog()\" [disabled]=\"disabled\">\r\n \uD83D\uDCF7 {{ placeholder }}\r\n </button>\r\n </div>\r\n\r\n <ng-template #previewTemplate>\r\n <div class=\"fv-image-preview-container\">\r\n <img [src]=\"selectedImage!.url\" class=\"fv-image-preview\" alt=\"Selected image\" />\r\n <div class=\"fv-image-actions\">\r\n <button type=\"button\" class=\"fv-image-change-button\" (click)=\"openImageDialog()\" [disabled]=\"disabled\">\r\n Change\r\n </button>\r\n <button type=\"button\" class=\"fv-image-remove-button\" (click)=\"removeImage()\" [disabled]=\"disabled\">\r\n Remove\r\n </button>\r\n </div>\r\n <div class=\"fv-image-info\">\r\n {{ selectedImage!.width }} \u00D7 {{ selectedImage!.height }} \u2022\r\n {{ formatFileSize(selectedImage!.size) }}\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-image-selector-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-image-selector-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-image-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-image-selector-button{padding:40px;border:2px dashed #3498db;border-radius:8px;background-color:#f8f9fa;color:#3498db;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s,border-color .2s;width:100%;text-align:center;box-sizing:border-box}.fv-image-selector-button:hover:not(:disabled){background-color:#e7f3ff}.fv-image-selector-button-error{border-color:#e74c3c;color:#e74c3c}.fv-image-selector-button-disabled{border-color:#ccc;background-color:#f5f5f5;color:#ccc;opacity:.6;cursor:not-allowed}.fv-image-preview-container{display:flex;flex-direction:column;align-items:center}.fv-image-preview{width:100%;max-width:600px;height:200px;object-fit:cover;border-radius:8px;background-color:#f0f0f0;margin-bottom:8px}.fv-image-actions{display:flex;gap:8px;margin-bottom:4px}.fv-image-change-button{padding:8px 16px;background-color:#3498db;color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s}.fv-image-change-button:hover:not(:disabled){background-color:#2980b9}.fv-image-change-button:disabled{opacity:.6;cursor:not-allowed}.fv-image-remove-button{padding:8px 16px;background-color:#e74c3c;color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color .2s}.fv-image-remove-button:hover:not(:disabled){background-color:#c0392b}.fv-image-remove-button:disabled{opacity:.6;cursor:not-allowed}.fv-image-info{font-size:12px;color:#6c757d;text-align:center}.fv-image-selector-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
1267
+ args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-image-selector', template: "<div class=\"fv-image-selector-container\">\r\n <label *ngIf=\"label\" class=\"fv-image-selector-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <input #imageInput type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" style=\"display: none\" />\r\n\r\n <!-- Initial State -->\r\n <div *ngIf=\"!selectedImage && !isEditorOpen\">\r\n <button type=\"button\" class=\"fv-upload-btn\" (click)=\"openImageDialog()\" [disabled]=\"disabled\">\r\n Upload\r\n <svg viewBox=\"0 0 24 24\" class=\"upload-icon\">\r\n <path d=\"M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z\" />\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <!-- Preview State -->\r\n <div *ngIf=\"selectedImage && !isEditorOpen\" class=\"fv-preview-row\">\r\n <div class=\"fv-preview-wrapper\">\r\n <img [src]=\"selectedImage.url\" class=\"fv-circle-preview\" alt=\"Preview\" />\r\n </div>\r\n <span class=\"fv-filename\">{{ selectedImage.file.name }}</span>\r\n <button type=\"button\" class=\"fv-trash-btn\" (click)=\"removeImage()\" [disabled]=\"disabled\">\r\n <svg viewBox=\"0 0 24 24\" class=\"trash-icon\">\r\n <path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z\" />\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <!-- Modal -->\r\n <div *ngIf=\"isEditorOpen\" class=\"fv-modal-overlay\">\r\n <div class=\"fv-modal-content\">\r\n <div class=\"fv-modal-header\">\r\n <h3>Adjust Photo</h3>\r\n <button type=\"button\" class=\"close-icon\" (click)=\"closeEditor()\">\u00D7</button>\r\n </div>\r\n <div class=\"fv-modal-body\">\r\n <div class=\"fv-crop-container\" (mousedown)=\"startDrag($event)\">\r\n <img #editorImage [src]=\"editImageSrc\" class=\"fv-crop-image\"\r\n [style.transform]=\"'translate(' + panX + 'px, ' + panY + 'px) scale(' + scale + ')'\"\r\n draggable=\"false\" />\r\n <div class=\"fv-crop-mask\"></div>\r\n </div>\r\n\r\n <div class=\"fv-zoom-controls\">\r\n <span class=\"zoom-label\">Zoom</span>\r\n <button type=\"button\" class=\"zoom-btn\" (click)=\"zoomOut()\">-</button>\r\n <input type=\"range\" [min]=\"minScale\" [max]=\"maxScale\" [step]=\"step\" [value]=\"scale\"\r\n (input)=\"onSliderChange($event)\" class=\"zoom-slider\">\r\n <button type=\"button\" class=\"zoom-btn\" (click)=\"zoomIn()\">+</button>\r\n <span class=\"zoom-value\">{{ (scale * 100) | number:'1.0-0' }}%</span>\r\n </div>\r\n\r\n <p class=\"fv-hint-text\">Drag the image to reposition. Use the zoom control to fill the circle.</p>\r\n </div>\r\n\r\n <div class=\"fv-modal-footer\">\r\n <button type=\"button\" class=\"btn-reset\" (click)=\"resetEditor()\">Reset</button>\r\n <div class=\"footer-actions\">\r\n <button type=\"button\" class=\"btn-cancel\" (click)=\"closeEditor()\">Cancel</button>\r\n <button type=\"button\" class=\"btn-apply\" (click)=\"applyCrop()\">Apply</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-image-selector-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif;box-sizing:border-box}.fv-image-selector-container{display:flex;flex-direction:column;margin-bottom:20px;width:100%}.fv-image-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:8px;display:block}.required-asterisk{color:#e74c3c;font-weight:700;margin-left:4px}.fv-upload-btn{display:inline-flex;align-items:center;justify-content:space-between;background:#fff;border:1px solid #dcdcdc;border-radius:8px;padding:0 0 0 20px;font-size:16px;color:#1f2b41;cursor:pointer;overflow:hidden;height:48px;min-width:160px;transition:box-shadow .2s}.fv-upload-btn:hover:not(:disabled){box-shadow:0 2px 5px #0000000d;border-color:#bbb}.fv-upload-btn:disabled{opacity:.6;cursor:not-allowed;background:#f9f9f9}.upload-icon{width:50px;height:100%;padding:12px;background:#f4f5f7;border-left:1px solid #dcdcdc;fill:#333;margin-left:20px}.fv-preview-row{display:flex;align-items:center;gap:16px;padding:5px 0}.fv-preview-wrapper{position:relative;width:60px;height:60px}.fv-circle-preview{width:60px;height:60px;border-radius:50%;border:1px solid #eee;object-fit:cover}.fv-filename{flex:1;font-size:14px;color:#666;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:300px}.fv-trash-btn{background:none;border:none;cursor:pointer;padding:8px;border-radius:50%;transition:background .2s;display:flex}.fv-trash-btn:hover:not(:disabled){background:#fee}.trash-icon{width:24px;height:24px;fill:red}.fv-modal-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#0009;display:flex;align-items:center;justify-content:center;z-index:9999}.fv-modal-content{background:#fff;width:650px;max-width:90vw;border-radius:8px;overflow:hidden;box-shadow:0 10px 30px #0003;display:flex;flex-direction:column}.fv-modal-header{display:flex;justify-content:space-between;align-items:center;padding:16px 24px;border-bottom:1px solid #f0f0f0}.fv-modal-header h3{margin:0;font-size:18px;font-weight:600;color:#1f2b41}.close-icon{background:none;border:none;font-size:24px;line-height:1;cursor:pointer;color:#888}.fv-modal-body{padding:24px;display:flex;flex-direction:column;align-items:center;background:#fff}.fv-crop-container{width:100%;height:350px;background:#333;position:relative;overflow:hidden;margin-bottom:24px;border-radius:4px;cursor:move;cursor:grab;display:flex;align-items:center;justify-content:center}.fv-crop-container:active{cursor:grabbing}.fv-crop-image{max-width:none;max-height:none;-webkit-user-select:none;user-select:none;pointer-events:none;transform-origin:center center}.fv-crop-mask{position:absolute;inset:0;background:radial-gradient(circle at center,transparent 150px,rgba(0,0,0,.6) 151px);pointer-events:none}.fv-zoom-controls{display:flex;align-items:center;gap:16px;width:100%;max-width:500px;margin-bottom:12px}.zoom-label{font-size:14px;font-weight:500;color:#333;min-width:40px}.zoom-btn{width:32px;height:32px;border-radius:50%;background:#e6f0ff;color:#006aff;border:none;font-weight:700;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;padding-bottom:2px}.zoom-btn:hover{background:#d0e1ff}.zoom-slider{flex:1;-webkit-appearance:none;height:4px;background:#ddd;border-radius:2px;outline:none}.zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;background:#006aff;cursor:pointer;box-shadow:0 0 0 4px #006aff33}.zoom-value{font-size:14px;color:#666;min-width:40px;text-align:right}.fv-hint-text{font-size:13px;color:#7f8c8d;margin:0;text-align:center}.fv-modal-footer{padding:16px 24px;border-top:1px solid #f0f0f0;display:flex;justify-content:space-between;align-items:center;background:#fff}.footer-actions{display:flex;gap:12px}.btn-reset,.btn-cancel,.btn-apply{padding:8px 20px;border-radius:6px;font-size:14px;font-weight:500;cursor:pointer;transition:background .2s}.btn-reset{background:#f5f5f5;border:1px solid #ddd;color:#333}.btn-reset:hover{background:#eee}.btn-cancel{background:#fff;border:1px solid #ddd;color:#333}.btn-cancel:hover{border-color:#bbb}.btn-apply{background:#006aff;border:1px solid #006aff;color:#fff}.btn-apply:hover{background:#0056cc;border-color:#0056cc}.fv-image-selector-error-message{margin-top:6px;font-size:12px;color:#e74c3c;display:flex;align-items:center;gap:4px}\n"] }]
1178
1268
  }], ctorParameters: () => [{ type: i1$1.DomSanitizer }], propDecorators: { label: [{
1179
1269
  type: Input
1180
1270
  }], placeholder: [{
@@ -1194,6 +1284,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1194
1284
  }], imageInput: [{
1195
1285
  type: ViewChild,
1196
1286
  args: ['imageInput']
1287
+ }], editorImage: [{
1288
+ type: ViewChild,
1289
+ args: ['editorImage']
1290
+ }], onDrag: [{
1291
+ type: HostListener,
1292
+ args: ['document:mousemove', ['$event']]
1293
+ }], endDrag: [{
1294
+ type: HostListener,
1295
+ args: ['document:mouseup']
1197
1296
  }] } });
1198
1297
 
1199
1298
  class FvRichTextEditorComponent {
@@ -1227,9 +1326,7 @@ class FvRichTextEditorComponent {
1227
1326
  this.valueChange.emit(value);
1228
1327
  });
1229
1328
  // Validate initial value
1230
- if (this.control.value) {
1231
- this.validateValue(this.control.value);
1232
- }
1329
+ this.validateValue(this.control.value);
1233
1330
  }
1234
1331
  ngOnDestroy() {
1235
1332
  this.subscription?.unsubscribe();
@@ -1320,11 +1417,11 @@ class FvRichTextEditorComponent {
1320
1417
  return text.trim() ? text.trim().split(/\s+/).length : 0;
1321
1418
  }
1322
1419
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvRichTextEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1323
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvRichTextEditorComponent, isStandalone: true, selector: "fv-rich-text-editor", inputs: { label: "label", placeholder: "placeholder", schema: "schema", control: "control", disabled: "disabled", readonly: "readonly", minHeight: "minHeight", showToolbar: "showToolbar" }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus" }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true }], ngImport: i0, template: "<div class=\"fv-rich-text-editor-container\">\r\n <label *ngIf=\"label\" class=\"fv-rich-text-editor-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"showToolbar && !disabled && !readonly\" class=\"fv-rich-text-toolbar\">\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('**', '**')\" title=\"Bold\">\r\n <strong>B</strong>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('*', '*')\" title=\"Italic\">\r\n <em>I</em>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('__', '__')\" title=\"Underline\">\r\n <u>U</u>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('~~', '~~')\" title=\"Strikethrough\">\r\n <s>S</s>\r\n </button>\r\n\r\n <div class=\"fv-toolbar-divider\"></div>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"handleBulletList()\" title=\"Bullet List\">\r\n \u2022 List\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"handleNumberedList()\" title=\"Numbered List\">\r\n 1. List\r\n </button>\r\n\r\n <div class=\"fv-toolbar-divider\"></div>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button fv-toolbar-button-danger\" (click)=\"handleClear()\" title=\"Clear\">\r\n Clear\r\n </button>\r\n </div>\r\n\r\n <textarea #editor class=\"fv-rich-text-editor\" [class.fv-rich-text-editor-error]=\"errorMessage\"\r\n [class.fv-rich-text-editor-disabled]=\"disabled\" [class.fv-rich-text-editor-focused]=\"isFocused\"\r\n [formControl]=\"control\" [placeholder]=\"placeholder\" [readonly]=\"readonly\" [disabled]=\"disabled\"\r\n [style.min-height.px]=\"minHeight\" (blur)=\"onBlur()\" (focus)=\"onFocus()\"></textarea>\r\n\r\n <div class=\"fv-rich-text-footer\">\r\n <div class=\"fv-rich-text-stats\">\r\n {{ getCharacterCount() }} characters \u2022 {{ getWordCount() }} words\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-rich-text-editor-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-rich-text-editor-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-rich-text-editor-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-rich-text-toolbar{display:flex;align-items:center;gap:4px;padding:4px;margin-bottom:8px;border:1px solid #dee2e6;border-radius:8px;background-color:#f8f9fa;flex-wrap:wrap}.fv-toolbar-button{padding:6px 10px;background-color:#fff;border:1px solid #dee2e6;border-radius:8px;font-size:14px;font-weight:500;color:#1f2b41;cursor:pointer;transition:background-color .2s,border-color .2s}.fv-toolbar-button:hover{background-color:#e7f3ff;border-color:#3498db}.fv-toolbar-button-danger{color:#e74c3c}.fv-toolbar-button-danger:hover{background-color:#ffe0e0;border-color:#e74c3c}.fv-toolbar-divider{width:1px;height:24px;background-color:#dee2e6;margin:0 4px}.fv-rich-text-editor{padding:10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;background-color:#fff;color:#1f2b41;resize:vertical;transition:border-color .2s,box-shadow .2s;outline:none;box-sizing:border-box;width:100%}.fv-rich-text-editor::placeholder{color:#999}.fv-rich-text-editor:focus:not(:disabled):not([readonly]){border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-rich-text-editor-error{border-color:#e74c3c}.fv-rich-text-editor-disabled,.fv-rich-text-editor[readonly]{background-color:#ecf0f1;opacity:.6;cursor:not-allowed}.fv-rich-text-editor-focused{border-color:#3498db;border-width:1px}.fv-rich-text-footer{display:flex;justify-content:flex-end;margin-top:4px}.fv-rich-text-stats{font-size:12px;color:#6c757d}.fv-rich-text-editor-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
1420
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvRichTextEditorComponent, isStandalone: true, selector: "fv-rich-text-editor", inputs: { label: "label", placeholder: "placeholder", schema: "schema", control: "control", disabled: "disabled", readonly: "readonly", minHeight: "minHeight", showToolbar: "showToolbar" }, outputs: { valueChange: "valueChange", blur: "blur", focus: "focus" }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true }], ngImport: i0, template: "<div class=\"fv-rich-text-editor-container\">\r\n <label *ngIf=\"label\" class=\"fv-rich-text-editor-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"showToolbar && !disabled && !readonly\" class=\"fv-rich-text-toolbar\">\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('**', '**')\" title=\"Bold\">\r\n <strong>B</strong>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('*', '*')\" title=\"Italic\">\r\n <em>I</em>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('__', '__')\" title=\"Underline\">\r\n <u>U</u>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('~~', '~~')\" title=\"Strikethrough\">\r\n <s>S</s>\r\n </button>\r\n\r\n <div class=\"fv-toolbar-divider\"></div>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"handleBulletList()\" title=\"Bullet List\">\r\n \u2022 List\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"handleNumberedList()\" title=\"Numbered List\">\r\n 1. List\r\n </button>\r\n\r\n <div class=\"fv-toolbar-divider\"></div>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button fv-toolbar-button-danger\" (click)=\"handleClear()\" title=\"Clear\">\r\n Clear\r\n </button>\r\n </div>\r\n\r\n <textarea #editor class=\"fv-rich-text-editor\" [class.fv-rich-text-editor-error]=\"errorMessage && control?.touched\"\r\n [class.fv-rich-text-editor-disabled]=\"disabled\" [class.fv-rich-text-editor-focused]=\"isFocused\"\r\n [formControl]=\"control\" [placeholder]=\"placeholder\" [readonly]=\"readonly\" [disabled]=\"disabled\"\r\n [style.min-height.px]=\"minHeight\" (blur)=\"onBlur()\" (focus)=\"onFocus()\"></textarea>\r\n\r\n <div class=\"fv-rich-text-footer\">\r\n <div class=\"fv-rich-text-stats\">\r\n {{ getCharacterCount() }} characters \u2022 {{ getWordCount() }} words\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-rich-text-editor-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-rich-text-editor-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-rich-text-editor-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-rich-text-toolbar{display:flex;align-items:center;gap:4px;padding:4px;margin-bottom:8px;border:1px solid #dee2e6;border-radius:8px;background-color:#f8f9fa;flex-wrap:wrap}.fv-toolbar-button{padding:6px 10px;background-color:#fff;border:1px solid #dee2e6;border-radius:8px;font-size:14px;font-weight:500;color:#1f2b41;cursor:pointer;transition:background-color .2s,border-color .2s}.fv-toolbar-button:hover{background-color:#e7f3ff;border-color:#3498db}.fv-toolbar-button-danger{color:#e74c3c}.fv-toolbar-button-danger:hover{background-color:#ffe0e0;border-color:#e74c3c}.fv-toolbar-divider{width:1px;height:24px;background-color:#dee2e6;margin:0 4px}.fv-rich-text-editor{padding:10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;background-color:#fff;color:#1f2b41;resize:vertical;transition:border-color .2s,box-shadow .2s;outline:none;box-sizing:border-box;width:100%}.fv-rich-text-editor::placeholder{color:#999}.fv-rich-text-editor:focus:not(:disabled):not([readonly]){border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-rich-text-editor-error{border-color:#e74c3c}.fv-rich-text-editor-disabled,.fv-rich-text-editor[readonly]{background-color:#ecf0f1;opacity:.6;cursor:not-allowed}.fv-rich-text-editor-focused{border-color:#3498db;border-width:1px}.fv-rich-text-footer{display:flex;justify-content:flex-end;margin-top:4px}.fv-rich-text-stats{font-size:12px;color:#6c757d}.fv-rich-text-editor-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
1324
1421
  }
1325
1422
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvRichTextEditorComponent, decorators: [{
1326
1423
  type: Component,
1327
- args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-rich-text-editor', template: "<div class=\"fv-rich-text-editor-container\">\r\n <label *ngIf=\"label\" class=\"fv-rich-text-editor-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"showToolbar && !disabled && !readonly\" class=\"fv-rich-text-toolbar\">\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('**', '**')\" title=\"Bold\">\r\n <strong>B</strong>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('*', '*')\" title=\"Italic\">\r\n <em>I</em>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('__', '__')\" title=\"Underline\">\r\n <u>U</u>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('~~', '~~')\" title=\"Strikethrough\">\r\n <s>S</s>\r\n </button>\r\n\r\n <div class=\"fv-toolbar-divider\"></div>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"handleBulletList()\" title=\"Bullet List\">\r\n \u2022 List\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"handleNumberedList()\" title=\"Numbered List\">\r\n 1. List\r\n </button>\r\n\r\n <div class=\"fv-toolbar-divider\"></div>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button fv-toolbar-button-danger\" (click)=\"handleClear()\" title=\"Clear\">\r\n Clear\r\n </button>\r\n </div>\r\n\r\n <textarea #editor class=\"fv-rich-text-editor\" [class.fv-rich-text-editor-error]=\"errorMessage\"\r\n [class.fv-rich-text-editor-disabled]=\"disabled\" [class.fv-rich-text-editor-focused]=\"isFocused\"\r\n [formControl]=\"control\" [placeholder]=\"placeholder\" [readonly]=\"readonly\" [disabled]=\"disabled\"\r\n [style.min-height.px]=\"minHeight\" (blur)=\"onBlur()\" (focus)=\"onFocus()\"></textarea>\r\n\r\n <div class=\"fv-rich-text-footer\">\r\n <div class=\"fv-rich-text-stats\">\r\n {{ getCharacterCount() }} characters \u2022 {{ getWordCount() }} words\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"fv-rich-text-editor-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-rich-text-editor-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-rich-text-editor-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-rich-text-toolbar{display:flex;align-items:center;gap:4px;padding:4px;margin-bottom:8px;border:1px solid #dee2e6;border-radius:8px;background-color:#f8f9fa;flex-wrap:wrap}.fv-toolbar-button{padding:6px 10px;background-color:#fff;border:1px solid #dee2e6;border-radius:8px;font-size:14px;font-weight:500;color:#1f2b41;cursor:pointer;transition:background-color .2s,border-color .2s}.fv-toolbar-button:hover{background-color:#e7f3ff;border-color:#3498db}.fv-toolbar-button-danger{color:#e74c3c}.fv-toolbar-button-danger:hover{background-color:#ffe0e0;border-color:#e74c3c}.fv-toolbar-divider{width:1px;height:24px;background-color:#dee2e6;margin:0 4px}.fv-rich-text-editor{padding:10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;background-color:#fff;color:#1f2b41;resize:vertical;transition:border-color .2s,box-shadow .2s;outline:none;box-sizing:border-box;width:100%}.fv-rich-text-editor::placeholder{color:#999}.fv-rich-text-editor:focus:not(:disabled):not([readonly]){border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-rich-text-editor-error{border-color:#e74c3c}.fv-rich-text-editor-disabled,.fv-rich-text-editor[readonly]{background-color:#ecf0f1;opacity:.6;cursor:not-allowed}.fv-rich-text-editor-focused{border-color:#3498db;border-width:1px}.fv-rich-text-footer{display:flex;justify-content:flex-end;margin-top:4px}.fv-rich-text-stats{font-size:12px;color:#6c757d}.fv-rich-text-editor-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
1424
+ args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-rich-text-editor', template: "<div class=\"fv-rich-text-editor-container\">\r\n <label *ngIf=\"label\" class=\"fv-rich-text-editor-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"required-asterisk\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"showToolbar && !disabled && !readonly\" class=\"fv-rich-text-toolbar\">\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('**', '**')\" title=\"Bold\">\r\n <strong>B</strong>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('*', '*')\" title=\"Italic\">\r\n <em>I</em>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('__', '__')\" title=\"Underline\">\r\n <u>U</u>\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"insertText('~~', '~~')\" title=\"Strikethrough\">\r\n <s>S</s>\r\n </button>\r\n\r\n <div class=\"fv-toolbar-divider\"></div>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"handleBulletList()\" title=\"Bullet List\">\r\n \u2022 List\r\n </button>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button\" (click)=\"handleNumberedList()\" title=\"Numbered List\">\r\n 1. List\r\n </button>\r\n\r\n <div class=\"fv-toolbar-divider\"></div>\r\n\r\n <button type=\"button\" class=\"fv-toolbar-button fv-toolbar-button-danger\" (click)=\"handleClear()\" title=\"Clear\">\r\n Clear\r\n </button>\r\n </div>\r\n\r\n <textarea #editor class=\"fv-rich-text-editor\" [class.fv-rich-text-editor-error]=\"errorMessage && control?.touched\"\r\n [class.fv-rich-text-editor-disabled]=\"disabled\" [class.fv-rich-text-editor-focused]=\"isFocused\"\r\n [formControl]=\"control\" [placeholder]=\"placeholder\" [readonly]=\"readonly\" [disabled]=\"disabled\"\r\n [style.min-height.px]=\"minHeight\" (blur)=\"onBlur()\" (focus)=\"onFocus()\"></textarea>\r\n\r\n <div class=\"fv-rich-text-footer\">\r\n <div class=\"fv-rich-text-stats\">\r\n {{ getCharacterCount() }} characters \u2022 {{ getWordCount() }} words\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-rich-text-editor-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-rich-text-editor-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-rich-text-editor-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-rich-text-toolbar{display:flex;align-items:center;gap:4px;padding:4px;margin-bottom:8px;border:1px solid #dee2e6;border-radius:8px;background-color:#f8f9fa;flex-wrap:wrap}.fv-toolbar-button{padding:6px 10px;background-color:#fff;border:1px solid #dee2e6;border-radius:8px;font-size:14px;font-weight:500;color:#1f2b41;cursor:pointer;transition:background-color .2s,border-color .2s}.fv-toolbar-button:hover{background-color:#e7f3ff;border-color:#3498db}.fv-toolbar-button-danger{color:#e74c3c}.fv-toolbar-button-danger:hover{background-color:#ffe0e0;border-color:#e74c3c}.fv-toolbar-divider{width:1px;height:24px;background-color:#dee2e6;margin:0 4px}.fv-rich-text-editor{padding:10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;font-family:Poppins,sans-serif;background-color:#fff;color:#1f2b41;resize:vertical;transition:border-color .2s,box-shadow .2s;outline:none;box-sizing:border-box;width:100%}.fv-rich-text-editor::placeholder{color:#999}.fv-rich-text-editor:focus:not(:disabled):not([readonly]){border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-rich-text-editor-error{border-color:#e74c3c}.fv-rich-text-editor-disabled,.fv-rich-text-editor[readonly]{background-color:#ecf0f1;opacity:.6;cursor:not-allowed}.fv-rich-text-editor-focused{border-color:#3498db;border-width:1px}.fv-rich-text-footer{display:flex;justify-content:flex-end;margin-top:4px}.fv-rich-text-stats{font-size:12px;color:#6c757d}.fv-rich-text-editor-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
1328
1425
  }], propDecorators: { label: [{
1329
1426
  type: Input
1330
1427
  }], placeholder: [{
@@ -1580,7 +1677,7 @@ class FvNameCodeComponent {
1580
1677
  </div>
1581
1678
 
1582
1679
  <div *ngIf="isInvalid()" class="error-message">
1583
- {{ getErrorMessage() }}
1680
+ {{ getErrorMessage() }}
1584
1681
  </div>
1585
1682
  </div>
1586
1683
  `, isInline: true, styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-name-code-container{position:relative;width:100%;margin-bottom:0}.fv-label{display:block;font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.required-asterisk{color:#e74c3c;margin-left:2px}.search-box-wrapper{position:relative;display:flex;align-items:center}.fv-input{width:100%;padding:5px 10px;height:34px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;color:#1f2b41;outline:none;transition:all .2s;background-color:#fff;box-sizing:border-box;font-family:Poppins,sans-serif;min-width:0}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input.error{border-color:#e74c3c}.fv-input:disabled{background-color:#f5f5f5;cursor:not-allowed}.dropdown-list{position:absolute;top:100%;left:0;min-width:100%;width:max-content;max-width:300px;background:#fff;border:1px solid #ddd;border-radius:8px;margin-top:4px;max-height:200px;overflow-y:auto;overflow-x:hidden;z-index:1000;box-shadow:0 4px 6px #0000001a}.dropdown-item{padding:10px 12px;display:flex;justify-content:space-between;align-items:center;cursor:pointer;border-bottom:1px solid #f0f0f0;gap:12px}.dropdown-item:last-child{border-bottom:none}.dropdown-item:hover,.dropdown-item.highlighted{background-color:#f0f7ff}.code{color:#0056b3;font-weight:600;font-size:14px;white-space:nowrap}.name{color:#333;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:right}.no-results{padding:10px;color:#999;text-align:center;font-size:13px}.error-message{color:#e74c3c;font-size:12px;margin-top:4px;display:flex;align-items:center;gap:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
@@ -1622,7 +1719,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1622
1719
  </div>
1623
1720
 
1624
1721
  <div *ngIf="isInvalid()" class="error-message">
1625
- {{ getErrorMessage() }}
1722
+ {{ getErrorMessage() }}
1626
1723
  </div>
1627
1724
  </div>
1628
1725
  `, providers: [
@@ -1689,9 +1786,7 @@ class FvPhoneFieldComponent {
1689
1786
  this.subscription = this.control.valueChanges.subscribe((value) => {
1690
1787
  this.validateValue(value);
1691
1788
  });
1692
- if (this.control.value) {
1693
- this.validateValue(this.control.value);
1694
- }
1789
+ this.validateValue(this.control.value);
1695
1790
  }
1696
1791
  ngOnDestroy() {
1697
1792
  this.subscription?.unsubscribe();
@@ -1742,12 +1837,23 @@ class FvPhoneFieldComponent {
1742
1837
  isRequired() {
1743
1838
  return this.schema?.rules?.some(r => r.name === 'required' && r.params?.['enabled']) || false;
1744
1839
  }
1840
+ getErrorMessage() {
1841
+ if (!this.errorMessage)
1842
+ return '';
1843
+ const errorMessages = {
1844
+ ERR_REQUIRED: 'Phone number is required',
1845
+ ERR_MIN_LENGTH: 'Invalid phone number',
1846
+ ERR_MAX_LENGTH: 'Invalid phone number',
1847
+ ERR_REGEX_MISMATCH: 'Invalid phone number format',
1848
+ };
1849
+ return errorMessages[this.errorMessage] || this.errorMessage;
1850
+ }
1745
1851
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvPhoneFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1746
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvPhoneFieldComponent, isStandalone: true, selector: "fv-phone-field", inputs: { label: "label", control: "control", disabled: "disabled", schema: "schema" }, outputs: { blur: "blur", focus: "focus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-phone-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-phone-input-group\">\r\n <div class=\"fv-select-wrapper\">\r\n <select class=\"fv-country-code-select\" [disabled]=\"disabled\" [(ngModel)]=\"countryCode\">\r\n <option *ngFor=\"let code of countryCodes\" [value]=\"code\">{{code}}</option>\r\n </select>\r\n <i class=\"material-icons fv-select-arrow\">arrow_drop_down</i>\r\n </div>\r\n <input type=\"text\" [formControl]=\"control\" (input)=\"onInput($event)\" (blur)=\"onBlur()\" (focus)=\"onFocus()\"\r\n placeholder=\"10 digit mobile number\" class=\"fv-input fv-phone-input\"\r\n [class.fv-input-error]=\"errorMessage\" />\r\n </div>\r\n\r\n <span *ngIf=\"errorMessage\" class=\"fv-error-message\">\r\n {{ errorMessage }}\r\n </span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.material-icons{font-family:Material Icons!important}.fv-phone-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-phone-input-group{display:flex;align-items:center}.fv-country-code-select{padding:5px 24px 5px 8px;border:1px solid #8CBBA8;border-radius:8px 0 0 8px;background-color:#f8f9fa;border-right:none;outline:none;cursor:pointer;font-size:14px;font-weight:400;color:#1f2b41;height:34px;width:60px;box-sizing:border-box;appearance:none;-webkit-appearance:none;-moz-appearance:none}.fv-select-wrapper{position:relative;display:flex;align-items:center}.fv-select-arrow{position:absolute;right:4px;top:50%;transform:translateY(-50%);pointer-events:none;color:#666;font-size:20px}.fv-phone-input{flex:1;border-radius:0 8px 8px 0!important}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;transition:border-color .2s;width:100%;box-sizing:border-box;height:34px;background-color:#fff;color:#1f2b41;min-width:0}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
1852
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvPhoneFieldComponent, isStandalone: true, selector: "fv-phone-field", inputs: { label: "label", control: "control", disabled: "disabled", schema: "schema" }, outputs: { blur: "blur", focus: "focus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-phone-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-phone-input-group\">\r\n <div class=\"fv-select-wrapper\">\r\n <select class=\"fv-country-code-select\" [disabled]=\"disabled\" [(ngModel)]=\"countryCode\">\r\n <option *ngFor=\"let code of countryCodes\" [value]=\"code\">{{code}}</option>\r\n </select>\r\n <i class=\"material-icons fv-select-arrow\">arrow_drop_down</i>\r\n </div>\r\n <input type=\"text\" [formControl]=\"control\" (input)=\"onInput($event)\" (blur)=\"onBlur()\" (focus)=\"onFocus()\"\r\n placeholder=\"10 digit mobile number\" class=\"fv-input fv-phone-input\"\r\n [class.fv-input-error]=\"errorMessage && control?.touched\" />\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.material-icons{font-family:Material Icons!important}.fv-phone-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-phone-input-group{display:flex;align-items:center}.fv-country-code-select{padding:5px 24px 5px 8px;border:1px solid #8CBBA8;border-radius:8px 0 0 8px;background-color:#f8f9fa;border-right:none;outline:none;cursor:pointer;font-size:14px;font-weight:400;color:#1f2b41;height:34px;width:60px;box-sizing:border-box;appearance:none;-webkit-appearance:none;-moz-appearance:none}.fv-select-wrapper{position:relative;display:flex;align-items:center}.fv-select-arrow{position:absolute;right:4px;top:50%;transform:translateY(-50%);pointer-events:none;color:#666;font-size:20px}.fv-phone-input{flex:1;border-radius:0 8px 8px 0!important}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;transition:border-color .2s;width:100%;box-sizing:border-box;height:34px;background-color:#fff;color:#1f2b41;min-width:0}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
1747
1853
  }
1748
1854
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvPhoneFieldComponent, decorators: [{
1749
1855
  type: Component,
1750
- args: [{ selector: 'fv-phone-field', standalone: true, imports: [CommonModule, ReactiveFormsModule, FormsModule], template: "<div class=\"fv-phone-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-phone-input-group\">\r\n <div class=\"fv-select-wrapper\">\r\n <select class=\"fv-country-code-select\" [disabled]=\"disabled\" [(ngModel)]=\"countryCode\">\r\n <option *ngFor=\"let code of countryCodes\" [value]=\"code\">{{code}}</option>\r\n </select>\r\n <i class=\"material-icons fv-select-arrow\">arrow_drop_down</i>\r\n </div>\r\n <input type=\"text\" [formControl]=\"control\" (input)=\"onInput($event)\" (blur)=\"onBlur()\" (focus)=\"onFocus()\"\r\n placeholder=\"10 digit mobile number\" class=\"fv-input fv-phone-input\"\r\n [class.fv-input-error]=\"errorMessage\" />\r\n </div>\r\n\r\n <span *ngIf=\"errorMessage\" class=\"fv-error-message\">\r\n {{ errorMessage }}\r\n </span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.material-icons{font-family:Material Icons!important}.fv-phone-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-phone-input-group{display:flex;align-items:center}.fv-country-code-select{padding:5px 24px 5px 8px;border:1px solid #8CBBA8;border-radius:8px 0 0 8px;background-color:#f8f9fa;border-right:none;outline:none;cursor:pointer;font-size:14px;font-weight:400;color:#1f2b41;height:34px;width:60px;box-sizing:border-box;appearance:none;-webkit-appearance:none;-moz-appearance:none}.fv-select-wrapper{position:relative;display:flex;align-items:center}.fv-select-arrow{position:absolute;right:4px;top:50%;transform:translateY(-50%);pointer-events:none;color:#666;font-size:20px}.fv-phone-input{flex:1;border-radius:0 8px 8px 0!important}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;transition:border-color .2s;width:100%;box-sizing:border-box;height:34px;background-color:#fff;color:#1f2b41;min-width:0}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
1856
+ args: [{ selector: 'fv-phone-field', standalone: true, imports: [CommonModule, ReactiveFormsModule, FormsModule], template: "<div class=\"fv-phone-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n\r\n <div class=\"fv-phone-input-group\">\r\n <div class=\"fv-select-wrapper\">\r\n <select class=\"fv-country-code-select\" [disabled]=\"disabled\" [(ngModel)]=\"countryCode\">\r\n <option *ngFor=\"let code of countryCodes\" [value]=\"code\">{{code}}</option>\r\n </select>\r\n <i class=\"material-icons fv-select-arrow\">arrow_drop_down</i>\r\n </div>\r\n <input type=\"text\" [formControl]=\"control\" (input)=\"onInput($event)\" (blur)=\"onBlur()\" (focus)=\"onFocus()\"\r\n placeholder=\"10 digit mobile number\" class=\"fv-input fv-phone-input\"\r\n [class.fv-input-error]=\"errorMessage && control?.touched\" />\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.material-icons{font-family:Material Icons!important}.fv-phone-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-phone-input-group{display:flex;align-items:center}.fv-country-code-select{padding:5px 24px 5px 8px;border:1px solid #8CBBA8;border-radius:8px 0 0 8px;background-color:#f8f9fa;border-right:none;outline:none;cursor:pointer;font-size:14px;font-weight:400;color:#1f2b41;height:34px;width:60px;box-sizing:border-box;appearance:none;-webkit-appearance:none;-moz-appearance:none}.fv-select-wrapper{position:relative;display:flex;align-items:center}.fv-select-arrow{position:absolute;right:4px;top:50%;transform:translateY(-50%);pointer-events:none;color:#666;font-size:20px}.fv-phone-input{flex:1;border-radius:0 8px 8px 0!important}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;transition:border-color .2s;width:100%;box-sizing:border-box;height:34px;background-color:#fff;color:#1f2b41;min-width:0}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
1751
1857
  }], propDecorators: { label: [{
1752
1858
  type: Input
1753
1859
  }], control: [{
@@ -2063,6 +2169,7 @@ class FvMicrFieldComponent {
2063
2169
  if (!this.control)
2064
2170
  return;
2065
2171
  this.subscription = this.control.valueChanges.subscribe((value) => this.validateValue(value));
2172
+ this.validateValue(this.control.value);
2066
2173
  }
2067
2174
  ngOnDestroy() { this.subscription?.unsubscribe(); }
2068
2175
  onInput(event) {
@@ -2087,11 +2194,11 @@ class FvMicrFieldComponent {
2087
2194
  }
2088
2195
  isRequired() { return this.schema?.rules?.some(r => r.name === 'required' && r.params?.['enabled']) || false; }
2089
2196
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvMicrFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2090
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvMicrFieldComponent, isStandalone: true, selector: "fv-micr-field", inputs: { label: "label", control: "control", disabled: "disabled", schema: "schema" }, outputs: { blur: "blur", focus: "focus" }, ngImport: i0, template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n <input type=\"text\" [formControl]=\"control\" [disabled]=\"disabled\" (input)=\"onInput($event)\"\r\n placeholder=\"9 digit MICR\" class=\"fv-input\" [class.fv-input-error]=\"errorMessage\" />\r\n <span *ngIf=\"errorMessage\" class=\"fv-error-message\">{{ errorMessage }}</span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-micr-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
2197
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvMicrFieldComponent, isStandalone: true, selector: "fv-micr-field", inputs: { label: "label", control: "control", disabled: "disabled", schema: "schema" }, outputs: { blur: "blur", focus: "focus" }, ngImport: i0, template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n <input type=\"text\" [formControl]=\"control\" [disabled]=\"disabled\" (input)=\"onInput($event)\"\r\n placeholder=\"9 digit MICR\" class=\"fv-input\" [class.fv-input-error]=\"errorMessage && control?.touched\" />\r\n <span *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">{{ errorMessage }}</span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-micr-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
2091
2198
  }
2092
2199
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvMicrFieldComponent, decorators: [{
2093
2200
  type: Component,
2094
- args: [{ selector: 'fv-micr-field', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n <input type=\"text\" [formControl]=\"control\" [disabled]=\"disabled\" (input)=\"onInput($event)\"\r\n placeholder=\"9 digit MICR\" class=\"fv-input\" [class.fv-input-error]=\"errorMessage\" />\r\n <span *ngIf=\"errorMessage\" class=\"fv-error-message\">{{ errorMessage }}</span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-micr-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
2201
+ args: [{ selector: 'fv-micr-field', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n <input type=\"text\" [formControl]=\"control\" [disabled]=\"disabled\" (input)=\"onInput($event)\"\r\n placeholder=\"9 digit MICR\" class=\"fv-input\" [class.fv-input-error]=\"errorMessage && control?.touched\" />\r\n <span *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">{{ errorMessage }}</span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-micr-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
2095
2202
  }], propDecorators: { label: [{
2096
2203
  type: Input
2097
2204
  }], control: [{
@@ -2119,6 +2226,7 @@ class FvIbanFieldComponent {
2119
2226
  if (!this.control)
2120
2227
  return;
2121
2228
  this.subscription = this.control.valueChanges.subscribe((value) => this.validateValue(value));
2229
+ this.validateValue(this.control.value);
2122
2230
  }
2123
2231
  ngOnDestroy() { this.subscription?.unsubscribe(); }
2124
2232
  ngOnChanges(changes) {
@@ -2153,11 +2261,11 @@ class FvIbanFieldComponent {
2153
2261
  }
2154
2262
  isRequired() { return this.schema?.rules?.some(r => r.name === 'required' && r.params?.['enabled']) || false; }
2155
2263
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvIbanFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2156
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvIbanFieldComponent, isStandalone: true, selector: "fv-iban-field", inputs: { label: "label", control: "control", disabled: "disabled", schema: "schema" }, outputs: { blur: "blur", focus: "focus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n <input type=\"text\" [formControl]=\"control\" (input)=\"onInput($event)\" placeholder=\"IBAN\" class=\"fv-input\"\r\n [class.fv-input-error]=\"errorMessage\" />\r\n <span *ngIf=\"errorMessage\" class=\"fv-error-message\">{{ errorMessage }}</span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-iban-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
2264
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvIbanFieldComponent, isStandalone: true, selector: "fv-iban-field", inputs: { label: "label", control: "control", disabled: "disabled", schema: "schema" }, outputs: { blur: "blur", focus: "focus" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n <input type=\"text\" [formControl]=\"control\" (input)=\"onInput($event)\" placeholder=\"IBAN\" class=\"fv-input\"\r\n [class.fv-input-error]=\"errorMessage && control?.touched\" />\r\n <span *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">{{ errorMessage }}</span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-iban-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
2157
2265
  }
2158
2266
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvIbanFieldComponent, decorators: [{
2159
2267
  type: Component,
2160
- args: [{ selector: 'fv-iban-field', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n <input type=\"text\" [formControl]=\"control\" (input)=\"onInput($event)\" placeholder=\"IBAN\" class=\"fv-input\"\r\n [class.fv-input-error]=\"errorMessage\" />\r\n <span *ngIf=\"errorMessage\" class=\"fv-error-message\">{{ errorMessage }}</span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-iban-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
2268
+ args: [{ selector: 'fv-iban-field', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: "<div class=\"fv-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-label\">\r\n {{ label }}\r\n <span *ngIf=\"isRequired()\" class=\"fv-required-asterisk\">*</span>\r\n </label>\r\n <input type=\"text\" [formControl]=\"control\" (input)=\"onInput($event)\" placeholder=\"IBAN\" class=\"fv-input\"\r\n [class.fv-input-error]=\"errorMessage && control?.touched\" />\r\n <span *ngIf=\"errorMessage && control?.touched\" class=\"fv-error-message\">{{ errorMessage }}</span>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif}.fv-iban-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:100%}.fv-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px}.fv-required-asterisk{color:#e74c3c;margin-left:2px}.fv-input{padding:5px 10px;border:1px solid #8CBBA8;border-radius:8px;font-size:14px;font-weight:400;outline:none;width:100%;box-sizing:border-box;background-color:#fff;color:#1f2b41;transition:border-color .2s;height:34px}.fv-input:focus{border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.fv-input-error{border-color:#dc3545!important}.fv-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
2161
2269
  }], propDecorators: { label: [{
2162
2270
  type: Input
2163
2271
  }], control: [{
@@ -2210,10 +2318,7 @@ class FvEmailFieldComponent {
2210
2318
  this.validateValue(value);
2211
2319
  this.valueChange.emit(value);
2212
2320
  });
2213
- // Validate initial value
2214
- if (this.control.value) {
2215
- this.validateValue(this.control.value);
2216
- }
2321
+ this.validateValue(this.control.value);
2217
2322
  }
2218
2323
  ngOnDestroy() {
2219
2324
  this.subscription?.unsubscribe();
@@ -2312,9 +2417,7 @@ class FvPasswordFieldComponent {
2312
2417
  this.valueChange.emit(value);
2313
2418
  });
2314
2419
  // Validate initial value
2315
- if (this.control.value) {
2316
- this.validateValue(this.control.value);
2317
- }
2420
+ this.validateValue(this.control.value);
2318
2421
  }
2319
2422
  ngOnDestroy() {
2320
2423
  this.subscription?.unsubscribe();
@@ -2399,6 +2502,109 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2399
2502
  type: Output
2400
2503
  }] } });
2401
2504
 
2505
+ class FvServicePeriodComponent {
2506
+ label = 'Service Period';
2507
+ group;
2508
+ startField;
2509
+ endField;
2510
+ control;
2511
+ years = 0;
2512
+ months = 0;
2513
+ days = 0;
2514
+ totalDays = 0;
2515
+ sub = new Subscription();
2516
+ ngOnInit() {
2517
+ if (this.group && this.startField && this.endField) {
2518
+ const startCtrl = this.group.get(this.startField);
2519
+ const endCtrl = this.group.get(this.endField);
2520
+ if (startCtrl) {
2521
+ this.sub.add(startCtrl.valueChanges.subscribe(() => this.calculatePeriod()));
2522
+ }
2523
+ if (endCtrl) {
2524
+ this.sub.add(endCtrl.valueChanges.subscribe(() => this.calculatePeriod()));
2525
+ }
2526
+ if (this.control) {
2527
+ this.sub.add(this.control.valueChanges.subscribe(() => this.calculatePeriod()));
2528
+ }
2529
+ // Initial calc
2530
+ this.calculatePeriod();
2531
+ }
2532
+ }
2533
+ ngOnDestroy() {
2534
+ this.sub.unsubscribe();
2535
+ }
2536
+ calculatePeriod() {
2537
+ if (!this.group)
2538
+ return;
2539
+ const startVal = this.group.get(this.startField)?.value;
2540
+ const endVal = this.group.get(this.endField)?.value;
2541
+ if (!startVal || !endVal) {
2542
+ this.resetValues();
2543
+ return;
2544
+ }
2545
+ const start = new Date(startVal);
2546
+ const end = new Date(endVal);
2547
+ if (isNaN(start.getTime()) || isNaN(end.getTime())) {
2548
+ this.resetValues();
2549
+ return;
2550
+ }
2551
+ if (end < start) {
2552
+ this.resetValues();
2553
+ return;
2554
+ }
2555
+ // Calculate difference
2556
+ let years = end.getFullYear() - start.getFullYear();
2557
+ let months = end.getMonth() - start.getMonth();
2558
+ let days = end.getDate() - start.getDate();
2559
+ if (days < 0) {
2560
+ months--;
2561
+ // Days in previous month
2562
+ const prevMonth = new Date(end.getFullYear(), end.getMonth(), 0);
2563
+ days += prevMonth.getDate();
2564
+ }
2565
+ if (months < 0) {
2566
+ years--;
2567
+ months += 12;
2568
+ }
2569
+ // Apply Rounding if checkbox is checked
2570
+ if (this.control && this.control.value === true) {
2571
+ if (months >= 6) {
2572
+ years++;
2573
+ }
2574
+ months = 0;
2575
+ days = 0;
2576
+ }
2577
+ this.years = years;
2578
+ this.months = months;
2579
+ this.days = days;
2580
+ // Total Days
2581
+ const diffTime = Math.abs(end.getTime() - start.getTime());
2582
+ this.totalDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
2583
+ }
2584
+ resetValues() {
2585
+ this.years = 0;
2586
+ this.months = 0;
2587
+ this.days = 0;
2588
+ this.totalDays = 0;
2589
+ }
2590
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvServicePeriodComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2591
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvServicePeriodComponent, isStandalone: true, selector: "fv-service-period", inputs: { label: "label", group: "group", startField: "startField", endField: "endField", control: "control" }, ngImport: i0, template: "<div class=\"service-period-card\">\r\n <div class=\"sp-header\">\r\n <div class=\"sp-title-group\">\r\n <span class=\"sp-label\">{{ label }}</span>\r\n <svg class=\"sp-icon\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"#151D48\">\r\n <path\r\n d=\"M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z\" />\r\n </svg>\r\n </div>\r\n <button type=\"button\" class=\"btn-edit\">\r\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"#666\">\r\n <path\r\n d=\"M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z\" />\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <div class=\"sp-content\">\r\n <div class=\"sp-duration\">\r\n {{ years }} Years, {{ months }} Months, {{ days }} Days\r\n </div>\r\n <div class=\"sp-total\">\r\n Total: {{ totalDays }} days\r\n </div>\r\n </div>\r\n\r\n <div class=\"sp-divider\"></div>\r\n\r\n <div class=\"sp-footer\" *ngIf=\"control\">\r\n <label class=\"sp-checkbox-label\">\r\n <input type=\"checkbox\" [formControl]=\"control\">\r\n Round to years (if months >= 6)\r\n </label>\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif;box-sizing:border-box}.service-period-card{background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:20px;box-shadow:0 1px 3px #0000000d;width:100%}.sp-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:25px}.sp-title-group{display:flex;align-items:center;gap:8px}.sp-label{font-size:18px;font-weight:700;color:#1f2b41}.sp-icon{fill:#006aff}.btn-edit{background:none;border:none;cursor:pointer;padding:4px;color:#666;transition:color .2s}.btn-edit:hover{color:#333}.sp-content{text-align:center;margin-bottom:20px}.sp-duration{font-size:18px;font-weight:600;color:#333;margin-bottom:8px}.sp-total{font-size:13px;color:#666;font-weight:500}.sp-divider{height:1px;background:#e0e0e0;margin:15px 0}.sp-footer{display:flex;align-items:center;padding-left:5px}.sp-checkbox-label{display:flex;align-items:center;gap:10px;font-size:14px;color:#333;font-weight:500;cursor:pointer}input[type=checkbox]{width:18px;height:18px;cursor:pointer;border:1px solid #ccc;border-radius:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
2592
+ }
2593
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvServicePeriodComponent, decorators: [{
2594
+ type: Component,
2595
+ args: [{ selector: 'fv-service-period', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: "<div class=\"service-period-card\">\r\n <div class=\"sp-header\">\r\n <div class=\"sp-title-group\">\r\n <span class=\"sp-label\">{{ label }}</span>\r\n <svg class=\"sp-icon\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"#151D48\">\r\n <path\r\n d=\"M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z\" />\r\n </svg>\r\n </div>\r\n <button type=\"button\" class=\"btn-edit\">\r\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"#666\">\r\n <path\r\n d=\"M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z\" />\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <div class=\"sp-content\">\r\n <div class=\"sp-duration\">\r\n {{ years }} Years, {{ months }} Months, {{ days }} Days\r\n </div>\r\n <div class=\"sp-total\">\r\n Total: {{ totalDays }} days\r\n </div>\r\n </div>\r\n\r\n <div class=\"sp-divider\"></div>\r\n\r\n <div class=\"sp-footer\" *ngIf=\"control\">\r\n <label class=\"sp-checkbox-label\">\r\n <input type=\"checkbox\" [formControl]=\"control\">\r\n Round to years (if months >= 6)\r\n </label>\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";*{font-family:Poppins,sans-serif;box-sizing:border-box}.service-period-card{background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:20px;box-shadow:0 1px 3px #0000000d;width:100%}.sp-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:25px}.sp-title-group{display:flex;align-items:center;gap:8px}.sp-label{font-size:18px;font-weight:700;color:#1f2b41}.sp-icon{fill:#006aff}.btn-edit{background:none;border:none;cursor:pointer;padding:4px;color:#666;transition:color .2s}.btn-edit:hover{color:#333}.sp-content{text-align:center;margin-bottom:20px}.sp-duration{font-size:18px;font-weight:600;color:#333;margin-bottom:8px}.sp-total{font-size:13px;color:#666;font-weight:500}.sp-divider{height:1px;background:#e0e0e0;margin:15px 0}.sp-footer{display:flex;align-items:center;padding-left:5px}.sp-checkbox-label{display:flex;align-items:center;gap:10px;font-size:14px;color:#333;font-weight:500;cursor:pointer}input[type=checkbox]{width:18px;height:18px;cursor:pointer;border:1px solid #ccc;border-radius:4px}\n"] }]
2596
+ }], propDecorators: { label: [{
2597
+ type: Input
2598
+ }], group: [{
2599
+ type: Input
2600
+ }], startField: [{
2601
+ type: Input
2602
+ }], endField: [{
2603
+ type: Input
2604
+ }], control: [{
2605
+ type: Input
2606
+ }] } });
2607
+
2402
2608
  // add-update-form.component.ts
2403
2609
  class AddUpdateFormComponent {
2404
2610
  fb;
@@ -2527,44 +2733,120 @@ class AddUpdateFormComponent {
2527
2733
  return (column.type === 'file' &&
2528
2734
  (column.accept?.startsWith('image') || column.filePreview === true));
2529
2735
  }
2736
+ // Helpers for Repeatable Sections
2737
+ getSectionArray(section) {
2738
+ return this.form.get(section.sectionKey);
2739
+ }
2740
+ createSectionGroup(section) {
2741
+ const groupConfig = {};
2742
+ section.fields.forEach((column) => {
2743
+ let initialValue = column.value || '';
2744
+ if (column.type === 'month-year' && initialValue) {
2745
+ initialValue = this.formatMonthYearValue(initialValue);
2746
+ }
2747
+ groupConfig[column.name] = [
2748
+ { value: initialValue, disabled: column.disabled || false },
2749
+ ];
2750
+ });
2751
+ const group = this.fb.group(groupConfig);
2752
+ // Apply Validators
2753
+ section.fields.forEach((column) => {
2754
+ const validators = this.buildValidators(column.validations, group);
2755
+ if (validators.length > 0) {
2756
+ group.get(column.name)?.setValidators(validators);
2757
+ group.get(column.name)?.updateValueAndValidity();
2758
+ }
2759
+ });
2760
+ return group;
2761
+ }
2762
+ addSectionItem(section) {
2763
+ const array = this.getSectionArray(section);
2764
+ array.push(this.createSectionGroup(section));
2765
+ }
2766
+ removeSectionItem(section, index) {
2767
+ const array = this.getSectionArray(section);
2768
+ if (array.length > 1) {
2769
+ array.removeAt(index);
2770
+ }
2771
+ }
2772
+ isControlInvalid(control) {
2773
+ return !!(control && control.invalid && (control.touched || this.submitted));
2774
+ }
2775
+ getErrorMessageForControl(column, control) {
2776
+ if (!control?.errors)
2777
+ return '';
2778
+ const field = control;
2779
+ const errors = field.errors;
2780
+ if (errors['custom']) {
2781
+ const customValidation = column?.validations?.find((v) => v.type === 'custom');
2782
+ return customValidation?.message || 'Invalid field';
2783
+ }
2784
+ const errorKeys = Object.keys(errors);
2785
+ for (const key of errorKeys) {
2786
+ const validation = column?.validations?.find((v) => v.type === key);
2787
+ if (validation && validation.message) {
2788
+ return validation.message;
2789
+ }
2790
+ }
2791
+ if (errors['required'])
2792
+ return `${column?.label} is required`;
2793
+ if (errors['email'])
2794
+ return 'Invalid email format';
2795
+ if (errors['minlength'])
2796
+ return `Minimum length is ${errors['minlength'].requiredLength}`;
2797
+ if (errors['maxlength'])
2798
+ return `Maximum length is ${errors['maxlength'].requiredLength}`;
2799
+ if (errors['min'])
2800
+ return `Minimum value is ${errors['min'].min}`;
2801
+ if (errors['max'])
2802
+ return `Maximum value is ${errors['max'].max}`;
2803
+ if (errors['pattern'])
2804
+ return 'Invalid format';
2805
+ return 'Invalid field';
2806
+ }
2530
2807
  initializeForm() {
2531
2808
  this.submitted = false;
2532
2809
  const formGroupConfig = {};
2533
2810
  this.config.sections.forEach((section) => {
2534
- section.fields.forEach((column) => {
2535
- let initialValue = column.value || '';
2536
- // Format month-year fields to YYYY-MM format
2537
- if (column.type === 'month-year' && initialValue) {
2538
- initialValue = this.formatMonthYearValue(initialValue);
2539
- }
2540
- formGroupConfig[column.name] = [
2541
- { value: initialValue, disabled: column.disabled || false },
2542
- null, // We'll set validators after form creation
2543
- ];
2544
- // If it's a file field with an existing value, set the preview
2545
- if (column.type === 'file' && column.value) {
2546
- this.filePreviews.set(column.name, column.value);
2547
- // Extract filename from URL or base64
2548
- const fileName = this.extractFileName(column.value);
2549
- if (fileName) {
2550
- this.fileNames.set(column.name, fileName);
2811
+ if (section.isRepeatable && section.sectionKey) {
2812
+ formGroupConfig[section.sectionKey] = this.fb.array([
2813
+ this.createSectionGroup(section),
2814
+ ]);
2815
+ }
2816
+ else {
2817
+ section.fields.forEach((column) => {
2818
+ let initialValue = column.value || '';
2819
+ // Format month-year fields to YYYY-MM format
2820
+ if (column.type === 'month-year' && initialValue) {
2821
+ initialValue = this.formatMonthYearValue(initialValue);
2551
2822
  }
2552
- }
2553
- // Initialize search display for name-code fields - No longer needed as FvNameCode handles display internally
2554
- /* if (column.type === 'name-code' && initialValue && column.options) {
2555
- // Logic moved to component
2556
- } */
2557
- });
2823
+ formGroupConfig[column.name] = [
2824
+ { value: initialValue, disabled: column.disabled || false },
2825
+ null, // We'll set validators after form creation
2826
+ ];
2827
+ // If it's a file field with an existing value, set the preview
2828
+ if (column.type === 'file' && column.value) {
2829
+ this.filePreviews.set(column.name, column.value);
2830
+ // Extract filename from URL or base64
2831
+ const fileName = this.extractFileName(column.value);
2832
+ if (fileName) {
2833
+ this.fileNames.set(column.name, fileName);
2834
+ }
2835
+ }
2836
+ });
2837
+ }
2558
2838
  });
2559
2839
  this.form = this.fb.group(formGroupConfig);
2560
2840
  this.config.sections.forEach((section) => {
2561
- section.fields.forEach((column) => {
2562
- const validators = this.buildValidators(column.validations, this.form);
2563
- if (validators.length > 0) {
2564
- this.form.get(column.name)?.setValidators(validators);
2565
- this.form.get(column.name)?.updateValueAndValidity();
2566
- }
2567
- });
2841
+ if (!section.isRepeatable) {
2842
+ section.fields.forEach((column) => {
2843
+ const validators = this.buildValidators(column.validations, this.form);
2844
+ if (validators.length > 0) {
2845
+ this.form.get(column.name)?.setValidators(validators);
2846
+ this.form.get(column.name)?.updateValueAndValidity();
2847
+ }
2848
+ });
2849
+ }
2568
2850
  });
2569
2851
  // Subscribe to value changes for all fields to trigger onChange handlers
2570
2852
  Object.keys(this.form.controls).forEach((key) => {
@@ -2587,9 +2869,9 @@ class AddUpdateFormComponent {
2587
2869
  this.valueChangeSubscriptions.forEach((sub) => sub.unsubscribe());
2588
2870
  this.valueChangeSubscriptions = [];
2589
2871
  }
2590
- handleFieldChange(fieldName, eventValue) {
2872
+ handleFieldChange(fieldName, eventValue, control) {
2591
2873
  const column = this.getColumnByName(fieldName);
2592
- const formControl = this.form.get(fieldName);
2874
+ const formControl = control || this.form.get(fieldName);
2593
2875
  // Format month-year fields (especially expiryDate) to YYYY-MM
2594
2876
  if (column?.type === 'month-year' && eventValue) {
2595
2877
  const formattedValue = this.formatMonthYearValue(eventValue);
@@ -2601,7 +2883,7 @@ class AddUpdateFormComponent {
2601
2883
  if (column?.onChange) {
2602
2884
  // Use the event value directly (what the user just selected/typed)
2603
2885
  // This ensures we have the correct value even if form control hasn't updated yet
2604
- const valueToPass = eventValue !== undefined ? eventValue : this.form.get(fieldName)?.value;
2886
+ const valueToPass = eventValue !== undefined ? eventValue : formControl?.value;
2605
2887
  const onChangeFn = column.onChange; // Store the function reference
2606
2888
  // Store the selected value - this is what the user chose
2607
2889
  const selectedValue = valueToPass;
@@ -2656,7 +2938,7 @@ class AddUpdateFormComponent {
2656
2938
  }
2657
2939
  return value;
2658
2940
  }
2659
- handleMonthYearChange(fieldName, value) {
2941
+ handleMonthYearChange(fieldName, value, control) {
2660
2942
  // Format the input value to YYYY-MM format
2661
2943
  let formattedValue = value;
2662
2944
  // If it's in MM-YYYY format, convert to YYYY-MM
@@ -2678,11 +2960,11 @@ class AddUpdateFormComponent {
2678
2960
  else if (formattedValue.match(/^\d{1,6}$/)) {
2679
2961
  // Allow partial input while typing
2680
2962
  }
2681
- const formControl = this.form.get(fieldName);
2963
+ const formControl = control || this.form.get(fieldName);
2682
2964
  if (formControl && formattedValue !== value) {
2683
2965
  formControl.setValue(formattedValue, { emitEvent: false });
2684
2966
  }
2685
- this.handleFieldChange(fieldName, formattedValue);
2967
+ this.handleFieldChange(fieldName, formattedValue, control);
2686
2968
  }
2687
2969
  handleMonthYearBlur(fieldName) {
2688
2970
  const formControl = this.form.get(fieldName);
@@ -2867,24 +3149,9 @@ class AddUpdateFormComponent {
2867
3149
  handleSubmit() {
2868
3150
  this.submitted = true;
2869
3151
  if (this.form.invalid) {
2870
- // Mark all fields as touched to show red borders
2871
- Object.keys(this.form.controls).forEach((key) => {
2872
- this.form.get(key)?.markAsTouched();
2873
- });
2874
- // Count only required fields that are invalid
2875
- const allColumns = this.config.sections.flatMap((s) => s.fields);
2876
- const invalidRequiredCount = Object.keys(this.form.controls).filter((key) => {
2877
- const control = this.form.get(key);
2878
- if (!control?.invalid)
2879
- return false;
2880
- // Check if this field has required validation
2881
- const column = allColumns.find((c) => c.name === key);
2882
- return (column?.validations?.some((v) => v.type === 'required') || false);
2883
- }).length;
2884
- // Only emit if there are required fields missing
2885
- if (invalidRequiredCount > 0) {
2886
- this.validationError.emit(`Please fill all required fields marked with *. ${invalidRequiredCount} field(s) need attention.`);
2887
- }
3152
+ // Mark all fields as touched to show red borders (recursive)
3153
+ this.form.markAllAsTouched();
3154
+ this.validationError.emit('Please fill all required fields marked with *.');
2888
3155
  return;
2889
3156
  }
2890
3157
  const formData = this.form.getRawValue();
@@ -2899,7 +3166,7 @@ class AddUpdateFormComponent {
2899
3166
  }
2900
3167
  isFieldInvalid(fieldName) {
2901
3168
  const field = this.form.get(fieldName);
2902
- return !!(field && field.invalid && this.submitted);
3169
+ return !!(field && field.invalid && (field.touched || this.submitted));
2903
3170
  }
2904
3171
  getErrorMessage(fieldName) {
2905
3172
  const field = this.form.get(fieldName);
@@ -2952,9 +3219,41 @@ class AddUpdateFormComponent {
2952
3219
  {{ section.title }}
2953
3220
  </h3>
2954
3221
 
2955
- <div class="fields-row">
2956
- <ng-container *ngFor="let column of section.fields">
2957
- <div
3222
+ <ng-container *ngIf="section.isRepeatable && section.sectionKey; else flatSection">
3223
+ <div *ngFor="let group of getSectionArray(section).controls; let i = index" class="repeatable-group-wrapper">
3224
+ <div class="repeatable-actions">
3225
+ <button type="button" class="btn-icon btn-add" (click)="addSectionItem(section)">
3226
+ <svg viewBox="0 0 24 24" width="24" height="24" fill="#2ecc71">
3227
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/>
3228
+ </svg>
3229
+ </button>
3230
+ <button type="button" class="btn-icon btn-remove" (click)="removeSectionItem(section, i)" *ngIf="getSectionArray(section).controls.length > 1">
3231
+ <svg viewBox="0 0 24 24" width="24" height="24" fill="#e74c3c">
3232
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"/>
3233
+ </svg>
3234
+ </button>
3235
+ </div>
3236
+ <div class="fields-row">
3237
+ <ng-container *ngFor="let column of section.fields">
3238
+ <ng-container *ngTemplateOutlet="fieldRenderer; context: { column: column, control: group.get(column.name), group: group }">
3239
+ </ng-container>
3240
+ </ng-container>
3241
+ </div>
3242
+ </div>
3243
+ </ng-container>
3244
+
3245
+ <ng-template #flatSection>
3246
+ <div class="fields-row">
3247
+ <ng-container *ngFor="let column of section.fields">
3248
+ <ng-container *ngTemplateOutlet="fieldRenderer; context: { column: column, control: getControl(column.name), group: form }">
3249
+ </ng-container>
3250
+ </ng-container>
3251
+ </div>
3252
+ </ng-template>
3253
+ </div>
3254
+
3255
+ <ng-template #fieldRenderer let-column="column" let-control="control" let-group="group">
3256
+ <div
2958
3257
  class="form-field-wrapper"
2959
3258
  [ngClass]="'field-' + column.type"
2960
3259
  [ngStyle]="column.hidden ? { display: 'none' } : {}"
@@ -2967,13 +3266,13 @@ class AddUpdateFormComponent {
2967
3266
  [label]="column.label"
2968
3267
  [placeholder]="column.placeholder || ''"
2969
3268
  type="text"
2970
- [control]="getControl(column.name)"
3269
+ [control]="control"
2971
3270
  [schema]="getSchema(column)"
2972
3271
  [disabled]="column.disabled || false"
2973
3272
  [readonly]="false"
2974
3273
  [allowAlphabetsOnly]="column.allowAlphabetsOnly || false"
2975
3274
  [maxLength]="column.maxLength || null"
2976
- (valueChange)="handleFieldChange(column.name, $event)"
3275
+ (valueChange)="handleFieldChange(column.name, $event, control)"
2977
3276
  >
2978
3277
  </fv-entry-field>
2979
3278
  </ng-container>
@@ -2984,10 +3283,10 @@ class AddUpdateFormComponent {
2984
3283
  <fv-email-field
2985
3284
  [label]="column.label"
2986
3285
  [placeholder]="column.placeholder || ''"
2987
- [control]="getControl(column.name)"
3286
+ [control]="control"
2988
3287
  [schema]="getSchema(column)"
2989
3288
  [disabled]="column.disabled || false"
2990
- (valueChange)="handleFieldChange(column.name, $event)"
3289
+ (valueChange)="handleFieldChange(column.name, $event, control)"
2991
3290
  >
2992
3291
  </fv-email-field>
2993
3292
  </ng-container>
@@ -2998,10 +3297,10 @@ class AddUpdateFormComponent {
2998
3297
  <fv-password-field
2999
3298
  [label]="column.label"
3000
3299
  [placeholder]="column.placeholder || ''"
3001
- [control]="getControl(column.name)"
3300
+ [control]="control"
3002
3301
  [schema]="getSchema(column)"
3003
3302
  [disabled]="column.disabled || false"
3004
- (valueChange)="handleFieldChange(column.name, $event)"
3303
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3005
3304
  >
3006
3305
  </fv-password-field>
3007
3306
  </ng-container>
@@ -3012,10 +3311,10 @@ class AddUpdateFormComponent {
3012
3311
  <fv-number-field
3013
3312
  [label]="column.label"
3014
3313
  [placeholder]="column.placeholder || ''"
3015
- [control]="getControl(column.name)"
3314
+ [control]="control"
3016
3315
  [schema]="getSchema(column)"
3017
3316
  [disabled]="column.disabled || false"
3018
- (valueChange)="handleFieldChange(column.name, $event)"
3317
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3019
3318
  >
3020
3319
  </fv-number-field>
3021
3320
  </ng-container>
@@ -3027,10 +3326,10 @@ class AddUpdateFormComponent {
3027
3326
  [label]="column.label"
3028
3327
  [placeholder]="column.placeholder || 'Select option'"
3029
3328
  [options]="column.options || []"
3030
- [control]="getControl(column.name)"
3329
+ [control]="control"
3031
3330
  [schema]="getSchema(column)"
3032
3331
  [disabled]="column.disabled || false"
3033
- (valueChange)="handleFieldChange(column.name, $event)"
3332
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3034
3333
  >
3035
3334
  </fv-dropdown>
3036
3335
  </ng-container>
@@ -3040,9 +3339,9 @@ class AddUpdateFormComponent {
3040
3339
  >
3041
3340
  <fv-checkbox
3042
3341
  [label]="column.label"
3043
- [control]="getControl(column.name)"
3342
+ [control]="control"
3044
3343
  [disabled]="column.disabled || false"
3045
- (valueChange)="handleFieldChange(column.name, $event)"
3344
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3046
3345
  >
3047
3346
  </fv-checkbox>
3048
3347
  </ng-container>
@@ -3051,10 +3350,10 @@ class AddUpdateFormComponent {
3051
3350
  <fv-radio-group
3052
3351
  [label]="column.label"
3053
3352
  [options]="column.options || []"
3054
- [control]="getControl(column.name)"
3353
+ [control]="control"
3055
3354
  [disabled]="column.disabled || false"
3056
3355
  [layout]="column.layout || 'vertical'"
3057
- (valueChange)="handleFieldChange(column.name, $event)"
3356
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3058
3357
  >
3059
3358
  </fv-radio-group>
3060
3359
  </ng-container>
@@ -3062,10 +3361,10 @@ class AddUpdateFormComponent {
3062
3361
  <ng-container *ngIf="column.type === 'date' && !column.hidden">
3063
3362
  <fv-date-field
3064
3363
  [label]="column.label"
3065
- [control]="getControl(column.name)"
3364
+ [control]="control"
3066
3365
  [schema]="getSchema(column)"
3067
3366
  [disabled]="column.disabled || false"
3068
- (valueChange)="handleFieldChange(column.name, $event)"
3367
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3069
3368
  >
3070
3369
  </fv-date-field>
3071
3370
  </ng-container>
@@ -3075,10 +3374,10 @@ class AddUpdateFormComponent {
3075
3374
  >
3076
3375
  <fv-month-year-field
3077
3376
  [label]="column.label"
3078
- [control]="getControl(column.name)"
3377
+ [control]="control"
3079
3378
  [schema]="getSchema(column)"
3080
3379
  [disabled]="column.disabled || false"
3081
- (valueChange)="handleMonthYearChange(column.name, $event)"
3380
+ (valueChange)="handleMonthYearChange(column.name, $event, control)"
3082
3381
  >
3083
3382
  </fv-month-year-field>
3084
3383
  </ng-container>
@@ -3089,10 +3388,10 @@ class AddUpdateFormComponent {
3089
3388
  <fv-rich-text-editor
3090
3389
  [label]="column.label"
3091
3390
  [placeholder]="column.placeholder || ''"
3092
- [control]="getControl(column.name)"
3391
+ [control]="control"
3093
3392
  [schema]="getSchema(column)"
3094
3393
  [disabled]="column.disabled || false"
3095
- (valueChange)="handleFieldChange(column.name, $event)"
3394
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3096
3395
  >
3097
3396
  </fv-rich-text-editor>
3098
3397
  </ng-container>
@@ -3102,7 +3401,7 @@ class AddUpdateFormComponent {
3102
3401
  <fv-image-selector
3103
3402
  [label]="column.label"
3104
3403
  [placeholder]="column.placeholder || 'Select image'"
3105
- [control]="getControl(column.name)"
3404
+ [control]="control"
3106
3405
  [schema]="getSchema(column)"
3107
3406
  [disabled]="column.disabled || false"
3108
3407
  (valueChange)="onFileChange($event, column.name)"
@@ -3115,7 +3414,7 @@ class AddUpdateFormComponent {
3115
3414
  [label]="column.label"
3116
3415
  [placeholder]="column.placeholder || 'Select file'"
3117
3416
  [accept]="column.accept || '*/*'"
3118
- [control]="getControl(column.name)"
3417
+ [control]="control"
3119
3418
  [schema]="getSchema(column)"
3120
3419
  [disabled]="column.disabled || false"
3121
3420
  (valueChange)="onFileChange($event, column.name)"
@@ -3131,7 +3430,7 @@ class AddUpdateFormComponent {
3131
3430
  [label]="column.label"
3132
3431
  [placeholder]="column.placeholder || 'Search by Code or Name'"
3133
3432
  [options]="column.options ? mapSearchOptions(column.options) : []"
3134
- [control]="getControl(column.name)"
3433
+ [control]="control"
3135
3434
  [schema]="getSchema(column)"
3136
3435
  [disabled]="column.disabled || false"
3137
3436
  >
@@ -3139,43 +3438,50 @@ class AddUpdateFormComponent {
3139
3438
  </ng-container>
3140
3439
 
3141
3440
  <ng-container *ngIf="column.type === 'phone' && !column.hidden">
3142
- <fv-phone-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-phone-field>
3441
+ <fv-phone-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-phone-field>
3143
3442
  </ng-container>
3144
3443
 
3145
3444
  <ng-container *ngIf="column.type === 'uan' && !column.hidden">
3146
- <fv-uan-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-uan-field>
3445
+ <fv-uan-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-uan-field>
3147
3446
  </ng-container>
3148
3447
 
3149
3448
  <ng-container *ngIf="column.type === 'pf' && !column.hidden">
3150
- <fv-pf-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-pf-field>
3449
+ <fv-pf-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-pf-field>
3151
3450
  </ng-container>
3152
3451
 
3153
3452
  <ng-container *ngIf="column.type === 'esi' && !column.hidden">
3154
- <fv-esi-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-esi-field>
3453
+ <fv-esi-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-esi-field>
3155
3454
  </ng-container>
3156
3455
 
3157
3456
  <ng-container *ngIf="column.type === 'ifsc' && !column.hidden">
3158
- <fv-ifsc-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-ifsc-field>
3457
+ <fv-ifsc-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-ifsc-field>
3159
3458
  </ng-container>
3160
3459
 
3161
3460
  <ng-container *ngIf="column.type === 'micr' && !column.hidden">
3162
- <fv-micr-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-micr-field>
3461
+ <fv-micr-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-micr-field>
3163
3462
  </ng-container>
3164
3463
 
3165
3464
  <ng-container *ngIf="column.type === 'iban' && !column.hidden">
3166
- <fv-iban-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-iban-field>
3465
+ <fv-iban-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-iban-field>
3466
+ </ng-container>
3467
+
3468
+ <ng-container *ngIf="column.type === 'service-period' && !column.hidden">
3469
+ <fv-service-period
3470
+ [label]="column.label"
3471
+ [group]="group"
3472
+ [startField]="column.servicePeriodConfig?.startField || ''"
3473
+ [endField]="column.servicePeriodConfig?.endField || ''"
3474
+ [control]="control"
3475
+ ></fv-service-period>
3167
3476
  </ng-container>
3168
3477
  </div>
3169
- </ng-container>
3170
- </div>
3171
- </div>
3478
+ </ng-template>
3172
3479
 
3173
3480
 
3174
3481
  <div class="form-actions">
3175
3482
  <button
3176
3483
  type="submit"
3177
3484
  class="btn btn-primary"
3178
- [disabled]="form.invalid && submitted"
3179
3485
  >
3180
3486
  {{ config.submitLabel || 'Save' }}
3181
3487
  </button>
@@ -3189,7 +3495,7 @@ class AddUpdateFormComponent {
3189
3495
  </div>
3190
3496
  </form>
3191
3497
  </div>
3192
- `, isInline: true, styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";*{font-family:Poppins!important}.form-container{background:#f5f5f5;padding:5px 10px 10px;border-radius:8px;margin:0 auto;max-width:100%;box-sizing:border-box}.error-border{border-color:red!important}.hidden{display:none!important}.form-header{margin-bottom:5px}.form-header h2{margin:0;font-size:20px;font-weight:700;color:#303030}.dynamic-form{background:#fff;padding:10px;border-radius:6px;box-shadow:0 1px 3px #0000001a;max-width:100%;box-sizing:border-box}.form-section{margin-bottom:10px}.section-title{font-size:16px;font-weight:700;color:#303030;margin:0 0 16px;text-transform:uppercase;letter-spacing:.5px}.fields-row{display:grid;grid-template-columns:repeat(5,1fr);gap:16px;width:100%;box-sizing:border-box;overflow:visible}.form-field-wrapper{display:flex;position:relative;flex-direction:column;min-width:0;width:100%;box-sizing:border-box;overflow:visible}.password-toggle-btn{position:absolute;right:10px;top:32px;background:none;border:none;cursor:pointer;padding:4px;color:#666}.password-toggle-btn:hover{color:#333}.password-toggle-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}@media (min-width: 1200px) and (max-width: 1400px){.fields-row{grid-template-columns:repeat(5,1fr)}.form-control{width:180px;max-width:180px}}@media (max-width: 1200px){.fields-row{grid-template-columns:repeat(3,1fr)}.form-control{width:100%;max-width:100%}}@media (max-width: 992px){.fields-row{grid-template-columns:repeat(2,1fr);gap:12px}}@media (max-width: 768px){.fields-row{grid-template-columns:repeat(2,1fr);gap:10px}}@media (max-width: 640px){.fields-row{grid-template-columns:1fr;gap:12px}.form-field-wrapper{width:100%}}label{font-weight:600;margin-bottom:6px;color:#151d48;font-size:14px}.required{color:#e74c3c;margin-left:2px}.form-control{padding:5px 10px;width:100%;max-width:100%;height:34px;border:1px solid #8CBBA8;border-radius:8px;background-color:#fff;font-size:14px;font-weight:400;font-family:inherit;color:#1f2b41;transition:border-color .2s,box-shadow .2s;box-sizing:border-box}.form-control:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.form-control:disabled{background-color:#ecf0f1;cursor:not-allowed}textarea.form-control{resize:vertical;min-height:100px}select.form-control{cursor:pointer;appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23333' d='M6 9L1 4h10z'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 8px center;padding-right:40px}.checkbox-wrapper{display:flex;align-items:center;gap:8px}input[type=checkbox]{width:18px;height:18px;cursor:pointer}.checkbox-label{cursor:pointer;margin:0}.file-upload-container{display:flex;align-items:center;gap:20px;padding:20px;background:#fff;max-width:100%;box-sizing:border-box;flex-wrap:wrap}.material-icons{font-family:Material Icons!important;border-radius:4px;display:inline-block;width:40px;padding:8px;height:40px}.file-field-wrapper{width:100%;margin-top:16px;max-width:100%;box-sizing:border-box}.photo-preview-circle{width:100px;height:100px;border-radius:50%;overflow:hidden;border:2px solid #ddd;cursor:pointer;transition:border-color .2s}.photo-preview-circle:hover{border-color:#3498db}.preview-image,.default-photo{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.preview-image img{width:100%;height:100%;object-fit:cover}.default-photo{background:#f0f0f0;color:#999;font-size:40px}.file-info{display:flex;flex-direction:column;gap:8px}.btn-delete{border:none;border-radius:4px;cursor:pointer;font-size:14px;padding:0}.upload-btn-group{display:flex;align-items:center;border:1px solid #d3d3d3;border-radius:8px;background:#fff;overflow:hidden;width:fit-content}.btn-upload{background:#fff;color:#222;border:none;padding:0 18px;font-size:16px;border-right:1px solid #e0e0e0;border-radius:8px 0 0 8px;outline:none;box-shadow:none;cursor:pointer;height:40px;transition:background .2s}.btn-upload-icon{background:#f5f5f5;border:none;border-radius:0 8px 8px 0;display:flex;align-items:center;justify-content:center;width:48px;height:40px;cursor:pointer;transition:background .2s}.btn-upload-icon i.material-icons{font-size:22px;color:#222}.btn-upload:hover,.btn-upload-icon:hover{background:#f0f0f0}.btn-delete{background:#fff;color:red}.btn-delete:hover{background:#f0f0f0}.file-name{font-size:12px;color:#666;margin:10px 8px 8px}.file-label{font-weight:600;margin-bottom:6px;color:#151d48;font-size:14px;display:block}.radio-label{font-weight:600;margin-bottom:15px;color:#151d48;font-size:14px;display:block}.radio-group{display:flex;gap:20px;align-items:center}.radio-item{display:flex;align-items:center;gap:6px}input[type=radio]{width:18px;height:18px;cursor:pointer;accent-color:#3498db}.radio-option-label{cursor:pointer;margin:0;font-weight:400}.hint{font-size:12px;color:#7f8c8d;margin-top:4px}.error-message{color:#e74c3c;font-size:12px;margin-top:4px}.form-actions{display:flex;gap:8px;align-items:center;justify-content:space-between;gap:12px;padding-top:5px}.btn{padding:8px 20px;border:none;border-radius:7px;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s}.btn-primary{background-color:#006aff;color:#fff}.btn-primary:hover:not(:disabled){background-color:#2980b9}.btn-primary:disabled{background-color:#bdc3c7;cursor:not-allowed}.btn-secondary{background-color:#95a5a6;color:#fff}.btn-secondary:hover{background-color:#7f8c8d}.btn-outline{background-color:#303030;color:#fff}.btn-outline:hover{background-color:#2b2b2b;color:#fff}.month-year-wrapper{position:relative;display:flex;align-items:center;width:100%}.month-year-wrapper .form-control{padding-right:45px}.month-picker-hidden{position:absolute;opacity:0;pointer-events:none;width:0;height:0;border:none;padding:0;margin:0}.month-picker-btn{position:absolute;right:5px;background:transparent;border:none;cursor:pointer;padding:5px;display:flex;align-items:center;justify-content:center;color:#666;transition:color .2s;z-index:1}.month-picker-btn:hover:not(:disabled){color:#3498db}.month-picker-btn:disabled{cursor:not-allowed;opacity:.5}.month-picker-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}.search-select-wrapper{position:relative;width:100%}.search-select-input{padding-right:10px}.search-select-dropdown{position:absolute;top:calc(100% + 4px);left:0;right:auto;min-width:100%;max-width:calc(100vw - 16px);background:#fff;border:1px solid #d3d3d3;border-radius:8px;max-height:220px;overflow-y:auto;box-shadow:0 4px 10px #00000014;z-index:5;padding:6px 0;transform:translate(0);transition:transform .1s ease-out}.search-select-option{padding:8px 12px;cursor:pointer;display:flex;gap:8px;align-items:center;border-bottom:1px solid #f2f2f2}.search-select-option:last-child{border-bottom:none}.search-select-option:hover{background:#f5f9ff}.option-code{color:#0052cc;font-weight:700;min-width:90px}.option-name{color:#151d48;font-weight:500}.time-picker-wrapper{position:relative;display:flex;align-items:center;width:100%}.time-input{padding-right:45px;flex:1}.time-picker-btn{position:absolute;right:5px;background:transparent;border:none;cursor:pointer;padding:5px;display:flex;align-items:center;justify-content:center;color:#666;transition:color .2s;z-index:1}.time-picker-btn:hover:not(:disabled){color:#3498db}.time-picker-btn:disabled{cursor:not-allowed;opacity:.5}.time-picker-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}input[type=time]{cursor:pointer}input[type=time]::-webkit-calendar-picker-indicator{cursor:pointer;opacity:0;position:absolute;right:0;width:100%;height:100%}.date-picker-field{width:100%}.date-picker-field ::ng-deep .mat-mdc-form-field{width:100%}.date-picker-field ::ng-deep .mat-mdc-text-field-wrapper{background-color:#fff}.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__trailing{border-color:#8cbba8}.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:#8cbba8}.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#3498db}.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__leading,.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__notch,.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__trailing{border-color:red!important}.date-picker-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.date-picker-field ::ng-deep .mat-mdc-form-field-input-control input{font-family:Poppins!important;font-size:14px;font-weight:400;color:#1f2b41}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FvEntryFieldComponent, selector: "fv-entry-field", inputs: ["label", "placeholder", "schema", "control", "disabled", "readonly", "type", "allowAlphabetsOnly", "maxLength"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvNumberFieldComponent, selector: "fv-number-field", inputs: ["label", "placeholder", "schema", "control", "disabled", "readonly", "min", "max", "step"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvDropdownComponent, selector: "fv-dropdown", inputs: ["label", "placeholder", "options", "schema", "control", "disabled"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvRadioGroupComponent, selector: "fv-radio-group", inputs: ["label", "control", "options", "disabled", "required", "name", "layout"], outputs: ["valueChange"] }, { kind: "component", type: FvCheckboxComponent, selector: "fv-checkbox", inputs: ["label", "control", "disabled", "required"], outputs: ["valueChange"] }, { kind: "component", type: FvDateFieldComponent, selector: "fv-date-field", inputs: ["label", "schema", "control", "disabled", "readonly", "min", "max"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvMonthYearFieldComponent, selector: "fv-month-year-field", inputs: ["label", "schema", "control", "disabled", "readonly", "min", "max"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvFileSelectorComponent, selector: "fv-file-selector", inputs: ["label", "placeholder", "schema", "control", "disabled", "accept", "maxSize"], outputs: ["valueChange", "blur"] }, { kind: "component", type: FvImageSelectorComponent, selector: "fv-image-selector", inputs: ["label", "placeholder", "schema", "control", "disabled", "maxSize"], outputs: ["valueChange", "blur"] }, { kind: "component", type: FvRichTextEditorComponent, selector: "fv-rich-text-editor", inputs: ["label", "placeholder", "schema", "control", "disabled", "readonly", "minHeight", "showToolbar"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvNameCodeComponent, selector: "fv-name-code", inputs: ["label", "placeholder", "options", "schema", "control", "disabled"], outputs: ["selectionChange"] }, { kind: "component", type: FvPhoneFieldComponent, selector: "fv-phone-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvUanFieldComponent, selector: "fv-uan-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvPfFieldComponent, selector: "fv-pf-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvEsiFieldComponent, selector: "fv-esi-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvIfscFieldComponent, selector: "fv-ifsc-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvMicrFieldComponent, selector: "fv-micr-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvIbanFieldComponent, selector: "fv-iban-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvEmailFieldComponent, selector: "fv-email-field", inputs: ["label", "placeholder", "schema", "control", "disabled", "readonly"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvPasswordFieldComponent, selector: "fv-password-field", inputs: ["label", "placeholder", "schema", "control", "disabled", "readonly"], outputs: ["valueChange", "blur", "focus"] }] });
3498
+ `, isInline: true, styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";*{font-family:Poppins!important}.form-container{background:#f5f5f5;padding:5px 10px 10px;border-radius:8px;margin:0 auto;max-width:100%;box-sizing:border-box}.error-border{border-color:red!important}.hidden{display:none!important}.form-header{margin-bottom:5px}.form-header h2{margin:0;font-size:20px;font-weight:700;color:#303030}.dynamic-form{background:#fff;padding:10px;border-radius:6px;box-shadow:0 1px 3px #0000001a;max-width:100%;box-sizing:border-box}.form-section{margin-bottom:10px}.section-title{font-size:16px;font-weight:700;color:#303030;margin:0 0 16px;text-transform:uppercase;letter-spacing:.5px}.fields-row{display:grid;grid-template-columns:repeat(5,1fr);gap:16px;width:100%;box-sizing:border-box;overflow:visible}.form-field-wrapper{display:flex;position:relative;flex-direction:column;min-width:0;width:100%;box-sizing:border-box;overflow:visible}.password-toggle-btn{position:absolute;right:10px;top:32px;background:none;border:none;cursor:pointer;padding:4px;color:#666}.password-toggle-btn:hover{color:#333}.password-toggle-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}@media (min-width: 1200px) and (max-width: 1400px){.fields-row{grid-template-columns:repeat(5,1fr)}.form-control{width:180px;max-width:180px}}@media (max-width: 1200px){.fields-row{grid-template-columns:repeat(3,1fr)}.form-control{width:100%;max-width:100%}}@media (max-width: 992px){.fields-row{grid-template-columns:repeat(2,1fr);gap:12px}}@media (max-width: 768px){.fields-row{grid-template-columns:repeat(2,1fr);gap:10px}}@media (max-width: 640px){.fields-row{grid-template-columns:1fr;gap:12px}.form-field-wrapper{width:100%}}label{font-weight:600;margin-bottom:6px;color:#151d48;font-size:14px}.required{color:#e74c3c;margin-left:2px}.form-control{padding:5px 10px;width:100%;max-width:100%;height:34px;border:1px solid #8CBBA8;border-radius:8px;background-color:#fff;font-size:14px;font-weight:400;font-family:inherit;color:#1f2b41;transition:border-color .2s,box-shadow .2s;box-sizing:border-box}.form-control:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.form-control:disabled{background-color:#ecf0f1;cursor:not-allowed}textarea.form-control{resize:vertical;min-height:100px}select.form-control{cursor:pointer;appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23333' d='M6 9L1 4h10z'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 8px center;padding-right:40px}.checkbox-wrapper{display:flex;align-items:center;gap:8px}input[type=checkbox]{width:18px;height:18px;cursor:pointer}.checkbox-label{cursor:pointer;margin:0}.file-upload-container{display:flex;align-items:center;gap:20px;padding:20px;background:#fff;max-width:100%;box-sizing:border-box;flex-wrap:wrap}.material-icons{font-family:Material Icons!important;border-radius:4px;display:inline-block;width:40px;padding:8px;height:40px}.file-field-wrapper{width:100%;margin-top:16px;max-width:100%;box-sizing:border-box}.photo-preview-circle{width:100px;height:100px;border-radius:50%;overflow:hidden;border:2px solid #ddd;cursor:pointer;transition:border-color .2s}.photo-preview-circle:hover{border-color:#3498db}.preview-image,.default-photo{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.preview-image img{width:100%;height:100%;object-fit:cover}.default-photo{background:#f0f0f0;color:#999;font-size:40px}.file-info{display:flex;flex-direction:column;gap:8px}.btn-delete{border:none;border-radius:4px;cursor:pointer;font-size:14px;padding:0}.upload-btn-group{display:flex;align-items:center;border:1px solid #d3d3d3;border-radius:8px;background:#fff;overflow:hidden;width:fit-content}.btn-upload{background:#fff;color:#222;border:none;padding:0 18px;font-size:16px;border-right:1px solid #e0e0e0;border-radius:8px 0 0 8px;outline:none;box-shadow:none;cursor:pointer;height:40px;transition:background .2s}.btn-upload-icon{background:#f5f5f5;border:none;border-radius:0 8px 8px 0;display:flex;align-items:center;justify-content:center;width:48px;height:40px;cursor:pointer;transition:background .2s}.btn-upload-icon i.material-icons{font-size:22px;color:#222}.btn-upload:hover,.btn-upload-icon:hover{background:#f0f0f0}.btn-delete{background:#fff;color:red}.btn-delete:hover{background:#f0f0f0}.file-name{font-size:12px;color:#666;margin:10px 8px 8px}.file-label{font-weight:600;margin-bottom:6px;color:#151d48;font-size:14px;display:block}.radio-label{font-weight:600;margin-bottom:15px;color:#151d48;font-size:14px;display:block}.radio-group{display:flex;gap:20px;align-items:center}.radio-item{display:flex;align-items:center;gap:6px}input[type=radio]{width:18px;height:18px;cursor:pointer;accent-color:#3498db}.radio-option-label{cursor:pointer;margin:0;font-weight:400}.hint{font-size:12px;color:#7f8c8d;margin-top:4px}.error-message{color:#e74c3c;font-size:12px;margin-top:4px}.form-actions{display:flex;gap:8px;align-items:center;justify-content:space-between;gap:12px;padding-top:5px}.btn{padding:8px 20px;border:none;border-radius:7px;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s}.btn-primary{background-color:#006aff;color:#fff}.btn-primary:hover:not(:disabled){background-color:#2980b9}.btn-primary:disabled{background-color:#bdc3c7;cursor:not-allowed}.btn-secondary{background-color:#95a5a6;color:#fff}.btn-secondary:hover{background-color:#7f8c8d}.btn-outline{background-color:#303030;color:#fff}.btn-outline:hover{background-color:#2b2b2b;color:#fff}.month-year-wrapper{position:relative;display:flex;align-items:center;width:100%}.month-year-wrapper .form-control{padding-right:45px}.month-picker-hidden{position:absolute;opacity:0;pointer-events:none;width:0;height:0;border:none;padding:0;margin:0}.month-picker-btn{position:absolute;right:5px;background:transparent;border:none;cursor:pointer;padding:5px;display:flex;align-items:center;justify-content:center;color:#666;transition:color .2s;z-index:1}.month-picker-btn:hover:not(:disabled){color:#3498db}.month-picker-btn:disabled{cursor:not-allowed;opacity:.5}.month-picker-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}.search-select-wrapper{position:relative;width:100%}.search-select-input{padding-right:10px}.search-select-dropdown{position:absolute;top:calc(100% + 4px);left:0;right:auto;min-width:100%;max-width:calc(100vw - 16px);background:#fff;border:1px solid #d3d3d3;border-radius:8px;max-height:220px;overflow-y:auto;box-shadow:0 4px 10px #00000014;z-index:5;padding:6px 0;transform:translate(0);transition:transform .1s ease-out}.search-select-option{padding:8px 12px;cursor:pointer;display:flex;gap:8px;align-items:center;border-bottom:1px solid #f2f2f2}.search-select-option:last-child{border-bottom:none}.search-select-option:hover{background:#f5f9ff}.repeatable-group-wrapper{position:relative;background:#fff;border:1px solid #eee;border-radius:8px;padding:20px 100px 20px 20px;margin-bottom:15px;box-shadow:0 1px 2px #0000000d}.repeatable-actions{position:absolute;top:45px;right:15px;display:flex;gap:8px;z-index:10}.btn-icon{background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:background .2s}.btn-icon:hover{background:#f0f0f0}.option-code{color:#0052cc;font-weight:700;min-width:90px}.option-name{color:#151d48;font-weight:500}.time-picker-wrapper{position:relative;display:flex;align-items:center;width:100%}.time-input{padding-right:45px;flex:1}.time-picker-btn{position:absolute;right:5px;background:transparent;border:none;cursor:pointer;padding:5px;display:flex;align-items:center;justify-content:center;color:#666;transition:color .2s;z-index:1}.time-picker-btn:hover:not(:disabled){color:#3498db}.time-picker-btn:disabled{cursor:not-allowed;opacity:.5}.time-picker-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}input[type=time]{cursor:pointer}input[type=time]::-webkit-calendar-picker-indicator{cursor:pointer;opacity:0;position:absolute;right:0;width:100%;height:100%}.date-picker-field{width:100%}.date-picker-field ::ng-deep .mat-mdc-form-field{width:100%}.date-picker-field ::ng-deep .mat-mdc-text-field-wrapper{background-color:#fff}.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__trailing{border-color:#8cbba8}.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:#8cbba8}.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#3498db}.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__leading,.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__notch,.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__trailing{border-color:red!important}.date-picker-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.date-picker-field ::ng-deep .mat-mdc-form-field-input-control input{font-family:Poppins!important;font-size:14px;font-weight:400;color:#1f2b41}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FvEntryFieldComponent, selector: "fv-entry-field", inputs: ["label", "placeholder", "schema", "control", "disabled", "readonly", "type", "allowAlphabetsOnly", "maxLength"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvNumberFieldComponent, selector: "fv-number-field", inputs: ["label", "placeholder", "schema", "control", "disabled", "readonly", "min", "max", "step"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvDropdownComponent, selector: "fv-dropdown", inputs: ["label", "placeholder", "options", "schema", "control", "disabled"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvRadioGroupComponent, selector: "fv-radio-group", inputs: ["label", "control", "options", "disabled", "required", "name", "layout"], outputs: ["valueChange"] }, { kind: "component", type: FvCheckboxComponent, selector: "fv-checkbox", inputs: ["label", "control", "disabled", "required"], outputs: ["valueChange"] }, { kind: "component", type: FvDateFieldComponent, selector: "fv-date-field", inputs: ["label", "schema", "control", "disabled", "readonly", "min", "max"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvMonthYearFieldComponent, selector: "fv-month-year-field", inputs: ["label", "schema", "control", "disabled", "readonly", "min", "max"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvFileSelectorComponent, selector: "fv-file-selector", inputs: ["label", "placeholder", "schema", "control", "disabled", "accept", "maxSize"], outputs: ["valueChange", "blur"] }, { kind: "component", type: FvImageSelectorComponent, selector: "fv-image-selector", inputs: ["label", "placeholder", "schema", "control", "disabled", "maxSize"], outputs: ["valueChange", "blur"] }, { kind: "component", type: FvRichTextEditorComponent, selector: "fv-rich-text-editor", inputs: ["label", "placeholder", "schema", "control", "disabled", "readonly", "minHeight", "showToolbar"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvNameCodeComponent, selector: "fv-name-code", inputs: ["label", "placeholder", "options", "schema", "control", "disabled"], outputs: ["selectionChange"] }, { kind: "component", type: FvPhoneFieldComponent, selector: "fv-phone-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvUanFieldComponent, selector: "fv-uan-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvPfFieldComponent, selector: "fv-pf-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvEsiFieldComponent, selector: "fv-esi-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvIfscFieldComponent, selector: "fv-ifsc-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvMicrFieldComponent, selector: "fv-micr-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvIbanFieldComponent, selector: "fv-iban-field", inputs: ["label", "control", "disabled", "schema"], outputs: ["blur", "focus"] }, { kind: "component", type: FvEmailFieldComponent, selector: "fv-email-field", inputs: ["label", "placeholder", "schema", "control", "disabled", "readonly"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvPasswordFieldComponent, selector: "fv-password-field", inputs: ["label", "placeholder", "schema", "control", "disabled", "readonly"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvServicePeriodComponent, selector: "fv-service-period", inputs: ["label", "group", "startField", "endField", "control"] }] });
3193
3499
  }
3194
3500
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AddUpdateFormComponent, decorators: [{
3195
3501
  type: Component,
@@ -3216,6 +3522,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3216
3522
  FvIbanFieldComponent,
3217
3523
  FvEmailFieldComponent,
3218
3524
  FvPasswordFieldComponent,
3525
+ FvServicePeriodComponent
3219
3526
  ], template: `
3220
3527
  <div class="form-container">
3221
3528
  <div class="form-header" *ngIf="config.formTitle">
@@ -3228,9 +3535,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3228
3535
  {{ section.title }}
3229
3536
  </h3>
3230
3537
 
3231
- <div class="fields-row">
3232
- <ng-container *ngFor="let column of section.fields">
3233
- <div
3538
+ <ng-container *ngIf="section.isRepeatable && section.sectionKey; else flatSection">
3539
+ <div *ngFor="let group of getSectionArray(section).controls; let i = index" class="repeatable-group-wrapper">
3540
+ <div class="repeatable-actions">
3541
+ <button type="button" class="btn-icon btn-add" (click)="addSectionItem(section)">
3542
+ <svg viewBox="0 0 24 24" width="24" height="24" fill="#2ecc71">
3543
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/>
3544
+ </svg>
3545
+ </button>
3546
+ <button type="button" class="btn-icon btn-remove" (click)="removeSectionItem(section, i)" *ngIf="getSectionArray(section).controls.length > 1">
3547
+ <svg viewBox="0 0 24 24" width="24" height="24" fill="#e74c3c">
3548
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"/>
3549
+ </svg>
3550
+ </button>
3551
+ </div>
3552
+ <div class="fields-row">
3553
+ <ng-container *ngFor="let column of section.fields">
3554
+ <ng-container *ngTemplateOutlet="fieldRenderer; context: { column: column, control: group.get(column.name), group: group }">
3555
+ </ng-container>
3556
+ </ng-container>
3557
+ </div>
3558
+ </div>
3559
+ </ng-container>
3560
+
3561
+ <ng-template #flatSection>
3562
+ <div class="fields-row">
3563
+ <ng-container *ngFor="let column of section.fields">
3564
+ <ng-container *ngTemplateOutlet="fieldRenderer; context: { column: column, control: getControl(column.name), group: form }">
3565
+ </ng-container>
3566
+ </ng-container>
3567
+ </div>
3568
+ </ng-template>
3569
+ </div>
3570
+
3571
+ <ng-template #fieldRenderer let-column="column" let-control="control" let-group="group">
3572
+ <div
3234
3573
  class="form-field-wrapper"
3235
3574
  [ngClass]="'field-' + column.type"
3236
3575
  [ngStyle]="column.hidden ? { display: 'none' } : {}"
@@ -3243,13 +3582,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3243
3582
  [label]="column.label"
3244
3583
  [placeholder]="column.placeholder || ''"
3245
3584
  type="text"
3246
- [control]="getControl(column.name)"
3585
+ [control]="control"
3247
3586
  [schema]="getSchema(column)"
3248
3587
  [disabled]="column.disabled || false"
3249
3588
  [readonly]="false"
3250
3589
  [allowAlphabetsOnly]="column.allowAlphabetsOnly || false"
3251
3590
  [maxLength]="column.maxLength || null"
3252
- (valueChange)="handleFieldChange(column.name, $event)"
3591
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3253
3592
  >
3254
3593
  </fv-entry-field>
3255
3594
  </ng-container>
@@ -3260,10 +3599,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3260
3599
  <fv-email-field
3261
3600
  [label]="column.label"
3262
3601
  [placeholder]="column.placeholder || ''"
3263
- [control]="getControl(column.name)"
3602
+ [control]="control"
3264
3603
  [schema]="getSchema(column)"
3265
3604
  [disabled]="column.disabled || false"
3266
- (valueChange)="handleFieldChange(column.name, $event)"
3605
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3267
3606
  >
3268
3607
  </fv-email-field>
3269
3608
  </ng-container>
@@ -3274,10 +3613,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3274
3613
  <fv-password-field
3275
3614
  [label]="column.label"
3276
3615
  [placeholder]="column.placeholder || ''"
3277
- [control]="getControl(column.name)"
3616
+ [control]="control"
3278
3617
  [schema]="getSchema(column)"
3279
3618
  [disabled]="column.disabled || false"
3280
- (valueChange)="handleFieldChange(column.name, $event)"
3619
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3281
3620
  >
3282
3621
  </fv-password-field>
3283
3622
  </ng-container>
@@ -3288,10 +3627,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3288
3627
  <fv-number-field
3289
3628
  [label]="column.label"
3290
3629
  [placeholder]="column.placeholder || ''"
3291
- [control]="getControl(column.name)"
3630
+ [control]="control"
3292
3631
  [schema]="getSchema(column)"
3293
3632
  [disabled]="column.disabled || false"
3294
- (valueChange)="handleFieldChange(column.name, $event)"
3633
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3295
3634
  >
3296
3635
  </fv-number-field>
3297
3636
  </ng-container>
@@ -3303,10 +3642,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3303
3642
  [label]="column.label"
3304
3643
  [placeholder]="column.placeholder || 'Select option'"
3305
3644
  [options]="column.options || []"
3306
- [control]="getControl(column.name)"
3645
+ [control]="control"
3307
3646
  [schema]="getSchema(column)"
3308
3647
  [disabled]="column.disabled || false"
3309
- (valueChange)="handleFieldChange(column.name, $event)"
3648
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3310
3649
  >
3311
3650
  </fv-dropdown>
3312
3651
  </ng-container>
@@ -3316,9 +3655,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3316
3655
  >
3317
3656
  <fv-checkbox
3318
3657
  [label]="column.label"
3319
- [control]="getControl(column.name)"
3658
+ [control]="control"
3320
3659
  [disabled]="column.disabled || false"
3321
- (valueChange)="handleFieldChange(column.name, $event)"
3660
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3322
3661
  >
3323
3662
  </fv-checkbox>
3324
3663
  </ng-container>
@@ -3327,10 +3666,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3327
3666
  <fv-radio-group
3328
3667
  [label]="column.label"
3329
3668
  [options]="column.options || []"
3330
- [control]="getControl(column.name)"
3669
+ [control]="control"
3331
3670
  [disabled]="column.disabled || false"
3332
3671
  [layout]="column.layout || 'vertical'"
3333
- (valueChange)="handleFieldChange(column.name, $event)"
3672
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3334
3673
  >
3335
3674
  </fv-radio-group>
3336
3675
  </ng-container>
@@ -3338,10 +3677,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3338
3677
  <ng-container *ngIf="column.type === 'date' && !column.hidden">
3339
3678
  <fv-date-field
3340
3679
  [label]="column.label"
3341
- [control]="getControl(column.name)"
3680
+ [control]="control"
3342
3681
  [schema]="getSchema(column)"
3343
3682
  [disabled]="column.disabled || false"
3344
- (valueChange)="handleFieldChange(column.name, $event)"
3683
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3345
3684
  >
3346
3685
  </fv-date-field>
3347
3686
  </ng-container>
@@ -3351,10 +3690,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3351
3690
  >
3352
3691
  <fv-month-year-field
3353
3692
  [label]="column.label"
3354
- [control]="getControl(column.name)"
3693
+ [control]="control"
3355
3694
  [schema]="getSchema(column)"
3356
3695
  [disabled]="column.disabled || false"
3357
- (valueChange)="handleMonthYearChange(column.name, $event)"
3696
+ (valueChange)="handleMonthYearChange(column.name, $event, control)"
3358
3697
  >
3359
3698
  </fv-month-year-field>
3360
3699
  </ng-container>
@@ -3365,10 +3704,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3365
3704
  <fv-rich-text-editor
3366
3705
  [label]="column.label"
3367
3706
  [placeholder]="column.placeholder || ''"
3368
- [control]="getControl(column.name)"
3707
+ [control]="control"
3369
3708
  [schema]="getSchema(column)"
3370
3709
  [disabled]="column.disabled || false"
3371
- (valueChange)="handleFieldChange(column.name, $event)"
3710
+ (valueChange)="handleFieldChange(column.name, $event, control)"
3372
3711
  >
3373
3712
  </fv-rich-text-editor>
3374
3713
  </ng-container>
@@ -3378,7 +3717,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3378
3717
  <fv-image-selector
3379
3718
  [label]="column.label"
3380
3719
  [placeholder]="column.placeholder || 'Select image'"
3381
- [control]="getControl(column.name)"
3720
+ [control]="control"
3382
3721
  [schema]="getSchema(column)"
3383
3722
  [disabled]="column.disabled || false"
3384
3723
  (valueChange)="onFileChange($event, column.name)"
@@ -3391,7 +3730,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3391
3730
  [label]="column.label"
3392
3731
  [placeholder]="column.placeholder || 'Select file'"
3393
3732
  [accept]="column.accept || '*/*'"
3394
- [control]="getControl(column.name)"
3733
+ [control]="control"
3395
3734
  [schema]="getSchema(column)"
3396
3735
  [disabled]="column.disabled || false"
3397
3736
  (valueChange)="onFileChange($event, column.name)"
@@ -3407,7 +3746,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3407
3746
  [label]="column.label"
3408
3747
  [placeholder]="column.placeholder || 'Search by Code or Name'"
3409
3748
  [options]="column.options ? mapSearchOptions(column.options) : []"
3410
- [control]="getControl(column.name)"
3749
+ [control]="control"
3411
3750
  [schema]="getSchema(column)"
3412
3751
  [disabled]="column.disabled || false"
3413
3752
  >
@@ -3415,43 +3754,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3415
3754
  </ng-container>
3416
3755
 
3417
3756
  <ng-container *ngIf="column.type === 'phone' && !column.hidden">
3418
- <fv-phone-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-phone-field>
3757
+ <fv-phone-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-phone-field>
3419
3758
  </ng-container>
3420
3759
 
3421
3760
  <ng-container *ngIf="column.type === 'uan' && !column.hidden">
3422
- <fv-uan-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-uan-field>
3761
+ <fv-uan-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-uan-field>
3423
3762
  </ng-container>
3424
3763
 
3425
3764
  <ng-container *ngIf="column.type === 'pf' && !column.hidden">
3426
- <fv-pf-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-pf-field>
3765
+ <fv-pf-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-pf-field>
3427
3766
  </ng-container>
3428
3767
 
3429
3768
  <ng-container *ngIf="column.type === 'esi' && !column.hidden">
3430
- <fv-esi-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-esi-field>
3769
+ <fv-esi-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-esi-field>
3431
3770
  </ng-container>
3432
3771
 
3433
3772
  <ng-container *ngIf="column.type === 'ifsc' && !column.hidden">
3434
- <fv-ifsc-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-ifsc-field>
3773
+ <fv-ifsc-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-ifsc-field>
3435
3774
  </ng-container>
3436
3775
 
3437
3776
  <ng-container *ngIf="column.type === 'micr' && !column.hidden">
3438
- <fv-micr-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-micr-field>
3777
+ <fv-micr-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-micr-field>
3439
3778
  </ng-container>
3440
3779
 
3441
3780
  <ng-container *ngIf="column.type === 'iban' && !column.hidden">
3442
- <fv-iban-field [label]="column.label" [control]="getControl(column.name)" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-iban-field>
3781
+ <fv-iban-field [label]="column.label" [control]="control" [schema]="getSchema(column)" [disabled]="column.disabled || false"></fv-iban-field>
3782
+ </ng-container>
3783
+
3784
+ <ng-container *ngIf="column.type === 'service-period' && !column.hidden">
3785
+ <fv-service-period
3786
+ [label]="column.label"
3787
+ [group]="group"
3788
+ [startField]="column.servicePeriodConfig?.startField || ''"
3789
+ [endField]="column.servicePeriodConfig?.endField || ''"
3790
+ [control]="control"
3791
+ ></fv-service-period>
3443
3792
  </ng-container>
3444
3793
  </div>
3445
- </ng-container>
3446
- </div>
3447
- </div>
3794
+ </ng-template>
3448
3795
 
3449
3796
 
3450
3797
  <div class="form-actions">
3451
3798
  <button
3452
3799
  type="submit"
3453
3800
  class="btn btn-primary"
3454
- [disabled]="form.invalid && submitted"
3455
3801
  >
3456
3802
  {{ config.submitLabel || 'Save' }}
3457
3803
  </button>
@@ -3465,7 +3811,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3465
3811
  </div>
3466
3812
  </form>
3467
3813
  </div>
3468
- `, styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";*{font-family:Poppins!important}.form-container{background:#f5f5f5;padding:5px 10px 10px;border-radius:8px;margin:0 auto;max-width:100%;box-sizing:border-box}.error-border{border-color:red!important}.hidden{display:none!important}.form-header{margin-bottom:5px}.form-header h2{margin:0;font-size:20px;font-weight:700;color:#303030}.dynamic-form{background:#fff;padding:10px;border-radius:6px;box-shadow:0 1px 3px #0000001a;max-width:100%;box-sizing:border-box}.form-section{margin-bottom:10px}.section-title{font-size:16px;font-weight:700;color:#303030;margin:0 0 16px;text-transform:uppercase;letter-spacing:.5px}.fields-row{display:grid;grid-template-columns:repeat(5,1fr);gap:16px;width:100%;box-sizing:border-box;overflow:visible}.form-field-wrapper{display:flex;position:relative;flex-direction:column;min-width:0;width:100%;box-sizing:border-box;overflow:visible}.password-toggle-btn{position:absolute;right:10px;top:32px;background:none;border:none;cursor:pointer;padding:4px;color:#666}.password-toggle-btn:hover{color:#333}.password-toggle-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}@media (min-width: 1200px) and (max-width: 1400px){.fields-row{grid-template-columns:repeat(5,1fr)}.form-control{width:180px;max-width:180px}}@media (max-width: 1200px){.fields-row{grid-template-columns:repeat(3,1fr)}.form-control{width:100%;max-width:100%}}@media (max-width: 992px){.fields-row{grid-template-columns:repeat(2,1fr);gap:12px}}@media (max-width: 768px){.fields-row{grid-template-columns:repeat(2,1fr);gap:10px}}@media (max-width: 640px){.fields-row{grid-template-columns:1fr;gap:12px}.form-field-wrapper{width:100%}}label{font-weight:600;margin-bottom:6px;color:#151d48;font-size:14px}.required{color:#e74c3c;margin-left:2px}.form-control{padding:5px 10px;width:100%;max-width:100%;height:34px;border:1px solid #8CBBA8;border-radius:8px;background-color:#fff;font-size:14px;font-weight:400;font-family:inherit;color:#1f2b41;transition:border-color .2s,box-shadow .2s;box-sizing:border-box}.form-control:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.form-control:disabled{background-color:#ecf0f1;cursor:not-allowed}textarea.form-control{resize:vertical;min-height:100px}select.form-control{cursor:pointer;appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23333' d='M6 9L1 4h10z'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 8px center;padding-right:40px}.checkbox-wrapper{display:flex;align-items:center;gap:8px}input[type=checkbox]{width:18px;height:18px;cursor:pointer}.checkbox-label{cursor:pointer;margin:0}.file-upload-container{display:flex;align-items:center;gap:20px;padding:20px;background:#fff;max-width:100%;box-sizing:border-box;flex-wrap:wrap}.material-icons{font-family:Material Icons!important;border-radius:4px;display:inline-block;width:40px;padding:8px;height:40px}.file-field-wrapper{width:100%;margin-top:16px;max-width:100%;box-sizing:border-box}.photo-preview-circle{width:100px;height:100px;border-radius:50%;overflow:hidden;border:2px solid #ddd;cursor:pointer;transition:border-color .2s}.photo-preview-circle:hover{border-color:#3498db}.preview-image,.default-photo{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.preview-image img{width:100%;height:100%;object-fit:cover}.default-photo{background:#f0f0f0;color:#999;font-size:40px}.file-info{display:flex;flex-direction:column;gap:8px}.btn-delete{border:none;border-radius:4px;cursor:pointer;font-size:14px;padding:0}.upload-btn-group{display:flex;align-items:center;border:1px solid #d3d3d3;border-radius:8px;background:#fff;overflow:hidden;width:fit-content}.btn-upload{background:#fff;color:#222;border:none;padding:0 18px;font-size:16px;border-right:1px solid #e0e0e0;border-radius:8px 0 0 8px;outline:none;box-shadow:none;cursor:pointer;height:40px;transition:background .2s}.btn-upload-icon{background:#f5f5f5;border:none;border-radius:0 8px 8px 0;display:flex;align-items:center;justify-content:center;width:48px;height:40px;cursor:pointer;transition:background .2s}.btn-upload-icon i.material-icons{font-size:22px;color:#222}.btn-upload:hover,.btn-upload-icon:hover{background:#f0f0f0}.btn-delete{background:#fff;color:red}.btn-delete:hover{background:#f0f0f0}.file-name{font-size:12px;color:#666;margin:10px 8px 8px}.file-label{font-weight:600;margin-bottom:6px;color:#151d48;font-size:14px;display:block}.radio-label{font-weight:600;margin-bottom:15px;color:#151d48;font-size:14px;display:block}.radio-group{display:flex;gap:20px;align-items:center}.radio-item{display:flex;align-items:center;gap:6px}input[type=radio]{width:18px;height:18px;cursor:pointer;accent-color:#3498db}.radio-option-label{cursor:pointer;margin:0;font-weight:400}.hint{font-size:12px;color:#7f8c8d;margin-top:4px}.error-message{color:#e74c3c;font-size:12px;margin-top:4px}.form-actions{display:flex;gap:8px;align-items:center;justify-content:space-between;gap:12px;padding-top:5px}.btn{padding:8px 20px;border:none;border-radius:7px;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s}.btn-primary{background-color:#006aff;color:#fff}.btn-primary:hover:not(:disabled){background-color:#2980b9}.btn-primary:disabled{background-color:#bdc3c7;cursor:not-allowed}.btn-secondary{background-color:#95a5a6;color:#fff}.btn-secondary:hover{background-color:#7f8c8d}.btn-outline{background-color:#303030;color:#fff}.btn-outline:hover{background-color:#2b2b2b;color:#fff}.month-year-wrapper{position:relative;display:flex;align-items:center;width:100%}.month-year-wrapper .form-control{padding-right:45px}.month-picker-hidden{position:absolute;opacity:0;pointer-events:none;width:0;height:0;border:none;padding:0;margin:0}.month-picker-btn{position:absolute;right:5px;background:transparent;border:none;cursor:pointer;padding:5px;display:flex;align-items:center;justify-content:center;color:#666;transition:color .2s;z-index:1}.month-picker-btn:hover:not(:disabled){color:#3498db}.month-picker-btn:disabled{cursor:not-allowed;opacity:.5}.month-picker-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}.search-select-wrapper{position:relative;width:100%}.search-select-input{padding-right:10px}.search-select-dropdown{position:absolute;top:calc(100% + 4px);left:0;right:auto;min-width:100%;max-width:calc(100vw - 16px);background:#fff;border:1px solid #d3d3d3;border-radius:8px;max-height:220px;overflow-y:auto;box-shadow:0 4px 10px #00000014;z-index:5;padding:6px 0;transform:translate(0);transition:transform .1s ease-out}.search-select-option{padding:8px 12px;cursor:pointer;display:flex;gap:8px;align-items:center;border-bottom:1px solid #f2f2f2}.search-select-option:last-child{border-bottom:none}.search-select-option:hover{background:#f5f9ff}.option-code{color:#0052cc;font-weight:700;min-width:90px}.option-name{color:#151d48;font-weight:500}.time-picker-wrapper{position:relative;display:flex;align-items:center;width:100%}.time-input{padding-right:45px;flex:1}.time-picker-btn{position:absolute;right:5px;background:transparent;border:none;cursor:pointer;padding:5px;display:flex;align-items:center;justify-content:center;color:#666;transition:color .2s;z-index:1}.time-picker-btn:hover:not(:disabled){color:#3498db}.time-picker-btn:disabled{cursor:not-allowed;opacity:.5}.time-picker-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}input[type=time]{cursor:pointer}input[type=time]::-webkit-calendar-picker-indicator{cursor:pointer;opacity:0;position:absolute;right:0;width:100%;height:100%}.date-picker-field{width:100%}.date-picker-field ::ng-deep .mat-mdc-form-field{width:100%}.date-picker-field ::ng-deep .mat-mdc-text-field-wrapper{background-color:#fff}.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__trailing{border-color:#8cbba8}.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:#8cbba8}.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#3498db}.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__leading,.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__notch,.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__trailing{border-color:red!important}.date-picker-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.date-picker-field ::ng-deep .mat-mdc-form-field-input-control input{font-family:Poppins!important;font-size:14px;font-weight:400;color:#1f2b41}\n"] }]
3814
+ `, styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";@import\"https://fonts.googleapis.com/icon?family=Material+Icons\";*{font-family:Poppins!important}.form-container{background:#f5f5f5;padding:5px 10px 10px;border-radius:8px;margin:0 auto;max-width:100%;box-sizing:border-box}.error-border{border-color:red!important}.hidden{display:none!important}.form-header{margin-bottom:5px}.form-header h2{margin:0;font-size:20px;font-weight:700;color:#303030}.dynamic-form{background:#fff;padding:10px;border-radius:6px;box-shadow:0 1px 3px #0000001a;max-width:100%;box-sizing:border-box}.form-section{margin-bottom:10px}.section-title{font-size:16px;font-weight:700;color:#303030;margin:0 0 16px;text-transform:uppercase;letter-spacing:.5px}.fields-row{display:grid;grid-template-columns:repeat(5,1fr);gap:16px;width:100%;box-sizing:border-box;overflow:visible}.form-field-wrapper{display:flex;position:relative;flex-direction:column;min-width:0;width:100%;box-sizing:border-box;overflow:visible}.password-toggle-btn{position:absolute;right:10px;top:32px;background:none;border:none;cursor:pointer;padding:4px;color:#666}.password-toggle-btn:hover{color:#333}.password-toggle-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}@media (min-width: 1200px) and (max-width: 1400px){.fields-row{grid-template-columns:repeat(5,1fr)}.form-control{width:180px;max-width:180px}}@media (max-width: 1200px){.fields-row{grid-template-columns:repeat(3,1fr)}.form-control{width:100%;max-width:100%}}@media (max-width: 992px){.fields-row{grid-template-columns:repeat(2,1fr);gap:12px}}@media (max-width: 768px){.fields-row{grid-template-columns:repeat(2,1fr);gap:10px}}@media (max-width: 640px){.fields-row{grid-template-columns:1fr;gap:12px}.form-field-wrapper{width:100%}}label{font-weight:600;margin-bottom:6px;color:#151d48;font-size:14px}.required{color:#e74c3c;margin-left:2px}.form-control{padding:5px 10px;width:100%;max-width:100%;height:34px;border:1px solid #8CBBA8;border-radius:8px;background-color:#fff;font-size:14px;font-weight:400;font-family:inherit;color:#1f2b41;transition:border-color .2s,box-shadow .2s;box-sizing:border-box}.form-control:focus{outline:none;border-color:#3498db;box-shadow:0 0 0 3px #3498db1a}.form-control:disabled{background-color:#ecf0f1;cursor:not-allowed}textarea.form-control{resize:vertical;min-height:100px}select.form-control{cursor:pointer;appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23333' d='M6 9L1 4h10z'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 8px center;padding-right:40px}.checkbox-wrapper{display:flex;align-items:center;gap:8px}input[type=checkbox]{width:18px;height:18px;cursor:pointer}.checkbox-label{cursor:pointer;margin:0}.file-upload-container{display:flex;align-items:center;gap:20px;padding:20px;background:#fff;max-width:100%;box-sizing:border-box;flex-wrap:wrap}.material-icons{font-family:Material Icons!important;border-radius:4px;display:inline-block;width:40px;padding:8px;height:40px}.file-field-wrapper{width:100%;margin-top:16px;max-width:100%;box-sizing:border-box}.photo-preview-circle{width:100px;height:100px;border-radius:50%;overflow:hidden;border:2px solid #ddd;cursor:pointer;transition:border-color .2s}.photo-preview-circle:hover{border-color:#3498db}.preview-image,.default-photo{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.preview-image img{width:100%;height:100%;object-fit:cover}.default-photo{background:#f0f0f0;color:#999;font-size:40px}.file-info{display:flex;flex-direction:column;gap:8px}.btn-delete{border:none;border-radius:4px;cursor:pointer;font-size:14px;padding:0}.upload-btn-group{display:flex;align-items:center;border:1px solid #d3d3d3;border-radius:8px;background:#fff;overflow:hidden;width:fit-content}.btn-upload{background:#fff;color:#222;border:none;padding:0 18px;font-size:16px;border-right:1px solid #e0e0e0;border-radius:8px 0 0 8px;outline:none;box-shadow:none;cursor:pointer;height:40px;transition:background .2s}.btn-upload-icon{background:#f5f5f5;border:none;border-radius:0 8px 8px 0;display:flex;align-items:center;justify-content:center;width:48px;height:40px;cursor:pointer;transition:background .2s}.btn-upload-icon i.material-icons{font-size:22px;color:#222}.btn-upload:hover,.btn-upload-icon:hover{background:#f0f0f0}.btn-delete{background:#fff;color:red}.btn-delete:hover{background:#f0f0f0}.file-name{font-size:12px;color:#666;margin:10px 8px 8px}.file-label{font-weight:600;margin-bottom:6px;color:#151d48;font-size:14px;display:block}.radio-label{font-weight:600;margin-bottom:15px;color:#151d48;font-size:14px;display:block}.radio-group{display:flex;gap:20px;align-items:center}.radio-item{display:flex;align-items:center;gap:6px}input[type=radio]{width:18px;height:18px;cursor:pointer;accent-color:#3498db}.radio-option-label{cursor:pointer;margin:0;font-weight:400}.hint{font-size:12px;color:#7f8c8d;margin-top:4px}.error-message{color:#e74c3c;font-size:12px;margin-top:4px}.form-actions{display:flex;gap:8px;align-items:center;justify-content:space-between;gap:12px;padding-top:5px}.btn{padding:8px 20px;border:none;border-radius:7px;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s}.btn-primary{background-color:#006aff;color:#fff}.btn-primary:hover:not(:disabled){background-color:#2980b9}.btn-primary:disabled{background-color:#bdc3c7;cursor:not-allowed}.btn-secondary{background-color:#95a5a6;color:#fff}.btn-secondary:hover{background-color:#7f8c8d}.btn-outline{background-color:#303030;color:#fff}.btn-outline:hover{background-color:#2b2b2b;color:#fff}.month-year-wrapper{position:relative;display:flex;align-items:center;width:100%}.month-year-wrapper .form-control{padding-right:45px}.month-picker-hidden{position:absolute;opacity:0;pointer-events:none;width:0;height:0;border:none;padding:0;margin:0}.month-picker-btn{position:absolute;right:5px;background:transparent;border:none;cursor:pointer;padding:5px;display:flex;align-items:center;justify-content:center;color:#666;transition:color .2s;z-index:1}.month-picker-btn:hover:not(:disabled){color:#3498db}.month-picker-btn:disabled{cursor:not-allowed;opacity:.5}.month-picker-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}.search-select-wrapper{position:relative;width:100%}.search-select-input{padding-right:10px}.search-select-dropdown{position:absolute;top:calc(100% + 4px);left:0;right:auto;min-width:100%;max-width:calc(100vw - 16px);background:#fff;border:1px solid #d3d3d3;border-radius:8px;max-height:220px;overflow-y:auto;box-shadow:0 4px 10px #00000014;z-index:5;padding:6px 0;transform:translate(0);transition:transform .1s ease-out}.search-select-option{padding:8px 12px;cursor:pointer;display:flex;gap:8px;align-items:center;border-bottom:1px solid #f2f2f2}.search-select-option:last-child{border-bottom:none}.search-select-option:hover{background:#f5f9ff}.repeatable-group-wrapper{position:relative;background:#fff;border:1px solid #eee;border-radius:8px;padding:20px 100px 20px 20px;margin-bottom:15px;box-shadow:0 1px 2px #0000000d}.repeatable-actions{position:absolute;top:45px;right:15px;display:flex;gap:8px;z-index:10}.btn-icon{background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:background .2s}.btn-icon:hover{background:#f0f0f0}.option-code{color:#0052cc;font-weight:700;min-width:90px}.option-name{color:#151d48;font-weight:500}.time-picker-wrapper{position:relative;display:flex;align-items:center;width:100%}.time-input{padding-right:45px;flex:1}.time-picker-btn{position:absolute;right:5px;background:transparent;border:none;cursor:pointer;padding:5px;display:flex;align-items:center;justify-content:center;color:#666;transition:color .2s;z-index:1}.time-picker-btn:hover:not(:disabled){color:#3498db}.time-picker-btn:disabled{cursor:not-allowed;opacity:.5}.time-picker-btn .material-icons{font-size:20px;width:auto;height:auto;padding:0}input[type=time]{cursor:pointer}input[type=time]::-webkit-calendar-picker-indicator{cursor:pointer;opacity:0;position:absolute;right:0;width:100%;height:100%}.date-picker-field{width:100%}.date-picker-field ::ng-deep .mat-mdc-form-field{width:100%}.date-picker-field ::ng-deep .mat-mdc-text-field-wrapper{background-color:#fff}.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__trailing{border-color:#8cbba8}.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:#8cbba8}.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__leading,.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__notch,.date-picker-field ::ng-deep .mdc-text-field--outlined.mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#3498db}.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__leading,.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__notch,.date-picker-field.error-border ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__trailing{border-color:red!important}.date-picker-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.date-picker-field ::ng-deep .mat-mdc-form-field-input-control input{font-family:Poppins!important;font-size:14px;font-weight:400;color:#1f2b41}\n"] }]
3469
3815
  }], ctorParameters: () => [{ type: i2.FormBuilder }, { type: i0.ElementRef }], propDecorators: { config: [{
3470
3816
  type: Input
3471
3817
  }], validationError: [{