@fovestta2/web-angular 1.0.28 → 1.1.1
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 +3 -3
- package/esm2022/lib/fv-document-field/fv-document-field.component.mjs +3 -3
- package/esm2022/lib/fv-dropdown/fv-dropdown.component.mjs +12 -4
- package/esm2022/lib/fv-file-selector/fv-file-selector.component.mjs +19 -3
- package/esm2022/lib/fv-image-selector/fv-image-selector.component.mjs +32 -19
- package/esm2022/lib/fv-name-code/fv-name-code.component.mjs +14 -4
- package/fesm2022/fovestta2-web-angular.mjs +77 -30
- package/fesm2022/fovestta2-web-angular.mjs.map +1 -1
- package/lib/fv-file-selector/fv-file-selector.component.d.ts +2 -0
- package/lib/fv-image-selector/fv-image-selector.component.d.ts +2 -0
- package/package.json +5 -2
|
@@ -696,6 +696,9 @@ class FvDropdownComponent {
|
|
|
696
696
|
// Search Control Changes
|
|
697
697
|
this.searchControl.valueChanges.subscribe(term => {
|
|
698
698
|
this.filterOptions(term || '');
|
|
699
|
+
if (!this.disabled && !this.isOpen) {
|
|
700
|
+
this.isOpen = true;
|
|
701
|
+
}
|
|
699
702
|
});
|
|
700
703
|
// Validate initial value
|
|
701
704
|
this.validateValue(this.control.value);
|
|
@@ -737,7 +740,12 @@ class FvDropdownComponent {
|
|
|
737
740
|
}
|
|
738
741
|
}
|
|
739
742
|
filterOptions(term) {
|
|
740
|
-
|
|
743
|
+
let actualTerm = term;
|
|
744
|
+
const selected = this.findOption(this.control?.value);
|
|
745
|
+
if (selected && term === selected.label) {
|
|
746
|
+
actualTerm = '';
|
|
747
|
+
}
|
|
748
|
+
this.filteredOptions = this.generateViewItems(this.options, actualTerm);
|
|
741
749
|
// Adjust highlight index if needed (reset if search changed)
|
|
742
750
|
if (this.filteredOptions.length === 1 && !this.filteredOptions[0].isGroupLabel) {
|
|
743
751
|
this.highlightedIndex = 0;
|
|
@@ -969,11 +977,11 @@ class FvDropdownComponent {
|
|
|
969
977
|
return viewItem.value === this.control?.value;
|
|
970
978
|
}
|
|
971
979
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvDropdownComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
972
|
-
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 <ng-container *ngFor=\"let option of filteredOptions; let i = index\">\r\n <div *ngIf=\"option.isGroupLabel\" class=\"fv-dropdown-group-label\">\r\n {{ option.label }}\r\n </div>\r\n <div *ngIf=\"!option.isGroupLabel\" class=\"fv-dropdown-option\"\r\n [class.fv-dropdown-option-selected]=\"isSelected(option)\"\r\n [class.fv-dropdown-option-highlighted]=\"i === highlightedIndex\"\r\n [class.child-option]=\"option.isChild\" (mouseenter)=\"highlightedIndex = i\"\r\n (
|
|
980
|
+
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(); onInputFocus()\"\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 <ng-container *ngFor=\"let option of filteredOptions; let i = index\">\r\n <div *ngIf=\"option.isGroupLabel\" class=\"fv-dropdown-group-label\">\r\n {{ option.label }}\r\n </div>\r\n <div *ngIf=\"!option.isGroupLabel\" class=\"fv-dropdown-option\"\r\n [class.fv-dropdown-option-selected]=\"isSelected(option)\"\r\n [class.fv-dropdown-option-highlighted]=\"i === highlightedIndex\"\r\n [class.child-option]=\"option.isChild\" (mouseenter)=\"highlightedIndex = i\"\r\n (mousedown)=\"selectOption(option); $event.preventDefault()\">\r\n {{ option.label }}\r\n </div>\r\n </ng-container>\r\n\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:#151d48;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}.fv-dropdown-group-label{font-weight:600;color:#151d48;padding:8px 16px;background-color:#f9f9f9;cursor:default;pointer-events:none;font-size:14px;letter-spacing:normal;border-bottom:1px solid #eee}.fv-dropdown-option.child-option{padding-left:32px}\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"] }] });
|
|
973
981
|
}
|
|
974
982
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvDropdownComponent, decorators: [{
|
|
975
983
|
type: Component,
|
|
976
|
-
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 <ng-container *ngFor=\"let option of filteredOptions; let i = index\">\r\n <div *ngIf=\"option.isGroupLabel\" class=\"fv-dropdown-group-label\">\r\n {{ option.label }}\r\n </div>\r\n <div *ngIf=\"!option.isGroupLabel\" class=\"fv-dropdown-option\"\r\n [class.fv-dropdown-option-selected]=\"isSelected(option)\"\r\n [class.fv-dropdown-option-highlighted]=\"i === highlightedIndex\"\r\n [class.child-option]=\"option.isChild\" (mouseenter)=\"highlightedIndex = i\"\r\n (
|
|
984
|
+
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(); onInputFocus()\"\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 <ng-container *ngFor=\"let option of filteredOptions; let i = index\">\r\n <div *ngIf=\"option.isGroupLabel\" class=\"fv-dropdown-group-label\">\r\n {{ option.label }}\r\n </div>\r\n <div *ngIf=\"!option.isGroupLabel\" class=\"fv-dropdown-option\"\r\n [class.fv-dropdown-option-selected]=\"isSelected(option)\"\r\n [class.fv-dropdown-option-highlighted]=\"i === highlightedIndex\"\r\n [class.child-option]=\"option.isChild\" (mouseenter)=\"highlightedIndex = i\"\r\n (mousedown)=\"selectOption(option); $event.preventDefault()\">\r\n {{ option.label }}\r\n </div>\r\n </ng-container>\r\n\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:#151d48;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}.fv-dropdown-group-label{font-weight:600;color:#151d48;padding:8px 16px;background-color:#f9f9f9;cursor:default;pointer-events:none;font-size:14px;letter-spacing:normal;border-bottom:1px solid #eee}.fv-dropdown-option.child-option{padding-left:32px}\n"] }]
|
|
977
985
|
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { label: [{
|
|
978
986
|
type: Input
|
|
979
987
|
}], placeholder: [{
|
|
@@ -1131,6 +1139,22 @@ class FvFileSelectorComponent {
|
|
|
1131
1139
|
}
|
|
1132
1140
|
this.blur.emit();
|
|
1133
1141
|
}
|
|
1142
|
+
viewFile() {
|
|
1143
|
+
const fileInfo = this.selectedFile;
|
|
1144
|
+
if (!fileInfo || !fileInfo.file)
|
|
1145
|
+
return;
|
|
1146
|
+
const url = URL.createObjectURL(fileInfo.file);
|
|
1147
|
+
const link = document.createElement('a');
|
|
1148
|
+
link.href = url;
|
|
1149
|
+
link.download = fileInfo.name || 'document';
|
|
1150
|
+
document.body.appendChild(link);
|
|
1151
|
+
link.click();
|
|
1152
|
+
document.body.removeChild(link);
|
|
1153
|
+
setTimeout(() => URL.revokeObjectURL(url), 100);
|
|
1154
|
+
}
|
|
1155
|
+
canView() {
|
|
1156
|
+
return !!(this.selectedFile && this.selectedFile.file);
|
|
1157
|
+
}
|
|
1134
1158
|
getFileIcon() {
|
|
1135
1159
|
const fileInfo = this.selectedFile || this.control?.value;
|
|
1136
1160
|
if (!fileInfo)
|
|
@@ -1168,11 +1192,11 @@ class FvFileSelectorComponent {
|
|
|
1168
1192
|
return errorMessages[this.errorMessage] || this.errorMessage;
|
|
1169
1193
|
}
|
|
1170
1194
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvFileSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1171
|
-
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\" *ngIf=\"control\">\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 <div
|
|
1195
|
+
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\" *ngIf=\"control\">\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 <div class=\"fv-file-selector-box\" [class.fv-disabled]=\"disabled\"\r\n [class.fv-error]=\"errorMessage && control?.touched\">\r\n <div class=\"fv-file-selector-main\" (click)=\"openFileDialog()\">\r\n <span class=\"fv-upload-text\">{{ selectedFile ? selectedFile.name : placeholder }}</span>\r\n </div>\r\n <div class=\"fv-upload-icon-section\">\r\n <ng-container *ngIf=\"!selectedFile\">\r\n <svg viewBox=\"0 0 24 24\" class=\"upload-icon\" (click)=\"openFileDialog()\">\r\n <path d=\"M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z\" fill=\"currentColor\" />\r\n </svg>\r\n </ng-container>\r\n <ng-container *ngIf=\"selectedFile\">\r\n <div class=\"fv-action-icons\">\r\n <span class=\"material-icons view-icon\" (click)=\"viewFile()\" title=\"View\"\r\n *ngIf=\"canView()\">visibility</span>\r\n <span class=\"material-icons delete-icon\" (click)=\"removeFile()\" title=\"Delete\">delete</span>\r\n </div>\r\n </ng-container>\r\n </div>\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:fit-content;max-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-box{display:flex;align-items:center;border:1px solid #dcdcdc;border-radius:8px;background:#fff;padding:0;cursor:default;overflow:hidden;width:100%;min-width:160px;height:34px;transition:all .2s ease}.fv-file-selector-box:hover:not(.fv-disabled){border-color:#8cbba8}.fv-disabled{cursor:not-allowed!important;background-color:#f9f9f9;opacity:.7}.fv-error{border-color:#dc3545!important}.fv-file-selector-main{flex:1;display:flex;align-items:center;padding:0 12px;height:100%;cursor:pointer;overflow:hidden}.fv-upload-text{font-size:14px;color:#1f2b41;text-align:left;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fv-upload-icon-section{background:#f4f5f7;padding:0 12px;border-left:1px solid #dcdcdc;display:flex;align-items:center;justify-content:center;color:#333;height:100%}.upload-icon{width:20px;height:20px;cursor:pointer}.fv-action-icons{display:flex;align-items:center;gap:8px}.material-icons{font-size:20px}.view-icon{color:#3498db;cursor:pointer}.delete-icon{color:#e74c3c;cursor:pointer}.view-icon:hover,.delete-icon:hover{opacity:.8}.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 }] });
|
|
1172
1196
|
}
|
|
1173
1197
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvFileSelectorComponent, decorators: [{
|
|
1174
1198
|
type: Component,
|
|
1175
|
-
args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-file-selector', template: "<div class=\"fv-file-selector-container\" *ngIf=\"control\">\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 <div
|
|
1199
|
+
args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-file-selector', template: "<div class=\"fv-file-selector-container\" *ngIf=\"control\">\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 <div class=\"fv-file-selector-box\" [class.fv-disabled]=\"disabled\"\r\n [class.fv-error]=\"errorMessage && control?.touched\">\r\n <div class=\"fv-file-selector-main\" (click)=\"openFileDialog()\">\r\n <span class=\"fv-upload-text\">{{ selectedFile ? selectedFile.name : placeholder }}</span>\r\n </div>\r\n <div class=\"fv-upload-icon-section\">\r\n <ng-container *ngIf=\"!selectedFile\">\r\n <svg viewBox=\"0 0 24 24\" class=\"upload-icon\" (click)=\"openFileDialog()\">\r\n <path d=\"M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z\" fill=\"currentColor\" />\r\n </svg>\r\n </ng-container>\r\n <ng-container *ngIf=\"selectedFile\">\r\n <div class=\"fv-action-icons\">\r\n <span class=\"material-icons view-icon\" (click)=\"viewFile()\" title=\"View\"\r\n *ngIf=\"canView()\">visibility</span>\r\n <span class=\"material-icons delete-icon\" (click)=\"removeFile()\" title=\"Delete\">delete</span>\r\n </div>\r\n </ng-container>\r\n </div>\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:fit-content;max-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-box{display:flex;align-items:center;border:1px solid #dcdcdc;border-radius:8px;background:#fff;padding:0;cursor:default;overflow:hidden;width:100%;min-width:160px;height:34px;transition:all .2s ease}.fv-file-selector-box:hover:not(.fv-disabled){border-color:#8cbba8}.fv-disabled{cursor:not-allowed!important;background-color:#f9f9f9;opacity:.7}.fv-error{border-color:#dc3545!important}.fv-file-selector-main{flex:1;display:flex;align-items:center;padding:0 12px;height:100%;cursor:pointer;overflow:hidden}.fv-upload-text{font-size:14px;color:#1f2b41;text-align:left;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fv-upload-icon-section{background:#f4f5f7;padding:0 12px;border-left:1px solid #dcdcdc;display:flex;align-items:center;justify-content:center;color:#333;height:100%}.upload-icon{width:20px;height:20px;cursor:pointer}.fv-action-icons{display:flex;align-items:center;gap:8px}.material-icons{font-size:20px}.view-icon{color:#3498db;cursor:pointer}.delete-icon{color:#e74c3c;cursor:pointer}.view-icon:hover,.delete-icon:hover{opacity:.8}.fv-file-selector-error-message{margin-top:4px;font-size:12px;color:#e74c3c}\n"] }]
|
|
1176
1200
|
}], propDecorators: { label: [{
|
|
1177
1201
|
type: Input
|
|
1178
1202
|
}], placeholder: [{
|
|
@@ -1216,9 +1240,12 @@ class FvImageSelectorComponent {
|
|
|
1216
1240
|
editImageSrc = null;
|
|
1217
1241
|
editImageName = '';
|
|
1218
1242
|
scale = 1;
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1243
|
+
fitScale = 1; // auto-calculated scale to fit image in the crop circle
|
|
1244
|
+
minScale = 0.1;
|
|
1245
|
+
maxScale = 5;
|
|
1246
|
+
step = 0.05;
|
|
1247
|
+
// The crop circle display diameter in the modal (matches CSS .fv-crop-mask radius = 150px)
|
|
1248
|
+
CROP_DISPLAY_SIZE = 300;
|
|
1222
1249
|
panX = 0;
|
|
1223
1250
|
panY = 0;
|
|
1224
1251
|
isDragging = false;
|
|
@@ -1288,9 +1315,18 @@ class FvImageSelectorComponent {
|
|
|
1288
1315
|
}
|
|
1289
1316
|
const reader = new FileReader();
|
|
1290
1317
|
reader.onload = (e) => {
|
|
1291
|
-
|
|
1318
|
+
const src = e.target.result;
|
|
1319
|
+
this.editImageSrc = src;
|
|
1292
1320
|
this.editImageName = file.name;
|
|
1293
|
-
|
|
1321
|
+
// Load image to get natural dimensions before opening editor
|
|
1322
|
+
const tempImg = new Image();
|
|
1323
|
+
tempImg.onload = () => {
|
|
1324
|
+
// Calculate scale to fit entire image inside the crop circle
|
|
1325
|
+
const fitS = Math.min(this.CROP_DISPLAY_SIZE / tempImg.naturalWidth, this.CROP_DISPLAY_SIZE / tempImg.naturalHeight);
|
|
1326
|
+
this.fitScale = fitS;
|
|
1327
|
+
this.openEditor();
|
|
1328
|
+
};
|
|
1329
|
+
tempImg.src = src;
|
|
1294
1330
|
input.value = '';
|
|
1295
1331
|
};
|
|
1296
1332
|
reader.readAsDataURL(file);
|
|
@@ -1298,7 +1334,7 @@ class FvImageSelectorComponent {
|
|
|
1298
1334
|
}
|
|
1299
1335
|
openEditor() {
|
|
1300
1336
|
this.isEditorOpen = true;
|
|
1301
|
-
this.scale =
|
|
1337
|
+
this.scale = this.fitScale;
|
|
1302
1338
|
this.panX = 0;
|
|
1303
1339
|
this.panY = 0;
|
|
1304
1340
|
}
|
|
@@ -1335,7 +1371,7 @@ class FvImageSelectorComponent {
|
|
|
1335
1371
|
this.scale = Math.max(this.scale - this.step, this.minScale);
|
|
1336
1372
|
}
|
|
1337
1373
|
resetEditor() {
|
|
1338
|
-
this.scale =
|
|
1374
|
+
this.scale = this.fitScale;
|
|
1339
1375
|
this.panX = 0;
|
|
1340
1376
|
this.panY = 0;
|
|
1341
1377
|
}
|
|
@@ -1347,31 +1383,32 @@ class FvImageSelectorComponent {
|
|
|
1347
1383
|
const ctx = canvas.getContext('2d');
|
|
1348
1384
|
if (!ctx)
|
|
1349
1385
|
return;
|
|
1350
|
-
// Output size
|
|
1351
|
-
const size =
|
|
1386
|
+
// Output size matches the crop circle size (300x300)
|
|
1387
|
+
const size = this.CROP_DISPLAY_SIZE;
|
|
1352
1388
|
canvas.width = size;
|
|
1353
1389
|
canvas.height = size;
|
|
1390
|
+
// Clip to circle for clean avatar output
|
|
1391
|
+
ctx.beginPath();
|
|
1392
|
+
ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
|
|
1393
|
+
ctx.clip();
|
|
1354
1394
|
// Fill background
|
|
1355
1395
|
ctx.fillStyle = '#ffffff';
|
|
1356
1396
|
ctx.fillRect(0, 0, size, size);
|
|
1357
|
-
//
|
|
1397
|
+
// The crop container in the DOM is 100% wide but crop mask circle is CROP_DISPLAY_SIZE px diameter.
|
|
1398
|
+
// panX/panY are in display pixels (relative to the container center).
|
|
1399
|
+
// We draw: center canvas, apply pan (already in canvas-equivalent pixels since display size == output size), scale, then center image.
|
|
1358
1400
|
ctx.translate(size / 2, size / 2);
|
|
1359
1401
|
ctx.translate(this.panX, this.panY);
|
|
1360
1402
|
ctx.scale(this.scale, this.scale);
|
|
1361
1403
|
ctx.translate(-img.naturalWidth / 2, -img.naturalHeight / 2);
|
|
1362
1404
|
ctx.drawImage(img, 0, 0);
|
|
1363
1405
|
const dataUrl = canvas.toDataURL('image/png');
|
|
1364
|
-
// Convert base64 to File object to mimic file selection
|
|
1365
|
-
// We'll use the original name but .png
|
|
1366
|
-
// This 'file' is mocked, but 'url' is what is displayed.
|
|
1367
|
-
// In real app we might want to upload the blob.
|
|
1368
|
-
// For ImageInfo we store the dataURL as url.
|
|
1369
1406
|
const imageInfo = {
|
|
1370
1407
|
file: new File([this.dataURLtoBlob(dataUrl)], this.editImageName, { type: 'image/png' }),
|
|
1371
1408
|
url: this.sanitizer.bypassSecurityTrustUrl(dataUrl),
|
|
1372
1409
|
width: size,
|
|
1373
1410
|
height: size,
|
|
1374
|
-
size: 0
|
|
1411
|
+
size: 0
|
|
1375
1412
|
};
|
|
1376
1413
|
this.selectedImage = imageInfo;
|
|
1377
1414
|
if (this.control) {
|
|
@@ -1427,11 +1464,11 @@ class FvImageSelectorComponent {
|
|
|
1427
1464
|
return errorMessages[this.errorMessage] || this.errorMessage;
|
|
1428
1465
|
}
|
|
1429
1466
|
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 });
|
|
1430
|
-
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\" *ngIf=\"control\">\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 && control?.touched\" 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:fit-content;max-width:100%}.fv-image-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700;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 16px;font-size:14px;color:#1f2b41;cursor:pointer;overflow:hidden;height:34px;width:100px;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:40px;height:100%;padding:8px;background:#f4f5f7;border-left:1px solid #dcdcdc;fill:#333;margin-left:16px}.fv-preview-row{display:flex;align-items:center;gap:8px;padding:8px 0;width:100px;box-sizing:border-box}.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:6px;border-radius:50%;transition:background .2s;display:flex}.fv-trash-btn:hover:not(:disabled){background:#fee}.trash-icon{width:22px;height:22px;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;appearance:none;height:4px;background:#ddd;border-radius:2px;outline:none}.zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;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 }] });
|
|
1467
|
+
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\" *ngIf=\"control\">\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 <!-- Avatar circle -->\r\n <div class=\"fv-avatar-wrapper\" [class.fv-disabled]=\"disabled\" [class.fv-error]=\"errorMessage && control?.touched\">\r\n <div class=\"fv-avatar-circle\" (click)=\"!disabled && openImageDialog()\">\r\n <ng-container *ngIf=\"selectedImage; else emptyAvatar\">\r\n <img [src]=\"selectedImage.url\" alt=\"Profile Photo\" class=\"fv-avatar-img\" />\r\n <div class=\"fv-avatar-overlay\">\r\n <svg viewBox=\"0 0 24 24\" class=\"fv-overlay-icon\">\r\n <path d=\"M12 20h9\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" />\r\n <path d=\"M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4L16.5 3.5z\" stroke=\"white\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\" />\r\n </svg>\r\n </div>\r\n </ng-container>\r\n <ng-template #emptyAvatar>\r\n <div class=\"fv-avatar-placeholder\">\r\n <svg viewBox=\"0 0 24 24\" class=\"fv-camera-icon\">\r\n <path d=\"M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z\"\r\n stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\" stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\" />\r\n <circle cx=\"12\" cy=\"13\" r=\"4\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\" />\r\n </svg>\r\n <span class=\"fv-avatar-hint\">Upload Photo</span>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- Action buttons below the circle -->\r\n <div class=\"fv-avatar-actions\" *ngIf=\"selectedImage && !disabled\">\r\n <button type=\"button\" class=\"fv-avatar-btn fv-btn-edit\" (click)=\"openImageDialog()\" title=\"Change Photo\">\r\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\">\r\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\" stroke=\"currentColor\"\r\n stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n <path d=\"M18.5 2.5a2.121 2.121 0 1 1 3 3L12 15l-4 1 1-4 9.5-9.5z\" stroke=\"currentColor\"\r\n stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n Change\r\n </button>\r\n <button type=\"button\" class=\"fv-avatar-btn fv-btn-remove\" (click)=\"removeImage()\" title=\"Remove Photo\">\r\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\">\r\n <polyline points=\"3 6 5 6 21 6\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n <path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\" stroke=\"currentColor\" stroke-width=\"2\"\r\n fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n Remove\r\n </button>\r\n </div>\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 && control?.touched\" class=\"fv-image-selector-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n\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:fit-content;max-width:100%}.fv-image-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700;margin-left:4px}.fv-avatar-wrapper{display:flex;flex-direction:column;align-items:flex-start;gap:10px}.fv-avatar-wrapper.fv-error .fv-avatar-circle{border-color:#dc3545!important}.fv-avatar-wrapper.fv-disabled .fv-avatar-circle{opacity:.6;cursor:not-allowed}.fv-avatar-circle{width:100px;height:100px;border-radius:50%;overflow:hidden;border:2px solid #dcdcdc;cursor:pointer;position:relative;background:#f4f5f7;transition:border-color .2s ease;flex-shrink:0}.fv-avatar-circle:hover:not(.fv-disabled){border-color:#8cbba8}.fv-avatar-img{width:100%;height:100%;object-fit:cover;display:block}.fv-avatar-overlay{position:absolute;inset:0;background:#0006;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease}.fv-avatar-circle:hover .fv-avatar-overlay{opacity:1}.fv-overlay-icon{width:24px;height:24px}.fv-avatar-placeholder{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;color:#aaa}.fv-camera-icon{width:36px;height:36px}.fv-avatar-hint{font-size:10px;font-weight:500;color:#aaa;text-align:center;line-height:1.2}.fv-avatar-actions{display:flex;gap:8px}.fv-avatar-btn{display:flex;align-items:center;gap:5px;padding:5px 12px;border-radius:6px;font-size:12px;font-weight:500;cursor:pointer;border:1px solid #dcdcdc;transition:all .2s ease;font-family:Poppins,sans-serif}.fv-btn-edit{background:#fff;color:#3498db;border-color:#3498db}.fv-btn-edit:hover{background:#e8f4fc}.fv-btn-remove{background:#fff;color:#e74c3c;border-color:#e74c3c}.fv-btn-remove:hover{background:#fdecea}.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:300px;height:300px;background:#333;position:relative;overflow:hidden;margin:0 auto 24px;border-radius:50%;cursor:move;cursor:grab;display:flex;align-items:center;justify-content:center;border:2px solid #555}.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{display: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;appearance:none;height:4px;background:#ddd;border-radius:2px;outline:none}.zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;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 }] });
|
|
1431
1468
|
}
|
|
1432
1469
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvImageSelectorComponent, decorators: [{
|
|
1433
1470
|
type: Component,
|
|
1434
|
-
args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-image-selector', template: "<div class=\"fv-image-selector-container\" *ngIf=\"control\">\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 && control?.touched\" 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:fit-content;max-width:100%}.fv-image-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700;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 16px;font-size:14px;color:#1f2b41;cursor:pointer;overflow:hidden;height:34px;width:100px;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:40px;height:100%;padding:8px;background:#f4f5f7;border-left:1px solid #dcdcdc;fill:#333;margin-left:16px}.fv-preview-row{display:flex;align-items:center;gap:8px;padding:8px 0;width:100px;box-sizing:border-box}.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:6px;border-radius:50%;transition:background .2s;display:flex}.fv-trash-btn:hover:not(:disabled){background:#fee}.trash-icon{width:22px;height:22px;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;appearance:none;height:4px;background:#ddd;border-radius:2px;outline:none}.zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;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"] }]
|
|
1471
|
+
args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-image-selector', template: "<div class=\"fv-image-selector-container\" *ngIf=\"control\">\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 <!-- Avatar circle -->\r\n <div class=\"fv-avatar-wrapper\" [class.fv-disabled]=\"disabled\" [class.fv-error]=\"errorMessage && control?.touched\">\r\n <div class=\"fv-avatar-circle\" (click)=\"!disabled && openImageDialog()\">\r\n <ng-container *ngIf=\"selectedImage; else emptyAvatar\">\r\n <img [src]=\"selectedImage.url\" alt=\"Profile Photo\" class=\"fv-avatar-img\" />\r\n <div class=\"fv-avatar-overlay\">\r\n <svg viewBox=\"0 0 24 24\" class=\"fv-overlay-icon\">\r\n <path d=\"M12 20h9\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" />\r\n <path d=\"M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4L16.5 3.5z\" stroke=\"white\"\r\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\" />\r\n </svg>\r\n </div>\r\n </ng-container>\r\n <ng-template #emptyAvatar>\r\n <div class=\"fv-avatar-placeholder\">\r\n <svg viewBox=\"0 0 24 24\" class=\"fv-camera-icon\">\r\n <path d=\"M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z\"\r\n stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\" stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\" />\r\n <circle cx=\"12\" cy=\"13\" r=\"4\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\" />\r\n </svg>\r\n <span class=\"fv-avatar-hint\">Upload Photo</span>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- Action buttons below the circle -->\r\n <div class=\"fv-avatar-actions\" *ngIf=\"selectedImage && !disabled\">\r\n <button type=\"button\" class=\"fv-avatar-btn fv-btn-edit\" (click)=\"openImageDialog()\" title=\"Change Photo\">\r\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\">\r\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\" stroke=\"currentColor\"\r\n stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n <path d=\"M18.5 2.5a2.121 2.121 0 1 1 3 3L12 15l-4 1 1-4 9.5-9.5z\" stroke=\"currentColor\"\r\n stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n Change\r\n </button>\r\n <button type=\"button\" class=\"fv-avatar-btn fv-btn-remove\" (click)=\"removeImage()\" title=\"Remove Photo\">\r\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\">\r\n <polyline points=\"3 6 5 6 21 6\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\"\r\n stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n <path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\" stroke=\"currentColor\" stroke-width=\"2\"\r\n fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n Remove\r\n </button>\r\n </div>\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 && control?.touched\" class=\"fv-image-selector-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n\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:fit-content;max-width:100%}.fv-image-selector-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700;margin-left:4px}.fv-avatar-wrapper{display:flex;flex-direction:column;align-items:flex-start;gap:10px}.fv-avatar-wrapper.fv-error .fv-avatar-circle{border-color:#dc3545!important}.fv-avatar-wrapper.fv-disabled .fv-avatar-circle{opacity:.6;cursor:not-allowed}.fv-avatar-circle{width:100px;height:100px;border-radius:50%;overflow:hidden;border:2px solid #dcdcdc;cursor:pointer;position:relative;background:#f4f5f7;transition:border-color .2s ease;flex-shrink:0}.fv-avatar-circle:hover:not(.fv-disabled){border-color:#8cbba8}.fv-avatar-img{width:100%;height:100%;object-fit:cover;display:block}.fv-avatar-overlay{position:absolute;inset:0;background:#0006;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease}.fv-avatar-circle:hover .fv-avatar-overlay{opacity:1}.fv-overlay-icon{width:24px;height:24px}.fv-avatar-placeholder{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;color:#aaa}.fv-camera-icon{width:36px;height:36px}.fv-avatar-hint{font-size:10px;font-weight:500;color:#aaa;text-align:center;line-height:1.2}.fv-avatar-actions{display:flex;gap:8px}.fv-avatar-btn{display:flex;align-items:center;gap:5px;padding:5px 12px;border-radius:6px;font-size:12px;font-weight:500;cursor:pointer;border:1px solid #dcdcdc;transition:all .2s ease;font-family:Poppins,sans-serif}.fv-btn-edit{background:#fff;color:#3498db;border-color:#3498db}.fv-btn-edit:hover{background:#e8f4fc}.fv-btn-remove{background:#fff;color:#e74c3c;border-color:#e74c3c}.fv-btn-remove:hover{background:#fdecea}.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:300px;height:300px;background:#333;position:relative;overflow:hidden;margin:0 auto 24px;border-radius:50%;cursor:move;cursor:grab;display:flex;align-items:center;justify-content:center;border:2px solid #555}.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{display: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;appearance:none;height:4px;background:#ddd;border-radius:2px;outline:none}.zoom-slider::-webkit-slider-thumb{-webkit-appearance:none;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"] }]
|
|
1435
1472
|
}], ctorParameters: () => [{ type: i1$1.DomSanitizer }], propDecorators: { label: [{
|
|
1436
1473
|
type: Input
|
|
1437
1474
|
}], placeholder: [{
|
|
@@ -1633,7 +1670,7 @@ class FvNameCodeComponent {
|
|
|
1633
1670
|
value = null;
|
|
1634
1671
|
ngOnChanges(changes) {
|
|
1635
1672
|
if (changes['options']) {
|
|
1636
|
-
this.
|
|
1673
|
+
this.filterOptions(this.searchControl?.value || '');
|
|
1637
1674
|
if (this.value !== null && this.value !== undefined) {
|
|
1638
1675
|
this.writeValue(this.value);
|
|
1639
1676
|
}
|
|
@@ -1643,6 +1680,9 @@ class FvNameCodeComponent {
|
|
|
1643
1680
|
this.filteredOptions = this.options;
|
|
1644
1681
|
this.searchControl.valueChanges.subscribe(term => {
|
|
1645
1682
|
this.filterOptions(term || '');
|
|
1683
|
+
if (!this.disabled && !this.isOpen) {
|
|
1684
|
+
this.isOpen = true;
|
|
1685
|
+
}
|
|
1646
1686
|
});
|
|
1647
1687
|
if (this.control) {
|
|
1648
1688
|
if (this.control.value !== null && this.control.value !== undefined) {
|
|
@@ -1662,6 +1702,11 @@ class FvNameCodeComponent {
|
|
|
1662
1702
|
this.filteredOptions = this.options;
|
|
1663
1703
|
return;
|
|
1664
1704
|
}
|
|
1705
|
+
const selectedOption = this.options.find(o => o.value === this.value);
|
|
1706
|
+
if (selectedOption && term === `${selectedOption.code} - ${selectedOption.name}`) {
|
|
1707
|
+
this.filteredOptions = this.options;
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1665
1710
|
const lowerTerm = term.toLowerCase();
|
|
1666
1711
|
this.filteredOptions = this.options.filter(opt => opt.code.toLowerCase().includes(lowerTerm) ||
|
|
1667
1712
|
opt.name.toLowerCase().includes(lowerTerm));
|
|
@@ -1854,6 +1899,7 @@ class FvNameCodeComponent {
|
|
|
1854
1899
|
[placeholder]="(searchControl.value || isOpen) ? '' : (placeholder || 'Search by Code or Name')"
|
|
1855
1900
|
[formControl]="searchControl"
|
|
1856
1901
|
(focus)="openDropdown()"
|
|
1902
|
+
(click)="openDropdown()"
|
|
1857
1903
|
(blur)="onBlur()"
|
|
1858
1904
|
[class.error]="isInvalid()"
|
|
1859
1905
|
/>
|
|
@@ -1864,7 +1910,7 @@ class FvNameCodeComponent {
|
|
|
1864
1910
|
class="dropdown-item"
|
|
1865
1911
|
*ngFor="let option of filteredOptions; let i = index"
|
|
1866
1912
|
[class.highlighted]="i === highlightedIndex"
|
|
1867
|
-
(
|
|
1913
|
+
(mousedown)="selectOption(option); $event.preventDefault()"
|
|
1868
1914
|
>
|
|
1869
1915
|
<span class="code">{{ option.code }}</span>
|
|
1870
1916
|
<span class="name">{{ option.name }}</span>
|
|
@@ -1896,6 +1942,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
1896
1942
|
[placeholder]="(searchControl.value || isOpen) ? '' : (placeholder || 'Search by Code or Name')"
|
|
1897
1943
|
[formControl]="searchControl"
|
|
1898
1944
|
(focus)="openDropdown()"
|
|
1945
|
+
(click)="openDropdown()"
|
|
1899
1946
|
(blur)="onBlur()"
|
|
1900
1947
|
[class.error]="isInvalid()"
|
|
1901
1948
|
/>
|
|
@@ -1906,7 +1953,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
1906
1953
|
class="dropdown-item"
|
|
1907
1954
|
*ngFor="let option of filteredOptions; let i = index"
|
|
1908
1955
|
[class.highlighted]="i === highlightedIndex"
|
|
1909
|
-
(
|
|
1956
|
+
(mousedown)="selectOption(option); $event.preventDefault()"
|
|
1910
1957
|
>
|
|
1911
1958
|
<span class="code">{{ option.code }}</span>
|
|
1912
1959
|
<span class="name">{{ option.name }}</span>
|
|
@@ -3015,11 +3062,11 @@ class FvDocumentFieldComponent {
|
|
|
3015
3062
|
return errorMessages[this.errorMessage] || this.errorMessage;
|
|
3016
3063
|
}
|
|
3017
3064
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvDocumentFieldComponent, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
|
|
3018
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvDocumentFieldComponent, isStandalone: true, selector: "fv-document-field", inputs: { label: "label", placeholder: "placeholder", schema: "schema", control: "control", disabled: "disabled", accept: "accept", maxSize: "maxSize" }, outputs: { valueChange: "valueChange" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-document-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-document-field-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 <div
|
|
3065
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FvDocumentFieldComponent, isStandalone: true, selector: "fv-document-field", inputs: { label: "label", placeholder: "placeholder", schema: "schema", control: "control", disabled: "disabled", accept: "accept", maxSize: "maxSize" }, outputs: { valueChange: "valueChange" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"fv-document-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-document-field-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 <div class=\"fv-upload-button-container\" [class.fv-disabled]=\"disabled\"\r\n [class.fv-error]=\"errorMessage && control?.touched\">\r\n <div class=\"fv-upload-main-section\" (click)=\"openFileDialog()\">\r\n <span class=\"fv-upload-text\">{{ fileInfo ? fileInfo.name : placeholder }}</span>\r\n </div>\r\n <div class=\"fv-upload-icon-section\">\r\n <ng-container *ngIf=\"!fileInfo\">\r\n <svg viewBox=\"0 0 24 24\" class=\"upload-icon\" (click)=\"openFileDialog()\">\r\n <path d=\"M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z\" fill=\"currentColor\" />\r\n </svg>\r\n </ng-container>\r\n <ng-container *ngIf=\"fileInfo\">\r\n <div class=\"fv-action-icons\">\r\n <span class=\"material-icons view-icon\" (click)=\"viewFile()\" title=\"View\">visibility</span>\r\n <span class=\"material-icons delete-icon\" (click)=\"removeFile()\" title=\"Delete\">delete</span>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-document-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>\r\n\r\n<!-- Preview Overlay/Modal for Image -->\r\n<div *ngIf=\"showPreview\" class=\"fv-preview-overlay\" (click)=\"closePreview()\">\r\n <div class=\"fv-preview-content\" (click)=\"$event.stopPropagation()\">\r\n <img [src]=\"previewUrl\" alt=\"Preview\" />\r\n <div class=\"fv-preview-actions\">\r\n <button type=\"button\" class=\"fv-download-btn\" (click)=\"downloadFile()\">Download</button>\r\n <button type=\"button\" class=\"fv-close-btn\" (click)=\"closePreview()\">Close</button>\r\n </div>\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";.fv-document-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:fit-content;max-width:100%;font-family:Poppins,sans-serif}.fv-document-field-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-upload-button-container{display:flex;align-items:center;border:1px solid #dcdcdc;border-radius:8px;background:#fff;padding:0;cursor:default;overflow:hidden;width:100%;min-width:160px;height:34px;transition:all .2s ease}.fv-upload-button-container:hover:not(.fv-disabled){border-color:#8cbba8}.fv-disabled{cursor:not-allowed!important;background-color:#f9f9f9;opacity:.7}.fv-error{border-color:#dc3545!important}.fv-upload-main-section{flex:1;display:flex;align-items:center;padding:0 12px;height:100%;cursor:pointer;overflow:hidden}.fv-upload-text{font-size:14px;color:#1f2b41;text-align:left;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fv-upload-icon-section{background:#f4f5f7;padding:0 12px;border-left:1px solid #dcdcdc;display:flex;align-items:center;justify-content:center;color:#333;height:100%}.upload-icon{width:20px;height:20px;cursor:pointer}.fv-action-icons{display:flex;align-items:center;gap:8px}.material-icons{font-size:20px}.view-icon{color:#3498db;cursor:pointer}.delete-icon{color:#e74c3c;cursor:pointer}.view-icon:hover,.delete-icon:hover{opacity:.8}.fv-document-error-message{margin-top:4px;font-size:12px;color:#e74c3c}.fv-preview-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#000c;display:flex;justify-content:center;align-items:center;z-index:10000}.fv-preview-content{position:relative;background:#fff;padding:24px;border-radius:12px;max-width:90vw;max-height:90vh;display:flex;flex-direction:column;align-items:center}.fv-preview-content img{max-width:100%;max-height:70vh;object-fit:contain;border-radius:4px;margin-bottom:20px}.fv-preview-actions{display:flex;gap:16px}.fv-preview-actions button{padding:10px 24px;border-radius:6px;border:none;cursor:pointer;font-weight:500;transition:background .2s}.fv-download-btn{background:#3498db;color:#fff}.fv-download-btn:hover{background:#2980b9}.fv-close-btn{background:#f0f0f0;color:#333}.fv-close-btn:hover{background:#e0e0e0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }] });
|
|
3019
3066
|
}
|
|
3020
3067
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FvDocumentFieldComponent, decorators: [{
|
|
3021
3068
|
type: Component,
|
|
3022
|
-
args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-document-field', template: "<div class=\"fv-document-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-document-field-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 <div
|
|
3069
|
+
args: [{ standalone: true, imports: [CommonModule, ReactiveFormsModule], selector: 'fv-document-field', template: "<div class=\"fv-document-field-container\">\r\n <label *ngIf=\"label\" class=\"fv-document-field-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 <div class=\"fv-upload-button-container\" [class.fv-disabled]=\"disabled\"\r\n [class.fv-error]=\"errorMessage && control?.touched\">\r\n <div class=\"fv-upload-main-section\" (click)=\"openFileDialog()\">\r\n <span class=\"fv-upload-text\">{{ fileInfo ? fileInfo.name : placeholder }}</span>\r\n </div>\r\n <div class=\"fv-upload-icon-section\">\r\n <ng-container *ngIf=\"!fileInfo\">\r\n <svg viewBox=\"0 0 24 24\" class=\"upload-icon\" (click)=\"openFileDialog()\">\r\n <path d=\"M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z\" fill=\"currentColor\" />\r\n </svg>\r\n </ng-container>\r\n <ng-container *ngIf=\"fileInfo\">\r\n <div class=\"fv-action-icons\">\r\n <span class=\"material-icons view-icon\" (click)=\"viewFile()\" title=\"View\">visibility</span>\r\n <span class=\"material-icons delete-icon\" (click)=\"removeFile()\" title=\"Delete\">delete</span>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage && control?.touched\" class=\"fv-document-error-message\">\r\n \u26A0 {{ getErrorMessage() }}\r\n </div>\r\n</div>\r\n\r\n<!-- Preview Overlay/Modal for Image -->\r\n<div *ngIf=\"showPreview\" class=\"fv-preview-overlay\" (click)=\"closePreview()\">\r\n <div class=\"fv-preview-content\" (click)=\"$event.stopPropagation()\">\r\n <img [src]=\"previewUrl\" alt=\"Preview\" />\r\n <div class=\"fv-preview-actions\">\r\n <button type=\"button\" class=\"fv-download-btn\" (click)=\"downloadFile()\">Download</button>\r\n <button type=\"button\" class=\"fv-close-btn\" (click)=\"closePreview()\">Close</button>\r\n </div>\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap\";.fv-document-field-container{display:flex;flex-direction:column;margin-bottom:16px;width:fit-content;max-width:100%;font-family:Poppins,sans-serif}.fv-document-field-label{font-size:14px;font-weight:600;color:#151d48;margin-bottom:6px;display:block}.required-asterisk{color:#e74c3c;font-weight:700}.fv-upload-button-container{display:flex;align-items:center;border:1px solid #dcdcdc;border-radius:8px;background:#fff;padding:0;cursor:default;overflow:hidden;width:100%;min-width:160px;height:34px;transition:all .2s ease}.fv-upload-button-container:hover:not(.fv-disabled){border-color:#8cbba8}.fv-disabled{cursor:not-allowed!important;background-color:#f9f9f9;opacity:.7}.fv-error{border-color:#dc3545!important}.fv-upload-main-section{flex:1;display:flex;align-items:center;padding:0 12px;height:100%;cursor:pointer;overflow:hidden}.fv-upload-text{font-size:14px;color:#1f2b41;text-align:left;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fv-upload-icon-section{background:#f4f5f7;padding:0 12px;border-left:1px solid #dcdcdc;display:flex;align-items:center;justify-content:center;color:#333;height:100%}.upload-icon{width:20px;height:20px;cursor:pointer}.fv-action-icons{display:flex;align-items:center;gap:8px}.material-icons{font-size:20px}.view-icon{color:#3498db;cursor:pointer}.delete-icon{color:#e74c3c;cursor:pointer}.view-icon:hover,.delete-icon:hover{opacity:.8}.fv-document-error-message{margin-top:4px;font-size:12px;color:#e74c3c}.fv-preview-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#000c;display:flex;justify-content:center;align-items:center;z-index:10000}.fv-preview-content{position:relative;background:#fff;padding:24px;border-radius:12px;max-width:90vw;max-height:90vh;display:flex;flex-direction:column;align-items:center}.fv-preview-content img{max-width:100%;max-height:70vh;object-fit:contain;border-radius:4px;margin-bottom:20px}.fv-preview-actions{display:flex;gap:16px}.fv-preview-actions button{padding:10px 24px;border-radius:6px;border:none;cursor:pointer;font-weight:500;transition:background .2s}.fv-download-btn{background:#3498db;color:#fff}.fv-download-btn:hover{background:#2980b9}.fv-close-btn{background:#f0f0f0;color:#333}.fv-close-btn:hover{background:#e0e0e0}\n"] }]
|
|
3023
3070
|
}], ctorParameters: () => [{ type: i1$1.DomSanitizer }], propDecorators: { label: [{
|
|
3024
3071
|
type: Input
|
|
3025
3072
|
}], placeholder: [{
|
|
@@ -4226,7 +4273,7 @@ class AddUpdateFormComponent {
|
|
|
4226
4273
|
</div>
|
|
4227
4274
|
</form>
|
|
4228
4275
|
</div>
|
|
4229
|
-
`, 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: ["valueChange"] }, { 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"] }, { kind: "component", type: FvDocumentFieldComponent, selector: "fv-document-field", inputs: ["label", "placeholder", "schema", "control", "disabled", "accept", "maxSize"], outputs: ["valueChange"] }, { kind: "component", type: FvTimeFieldComponent, selector: "fv-time-field", inputs: ["label", "schema", "control", "disabled", "readonly", "placeholder"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvToggleComponent, selector: "fv-toggle", inputs: ["label", "control", "disabled", "required"], outputs: ["valueChange"] }] });
|
|
4276
|
+
`, 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%}}.checkbox-label{cursor:pointer;margin:0}.form-actions{display:flex;justify-content:flex-end;gap:12px;margin-top:20px}.btn{padding:8px 16px;border-radius:4px;font-weight:500;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:14px;transition:all .2s}.btn-primary{background-color:#006aff;color:#fff;border:1px solid #006aff}.btn-primary:hover:not(:disabled){background-color:#0056cc}.btn-outline{background-color:transparent;color:#303030;border:1px solid #dcdcdc}.btn-outline:hover:not(:disabled){background-color:#f5f5f5}.btn:disabled{opacity:.6;cursor:not-allowed}.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: ["valueChange"] }, { 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"] }, { kind: "component", type: FvDocumentFieldComponent, selector: "fv-document-field", inputs: ["label", "placeholder", "schema", "control", "disabled", "accept", "maxSize"], outputs: ["valueChange"] }, { kind: "component", type: FvTimeFieldComponent, selector: "fv-time-field", inputs: ["label", "schema", "control", "disabled", "readonly", "placeholder"], outputs: ["valueChange", "blur", "focus"] }, { kind: "component", type: FvToggleComponent, selector: "fv-toggle", inputs: ["label", "control", "disabled", "required"], outputs: ["valueChange"] }] });
|
|
4230
4277
|
}
|
|
4231
4278
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AddUpdateFormComponent, decorators: [{
|
|
4232
4279
|
type: Component,
|
|
@@ -4588,7 +4635,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4588
4635
|
</div>
|
|
4589
4636
|
</form>
|
|
4590
4637
|
</div>
|
|
4591
|
-
`, 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%}}
|
|
4638
|
+
`, 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%}}.checkbox-label{cursor:pointer;margin:0}.form-actions{display:flex;justify-content:flex-end;gap:12px;margin-top:20px}.btn{padding:8px 16px;border-radius:4px;font-weight:500;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:14px;transition:all .2s}.btn-primary{background-color:#006aff;color:#fff;border:1px solid #006aff}.btn-primary:hover:not(:disabled){background-color:#0056cc}.btn-outline{background-color:transparent;color:#303030;border:1px solid #dcdcdc}.btn-outline:hover:not(:disabled){background-color:#f5f5f5}.btn:disabled{opacity:.6;cursor:not-allowed}.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"] }]
|
|
4592
4639
|
}], ctorParameters: () => [{ type: i2.FormBuilder }, { type: i0.ElementRef }], propDecorators: { config: [{
|
|
4593
4640
|
type: Input
|
|
4594
4641
|
}], validationError: [{
|