@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.
- package/esm2022/lib/add-update-form/add-update-form.component.mjs +273 -132
- package/esm2022/lib/fv-date-field/fv-date-field.component.mjs +4 -6
- package/esm2022/lib/fv-dropdown/fv-dropdown.component.mjs +7 -6
- package/esm2022/lib/fv-email-field/fv-email-field.component.mjs +2 -5
- package/esm2022/lib/fv-entry-field/fv-entry-field.component.mjs +2 -4
- package/esm2022/lib/fv-file-selector/fv-file-selector.component.mjs +4 -4
- package/esm2022/lib/fv-iban-field/fv-iban-field.component.mjs +4 -3
- package/esm2022/lib/fv-image-selector/fv-image-selector.component.mjs +128 -33
- package/esm2022/lib/fv-micr-field/fv-micr-field.component.mjs +4 -3
- package/esm2022/lib/fv-month-year-field/fv-month-year-field.component.mjs +17 -9
- package/esm2022/lib/fv-name-code/fv-name-code.component.mjs +3 -3
- package/esm2022/lib/fv-number-field/fv-number-field.component.mjs +4 -6
- package/esm2022/lib/fv-password-field/fv-password-field.component.mjs +2 -4
- package/esm2022/lib/fv-phone-field/fv-phone-field.component.mjs +15 -6
- package/esm2022/lib/fv-radio-group/fv-radio-group.component.mjs +3 -3
- package/esm2022/lib/fv-rich-text-editor/fv-rich-text-editor.component.mjs +4 -6
- package/esm2022/lib/fv-service-period/fv-service-period.component.mjs +110 -0
- package/fesm2022/fovestta2-web-angular.mjs +562 -216
- package/fesm2022/fovestta2-web-angular.mjs.map +1 -1
- package/lib/add-update-form/add-update-form.component.d.ts +16 -4
- package/lib/fv-image-selector/fv-image-selector.component.d.ts +24 -0
- package/lib/fv-phone-field/fv-phone-field.component.d.ts +1 -0
- package/lib/fv-service-period/fv-service-period.component.d.ts +21 -0
- 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
|
-
|
|
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
|
-
|
|
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]=\"
|
|
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]=\"
|
|
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
|
-
|
|
300
|
-
|
|
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: '
|
|
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
|
-
|
|
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]=\"
|
|
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]=\"
|
|
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
|
-
|
|
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\"
|
|
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\"
|
|
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:
|
|
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:
|
|
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 =
|
|
824
|
-
const
|
|
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\"
|
|
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\"
|
|
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\"
|
|
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\"
|
|
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;
|
|
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
|
|
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
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 <
|
|
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 <
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
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
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
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.
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
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 :
|
|
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
|
-
|
|
2872
|
-
|
|
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
|
-
<
|
|
2956
|
-
<
|
|
2957
|
-
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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
|
-
|
|
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
|
-
<
|
|
3232
|
-
<
|
|
3233
|
-
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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
|
-
|
|
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: [{
|